]> diplodocus.org Git - nmh/commitdiff
Beginnings of RFC 2231 support. Does not compile (yet).
authorKen Hornstein <kenh@pobox.com>
Wed, 12 Feb 2014 04:17:00 +0000 (23:17 -0500)
committerKen Hornstein <kenh@pobox.com>
Wed, 12 Feb 2014 04:17:00 +0000 (23:17 -0500)
h/mhparse.h
h/mime.h
uip/mhoutsbr.c
uip/mhparse.c

index 58c8a8a..3ff7c70 100644 (file)
@@ -52,7 +52,7 @@ struct Parameter {
     char *pm_value;    /* Parameter value */
     char *pm_charset;  /* Parameter character set (optional) */
     char *pm_lang;     /* Parameter language tag (optional) */
     char *pm_value;    /* Parameter value */
     char *pm_charset;  /* Parameter character set (optional) */
     char *pm_lang;     /* Parameter language tag (optional) */
-    PM   *pm_next;     /* Pointer to next element */
+    PM   pm_next;      /* Pointer to next element */
 };
 
 /*
 };
 
 /*
@@ -62,8 +62,8 @@ struct Parameter {
 struct CTinfo {
     char *ci_type;             /* content type     */
     char *ci_subtype;          /* content subtype  */
 struct CTinfo {
     char *ci_type;             /* content type     */
     char *ci_subtype;          /* content subtype  */
-    PM   *ci_first_pm;         /* Pointer to first MIME parameter */
-    PM   *ci_last_pm;          /* Pointer to last MIME parameter */
+    PM   ci_first_pm;          /* Pointer to first MIME parameter */
+    PM   ci_last_pm;           /* Pointer to last MIME parameter */
     char *ci_comment;          /* RFC-822 comments */
     char *ci_magic;
 };
     char *ci_comment;          /* RFC-822 comments */
     char *ci_magic;
 };
@@ -112,8 +112,8 @@ struct Content {
     char *c_descr;             /* Content-Description:              */
     char *c_dispo;             /* Content-Disposition:              */
     char *c_dispo_type;                /* Type of Content-Disposition       */
     char *c_descr;             /* Content-Description:              */
     char *c_dispo;             /* Content-Disposition:              */
     char *c_dispo_type;                /* Type of Content-Disposition       */
-    PM *c_dispo_first;         /* Pointer to first disposition parm */
-    PM *c_dispo_last;          /* Pointer to last disposition parm  */
+    PM c_dispo_first;          /* Pointer to first disposition parm */
+    PM c_dispo_last;           /* Pointer to last disposition parm  */
     char *c_partno;            /* within multipart content          */
 
     /* Content-Type info */
     char *c_partno;            /* within multipart content          */
 
     /* Content-Type info */
@@ -343,4 +343,20 @@ const char *ce_str (int);
 const struct str2init *get_ce_method (const char *);
 int parse_header_attrs (const char *, int, char **, CI, int *);
 
 const struct str2init *get_ce_method (const char *);
 int parse_header_attrs (const char *, int, char **, CI, int *);
 
+/*
+ * Given a linked list of parameters, build an output string for them.  This
+ * string is designed to be concatenated on an already-built header.
+ *
+ * Arguments are:
+ *
+ * initialwidth        - Current width of the header.  Used to compute when to wrap
+ *               parameters on the first line.  The following lines will
+ *               be prefixed by a tab (\t) character.
+ * params      - Pointer to head of linked list of parameters.
+ *
+ * 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);
+
 extern int checksw;    /* Add Content-MD5 field */
 extern int checksw;    /* Add Content-MD5 field */
index d152094..fad1cf8 100644 (file)
--- a/h/mime.h
+++ b/h/mime.h
                     && (c) != '/' && (c) != '['  && (c) != ']' \
                     && (c) != '?' && (c) != '=')
 
                     && (c) != '/' && (c) != '['  && (c) != ']' \
                     && (c) != '?' && (c) != '=')
 
