From: David Levine Date: Sat, 11 Jan 2014 15:22:36 +0000 (-0600) Subject: Reworked attach to add charset to Content-Type string for X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/b4f2851d40874160fbe4c5ce288ae2e7fd8fbb4b Reworked attach to add charset to Content-Type string for text content. --- 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 }, };