X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/2f00e89cc181a9dc9206f2c613789a2e70be1214..79f7ee461078bd837632b2ca9fdf94ab4a080a36:/uip/mhparse.c diff --git a/uip/mhparse.c b/uip/mhparse.c index 371d0c92..c381bb85 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -109,7 +109,7 @@ void free_encoding (CT, int); * static prototypes */ static CT get_content (FILE *, char *, int); -static int get_comment (const char *, CI, char **, int); +static int get_comment (const char *, const char *, char **, char **); static int InitGeneric (CT); static int InitText (CT); @@ -135,6 +135,10 @@ static int readDigest (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 *); +static size_t encode_param(PM, char *, size_t, size_t, size_t, int); +static size_t normal_param(PM, char *, size_t, size_t, size_t); +static int get_dispo (char *, CT, int); struct str2init str2cts[] = { { "application", CT_APPLICATION, InitApplication }, @@ -377,7 +381,7 @@ get_content (FILE *in, char *file, int toplevel) fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp); if (*cp == '(' && - get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK) + get_comment (ct->c_file, VRSN_FIELD, &cp, NULL) == NOTOK) goto out; for (dp = cp; istoken (*dp); dp++) @@ -487,7 +491,7 @@ get_content (FILE *in, char *file, int toplevel) fprintf (stderr, "%s: %s\n", MD5_FIELD, cp); if (*cp == '(' && - get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK) { + get_comment (ct->c_file, MD5_FIELD, &cp, NULL) == NOTOK) { free (ep); goto out; } @@ -510,7 +514,8 @@ get_content (FILE *in, char *file, int toplevel) } else if (!strcasecmp (hp->name, DISPO_FIELD)) { /* Get Content-Disposition field */ - ct->c_dispo = add (hp->value, ct->c_dispo); + if (get_dispo(hp->value, ct, 0) == NOTOK) + goto out; } next_header: @@ -676,14 +681,12 @@ extract_name_value (char *name_suffix, char *value) { int get_ctinfo (char *cp, CT ct, int magic) { - int i; char *dp; char c; CI ci; int status; ci = &ct->c_ctinfo; - i = strlen (invo_name) + 2; /* store copy of Content-Type line */ cp = ct->c_ctline = add (cp, NULL); @@ -704,7 +707,8 @@ get_ctinfo (char *cp, CT ct, int magic) if (debugsw) fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp); - if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK) + if (*cp == '(' && get_comment (ct->c_file, TYPE_FIELD, &cp, + &ci->ci_comment) == NOTOK) return NOTOK; for (dp = cp; istoken (*dp); dp++) @@ -727,7 +731,8 @@ get_ctinfo (char *cp, CT ct, int magic) while (isspace ((unsigned char) *cp)) cp++; - if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK) + if (*cp == '(' && get_comment (ct->c_file, TYPE_FIELD, &cp, + &ci->ci_comment) == NOTOK) return NOTOK; if (*cp != '/') { @@ -740,7 +745,8 @@ get_ctinfo (char *cp, CT ct, int magic) while (isspace ((unsigned char) *cp)) cp++; - if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK) + if (*cp == '(' && get_comment (ct->c_file, TYPE_FIELD, &cp, + &ci->ci_comment) == NOTOK) return NOTOK; for (dp = cp; istoken (*dp); dp++) @@ -765,11 +771,14 @@ magic_skip: while (isspace ((unsigned char) *cp)) cp++; - if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK) + if (*cp == '(' && get_comment (ct->c_file, TYPE_FIELD, &cp, + &ci->ci_comment) == NOTOK) return NOTOK; - if (parse_header_attrs (ct->c_file, i, &cp, ci, &status) == NOTOK) { - return status; + if ((status = parse_header_attrs (ct->c_file, TYPE_FIELD, &cp, + &ci->ci_first_pm, &ci->ci_last_pm, + &ci->ci_comment)) != OK) { + return status == NOTOK ? NOTOK : OK; } /* @@ -828,7 +837,7 @@ magic_skip: * Get any {Content-Disposition} given in buffer. */ if (magic && *cp == '{') { - ct->c_dispo = ++cp; + ++cp; for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) if (*dp == '}') break; @@ -840,10 +849,10 @@ magic_skip: c = *dp; *dp = '\0'; - if (*ct->c_dispo) - ct->c_dispo = concat (ct->c_dispo, "\n", NULL); - else - ct->c_dispo = NULL; + + if (get_dispo(cp, ct, 1) != OK) + return NOTOK; + *dp++ = c; cp = dp; @@ -914,16 +923,90 @@ magic_skip: } else advise (NULL, - "extraneous information in message %s's %s: field\n%*.*s(%s)", - ct->c_file, TYPE_FIELD, i, i, "", cp); + "extraneous information in message %s's %s: field\n%*s(%s)", + ct->c_file, TYPE_FIELD, strlen(invo_name) + 2, "", cp); + } + + return OK; +} + + +/* + * Parse out a Content-Disposition header. A lot of this is cribbed from + * get_ctinfo(). + */ +static int +get_dispo (char *cp, CT ct, int buildflag) +{ + char *dp, *dispoheader; + char c; + int status; + + /* + * Save the whole copy of the Content-Disposition header, unless we're + * processing a mhbuild directive. A NULL c_dispo will be a flag to + * mhbuild that the disposition header needs to be generated at that + * time. + */ + + dispoheader = cp = add(cp, NULL); + + while (isspace ((unsigned char) *cp)) /* trim leading spaces */ + cp++; + + /* change newlines to spaces */ + for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) + *dp++ = ' '; + + /* trim trailing spaces */ + for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) + if (!isspace ((unsigned char) *dp)) + break; + *++dp = '\0'; + + if (debugsw) + fprintf (stderr, "%s: %s\n", DISPO_FIELD, cp); + + if (*cp == '(' && get_comment (ct->c_file, DISPO_FIELD, &cp, NULL) == + NOTOK) { + free(dispoheader); + return NOTOK; + } + + for (dp = cp; istoken (*dp); dp++) + continue; + c = *dp, *dp = '\0'; + ct->c_dispo_type = add (cp, NULL); /* store disposition type */ + *dp = c, cp = dp; + + if (*cp == '(' && get_comment (ct->c_file, DISPO_FIELD, &cp, NULL) == NOTOK) + return NOTOK; + + if ((status = parse_header_attrs (ct->c_file, DISPO_FIELD, &cp, + &ct->c_dispo_first, &ct->c_dispo_last, + NULL)) != OK) { + if (status == NOTOK) { + free(dispoheader); + return NOTOK; + } + } else if (*cp) { + advise (NULL, + "extraneous information in message %s's %s: field\n%*s(%s)", + ct->c_file, DISPO_FIELD, strlen(invo_name) + 2, "", cp); } + if (buildflag) + free(dispoheader); + else + ct->c_dispo = dispoheader; + return OK; } static int -get_comment (const char *filename, CI ci, char **ap, int istype) +get_comment (const char *filename, const char *fieldname, char **ap, + char **commentp) { int i; char *bp, *cp; @@ -938,7 +1021,7 @@ get_comment (const char *filename, CI ci, char **ap, int istype) case '\0': invalid: advise (NULL, "invalid comment in message %s's %s: field", - filename, istype ? TYPE_FIELD : VRSN_FIELD); + filename, fieldname); return NOTOK; case '\\': @@ -965,12 +1048,12 @@ invalid: } *bp = '\0'; - if (istype) { - if ((dp = ci->ci_comment)) { - ci->ci_comment = concat (dp, " ", buffer, NULL); + if (commentp) { + if ((dp = *commentp)) { + *commentp = concat (dp, " ", buffer, NULL); free (dp); } else { - ci->ci_comment = add (buffer, NULL); + *commentp = add (buffer, NULL); } } @@ -1007,7 +1090,8 @@ InitText (CT ct) { 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; @@ -1028,13 +1112,13 @@ InitText (CT ct) 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 */ - if (*ap) { - chset = *ep; + if (pm) { + chset = pm->pm_value; t->tx_charset = CHARSET_SPECIFIED; } else { t->tx_charset = CHARSET_UNSPECIFIED; @@ -1066,7 +1150,8 @@ InitMultiPart (CT ct) { 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; @@ -1111,15 +1196,15 @@ InitMultiPart (CT ct) * 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 */ - 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); @@ -1324,7 +1409,7 @@ InitMessage (CT ct) case MESSAGE_PARTIAL: { - char **ap, **ep; + PM pm; struct partial *p; if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL) @@ -1332,25 +1417,25 @@ InitMessage (CT ct) 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; } - 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", - *ap, ci->ci_type, ci->ci_subtype, + pm->pm_name, ci->ci_type, ci->ci_subtype, 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; @@ -1465,21 +1550,21 @@ no_body: 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; - 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++) - if (!strcasecmp (*ep, s2i->si_key)) + if (!strcasecmp (pm->pm_value, 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; @@ -1493,46 +1578,46 @@ params_external (CT ct, int composing) return NOTOK; continue; } - if (!strcasecmp (*ap, "name")) { - e->eb_name = *ep; + if (!strcasecmp (pm->pm_name, "name")) { + e->eb_name = pm->pm_value; continue; } - if (!strcasecmp (*ap, "permission")) { - e->eb_permission = *ep; + if (!strcasecmp (pm->pm_name, "permission")) { + e->eb_permission = pm->pm_value; continue; } - if (!strcasecmp (*ap, "site")) { - e->eb_site = *ep; + if (!strcasecmp (pm->pm_name, "site")) { + e->eb_site = pm->pm_value; continue; } - if (!strcasecmp (*ap, "directory")) { - e->eb_dir = *ep; + if (!strcasecmp (pm->pm_name, "directory")) { + e->eb_dir = pm->pm_value; continue; } - if (!strcasecmp (*ap, "mode")) { - e->eb_mode = *ep; + if (!strcasecmp (pm->pm_name, "mode")) { + e->eb_mode = pm->pm_value; 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; } - if (!strcasecmp (*ap, "server")) { - e->eb_server = *ep; + if (!strcasecmp (pm->pm_name, "server")) { + e->eb_server = pm->pm_value; continue; } - if (!strcasecmp (*ap, "subject")) { - e->eb_subject = *ep; + if (!strcasecmp (pm->pm_name, "subject")) { + e->eb_subject = pm->pm_value; continue; } - if (!strcasecmp (*ap, "url")) { + if (!strcasecmp (pm->pm_name, "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)) @@ -1542,8 +1627,8 @@ params_external (CT ct, int composing) *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; } } @@ -1677,7 +1762,7 @@ static int openBase64 (CT ct, char **file) { int bitno, cc, digested; - int fd, len, skip, own_ct_fp = 0; + int fd, len, skip, own_ct_fp = 0, text = ct->c_type == CT_TEXT; uint32_t bits; unsigned char value, b; char *cp, *ep, buffer[BUFSIZ]; @@ -1700,12 +1785,6 @@ openBase64 (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1723,9 +1802,22 @@ openBase64 (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -1789,17 +1881,20 @@ openBase64 (CT ct, char **file) test_end: if ((bitno -= 6) < 0) { b = (bits >> 16) & 0xff; - putc ((char) b, ce->ce_fp); + if (!text || b != '\r') + putc ((char) b, ce->ce_fp); if (digested) MD5Update (&mdContext, &b, 1); if (skip < 2) { b = (bits >> 8) & 0xff; - putc ((char) b, ce->ce_fp); + if (! text || b != '\r') + putc ((char) b, ce->ce_fp); if (digested) MD5Update (&mdContext, &b, 1); if (skip < 1) { b = bits & 0xff; - putc ((char) b, ce->ce_fp); + if (! text || b != '\r') + putc ((char) b, ce->ce_fp); if (digested) MD5Update (&mdContext, &b, 1); } @@ -1929,12 +2024,6 @@ openQuoted (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1952,9 +2041,22 @@ openQuoted (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2148,12 +2250,6 @@ open7Bit (CT ct, char **file) } if (*file == NULL) { - char *tempfile; - if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { - adios(NULL, "unable to create temporary file in %s", - get_temp_dir()); - } - ce->ce_file = add (tempfile, NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -2171,9 +2267,22 @@ open7Bit (CT ct, char **file) cp = context_find (buffer); } if (cp != NULL && *cp != '\0') { - if (! ce->ce_unlink) { - ce->ce_file = add (cp, ce->ce_file); - } + if (ce->ce_unlink) { + /* Create temporary file with filename extension. */ + if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } else if (*file == NULL) { + char *tempfile; + if ((tempfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) { + adios(NULL, "unable to create temporary file in %s", + get_temp_dir()); + } + ce->ce_file = add (tempfile, NULL); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2182,28 +2291,18 @@ open7Bit (CT ct, char **file) } if (ct->c_type == CT_MULTIPART) { - char **ap, **ep; CI ci = &ct->c_ctinfo; + char *buffer; 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++) { - putc (';', ce->ce_fp); - len++; - - snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); + buffer = output_params(len, ci->ci_first_pm, &len); - if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { - fputs ("\n\t", ce->ce_fp); - len = 8; - } else { - putc (' ', ce->ce_fp); - len++; - } - fprintf (ce->ce_fp, "%s", buffer); - len += cc; + if (buffer) { + fputs (buffer, ce->ce_fp); + free(buffer); } if (ci->ci_comment) { @@ -3208,32 +3307,22 @@ get_ce_method (const char *method) { } int -parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci, - int *status) { - char **attr = ci->ci_attrs; +parse_header_attrs (const char *filename, const char *fieldname, + char **header_attrp, PM *param_head, PM *param_tail, + char **commentp) +{ char *cp = *header_attrp; + PM pm; while (*cp == ';') { char *dp, *vp, *up, c; - /* Relies on knowledge of this declaration: - * char *ci_attrs[NPARMS + 2]; - */ - if (attr >= ci->ci_attrs + sizeof ci->ci_attrs/sizeof (char *) - 2) { - advise (NULL, - "too many parameters in message %s's %s: field (%d max)", - filename, TYPE_FIELD, NPARMS); - *status = NOTOK; - return NOTOK; - } - cp++; while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && - get_comment (filename, ci, &cp, 1) == NOTOK) { - *status = NOTOK; + get_comment (filename, fieldname, &cp, commentp) == NOTOK) { return NOTOK; } @@ -3241,9 +3330,8 @@ parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci, advise (NULL, "extraneous trailing ';' in message %s's %s: " "parameter list", - filename, TYPE_FIELD); - *status = OK; - return NOTOK; + filename, fieldname); + return DONE; } /* down case the attribute name */ @@ -3256,19 +3344,23 @@ parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci, if (dp == cp || *dp != '=') { advise (NULL, "invalid parameter in message %s's %s: " - "field\n%*.*sparameter %s (error detected at offset %d)", - filename, TYPE_FIELD, len, len, "", cp, dp - cp); - *status = NOTOK; + "field\n%*sparameter %s (error detected at offset %d)", + filename, fieldname, strlen(invo_name) + 2, "",cp, dp - cp); return NOTOK; } - vp = (*attr = add (cp, NULL)) + (up - cp); + pm = mh_xmalloc(sizeof(*pm)); + memset(pm, 0, sizeof(*pm)); + + /* This is all mega-bozo and needs cleanup */ + vp = (pm->pm_name = add (cp, NULL)) + (up - cp); *vp = '\0'; for (dp++; isspace ((unsigned char) *dp);) dp++; /* Now store the attribute value. */ - ci->ci_values[attr - ci->ci_attrs] = vp = *attr + (dp - cp); + + vp = pm->pm_name + (dp - cp); if (*dp == '"') { for (cp = ++dp, dp = vp;;) { @@ -3277,9 +3369,9 @@ parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci, bad_quote: advise (NULL, "invalid quoted-string in message %s's %s: " - "field\n%*.*s(parameter %s)", - filename, TYPE_FIELD, len, len, "", *attr); - *status = NOTOK; + "field\n%*s(parameter %s)", + filename, fieldname, strlen(invo_name) + 2, "", + pm->pm_name); return NOTOK; case '\\': @@ -3303,12 +3395,13 @@ bad_quote: continue; *dp = '\0'; } + pm->pm_value = getcpy(vp); if (!*vp) { advise (NULL, "invalid parameter in message %s's %s: " - "field\n%*.*s(parameter %s)", - filename, TYPE_FIELD, len, len, "", *attr); - *status = NOTOK; + "field\n%*s(parameter %s)", + filename, fieldname, strlen(invo_name) + 2, "", + pm->pm_name); return NOTOK; } @@ -3316,14 +3409,397 @@ bad_quote: cp++; if (*cp == '(' && - get_comment (filename, ci, &cp, 1) == NOTOK) { - *status = NOTOK; + get_comment (filename, fieldname, &cp, commentp) == NOTOK) { return NOTOK; } - ++attr; + if (*param_head == NULL) { + *param_head = pm; + *param_tail = pm; + } else { + (*param_tail)->pm_next = pm; + *param_tail = pm; + } } *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, int *offsetout) +{ + char *paramout = NULL; + char line[CPERLIN * 2], *q; + int curlen, index, eightbit, encode, i; + size_t valoff; + + while (params != NULL) { + encode = 0; + index = 0; + valoff = 0; + q = line; + + if (strlen(params->pm_name) > CPERLIN) { + advise(NULL, "Parameter name \"%s\" is too long", params->pm_name); + if (paramout) + free(paramout); + return NULL; + } + + curlen = param_len(params, index, valoff, &eightbit); + + /* + * Loop until we get a parameter that fits within a line. We + * assume new lines start with a tab, so check our overflow based + * on that. + */ + + while (curlen + 8 > CPERLIN - 1) { + int curvallen = strlen(params->pm_value + valoff) - + (curlen + 8 - (CPERLIN - 1)); + + *q++ = ';'; + *q++ = '\n'; + *q++ = '\t'; + + /* + * curvallen holds how many characters we take from this + * current value. Make sure it's at least 1. + */ + + if (curvallen < 1) + curvallen = 1; + + /* + * At this point we're definitely continuing the line, so + * be sure to include the parameter name and section index. + */ + + q += snprintf(q, sizeof(line) - (q - line), "%s*%d", + params->pm_name, index); + + /* + * If eightbit was set and we're on index 0, we need to include + * the character set and encode the first section. Otherwise + * only encode if the section we're on contains an 8bit character. + */ + + if (eightbit && index == 0) + encode = 1; + else if (eightbit && contains8bit(params->pm_value + valoff, + params->pm_value + valoff + curvallen)) + encode = 1; + + /* + * Both of these functions do a NUL termination + */ + + if (encode) + i = encode_param(params, q, sizeof(line) - (q - line), + curvallen, valoff, index); + else + i = normal_param(params, q, sizeof(line) - (q - line), + curvallen, valoff); + + if (i == 0) { + if (paramout) + free(paramout); + return NULL; + } + + valoff += curvallen; + index++; + curlen = param_len(params, index, valoff, &eightbit); + q = line; + + /* + * "line" starts with a ;\n\t, so that doesn't count against + * the length. But add 8 since it starts with a tab; that's + * how we end up with 5. + */ + + initialwidth = strlen(line) + 5; + + /* + * At this point the line should be built, so add it to our + * current output buffer. + */ + + paramout = add(line, paramout); + } + + /* + * 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) { + *q++ = ';'; + *q++ = '\n'; + *q++ = '\t'; + initialwidth = 8; + } else { + *q++ = ';'; + *q++ = ' '; + initialwidth += 2; + } + + /* + * At this point, we're either finishing a contined parameter, or + * we're working on a new one. + */ + + if (index > 0) { + q += snprintf(q, sizeof(line) - (q - line), "%s*%d", + params->pm_name, index); + } else { + strncpy(q, params->pm_name, sizeof(line) - (q - line)); + q += strlen(q); + } + + if (eightbit) + i = encode_param(params, q, sizeof(line) - (q - line), + strlen(params->pm_value + valoff), valoff, index); + else + i = normal_param(params, q, sizeof(line) - (q - line), + strlen(params->pm_value + valoff), valoff); + + if (i == 0) { + if (paramout) + free(paramout); + return NULL; + } + + paramout = add(line, paramout); + initialwidth += strlen(line); + + params = params->pm_next; + } + + if (offsetout) + *offsetout = initialwidth; + + 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; + + /* + * 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. + */ + + *eightbit = contains8bit(start, NULL); + + /* + * 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) { + /* + * If we don't have a charset or language tag in this parameter, + * add them now. + */ + + if (! pm->pm_charset) + pm->pm_charset = getcpy(write_charset_8bit()); + if (! pm->pm_lang) + pm->pm_lang = getcpy(NULL); /* Default to a blank lang tag */ + + len++; /* For the encoding we need to do */ + if (index == 0) { + len += strlen(pm->pm_charset) + strlen(pm->pm_lang) + 2; + } 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; /* * */ + if (index > 9) + len++; + } + for (p = start; *p != '\0'; p++) { + if (isparamencode(*p)) + len += 3; + else + len++; + } + } else { + /* + * Calculate the string length, but add room for quoting \ + * and " if necessary. Also account for quotes at beginning + * and end. + */ + for (p = start; *p != '\0'; p++) { + switch (*p) { + case '"': + case '\\': + len++; + /* FALL THROUGH */ + default: + len++; + } + } + + len += 2; + } + + return len; +} + +/* + * Output an encoded parameter string. + */ + +static size_t +encode_param(PM pm, char *output, size_t len, size_t valuelen, + size_t valueoff, int index) +{ + size_t outlen = 0, n; + char *endptr = output + len, *p; + + /* + * First, output the marker for an encoded string. + */ + + *output++ = '*'; + *output++ = '='; + outlen += 2; + + /* + * If the index is 0, output the character set and language tag. + * If theses were NULL, they should have already been filled in + * by param_len(). + */ + + if (index == 0) { + n = snprintf(output, len - outlen, "%s'%s'", pm->pm_charset, + pm->pm_lang); + output += n; + outlen += n; + if (output > endptr) { + advise(NULL, "Internal error: parameter buffer overflow"); + return 0; + } + } + + /* + * Copy over the value, encoding if necessary + */ + + p = pm->pm_value + valueoff; + while (valuelen-- > 0) { + if (isparamencode(*p)) { + n = snprintf(output, len - outlen, "%%%02X", (unsigned char) *p++); + output += n; + outlen += n; + } else { + *output++ = *p++; + outlen++; + } + if (output > endptr) { + advise(NULL, "Internal error: parameter buffer overflow"); + return 0; + } + } + + *output = '\0'; + + return outlen; +} + +/* + * Output a "normal" parameter, without encoding. Be sure to escape + * quotes and backslashes if necessary. + */ + +static size_t +normal_param(PM pm, char *output, size_t len, size_t valuelen, + size_t valueoff) +{ + size_t outlen = 0; + char *endptr = output + len, *p; + + *output++ = '='; + *output++ = '"'; + outlen += 2; + + p = pm->pm_value + valueoff; + + while (valuelen-- > 0) { + switch (*p) { + case '\\': + case '"': + *output++ = '\\'; + outlen++; + default: + *output++ = *p++; + outlen++; + } + if (output > endptr) { + advise(NULL, "Internal error: parameter buffer overflow"); + return 0; + } + } + + if (output - 2 > endptr) { + advise(NULL, "Internal error: parameter buffer overflow"); + return 0; + } + + *output++ = '"'; + *output++ = '\0'; + + return outlen + 1; +} + +/* + * Add a parameter to the parameter linked list + */ + +PM +add_param(PM *first, PM *last, const char *name, const char *value) +{ + PM pm = mh_xmalloc(sizeof(*pm)); + + memset(pm, 0, sizeof(*pm)); + + pm->pm_name = getcpy(name); + pm->pm_value = getcpy(value); + + if (*first) { + (*last)->pm_next = pm; + *last = pm; + } else { + *first = pm; + *last = pm; + } + + return pm; +}