X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/64de90c03d49e3484f0e00818f9d8559a1292065..fb6b8301237bcdd72bb86d34af68f378a07e4d70:/uip/mhparse.c?ds=inline diff --git a/uip/mhparse.c b/uip/mhparse.c index 9cd38f99..85c32924 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -23,8 +22,6 @@ extern int debugsw; -extern pid_t xpid; /* mhshowsbr.c */ - /* cache policies */ extern int rcachesw; /* mhcachesbr.c */ extern int wcachesw; /* mhcachesbr.c */ @@ -36,10 +33,24 @@ int checksw = 0; /* check Content-MD5 field */ * 1) Instruct parser not to detect invalid Content-Transfer-Encoding * in a multipart. * 2) Suppress the warning about bogus multipart content, and report it. + * 3) Suppress the warning about extraneous trailing ';' in header parameter + * lists. */ int skip_mp_cte_check; int suppress_bogus_mp_content_warning; int bogus_mp_content; +int suppress_extraneous_trailing_semicolon_warning; + +/* + * By default, suppress warning about multiple MIME-Version header fields. + */ +int suppress_multiple_mime_version_warning = 1; + +/* list of preferred type/subtype pairs, for -prefer */ +char *preferred_types[NPREFS], + *preferred_subtypes[NPREFS]; +int npreferred; + /* * Structures for TEXT messages @@ -61,6 +72,7 @@ struct k2v SubMultiPart[] = { { "alternative", MULTI_ALTERNATE }, { "digest", MULTI_DIGEST }, { "parallel", MULTI_PARALLEL }, + { "related", MULTI_RELATED }, { NULL, MULTI_UNKNOWN } /* this one must be last! */ }; @@ -101,7 +113,7 @@ static struct k2v EncodingType[] = { int find_cache (CT, int, int *, char *, char *, int); /* mhmisc.c */ -int part_ok (CT, int); +int part_ok (CT); int type_ok (CT, int); void content_error (char *, CT, char *, ...); @@ -117,7 +129,8 @@ static int get_comment (const char *, const char *, char **, char **); static int InitGeneric (CT); static int InitText (CT); static int InitMultiPart (CT); -void reverse_parts (CT); +static void reverse_parts (CT); +static void prefer_parts(CT ct); static int InitMessage (CT); static int InitApplication (CT); static int init_encoding (CT, OpenCEFunc); @@ -141,7 +154,6 @@ static int openURL (CT, char **); static int parse_header_attrs (const char *, const char *, char **, PM *, PM *, char **); static size_t param_len(PM, int, size_t, int *, int *, size_t *); -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); @@ -183,19 +195,6 @@ struct str2init str2methods[] = { }; -int -pidcheck (int status) -{ - if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT) - return status; - - fflush (stdout); - fflush (stderr); - done (1); - return 1; -} - - /* * Main entry point for parsing a MIME message or file. * It returns the Content structure for the top level @@ -209,6 +208,9 @@ parse_mime (char *file) char buffer[BUFSIZ]; FILE *fp; CT ct; + size_t n; + + bogus_mp_content = 0; /* * Check if file is actually standard input @@ -220,10 +222,15 @@ parse_mime (char *file) get_temp_dir()); return NULL; } - file = add (tfile, NULL); + file = mh_xstrdup(tfile); - while (fgets (buffer, sizeof(buffer), stdin)) - fputs (buffer, fp); + while ((n = fread(buffer, 1, sizeof(buffer), stdin)) > 0) { + if (fwrite(buffer, 1, n, fp) != n) { + (void) m_unlink (file); + advise (file, "error copying to temporary file"); + return NULL; + } + } fflush (fp); if (ferror (stdin)) { @@ -292,9 +299,7 @@ get_content (FILE *in, char *file, int toplevel) m_getfld_state_t gstate = 0; /* allocate the content structure */ - if (!(ct = (CT) calloc (1, sizeof(*ct)))) - adios (NULL, "out of memory"); - + NEW0(ct); ct->c_fp = in; ct->c_file = add (file, NULL); ct->c_begin = ftell (ct->c_fp) + 1; @@ -312,8 +317,8 @@ get_content (FILE *in, char *file, int toplevel) compnum++; /* get copies of the buffers */ - np = add (name, NULL); - vp = add (buf, NULL); + np = mh_xstrdup(name); + vp = mh_xstrdup(buf); /* if necessary, get rest of field */ while (state == FLDPLUS) { @@ -330,7 +335,15 @@ get_content (FILE *in, char *file, int toplevel) continue; case BODY: - ct->c_begin = ftell (in) - strlen (buf); + if (name[0] == ':') { + /* Special case: no blank line between header and body. The + file position indicator is on the newline at the end of the + line, but it needs to be one prior to the beginning of the + line. So subtract the length of the line, bufsz, plus 1. */ + ct->c_begin = ftell (in) - (bufsz + 1); + } else { + ct->c_begin = ftell (in) - (bufsz - 1); + } break; case FILEEOF: @@ -363,16 +376,12 @@ get_content (FILE *in, char *file, int toplevel) if (!strcasecmp (hp->name, VRSN_FIELD)) { int ucmp; char c, *cp, *dp; + char *vrsn; - if (ct->c_vrsn) { - advise (NULL, "message %s has multiple %s: fields", - ct->c_file, VRSN_FIELD); - goto next_header; - } - ct->c_vrsn = add (hp->value, NULL); + vrsn = add (hp->value, NULL); /* Now, cleanup this field */ - cp = ct->c_vrsn; + cp = vrsn; while (isspace ((unsigned char) *cp)) cp++; @@ -399,6 +408,14 @@ get_content (FILE *in, char *file, int toplevel) admonish (NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp); } + if (!ct->c_vrsn) { + ct->c_vrsn = vrsn; + } else { + if (! suppress_multiple_mime_version_warning) + advise (NULL, "message %s has multiple %s: fields", + ct->c_file, VRSN_FIELD); + free(vrsn); + } } else if (!strcasecmp (hp->name, TYPE_FIELD)) { /* Get Content-Type field */ @@ -577,7 +594,7 @@ add_header (CT ct, char *name, char *value) HF hp; /* allocate header field structure */ - hp = mh_xmalloc (sizeof(*hp)); + NEW(hp); /* link data into header structure */ hp->name = name; @@ -637,19 +654,15 @@ get_ctinfo (char *cp, CT ct, int magic) for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; - ci->ci_type = add (cp, NULL); /* store content type */ + ci->ci_type = mh_xstrdup(cp); /* store content type */ *dp = c, cp = dp; if (!*ci->ci_type) { - advise (NULL, "invalid %s: field in message %s (empty type)", + advise (NULL, "invalid %s: field in message %s (empty type)", TYPE_FIELD, ct->c_file); return NOTOK; } - - /* down case the content type string */ - for (dp = ci->ci_type; *dp; dp++) - if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp)) - *dp = tolower ((unsigned char) *dp); + to_lower(ci->ci_type); while (isspace ((unsigned char) *cp)) cp++; @@ -660,7 +673,7 @@ get_ctinfo (char *cp, CT ct, int magic) if (*cp != '/') { if (!magic) - ci->ci_subtype = add ("", NULL); + ci->ci_subtype = mh_xstrdup(""); goto magic_skip; } @@ -675,7 +688,7 @@ get_ctinfo (char *cp, CT ct, int magic) for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; - ci->ci_subtype = add (cp, NULL); /* store the content subtype */ + ci->ci_subtype = mh_xstrdup(cp); /* store the content subtype */ *dp = c, cp = dp; if (!*ci->ci_subtype) { @@ -684,11 +697,7 @@ get_ctinfo (char *cp, CT ct, int magic) TYPE_FIELD, ct->c_file, ci->ci_type); return NOTOK; } - - /* down case the content subtype string */ - for (dp = ci->ci_subtype; *dp; dp++) - if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp)) - *dp = tolower ((unsigned char) *dp); + to_lower(ci->ci_subtype); magic_skip: while (isspace ((unsigned char) *cp)) @@ -708,10 +717,8 @@ magic_skip: * Get any given in buffer */ if (magic && *cp == '<') { - if (ct->c_id) { - free (ct->c_id); - ct->c_id = NULL; - } + mh_xfree(ct->c_id); + ct->c_id = NULL; if (!(dp = strchr(ct->c_id = ++cp, '>'))) { advise (NULL, "invalid ID in message %s", ct->c_file); return NOTOK; @@ -829,7 +836,7 @@ magic_skip: */ if (*cp) { if (magic) { - ci->ci_magic = add (cp, NULL); + ci->ci_magic = mh_xstrdup(cp); /* If there is a Content-Disposition header and it doesn't have a *filename=, extract it from the magic contents. @@ -838,7 +845,7 @@ magic_skip: if (ct->c_dispo_type && !get_param(ct->c_dispo_first, "filename", '_', 1)) { add_param(&ct->c_dispo_first, &ct->c_dispo_last, "filename", - r1bindex(ci->ci_magic, '/')); + r1bindex(ci->ci_magic, '/'), 0); } } else @@ -896,7 +903,7 @@ get_dispo (char *cp, CT ct, int buildflag) for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; - ct->c_dispo_type = add (cp, NULL); /* store disposition type */ + ct->c_dispo_type = mh_xstrdup(cp); /* store disposition type */ *dp = c, cp = dp; if (*cp == '(' && get_comment (ct->c_file, DISPO_FIELD, &cp, NULL) == NOTOK) @@ -973,7 +980,7 @@ invalid: *commentp = concat (dp, " ", buffer, NULL); free (dp); } else { - *commentp = add (buffer, NULL); + *commentp = mh_xstrdup(buffer); } } @@ -1012,7 +1019,6 @@ InitText (CT ct) char *chset = NULL; char *cp; PM pm; - struct k2v *kv; struct text *t; CI ci = &ct->c_ctinfo; @@ -1021,14 +1027,10 @@ InitText (CT ct) ci->ci_subtype = add ("plain", ci->ci_subtype); /* match subtype */ - for (kv = SubText; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) - break; - ct->c_subtype = kv->kv_value; + ct->c_subtype = ct_str_subtype (CT_TEXT, ci->ci_subtype); /* allocate text character set structure */ - if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL) - adios (NULL, "out of memory"); + NEW0(t); ct->c_ctparams = (void *) t; /* scan for charset parameter */ @@ -1054,7 +1056,7 @@ InitText (CT ct) if (chset != NULL && !check_charset (chset, strlen (chset))) { snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset); if ((cp = context_find (buffer))) - ct->c_termproc = getcpy (cp); + ct->c_termproc = mh_xstrdup(cp); } return OK; @@ -1072,9 +1074,11 @@ InitMultiPart (CT ct) long last, pos; char *cp, *dp; PM pm; - char *bp, buffer[BUFSIZ]; + char *bp; + char *bufp = NULL; + size_t buflen; + ssize_t gotlen; struct multipart *m; - struct k2v *kv; struct part *part, **next; CI ci = &ct->c_ctinfo; CT p; @@ -1088,7 +1092,7 @@ InitMultiPart (CT ct) ct->c_encoding != CE_8BIT && ct->c_encoding != CE_BINARY) { /* Copy the Content-Transfer-Encoding header field body so we can remove any trailing whitespace and leading blanks from it. */ - char *cte = add (ct->c_celine ? ct->c_celine : "(null)", NULL); + char *cte = mh_xstrdup(ct->c_celine ? ct->c_celine : "(null)"); bp = cte + strlen (cte) - 1; while (bp >= cte && isspace ((unsigned char) *bp)) *bp-- = '\0'; @@ -1096,8 +1100,9 @@ InitMultiPart (CT ct) admonish (NULL, "\"%s/%s\" type in message %s must be encoded in\n" - "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround " - "is to\nmanually edit the file and change the \"%s\"\n" + "7bit, 8bit, or binary, per RFC 2045 (6.4). " + "mhfixmsg -fixcte can fix it, or\n" + "manually edit the file and change the \"%s\"\n" "Content-Transfer-Encoding to one of those. For now", ci->ci_type, ci->ci_subtype, ct->c_file, bp); free (cte); @@ -1106,10 +1111,7 @@ InitMultiPart (CT ct) } /* match subtype */ - for (kv = SubMultiPart; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) - break; - ct->c_subtype = kv->kv_value; + ct->c_subtype = ct_str_subtype (CT_MULTIPART, ci->ci_subtype); /* * Check for "boundary" parameter, which is @@ -1132,8 +1134,7 @@ InitMultiPart (CT ct) } /* allocate primary structure for multipart info */ - if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL) - adios (NULL, "out of memory"); + NEW0(m); ct->c_ctparams = (void *) m; /* check if boundary parameter contains only whitespace characters */ @@ -1166,24 +1167,24 @@ InitMultiPart (CT ct) part = NULL; inout = 1; - while (fgets (buffer, sizeof(buffer) - 1, fp)) { + while ((gotlen = getline(&bufp, &buflen, fp)) != -1) { if (pos > last) break; - pos += strlen (buffer); - if (buffer[0] != '-' || buffer[1] != '-') + pos += gotlen; + if (bufp[0] != '-' || bufp[1] != '-') continue; if (inout) { - if (strcmp (buffer + 2, m->mp_start)) + if (strcmp (bufp + 2, m->mp_start)) continue; next_part: - if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL) - adios (NULL, "out of memory"); + NEW0(part); *next = part; next = &part->mp_next; if (!(p = get_content (fp, ct->c_file, ct->c_subtype == MULTI_DIGEST ? -1 : 0))) { + free(bufp); ct->c_fp = NULL; return NOTOK; } @@ -1193,20 +1194,19 @@ next_part: fseek (fp, pos, SEEK_SET); inout = 0; } else { - if (strcmp (buffer + 2, m->mp_start) == 0) { + if (strcmp (bufp + 2, m->mp_start) == 0) { inout = 1; end_part: p = part->mp_part; - p->c_end = ftell(fp) - (strlen(buffer) + 1); + p->c_end = ftell(fp) - (gotlen + 1); if (p->c_end < p->c_begin) p->c_begin = p->c_end; if (inout) goto next_part; goto last_part; - } else { - if (strcmp (buffer + 2, m->mp_stop) == 0) - goto end_part; } + if (strcmp (bufp + 2, m->mp_stop) == 0) + goto end_part; } } @@ -1225,14 +1225,16 @@ end_part: continue; *next = NULL; free_content (p); - free ((char *) part); + free(part); } } last_part: /* reverse the order of the parts for multipart/alternative */ - if (ct->c_subtype == MULTI_ALTERNATE) + if (ct->c_subtype == MULTI_ALTERNATE) { reverse_parts (ct); + prefer_parts (ct); + } /* * label all subparts with part number, and @@ -1255,10 +1257,11 @@ last_part: p = part->mp_part; sprintf (pp, "%d", partnum); - p->c_partno = add (partnam, NULL); + p->c_partno = mh_xstrdup(partnam); /* initialize the content of the subparts */ if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) { + free(bufp); fclose (ct->c_fp); ct->c_fp = NULL; return NOTOK; @@ -1269,6 +1272,7 @@ last_part: get_leftover_mp_content (ct, 1); get_leftover_mp_content (ct, 0); + free(bufp); fclose (ct->c_fp); ct->c_fp = NULL; return OK; @@ -1276,10 +1280,16 @@ last_part: /* - * reverse the order of the parts of a multipart/alternative + * reverse the order of the parts of a multipart/alternative, + * presumably to put the "most favored" alternative first, for + * ease of choosing/displaying it later on. from a mail message on + * nmh-workers, from kenh: + * "Stock" MH 6.8.5 did not have a reverse_parts() function, but I + * see code in mhn that did the same thing... Acccording to the RCS + * logs, that code was around from the initial checkin of mhn.c by + * John Romine in 1992, which is as far back as we have." */ - -void +static void reverse_parts (CT ct) { struct multipart *m = (struct multipart *) ct->c_ctparams; @@ -1295,6 +1305,84 @@ reverse_parts (CT ct) } } +static void +move_preferred_part (CT ct, char *type, char *subtype) +{ + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part, *prev, *head, *nhead, *ntail; + struct part h, n; + CI ci; + + /* move the matching part(s) to the head of the list: walk the + * list of parts, move matching parts to a new list (maintaining + * their order), and finally, concatenate the old list onto the + * new. + */ + + head = &h; + nhead = &n; + + head->mp_next = m->mp_parts; + nhead->mp_next = NULL; + ntail = nhead; + + prev = head; + part = head->mp_next; + while (part != NULL) { + ci = &part->mp_part->c_ctinfo; + if (!strcasecmp(ci->ci_type, type) && + (!subtype || !strcasecmp(ci->ci_subtype, subtype))) { + prev->mp_next = part->mp_next; + part->mp_next = NULL; + ntail->mp_next = part; + ntail = part; + part = prev->mp_next; + } else { + prev = part; + part = prev->mp_next; + } + } + ntail->mp_next = head->mp_next; + m->mp_parts = nhead->mp_next; + +} + +/* + * move parts that match the user's preferences (-prefer) to the head + * of the line. process preferences in reverse so first one given + * ends up first in line + */ +static void +prefer_parts(CT ct) +{ + int i; + for (i = npreferred-1; i >= 0; i--) + move_preferred_part(ct, preferred_types[i], preferred_subtypes[i]); +} + + + +/* parse_mime() arranges alternates in reverse (priority) order. This + function can be used to reverse them back. This will put, for + example, a text/plain part before a text/html part in a + multipart/alternative part, for example, where it belongs. */ +void +reverse_alternative_parts (CT ct) { + if (ct->c_type == CT_MULTIPART) { + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part; + + if (ct->c_subtype == MULTI_ALTERNATE) { + reverse_parts (ct); + } + + /* And call recursively on each part of a multipart. */ + for (part = m->mp_parts; part; part = part->mp_next) { + reverse_alternative_parts (part->mp_part); + } + } +} + /* * MESSAGE @@ -1303,7 +1391,6 @@ reverse_parts (CT ct) static int InitMessage (CT ct) { - struct k2v *kv; CI ci = &ct->c_ctinfo; if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) { @@ -1318,10 +1405,7 @@ InitMessage (CT ct) ci->ci_subtype = add ("rfc822", ci->ci_subtype); /* match subtype */ - for (kv = SubMessage; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) - break; - ct->c_subtype = kv->kv_value; + ct->c_subtype = ct_str_subtype (CT_MESSAGE, ci->ci_subtype); switch (ct->c_subtype) { case MESSAGE_RFC822: @@ -1332,8 +1416,7 @@ InitMessage (CT ct) PM pm; struct partial *p; - if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL) - adios (NULL, "out of memory"); + NEW0(p); ct->c_ctparams = (void *) p; /* scan for parameters "id", "number", and "total" */ @@ -1381,8 +1464,7 @@ invalid_param: CT p; FILE *fp; - if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL) - adios (NULL, "out of memory"); + NEW0(e); ct->c_ctparams = (void *) e; if (!ct->c_fp @@ -1406,14 +1488,14 @@ invalid_param: && p->c_ceopenfnx == openMail) { int cc, size; char *bp; - + if ((size = ct->c_end - p->c_begin) <= 0) { if (!e->eb_subject) content_error (NULL, ct, "empty body for access-type=mail-server"); goto no_body; } - + e->eb_body = bp = mh_xmalloc ((unsigned) size); fseek (p->c_fp, p->c_begin, SEEK_SET); while (size > 0) @@ -1571,14 +1653,10 @@ params_external (CT ct, int composing) static int InitApplication (CT ct) { - struct k2v *kv; CI ci = &ct->c_ctinfo; /* match subtype */ - for (kv = SubApplication; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) - break; - ct->c_subtype = kv->kv_value; + ct->c_subtype = ct_str_subtype (CT_APPLICATION, ci->ci_subtype); return OK; } @@ -1626,8 +1704,7 @@ size_encoding (CT ct) if (ce->ce_file) { if (stat (ce->ce_file, &st) != NOTOK) return (long) st.st_size; - else - return 0L; + return 0L; } if (ct->c_encoding == CE_EXTERNAL) @@ -1651,26 +1728,6 @@ size_encoding (CT ct) * BASE64 */ -static unsigned char b642nib[0x80] = { - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, - 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, - 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, - 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, - 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, - 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, - 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, - 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, - 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff -}; - - static int InitBase64 (CT ct) { @@ -1681,15 +1738,15 @@ InitBase64 (CT ct) static int openBase64 (CT ct, char **file) { - int bitno, cc, digested; - 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]; + ssize_t cc, len; + int fd, own_ct_fp = 0; + char *cp, *buffer = NULL; /* sbeck -- handle suffixes */ CI ci; CE ce = &ct->c_cefile; - MD5_CTX mdContext; + unsigned char *decoded; + size_t decoded_len; + unsigned char digest[16]; if (ce->ce_fp) { fseek (ce->ce_fp, 0L, SEEK_SET); @@ -1707,21 +1764,13 @@ openBase64 (CT ct, char **file) if (*file == NULL) { ce->ce_unlink = 1; } else { - ce->ce_file = add (*file, NULL); + ce->ce_file = mh_xstrdup(*file); ce->ce_unlink = 0; } /* sbeck@cise.ufl.edu -- handle suffixes */ ci = &ct->c_ctinfo; - snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s", - invo_name, ci->ci_type, ci->ci_subtype); - cp = context_find (buffer); - if (cp == NULL || *cp == '\0') { - snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name, - ci->ci_type); - cp = context_find (buffer); - } - if (cp != NULL && *cp != '\0') { + if ((cp = context_find_by_type ("suffix", ci->ci_type, ci->ci_subtype))) { if (ce->ce_unlink) { /* Create temporary file with filename extension. */ if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { @@ -1737,7 +1786,7 @@ openBase64 (CT ct, char **file) adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } - ce->ce_file = add (tempfile, NULL); + ce->ce_file = mh_xstrdup(tempfile); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -1748,6 +1797,8 @@ openBase64 (CT ct, char **file) if ((len = ct->c_end - ct->c_begin) < 0) adios (NULL, "internal error(1)"); + buffer = mh_xmalloc (len + 1); + if (! ct->c_fp) { if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) { content_error (ct->c_file, ct, "unable to open for reading"); @@ -1755,17 +1806,11 @@ openBase64 (CT ct, char **file) } own_ct_fp = 1; } - - if ((digested = ct->c_digested)) - MD5Init (&mdContext); - - bitno = 18; - bits = 0L; - skip = 0; lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET); + cp = buffer; while (len > 0) { - switch (cc = read (fd, buffer, sizeof(buffer) - 1)) { + switch (cc = read (fd, cp, len)) { case NOTOK: content_error (ct->c_file, ct, "error reading from"); goto clean_up; @@ -1778,75 +1823,41 @@ openBase64 (CT ct, char **file) if (cc > len) cc = len; len -= cc; - - for (ep = (cp = buffer) + cc; cp < ep; cp++) { - switch (*cp) { - default: - if (isspace ((unsigned char) *cp)) - break; - if (skip || (((unsigned char) *cp) & 0x80) - || (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) { - if (debugsw) { - fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", - (unsigned char) *cp, - (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)), - skip); - } - content_error (NULL, ct, - "invalid BASE64 encoding -- continuing"); - continue; - } - - bits |= value << bitno; -test_end: - if ((bitno -= 6) < 0) { - b = (bits >> 16) & 0xff; - if (!text || b != '\r') - putc ((char) b, ce->ce_fp); - if (digested) - MD5Update (&mdContext, &b, 1); - if (skip < 2) { - b = (bits >> 8) & 0xff; - if (! text || b != '\r') - putc ((char) b, ce->ce_fp); - if (digested) - MD5Update (&mdContext, &b, 1); - if (skip < 1) { - b = bits & 0xff; - if (! text || b != '\r') - putc ((char) b, ce->ce_fp); - if (digested) - MD5Update (&mdContext, &b, 1); - } - } - - if (ferror (ce->ce_fp)) { - content_error (ce->ce_file, ct, - "error writing to"); - goto clean_up; - } - bitno = 18, bits = 0L, skip = 0; - } - break; - - case '=': - if (++skip > 3) - goto self_delimiting; - goto test_end; - } - } - } + cp += cc; + } } - if (bitno != 18) { - if (debugsw) - fprintf (stderr, "premature ending (bitno %d)\n", bitno); + /* decodeBase64() requires null-terminated input. */ + *cp = '\0'; - content_error (NULL, ct, "invalid BASE64 encoding"); - goto clean_up; + if (decodeBase64 (buffer, &decoded, &decoded_len, ct->c_type == CT_TEXT, + ct->c_digested ? digest : NULL) == OK) { + size_t i; + unsigned char *decoded_p = decoded; + for (i = 0; i < decoded_len; ++i) { + putc (*decoded_p++, ce->ce_fp); + } + free(decoded); + if (ferror (ce->ce_fp)) { + content_error (ce->ce_file, ct, "error writing to"); + goto clean_up; + } + + if (ct->c_digested) { + if (memcmp(digest, ct->c_digest, + sizeof(digest) / sizeof(digest[0]))) { + content_error (NULL, ct, + "content integrity suspect (digest mismatch) -- continuing"); + } else { + if (debugsw) { + fprintf (stderr, "content integrity confirmed\n"); + } + } + } + } else { + goto clean_up; } -self_delimiting: fseek (ct->c_fp, 0L, SEEK_SET); if (fflush (ce->ce_fp)) { @@ -1854,19 +1865,6 @@ self_delimiting: goto clean_up; } - if (digested) { - unsigned char digest[16]; - - MD5Final (digest, &mdContext); - if (memcmp((char *) digest, (char *) ct->c_digest, - sizeof(digest) / sizeof(digest[0]))) - content_error (NULL, ct, - "content integrity suspect (digest mismatch) -- continuing"); - else - if (debugsw) - fprintf (stderr, "content integrity confirmed\n"); - } - fseek (ce->ce_fp, 0L, SEEK_SET); ready_to_go: @@ -1875,6 +1873,7 @@ ready_to_go: fclose (ct->c_fp); ct->c_fp = NULL; } + free (buffer); return fileno (ce->ce_fp); clean_up: @@ -1883,6 +1882,7 @@ clean_up: ct->c_fp = NULL; } free_encoding (ct, 0); + free (buffer); return NOTOK; } @@ -1900,18 +1900,18 @@ static char hex2nib[0x80] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, + 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, + 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -static int +static int InitQuoted (CT ct) { return init_encoding (ct, openQuoted); @@ -1923,7 +1923,9 @@ openQuoted (CT ct, char **file) { int cc, digested, len, quoted, own_ct_fp = 0; char *cp, *ep; - char buffer[BUFSIZ]; + char *bufp = NULL; + size_t buflen; + ssize_t gotlen; unsigned char mask; CE ce = &ct->c_cefile; /* sbeck -- handle suffixes */ @@ -1946,21 +1948,13 @@ openQuoted (CT ct, char **file) if (*file == NULL) { ce->ce_unlink = 1; } else { - ce->ce_file = add (*file, NULL); + ce->ce_file = mh_xstrdup(*file); ce->ce_unlink = 0; } /* sbeck@cise.ufl.edu -- handle suffixes */ ci = &ct->c_ctinfo; - snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s", - invo_name, ci->ci_type, ci->ci_subtype); - cp = context_find (buffer); - if (cp == NULL || *cp == '\0') { - snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name, - ci->ci_type); - cp = context_find (buffer); - } - if (cp != NULL && *cp != '\0') { + if ((cp = context_find_by_type ("suffix", ci->ci_type, ci->ci_subtype))) { if (ce->ce_unlink) { /* Create temporary file with filename extension. */ if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { @@ -1976,7 +1970,7 @@ openQuoted (CT ct, char **file) adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } - ce->ce_file = add (tempfile, NULL); + ce->ce_file = mh_xstrdup(tempfile); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2005,16 +1999,16 @@ openQuoted (CT ct, char **file) fseek (ct->c_fp, ct->c_begin, SEEK_SET); while (len > 0) { - if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) { + if ((gotlen = getline(&bufp, &buflen, ct->c_fp)) == -1) { content_error (NULL, ct, "premature eof"); goto clean_up; } - if ((cc = strlen (buffer)) > len) + if ((cc = gotlen) > len) cc = len; len -= cc; - for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--) + for (ep = (cp = bufp) + cc - 1; cp <= ep; ep--) if (!isspace ((unsigned char) *ep)) break; *++ep = '\n', ep++; @@ -2119,6 +2113,7 @@ ready_to_go: fclose (ct->c_fp); ct->c_fp = NULL; } + free (bufp); return fileno (ce->ce_fp); clean_up: @@ -2127,6 +2122,7 @@ clean_up: fclose (ct->c_fp); ct->c_fp = NULL; } + free (bufp); return NOTOK; } @@ -2172,21 +2168,13 @@ open7Bit (CT ct, char **file) if (*file == NULL) { ce->ce_unlink = 1; } else { - ce->ce_file = add (*file, NULL); + ce->ce_file = mh_xstrdup(*file); ce->ce_unlink = 0; } /* sbeck@cise.ufl.edu -- handle suffixes */ ci = &ct->c_ctinfo; - snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s", - invo_name, ci->ci_type, ci->ci_subtype); - cp = context_find (buffer); - if (cp == NULL || *cp == '\0') { - snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name, - ci->ci_type); - cp = context_find (buffer); - } - if (cp != NULL && *cp != '\0') { + if ((cp = context_find_by_type ("suffix", ci->ci_type, ci->ci_subtype))) { if (ce->ce_unlink) { /* Create temporary file with filename extension. */ if ((ce->ce_file = m_mktemps(invo_name, cp, NULL, NULL)) == NULL) { @@ -2202,7 +2190,7 @@ open7Bit (CT ct, char **file) adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } - ce->ce_file = add (tempfile, NULL); + ce->ce_file = mh_xstrdup(tempfile); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2274,7 +2262,9 @@ open7Bit (CT ct, char **file) cc = len; len -= cc; - fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp); + if ((int) fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp) < cc) { + advise ("open7Bit", "fwrite"); + } if (ferror (ce->ce_fp)) { content_error (ce->ce_file, ct, "error writing to"); goto clean_up; @@ -2333,14 +2323,14 @@ openExternal (CT ct, CT cb, CE ce, char **file, int *fd) if (find_cache (ct, rcachesw, (int *) 0, cb->c_id, cachefile, sizeof(cachefile)) != NOTOK) { if ((ce->ce_fp = fopen (cachefile, "r"))) { - ce->ce_file = getcpy (cachefile); + ce->ce_file = mh_xstrdup(cachefile); ce->ce_unlink = 0; goto ready_already; - } else { - admonish (cachefile, "unable to fopen for reading"); } + admonish (cachefile, "unable to fopen for reading"); } + *fd = fileno (ce->ce_fp); return OK; ready_already: @@ -2384,7 +2374,7 @@ openFile (CT ct, char **file) return NOTOK; } - ce->ce_file = getcpy (e->eb_name); + ce->ce_file = mh_xstrdup(e->eb_name); ce->ce_unlink = 0; if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) { @@ -2408,7 +2398,9 @@ openFile (CT ct, char **file) while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0) - fwrite (buffer, sizeof(*buffer), cc, fp); + if ((int) fwrite (buffer, sizeof(*buffer), cc, fp) < cc) { + advise ("openFile", "fwrite"); + } fflush (fp); if (ferror (gp)) { @@ -2478,13 +2470,6 @@ openFTP (CT ct, char **file) return NOTOK; } - if (xpid) { - if (xpid < 0) - xpid = -xpid; - pidcheck (pidwait (xpid, NOTOK)); - xpid = 0; - } - /* Get the buffer ready to go */ bp = buffer; buflen = sizeof(buffer); @@ -2521,7 +2506,7 @@ openFTP (CT ct, char **file) /* * Now, check the answer */ - if (!getanswer (buffer)) + if (!read_yes_or_no_if_tty (buffer)) return NOTOK; if (e->eb_flags) { @@ -2530,7 +2515,7 @@ openFTP (CT ct, char **file) LocalName (1)); pass = buffer; } else { - ruserpass (e->eb_site, &username, &password); + ruserpass (e->eb_site, &username, &password, 0); user = username; pass = password; } @@ -2548,16 +2533,16 @@ openFTP (CT ct, char **file) } if (*file) - ce->ce_file = add (*file, NULL); + ce->ce_file = mh_xstrdup(*file); else if (caching) - ce->ce_file = add (cachefile, NULL); + ce->ce_file = mh_xstrdup(cachefile); else { 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_file = mh_xstrdup(tempfile); } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2624,7 +2609,9 @@ openFTP (CT ct, char **file) while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0) - fwrite (buffer, sizeof(*buffer), cc, fp); + if ((int) fwrite (buffer, sizeof(*buffer), cc, fp) < cc) { + advise ("openFTP", "fwrite"); + } fflush (fp); if (ferror (gp)) { @@ -2684,13 +2671,6 @@ openMail (CT ct, char **file) return NOTOK; } - if (xpid) { - if (xpid < 0) - xpid = -xpid; - pidcheck (pidwait (xpid, NOTOK)); - xpid = 0; - } - /* Get buffer ready to go */ bp = buffer; buflen = sizeof(buffer); @@ -2713,7 +2693,7 @@ openMail (CT ct, char **file) e->eb_subject ? e->eb_subject : e->eb_body); /* Now, check answer */ - if (!getanswer (buffer)) + if (!read_yes_or_no_if_tty (buffer)) return NOTOK; vecp = 0; @@ -2751,10 +2731,10 @@ openMail (CT ct, char **file) adios(NULL, "unable to create temporary file in %s", get_temp_dir()); } - ce->ce_file = add (tempfile, NULL); + ce->ce_file = mh_xstrdup(tempfile); ce->ce_unlink = 1; } else { - ce->ce_file = add (*file, NULL); + ce->ce_file = mh_xstrdup(*file); ce->ce_unlink = 0; } @@ -2765,9 +2745,8 @@ openMail (CT ct, char **file) /* showproc is for mhshow and mhstore, though mhlist -debug * prints it, too. */ - if (ct->c_showproc) - free (ct->c_showproc); - ct->c_showproc = add ("true", NULL); + mh_xfree(ct->c_showproc); + ct->c_showproc = mh_xstrdup("true"); fseek (ce->ce_fp, 0L, SEEK_SET); *file = ce->ce_file; @@ -2821,13 +2800,6 @@ openURL (CT ct, char **file) return NOTOK; } - if (xpid) { - if (xpid < 0) - xpid = -xpid; - pidcheck (pidwait (xpid, NOTOK)); - xpid = 0; - } - ce->ce_unlink = (*file == NULL); caching = 0; cachefile[0] = '\0'; @@ -2841,16 +2813,16 @@ openURL (CT ct, char **file) } if (*file) - ce->ce_file = add(*file, NULL); + ce->ce_file = mh_xstrdup(*file); else if (caching) - ce->ce_file = add(cachefile, NULL); + ce->ce_file = mh_xstrdup(cachefile); else { 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_file = mh_xstrdup(tempfile); } if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) { @@ -2898,7 +2870,9 @@ openURL (CT ct, char **file) while ((cc = fread(buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0) - fwrite(buffer, sizeof(*buffer), cc, fp); + if ((int) fwrite(buffer, sizeof(*buffer), cc, fp) < cc) { + advise ("openURL", "fwrite"); + } fflush(fp); @@ -2916,79 +2890,44 @@ openURL (CT ct, char **file) return fd; } + +/* + * Stores MD5 digest (in cp, from Content-MD5 header) in ct->c_digest. It + * has to be base64 decoded. + */ static int readDigest (CT ct, char *cp) { - int bitno, skip; - uint32_t bits; - char *bp = cp; - unsigned char *dp, value, *ep; - - bitno = 18; - bits = 0L; - skip = 0; - - for (ep = (dp = ct->c_digest) - + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++) - switch (*cp) { - default: - if (skip - || (*cp & 0x80) - || (value = b642nib[*cp & 0x7f]) > 0x3f) { - if (debugsw) - fprintf (stderr, "invalid BASE64 encoding\n"); - return NOTOK; - } + unsigned char *digest; - bits |= value << bitno; -test_end: - if ((bitno -= 6) < 0) { - if (dp + (3 - skip) > ep) - goto invalid_digest; - *dp++ = (bits >> 16) & 0xff; - if (skip < 2) { - *dp++ = (bits >> 8) & 0xff; - if (skip < 1) - *dp++ = bits & 0xff; - } - bitno = 18; - bits = 0L; - skip = 0; - } - break; + size_t len; + if (decodeBase64 (cp, &digest, &len, 0, NULL) == OK) { + const size_t maxlen = sizeof ct->c_digest / sizeof ct->c_digest[0]; - case '=': - if (++skip > 3) - goto self_delimiting; - goto test_end; - } - if (bitno != 18) { - if (debugsw) - fprintf (stderr, "premature ending (bitno %d)\n", bitno); + if (strlen ((char *) digest) <= maxlen) { + memcpy (ct->c_digest, digest, maxlen); - return NOTOK; - } -self_delimiting: - if (dp != ep) { -invalid_digest: - if (debugsw) { - while (*cp) - cp++; - fprintf (stderr, "invalid MD5 digest (got %d octets)\n", - (int)(cp - bp)); - } + if (debugsw) { + size_t i; - return NOTOK; - } + fprintf (stderr, "MD5 digest="); + for (i = 0; i < maxlen; ++i) { + fprintf (stderr, "%02x", ct->c_digest[i] & 0xff); + } + fprintf (stderr, "\n"); + } + + return OK; + } + if (debugsw) { + fprintf (stderr, "invalid MD5 digest (got %d octets)\n", + (int) strlen ((char *) digest)); + } - if (debugsw) { - fprintf (stderr, "MD5 digest="); - for (dp = ct->c_digest; dp < ep; dp++) - fprintf (stderr, "%02x", *dp & 0xff); - fprintf (stderr, "\n"); + return NOTOK; } - return OK; + return NOTOK; } @@ -2996,12 +2935,15 @@ invalid_digest: after the last subpart that hasn't been stored anywhere else, so do that. */ int -get_leftover_mp_content (CT ct, int before /* or after */) { +get_leftover_mp_content (CT ct, int before /* or after */) +{ struct multipart *m = (struct multipart *) ct->c_ctparams; char *boundary; int found_boundary = 0; - char buffer[BUFSIZ]; int max = BUFSIZ; + char *bufp = NULL; + size_t buflen; + ssize_t gotlen; int read = 0; char *content = NULL; @@ -3034,18 +2976,18 @@ get_leftover_mp_content (CT ct, int before /* or after */) { } /* Back up by 1 to pick up the newline. */ - while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) { - read += strlen (buffer); + while ((gotlen = getline(&bufp, &buflen, ct->c_fp)) != -1) { + read += gotlen; /* Don't look beyond beginning of first subpart (before) or next part (after). */ - if (read > max) buffer[read-max] = '\0'; + if (read > max) bufp[read-max] = '\0'; if (before) { - if (! strcmp (buffer, boundary)) { + if (! strcmp (bufp, boundary)) { found_boundary = 1; } } else { - if (! found_boundary && ! strcmp (buffer, boundary)) { + if (! found_boundary && ! strcmp (bufp, boundary)) { found_boundary = 1; continue; } @@ -3054,12 +2996,12 @@ get_leftover_mp_content (CT ct, int before /* or after */) { if ((before && ! found_boundary) || (! before && found_boundary)) { if (content) { char *old_content = content; - content = concat (content, buffer, NULL); + content = concat (content, bufp, NULL); free (old_content); } else { content = before - ? concat ("\n", buffer, NULL) - : concat (buffer, NULL); + ? concat ("\n", bufp, NULL) + : concat (bufp, NULL); } } @@ -3093,6 +3035,7 @@ get_leftover_mp_content (CT ct, int before /* or after */) { } free (boundary); + free (bufp); return OK; } @@ -3156,6 +3099,8 @@ ct_subtype_str (int type, int subtype) { return "digest"; case MULTI_PARALLEL: return "parallel"; + case MULTI_RELATED: + return "related"; default: return "unknown_multipart_subtype"; } @@ -3176,6 +3121,62 @@ ct_subtype_str (int type, int subtype) { } +int +ct_str_type (const char *type) { + struct str2init *s2i; + + for (s2i = str2cts; s2i->si_key; ++s2i) { + if (! strcasecmp (type, s2i->si_key)) { + break; + } + } + if (! s2i->si_key && ! uprf (type, "X-")) { + ++s2i; + } + + return s2i->si_val; +} + + +int +ct_str_subtype (int type, const char *subtype) { + struct k2v *kv; + + switch (type) { + case CT_APPLICATION: + for (kv = SubApplication; kv->kv_key; ++kv) { + if (! strcasecmp (subtype, kv->kv_key)) { + break; + } + } + return kv->kv_value; + case CT_MESSAGE: + for (kv = SubMessage; kv->kv_key; ++kv) { + if (! strcasecmp (subtype, kv->kv_key)) { + break; + } + } + return kv->kv_value; + case CT_MULTIPART: + for (kv = SubMultiPart; kv->kv_key; ++kv) { + if (! strcasecmp (subtype, kv->kv_key)) { + break; + } + } + return kv->kv_value; + case CT_TEXT: + for (kv = SubText; kv->kv_key; ++kv) { + if (! strcasecmp (subtype, kv->kv_key)) { + break; + } + } + return kv->kv_value; + default: + return 0; + } +} + + /* Find the content type and InitFunc for the CT. */ const struct str2init * get_ct_init (int type) { @@ -3255,6 +3256,7 @@ parse_header_attrs (const char *filename, const char *fieldname, struct sectlist { char *value; int index; + int len; struct sectlist *next; } *sp, *sp2; struct parmlist { @@ -3263,11 +3265,11 @@ parse_header_attrs (const char *filename, const char *fieldname, char *lang; struct sectlist *sechead; struct parmlist *next; - } *pp, *phead = NULL; + } *pp, *pp2, *phead = NULL; while (*cp == ';') { char *dp, *vp, *up, *nameptr, *valptr, *charset = NULL, *lang = NULL; - int encoded = 0, partial = 0, index = 0, len = 0; + int encoded = 0, partial = 0, len = 0, index = 0; cp++; while (isspace ((unsigned char) *cp)) @@ -3279,17 +3281,18 @@ parse_header_attrs (const char *filename, const char *fieldname, } if (*cp == 0) { - advise (NULL, - "extraneous trailing ';' in message %s's %s: " - "parameter list", - filename, fieldname); + if (! suppress_extraneous_trailing_semicolon_warning) { + advise (NULL, + "extraneous trailing ';' in message %s's %s: " + "parameter list", + filename, fieldname); + } return DONE; } /* down case the attribute name */ for (dp = cp; istoken ((unsigned char) *dp); dp++) - if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp)) - *dp = tolower ((unsigned char) *dp); + *dp = tolower ((unsigned char) *dp); for (up = dp; isspace ((unsigned char) *dp);) dp++; @@ -3357,7 +3360,8 @@ parse_header_attrs (const char *filename, const char *fieldname, */ if (index == 0) { vp = dp; - while (*vp != '\'' && !isspace((unsigned char) *vp)) + while (*vp != '\'' && !isspace((unsigned char) *vp) && + *vp != '\0') vp++; if (*vp == '\'') { if (vp != dp) { @@ -3376,30 +3380,32 @@ parse_header_attrs (const char *filename, const char *fieldname, free(nameptr); return NOTOK; } - } + dp = vp; - vp = (dp = vp); - while (*vp != '\'' && !isspace((unsigned char) *vp)) - vp++; + while (*vp != '\'' && !isspace((unsigned char) *vp) && + *vp != '\0') + vp++; - if (*vp == '\'') { - if (vp != dp) { - len = vp - dp; - lang = mh_xmalloc(len + 1); - strncpy(lang, dp, len); - lang[len] = '\0'; + if (*vp == '\'') { + if (vp != dp) { + len = vp - dp; + lang = mh_xmalloc(len + 1); + strncpy(lang, dp, len); + lang[len] = '\0'; + } else { + lang = NULL; + } + vp++; } else { - lang = NULL; + advise(NULL, "missing language tag in message %s's %s: " + "field\n%*s(parameter %s)", filename, fieldname, + strlen(invo_name) + 2, "", nameptr); + free(nameptr); + mh_xfree(charset); + return NOTOK; } - vp++; - } else { - advise(NULL, "missing language tag in message %s's %s: " - "field\n%*s(parameter %s)", filename, fieldname, - strlen(invo_name) + 2, "", nameptr); - free(nameptr); - if (charset) - free(charset); - return NOTOK; + + dp = vp; } /* @@ -3409,8 +3415,7 @@ parse_header_attrs (const char *filename, const char *fieldname, * length so we can allocate the correct buffer size. */ - for (dp = vp, len = 0; *vp != '\0' && !isspace((unsigned char) *vp); - vp++) { + for (vp = dp, len = 0; istoken(*vp); vp++) { if (*vp == '%') { if (*(vp + 1) == '\0' || !isxdigit((unsigned char) *(vp + 1)) || @@ -3421,10 +3426,8 @@ parse_header_attrs (const char *filename, const char *fieldname, filename, fieldname, strlen(invo_name) + 2, "", nameptr); free(nameptr); - if (charset) - free(charset); - if (lang) - free(lang); + mh_xfree(charset); + mh_xfree(lang); return NOTOK; } vp += 2; @@ -3434,7 +3437,7 @@ parse_header_attrs (const char *filename, const char *fieldname, up = valptr = mh_xmalloc(len + 1); - for (vp = dp; *vp != '\0' && !isspace((unsigned char) *vp); vp++) { + for (vp = dp; istoken(*vp); vp++) { if (*vp == '%') { *up++ = decode_qp(*(vp + 1), *(vp + 2)); vp += 2; @@ -3467,10 +3470,8 @@ bad_quote: filename, fieldname, strlen(invo_name) + 2, "", nameptr); free(nameptr); - if (charset) - free(charset); - if (lang) - free(lang); + mh_xfree(charset); + mh_xfree(lang); return NOTOK; case '"': break; @@ -3522,13 +3523,15 @@ bad_quote: if (partial) { for (pp = phead; pp != NULL; pp = pp->next) { - if (strcasecmp(nameptr, pp->name) == 0) + if (strcasecmp(nameptr, pp->name) == 0) { + free (nameptr); + nameptr = pp->name; break; + } } if (pp == NULL) { - pp = mh_xmalloc(sizeof(*pp)); - memset(pp, 0, sizeof(*pp)); + NEW0(pp); pp->name = nameptr; pp->next = phead; phead = pp; @@ -3538,10 +3541,10 @@ bad_quote: * Insert this into the section linked list */ - sp = mh_xmalloc(sizeof(*sp)); - memset(sp, 0, sizeof(*sp)); + NEW0(sp); sp->value = valptr; sp->index = index; + sp->len = len; if (pp->sechead == NULL || pp->sechead->index > index) { sp->next = pp->sechead; @@ -3556,7 +3559,7 @@ bad_quote: return NOTOK; } if (sp2->index < sp->index && - (sp2->next == NULL || sp2->next->index < sp->index)) { + (sp2->next == NULL || sp2->next->index > sp->index)) { sp->next = sp2->next; sp2->next = sp; break; @@ -3565,7 +3568,7 @@ bad_quote: if (sp2 == NULL) { advise(NULL, "Internal error: cannot insert partial " - "param in message %s's %s: field\n*s(parameter %s)", + "param in message %s's %s: field\n%*s(parameter %s)", filename, fieldname, strlen(invo_name) + 2, "", nameptr); return NOTOK; @@ -3577,17 +3580,13 @@ bad_quote: */ if (index == 0 && encoded) { - if (pp->charset) - free(pp->charset); + mh_xfree(pp->charset); pp->charset = charset; - if (pp->lang) - free(pp->lang); + mh_xfree(pp->lang); pp->lang = lang; } } else { - pm = add_param(param_head, param_tail, nameptr, valptr); - free(nameptr); - free(valptr); + pm = add_param(param_head, param_tail, nameptr, valptr, 1); pm->pm_charset = charset; pm->pm_lang = lang; } @@ -3601,10 +3600,63 @@ bad_quote: } } + /* + * Now that we're done, reassemble all of the partial parameters. + */ + + for (pp = phead; pp != NULL; ) { + char *p, *q; + size_t tlen = 0; + int pindex = 0; + for (sp = pp->sechead; sp != NULL; sp = sp->next) { + if (sp->index != pindex++) { + advise(NULL, "missing section %d for parameter in " + "message %s's %s: field\n%*s(parameter %s)", pindex - 1, + filename, fieldname, strlen(invo_name) + 2, "", + pp->name); + return NOTOK; + } + tlen += sp->len; + } + + p = q = mh_xmalloc(tlen + 1); + for (sp = pp->sechead; sp != NULL; ) { + memcpy(q, sp->value, sp->len); + q += sp->len; + free(sp->value); + sp2 = sp->next; + free(sp); + sp = sp2; + } + + p[tlen] = '\0'; + + pm = add_param(param_head, param_tail, pp->name, p, 1); + pm->pm_charset = pp->charset; + pm->pm_lang = pp->lang; + pp2 = pp->next; + free(pp); + pp = pp2; + } + *header_attrp = cp; return OK; } +/* + * Return the charset for a particular content type. + */ + +char * +content_charset (CT ct) { + char *ret_charset = NULL; + + ret_charset = get_param(ct->c_ctinfo.ci_first_pm, "charset", '?', 0); + + return ret_charset ? ret_charset : mh_xstrdup("US-ASCII"); +} + + /* * 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 @@ -3630,8 +3682,7 @@ output_params(size_t initialwidth, PM params, int *offsetout, int external) if (strlen(params->pm_name) > CPERLIN) { advise(NULL, "Parameter name \"%s\" is too long", params->pm_name); - if (paramout) - free(paramout); + mh_xfree(paramout); return NULL; } @@ -3668,8 +3719,7 @@ output_params(size_t initialwidth, PM params, int *offsetout, int external) numchars, valoff); if (i == 0) { - if (paramout) - free(paramout); + mh_xfree(paramout); return NULL; } @@ -3732,8 +3782,7 @@ output_params(size_t initialwidth, PM params, int *offsetout, int external) strlen(params->pm_value + valoff), valoff); if (i == 0) { - if (paramout) - free(paramout); + mh_xfree(paramout); return NULL; } @@ -3831,10 +3880,14 @@ param_len(PM pm, int index, size_t valueoff, int *encode, int *cont, * add them now. */ - if (! pm->pm_charset) - pm->pm_charset = getcpy(write_charset_8bit()); + if (! pm->pm_charset) { + pm->pm_charset = mh_xstrdup(write_charset_8bit()); + if (strcasecmp(pm->pm_charset, "US-ASCII") == 0) + adios(NULL, "8-bit characters in parameter \"%s\", but " + "local character set is US-ASCII", pm->pm_name); + } if (! pm->pm_lang) - pm->pm_lang = getcpy(NULL); /* Default to a blank lang tag */ + pm->pm_lang = mh_xstrdup(""); /* Default to a blank lang tag */ len++; /* For the encoding marker */ maxfit--; @@ -3905,7 +3958,7 @@ param_len(PM pm, int index, size_t valueoff, int *encode, int *cont, * Output an encoded parameter string. */ -static size_t +size_t encode_param(PM pm, char *output, size_t len, size_t valuelen, size_t valueoff, int index) { @@ -4012,14 +4065,13 @@ normal_param(PM pm, char *output, size_t len, size_t valuelen, */ PM -add_param(PM *first, PM *last, const char *name, const char *value) +add_param(PM *first, PM *last, char *name, char *value, int nocopy) { - PM pm = mh_xmalloc(sizeof(*pm)); - - memset(pm, 0, sizeof(*pm)); + PM pm; - pm->pm_name = getcpy(name); - pm->pm_value = getcpy(value); + NEW0(pm); + pm->pm_name = nocopy ? name : getcpy(name); + pm->pm_value = nocopy ? value : getcpy(value); if (*first) { (*last)->pm_next = pm; @@ -4032,6 +4084,33 @@ add_param(PM *first, PM *last, const char *name, const char *value) return pm; } +/* + * Either replace a current parameter with a new value, or add the parameter + * to the parameter linked list. + */ + +PM +replace_param(PM *first, PM *last, char *name, char *value, int nocopy) +{ + PM pm; + + for (pm = *first; pm != NULL; pm = pm->pm_next) { + if (strcasecmp(name, pm->pm_name) == 0) { + /* + * If nocopy is set, it's assumed that we own both name + * and value. We don't need name, so we discard it now. + */ + if (nocopy) + free(name); + free(pm->pm_value); + pm->pm_value = nocopy ? value : getcpy(value); + return pm; + } + } + + return add_param(first, last, name, value, nocopy); +} + /* * Retrieve a parameter value from a parameter linked list. If the parameter * value needs converted to the local character set, do that now. @@ -4044,8 +4123,7 @@ get_param(PM first, const char *name, char replace, int fetchonly) if (strcasecmp(name, first->pm_name) == 0) { if (fetchonly) return first->pm_value; - else - return getcpy(get_param_value(first, replace)); + return getcpy(get_param_value(first, replace)); } first = first->pm_next; } @@ -4067,7 +4145,10 @@ char *get_param_value(PM pm, char replace) int utf8; iconv_t cd; ICONV_CONST char *p; +#else /* HAVE_ICONV */ + char *p; #endif /* HAVE_ICONV */ + char *q; /* @@ -4125,7 +4206,7 @@ char *get_param_value(PM pm, char replace) } if (utf8) { for (++p, --inbytes; - inbytes > 0 && (((unsigned char) *q) & 0xc0) == 0x80; + inbytes > 0 && (((unsigned char) *p) & 0xc0) == 0x80; ++p, --inbytes) continue; } else { @@ -4142,17 +4223,18 @@ char *get_param_value(PM pm, char replace) *q = '\0'; return buffer; -#endif /* HAVE_ICONV */ noiconv: +#endif /* HAVE_ICONV */ + /* - * Take everything non-ASCII and substituite the replacement character + * Take everything non-ASCII and substitute the replacement character */ q = buffer; bufsize = sizeof(buffer); for (p = pm->pm_value; *p != '\0' && bufsize > 1; p++, q++, bufsize--) { - if (isascii((unsigned char) *p) && !iscntrl((unsigned char) *p)) + if (isascii((unsigned char) *p) && isprint((unsigned char) *p)) *q = *p; else *q = replace;