HEAD after a git-bisect.
##
## Stuff that should be cleaned via "make maintainer-clean"
##
-MAINTAINERCLEANFILES = cscope.files cscope.out cscope.in.out cscope.po.out *.plist
+MAINTAINERCLEANFILES = atags cscope.files cscope.out cscope.in.out cscope.po.out *.plist
##
## And our own superclean, to get everything left by maintainer-clean.
## Hard-code the config/version.c target, instead of using $@, so this
## rule works for the distcheck target with Solaris (System V) make.
## distcheck uses VPATH, causes that make to prepend the VPATH to $@.
-config/version.c: Makefile
- sh $(srcdir)/config/version.sh $(VERSION) > ./config/version.c
+config/version.c: Makefile $(srcdir)/config/version.sh
+ env srcdir="$(srcdir)" sh $(srcdir)/config/version.sh $(VERSION) > ./config/version.c
sbr/sigmsg.h: $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H)
$(AWK) -f $(srcdir)/sbr/sigmsg.awk $(SIGNAL_H) > $@
IFS=" "
-echo "char *version_str = \"nmh-$VERSION [compiled on $HOSTNAME at `date`]\";"
+if [ -d "${srcdir}/.git" ] ; then
+ branch=`(git branch | grep '^\*' | tr -d '* ') || true`
+fi
+if [ "${branch}" -a "${branch}" != "master" ] ; then
+ echo "char *version_str = \"nmh-$VERSION [branch ${branch}] [compiled on $HOSTNAME at `date`]\";"
+else
+ echo "char *version_str = \"nmh-$VERSION [compiled on $HOSTNAME at `date`]\";"
+fi
echo "char *version_num = \"nmh-$VERSION\";"
config_prefix=`cd $mhbin/.. && pwd`
- mtsconf=`dirname "$mhbin"`/etc/mts.conf
+ mtsconf=`mhparam etcdir`/mts.conf
if [ -f "$mtsconf" ]; then
mts_entry=`grep '^mts:' "$mtsconf"`
if [ "$mts_entry" ]; then
obsolete support programs that should be removed.
- When building from source, configure will derive ${prefix} from an existing
nmh installation if it finds one in your $PATH.
+- Added getmymbox and getmyaddr mh-format(5) function escapes.
+- The -changecur and -nochangecur switches have been added to mhfixmsg(1).
+- mhfixmsg now removes an extraneous trailing semicolon from header
+ parameter lists.
-----------------
OBSOLETE FEATURES
fi
echo "mhstore-store-text: %m%P.txt" >> $TMP
+echo "mhstore-store-text/html: %m%P.html" >> $TMP
echo "mhstore-store-text/richtext: %m%P.rt" >> $TMP
echo "mhstore-store-video/mpeg: %m%P.mpg" >> $TMP
echo "mhstore-store-application/PostScript: %m%P.ps" >> $TMP
# This entry is used to retrieve external-body types that use a "url"
# access-type.
-PGM=`$SEARCHPROG "$SEARCHPATH" curl`
-if [ ! -z "$PGM" ]; then
- echo "nmh-access-url: $PGM -L" >> $TMP
-fi
+case "`uname`" in
+ FreeBSD)
+ echo "nmh-access-url: fetch -o -" >> $TMP
+ ;;
+ *)
+ PGM=`$SEARCHPROG "$SEARCHPATH" curl`
+ if [ ! -z "$PGM" ]; then
+ echo "nmh-access-url: $PGM -L" >> $TMP
+ fi
+ ;;
+esac
# output a sorted version of the file
sort < $TMP
#define FT_LV_HOSTTYPE 63 /* set "value" to addr host type */
#define FT_LV_INGRPF 64 /* set "value" to addr in-group flag */
#define FT_LS_UNQUOTE 65 /* remove RFC 2822 quotes from "str" */
-#define FT_LV_NOHOSTF 66 /* set "value" to addr no-host flag */
+#define FT_LV_NOHOSTF 66 /* set "value" to addr no-host flag */
/* Date Coercion */
#define FT_LOCALDATE 67 /* Coerce date to local timezone */
#define FT_FORMATADDR 71 /* let external routine format addr */
#define FT_CONCATADDR 72 /* formataddr w/out duplicate removal */
#define FT_MYMBOX 73 /* do "mymbox" test on comp */
+#define FT_GETMYMBOX 74 /* return "mymbox" mailbox string */
+#define FT_GETMYADDR 75 /* return "mymbox" addr string */
/* conditionals & control flow (must be last) */
-#define FT_SAVESTR 74 /* save current str reg */
-#define FT_DONE 75 /* stop formatting */
-#define FT_PAUSE 76 /* pause */
-#define FT_NOP 77 /* nop */
-#define FT_GOTO 78 /* (relative) goto */
-#define FT_IF_S_NULL 79 /* test if "str" null */
-#define FT_IF_S 80 /* test if "str" non-null */
-#define FT_IF_V_EQ 81 /* test if "value" = literal */
-#define FT_IF_V_NE 82 /* test if "value" != literal */
-#define FT_IF_V_GT 83 /* test if "value" > literal */
-#define FT_IF_MATCH 84 /* test if "str" contains literal */
-#define FT_IF_AMATCH 85 /* test if "str" starts with literal */
-#define FT_S_NULL 86 /* V = 1 if "str" null */
-#define FT_S_NONNULL 87 /* V = 1 if "str" non-null */
-#define FT_V_EQ 88 /* V = 1 if "value" = literal */
-#define FT_V_NE 89 /* V = 1 if "value" != literal */
-#define FT_V_GT 90 /* V = 1 if "value" > literal */
-#define FT_V_MATCH 91 /* V = 1 if "str" contains literal */
-#define FT_V_AMATCH 92 /* V = 1 if "str" starts with literal */
+#define FT_SAVESTR 76 /* save current str reg */
+#define FT_DONE 77 /* stop formatting */
+#define FT_PAUSE 78 /* pause */
+#define FT_NOP 79 /* nop */
+#define FT_GOTO 80 /* (relative) goto */
+#define FT_IF_S_NULL 81 /* test if "str" null */
+#define FT_IF_S 82 /* test if "str" non-null */
+#define FT_IF_V_EQ 83 /* test if "value" = literal */
+#define FT_IF_V_NE 84 /* test if "value" != literal */
+#define FT_IF_V_GT 85 /* test if "value" > literal */
+#define FT_IF_MATCH 86 /* test if "str" contains literal */
+#define FT_IF_AMATCH 87 /* test if "str" starts with literal */
+#define FT_S_NULL 88 /* V = 1 if "str" null */
+#define FT_S_NONNULL 89 /* V = 1 if "str" non-null */
+#define FT_V_EQ 90 /* V = 1 if "value" = literal */
+#define FT_V_NE 91 /* V = 1 if "value" != literal */
+#define FT_V_GT 92 /* V = 1 if "value" > literal */
+#define FT_V_MATCH 93 /* V = 1 if "str" contains literal */
+#define FT_V_AMATCH 94 /* V = 1 if "str" starts with literal */
#define IF_FUNCS FT_S_NULL /* start of "if" functions */
char *new_fs (char *form, char *format, char *default_fs);
+/*
+ * Free memory allocated by new_fs(). It allocates to a static so
+ * no argument is necessary.
+ */
+void free_fs ();
+
/*
* Compile a format string into a set of format instructions. Arguments are:
*
-
/*
* mh.h -- main header file for all of nmh
*/
/* Append possibly multi-byte character(s): */
void charstring_push_back_chars (charstring_t, const char [], size_t, size_t);
void charstring_append (charstring_t, const charstring_t);
+void charstring_append_cstring (charstring_t, const char []);
void charstring_clear (charstring_t);
/* Don't store return value of charstring_buffer() and use later after
intervening push_back's; use charstring_buffer_copy() instead. */
#define MULTI_ALTERNATE 0x02
#define MULTI_DIGEST 0x03
#define MULTI_PARALLEL 0x04
+#define MULTI_RELATED 0x05
/* Structure for subparts of a multipart content */
struct part {
struct format *fmt);
extern int checksw; /* Add Content-MD5 field */
+
+/*
+ * mhstore
+ * Put it here because it uses the CT typedef.
+ */
+typedef struct mhstoreinfo *mhstoreinfo_t;
+mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
+int mhstoreinfo_files_not_clobbered(const mhstoreinfo_t);
+void mhstoreinfo_free(mhstoreinfo_t);
+void store_all_messages (mhstoreinfo_t);
#define DESCR_FIELD "Content-Description"
#define DISPO_FIELD "Content-Disposition"
#define MD5_FIELD "Content-MD5"
-#define ATTACH_FIELD "Attach"
+#define PSEUDOHEADER_PREFIX "Nmh-"
+#define ATTACH_FIELD PSEUDOHEADER_PREFIX "Attach"
+#define ATTACH_FIELD_ALT "Attach"
#define isatom(c) (isascii((unsigned char) c) \
&& !isspace ((unsigned char) c) \
void *mh_xmalloc(size_t);
void *mh_xrealloc(void *, size_t);
+void *mh_xcalloc(size_t, size_t);
char *pwd(void);
char *add(const char *, char *);
char *addlist(char *, const char *);
-.TH MH-ALIAS %manext5% "Apr 18, 2014" "%nmhversion%"
+.TH MH-ALIAS %manext5% "November 15, 2014" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
Build a list of all addresses from the message to be delivered,
eliminating duplicate addresses.
.IP 2)
-If this draft originated on the local host, then for those addresses in
-the message that have no host specified, perform alias resolution.
+For those addresses in the message that have no host specified,
+perform alias resolution.
.IP 3)
For each line in the alias file, compare \*(lqalias\*(rq against all of
the existing addresses. If a match, remove the matched \*(lqalias\*(rq
-.TH MH-FORMAT %manext5% "November 4, 2012" "%nmhversion%"
+.TH MH-FORMAT %manext5% "October 27, 2014" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
month date string month of the year (abbrev.)
lmonth date string month of the year
year date integer year (may be > 100)
-zone date integer timezone in hours
+zone date integer timezone in minutes
tzone date string timezone string
szone date integer timezone explicit?
(1=explicit,0=implicit,\-1=unknown)
pers addr string the personal name*
note addr string commentary text*
mbox addr string the local mailbox*
-mymbox addr integer List has the user's address? (0 or 1)
+mymbox addr integer list has the user's address? (0 or 1)
+getmymbox addr string the user's (first) address,
+ with personal name
+getmyaddr addr string the user's (first) address,
+ without personal name
host addr string the host domain*
nohost addr integer no host was present (0 or 1)*
type addr integer host type* (0=local,1=network,
-.TH MHFIXMSG %manext1% "August 20, 2014" "%nmhversion%"
+.TH MHFIXMSG %manext1% "November 30, 2014" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
.RB [ \-rmmproc
.IR program ]
.RB [ \-normmproc ]
+.RB [ \-changecur " | " \-nochangecur ]
.RB [ \-verbose " | " \-noverbose ]
.RB [ \-version ]
.RB [ \-help ]
.B nmh
programs that parse MIME messages.
.PP
+.B mhfixmsg
+applies one transformation unconditionally: it removes an extraneous
+trailing semicolon from the parameter lists of MIME header fields.
+.PP
The
.B \-verbose
switch directs
.B mhfixmsg
to output informational message for each transformation applied.
.PP
+The return status of
+.B mhfixmsg
+is 0 if all of the requested transformations are performed, or
+non-zero otherwise.
+.RB ( mhfixmsg
+will not decode to binary content, but a request to do so is
+not considered a failure, and is noted with
+.BR \-verbose .)
+If a problem is detected with any one of multiple messages such that
+the return status is non-zero, then none of the messages will be
+modified.
+.PP
The
.B \-file
.I file
.B mhfixmsg
immediately after a successful invocation of
.BR inc .
-For example, with bash:
+One approach could be based on:
.PP
.RS 5
-alias inc='inc && mhfixmsg'
+msgs=`inc -format '%(msg)'` && [ -n "$msgs" ] && scan $msgs && \
+mhfixmsg -nochangecur $msgs
.RE
.PP
+Another approach would rely on adding a sequence to Unseen-Sequence,
+which
+.B inc
+sets with the newly incorporated messages. Those could then be
+supplied to
+.BR mhfixmsg .
.SS "Integration with procmail"
By way of example, here is an excerpt from a procmailrc file
that filters messages through
.RB ` \-noreplacetextplain '
.RB ` \-fixboundary '
.RB ` \-fixcte '
+.RB ` \-changecur '
.RB ` \-noverbose '
.fi
.SH CONTEXT
If a folder is given, it will become the current folder. The last
-message selected from a folder will become the current message. If
+message selected from a folder will become the current message, unless
the
+.B \-nochangecur
+switch is enabled. If the
.B \-file
switch or an absolute pathname is used, the context will not be
modified.
will attempt to
verify the integrity of the content.
.SS "Storing the Contents"
-The
.B mhstore
will store the contents of the named messages in
\*(lqnative\*(rq (decoded) format. Two things must be determined:
-.TH REPL %manext1% "April 18, 2014" "%nmhversion%"
+.TH REPL %manext1% "November 4, 2014" "%nmhversion%"
.\"
.\" %nmhwarning%
.\"
.na
.B repl
.RI [ +folder ]
-.RI [ msgs ]
+.RI [ msg ]
.RB [ \-annotate " | " \-noannotate ]
.RB [ \-group " | " \-nogroup ]
.RB [ \-cc
#include <h/addrsbr.h>
#include <h/mf.h>
#include <h/mts.h>
+#include <h/utils.h>
/* High level parsing of addresses:
dftype = LOCALHOST;
}
- mp = (struct mailname *) calloc ((size_t) 1, sizeof(*mp));
+ mp = (struct mailname *) mh_xcalloc ((size_t) 1, sizeof(*mp));
if (mp == NULL) {
if (eresult) {
strncpy (eresult, "insufficient memory to represent address",
const size_t num = src->cur - src->buffer;
if (num > 0) {
- charstring_reserve (dest, num + (dest->cur - dest->buffer));
+ charstring_reserve (dest, dest->cur - dest->buffer + num);
memcpy (dest->cur, src->buffer, num);
dest->cur += num;
dest->chars += src->chars;
}
}
+void
+charstring_append_cstring (charstring_t dest, const char src[]) {
+ const size_t num = strlen (src);
+
+ if (num > 0) {
+ charstring_reserve (dest, dest->cur - dest->buffer + num);
+ memcpy (dest->cur, src, num); /* Exclude src's trailing newline. */
+ dest->cur += num;
+ dest->chars += num;
+ }
+}
+
void
charstring_clear (charstring_t s) {
s->cur = s->buffer;
char *
charstring_buffer_copy (const charstring_t s) {
- char *copy = strdup (charstring_buffer (s));
+ char *copy = mh_xmalloc (s->cur - s->buffer + 1);
- if (copy) {
- return copy;
- } else {
- advise ("strdup", "unable to copy charstring buffer");
- return NULL;
- }
+ /* Use charstring_buffer() to null terminate the buffer. */
+ memcpy (copy, charstring_buffer (s), s->cur - s->buffer + 1);
+
+ return copy;
}
size_t
{ "friendly", TF_COMP, FT_LS_FRIENDLY, FT_PARSEADDR, TFL_PUTS },
{ "mymbox", TF_COMP, FT_LV_COMPFLAG, FT_MYMBOX, TFL_PUTN },
+ { "getmymbox", TF_COMP, FT_STR, FT_GETMYMBOX, 0 },
+ { "getmyaddr", TF_COMP, FT_LS_ADDR, FT_GETMYADDR, TFL_PUTS },
{ "unquote", TF_EXPR, FT_LS_UNQUOTE, 0, TFL_PUTS },
/* Add new component to the hash table */
#define NEWCOMP(cm,name) do { \
- cm = ((struct comp *) calloc(1, sizeof (struct comp)));\
+ cm = ((struct comp *) mh_xcalloc (1, sizeof (struct comp)));\
cm->c_name = getcpy(name);\
cm->c_refcount++;\
ncomp++;\
*/
i = strlen(fstring)/2 + 1;
if (i==1) i++;
- next_fp = formatvec = (struct format *)calloc ((size_t) i,
+ next_fp = formatvec = (struct format *)mh_xcalloc ((size_t) i,
sizeof(struct format));
if (next_fp == NULL)
adios (NULL, "unable to allocate format storage");
if (cm->c_type & CT_ADDR) {
CERROR("component used as both date and address");
}
- cm->c_tws = (struct tws *)
- calloc((size_t) 1, sizeof(*cm->c_tws));
+ if (cm->c_tws) {
+ memset (cm->c_tws, 0, sizeof *cm->c_tws);
+ } else {
+ cm->c_tws = mh_xcalloc (1, sizeof *cm->c_tws);
+ }
fp->f_type = preprocess;
PUTCOMP(sp);
cm->c_type |= CT_DATE;
break;
case FT_MYMBOX:
+ case FT_GETMYMBOX:
+ case FT_GETMYADDR:
if (!primed) {
ismymbox ((struct mailname *) 0);
primed++;
}
+void
+free_fs (){
+ free (formats);
+ formats = 0;
+}
+
+
/*
* Expand escapes in format strings
*/
value = -1;
break;
case FT_LV_DST:
- value = fmt->f_comp->c_tws->tw_flags & TW_DST;
+ value = fmt->f_comp->c_tws->tw_flags & TW_DST ? 1 : 0;
break;
case FT_LS_822DATE:
str = dasctime (fmt->f_comp->c_tws , TW_ZONE);
break;
case FT_MYMBOX:
+ case FT_GETMYMBOX:
+ case FT_GETMYADDR:
/*
* if there's no component, we say true. Otherwise we
* say "true" only if we can parse the address and it
if ((sp = comp->c_text) && (sp = getname(sp)) &&
(mn = getm (sp, NULL, 0, NULL, 0))) {
comp->c_mn = mn;
- if (ismymbox(mn))
+ if (ismymbox(mn)) {
comp->c_flags |= CF_TRUE;
- else
+ /* Set str for use with FT_GETMYMBOX. With
+ FT_GETMYADDR, comp->c_mn will be run through
+ FT_LS_ADDR, which will strip off any pers
+ name. */
+ str = mn->m_text;
+ } else {
comp->c_flags &= ~CF_TRUE;
+ }
while ((sp = getname(sp)))
if ((comp->c_flags & CF_TRUE) == 0 &&
(mn = getm (sp, NULL, 0, NULL, 0)))
- if (ismymbox(mn))
+ if (ismymbox(mn)) {
comp->c_flags |= CF_TRUE;
+ /* Set str and comp->c_text for use with
+ FT_GETMYMBOX. With FT_GETMYADDR,
+ comp->c_mn will be run through
+ FT_LS_ADDR, which will strip off any
+ pers name. */
+ free (comp->c_text);
+ comp->c_text = str = strdup (mn->m_text);
+ comp->c_mn = mn;
+ }
+ comp->c_flags |= CF_PARSED;
} else {
while (getname("")) /* XXX */
;
if (comp->c_text == 0)
comp->c_flags |= CF_TRUE;
- else
+ else {
comp->c_flags &= ~CF_TRUE;
+ }
comp->c_mn = &fmt_mnull;
}
+ if ((comp->c_flags & CF_TRUE) == 0 &&
+ (fmt->f_type == FT_GETMYMBOX || fmt->f_type == FT_GETMYADDR)) {
+ /* Fool FT_LS_ADDR into not producing an address. */
+ comp->c_mn = &fmt_mnull; comp->c_text = NULL;
+ }
break;
}
* separator) or the last char (since the matchc would have found it
* if it was a real delim).
*/
- s->pat_map = (char **) calloc (256, sizeof(char *));
+ s->pat_map = (char **) mh_xcalloc (256, sizeof(char *));
for (cp = s->fdelim + 1; cp < s->delimend; cp++ )
s->pat_map[(unsigned char)*cp] = cp;
return memory;
}
+/*
+ * Safely call calloc
+ */
+void *
+mh_xcalloc(size_t nmemb, size_t size)
+{
+ void *memory;
+
+ if (nmemb == 0 || size == 0)
+ adios(NULL, "Tried to calloc 0 bytes");
+
+ if ((memory = calloc(nmemb, size))) {
+ return memory;
+ } else {
+ adios(NULL, "calloc failed");
+ }
+}
+
/*
* Return the present working directory, if the current directory does not
* exist, or is too long, make / the pwd.
#### Remove existing Local-Mailbox: profile component, if any. Then
#### add one.
-grep -v 'Local-Mailbox: ' ${MH} > ${MH}.new
-mv -f ${MH}.new ${MH}
-echo "Local-Mailbox: ${myname}" >> ${MH}
+grep -v 'Local-Mailbox: ' "$MH" > "$MH".new
+mv -f "$MH".new "$MH"
+echo "Local-Mailbox: ${myname}" >> "$MH"
run_test "echo \
`run_prog ${MH_LIBEXEC_DIR}/ap -format '%(mymbox{text})' "${myname}"`" \
`run_prog ${MH_LIBEXEC_DIR}/ap -format '%(mymbox{text})' "${myname}"`" \
1 "Local-Mailbox with Alternate-Mailbox test"
+# check getmymbox, without match
+run_test 'fmttest -message -format %(getmymbox{from}) first' ''
+
+# check getmyaddr, without match
+run_test 'fmttest -message -format %(getmyaddr{from}) first' ''
+
+grep -v 'Alternate-Mailboxes: ' "$MH" > "$MH".new
+mv -f "$MH".new "$MH"
+cat >>"$MH" <<EOF
+Alternate-Mailboxes: test1@example.com
+EOF
+
+# check getmymbox, with match
+run_test 'fmttest -message -format %(getmymbox{from}) first' \
+ 'Test1 <test1@example.com>'
+
+# check getmyaddr, with match
+run_test 'fmttest -message -format %(getmyaddr{from}) first' \
+ 'test1@example.com'
+
+# check getmymbox and getmyaddr, with match of other than first address in
+# component
+cat >`mhpath new` <<'EOF'
+From: Test11 <test11@example.com>
+Cc: Test0 <test0@example.com>, Test3 <test3@example.com>,
+ Test1 <test1@example.com>, Test2 <test2@example.com>
+To: Some User <user@example.com>
+Date: Fri, 29 Sep 2006 00:00:00
+Message-Id: 11@test.nmh
+Subject: Testing message 11
+
+This is message number 11
+EOF
+run_test 'fmttest -message -format %(getmymbox{cc}) last' \
+ 'Test1 <test1@example.com>'
+run_test 'fmttest -message -format %(getmyaddr{cc}) last' \
+ 'test1@example.com'
+
exit $failed
#!/bin/sh
######################################################
#
-# Test the use of the Attach: header
+# Test the use of the Nmh-Attach: header
#
######################################################
To: Mr Test <mrtest@example.com>
cc:
Fcc: +outbox
-Attach: ${srcdir}/test/mhbuild/tiny.jpg
+Nmh-Attach: ${srcdir}/test/mhbuild/tiny.jpg
------
EOF
To: Mr Test <mrtest@example.com>
cc:
Fcc: +outbox
-Attach: ${srcdir}/test/mhbuild/tiny.jpg
-Attach: ${srcdir}/test/mhbuild/nulls
+Nmh-Attach: ${srcdir}/test/mhbuild/tiny.jpg
+Nmh-Attach: ${srcdir}/test/mhbuild/nulls
------
This is a test
EOF
To: Mr Test <mrtest@example.com>
cc:
Fcc: +outbox
-Attach:
+Nmh-Attach:
------
This is a test of an empty attach header.
EOF
#
# Two parts, one attachment; make sure mixing of multiparts at different
-# levels works
+# levels works. This also tests Attach: instead of Nmh-Attach:.
#
cat > "$draft" <<EOF
-outfile file
-rmmproc program
-normmproc
+ -[no]changecur
-[no]verbose
-version
-help
------- =_aaaaaaaaaa0
Content-Type: multipart/related;
- type="multipart/alternative";
- boundary="subpart__1.1"
+ type="text/plain"; boundary="subpart__1.1"
--subpart__1.1
Content-Type: text/plain; charset="iso-8859-1"
------- =_aaaaaaaaaa0
Content-Type: multipart/related;
- type="multipart/alternative";
- boundary="subpart__1.1"
+ type="text/plain"; boundary="subpart__1.1"
--subpart__1.1
Content-Type: text/plain; charset="iso-8859-1"
check "$expected" "$actual"
-# check that text/plain part is added to lone text/html in multipart/related
-prepare_space >"$expected" <<EOF
+# check that text/plain part is not added to text/html in multipart/related
+# when it shouldn't be
+cat >`mhpath new` <<EOF
MIME-Version: 1.0
Date: Tue, 26 Feb 2013 18:07:20 -0600
Subject: multipart/related, not /alternative
Content-Type: multipart/related;
- boundary="----=_Part_90310_101292502.1"
+ type="text/html"; boundary="----=_Part_90310_101292502.1"
------=_Part_90310_101292502.1
-Content-Type: multipart/alternative; boundary="----=_nmh-multipart1"
-
-------=_nmh-multipart1
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-This is the real content.
-
-------=_nmh-multipart1
Content-Type: text/html; charset="us-ascii"
Content-Transfer-Encoding: 7bit
</body>
</html>
-------=_nmh-multipart1--
-
------=_Part_90310_101292502.1
Content-Type: text/plain; charset="us-ascii"
Content-Transfer-Encoding: 7bit
------=_Part_90310_101292502.1--
EOF
-cat >`mhpath new` <<EOF
-MIME-Version: 1.0
-Date: Tue, 26 Feb 2013 18:07:20 -0600
-Subject: multipart/related, not /alternative
-Content-Type: multipart/related;
- boundary="----=_Part_90310_101292502.1"
-
-------=_Part_90310_101292502.1
-Content-Type: text/html; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-<html><head>
- <meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
- <title>HTML Content</title>
- </head>
- <body>
- This is the real content.
- </body>
-</html>
-------=_Part_90310_101292502.1
-Content-Type: text/plain; charset="us-ascii"
-Content-Transfer-Encoding: 7bit
-
-Your email client does not support HTML messages
-------=_Part_90310_101292502.1--
-EOF
+cp -p `mhpath last` "$expected"
if [ $can_reformat_texthtml -eq 1 ]; then
#### lynx inserts multiple blank lines, so squeeze them.
check "$expected" "$actual"
+# check removal of extraneous trailing semicolon from header parameter list
+cat >"$expected" <<EOF
+To: recipient@example.com
+Subject: test
+From: sender@example.com
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii"
+
+test
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+ name="DATE"
+Content-Description: check folded headers, with and without trailing semicolon
+Content-Disposition: attachment;
+ filename="DATE"
+
+14 April 2014
+
+------- =_aaaaaaaaaa0--
+EOF
+
+cat >`mhpath new` <<EOF
+To: recipient@example.com
+Subject: test
+From: sender@example.com
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="----- =_aaaaaaaaaa0"
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+
+test
+
+------- =_aaaaaaaaaa0
+Content-Type: text/plain; charset="us-ascii";
+ name="DATE"
+Content-Description: check folded headers, with and without trailing semicolon
+Content-Disposition: attachment;
+ filename="DATE";
+
+14 April 2014
+
+------- =_aaaaaaaaaa0--
+EOF
+run_test 'mhfixmsg last -outfile '"$actual" ''
+check "$expected" "$actual"
+
+
# check rmmproc
cat >"$MH_TEST_DIR/Mail/rmmproc" <<'EOF'
mv "$1" "$1.backup"
# check -nocheck, the default
run_test 'mhstore last -part 2 -check -nocheck' \
'storing message 11 part 2 as file 11.2.txt'
-check "$expected" 11.2.txt
+check "$expected" 11.2.txt 'keep first'
+
+# check -outfile - with -file
+run_prog mhstore -noverbose -file `mhpath 11` -part 2 -outfile - >"$actual" 2>&1
+check "$expected" "$actual"
# check reassembly of message/partial messages (RFC 1521, 7.3.2)
msgfile=`mhpath new`
switches are:
-[no]group
-[no]annotate
- -cc all|to|cc|me
- -nocc type
+ -[no]cc all|to|cc|me
-draftfolder +folder
-draftmessage msg
-nodraftfolder
#include <h/addrsbr.h>
#include <h/fmt_scan.h>
#include <h/mts.h>
+#include <h/utils.h>
#define NADDRS 100
(q = &pq)->pq_next = NULL;
while ((cp = getname (arg))) {
- if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+ if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
adios (NULL, "unable to allocate pqpair memory");
if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
p->pq_text = getcpy (cp);
seq_setprev (mp); /* set the previous-sequence */
smsgs = (struct smsg *)
- calloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs));
+ mh_xcalloc ((size_t) (MAXFOLDER + 2), sizeof(*smsgs));
if (smsgs == NULL)
adios (NULL, "unable to allocate burst storage");
char buffer[BUFSIZ];
register struct drop *cp, *dp, *ep, *pp;
- pp = (struct drop *) calloc ((size_t) (len = MAXFOLDER), sizeof(*dp));
+ pp = (struct drop *) mh_xcalloc ((size_t) (len = MAXFOLDER), sizeof(*dp));
if (pp == NULL) {
if (noisy)
admonish (NULL, "unable to allocate drop storage");
}
msgp = mp->d_id;
- dp = (struct drop *) calloc ((size_t) (msgp + 1), sizeof(*dp));
+ dp = (struct drop *) mh_xcalloc ((size_t) (msgp + 1), sizeof(*dp));
if (dp == NULL) {
close (md);
return 0;
case FT_LS_FRIENDLY:
case FT_PARSEADDR:
case FT_MYMBOX:
+ case FT_GETMYMBOX:
+ case FT_GETMYADDR:
printf(", c_name ");
litputs(fmt->f_comp->c_name);
if (fmt->f_comp->c_type)
case FT_FORMATADDR: return("FORMATADDR");
case FT_CONCATADDR: return("CONCATADDR");
case FT_MYMBOX: return("MYMBOX");
+ case FT_GETMYMBOX: return("GETMYMBOX");
+ case FT_GETMYADDR: return("GETMYADDR");
case FT_SAVESTR: return("SAVESTR");
#ifdef FT_PAUSE
case FT_PAUSE: return ("PAUSE");
for (i = 0; i < addrs->size; i++) {
(q = &pq)->pq_next = NULL;
while ((cp = getname(addrs->msgs[i]))) {
- if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+ if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
adios (NULL, "unable to allocate pqpair memory");
if ((mp = getm(cp, NULL, 0, error, sizeof(error))) == NULL) {
p->pq_text = getcpy(cp);
case FT_LS_FRIENDLY:
case FT_PARSEADDR:
case FT_MYMBOX:
+ case FT_GETMYMBOX:
+ case FT_GETMYADDR:
printf(", c_name ");
litputs(fmt->f_comp->c_name);
if (fmt->f_comp->c_type)
case FT_FORMATADDR: return("FORMATADDR");
case FT_CONCATADDR: return("CONCATADDR");
case FT_MYMBOX: return("MYMBOX");
+ case FT_GETMYMBOX: return("GETMYMBOX");
+ case FT_GETMYADDR: return("GETMYADDR");
#ifdef FT_ADDTOSEQ
case FT_ADDTOSEQ: return("ADDTOSEQ");
#endif
{
struct mailname *mp, *mp2;
- for (mp = mq.m_next; mp; mp = mp2->m_next) {
- mp2 = mp;
+ for (mp = mq.m_next; mp; mp = mp2) {
+ mp2 = mp->m_next;
mnfree(mp);
}
}
*/
if ((out = fopen (defpath, "w")) == NULL)
adios (defpath, "unable to write");
+ /*
+ * The main purpose of this first line is to fool file(1).
+ * Without it, if the first line of the profile is Path:,
+ * file 5.19 reports its type as message/news. With it,
+ * it reports the type as text/plain.
+ */
+ fprintf (out, "MH-Profile-Version: 1.0\n");
for (np = m_defs; np; np = np->n_next) {
if (!np->n_context)
fprintf (out, "%s: %s\n", np->n_name, np->n_field);
/*
* Allocate space for primary (outside) content
*/
- if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+ if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
adios (NULL, "out of memory");
/*
* header; in that case, add it to our attach list
*/
- if (strcasecmp(ATTACH_FIELD, np) == 0) {
+ if (strcasecmp(ATTACH_FIELD, np) == 0 ||
+ strcasecmp(ATTACH_FIELD_ALT, np) == 0) {
struct attach_list *entry;
char *s = vp, *e = vp + strlen(vp) - 1;
free(np);
ct->c_type = CT_MULTIPART;
ct->c_subtype = MULTI_MIXED;
- if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+ if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) m;
pp = &m->mp_parts;
if (!p)
continue;
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*pp = part;
pp = &part->mp_next;
adios("reading", "Unable to open %s for", at_entry->filename);
}
- if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+ if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
adios(NULL, "out of memory");
init_decoded_content(p, infile);
setup_attach_content(p, at_entry->filename);
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*pp = part;
pp = &part->mp_next;
struct part *part;
struct text *t;
- if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+ if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
adios(NULL, "out of memory");
init_decoded_content(p, infile);
p->c_begin = ftell(in);
p->c_end = ftell(in);
- if ((t = (struct text *) calloc (1, sizeof (*t))) == NULL)
+ if ((t = (struct text *) mh_xcalloc (1, sizeof (*t))) == NULL)
adios (NULL, "out of memory");
t->tx_charset = CHARSET_SPECIFIED;
p->c_ctparams = t;
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*pp = part;
part->mp_part = p;
}
/* allocate basic Content structure */
- if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+ if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
adios (NULL, "out of memory");
*ctp = ct;
* reference, we need to create another Content structure
* for the message/external-body to wrap it in.
*/
- if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
+ if ((ct = (CT) mh_xcalloc (1, sizeof(*ct))) == NULL)
adios (NULL, "out of memory");
init_decoded_content(ct, infilename);
*ctp = ct;
ct->c_type = CT_MESSAGE;
ct->c_subtype = MESSAGE_EXTERNAL;
- if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+ if ((e = (struct exbody *) mh_xcalloc (1, sizeof(*e))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) e;
ct->c_type = CT_MULTIPART;
ct->c_subtype = MULTI_DIGEST;
- if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+ if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) m;
pp = &m->mp_parts;
CT p;
CE pe;
- if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+ if ((p = (CT) mh_xcalloc (1, sizeof(*p))) == NULL)
adios (NULL, "out of memory");
init_decoded_content (p, infilename);
pe = &p->c_cefile;
if (listsw && stat (pe->ce_file, &st) != NOTOK)
p->c_end = (long) st.st_size;
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*pp = part;
pp = &part->mp_next;
ct->c_type = CT_MULTIPART;
ct->c_subtype = vrsn;
- if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+ if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) m;
if (!p)
continue;
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*pp = part;
pp = &part->mp_next;
X("outfile file", 0, OUTFILESW) \
X("rmmproc program", 0, RPROCSW) \
X("normmproc", 0, NRPRCSW) \
+ X("changecur", 0, CHGSW) \
+ X("nochangecur", 0, NCHGSW) \
X("verbose", 0, VERBSW) \
X("noverbose", 0, NVERBSW) \
X("version", 0, VERSIONSW) \
extern int skip_mp_cte_check; /* flag to InitMultiPart */
extern int suppress_bogus_mp_content_warning; /* flag to InitMultiPart */
extern int bogus_mp_content; /* flag from InitMultiPart */
+/* flags to/from parse_header_attrs */
+extern int suppress_extraneous_trailing_semicolon_warning;
+extern int extraneous_trailing_semicolon;
/* mhoutsbr.c */
int output_message (CT, char *);
static int fix_multipart_cte (CT, int *);
static int set_ce (CT, int);
static int ensure_text_plain (CT *, CT, int *, int);
+static int find_textplain_sibling (CT, int, int *);
+static int insert_new_text_plain_part (CT, int, CT);
static CT build_text_plain_part (CT);
static CT divide_part (CT);
static void copy_ctinfo (CI, CI);
static int content_encoding (CT, const char **);
static int strip_crs (CT, int *);
static int convert_charsets (CT, char *, int *);
+static int fix_always (CT, int *);
static int write_content (CT, char *, char *, int, int);
static int remove_file (char *);
static void report (char *, char *, char *, char *, ...);
CT *ctp;
FILE *fp;
int using_stdin = 0;
+ int chgflag = 1;
int status = OK;
fix_transformations fx;
fx.reformat = fx.fixcte = fx.fixboundary = 1;
case NRPRCSW:
rmmproc = NULL;
continue;
+ case CHGSW:
+ chgflag = 1;
+ continue;
+ case NCHGSW:
+ chgflag = 0;
+ continue;
case VERBSW:
verbosw = 1;
continue;
}
suppress_bogus_mp_content_warning = skip_mp_cte_check = 1;
+ suppress_extraneous_trailing_semicolon_warning = 1;
if (! context_find ("path"))
free (path ("./", TFOLDER));
}
}
- if (! (cts = (CT *) calloc ((size_t) 2, sizeof *cts))) {
+ if (! (cts = (CT *) mh_xcalloc ((size_t) 2, sizeof *cts))) {
adios (NULL, "out of memory");
}
ctp = cts;
done (1);
seq_setprev (mp); /* set the previous-sequence */
- if (! (cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof *cts))) {
+ if (! (cts =
+ (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof *cts))) {
adios (NULL, "out of memory");
}
ctp = cts;
}
}
- seq_setcur (mp, mp->hghsel); /* update current message */
+ if (chgflag) {
+ seq_setcur (mp, mp->hghsel); /* update current message */
+ }
seq_save (mp); /* synchronize sequences */
context_replace (pfolder, folder);/* update current folder */
context_save (); /* save the context file */
}
reverse_alternative_parts (*ctp);
+ status = fix_always (*ctp, &message_mods);
if (status == OK && fx->fixboundary) {
status = fix_boundary (ctp, &message_mods);
}
status = convert_charsets (*ctp, fx->textcharset, &message_mods);
}
- if (! (*ctp)->c_umask) {
+ if (status == OK && ! (*ctp)->c_umask) {
/* Set the umask for the contents file. This currently
isn't used but just in case it is in the future. */
struct stat st;
struct multipart *mp;
int status = OK;
- if (bogus_mp_content) {
+ if (ct && (*ct)->c_type == CT_MULTIPART && bogus_mp_content) {
mp = (struct multipart *) (*ct)->c_ctparams;
/*
"fix multipart boundary");
}
} else {
+ *ct = NULL;
advise (NULL, "unable to parse fixed part");
status = NOTOK;
}
switch ((*ct)->c_type) {
case CT_TEXT: {
- int has_text_plain = 0;
-
/* Nothing to do for text/plain. */
if ((*ct)->c_subtype == TEXT_PLAIN) { return OK; }
if (parent && parent->c_type == CT_MULTIPART &&
parent->c_subtype == MULTI_ALTERNATE) {
- struct multipart *mp = (struct multipart *) parent->c_ctparams;
- struct part *part, *prev;
int new_subpart_number = 1;
+ int has_text_plain =
+ find_textplain_sibling (parent, replacetextplain,
+ &new_subpart_number);
- /* See if there is a sibling text/plain. */
- for (prev = part = mp->mp_parts; part; part = part->mp_next) {
- ++new_subpart_number;
- if (part->mp_part->c_type == CT_TEXT &&
- part->mp_part->c_subtype == TEXT_PLAIN) {
- if (replacetextplain) {
- struct part *old_part;
- if (part == mp->mp_parts) {
- old_part = mp->mp_parts;
- mp->mp_parts = part->mp_next;
- } else {
- old_part = prev->mp_next;
- prev->mp_next = part->mp_next;
- }
- if (verbosw) {
- report (NULL, parent->c_partno, parent->c_file,
- "remove text/plain part %s",
- old_part->mp_part->c_partno);
- }
- free_content (old_part->mp_part);
- free (old_part);
- } else {
- has_text_plain = 1;
+ if (! has_text_plain) {
+ /* Parent is a multipart/alternative. Insert a new
+ text/plain subpart. */
+ const int inserted =
+ insert_new_text_plain_part (*ct, new_subpart_number,
+ parent);
+ if (inserted) {
+ ++*message_mods;
+ if (verbosw) {
+ report (NULL, parent->c_partno, parent->c_file,
+ "insert text/plain part");
}
- break;
+ } else {
+ status = NOTOK;
}
- prev = part;
+ }
+ } else if (parent && parent->c_type == CT_MULTIPART &&
+ parent->c_subtype == MULTI_RELATED) {
+ char *type_subtype =
+ concat ((*ct)->c_ctinfo.ci_type, "/",
+ (*ct)->c_ctinfo.ci_subtype, NULL);
+ const char *parent_type =
+ get_param (parent->c_ctinfo.ci_first_pm, "type", '?', 1);
+ int new_subpart_number = 1;
+ int has_text_plain = 0;
+
+ /* Have to do string comparison on the subtype because we
+ don't enumerate all of them in c_subtype values.
+ parent_type will be NULL if the multipart/related part
+ doesn't have a type parameter. The type parameter must
+ be specified according to RFC 2387 Sec. 3.1 but not all
+ messages comply. */
+ if (parent_type && strcasecmp (type_subtype, parent_type) == 0) {
+ /* The type of this part matches the root type of the
+ parent multipart/related. Look to see if there's
+ text/plain sibling. */
+ has_text_plain =
+ find_textplain_sibling (parent, replacetextplain,
+ &new_subpart_number);
}
+ free (type_subtype);
+
if (! has_text_plain) {
/* Parent is a multipart/alternative. Insert a new
text/plain subpart. */
- struct part *new_part = mh_xmalloc (sizeof *new_part);
-
- if ((new_part->mp_part = build_text_plain_part (*ct))) {
- char buffer[16];
- snprintf (buffer, sizeof buffer, "%d", new_subpart_number);
-
- new_part->mp_next = mp->mp_parts;
- mp->mp_parts = new_part;
- new_part->mp_part->c_partno =
- concat (parent->c_partno ? parent->c_partno : "1", ".",
- buffer, NULL);
-
+ const int inserted =
+ insert_new_text_plain_part (*ct, new_subpart_number,
+ parent);
+ if (inserted) {
++*message_mods;
if (verbosw) {
report (NULL, parent->c_partno, parent->c_file,
"insert text/plain part");
}
} else {
- free_content (new_part->mp_part);
- free (new_part);
status = NOTOK;
}
}
case CT_MESSAGE:
if ((*ct)->c_subtype == MESSAGE_EXTERNAL) {
- struct exbody *e;
+ struct exbody *e = (struct exbody *) (*ct)->c_ctparams;
- e = (struct exbody *) (*ct)->c_ctparams;
status = ensure_text_plain (&e->eb_content, *ct, message_mods,
replacetextplain);
}
}
+/* See if there is a sibling text/plain. */
+static int
+find_textplain_sibling (CT parent, int replacetextplain,
+ int *new_subpart_number) {
+ struct multipart *mp = (struct multipart *) parent->c_ctparams;
+ struct part *part, *prev;
+ int has_text_plain = 0;
+
+ for (prev = part = mp->mp_parts; part; part = part->mp_next) {
+ ++*new_subpart_number;
+ if (part->mp_part->c_type == CT_TEXT &&
+ part->mp_part->c_subtype == TEXT_PLAIN) {
+ if (replacetextplain) {
+ struct part *old_part;
+ if (part == mp->mp_parts) {
+ old_part = mp->mp_parts;
+ mp->mp_parts = part->mp_next;
+ } else {
+ old_part = prev->mp_next;
+ prev->mp_next = part->mp_next;
+ }
+ if (verbosw) {
+ report (NULL, parent->c_partno, parent->c_file,
+ "remove text/plain part %s",
+ old_part->mp_part->c_partno);
+ }
+ free_content (old_part->mp_part);
+ free (old_part);
+ } else {
+ has_text_plain = 1;
+ }
+ break;
+ }
+ prev = part;
+ }
+
+ return has_text_plain;
+}
+
+
+static int
+insert_new_text_plain_part (CT ct, int new_subpart_number, CT parent) {
+ struct multipart *mp = (struct multipart *) parent->c_ctparams;
+ struct part *new_part = mh_xmalloc (sizeof *new_part);
+
+ if ((new_part->mp_part = build_text_plain_part (ct))) {
+ char buffer[16];
+ snprintf (buffer, sizeof buffer, "%d", new_subpart_number);
+
+ new_part->mp_next = mp->mp_parts;
+ mp->mp_parts = new_part;
+ new_part->mp_part->c_partno =
+ concat (parent->c_partno ? parent->c_partno : "1", ".",
+ buffer, NULL);
+
+ return 1;
+ } else {
+ free_content (new_part->mp_part);
+ free (new_part);
+
+ return 0;
+ }
+}
+
+
static CT
build_text_plain_part (CT encoded_part) {
CT tp_part = divide_part (encoded_part);
}
free_content (tp_part);
- (void) m_unlink (tmp_plain_file);
+ if (tmp_plain_file) { (void) m_unlink (tmp_plain_file); }
free (tmp_plain_file);
return NULL;
divide_part (CT ct) {
CT new_part;
- if ((new_part = (CT) calloc (1, sizeof *new_part)) == NULL)
+ if ((new_part = (CT) mh_xcalloc (1, sizeof *new_part)) == NULL)
adios (NULL, "out of memory");
/* Just copy over what is needed for decoding. c_vrsn and
/* Identifies 7bit or 8bit content based on charset. */
static int
charset_encoding (CT ct) {
- int encoding =
- strcasecmp (content_charset (ct), "US-ASCII") ? CE_8BIT : CE_7BIT;
+ char *ct_charset = content_charset (ct);
+ int encoding = strcasecmp (ct_charset, "US-ASCII") ? CE_8BIT : CE_7BIT;
+
+ free (ct_charset);
return encoding;
}
struct multipart *m;
const struct str2init *ctinit;
- if ((ct = (CT) calloc (1, sizeof *ct)) == NULL)
+ if ((ct = (CT) mh_xcalloc (1, sizeof *ct)) == NULL)
adios (NULL, "out of memory");
/* Set up the multipart/alternative part. These fields of *ct were
- initialized to 0 by calloc():
+ initialized to 0 by mh_xcalloc():
c_fp, c_unlink, c_begin, c_end,
c_vrsn, c_ctline, c_celine,
c_id, c_descr, c_dispo, c_partno,
}
add_param(&ct->c_ctinfo.ci_first_pm, &ct->c_ctinfo.ci_last_pm,
- "boundary", boundary, 0);
+ "boundary", boundary, 0);
p = (struct part *) mh_xmalloc (sizeof *p);
p->mp_next = (struct part *) mh_xmalloc (sizeof *p->mp_next);
p->mp_next->mp_next = NULL;
p->mp_next->mp_part = first_alt;
- if ((m = (struct multipart *) calloc (1, sizeof (struct multipart))) ==
+ if ((m = (struct multipart *) mh_xcalloc (1, sizeof (struct multipart))) ==
NULL)
adios (NULL, "out of memory");
m->mp_start = concat (boundary, "\n", NULL);
m->mp_stop = concat (boundary, "--\n", NULL);
m->mp_parts = p;
- ct->c_ctparams = (void *) m;
+ ct->c_ctparams = m;
free (boundary);
case CT_MESSAGE:
if (ct->c_subtype == MESSAGE_EXTERNAL) {
- struct exbody *e;
+ struct exbody *e = (struct exbody *) ct->c_ctparams;
- e = (struct exbody *) ct->c_ctparams;
status = decode_text_parts (e->eb_content, encoding, message_mods);
}
break;
}
}
+ free (charset);
+
return status;
}
status = convert_charset (ct, dest_charset, message_mods);
if (status == OK) {
if (verbosw) {
+ char *ct_charset = content_charset (ct);
+
report (NULL, ct->c_partno, ct->c_file,
- "convert %s to %s",
- content_charset(ct), dest_charset);
+ "convert %s to %s", ct_charset, dest_charset);
+ free (ct_charset);
}
} else {
+ char *ct_charset = content_charset (ct);
+
report ("iconv", ct->c_partno, ct->c_file,
- "failed to convert %s to %s",
- content_charset(ct), dest_charset);
+ "failed to convert %s to %s", ct_charset, dest_charset);
+ free (ct_charset);
}
}
break;
case CT_MESSAGE:
if (ct->c_subtype == MESSAGE_EXTERNAL) {
- struct exbody *e;
+ struct exbody *e = (struct exbody *) ct->c_ctparams;
- e = (struct exbody *) ct->c_ctparams;
status =
convert_charsets (e->eb_content, dest_charset, message_mods);
}
}
+/*
+ * Fix various problems that aren't handled elsewhere. These
+ * are fixed unconditionally: there are no switches to disable
+ * them. (Currently, "problems" is just one: an extraneous
+ * semicolon at the end of a header parameter list.)
+ */
+static int
+fix_always (CT ct, int *message_mods) {
+ int status = OK;
+
+ switch (ct->c_type) {
+ case CT_MULTIPART: {
+ struct multipart *m = (struct multipart *) ct->c_ctparams;
+ struct part *part;
+
+ for (part = m->mp_parts; status == OK && part; part = part->mp_next) {
+ status = fix_always (part->mp_part, message_mods);
+ }
+ break;
+ }
+
+ case CT_MESSAGE:
+ if (ct->c_subtype == MESSAGE_EXTERNAL) {
+ struct exbody *e = (struct exbody *) ct->c_ctparams;
+
+ status = fix_always (e->eb_content, message_mods);
+ }
+ break;
+
+ default: {
+ HF hf;
+
+ for (hf = ct->c_first_hf; hf; hf = hf->next) {
+ size_t len = strlen (hf->value);
+
+ /* whitespace following a trailing ';' will be nuked as well */
+ if (hf->value[len - 1] == '\n')
+ while (isspace((unsigned char)(hf->value[len - 2]))) len--;
+
+ if (hf->value[len - 2] == ';') {
+ /* Remove trailing ';' from parameter value. */
+ hf->value[len - 2] = '\n';
+ hf->value[len - 1] = '\0';
+
+ /* Also, if Content-Type parameter, remove trailing ';'
+ from ct->c_ctline. This probably isn't necessary
+ but can't hurt. */
+ if (strcasecmp(hf->name, "Content-Type") == 0 && ct->c_ctline) {
+ size_t l = strlen(ct->c_ctline) - 1;
+ while (isspace((unsigned char)(ct->c_ctline[l])) || ct->c_ctline[l] == ';')
+ ct->c_ctline[l--] = '\0';
+ }
+
+ ++*message_mods;
+ if (verbosw) {
+ report (NULL, ct->c_partno, ct->c_file,
+ "remove trailing ; from %s parameter value",
+ hf->name);
+ }
+ }
+ }
+ }}
+
+ return status;
+}
+
+
static int
write_content (CT ct, char *input_filename, char *outfile, int modify_inplace,
int message_mods) {
{
CT *ctp;
- if ((ctp = cts))
- for (; *ctp; ctp++)
- free_content (*ctp);
+ for (ctp = cts; ctp && *ctp; ctp++)
+ free_content (*ctp);
+
+ free (cts);
exit (status);
}
* check if message is coming from file
*/
if (file) {
- if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
done (1);
seq_setprev (mp); /* set the previous-sequence */
- if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
return 1;
}
- args = (struct arglist *) calloc((size_t) 1, sizeof(struct arglist));
+ args = (struct arglist *) mh_xcalloc ((size_t) 1, sizeof(struct arglist));
if (arglist_tail)
arglist_tail->a_next = args;
(q = &pq)->pq_next = NULL;
while ((cp = getname (ap))) {
- if ((p = (struct pqpair *) calloc ((size_t) 1, sizeof(*p))) == NULL)
+ if ((p = (struct pqpair *) mh_xcalloc ((size_t) 1, sizeof(*p))) == NULL)
adios (NULL, "unable to allocate pqpair memory");
else {
if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
{
struct mcomp *c1;
- if ((c1 = (struct mcomp *) calloc ((size_t) 1, sizeof(*c1))) == NULL)
+ if ((c1 = (struct mcomp *) mh_xcalloc ((size_t) 1, sizeof(*c1))) == NULL)
adios (NULL, "unable to allocate comp memory");
else {
c1->c_flags = flags & ~INIT;
int type_ok (CT, int);
void flush_errors (void);
-/* mhstoresbr.c */
-typedef struct mhstoreinfo *mhstoreinfo_t;
-mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
-void mhstoreinfo_free(mhstoreinfo_t);
-void store_all_messages (mhstoreinfo_t);
-
/* mhcachesbr.c */
void cache_all_messages (CT *);
* check if message is coming from file
*/
if (file) {
- if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
done (1);
seq_setprev (mp); /* set the previous-sequence */
- if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
putc ('\n', out);
(*ct->c_ceclosefnx) (ct);
+ free (bufp);
return OK;
}
* 1) Instruct parser not to detect invalid Content-Transfer-Encoding
* in a multipart.
* 2) Suppress the warning about bogus multipart content, and report it.
+ * 3) Suppress the warning about extraneous trailing ';' in header parameter
+ * lists, and report it.
*/
int skip_mp_cte_check;
int suppress_bogus_mp_content_warning;
int bogus_mp_content;
+int suppress_extraneous_trailing_semicolon_warning;
+int extraneous_trailing_semicolon;
/*
* Structures for TEXT messages
{ "alternative", MULTI_ALTERNATE },
{ "digest", MULTI_DIGEST },
{ "parallel", MULTI_PARALLEL },
+ { "related", MULTI_RELATED },
{ NULL, MULTI_UNKNOWN } /* this one must be last! */
};
m_getfld_state_t gstate = 0;
/* allocate the content structure */
- if (!(ct = (CT) calloc (1, sizeof(*ct))))
+ if (!(ct = (CT) mh_xcalloc (1, sizeof(*ct))))
adios (NULL, "out of memory");
ct->c_fp = in;
ct->c_subtype = kv->kv_value;
/* allocate text character set structure */
- if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
+ if ((t = (struct text *) mh_xcalloc (1, sizeof(*t))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) t;
}
/* allocate primary structure for multipart info */
- if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
+ if ((m = (struct multipart *) mh_xcalloc (1, sizeof(*m))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) m;
if (strcmp (bufp + 2, m->mp_start))
continue;
next_part:
- if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+ if ((part = (struct part *) mh_xcalloc (1, sizeof(*part))) == NULL)
adios (NULL, "out of memory");
*next = part;
next = &part->mp_next;
if (!(p = get_content (fp, ct->c_file,
ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
free(bufp);
- fclose (ct->c_fp);
ct->c_fp = NULL;
return NOTOK;
}
PM pm;
struct partial *p;
- if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
+ if ((p = (struct partial *) mh_xcalloc (1, sizeof(*p))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) p;
CT p;
FILE *fp;
- if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
+ if ((e = (struct exbody *) mh_xcalloc (1, sizeof(*e))) == NULL)
adios (NULL, "out of memory");
ct->c_ctparams = (void *) e;
fclose (ct->c_fp);
ct->c_fp = NULL;
}
+ free (bufp);
return fileno (ce->ce_fp);
clean_up:
fclose (ct->c_fp);
ct->c_fp = NULL;
}
+ free (bufp);
return NOTOK;
}
return "digest";
case MULTI_PARALLEL:
return "parallel";
+ case MULTI_RELATED:
+ return "related";
default:
return "unknown_multipart_subtype";
}
}
if (*cp == 0) {
- advise (NULL,
- "extraneous trailing ';' in message %s's %s: "
- "parameter list",
- filename, fieldname);
+ if (! suppress_extraneous_trailing_semicolon_warning) {
+ advise (NULL,
+ "extraneous trailing ';' in message %s's %s: "
+ "parameter list",
+ filename, fieldname);
+ }
+ extraneous_trailing_semicolon = 1;
return DONE;
}
"%s's %s: field\n%*s(parameter %s)", sp->index,
filename, fieldname, strlen(invo_name) + 2, "",
nameptr);
+ free (nameptr);
return NOTOK;
}
if (sp2->index < sp->index &&
"param in message %s's %s: field\n%*s(parameter %s)",
filename, fieldname, strlen(invo_name) + 2, "",
nameptr);
+ free (nameptr);
return NOTOK;
}
}
}
/*
- * Return the charset for a particular content type. Return pointer is
- * only valid until the next call to content_charset().
+ * Return the charset for a particular content type.
*/
char *
content_charset (CT ct) {
- static char *ret_charset = NULL;
-
- if (ret_charset != NULL) {
- free(ret_charset);
- }
+ char *ret_charset = NULL;
ret_charset = get_param(ct->c_ctinfo.ci_first_pm, "charset", '?', 0);
- return ret_charset ? ret_charset : "US-ASCII";
+ return ret_charset ? ret_charset : getcpy ("US-ASCII");
}
* check if message is coming from file
*/
if (file) {
- if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
seq_setprev (mp); /* set the Previous-Sequence */
seq_setunseen (mp, 1); /* unset the Unseen-Sequence */
- if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
return NOTOK;
} else {
+ char *charset = content_charset (ct);
admonish (NULL, "unable to convert character set%s%s to %s",
ct->c_partno ? " of part " : "",
ct->c_partno ? ct->c_partno : "",
- content_charset (ct));
+ charset);
+ free (charset);
}
}
int alternating, nowalternate, result;
struct multipart *m = (struct multipart *) ct->c_ctparams;
struct part *part;
- int any_part_ok;
+ int request_matched;
CT p;
alternating = 0;
alternating = 1;
}
-/*
- * alternate -> we are a part inside an multipart/alternative
- * alternating -> we are a multipart/alternative
- */
+ /*
+ * alternate -> we are a part inside an multipart/alternative
+ * alternating -> we are a multipart/alternative
+ */
result = alternate ? NOTOK : OK;
- any_part_ok = 0;
+ request_matched = 0;
for (part = m->mp_parts; part; part = part->mp_next) {
p = part->mp_part;
if (part_ok (p, 1) && type_ok (p, 1)) {
int inneresult;
- any_part_ok = 1;
+ request_matched = 1;
inneresult = show_switch (p, nowalternate, concatsw, textonly,
inlineonly, fmt);
}
}
- if (alternating && !part && any_part_ok) {
+ if (alternating && !part && request_matched) {
if (!alternate)
content_error (NULL, ct, "don't know how to display any of the contents");
result = NOTOK;
}
out:
- return result;
+ /* if no parts matched what was requested, there can't have been
+ * any display errors, so we report OK. */
+ return request_matched ? result : OK;
}
if ((conv_desc = iconv_open (dest_charset, src_charset)) ==
(iconv_t) -1) {
advise (NULL, "Can't convert %s to %s", src_charset, dest_charset);
+ free (src_charset);
return NOTOK;
}
#endif /* ! HAVE_ICONV */
}
+ free (src_charset);
return status;
}
} else {
status = NOTOK;
}
-
- free (charset);
}
+ free (charset);
#else /* ! HAVE_ICONV */
NMH_UNUSED (ct);
NMH_UNUSED (file);
extern char *cache_public;
extern char *cache_private;
-/* mhstoresbr.c */
-typedef struct mhstoreinfo *mhstoreinfo_t;
-mhstoreinfo_t mhstoreinfo_create(CT *, char *, const char *, int, int);
-int mhstoreinfo_files_not_clobbered(const mhstoreinfo_t);
-void mhstoreinfo_free(mhstoreinfo_t);
-
/* mhmisc.c */
extern int npart;
extern int ntype;
int type_ok (CT, int);
void flush_errors (void);
-/* mhstoresbr.c */
-void store_all_messages (mhstoreinfo_t);
-
/* mhfree.c */
extern CT *cts;
void freects_done (int) NORETURN;
* check if message is coming from file
*/
if (file) {
- if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) 2, sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
if ((ct = parse_mime (file))) {
*ctp++ = ct;
if (outfile) {
- ct->c_storage = outfile;
+ ct->c_storage = add (outfile, NULL);
}
}
} else {
done (1);
seq_setprev (mp); /* set the previous-sequence */
- if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
+ if (!(cts = (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
adios (NULL, "out of memory");
ctp = cts;
enum clobber_policy_t clobber_policy; /* -clobber selection */
};
-typedef struct mhstoreinfo *mhstoreinfo_t;
-
mhstoreinfo_t
mhstoreinfo_create (CT *ct, char *pwd, const char *csw, int asw, int vsw) {
mhstoreinfo_t info = mh_xmalloc (sizeof *info);
void
mhstoreinfo_free (mhstoreinfo_t info) {
free (info->cwd);
+ free (info->dir);
free (info);
}
int type_ok (CT, int);
void flush_errors (void);
-/*
- * prototypes
- */
-void store_all_messages (mhstoreinfo_t);
-
/*
* static prototypes
*/
/* Support mhstore -outfile. The MIME parser doesn't
load c_storage, so we know that p->c_storage is
NULL here. */
- p->c_storage = ct->c_storage;
+ p->c_storage = add (ct->c_storage, NULL);
}
result = store_switch (p, info);
- p->c_storage = NULL;
if (result == OK && ct->c_subtype == MULTI_ALTERNATE)
break;
return NOTOK;
}
- if ((base = (CT *) calloc ((size_t) (i + 1), sizeof(*base))) == NULL)
+ if ((base = (CT *) mh_xcalloc ((size_t) (i + 1), sizeof(*base))) == NULL)
adios (NULL, "out of memory");
ctq = base;
if (ct->c_storage) {
/* Support mhstore -outfile. The MIME parser doesn't load
c_storage, so we know that p->c_storage is NULL here. */
- p->c_storage = ct->c_storage;
+ p->c_storage = add (ct->c_storage, NULL);
}
result = store_switch (p, info);
- p->c_storage = NULL;
p->c_partno = NULL;
return result;
}
} else {
/* The output filename was explicitly specified, so use it. */
- if ((ct->c_storage = clobber_check (add (ct->c_storage, NULL), info)) ==
+ if ((ct->c_storage = clobber_check (ct->c_storage, info)) ==
NULL) {
return NOTOK;
}
char *cwd = NULL;
int check_again;
+ if (! strcmp (original_file, "-")) {
+ return original_file;
+ }
+
if (info->clobber_policy == NMH_CLOBBER_ASK) {
/* Save cwd for possible use in loop below. */
char *slash;
if (slash) {
*slash = '\0';
} else {
- /* original_file wasn't a full path, which shouldn't happen. */
+ /* original_file isn't a full path, which should only happen if
+ it is -. */
+ free (cwd);
cwd = NULL;
}
}
original_file = file;
} while (check_again);
- if (cwd) {
- free (cwd);
- }
+ free (cwd);
return file;
}
{
register struct nexus *p;
- if ((p = (struct nexus *) calloc ((size_t) 1, sizeof *p)) == NULL)
+ if ((p = (struct nexus *) mh_xcalloc ((size_t) 1, sizeof *p)) == NULL)
adios (NULL, "unable to allocate component storage");
p->n_action = action;
X("annotate", 0, ANNOSW) \
X("noannotate", 0, NANNOSW) \
X("cc all|to|cc|me", 0, CCSW) \
- X("nocc type", 0, NCCSW) \
+ X("nocc all|to|cc|me", 0, NCCSW) \
X("draftfolder +folder", 0, DFOLDSW) \
X("draftmessage msg", 0, DMSGSW) \
X("nodraftfolder", 0, NDFLDSW) \
* and it's our responsibility to free it.
*/
- nxtbuf = compbuffers = (char **) calloc((size_t) ncomps, sizeof(char *));
+ nxtbuf = compbuffers = (char **) mh_xcalloc ((size_t) ncomps, sizeof(char *));
if (nxtbuf == NULL)
adios (NULL, "unable to allocate component buffers");
- used_buf = (struct comp **) calloc((size_t) (ncomps+1),
+ used_buf = (struct comp **) mh_xcalloc ((size_t) (ncomps+1),
sizeof(struct comp *));
if (used_buf == NULL)
adios (NULL, "unable to allocate component buffer stack");
if (! datecomp->c_text) {
if (datecomp->c_tws == NULL)
datecomp->c_tws = (struct tws *)
- calloc((size_t) 1, sizeof(*datecomp->c_tws));
+ mh_xcalloc ((size_t) 1, sizeof(*datecomp->c_tws));
if (datecomp->c_tws == NULL)
adios (NULL, "unable to allocate tws buffer");
*datecomp->c_tws = *dlocaltime ((time_t *) &st.st_mtime);
* the collection of messages with the same subj
* given a message number.
*/
- il = (struct smsg ***) calloc (mp->hghsel+1, sizeof(*il));
+ il = (struct smsg ***) mh_xcalloc (mp->hghsel+1, sizeof(*il));
if (! il)
adios (NULL, "couldn't allocate msg list");
for (i = 0; i < nmsgs; i++)
twscopy (&tb, dlocaltimenow ());
smsgs = (struct smsg *)
- calloc ((size_t) (mp->hghsel - mp->lowsel + 2),
+ mh_xcalloc ((size_t) (mp->hghsel - mp->lowsel + 2),
sizeof(*smsgs));
if (smsgs == NULL)
adios (NULL, "unable to allocate sort storage");