+/*
+ * Definitions for RFC 2231 encoding
+ */
+#define istspecial(c)  ((c) == '(' || (c) == ')' || (c) == '<' || (c) == '>' \
+                       || (c) == '@' || (c) == ',' || (c) == ';' \
+                       || (c) == ':' || (c) == '\\' || (c) == '"' \
+                       || (c) == '/' || (c) == '[' || (c) == ']' \
+                       || (c) == '?' || (c) == '=')
+
+#define isparamencode(c)  (!isascii((unsigned char) c) || \
+                          iscntrl((unsigned char) c) || istspecial(c) || \
+                          (c) == '*' || (c) == '\'' || (c) == '%')
+
 #define        MAXTEXTPERLN 78
 #define        MAXLONGLINE 998
 #define        CPERLIN 76
 #define        MAXTEXTPERLN 78
 #define        MAXLONGLINE 998
 #define        CPERLIN 76
index 88f610d..7c3cbe9 100644 (file)
@@ -81,11 +81,12 @@ output_content (CT ct, FILE *out)
 {
     int result = 0;
     CI ci = &ct->c_ctinfo;
 {
     int result = 0;
     CI ci = &ct->c_ctinfo;
-    char *boundary = ci->ci_values[0], **ap, **vp;
+    PM pm;
+    char *boundary = "";
 
 
-    for (ap = ci->ci_attrs, vp = ci->ci_values; *ap; ++ap, ++vp) {
-        if (! strcasecmp ("boundary", *ap)) {
-            boundary = *vp;
+    for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+        if (! strcasecmp ("boundary", pm->pm_name)) {
+            boundary = pm->pm_value;
             break;
         }
     }
             break;
         }
     }
@@ -163,7 +164,7 @@ output_content (CT ct, FILE *out)
               body, don't emit the newline that would appear between
               the headers and body.  In that case, the call to
               write8Bit() shouldn't be needed, but is harmless. */
               body, don't emit the newline that would appear between
               the headers and body.  In that case, the call to
               write8Bit() shouldn't be needed, but is harmless. */
-           if (ct->c_ctinfo.ci_attrs[0] != NULL  ||
+           if (ct->c_ctinfo.ci_first_pm != NULL  ||
                ct->c_begin != ct->c_end) {
                putc ('\n', out);
            }
                ct->c_begin != ct->c_end) {
                putc ('\n', out);
            }
@@ -226,7 +227,8 @@ output_headers (CT ct, FILE *out)
 static int
 writeExternalBody (CT ct, FILE *out)
 {
 static int
 writeExternalBody (CT ct, FILE *out)
 {
-    char **ap, **ep, *cp;
+    char *cp;
+    PM pm;
     struct exbody *e = (struct exbody *) ct->c_ctparams;
                
     putc ('\n', out);
     struct exbody *e = (struct exbody *) ct->c_ctparams;
                
     putc ('\n', out);
@@ -246,17 +248,17 @@ writeExternalBody (CT ct, FILE *out)
                continue;
 
            case 'N':
                continue;
 
            case 'N':
-               for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
-                   if (!strcasecmp (*ap, "name")) {
-                       fprintf (out, "%s", *ep);
+               for (pm = ci2->ci_first_pm; pm; pm = pm->pm_next)
+                   if (!strcasecmp (pm->pm_name, "name")) {
+                       fprintf (out, "%s", pm->pm_value);
                        break;
                    }
                continue;
 
            case 'T':
                fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
                        break;
                    }
                continue;
 
            case 'T':
                fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype);
-               for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++)
-                   fprintf (out, "; %s=\"%s\"", *ap, *ep);
+               for (pm = ci2->ci_first_pm; pm; pm = pm->pm_next)
+                   fprintf (out, "; %s=\"%s\"", pm->pm_name, pm->pm_value);
                continue;
 
            case 'n':
                continue;
 
            case 'n':
index 8af2690..4509e76 100644 (file)
@@ -135,6 +135,7 @@ static int readDigest (CT, char *);
 static int get_leftover_mp_content (CT, int);
 static int InitURL (CT);
 static int openURL (CT, char **);
 static int get_leftover_mp_content (CT, int);
 static int InitURL (CT);
 static int openURL (CT, char **);
+static size_t param_len(PM, int, size_t, int *);
 
 struct str2init str2cts[] = {
     { "application", CT_APPLICATION, InitApplication },
 
 struct str2init str2cts[] = {
     { "application", CT_APPLICATION, InitApplication },
@@ -1007,7 +1008,8 @@ InitText (CT ct)
 {
     char buffer[BUFSIZ];
     char *chset = NULL;
 {
     char buffer[BUFSIZ];
     char *chset = NULL;
-    char **ap, **ep, *cp;
+    char *cp;
+    PM pm;
     struct k2v *kv;
     struct text *t;
     CI ci = &ct->c_ctinfo;
     struct k2v *kv;
     struct text *t;
     CI ci = &ct->c_ctinfo;
@@ -1028,13 +1030,13 @@ InitText (CT ct)
     ct->c_ctparams = (void *) t;
 
     /* scan for charset parameter */
     ct->c_ctparams = (void *) t;
 
     /* scan for charset parameter */
-    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
-       if (!strcasecmp (*ap, "charset"))
+    for (pm = ci->ci_first_pm; pm; pm = pm->pm_next)
+       if (!strcasecmp (pm->pm_name, "charset"))
            break;
 
     /* check if content specified a character set */
            break;
 
     /* check if content specified a character set */
-    if (*ap) {
-       chset = *ep;
+    if (pm) {
+       chset = pm->pm_value;
        t->tx_charset = CHARSET_SPECIFIED;
     } else {
        t->tx_charset = CHARSET_UNSPECIFIED;
        t->tx_charset = CHARSET_SPECIFIED;
     } else {
        t->tx_charset = CHARSET_UNSPECIFIED;
@@ -1066,7 +1068,8 @@ InitMultiPart (CT ct)
 {
     int        inout;
     long last, pos;
 {
     int        inout;
     long last, pos;
-    char *cp, *dp, **ap, **ep;
+    char *cp, *dp;
+    PM pm;
     char *bp, buffer[BUFSIZ];
     struct multipart *m;
     struct k2v *kv;
     char *bp, buffer[BUFSIZ];
     struct multipart *m;
     struct k2v *kv;
@@ -1111,15 +1114,15 @@ InitMultiPart (CT ct)
      * required for multipart messages.
      */
     bp = 0;
      * required for multipart messages.
      */
     bp = 0;
-    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-       if (!strcasecmp (*ap, "boundary")) {
-           bp = *ep;
+    for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+       if (!strcasecmp (pm->pm_name, "boundary")) {
+           bp = pm->pm_value;
            break;
        }
     }
 
     /* complain if boundary parameter is missing */
            break;
        }
     }
 
     /* complain if boundary parameter is missing */
-    if (!*ap) {
+    if (!pm) {
        advise (NULL,
                "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
        advise (NULL,
                "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
                ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
@@ -1324,7 +1327,7 @@ InitMessage (CT ct)
 
        case MESSAGE_PARTIAL:
            {
 
        case MESSAGE_PARTIAL:
            {
-               char **ap, **ep;
+               PM pm;
                struct partial *p;
 
                if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
                struct partial *p;
 
                if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
@@ -1332,25 +1335,25 @@ InitMessage (CT ct)
                ct->c_ctparams = (void *) p;
 
                /* scan for parameters "id", "number", and "total" */
                ct->c_ctparams = (void *) p;
 
                /* scan for parameters "id", "number", and "total" */
-               for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-                   if (!strcasecmp (*ap, "id")) {
-                       p->pm_partid = add (*ep, NULL);
+               for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+                   if (!strcasecmp (pm->pm_name, "id")) {
+                       p->pm_partid = add (pm->pm_value, NULL);
                        continue;
                    }
                        continue;
                    }
-                   if (!strcasecmp (*ap, "number")) {
-                       if (sscanf (*ep, "%d", &p->pm_partno) != 1
+                   if (!strcasecmp (pm->pm_name, "number")) {
+                       if (sscanf (pm->pm_value, "%d", &p->pm_partno) != 1
                                || p->pm_partno < 1) {
 invalid_param:
                            advise (NULL,
                                    "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
                                || p->pm_partno < 1) {
 invalid_param:
                            advise (NULL,
                                    "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
-                                   *ap, ci->ci_type, ci->ci_subtype,
+                                   pm->pm_name, ci->ci_type, ci->ci_subtype,
                                    ct->c_file, TYPE_FIELD);
                            return NOTOK;
                        }
                        continue;
                    }
                                    ct->c_file, TYPE_FIELD);
                            return NOTOK;
                        }
                        continue;
                    }
-                   if (!strcasecmp (*ap, "total")) {
-                       if (sscanf (*ep, "%d", &p->pm_maxno) != 1
+                   if (!strcasecmp (pm->pm_name, "total")) {
+                       if (sscanf (pm->pm_value, "%d", &p->pm_maxno) != 1
                                || p->pm_maxno < 1)
                            goto invalid_param;
                        continue;
                                || p->pm_maxno < 1)
                            goto invalid_param;
                        continue;
@@ -1465,21 +1468,21 @@ no_body:
 int
 params_external (CT ct, int composing)
 {
 int
 params_external (CT ct, int composing)
 {
-    char **ap, **ep;
+    PM pm;
     struct exbody *e = (struct exbody *) ct->c_ctparams;
     CI ci = &ct->c_ctinfo;
 
     ct->c_ceopenfnx = NULL;
     struct exbody *e = (struct exbody *) ct->c_ctparams;
     CI ci = &ct->c_ctinfo;
 
     ct->c_ceopenfnx = NULL;
-    for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
-       if (!strcasecmp (*ap, "access-type")) {
+    for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
+       if (!strcasecmp (pm->pm_name, "access-type")) {
            struct str2init *s2i;
            CT p = e->eb_content;
 
            for (s2i = str2methods; s2i->si_key; s2i++)
            struct str2init *s2i;
            CT p = e->eb_content;
 
            for (s2i = str2methods; s2i->si_key; s2i++)
-               if (!strcasecmp (*ep, s2i->si_key))
+               if (!strcasecmp (pm->pm_value, s2i->si_key))
                    break;
            if (!s2i->si_key) {
                    break;
            if (!s2i->si_key) {
-               e->eb_access = *ep;
+               e->eb_access = pm->pm_value;
                e->eb_flags = NOTOK;
                p->c_encoding = CE_EXTERNAL;
                continue;
                e->eb_flags = NOTOK;
                p->c_encoding = CE_EXTERNAL;
                continue;
@@ -1493,46 +1496,46 @@ params_external (CT ct, int composing)
                return NOTOK;
            continue;
        }
                return NOTOK;
            continue;
        }
-       if (!strcasecmp (*ap, "name")) {
-           e->eb_name = *ep;
+       if (!strcasecmp (pm->pm_name, "name")) {
+           e->eb_name = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "permission")) {
-           e->eb_permission = *ep;
+       if (!strcasecmp (pm->pm_name, "permission")) {
+           e->eb_permission = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "site")) {
-           e->eb_site = *ep;
+       if (!strcasecmp (pm->pm_name, "site")) {
+           e->eb_site = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "directory")) {
-           e->eb_dir = *ep;
+       if (!strcasecmp (pm->pm_name, "directory")) {
+           e->eb_dir = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "mode")) {
-           e->eb_mode = *ep;
+       if (!strcasecmp (pm->pm_name, "mode")) {
+           e->eb_mode = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "size")) {
-           sscanf (*ep, "%lu", &e->eb_size);
+       if (!strcasecmp (pm->pm_name, "size")) {
+           sscanf (pm->pm_value, "%lu", &e->eb_size);
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "server")) {
-           e->eb_server = *ep;
+       if (!strcasecmp (pm->pm_name, "server")) {
+           e->eb_server = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "subject")) {
-           e->eb_subject = *ep;
+       if (!strcasecmp (pm->pm_name, "subject")) {
+           e->eb_subject = pm->pm_value;
            continue;
        }
            continue;
        }
-       if (!strcasecmp (*ap, "url")) {
+       if (!strcasecmp (pm->pm_name, "url")) {
            /*
             * According to RFC 2017, we have to remove all whitespace from
             * the URL
             */
 
            /*
             * According to RFC 2017, we have to remove all whitespace from
             * the URL
             */
 
-           char *u, *p = *ep;
-           e->eb_url = u = mh_xmalloc(strlen(*ep) + 1);
+           char *u, *p = pm->pm_value;
+           e->eb_url = u = mh_xmalloc(strlen(pm->pm_value) + 1);
 
            for (; *p != '\0'; p++) {
                if (! isspace((unsigned char) *p))
 
            for (; *p != '\0'; p++) {
                if (! isspace((unsigned char) *p))
@@ -1542,8 +1545,8 @@ params_external (CT ct, int composing)
            *u = '\0';
            continue;
        }
            *u = '\0';
            continue;
        }
-       if (composing && !strcasecmp (*ap, "body")) {
-           e->eb_body = getcpy (*ep);
+       if (composing && !strcasecmp (pm->pm_name, "body")) {
+           e->eb_body = getcpy (pm->pm_value);
            continue;
        }
     }
            continue;
        }
     }
@@ -2206,18 +2209,18 @@ open7Bit (CT ct, char **file)
     }
 
     if (ct->c_type == CT_MULTIPART) {
     }
 
     if (ct->c_type == CT_MULTIPART) {
-       char **ap, **ep;
+       PM pm;
        CI ci = &ct->c_ctinfo;
 
        len = 0;
        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);
        CI ci = &ct->c_ctinfo;
 
        len = 0;
        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);
-       for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
+       for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) {
            putc (';', ce->ce_fp);
            len++;
 
            putc (';', ce->ce_fp);
            len++;
 
-           snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
+           snprintf (buffer, sizeof(buffer), "%s=\"%s\"", , *ep);
 
            if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
                fputs ("\n\t", ce->ce_fp);
 
            if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
                fputs ("\n\t", ce->ce_fp);
@@ -3351,3 +3354,124 @@ bad_quote:
     *header_attrp = cp;
     return OK;
 }
     *header_attrp = cp;
     return OK;
 }
+
+/*
+ * Create a string based on a list of output parameters.  Assume that this
+ * parameter string will be appended to an existing header, so start out
+ * with the separator (;).  Perform RFC 2231 encoding when necessary.
+ */
+
+char *
+output_params(size_t initialwidth, PM params)
+{
+    char *paramout = NULL;
+    char line[CPERLIN + 1], *q;
+    int curlen, index, eightbit;
+    size_t valoff;
+
+    while (params != NULL) {
+       index = 0;
+       valoff = 0;
+       q = line;
+
+       curlen = param_len(params, index, valoff, &eightbit);
+
+       /*
+        * If this won't fit on the line, start a new one.  Save room in
+        * case we need a semicolon on the end
+        */
+
+       if (initialwidth + curlen > CPERLIN - 1) {
+           paramout = add(";\n\t", paramout);
+           initialwidth = 8;
+       }
+
+       /*
+        * Loop until we get a parameter that fits within a line.
+        */
+
+       while (initialwidth + curlen > CPERLIN - 1) {
+           int curvallen = strlen(params->pm_value + valoff) -
+                               (initialwidth + curlen - (CPERLIN - 1));
+
+           /*
+            * curvallen holds how many characters we take from this
+            * current value.  Make sure it's at least 1.
+            */
+
+           if (curvallen < 1)
+               curvallen = 1;
+       }
+       params = params->pm_next;
+    }
+
+    return paramout;
+}
+
+/*
+ * Calculate the size of a parameter.  Include any necessary encoding.
+ * Start the length computation from where "offset" is marked.
+ */
+
+static size_t
+param_len(PM pm, int index, size_t valueoff, int *eightbit)
+{
+    char *start = pm->pm_value + valueoff, *p;
+    size_t len = 0;
+
+    *eightbit = 0;
+
+    /*
+     * Add up the length.  First, start with the parameter name, and include
+     * the equal sign.
+     */
+
+    len += strlen(pm->pm_name) + 1;
+
+    /*
+     * Scan the parameter value.  If we find an 8-bit character, then
+     * we need to compute the locale name for the length.
+     */
+
+    for (p = start; *p != '\0'; p++) {
+       if (! isascii((unsigned char) *p)) {
+           *eightbit = 1;
+           break;
+       }
+    }
+
+    /*
+     * If we've got 8-bit character, put the locale on the front (if we're
+     * doing part 0.  Also compute the length of the string based on the
+     * encoding we need to do.
+     */
+
+    if (*eightbit) {
+       len++;          /* For the encoding we need to do */
+       if (index == 0) {
+           len += strlen(write_charset_8bit()) + 2;    /* Plus extra '' */
+       } else {
+           /*
+            * We know we definitely need to include an index.
+            * This will get the length wrong if we have more than 99
+            * sections. I can live with that.
+            */
+           len += 2;   /* *<N> */
+           if (index > 9)
+               len++;
+       }
+       for (p = start; *p != '\0'; p++) {
+           if (isparamencode(*p))
+               len += 3;
+           else
+               len++;
+       }
+    } else {
+       /*
+        * Much simpler!  Don't forget opening and closing quotes.
+        */
+       len += strlen(start) + 2;
+    }
+
+    return len;
+}