]> diplodocus.org Git - nmh/commitdiff
Support complete encoding for encoding Content-Type parameters. Start
authorKen Hornstein <kenh@pobox.com>
Fri, 28 Feb 2014 02:38:09 +0000 (21:38 -0500)
committerKen Hornstein <kenh@pobox.com>
Fri, 28 Feb 2014 02:38:09 +0000 (21:38 -0500)
work on retrieving parameters and converting them to the correct charset.

Makefile.am
h/mhparse.h
test/mhbuild/test-ext-params
uip/mhbuildsbr.c
uip/mhparse.c

index bf933ef..b977d7e 100644 (file)
@@ -289,7 +289,7 @@ uip_anno_LDADD = $(LDADD) $(POSTLINK)
 
 uip_burst_SOURCES = uip/burst.c uip/mhparse.c uip/mhmisc.c uip/mhfree.c \
                    uip/mhcachesbr.c uip/md5.c
-uip_burst_LDADD = $(LDADD) $(POSTLINK)
+uip_burst_LDADD = $(LDADD) $(ICONVLIB) $(POSTLINK)
 
 uip_comp_SOURCES = uip/comp.c uip/whatnowproc.c uip/whatnowsbr.c uip/sendsbr.c \
                   uip/annosbr.c uip/distsbr.c
@@ -321,7 +321,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_mhbuild_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhbuild_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_mhfixmsg_SOURCES = uip/mhfixmsg.c uip/mhparse.c uip/mhcachesbr.c \
                       uip/mhoutsbr.c uip/mhmisc.c uip/mhfree.c \
@@ -330,12 +330,12 @@ uip_mhfixmsg_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_mhlist_SOURCES = uip/mhlist.c uip/mhparse.c uip/mhcachesbr.c \
                     uip/mhlistsbr.c uip/mhmisc.c uip/mhfree.c uip/md5.c
-uip_mhlist_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhlist_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_mhn_SOURCES = uip/mhn.c uip/mhparse.c uip/mhcachesbr.c uip/mhshowsbr.c \
                  uip/mhlistsbr.c uip/mhstoresbr.c uip/mhmisc.c uip/mhfree.c \
                  uip/md5.c
-uip_mhn_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhn_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_mhparam_SOURCES = uip/mhparam.c
 uip_mhparam_LDADD = $(LDADD) $(POSTLINK)
@@ -346,12 +346,12 @@ uip_mhpath_LDADD = $(LDADD) $(POSTLINK)
 uip_mhshow_SOURCES = uip/mhshow.c uip/mhparse.c uip/mhcachesbr.c \
                     uip/mhshowsbr.c uip/mhlistsbr.c uip/mhmisc.c \
                     uip/mhfree.c uip/md5.c
-uip_mhshow_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhshow_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_mhstore_SOURCES = uip/mhstore.c uip/mhparse.c uip/mhcachesbr.c \
                      uip/mhshowsbr.c uip/mhlistsbr.c uip/mhstoresbr.c \
                      uip/mhmisc.c uip/mhfree.c uip/md5.c
-uip_mhstore_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhstore_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_msgchk_SOURCES = uip/msgchk.c uip/popsbr.c
 uip_msgchk_LDADD = $(LDADD) $(SASLLIB) $(POSTLINK)
@@ -430,7 +430,7 @@ uip_mhl_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 uip_mhtest_SOURCES = uip/mhtest.c uip/mhparse.c uip/mhcachesbr.c \
                     uip/mhoutsbr.c uip/mhmisc.c uip/mhfree.c \
                     uip/md5.c
-uip_mhtest_LDADD = $(LDADD) $(TERMLIB) $(POSTLINK)
+uip_mhtest_LDADD = $(LDADD) $(TERMLIB) $(ICONVLIB) $(POSTLINK)
 
 uip_post_SOURCES = uip/post.c uip/aliasbr.c
 uip_post_LDADD = mts/libmts.a $(LDADD) $(SASLLIB) $(TLSLIB) $(POSTLINK)
