From: David Levine Date: Sun, 22 Dec 2013 20:42:26 +0000 (-0600) Subject: With whatnow attach, determine the content MIME type that's inserted X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/80ec106609b472cffc2cf0d8ed4044b7f94da1fa With whatnow attach, determine the content MIME type that's inserted into the Content-Type header using file --mime or file -i, if available on the platform at configuration time. If not, continue to use the mhshow-suffix- method. Added a mimetypeproc component to mhparam so that a user can query the configuration. --- diff --git a/configure.ac b/configure.ac index 2ae86338..c86e91da 100644 --- a/configure.ac +++ b/configure.ac @@ -324,6 +324,19 @@ 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], + [nmh_mimetypeproc='"file --mime"']) +if test x"$nmh_mimetypeproc" = x; then + AS_IF([file -i "${srcdir}"/configure >/dev/null 2>&1], + [nmh_mimetypeproc='"file -i"']) +fi +AS_CASE([$nmh_mimetypeproc], [?*], + [AC_DEFINE_UNQUOTED([MIMETYPEPROC], [$nmh_mimetypeproc], + [Program, with arguments, that provides MIME type string.])]) + dnl ------------------ dnl CHECK HEADER FILES dnl ------------------ diff --git a/docs/pending-release-notes b/docs/pending-release-notes index b906a44f..d5a9a27d 100644 --- a/docs/pending-release-notes +++ b/docs/pending-release-notes @@ -60,6 +60,10 @@ NEW FEATURES - The -changecur and -nochangecur switches have been added to mhlist(1). - mhbuild(1) can now encode 8-bit message headers using RFC-2047 encoding rules. +- The whatnow(1) attach feature will determine the content type of an + attachment using a program such as file --mime, if available at + configuration time. If not, it will use mhshow-suffix- entries as + before. ----------------- OBSOLETE FEATURES diff --git a/man/send.man b/man/send.man index ece0e5c8..83cf19ae 100644 --- a/man/send.man +++ b/man/send.man @@ -1,7 +1,7 @@ .\" .\" %nmhwarning% .\" -.TH SEND %manext1% "April 14, 2013" "%nmhversion%" +.TH SEND %manext1% "December 22, 2013" "%nmhversion%" .SH NAME send \- send a message .SH SYNOPSIS @@ -103,7 +103,27 @@ The body of each header field whose name matches the is interpreted as a file name, and each file named is included as a separate part in the MIME message. .PP -For file names with dot suffixes, the profile is scanned for a +Determination of the content MIME type inserted into the Content-Type +header for each part depends on how the +.B nmh +installation was configured. If a program, such as +.B file +with a +.B --mime +or +.B -i +option, was found that can specify the type of a file as a MIME type +string, then that will be used. To determine if your +.B nmh +was so configured, run +.B mhparam mimetypeproc +and see if a non-empty string is displayed. +.PP +If your +.B nmh +was not configured with a program to specify a file type as a MIME +string, then a different method is used to determine the content-type +string. For file names with dot suffixes, the profile is scanned for a .I mhshow-suffix- entry for that suffix. The content-type for the part is taken from that profile entry if a match is @@ -473,7 +493,9 @@ for more information. .SH "SEE ALSO" .IR comp (1), .IR dist (1), +.IR file (1), .IR forw (1), +.IR mhparam (1), .IR repl (1), .IR whatnow (1), .IR mh\-alias (5), diff --git a/test/mhbuild/test-utf8-body b/test/mhbuild/test-utf8-body index 3446153f..5539489c 100755 --- a/test/mhbuild/test-utf8-body +++ b/test/mhbuild/test-utf8-body @@ -39,7 +39,7 @@ test_attachment () # use, so we'll just use sed to remove the actual date so we can easily # compare it against our "correct" output. And same for charset. # - sed -e 's/^Date:.*/Date:/' -e 's/; charset=".*//' \ + sed -e 's/^Date:.*/Date:/' -e 's/; charset="[^;]*//' \ "`mhpath cur`" > "${testname}.actual" check "${testname}.actual" "$1" diff --git a/test/mhmail/test-mhmail b/test/mhmail/test-mhmail index 31d6689e..c773c041 100755 --- a/test/mhmail/test-mhmail +++ b/test/mhmail/test-mhmail @@ -14,6 +14,20 @@ fi . "${srcdir}/test/post/test-post-common.sh" +# Find MIME type string, using mimetypeproc if configured with it. +MIMETYPEPROC=`mhparam mimetypeproc` +content_type_string() { + if test -z "$MIMETYPEPROC"; then + 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%s" \ + `$MIMETYPEPROC $1 | sed -e 's/.*: *//' -e 's/\(charset=\)\(.*\)/\1"\2"/'`\ + "; name=\"`basename $1`\"" + fi +} + # Customize test_post () for use with mhmail. # $1 is expected output file, provided by caller # $2 is mhmail switches, except for -body @@ -791,7 +805,7 @@ Content-Type: text/plain; charset="us-ascii" See how easy it is to add an attachment! ------- =_aaaaaaaaaa0 -Content-Type: text/plain; name="attachment.txt"; charset="us-ascii" +Content-Type: `content_type_string ${srcdir}/test/mhmail/attachment.txt` Content-Description: attachment.txt Content-Disposition: attachment; filename="attachment.txt" diff --git a/uip/mhparam.c b/uip/mhparam.c index 9da7ba13..351f0b9b 100644 --- a/uip/mhparam.c +++ b/uip/mhparam.c @@ -61,6 +61,13 @@ char *tls = ""; #endif +char *mimetypeproc = +#ifdef MIMETYPEPROC + MIMETYPEPROC; +#else + ""; +#endif + struct proc { char *p_name; char **p_field; @@ -77,6 +84,7 @@ static struct proc procs [] = { { "lproc", &lproc }, { "mailproc", &mailproc }, { "mhlproc", &mhlproc }, + { "mimetypeproc", &mimetypeproc }, { "moreproc", &moreproc }, { "msgprot", &msgprot }, { "mshproc", &mshproc }, diff --git a/uip/sendsbr.c b/uip/sendsbr.c index ecaf3928..96c6b52a 100644 --- a/uip/sendsbr.c +++ b/uip/sendsbr.c @@ -63,6 +63,7 @@ static int attach(char *, char *, int); static void clean_up_temporary_files(void); static int get_line(void); static void make_mime_composition_file_entry(char *, int, char *); +static char *mime_type (const char *); /* @@ -425,24 +426,28 @@ make_mime_composition_file_entry(char *file_name, int attachformat, char *p; /* miscellaneous string pointer */ struct stat st; /* file status buffer */ - content_type = default_content_type; - - /* - * Check the file name for a suffix. Scan the context for that suffix on a - * mhshow-suffix- entry. We use these entries to be compatible with mhnshow, - * and there's no reason to make the user specify each suffix twice. Context - * entries of the form "mhshow-suffix-contenttype" in the name have the suffix - * in the field, including the dot. - */ + if ((content_type = mime_type (file_name)) == NULL) { + /* + * Check the file name for a suffix. Scan the context for + * that suffix on a mhshow-suffix- entry. We use these + * entries to be compatible with mhnshow, and there's no + * reason to make the user specify each suffix twice. Context + * entries of the form "mhshow-suffix-contenttype" in the name + * have the suffix in the field, including the dot. + */ + if ((p = strrchr(file_name, '.')) != (char *)0) { + for (np = m_defs; np; np = np->n_next) { + if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 && + strcasecmp(p, np->n_field ? np->n_field : "") == 0) { + content_type = strdup (np->n_name + 14); + break; + } + } + } - if ((p = strrchr(file_name, '.')) != (char *)0) { - for (np = m_defs; np; np = np->n_next) { - if (strncasecmp(np->n_name, "mhshow-suffix-", 14) == 0 && - strcasecmp(p, np->n_field ? np->n_field : "") == 0) { - content_type = np->n_name + 14; - break; - } - } + if (content_type == NULL && default_content_type != NULL) { + content_type = strdup (default_content_type); + } } /* @@ -468,7 +473,8 @@ make_mime_composition_file_entry(char *file_name, int attachformat, (void)fclose(fp); - content_type = binary ? "application/octet-stream" : "text/plain"; + content_type = + strdup (binary ? "application/octet-stream" : "text/plain"); } /* @@ -579,6 +585,8 @@ make_mime_composition_file_entry(char *file_name, int attachformat, adios ((char *)0, "unsupported attachformat %d", attachformat); } + free (content_type); + /* * Finish up with the file name. */ @@ -588,6 +596,56 @@ make_mime_composition_file_entry(char *file_name, int attachformat, return; } +/* + * Try to use external command to determine mime type, and possibly + * encoding. Caller is responsible for free'ing returned memory. + */ +static char * +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 buf[BUFSIZ >= 2048 ? BUFSIZ : 2048]; + FILE *fp; /* content and pipe file pointer */ + char mimetypeproc[] = MIMETYPEPROC " '%s'"; + + if ((int) snprintf (cmd, sizeof cmd, mimetypeproc, file_name) < + (int) sizeof cmd) { + if ((fp = popen (cmd, "r")) != NULL) { + if (fgets (buf, sizeof cmd, fp)) { + char *cp; + + /* Skip leading :, if present. */ + if ((content_type = strchr (buf, ':')) != NULL) { + ++content_type; + while (*content_type && isblank (*content_type)) { + ++content_type; + } + } else { + content_type = buf; + } + + /* Truncate at newline (LF or CR), if present. */ + if ((cp = strpbrk (content_type, "\n\n")) != NULL) { + *cp = '\0'; + } + } else { + advise (NULL, "unable to read mime type"); + } + } else { + advise (NULL, "unable to run %s", buf); + } + } else { + advise (NULL, "filename to large to deduce mime type"); + } +#else + NMH_UNUSED (file_name); +#endif + + return content_type ? strdup (content_type) : NULL; +} + /* * Split large message into several messages of * type "message/partial" and send them.