From 430ffdd38f6ea76f20d8559a3e2a6f7835117d47 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Thu, 9 Jan 2014 16:03:21 -0500 Subject: [PATCH 01/16] Preliminary Attach: header support. Untested just yet. --- Makefile.am | 2 +- h/mime.h | 1 + uip/mhbuildsbr.c | 165 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 165 insertions(+), 3 deletions(-) diff --git a/Makefile.am b/Makefile.am index 83118f63..6dc80179 100644 --- a/Makefile.am +++ b/Makefile.am @@ -316,7 +316,7 @@ uip_mark_LDADD = $(LDADD) $(POSTLINK) uip_mhbuild_SOURCES = uip/mhbuild.c uip/mhbuildsbr.c uip/mhcachesbr.c \ uip/mhlistsbr.c uip/mhoutsbr.c uip/mhmisc.c \ - uip/mhfree.c uip/mhparse.c uip/md5.c + uip/mhfree.c uip/mhparse.c uip/md5.c uip/attach.c uip_mhbuild_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK) uip_mhfixmsg_SOURCES = uip/mhfixmsg.c uip/mhparse.c uip/mhcachesbr.c \ diff --git a/h/mime.h b/h/mime.h index 2f8a6a32..2ed5378c 100644 --- a/h/mime.h +++ b/h/mime.h @@ -12,6 +12,7 @@ #define DESCR_FIELD "Content-Description" #define DISPO_FIELD "Content-Disposition" #define MD5_FIELD "Content-MD5" +#define ATTACH_FIELD "Attach" #define isatom(c) (isascii((unsigned char) c) \ && !isspace ((unsigned char) c) \ diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index e4499170..58540f1b 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -52,6 +52,11 @@ pid_t xpid = 0; static char prefix[] = "----- =_aaaaaaaaaa"; +struct attach_list { + char *filename; + struct attach_list *next; +}; + /* * Maximum size of URL token in message/external-body */ @@ -73,6 +78,7 @@ void free_encoding (CT, int); * static prototypes */ static int init_decoded_content (CT); +static void setup_attach_content(CT, const char *); static char *fgetstr (char *, int, FILE *); static int user_content (FILE *, char *, char *, CT *); static void set_id (CT, int); @@ -134,6 +140,7 @@ build_mime (char *infile, int directives, int header_encoding) FILE *in; HF hp; m_getfld_state_t gstate = 0; + struct attach_list *attach_head = NULL, *attach_tail = NULL, *at_entry; directive_init(directives); @@ -197,8 +204,25 @@ build_mime (char *infile, int directives, int header_encoding) vp = add (buf, vp); /* add to previous value */ } - /* Now add the header data to the list */ - add_header (ct, np, vp); + /* + * Now add the header data to the list, unless it's an attach + * header; in that case, add it to our attach list + */ + + if (strcasecmp(ATTACH_FIELD, np) == 0) { + struct attach_list *entry; + free(np); + entry = mh_xmalloc(sizeof(*entry)); + entry->filename = vp; + if (! attach_tail) { + attach_tail->next = entry; + attach_tail = entry; + } else { + attach_head = attach_tail = entry; + } + } else { + add_header (ct, np, vp); + } finish_field: /* if this wasn't the last header field, then continue */ @@ -286,6 +310,44 @@ finish_field: */ fclose (in); + /* + * Add any Attach headers to the list of MIME parts at the end of the + * message. + */ + + for (at_entry = attach_head; at_entry; ) { + struct attach_list *at_prev = at_entry; + struct part *part; + CT p; + + if (! access(at_entry->filename, R_OK)) { + adios("reading", "Unable to open %s for", at_entry->filename); + } + + if ((p = (CT) calloc (1, sizeof(*p))) == NULL) + adios(NULL, "out of memory"); + + init_decoded_content(p); + + /* + * Initialize our content structure based on the filename, + * and fill in all of the relevant fields. Also place MIME + * parameters in the attributes array. + */ + + setup_attach_content(p, at_entry->filename); + + if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) + adios (NULL, "out of memory"); + *pp = part; + pp = &part->mp_next; + part->mp_part = p; + + at_entry = at_entry->next; + free(at_prev->filename); + free(at_prev); + } + /* check if any contents were found */ if (!m->mp_parts) adios (NULL, "no content directives found"); @@ -1709,3 +1771,102 @@ calculate_digest (CT ct, int asciiP) vp = concat (" ", outbuf, "\n", NULL); return vp; } + +/* + * Set things up for the content structure for file "filename" that + * we want to attach + */ + +static void +setup_attach_content(CT ct, const char *filename) +{ + char *type, **ap, **ep; + struct str2init *s2i; + + if (! (type = mime_type(filename))) { + adios(NULL, "Unable to determine MIME type of \"%s\"", filename); + } + + /* + * Parse the Content-Type. get_ctinfo() parses MIME parameters, but + * since we're just feeding it a MIME type we have to add those ourself. + * Map that to a valid content-type label and call any initialization + * function. + */ + + if (get_ctinfo(type, ct, 0) == NOTOK) + done(1); + + free(type); + + for (s2i = str2cts; s2i->si_key; s2i++) + if (strcasecmp(ct->c_ctinfo.ci_type, s2i->si_key) == 0) + break; + if (!s2i->si_key && !uprf(ct->c_ctinfo.ci_type, "X-")) + s2i++; + + /* + * Make sure the type isn't incompatible with what we can handle + */ + + switch (ct->c_type = s2i->si_val) { + case CT_MULTIPART: + adios (NULL, "multipart types must be specified by mhbuild directives"); + /* NOTREACHED */ + + case CT_MESSAGE: + if (strcasecmp(ct->c_ctinfo.ci_subtype, "partial") == 0) + adios(NULL, "Sorry, %s/%s isn't supported", ct->c_ctinfo.ci_type, + ct->c_ctinfo.ci_subtype); + if (strcasecmp(ct->c_ctinfo.ci_subtype, "external-body") == 0) + adios(NULL, "external-body messages must be specified " + "by mhbuild directives"); + /* Fall through */ + + default: + /* + * This sets the subtype, if it's significant + */ + if ((ct->c_ctinitfnx = s2i->si_init)) + (*ct->c_ctinitfnx)(ct); + break; + } + + /* + * Feed in a few attributes; specifically, the name attribute, the + * content-description, and the content-disposition. + */ + + for (ap = ct->c_ctinfo.ci_attrs, ep = ct->c_ctinfo.ci_values; *ap; + ap++, ep++) { + if (strcasecmp(*ap, "name") == 0) { + if (*ep) + free(*ep); + *ep = getcpy(filename); + break; + } + } + + if (*ap == NULL) { + *ap = getcpy("name"); + *ep = getcpy(filename); + } + + ct->c_descr = getcpy(filename); + + /* + * If it's a text/calendar, we need to make sure it's an inline, + * otherwise it won't work with some calendar programs. Otherwise + * assume attachment + */ + + if (strcasecmp(ct->c_ctinfo.ci_type, "text") == 0 && + strcasecmp(ct->c_ctinfo.ci_subtype, "calendar") == 0) { + ct->c_dispo = getcpy("inline; filename=\""); + } else { + ct->c_dispo = getcpy("attachment; filename=\""); + } + + ct->c_dispo = add(filename, ct->c_dispo); + ct->c_dispo = add("\"", ct->c_dispo); +} -- 2.48.1 From e4f6d48f7af4db62523d5351e2cf2849caa6ce54 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Thu, 9 Jan 2014 23:17:25 -0500 Subject: [PATCH 02/16] A bit closer, but don't run the test just yet. --- test/mhbuild/test-attach | 38 ++++++++++++++++++++++++++++++++++++++ test/mhbuild/tiny.jpg | 1 + uip/mhbuildsbr.c | 27 ++++++++++++++++++++++++--- 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100755 test/mhbuild/test-attach create mode 100644 test/mhbuild/tiny.jpg diff --git a/test/mhbuild/test-attach b/test/mhbuild/test-attach new file mode 100755 index 00000000..8bd3a64e --- /dev/null +++ b/test/mhbuild/test-attach @@ -0,0 +1,38 @@ +#!/bin/sh +###################################################### +# +# Test the use of the Attach: header +# +###################################################### + +if test -z "${MH_OBJ_DIR}"; then + srcdir=`dirname "$0"`/../.. + MH_OBJ_DIR=`cd "$srcdir" && pwd`; export MH_OBJ_DIR +fi + +. "$MH_OBJ_DIR/test/common.sh" + +setup_test + +draft="$MH_TEST_DIR/$$.draft" +expected="$MH_TEST_DIR/$$.expected" +actual="$MH_TEST_DIR/$$.actual" + +# +# Test out a simple draft +# + +cat > "$draft" < +cc: +Fcc: +outbox +Attach: ${srcdir}/test/mhbuild/tiny.jpg +------ +Test +EOF + +mhbuild "$draft" + +cat $draft + +exit $failed diff --git a/test/mhbuild/tiny.jpg b/test/mhbuild/tiny.jpg new file mode 100644 index 00000000..d17e4da5 --- /dev/null +++ b/test/mhbuild/tiny.jpg @@ -0,0 +1 @@ +ÿØ \ No newline at end of file diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 58540f1b..efd3c64f 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -211,10 +211,30 @@ build_mime (char *infile, int directives, int header_encoding) if (strcasecmp(ATTACH_FIELD, np) == 0) { struct attach_list *entry; + char *s = vp, *e = vp + strlen(vp) - 1; free(np); + + /* + * Make sure we can find the start of this filename. + * If it's blank, we skip completely. Otherwise, strip + * off any leading spaces and trailing newlines. + */ + + while (isspace((unsigned char) *s)) + s++; + + while (e > s && *e == '\n') + *e-- = '\0'; + + if (*s == '\0') { + free(vp); + goto finish_field; + } + entry = mh_xmalloc(sizeof(*entry)); - entry->filename = vp; - if (! attach_tail) { + entry->filename = getcpy(s); + free(vp); + if (attach_tail) { attach_tail->next = entry; attach_tail = entry; } else { @@ -320,7 +340,7 @@ finish_field: struct part *part; CT p; - if (! access(at_entry->filename, R_OK)) { + if (access(at_entry->filename, R_OK) != 0) { adios("reading", "Unable to open %s for", at_entry->filename); } @@ -1853,6 +1873,7 @@ setup_attach_content(CT ct, const char *filename) } ct->c_descr = getcpy(filename); + ct->c_cefile.ce_file = getcpy(filename); /* * If it's a text/calendar, we need to make sure it's an inline, -- 2.48.1 From 53bb0e7c20cda145c56c3aa4557c467e1832578b Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Fri, 10 Jan 2014 00:45:38 -0500 Subject: [PATCH 03/16] Make sure we use the 'short' filename and add newlines where appropriate. --- uip/mhbuildsbr.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index efd3c64f..d96e0d54 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -78,7 +78,7 @@ void free_encoding (CT, int); * static prototypes */ static int init_decoded_content (CT); -static void setup_attach_content(CT, const char *); +static void setup_attach_content(CT, char *); static char *fgetstr (char *, int, FILE *); static int user_content (FILE *, char *, char *, CT *); static void set_id (CT, int); @@ -1798,9 +1798,9 @@ calculate_digest (CT ct, int asciiP) */ static void -setup_attach_content(CT ct, const char *filename) +setup_attach_content(CT ct, char *filename) { - char *type, **ap, **ep; + char *type, **ap, **ep, *simplename = r1bindex(filename, '/'); struct str2init *s2i; if (! (type = mime_type(filename))) { @@ -1862,17 +1862,18 @@ setup_attach_content(CT ct, const char *filename) if (strcasecmp(*ap, "name") == 0) { if (*ep) free(*ep); - *ep = getcpy(filename); + *ep = getcpy(simplename); break; } } if (*ap == NULL) { *ap = getcpy("name"); - *ep = getcpy(filename); + *ep = getcpy(simplename); } - ct->c_descr = getcpy(filename); + ct->c_descr = getcpy(simplename); + ct->c_descr = add("\n", ct->c_descr); ct->c_cefile.ce_file = getcpy(filename); /* @@ -1888,6 +1889,6 @@ setup_attach_content(CT ct, const char *filename) ct->c_dispo = getcpy("attachment; filename=\""); } - ct->c_dispo = add(filename, ct->c_dispo); - ct->c_dispo = add("\"", ct->c_dispo); + ct->c_dispo = add(simplename, ct->c_dispo); + ct->c_dispo = add("\"\n", ct->c_dispo); } -- 2.48.1 From 6f84596dc6655711c9a42ec0353f42d69f9c3f7a Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 10 Jan 2014 19:03:56 -0600 Subject: [PATCH 04/16] Added -Wno-unused-result to silence warnings on Ubuntu. --- configure.ac | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/configure.ac b/configure.ac index f4fd6810..77996129 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,15 @@ if test -n "$auto_cflags"; then CFLAGS="$nmh_saved_cflags"]) test "$nmh_cv_has_wall" = "yes" && CFLAGS="${CFLAGS:+$CFLAGS }-Wall" + AC_CACHE_CHECK([whether compiler supports -Wno-unused-result], + [nmh_cv_has_wnur], + [nmh_saved_cflags="$CFLAGS" + CFLAGS="$CFLAGS -Wno-unused-result -Werror" + AC_TRY_COMPILE([],[],nmh_cv_has_wnur=yes,nmh_cv_has_wnur=no) + CFLAGS="$nmh_saved_cflags"]) + test "$nmh_cv_has_wnur" = "yes" && \ + CFLAGS="${CFLAGS:+$CFLAGS }-Wno-unused-result" + AC_CACHE_CHECK([whether compiler supports -Wextra], [nmh_cv_has_wextra], [nmh_saved_cflags="$CFLAGS" CFLAGS="$CFLAGS -Wextra -Werror" -- 2.48.1 From 1761743053b665d3d25c51195ff8e56f2eb2e165 Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 10 Jan 2014 19:36:34 -0600 Subject: [PATCH 05/16] No longer add -g or -s to LDFLAGS. autoconf handles -g. And "make install-strip" is the approved way to install stripped executables. --- configure.ac | 12 ------------ docs/pending-release-notes | 2 ++ 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/configure.ac b/configure.ac index 77996129..9b7bb49e 100644 --- a/configure.ac +++ b/configure.ac @@ -180,18 +180,6 @@ if test "$GCC" = yes; then LDFLAGS="${LDFLAGS:+$LDFLAGS }-Qunused-arguments" fi -dnl This really shouldn't be necessary. And we really shouldn't add -s. -if test x"$enable_debug" = x"yes"; then - test -z "$LDFLAGS" && LDFLAGS=-g -else - if test -z "$LDFLAGS"; then - case "$build_os" in - darwin*) LDFLAGS= ;; - *) LDFLAGS=-s ;; - esac - fi -fi - dnl This hack turns off assertions by default, assuming dnl that configure still uses this shell variable. Without dnl it, AC_HEADER_ASSERT enables assertions by default. diff --git a/docs/pending-release-notes b/docs/pending-release-notes index 1242e543..b4d3e631 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -104,6 +104,8 @@ OBSOLETE FEATURES to disable public sequence support has been removed. - Support for the -normalize and -nonormalize switches to the ali(1) and ap(8) commands has been removed. +- "make install" no longer strips executables. Use "make install-strip" + instead. ------------------- DEPRECATED FEATURES -- 2.48.1 From c02008a6745dd5f135370d375ff58d9789a3f13b Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 10 Jan 2014 20:49:24 -0800 Subject: [PATCH 06/16] Reworked LFLAGS hack for flex 2.5.35 and 2.5.36 to work on Ubuntu. And use sed instead of ed. --- configure.ac | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index 9b7bb49e..434efaac 100644 --- a/configure.ac +++ b/configure.ac @@ -503,11 +503,9 @@ dnl http://sourceforge.net/p/flex/bugs/140/ AS_IF([test "$LEX" = flex], [AS_CASE([`$LEX -V`], [flex\ 2.5.35], [LFLAGS=\ -'; printf "%s\n" '\''1,$$s/(size_t) \(num_to_read\)/\1/'\'' w q | ed -s $@; \ -true'], +'; sed -e "s/ int n;/ size_t n;/" $@ >$@.tmp && mv -f $@.tmp $@; true'], [flex\ 2.5.36], [LFLAGS=\ -'; printf "%s\n" '\''1,$$s/\( \)int i;/\1yy_size_t i;/'\'' w q | ed -s $@; \ -true']) +'; sed -s "s/\( \)int i;/\1yy_size_t i;/" $@ >$.tmp && mv -f $@.tmp $@; true']) AC_SUBST([LFLAGS])]) dnl ---------------- -- 2.48.1 From 3c0c7703cd4267119943391e31aa3b7e9d708fa7 Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 10 Jan 2014 23:25:19 -0600 Subject: [PATCH 07/16] Only add -Qunused-arguments to LDFLAGS if compiler is clang. --- configure.ac | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 434efaac..4f7229a3 100644 --- a/configure.ac +++ b/configure.ac @@ -167,18 +167,17 @@ AC_SUBST([POSTLINK]) dnl ----------------- dnl CUSTOMIZE LDFLAGS dnl ----------------- -if test "$GCC" = yes; then +AS_IF([test "$GCC" = yes && `${CC} --version 2>&1 | grep clang >/dev/null`], dnl Disable clang complaint about unused -ansi when linking. - AC_CACHE_CHECK([whether linker supports -Qunused-arguments], - [nmh_cv_has_q_unused_arguments], - [nmh_saved_ldflags="$LDFLAGS" - LDFLAGS="$LDFLAGS -Qunused-arguments" - AC_TRY_LINK([],[],nmh_cv_has_q_unused_arguments=yes, - nmh_cv_has_q_unused_arguments=no) - LDFLAGS="$nmh_saved_ldflags"]) - test "$nmh_cv_has_q_unused_arguments" = "yes" && \ - LDFLAGS="${LDFLAGS:+$LDFLAGS }-Qunused-arguments" -fi + [AC_CACHE_CHECK([whether linker supports -Qunused-arguments], + [nmh_cv_has_q_unused_arguments], + [nmh_saved_ldflags="$LDFLAGS" + LDFLAGS="$LDFLAGS -Qunused-arguments" + AC_TRY_LINK([],[],nmh_cv_has_q_unused_arguments=yes, + nmh_cv_has_q_unused_arguments=no) + LDFLAGS="$nmh_saved_ldflags"])]) +test "$nmh_cv_has_q_unused_arguments" = "yes" && \ + LDFLAGS="${LDFLAGS:+$LDFLAGS }-Qunused-arguments" dnl This hack turns off assertions by default, assuming dnl that configure still uses this shell variable. Without -- 2.48.1 From 439c0aa8fdbcba3dd6aba558df13f6c115077e7f Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Fri, 10 Jan 2014 13:31:05 -0500 Subject: [PATCH 08/16] Don't error out if the message body is empty. --- uip/mhbuildsbr.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index d96e0d54..53c06e95 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -248,12 +248,10 @@ finish_field: /* if this wasn't the last header field, then continue */ continue; - case FILEEOF: - adios (NULL, "draft has empty body -- no directives!"); - /* NOTREACHED */ - case BODY: fseek (in, (long) (-strlen (buf)), SEEK_CUR); + /* fall through */ + case FILEEOF: break; case LENERR: -- 2.48.1 From 2483080bcf4e9d381e519165e94788bc4c0e11bd Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sat, 11 Jan 2014 00:32:54 -0500 Subject: [PATCH 09/16] More tests, but not quite there yet. --- test/mhbuild/test-attach | 42 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/test/mhbuild/test-attach b/test/mhbuild/test-attach index 8bd3a64e..4eacc6e9 100755 --- a/test/mhbuild/test-attach +++ b/test/mhbuild/test-attach @@ -19,7 +19,7 @@ expected="$MH_TEST_DIR/$$.expected" actual="$MH_TEST_DIR/$$.actual" # -# Test out a simple draft +# Test out a simple draft, one part. # cat > "$draft" < "$actual" < +cc: +Fcc: +outbox +MIME-Version: 1.0 +Content-Type: image/jpeg; name="tiny.jpg" +Content-Description: tiny.jpg +Content-Disposition: attachment; filename="tiny.jpg" +Content-Transfer-Encoding: base64 + +/9g= +EOF + +check "$draft" "$actual" + +# +# Check out more common case, one (or more) attachments +# along with message text. +# + +cat > "$draft" < +cc: +Fcc: +outbox +Attach: ${srcdir}/test/mhbuild/tiny.jpg +------ +This is a test +EOF + +mhbuild "$draft" + +cat "$draft" + +# +# Two parts, one attachment +# -exit $failed +exit ${failed:-0} -- 2.48.1 From 83279431cd437b9f35dfa73d22996d911a052f0a Mon Sep 17 00:00:00 2001 From: David Levine Date: Fri, 10 Jan 2014 23:41:04 -0600 Subject: [PATCH 10/16] Fixed POSTLINK (on OpenBSD) now that it doesn't use -Qunused-arguments. --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4f7229a3..c77e9e00 100644 --- a/configure.ac +++ b/configure.ac @@ -160,7 +160,7 @@ dnl -------------- if test `uname` = OpenBSD; then dnl Filter out "often/almost always misused" warnings from OpenBSD linker. [POSTLINK="2>&1 | egrep -v ': [w]arning: s[a-z]+\(\) is \ -(almost always|often) misused, please use'; true"] +(almost always|often) misused, please use' || true"] fi AC_SUBST([POSTLINK]) -- 2.48.1 From ec45499eb813e6145d5c18e9126de0a95c153c72 Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sat, 11 Jan 2014 01:11:45 -0500 Subject: [PATCH 11/16] Switch over to using --mime-type, since --mime seems to include a charset for non-text MIME types. --- configure.ac | 13 +------------ m4/mimetype.m4 | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 12 deletions(-) create mode 100644 m4/mimetype.m4 diff --git a/configure.ac b/configure.ac index c77e9e00..46bb0e8d 100644 --- a/configure.ac +++ b/configure.ac @@ -304,18 +304,7 @@ AC_CACHE_CHECK(what group owns the mail spool, nmh_cv_ls_mail_grp, MAIL_SPOOL_GRP=$nmh_cv_ls_mail_grp AC_SUBST([MAIL_SPOOL_GRP])dnl -dnl --------------------------------------------- -dnl CHECK FOR PROGRAM TO PROVIDE MIME TYPE STRING -dnl --------------------------------------------- -AS_IF([file --mime "${srcdir}"/configure >/dev/null 2>&1], - [AS_IF([file --brief --mime "${srcdir}"/configure >/dev/null 2>&1], - [nmh_mimetypeproc='"file --brief --mime"'], - [nmh_mimetypeproc='"file --mime"'])]) -AS_CASE([$nmh_mimetypeproc], [?*], - [AC_DEFINE_UNQUOTED([MIMETYPEPROC], [$nmh_mimetypeproc], - [Program, with arguments, that provides MIME type string.])]) -AS_ECHO_N(["AS_ESCAPE([checking program to provide MIME type string ... ])"]) -AS_ECHO(["AS_ESCAPE([$nmh_mimetypeproc], [""])"]) +NMH_MIMETYPEPROC dnl ------------------ dnl CHECK HEADER FILES diff --git a/m4/mimetype.m4 b/m4/mimetype.m4 new file mode 100644 index 00000000..e767ff77 --- /dev/null +++ b/m4/mimetype.m4 @@ -0,0 +1,18 @@ +dnl +dnl Try to see if we have a program that can determine the MIME type +dnl of a particular file +dnl + +AC_DEFUN([NMH_MIMETYPEPROC], +[AC_CACHE_CHECK([for a program to provide a MIME type string], + [nmh_cv_mimetype_proc], + [nmh_cv_mimetype_proc= + for mprog in 'file --brief --mime-type' 'file --mime-type' + do + AS_IF([$mprog "${srcdir}/configure" > /dev/null 2>&1], + [nmh_cv_mimetype_proc="$mprog"; break]) + done]) +AS_IF([test X"$nmh_cv_mimetype_proc" != X], + [mimetype_proc="\"${nmh_cv_mimetype_proc}\"" + AC_DEFINE_UNQUOTED([MIMETYPEPROC], [$mimetype_proc], + [Program, with arguments, that provides MIME type string.])])]) -- 2.48.1 From a376784b51d76aee169b9f358bc7ccef9d70a04a Mon Sep 17 00:00:00 2001 From: Ken Hornstein Date: Sat, 11 Jan 2014 01:41:44 -0500 Subject: [PATCH 12/16] Change test-mhmail so it can handle a MIMETYPEPROC that doesn't output the character set. --- test/mhmail/test-mhmail | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/test/mhmail/test-mhmail b/test/mhmail/test-mhmail index df921b6d..27188c77 100755 --- a/test/mhmail/test-mhmail +++ b/test/mhmail/test-mhmail @@ -21,12 +21,9 @@ content_type_string() { echo "text/plain; name=\"`basename $1`\"; charset=\"us-ascii\"" else # 1) Excise leading filename followed by : and any whitespace. - # 2) Insert semicolon if there isn't one before the first space. - # OpenBSD file --mime spits out things like "text/plain charset=...". - # 3) Wrap charset value in double quotes. Assume that it isn't already. - printf "%s %s%s" \ - `$MIMETYPEPROC $1 | sed -e 's/.*: *//' -e 's/\([^;]\) /\1; /' \ - -e 's/\(charset=\)\(.*\)/\1"\2"/'` "; name=\"`basename $1`\"" + # 2) Wrap charset value in double quotes. Assume that it isn't already. + printf "%s%s" \ + `$MIMETYPEPROC $1` "; name=\"`basename $1`\"; charset=\"us-ascii\"" fi } -- 2.48.1 From b166a6e1e60845be7e40397c59d2e997af8fdf5b Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 11 Jan 2014 08:00:55 -0600 Subject: [PATCH 13/16] Reorganized autoconf test for -Qunused-warnings so that it outputs something when configuring on any platform. --- configure.ac | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 46bb0e8d..ed4b6b15 100644 --- a/configure.ac +++ b/configure.ac @@ -136,6 +136,7 @@ if test -n "$auto_cflags"; then CFLAGS="$nmh_saved_cflags"]) test "$nmh_cv_has_wall" = "yes" && CFLAGS="${CFLAGS:+$CFLAGS }-Wall" + dnl for Ubuntu AC_CACHE_CHECK([whether compiler supports -Wno-unused-result], [nmh_cv_has_wnur], [nmh_saved_cflags="$CFLAGS" @@ -167,15 +168,17 @@ AC_SUBST([POSTLINK]) dnl ----------------- dnl CUSTOMIZE LDFLAGS dnl ----------------- -AS_IF([test "$GCC" = yes && `${CC} --version 2>&1 | grep clang >/dev/null`], - dnl Disable clang complaint about unused -ansi when linking. - [AC_CACHE_CHECK([whether linker supports -Qunused-arguments], - [nmh_cv_has_q_unused_arguments], - [nmh_saved_ldflags="$LDFLAGS" - LDFLAGS="$LDFLAGS -Qunused-arguments" - AC_TRY_LINK([],[],nmh_cv_has_q_unused_arguments=yes, - nmh_cv_has_q_unused_arguments=no) - LDFLAGS="$nmh_saved_ldflags"])]) +dnl Disable clang complaint about unused -ansi when linking. +AC_CACHE_CHECK([whether linker supports -Qunused-arguments], + [nmh_cv_has_q_unused_arguments], + [AS_IF([test "$GCC" = yes && `${CC} --version 2>&1 | \ + grep clang >/dev/null`], + [nmh_saved_ldflags="$LDFLAGS" + LDFLAGS="$LDFLAGS -Qunused-arguments" + AC_TRY_LINK([],[],nmh_cv_has_q_unused_arguments=yes, + nmh_cv_has_q_unused_arguments=no) + LDFLAGS="$nmh_saved_ldflags"], + [nmh_cv_has_q_unused_arguments=no])]) test "$nmh_cv_has_q_unused_arguments" = "yes" && \ LDFLAGS="${LDFLAGS:+$LDFLAGS }-Qunused-arguments" -- 2.48.1 From c30f86b9629d62eaf9e6617e3303cdc7323026a6 Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 11 Jan 2014 08:09:20 -0600 Subject: [PATCH 14/16] Changed how build_nmh deals with older mhparam that doesn't report SASL/TLS. --- docs/contrib/build_nmh | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/contrib/build_nmh b/docs/contrib/build_nmh index b00ad90f..66383acf 100755 --- a/docs/contrib/build_nmh +++ b/docs/contrib/build_nmh @@ -157,10 +157,12 @@ if install-mh -check >/dev/null 2>&1; then case `$mhbin/mhparam tls` in *tls*) config_tls=y ;; - esac + esac else - echo "$0: -y not supported with currently installed nmh" - exit 1 + tput smso + echo "$0: SASL and TLS detection not supported with current nmh" + [ $yes -eq 1 ] && echo "will not configure either one in" + tput rmso fi fi fi -- 2.48.1 From b4f2851d40874160fbe4c5ce288ae2e7fd8fbb4b Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 11 Jan 2014 09:22:36 -0600 Subject: [PATCH 15/16] Reworked attach to add charset to Content-Type string for text content. --- configure.ac | 1 + m4/mimetype.m4 | 22 +++++++- test/mhmail/test-mhmail | 14 ++--- uip/attach.c | 116 +++++++++++++++++++++++++--------------- uip/mhparam.c | 70 +++++++++++++----------- 5 files changed, 142 insertions(+), 81 deletions(-) diff --git a/configure.ac b/configure.ac index ed4b6b15..f480184b 100644 --- a/configure.ac +++ b/configure.ac @@ -308,6 +308,7 @@ MAIL_SPOOL_GRP=$nmh_cv_ls_mail_grp AC_SUBST([MAIL_SPOOL_GRP])dnl NMH_MIMETYPEPROC +NMH_MIMEENCODINGPROC dnl ------------------ dnl CHECK HEADER FILES diff --git a/m4/mimetype.m4 b/m4/mimetype.m4 index e767ff77..b97c49d4 100644 --- a/m4/mimetype.m4 +++ b/m4/mimetype.m4 @@ -2,6 +2,10 @@ dnl dnl Try to see if we have a program that can determine the MIME type dnl of a particular file dnl +dnl Assume that if file(1) doesn't support --mime-type, then it's +dnl unusable. The --mime option to file 4.17 on CentOS 5.9, for +dnl example, prints out a long description after the mime type. We +dnl don't want that. AC_DEFUN([NMH_MIMETYPEPROC], [AC_CACHE_CHECK([for a program to provide a MIME type string], @@ -10,9 +14,23 @@ AC_DEFUN([NMH_MIMETYPEPROC], for mprog in 'file --brief --mime-type' 'file --mime-type' do AS_IF([$mprog "${srcdir}/configure" > /dev/null 2>&1], - [nmh_cv_mimetype_proc="$mprog"; break]) + [nmh_cv_mimetype_proc="$mprog"; break]) done]) AS_IF([test X"$nmh_cv_mimetype_proc" != X], [mimetype_proc="\"${nmh_cv_mimetype_proc}\"" AC_DEFINE_UNQUOTED([MIMETYPEPROC], [$mimetype_proc], - [Program, with arguments, that provides MIME type string.])])]) + [Program, with arguments, to provides MIME type.])])]) + +AC_DEFUN([NMH_MIMEENCODINGPROC], +[AC_CACHE_CHECK([for a program to provide a MIME encoding string], + [nmh_cv_mimeencoding_proc], + [nmh_cv_mimeencoding_proc= + for mprog in 'file --brief --mime-encoding' 'file --mime-encoding' + do + AS_IF([$mprog "${srcdir}/configure" > /dev/null 2>&1], + [nmh_cv_mimeencoding_proc="$mprog"; break]) + done]) +AS_IF([test X"$nmh_cv_mimeencoding_proc" != X], + [mimeencoding_proc="\"${nmh_cv_mimeencoding_proc}\"" + AC_DEFINE_UNQUOTED([MIMEENCODINGPROC], [$mimeencoding_proc], + [Program, with arguments, to provides MIME encoding.])])]) diff --git a/test/mhmail/test-mhmail b/test/mhmail/test-mhmail index 27188c77..043d95ef 100755 --- a/test/mhmail/test-mhmail +++ b/test/mhmail/test-mhmail @@ -14,16 +14,18 @@ fi . "${srcdir}/test/post/test-post-common.sh" -# Find MIME type string, using mimetypeproc if configured with it. +# Find MIME type string, using configured procs if available. MIMETYPEPROC=`mhparam mimetypeproc` +MIMEENCODINGPROC=`mhparam mimeencodingproc` content_type_string() { - if test -z "$MIMETYPEPROC"; then + if test -z "$MIMETYPEPROC" -o -z "$MIMEENCODINGPROC"; then + #### This should be the order of name and charset. echo "text/plain; name=\"`basename $1`\"; charset=\"us-ascii\"" else - # 1) Excise leading filename followed by : and any whitespace. - # 2) Wrap charset value in double quotes. Assume that it isn't already. - printf "%s%s" \ - `$MIMETYPEPROC $1` "; name=\"`basename $1`\"; charset=\"us-ascii\"" + #### Excise any leading filename followed by : and whitespace. + printf '%s; charset="%s"; name="%s"' \ + `$MIMETYPEPROC $1 | sed -e 's/.*: *//'` \ + `$MIMEENCODINGPROC $1 | sed -e 's/.*: *//'` `basename $1` fi } diff --git a/uip/attach.c b/uip/attach.c index d4902d55..b1a4f441 100644 --- a/uip/attach.c +++ b/uip/attach.c @@ -11,6 +11,9 @@ #include static int get_line(FILE *, char *, size_t); +#ifdef MIMETYPEPROC +static char *get_file_info(const char *, const char *); +#endif /* MIMETYPEPROC */ int attach(char *attachment_header_field_name, char *draft_file_name, @@ -285,69 +288,98 @@ get_line(FILE *draft_file, char *field, size_t field_size) * encoding. Caller is responsible for free'ing returned memory. */ char * -mime_type (const char *file_name) { +mime_type(const char *file_name) { char *content_type = NULL; /* mime content type */ #ifdef MIMETYPEPROC - char cmd[2 * PATH_MAX + 2]; /* file command buffer */ + char *mimetype; + + if ((mimetype = get_file_info(MIMETYPEPROC, file_name))) { +#ifdef MIMEENCODINGPROC + /* Try to append charset for text content. */ + char *mimeencoding; + + if (strncasecmp(mimetype, "text", 4) == 0) { + if ((mimeencoding = get_file_info(MIMEENCODINGPROC, file_name))) { + content_type = concat(mimetype, "; charset=", mimeencoding, + NULL); + } else { + content_type = strdup(mimetype); + } + } else { + content_type = strdup(mimetype); + } +#else /* MIMEENCODINGPROC */ + content_type = strdup(mimetype); +#endif /* MIMEENCODINGPROC */ + } +#else /* MIMETYPEPROC */ + NMH_UNUSED(file_name); +#endif /* MIMETYPEPROC */ + + return content_type; +} + + +#ifdef MIMETYPEPROC +/* + * Get information using proc about a file. + */ +static char * +get_file_info(const char *proc, const char *file_name) { char buf[BUFSIZ >= 2048 ? BUFSIZ : 2048]; - FILE *fp; /* content and pipe file pointer */ - char mimetypeproc[] = MIMETYPEPROC " '%s'"; + char *cmd, *cp; + char *quotec = "'"; + FILE *fp; + + if ((cp = strchr(file_name, '\''))) { + /* file_name contains a single quote. */ + if (strchr(file_name, '"')) { + advise(NULL, "filenames containing both single and double quotes " + "are unsupported for attachment"); + return NULL; + } else { + quotec = "\""; + } + } - if ((int) snprintf (cmd, sizeof cmd, mimetypeproc, file_name) < - (int) sizeof cmd) { - if ((fp = popen (cmd, "r")) != NULL) { - /* Make sure that buf has space for one additional - character, the semicolon that might be added below. */ - if (fgets (buf, sizeof buf - 1, fp)) { - char *cp, *space; + cmd = concat(proc, " ", quotec, file_name, quotec, NULL); + if ((cmd = concat(proc, " ", quotec, file_name, quotec, NULL))) { + if ((fp = popen(cmd, "r")) != NULL) { + if (fgets(buf, sizeof buf, fp)) { + char *eol; /* Skip leading :, if present. */ - if ((content_type = strchr (buf, ':')) != NULL) { - ++content_type; - while (*content_type && isblank (*content_type)) { - ++content_type; + if ((cp = strchr(buf, ':')) != NULL) { + ++cp; + while (*cp && isblank((unsigned char) *cp)) { + ++cp; } } else { - content_type = buf; + cp = buf; } /* Truncate at newline (LF or CR), if present. */ - if ((cp = strpbrk (content_type, "\n\n")) != NULL) { - *cp = '\0'; - } - - /* If necessary, insert semicolon between content type - and charset. Assume that the first space is between - them. */ - if ((space = strchr (content_type, ' ')) != NULL) { - ssize_t len = strlen (content_type); - - if (space - content_type > 0 && - len > space - content_type + 1) { - if (*(space - 1) != ';') { - /* The +1 is for the terminating NULL. */ - memmove (space + 1, space, - len - (space - content_type) + 1); - *space = ';'; - } - } + if ((eol = strpbrk(cp, "\n\r")) != NULL) { + *eol = '\0'; } } else { - advise (NULL, "unable to read mime type"); + advise(NULL, "unable to read mime type"); } + + (void) pclose(fp); } else { - advise (NULL, "unable to run %s", buf); + advise(NULL, "no output from %s", cmd); } + + free(cmd); } else { - advise (NULL, "filename too large to deduce mime type"); + advise(NULL, "concat with \"%s\" failed, out of memory?", proc); } -#else - NMH_UNUSED (file_name); -#endif - return content_type ? strdup (content_type) : NULL; + return cp ? strdup(cp) : NULL; } +#endif /* MIMETYPEPROC */ /* diff --git a/uip/mhparam.c b/uip/mhparam.c index 351f0b9b..b0c119cf 100644 --- a/uip/mhparam.c +++ b/uip/mhparam.c @@ -68,43 +68,51 @@ char *mimetypeproc = ""; #endif +char *mimeencodingproc = +#ifdef MIMEENCODINGPROC + MIMEENCODINGPROC; +#else + ""; +#endif + struct proc { char *p_name; char **p_field; }; static struct proc procs [] = { - { "context", &context }, - { "mh-sequences", &mh_seq }, - { "buildmimeproc", &buildmimeproc }, - { "fileproc", &fileproc }, - { "foldprot", &foldprot }, - { "formatproc", &formatproc }, - { "incproc", &incproc }, - { "lproc", &lproc }, - { "mailproc", &mailproc }, - { "mhlproc", &mhlproc }, - { "mimetypeproc", &mimetypeproc }, - { "moreproc", &moreproc }, - { "msgprot", &msgprot }, - { "mshproc", &mshproc }, - { "packproc", &packproc }, - { "postproc", &postproc }, - { "rmmproc", &rmmproc }, - { "sendproc", &sendproc }, - { "showmimeproc", &showmimeproc }, - { "showproc", &showproc }, - { "version", &version_num }, - { "vmhproc", &vmhproc }, - { "whatnowproc", &whatnowproc }, - { "whomproc", &whomproc }, - { "etcdir", &mhetcdir }, - { "libdir", &mhlibdir }, - { "sbackup", &sbackup }, - { "lockmethod", &lockmethod }, - { "sasl", &sasl }, - { "tls", &tls }, - { NULL, NULL }, + { "context", &context }, + { "mh-sequences", &mh_seq }, + { "buildmimeproc", &buildmimeproc }, + { "fileproc", &fileproc }, + { "foldprot", &foldprot }, + { "formatproc", &formatproc }, + { "incproc", &incproc }, + { "lproc", &lproc }, + { "mailproc", &mailproc }, + { "mhlproc", &mhlproc }, + { "mimetypeproc", &mimetypeproc }, + { "mimeencodingproc", &mimeencodingproc }, + { "moreproc", &moreproc }, + { "msgprot", &msgprot }, + { "mshproc", &mshproc }, + { "packproc", &packproc }, + { "postproc", &postproc }, + { "rmmproc", &rmmproc }, + { "sendproc", &sendproc }, + { "showmimeproc", &showmimeproc }, + { "showproc", &showproc }, + { "version", &version_num }, + { "vmhproc", &vmhproc }, + { "whatnowproc", &whatnowproc }, + { "whomproc", &whomproc }, + { "etcdir", &mhetcdir }, + { "libdir", &mhlibdir }, + { "sbackup", &sbackup }, + { "lockmethod", &lockmethod }, + { "sasl", &sasl }, + { "tls", &tls }, + { NULL, NULL }, }; -- 2.48.1 From c888736f7452c19ae3c7be0f194742ca8e189ace Mon Sep 17 00:00:00 2001 From: David Levine Date: Sat, 11 Jan 2014 10:15:27 -0600 Subject: [PATCH 16/16] Work around OpenBSD 5.4 file(1), which reports the --mime-encoding of text files as "binary". --- m4/mimetype.m4 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/m4/mimetype.m4 b/m4/mimetype.m4 index b97c49d4..30796866 100644 --- a/m4/mimetype.m4 +++ b/m4/mimetype.m4 @@ -21,14 +21,17 @@ AS_IF([test X"$nmh_cv_mimetype_proc" != X], AC_DEFINE_UNQUOTED([MIMETYPEPROC], [$mimetype_proc], [Program, with arguments, to provides MIME type.])])]) +dnl The OpenBSD 5.4 file (4.24) reports --mime-encoding of text +dnl files as "binary". Detect that by only accepting "us-ascii". AC_DEFUN([NMH_MIMEENCODINGPROC], [AC_CACHE_CHECK([for a program to provide a MIME encoding string], [nmh_cv_mimeencoding_proc], [nmh_cv_mimeencoding_proc= for mprog in 'file --brief --mime-encoding' 'file --mime-encoding' do - AS_IF([$mprog "${srcdir}/configure" > /dev/null 2>&1], - [nmh_cv_mimeencoding_proc="$mprog"; break]) + AS_IF([$mprog "${srcdir}/DATE" > /dev/null 2>&1], + AS_CASE([`$mprog "${srcdir}/DATE"`], + [us-ascii],[nmh_cv_mimeencoding_proc="$mprog"; break])) done]) AS_IF([test X"$nmh_cv_mimeencoding_proc" != X], [mimeencoding_proc="\"${nmh_cv_mimeencoding_proc}\"" -- 2.48.1