index 0d4ef46..d6afe09 100644 (file)
@@ -376,11 +376,14 @@ int parse_header_attrs (const char *filename, const char *fieldname,
  * params      - Pointer to head of linked list of parameters.
  * offsetout   - The final line offset after all the parameters have been
  *               output.  May be NULL.
- *
+ * external    - If set, outputting an external-body type and will not
+ *               output a "body" parameter.
+ 
  * Returns a pointer to the resulting parameter string.  This string must
  * be free()'d by the caller.  Returns NULL on error.
  */
-char *output_params(size_t initialwidth, PM params, int *offsetout);
+char *output_params(size_t initialwidth, PM params, int *offsetout,
+                   int external);
 
 /*
  * Add a parameter to the parameter linked list.
@@ -396,4 +399,23 @@ char *output_params(size_t initialwidth, PM params, int *offsetout);
  */
 PM add_param(PM *first, PM *last, const char *name, const char *value);
 
+/*
+ * Retrieve a parameter value from a parameter linked list.  Convert to the
+ * local character set if required.
+ *
+ * Arguments are:
+ *
+ * first       - Pointer to head of parameter linked list.
+ * name                - Name of parameter.
+ * replace     - If characters in the parameter list cannot be converted to
+ *               the local character set, replace with this character.
+ * fetchonly   - If true, return pointer to original value, no conversion
+ *               performed.
+ *
+ * Returns parameter value if found, NULL otherwise.  Memory must be free()'d
+ * unless fetchonly is set.
+ */
+
+char *get_param(PM first, const char *name, char replace, int fetchonly);
+
 extern int checksw;    /* Add Content-MD5 field */
index 5e4a681..c962cf4 100755 (executable)
@@ -101,17 +101,18 @@ EOF
 check "$draft" "$expected"
 
 #
-# Try out both!
+# Try out attach with a filename with 8-bit characters
 #
 
+cp "${srcdir}/test/mhbuild/tiny.jpg" "$MH_TEST_DIR/tïny.jpg"
+
 cat > "$draft" <<EOF
 To: Mr Test <mrtest@example.com>
 cc:
 Fcc: +outbox
+Attach: $MH_TEST_DIR/tïny.jpg
 ------
 This is a test message
-#image/jpeg {attachment; filename="This is an ëxample of a rather long filename that is longer than would fit on a normal line.jpg"} \
-${srcdir}/test/mhbuild/tiny.jpg
 EOF
 
 run_prog mhbuild "$draft"
@@ -129,11 +130,9 @@ Content-Type: text/plain; charset="us-ascii"
 This is a test message
 
 ------- =_aaaaaaaaaa0
-Content-Type: image/jpeg
-Content-Disposition: attachment;
-       filename*0*=UTF-8''This%20is%20an%20%C3%ABxample%20of%20a%20rather;
-       filename*1=" long filename that is longer than would fit on a norma";
-       filename*2="l line.jpg"
+Content-Type: image/jpeg; name*=UTF-8''t%C3%AFny.jpg
+Content-Description: =?UTF-8?B?dMOvbnkuanBn?=
+Content-Disposition: attachment; filename*=UTF-8''t%C3%AFny.jpg
 Content-Transfer-Encoding: base64
 
 /9g=
index 0077f6d..0d20a67 100644 (file)
@@ -1528,7 +1528,6 @@ build_headers (CT ct, int header_encoding)
     int        cc, mailbody, extbody, len;
     char *np, *vp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
-    PM pm;
 
     /*
      * If message is type multipart, then add the multipart
@@ -1568,59 +1567,16 @@ build_headers (CT ct, int header_encoding)
      * Append the attribute/value pairs to
      * the end of the Content-Type line.
      */
-    for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
-       if (mailbody && !strcasecmp (pm->pm_name, "body"))
-           continue;
-
-       vp = add (";", vp);
-       len++;
-
-       /*
-        * According to RFC 2017, if we have a URL longer than 40 characters
-        * we have to break it across multiple lines
-        */
-
-       if (extbody && strcasecmp (pm->pm_name, "url") == 0) {
-           char *value = pm->pm_value;
-
-           /* 7 here refers to " url=\"\"" */
-           if (len + 1 + (cc = (min(MAXURLTOKEN, strlen(value)) + 7)) >=
-                                                               CPERLIN) {
-               vp = add ("\n\t", vp);
-               len = 8;
-           } else {
-               vp = add (" ", vp);
-               len++;
-           }
-
-           vp = add ("url=\"", vp);
-           len += 5;
 
-           while (strlen(value) > MAXURLTOKEN) {
-               strncpy(buffer, value, MAXURLTOKEN);
-               buffer[MAXURLTOKEN] = '\0';
-               vp = add (buffer, vp);
-               vp = add ("\n\t", vp);
-               value += MAXURLTOKEN;
-               len = 8;
-           }
+    if (ci->ci_first_pm) {
+       char *s = output_params(len, ci->ci_first_pm, &len, mailbody);
 
-           vp = add (value, vp);
-           vp = add ("\"", vp);
-           len += strlen(value) + 1;
-           continue;
-       }
+       if (!s)
+           adios(NULL, "Internal error: failed outputting Content-Type "
+                 "parameters");
 
-       snprintf (buffer, sizeof(buffer), "%s=\"%s\"", pm->pm_name, pm->pm_value);
-       if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
-           vp = add ("\n\t", vp);
-           len = 8;
-       } else {
-           vp = add (" ", vp);
-           len++;
-       }
-       vp = add (buffer, vp);
-       len += cc;
+       vp = add (s, vp);
+       free(s);
     }
 
     /*
@@ -1672,7 +1628,7 @@ build_headers (CT ct, int header_encoding)
     } else if (ct->c_dispo_type) {
        vp = concat (" ", ct->c_dispo_type, NULL);
        len = strlen(DISPO_FIELD) + strlen(vp) + 1;
-       np = output_params(len, ct->c_dispo_first, NULL);
+       np = output_params(len, ct->c_dispo_first, NULL, 0);
        vp = add(np, vp);
        vp = add("\n", vp);
        if (np)
index 5bbaf44..a9ad26e 100644 (file)
@@ -16,6 +16,9 @@
 #include <h/mime.h>
 #include <h/mhparse.h>
 #include <h/utils.h>
+#ifdef HAVE_ICONV
+# include <iconv.h>
+#endif /* HAVE_ICONV */
 
 
 extern int debugsw;
