]> diplodocus.org Git - nmh/commitdiff
With whatnow attach, determine the content MIME type that's inserted
authorDavid Levine <levinedl@acm.org>
Sun, 22 Dec 2013 20:42:26 +0000 (14:42 -0600)
committerDavid Levine <levinedl@acm.org>
Sun, 22 Dec 2013 20:42:26 +0000 (14:42 -0600)
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.

configure.ac
docs/pending-release-notes
man/send.man
test/mhbuild/test-utf8-body
test/mhmail/test-mhmail
uip/mhparam.c
uip/sendsbr.c

index 2ae8633..c86e91d 100644 (file)
@@ -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 ------------------
index b906a44..d5a9a27 100644 (file)
@@ -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
index ece0e5c..83cf19a 100644 (file)
@@ -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),
index 3446153..5539489 100755 (executable)
@@ -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"
index 31d6689..c773c04 100755 (executable)
@@ -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"
 
index 9da7ba1..351f0b9 100644 (file)
@@ -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 },
index ecaf392..96c6b52 100644 (file)
@@ -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 <filename>:<whitespace>, 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.