MH_TEST_DIR="@abs_builddir@/test/testdir" \
auxexecdir="$(auxexecdir)" bindir="$(bindir)" \
mandir="$(mandir)" sysconfdir="$(sysconfdir)" \
+ supported_locks="$(supported_locks)" \
MULTIBYTE_ENABLED=$(MULTIBYTE_ENABLED) \
ICONV_ENABLED=$(ICONV_ENABLED) \
$(TESTS_SHELL) ## Keep at end of TESTS_ENVIRONMENT.
test/inc/test-deb359167 test/inc/test-eom-align \
test/inc/test-inc-scanout test/inc/test-msgchk \
test/inc/test-pop \
- test/install-mh/test-install-mh test/manpages/test-manpages \
+ test/install-mh/test-install-mh \
+ test/locking/test-datalocking test/locking/test-spoollocking \
+ test/manpages/test-manpages \
test/mhbuild/test-forw test/mhbuild/test-utf8-body \
+ test/mhfixmsg/test-mhfixmsg \
test/mhlist/test-mhlist test/mhmail/test-mhmail \
test/mhparam/test-mhparam test/mhpath/test-mhpath \
test/mhshow/test-cte-binary test/mhshow/test-qp \
##
bin_PROGRAMS = uip/ali uip/anno uip/burst uip/comp uip/dist uip/flist \
uip/fmttest uip/folder uip/forw uip/inc uip/install-mh \
- uip/mark uip/mhbuild uip/mhlist uip/mhn uip/mhparam \
- uip/mhpath uip/mhshow uip/mhstore uip/msgchk uip/msh uip/new \
- uip/packf uip/pick uip/prompter uip/refile uip/repl uip/rmf \
- uip/rmm uip/scan uip/send uip/show uip/sortm uip/whatnow \
- uip/whom
+ uip/mark uip/mhbuild uip/mhfixmsg uip/mhlist uip/mhn \
+ uip/mhparam uip/mhpath uip/mhshow uip/mhstore uip/msgchk \
+ uip/msh uip/new uip/packf uip/pick uip/prompter uip/refile \
+ uip/repl uip/rmf uip/rmm uip/scan uip/send uip/show uip/sortm \
+ uip/whatnow uip/whom
bin_SCRIPTS = uip/mhmail etc/sendfiles
man/forw.1 man/fprev.1 man/inc.1 man/install-mh.1 man/mark.1 \
man/mh-alias.5 man/mh-chart.7 man/mh-draft.5 man/mh-folders.5 \
man/mh-format.5 man/mh-mail.5 man/mh-profile.5 man/mh_profile.5 \
- man/mh-sequence.5 man/mh-tailor.5 man/mhbuild.1 man/mhl.1 \
- man/mhlist.1 man/mhmail.1 man/mhn.1 man/mhparam.1 man/mhpath.1 \
- man/mhshow.1 man/mhstore.1 man/msgchk.1 man/msh.1 \
+ man/mh-sequence.5 man/mh-tailor.5 man/mhbuild.1 man/mhfixmsg.1 \
+ man/mhl.1 man/mhlist.1 man/mhmail.1 man/mhn.1 man/mhparam.1 \
+ man/mhpath.1 man/mhshow.1 man/mhstore.1 man/msgchk.1 man/msh.1 \
man/mts.conf.5 man/new.1 man/next.1 man/nmh.7 man/packf.1 \
man/pick.1 man/post.8 man/prev.1 man/prompter.1 man/rcvdist.1 \
man/rcvpack.1 man/rcvstore.1 man/rcvtty.1 man/refile.1 \
man/mh-chart-gen.sh man/mh-draft.man man/mh-folders.man \
man/mh-format.man man/mh-mail.man man/mh-profile.man \
man/mh_profile.man man/mh-sequence.man man/mh-tailor.man \
- man/mhbuild.man man/mhl.man man/mhlist.man man/mhmail.man \
- man/mhn.man man/mhparam.man man/mhpath.man man/mhshow.man \
- man/mhstore.man man/msgchk.man man/msh.man man/mts.conf.man \
- man/new.man man/next.man man/nmh.man \
+ man/mhbuild.man man/mhfixmsg.man man/mhl.man man/mhlist.man \
+ man/mhmail.man man/mhn.man man/mhparam.man man/mhpath.man \
+ man/mhshow.man man/mhstore.man man/msgchk.man man/msh.man \
+ man/mts.conf.man man/new.man man/next.man man/nmh.man \
man/packf.man man/pick.man man/post.man man/prev.man \
man/prompter.man man/rcvdist.man man/rcvpack.man \
man/rcvstore.man man/rcvtty.man man/refile.man man/repl.man \
uip/mhfree.c uip/mhparse.c uip/termsbr.c uip/md5.c
uip_mhbuild_LDADD = $(LDADD) $(TERMLIB)
+uip_mhfixmsg_SOURCES = uip/mhfixmsg.c uip/mhparse.c uip/mhcachesbr.c \
+ uip/mhoutsbr.c uip/mhmisc.c uip/mhfree.c \
+ uip/mhshowsbr.c uip/mhlistsbr.c \
+ uip/termsbr.c uip/md5.c
+uip_mhfixmsg_LDADD = $(LDADD) $(ICONVLIB) $(TERMLIB)
+
uip_mhlist_SOURCES = uip/mhlist.c uip/mhparse.c uip/mhcachesbr.c \
uip/mhlistsbr.c uip/mhmisc.c uip/mhfree.c uip/termsbr.c \
uip/md5.c
$(SED) -e 's,%mts%,$(MTS),' \
-e 's,%mailspool%,$(mailspool),' \
-e 's,%smtpservers%,$(smtpservers),' \
+ -e 's,%default_locking%,$(default_locking),' \
+ -e 's,%supported_locks%,$(supported_locks),' \
< $(srcdir)/etc/mts.conf.in > $@
etc/mhn.defaults: $(srcdir)/etc/mhn.defaults.sh $(MHNSEARCHPROG)
sbr/m_mktemp.c sbr/getansreadline.c config/config.c \
config/version.c
+##
+## Because these files use the definitions in the libmh rule below,
+## they need to be rebuilt if the Makefile changes.
+##
+
+config/sbr_libmh_a-config.$(OBJEXT) sbr/sbr_libmh_a-mts.$(OBJEXT): Makefile
+
sbr_libmh_a_CPPFLAGS = -I./sbr -DNMHETCDIR='"$(sysconfdir)"' \
-DMAILSPOOL='"$(mailspool)"' \
-DSENDMAILPATH='"$(sendmailpath)"' -DNMHBINDIR='"$(bindir)"' \
@echo 's,%mandir%,$(mandir),g' >> $@
@echo 's,%mailspool%,$(mailspool),g' >> $@
@echo 's,%sendmailpath%,$(sendmailpath),g' >> $@
+ @echo 's,%default_locking%,$(default_locking),g' >> $@
+ @echo 's,%supported_locks%,$(supported_locks),g' >> $@
@echo 's,%manext1%,$(manext1),g' >> $@
@echo 's,%manext5%,$(manext5),g' >> $@
@echo 's,%manext7%,$(manext7),g' >> $@
AC_INIT([nmh], m4_normalize(m4_include([VERSION])), [nmh-workers@nongnu.org])
AC_CONFIG_SRCDIR([h/nmh.h])
AC_CONFIG_HEADER([config.h])
-AM_INIT_AUTOMAKE([-Wall color-tests foreign serial-tests subdir-objects 1.12])
+AM_INIT_AUTOMAKE([-Wall foreign serial-tests subdir-objects 1.12])
AC_CANONICAL_HOST
AC_DEFINE_UNQUOTED([BACKUP_PREFIX], "$backup_prefix",
[The prefix that is prepended to the name of message files when they are "removed" by rmm. This should typically be `,' or `#'.])dnl
- dnl What method of locking to use?
- AS_CASE(["$host_os"],
- [aix*|cygwin*|linux*],
- [default_locktype="fcntl"; default_locking=FCNTL_LOCKING],
- [freebsd*|openbsd*|darwin*], [default_locktype="flock"; default_locking=FLOCK_LOCKING],
- [default_locktype="dot"; default_locking=DOT_LOCKING])
-
- AC_ARG_WITH([locking],
- AS_HELP_STRING([--with-locking=@<:@dot|fcntl|flock|lockf@:>@],
- [specify the file locking method]))
-
- AS_IF([test x"$with_locking" = x"dot"],
- [LOCKTYPE="dot"
- AC_DEFINE([DOT_LOCKING], [1], [Define to use dot based file locking.])],
- [test x"$with_locking" = x"flock"],
- [LOCKTYPE="flock"
- AC_DEFINE([FLOCK_LOCKING], [1], [Define to use flock() based locking.])],
- [test x"$with_locking" = x"lockf"],
- [LOCKTYPE="lockf"
- AC_DEFINE([LOCKF_LOCKING], [1], [Define to use lockf() based locking.])],
- [test x"$with_locking" = x"fcntl"],
- [LOCKTYPE="fcntl"
- AC_DEFINE([FCNTL_LOCKING], [1], [Define to use fnctl() based locking.])],
- [LOCKTYPE="$default_locktype"
- AC_DEFINE_UNQUOTED([$default_locking], [1])])
-
- dnl Should we use a locking directory?
- AC_ARG_ENABLE([lockdir],
- [AS_HELP_STRING([--enable-lockdir=dir], [Store dot-lock files in "dir"])], [
- AS_IF([test "x$enableval" = xyes],[
- AC_MSG_ERROR([--enable-lockdir requires an argument])])
- AS_IF([test "x$LOCKTYPE" != xdot],[
- AC_MSG_ERROR([Can only use --enable-lockdir with dot locking])])
- AC_DEFINE_UNQUOTED([LOCKDIR], ["$enableval"],
- [Directory to store dot-locking lock files])
- ])
-
dnl What method of posting should post use?
AC_ARG_WITH([mts],
AS_HELP_STRING([--with-mts=@<:@smtp|sendmail/smtp|sendmail/pipe@:>@],
dnl Check for readline support
NMH_READLINE
+ dnl Check the locking functions supported and what we should use by default
+ NMH_LOCKING
+
dnl Check for iconv
NMH_CHECK_ICONV
RPM build root : ${nmhrpm}
backup prefix : ${backup_prefix}
transport system : ${MTS}
- file locking type : ${LOCKTYPE}
+ spool default locking type : ${with_locking}
default smtp servers : ${smtpservers}
- default editor : ${editorpath}
SASL support : ${sasl_support}
TLS support : ${tls_support}
])])dnl
- A new program, fmttest(1) is included to help debug format files
- mhshow/mhstore now have support for RFC-2017 (access-type=url) for
external message bodies.
+- Added -retainsequences switch to refile(1).
+- A new program, mhfixmsg(1), is included to rewrite MIME messages with
+ various transformations.
+- Added -[no]rmmproc switches to rmm(1).
+- Added support for Content-Disposition header (RFC 2183) to mhstore(1)
+ and mhn(1) when used with -auto.
+ - All nmh commands now support transactional locking for sequence files.
----------------------------
OBSOLETE/DEPRECATED FEATURES
-.TH MH-PROFILE %manext5% "November 6, 2012" "%nmhversion%"
+.TH MH-PROFILE %manext5% "March 18, 2013" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
(profile, default: 700)
.RE
.PP
+ .BR datalocking :
+ fcntl
+ .RS 5
+ The locking algorithm used to lock changes to any
+ .B nmh
+ data files, such as sequences or the context. The locking algorithm is
+ any one of the following entries:
+ .PP
+ .RS 5
+ .nf
+ %supported_locks%
+ .fi
+ .RE
+ .PP
+ Available locking algorithms can vary depending on what is supported by
+ the operating system. Note: currently transactional locking is only
+ supported on public sequences; see
+ .IR mh\-sequence (5)
+ for more information.
+ (profile, default: fcntl)
+ .RE
+ .PP
.IR program :
.I default switches
.RS 5
none
.RS 5
This is the program used by
-.B rmm
+.BR rmm ,
+.BR refile ,
and
-.B refile
+.B mhfixmsg
to delete a message from a folder.
.RE
.PP
static int
init_decoded_content (CT ct)
{
- CE ce;
-
- if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
- adios (NULL, "out of memory");
-
- ct->c_cefile = ce;
ct->c_ceopenfnx = open7Bit; /* since unencoded */
ct->c_ceclosefnx = close_encoding;
ct->c_cesizefnx = NULL; /* since unencoded */
/* allocate basic structure for handling decoded content */
init_decoded_content (ct);
- ce = ct->c_cefile;
+ ce = &ct->c_cefile;
ci = &ct->c_ctinfo;
set_id (ct, 0);
if (!folder)
folder = add (getfolder (1), NULL);
- if (!(mp = folder_read (folder)))
+ if (!(mp = folder_read (folder, 0)))
adios (NULL, "unable to read folder %s", folder);
for (ap = arguments; *ap; ap++) {
cp = *ap;
if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
adios (NULL, "out of memory");
init_decoded_content (p);
- pe = p->c_cefile;
+ pe = &p->c_cefile;
if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
done (1);
p->c_type = CT_MESSAGE;
static int
compose_content (CT ct)
{
- CE ce = ct->c_cefile;
+ CE ce = &ct->c_cefile;
switch (ct->c_type) {
case CT_MULTIPART:
char *cp = NULL, buffer[BUFSIZ];
struct text *t = NULL;
FILE *in = NULL;
- CE ce = ct->c_cefile;
+ CE ce = &ct->c_cefile;
/*
* handle multipart by scanning all subparts
unsigned char digest[16];
unsigned char outbuf[25];
MD5_CTX mdContext;
- CE ce = ct->c_cefile;
+ CE ce = &ct->c_cefile;
char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
FILE *in;
{
int cachetype;
char *file, cachefile[BUFSIZ];
- CE ce = ct->c_cefile;
+ CE ce = &ct->c_cefile;
if (!ct->c_id) {
advise (NULL, "no %s: field in %s", ID_FIELD, ct->c_file);
make_intermediates (mapfile);
mask = umask (writing == 2 ? 0077 : 0);
- if (!(fp = lkfopen (mapfile, "a")) && errno == ENOENT) {
+ if (!(fp = lkfopendata (mapfile, "a")) && errno == ENOENT) {
int fd;
if ((fd = creat (mapfile, 0666)) != NOTOK) {
close (fd);
- fp = lkfopen (mapfile, "a");
+ fp = lkfopendata (mapfile, "a");
}
}
umask (mask);
if (!fp)
return NOTOK;
fprintf (fp, "%s: %s\n", mapname, id);
- lkfclose (fp, mapfile);
+ lkfclosedata (fp, mapfile);
done_map:
if (*mapname == '/')
FILE *fp;
m_getfld_state_t gstate = 0;
- if (!(fp = lkfopen (mapfile, "r")))
+ if (!(fp = lkfopendata (mapfile, "r")))
return NOTOK;
for (;;) {
result = strcmp (id, dp);
free (dp);
if (result == 0) {
- lkfclose (fp, mapfile);
+ lkfclosedata (fp, mapfile);
return OK;
}
continue;
}
m_getfld_state_destroy (&gstate);
- lkfclose (fp, mapfile);
+ lkfclosedata (fp, mapfile);
return NOTOK;
}
struct msgs *mp;
/* Read the folder. */
- if ((mp = folder_read (folder))) {
+ if ((mp = folder_read (folder, 0))) {
/* Link file into folder */
msgnum = folder_addmsg (&mp, filename, 0, 0, 0, 0, (char *)0);
} else {
get_storeproc (CT ct)
{
char **ap, **ep, *cp;
- CI ci = &ct->c_ctinfo;
+ CI ci;
/*
* If the storeproc has already been defined,
if (ct->c_storeproc)
return;
+ /*
+ * If there's a Content-Disposition header and it has a filename,
+ * use that (RFC-2183).
+ */
+ if (ct->c_dispo) {
+ char *cp = strchr (ct->c_dispo, ';');
+ CI ci = calloc (1, sizeof *ci);
+ int status;
+ int found_filename = 0;
+
+ if (cp && parse_header_attrs (ct->c_file, strlen (invo_name) + 2, &cp,
+ ci, &status) == OK) {
+ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+ if (! strcasecmp (*ap, "filename")
+ && *(cp = *ep) != '/'
+ && *cp != '.'
+ && *cp != '|'
+ && *cp != '!'
+ && !strchr (cp, '%')) {
+ ct->c_storeproc = add (cp, NULL);
+ found_filename = 1;
+ }
+ free (*ap);
+ }
+ }
+
+ free (ci);
+ if (found_filename) return;
+ }
+
/*
* Check the attribute/value pairs, for the attribute "name".
* If found, do a few sanity checks and copy the value into
* the storeproc.
*/
+ ci = &ct->c_ctinfo;
for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
- if (!mh_strcasecmp (*ap, "name")
+ if (! strcasecmp (*ap, "name")
&& *(cp = *ep) != '/'
&& *cp != '.'
&& *cp != '|'
#include <h/utils.h>
#include <fcntl.h>
#include <errno.h>
+#include <assert.h>
#define REFILE_SWITCHES \
X("draft", 0, DRAFTSW) \
X("nolink", 0, NLINKSW) \
X("preserve", 0, PRESSW) \
X("nopreserve", 0, NPRESSW) \
+ X("retainsequences", 0, RETAINSEQSSW) \
+ X("noretainsequences", 0, NRETAINSEQSSW) \
X("unlink", 0, UNLINKSW) \
X("nounlink", 0, NUNLINKSW) \
X("src +folder", 0, SRCSW) \
static void opnfolds (struct st_fold *, int);
static void clsfolds (struct st_fold *, int);
static void remove_files (int, char **);
-static int m_file (char *, struct st_fold *, int, int, int);
+static int m_file (struct msgs *, char *, int, struct st_fold *, int, int, int);
+static void copy_seqs (struct msgs *, int, struct msgs *, int);
int
main (int argc, char **argv)
{
- int linkf = 0, preserve = 0, filep = 0;
+ int linkf = 0, preserve = 0, retainseqs = 0, filep = 0;
int foldp = 0, isdf = 0, unlink_msgs = 0;
int i, msgnum;
char *cp, *folder = NULL, buf[BUFSIZ];
while ((cp = *argp++)) {
if (*cp == '-') {
switch (smatch (++cp, switches)) {
- case AMBIGSW:
+ case AMBIGSW:
ambigsw (cp, switches);
done (1);
- case UNKWNSW:
+ case UNKWNSW:
adios (NULL, "-%s unknown\n", cp);
- case HELPSW:
+ case HELPSW:
snprintf (buf, sizeof(buf), "%s [msgs] [switches] +folder ...",
invo_name);
print_help (buf, switches, 1);
print_version(invo_name);
done (0);
- case LINKSW:
+ case LINKSW:
linkf++;
continue;
- case NLINKSW:
+ case NLINKSW:
linkf = 0;
continue;
- case PRESSW:
+ case PRESSW:
preserve++;
continue;
- case NPRESSW:
+ case NPRESSW:
preserve = 0;
continue;
+ case RETAINSEQSSW:
+ retainseqs = 1;
+ continue;
+ case NRETAINSEQSSW:
+ retainseqs = 0;
+ continue;
+
case UNLINKSW:
unlink_msgs++;
continue;
unlink_msgs = 0;
continue;
- case SRCSW:
+ case SRCSW:
if (folder)
adios (NULL, "only one source folder at a time!");
if (!(cp = *argp++) || *cp == '-')
isdf = 0;
files[filep++] = getcpy (m_draft (NULL, NULL, 1, &isdf));
continue;
- case FILESW:
+ case FILESW:
if (filep > NFOLDERS)
adios (NULL, "only %d files allowed!", NFOLDERS);
if (!(cp = *argp++) || *cp == '-')
files[filep++] = path (cp, TFILE);
continue;
- case RPROCSW:
+ case RPROCSW:
if (!(rmmproc = *argp++) || *rmmproc == '-')
adios (NULL, "missing argument to %s", argp[-2]);
continue;
- case NRPRCSW:
+ case NRPRCSW:
rmmproc = NULL;
continue;
}
adios (NULL, "use -file or some messages, not both");
opnfolds (folders, foldp);
for (i = 0; i < filep; i++)
- if (m_file (files[i], folders, foldp, preserve, 0))
+ if (m_file (0, files[i], 0, folders, foldp, preserve, 0))
done (1);
/* If -nolink, then "remove" files */
if (!linkf)
adios (maildir, "unable to change directory to");
/* read source folder and create message structure */
- if (!(mp = folder_read (folder)))
+ if (!(mp = folder_read (folder, 1)))
adios (NULL, "unable to read folder %s", folder);
/* check for empty folder */
for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
if (is_selected (mp, msgnum)) {
cp = getcpy (m_name (msgnum));
- if (m_file (cp, folders, foldp, preserve, !linkf))
+ if (m_file (mp, cp, retainseqs ? msgnum : 0, folders, foldp,
+ preserve, !linkf))
done (1);
free (cp);
}
if (chdir (nmaildir) == NOTOK)
adios (nmaildir, "unable to change directory to");
- if (!(mp = folder_read (fp->f_name)))
+ if (!(mp = folder_read (fp->f_name, 1)))
adios (NULL, "unable to read folder %s", fp->f_name);
mp->curmsg = 0;
/*
- * Link (or copy) the message into each of
- * the destination folders.
+ * Link (or copy) the message into each of the destination folders.
+ * If oldmsgnum is not 0, call copy_seqs().
*/
static int
-m_file (char *msgfile, struct st_fold *folders, int nfolders, int preserve, int refile)
+m_file (struct msgs *mp, char *msgfile, int oldmsgnum,
+ struct st_fold *folders, int nfolders, int preserve, int refile)
{
int msgnum;
struct st_fold *fp, *ep;
for (fp = folders, ep = folders + nfolders; fp < ep; fp++) {
if ((msgnum = folder_addmsg (&fp->f_mp, msgfile, 1, 0, preserve, nfolders == 1 && refile, maildir)) == -1)
return 1;
+ if (oldmsgnum) copy_seqs (mp, oldmsgnum, fp->f_mp, msgnum);
}
return 0;
}
+
+
+/*
+ * Copy sequence information for a refiled message to its
+ * new folder. Skip the cur sequence.
+ */
+static void
+copy_seqs (struct msgs *oldmp, int oldmsgnum, struct msgs *newmp, int newmsgnum)
+{
+ char **seq;
+ int seqnum;
+
+ for (seq = oldmp->msgattrs, seqnum = 0; *seq; ++seq, ++seqnum) {
+ if (strcmp (current, *seq)) {
+ assert (seqnum == seq_getnum (oldmp, *seq));
+ if (in_sequence (oldmp, seqnum, oldmsgnum)) {
+ seq_addmsg (newmp, *seq, newmsgnum,
+ is_seq_private (oldmp, seqnum) ? 0 : 1, 0);
+ }
+ }
+ }
+}
#define RMM_SWITCHES \
X("unlink", 0, UNLINKSW) \
X("nounlink", 0, NUNLINKSW) \
+ X("rmmproc program", 0, RPROCSW) \
+ X("normmproc", 0, NRPRCSW) \
X("version", 0, VERSIONSW) \
X("help", 0, HELPSW) \
while ((cp = *argp++)) {
if (*cp == '-') {
switch (smatch (++cp, switches)) {
- case AMBIGSW:
+ case AMBIGSW:
ambigsw (cp, switches);
done (1);
- case UNKWNSW:
+ case UNKWNSW:
adios (NULL, "-%s unknown\n", cp);
- case HELPSW:
+ case HELPSW:
snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
invo_name);
print_help (buf, switches, 1);
case NUNLINKSW:
unlink_msgs = 0;
continue;
+
+ case RPROCSW:
+ if (!(rmmproc = *argp++) || *rmmproc == '-')
+ adios (NULL, "missing argument to %s", argp[-2]);
+ continue;
+ case NRPRCSW:
+ rmmproc = NULL;
+ continue;
}
}
if (*cp == '+' || *cp == '@') {
adios (maildir, "unable to change directory to");
/* read folder and create message structure */
- if (!(mp = folder_read (folder)))
+ if (!(mp = folder_read (folder, 1)))
adios (NULL, "unable to read folder %s", folder);
/* check for empty folder */