@@ -2298,7 +2301,7 @@ open7Bit (CT ct, char **file)
        fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
        len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
            + 1 + strlen (ci->ci_subtype);
-       buffer = output_params(len, ci->ci_first_pm, &len);
+       buffer = output_params(len, ci->ci_first_pm, &len, 0);
 
        if (buffer) {
            fputs (buffer, ce->ce_fp);
@@ -3433,7 +3436,7 @@ bad_quote:
  */
 
 char *
-output_params(size_t initialwidth, PM params, int *offsetout)
+output_params(size_t initialwidth, PM params, int *offsetout, int external)
 {
     char *paramout = NULL;
     char line[CPERLIN * 2], *q;
@@ -3446,6 +3449,9 @@ output_params(size_t initialwidth, PM params, int *offsetout)
        valoff = 0;
        q = line;
 
+       if (external && strcasecmp(params->pm_name, "body") == 0)
+           continue;
+
        if (strlen(params->pm_name) > CPERLIN) {
            advise(NULL, "Parameter name \"%s\" is too long", params->pm_name);
            if (paramout)
@@ -3849,3 +3855,108 @@ add_param(PM *first, PM *last, const char *name, const char *value)
 
     return pm;
 }
+
+/*
+ * Retrieve a parameter value from a parameter linked list.  If the parameter
+ * value needs converted to the local character set, do that now.
+ */
+
+char *
+get_param(PM first, const char *name, char replace, int fetchonly)
+{
+    while (first != NULL) {
+       if (strcasecmp(name, first->pm_name) == 0) {
+           if (fetchonly)
+               return first->pm_value;
+           else {
+               char convbuf[BUFSIZ];
+               size_t inbytes, outbytes = sizeof(convbuf);
+#ifdef HAVE_ICONV
+               int utf8;
+               iconv_t cd;
+               ICONV_CONST char *p;
+               char *q;
+#endif /* HAVE_ICONV */
+               if (!first->pm_charset ||
+                               check_charset(first->pm_charset,
+                                             strlen(first->pm_charset))) {
+                   /*
+                    * No conversion necessary
+                    */
+                   return getcpy(first->pm_value);
+               }
+#ifdef HAVE_ICONV
+               utf8 = strcasecmp(first->pm_charset, "UTF-8") == 0;
+
+               cd = iconv_open(get_charset(), first->pm_charset);
+               if (cd == (iconv_t) -1) {
+                   goto noconvert;
+               }
+
+               inbytes = strlen(first->pm_value);
+               outbytes = sizeof(convbuf);
+               p = first->pm_value;
+               q = convbuf;
+
+               while (inbytes) {
+                   if (iconv(cd, &p, &inbytes, &q, &outbytes) == -1) {
+                       if (errno != EILSEQ) {
+                           iconv_close(cd);
+                           goto noconvert;
+                       }
+                       /*
+                        * Reset shift state, substitute our character,
+                        * try to restart conversion.
+                        */
+                       iconv(cd, NULL, NULL, &q, &outbytes);
+                       if (outbytes == 0) {
+                           iconv_close(cd);
+                           goto noconvert;
+                       }
+                       *q++ = replace;
+                       outbytes--;
+                       if (outbytes == 0) {
+                           iconv_close(cd);
+                           goto noconvert;
+                       }
+                       if (utf8) {
+                           for (++p, --inbytes; inbytes > 0 &&
+                                       (((unsigned char) *q) & 0xc0) == 0x80;
+                                ++p, --inbytes)
+                               continue;
+                       } else {
+                           p++;
+                           inbytes--;
+                       }
+                   }
+               }
+
+               iconv_close(cd);
+
+               if (outbytes == 0)
+                   q--;
+               *q = '\0';
+
+               return getcpy(convbuf);
+#endif /* HAVE_ICONV */
+noconvert:
+               for (p = first->pm_value, q = convbuf; *p && outbytes > 1;
+                    p++, q++, outbytes--) {
+                   if (isascii((unsigned char) p) &&
+                                       isprint((unsigned char) p))
+                       *q = *p;
+                   else
+                       *q = replace;
+               }
+
+               *q = '\0';
+
+               return getcpy(convbuf);
+           }
+       }
+
+       first = first->pm_next;
+    }
+
+    return NULL;
+}