X-Git-Url: http://git.marmaro.de/?p=mmh;a=blobdiff_plain;f=uip%2Fmhbuildsbr.c;h=8906d7fea90d45df7dcbcf1a1b83dea27afc8bd5;hp=227f111f525c99d8053597e7ae03ce0fbf507e09;hb=0569d6d1631dc90d4f2f2df6bdd0599c7ecc7814;hpb=7879ea4084333b448c5a3a49c1cb52023e3808d1 diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 227f111..8906d7f 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -3,6 +3,10 @@ * mhbuildsbr.c -- routines to expand/translate MIME composition files * * $Id$ + * + * This code is Copyright (c) 2002, by the authors of nmh. See the + * COPYRIGHT file in the root directory of the nmh distribution for + * complete copyright information. */ /* @@ -19,10 +23,11 @@ #include #include #include -#include +#include #include #include #include +#include #ifdef TIME_WITH_SYS_TIME # include @@ -40,14 +45,13 @@ #endif -extern int errno; - extern int debugsw; extern int verbosw; extern int ebcdicsw; extern int listsw; extern int rfc934sw; +extern int contentidsw; extern int endian; /* mhmisc.c */ @@ -182,6 +186,8 @@ static int scan_content (CT); static int build_headers (CT); static char *calculate_digest (CT, int); static int readDigest (CT, char *); +static char *incl_name_value (char *, char *, char *); +static char *extract_name_value (char *, char *); /* * Structures for mapping (content) types to @@ -294,15 +300,15 @@ build_mime (char *infile) compnum++; /* abort if draft has Mime-Version header field */ - if (!strcasecmp (name, VRSN_FIELD)) + if (!mh_strcasecmp (name, VRSN_FIELD)) adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD); /* abort if draft has Content-Transfer-Encoding header field */ - if (!strcasecmp (name, ENCODING_FIELD)) + if (!mh_strcasecmp (name, ENCODING_FIELD)) adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD); /* ignore any Content-Type fields in the header */ - if (!strcasecmp (name, TYPE_FIELD)) { + if (!mh_strcasecmp (name, TYPE_FIELD)) { while (state == FLDPLUS) state = m_getfld (state, name, buf, sizeof(buf), in); goto finish_field; @@ -491,7 +497,7 @@ get_content (FILE *in, char *file, int toplevel) compnum++; /* Get MIME-Version field */ - if (!strcasecmp (name, VRSN_FIELD)) { + if (!mh_strcasecmp (name, VRSN_FIELD)) { int ucmp; char c, *cp, *dp; @@ -527,7 +533,7 @@ get_content (FILE *in, char *file, int toplevel) for (dp = cp; istoken (*dp); dp++) continue; c = *dp, *dp = '\0'; - ucmp = !strcasecmp (cp, VRSN_VALUE); + ucmp = !mh_strcasecmp (cp, VRSN_VALUE); *dp = c; if (!ucmp) admonish (NULL, @@ -537,7 +543,7 @@ get_content (FILE *in, char *file, int toplevel) } /* Get Content-Type field */ - if (!strcasecmp (name, TYPE_FIELD)) { + if (!mh_strcasecmp (name, TYPE_FIELD)) { char *cp; struct str2init *s2i; CI ci = &ct->c_ctinfo; @@ -568,7 +574,7 @@ get_content (FILE *in, char *file, int toplevel) * flag for this content type. */ for (s2i = str2cts; s2i->si_key; s2i++) - if (!strcasecmp (ci->ci_type, s2i->si_key)) + if (!mh_strcasecmp (ci->ci_type, s2i->si_key)) break; if (!s2i->si_key && !uprf (ci->ci_type, "X-")) s2i++; @@ -578,7 +584,7 @@ get_content (FILE *in, char *file, int toplevel) } /* Get Content-Transfer-Encoding field */ - if (!strcasecmp (name, ENCODING_FIELD)) { + if (!mh_strcasecmp (name, ENCODING_FIELD)) { char *cp, *dp; char c; struct str2init *s2i; @@ -614,7 +620,7 @@ get_content (FILE *in, char *file, int toplevel) * for this transfer encoding. */ for (s2i = str2ces; s2i->si_key; s2i++) - if (!strcasecmp (cp, s2i->si_key)) + if (!mh_strcasecmp (cp, s2i->si_key)) break; if (!s2i->si_key && !uprf (cp, "X-")) s2i++; @@ -628,7 +634,7 @@ get_content (FILE *in, char *file, int toplevel) } /* Get Content-ID field */ - if (!strcasecmp (name, ID_FIELD)) { + if (!mh_strcasecmp (name, ID_FIELD)) { ct->c_id = add (buf, ct->c_id); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof(buf), in); @@ -638,7 +644,7 @@ get_content (FILE *in, char *file, int toplevel) } /* Get Content-Description field */ - if (!strcasecmp (name, DESCR_FIELD)) { + if (!mh_strcasecmp (name, DESCR_FIELD)) { ct->c_descr = add (buf, ct->c_descr); while (state == FLDPLUS) { state = m_getfld (state, name, buf, sizeof(buf), in); @@ -647,8 +653,18 @@ get_content (FILE *in, char *file, int toplevel) goto got_header; } + /* Get Content-Disposition field */ + if (!mh_strcasecmp (name, DISPO_FIELD)) { + ct->c_dispo = add (buf, ct->c_dispo); + while (state == FLDPLUS) { + state = m_getfld (state, name, buf, sizeof(buf), in); + ct->c_dispo = add (buf, ct->c_dispo); + } + goto got_header; + } + /* Get Content-MD5 field */ - if (!strcasecmp (name, MD5_FIELD)) { + if (!mh_strcasecmp (name, MD5_FIELD)) { char *cp, *dp, *ep; cp = add (buf, NULL); @@ -783,8 +799,7 @@ add_header (CT ct, char *name, char *value) HF hp; /* allocate header field structure */ - if (!(hp = malloc (sizeof(*hp)))) - adios (NULL, "out of memory"); + hp = mh_xmalloc (sizeof(*hp)); /* link data into header structure */ hp->name = name; @@ -1055,15 +1070,56 @@ bad_quote: } /* + * Get any {Content-Disposition} given in buffer. + */ + if (magic && *cp == '{') { + ct->c_dispo = ++cp; + for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) + if (*dp == '}') + break; + if (dp < cp) { + advise (NULL, "invalid disposition in message %s", ct->c_file); + ct->c_dispo = NULL; + return NOTOK; + } + + c = *dp; + *dp = '\0'; + if (*ct->c_dispo) + ct->c_dispo = concat (ct->c_dispo, "\n", NULL); + else + ct->c_dispo = NULL; + *dp++ = c; + cp = dp; + + while (isspace (*cp)) + cp++; + } + + /* * Check if anything is left over */ if (*cp) { - if (magic) + if (magic) { ci->ci_magic = add (cp, NULL); + + /* If there is a Content-Disposition header and it doesn't + have a *filename=, extract it from the magic contents. + The r1bindex call skips any leading directory + components. */ + if (ct->c_dispo) + ct->c_dispo = + incl_name_value (ct->c_dispo, + "filename", + r1bindex (extract_name_value ("name", + ci-> + ci_magic), + '/')); + } else advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", - ct->c_file, TYPE_FIELD, i, i, "", cp); + ct->c_file, TYPE_FIELD, i, i, "", cp); } return OK; @@ -1164,7 +1220,7 @@ InitText (CT ct) /* match subtype */ for (kv = SubText; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) + if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key)) break; ct->c_subtype = kv->kv_value; @@ -1178,14 +1234,14 @@ InitText (CT ct) /* scan for charset parameter */ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) - if (!strcasecmp (*ap, "charset")) + if (!mh_strcasecmp (*ap, "charset")) break; /* check if content specified a character set */ if (*ap) { /* match character set or set to CHARSET_UNKNOWN */ for (kv = Charset; kv->kv_key; kv++) - if (!strcasecmp (*ep, kv->kv_key)) + if (!mh_strcasecmp (*ep, kv->kv_key)) break; t->tx_charset = kv->kv_value; } @@ -1226,7 +1282,7 @@ InitMultiPart (CT ct) /* match subtype */ for (kv = SubMultiPart; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) + if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key)) break; ct->c_subtype = kv->kv_value; @@ -1235,7 +1291,7 @@ InitMultiPart (CT ct) * required for multipart messages. */ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "boundary")) { + if (!mh_strcasecmp (*ap, "boundary")) { bp = *ep; break; } @@ -1456,7 +1512,7 @@ InitMessage (CT ct) /* match subtype */ for (kv = SubMessage; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) + if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key)) break; ct->c_subtype = kv->kv_value; @@ -1475,11 +1531,11 @@ InitMessage (CT ct) /* scan for parameters "id", "number", and "total" */ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "id")) { + if (!mh_strcasecmp (*ap, "id")) { p->pm_partid = add (*ep, NULL); continue; } - if (!strcasecmp (*ap, "number")) { + if (!mh_strcasecmp (*ap, "number")) { if (sscanf (*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) { invalid_param: @@ -1491,7 +1547,7 @@ invalid_param: } continue; } - if (!strcasecmp (*ap, "total")) { + if (!mh_strcasecmp (*ap, "total")) { if (sscanf (*ep, "%d", &p->pm_maxno) != 1 || p->pm_maxno < 1) goto invalid_param; @@ -1551,8 +1607,7 @@ invalid_param: goto no_body; } - if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL) - adios (NULL, "out of memory"); + e->eb_body = bp = mh_xmalloc ((unsigned) size); fseek (p->c_fp, p->c_begin, SEEK_SET); while (size > 0) switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) { @@ -1613,12 +1668,12 @@ params_external (CT ct, int composing) CI ci = &ct->c_ctinfo; for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "access-type")) { + if (!mh_strcasecmp (*ap, "access-type")) { struct str2init *s2i; CT p = e->eb_content; for (s2i = str2methods; s2i->si_key; s2i++) - if (!strcasecmp (*ep, s2i->si_key)) + if (!mh_strcasecmp (*ep, s2i->si_key)) break; if (!s2i->si_key) { @@ -1636,39 +1691,39 @@ params_external (CT ct, int composing) return NOTOK; continue; } - if (!strcasecmp (*ap, "name")) { + if (!mh_strcasecmp (*ap, "name")) { e->eb_name = *ep; continue; } - if (!strcasecmp (*ap, "permission")) { + if (!mh_strcasecmp (*ap, "permission")) { e->eb_permission = *ep; continue; } - if (!strcasecmp (*ap, "site")) { + if (!mh_strcasecmp (*ap, "site")) { e->eb_site = *ep; continue; } - if (!strcasecmp (*ap, "directory")) { + if (!mh_strcasecmp (*ap, "directory")) { e->eb_dir = *ep; continue; } - if (!strcasecmp (*ap, "mode")) { + if (!mh_strcasecmp (*ap, "mode")) { e->eb_mode = *ep; continue; } - if (!strcasecmp (*ap, "size")) { + if (!mh_strcasecmp (*ap, "size")) { sscanf (*ep, "%lu", &e->eb_size); continue; } - if (!strcasecmp (*ap, "server")) { + if (!mh_strcasecmp (*ap, "server")) { e->eb_server = *ep; continue; } - if (!strcasecmp (*ap, "subject")) { + if (!mh_strcasecmp (*ap, "subject")) { e->eb_subject = *ep; continue; } - if (composing && !strcasecmp (*ap, "body")) { + if (composing && !mh_strcasecmp (*ap, "body")) { e->eb_body = getcpy (*ep); continue; } @@ -1697,7 +1752,7 @@ InitApplication (CT ct) /* match subtype */ for (kv = SubApplication; kv->kv_key; kv++) - if (!strcasecmp (ci->ci_subtype, kv->kv_key)) + if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key)) break; ct->c_subtype = kv->kv_value; @@ -2309,6 +2364,8 @@ open7Bit (CT ct, char **file) fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id); if (ct->c_descr) fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr); + if (ct->c_dispo) + fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo); fprintf (ce->ce_fp, "\n"); } @@ -2446,7 +2503,7 @@ openFile (CT ct, char **file) return NOTOK; } - if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write")) + if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write")) && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id, cachefile, sizeof(cachefile)) != NOTOK) { int mask; @@ -2594,7 +2651,7 @@ openFTP (CT ct, char **file) ce->ce_unlink = (*file == NULL); caching = 0; cachefile[0] = '\0'; - if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write")) + if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write")) && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id, cachefile, sizeof(cachefile)) != NOTOK) { if (*file == NULL) { @@ -2630,7 +2687,7 @@ openFTP (CT ct, char **file) vec[vecp++] = e->eb_dir; vec[vecp++] = e->eb_name; vec[vecp++] = ce->ce_file, - vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii") + vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii") ? "ascii" : "binary"; vec[vecp] = NULL; @@ -2667,7 +2724,7 @@ losing_ftp: else if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name, ce->ce_file, - e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0) + e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0) == NOTOK) goto losing_ftp; #endif @@ -2948,6 +3005,29 @@ again_descr: } } + if (headers >= 0 && uprf (buffer, DISPO_FIELD) + && buffer[i = strlen (DISPO_FIELD)] == ':') { + headers = 1; + +again_dispo: + ct->c_dispo = add (buffer + i + 1, ct->c_dispo); + if (!fgetstr (buffer, sizeof(buffer) - 1, in)) + adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD); + switch (buffer[0]) { + case ' ': + case '\t': + i = -1; + goto again_dispo; + + case '#': + adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD); + /* NOTREACHED */ + + default: + break; + } + } + if (headers != 1 || buffer[0] != '\n') fputs (buffer, out); @@ -2976,7 +3056,7 @@ rock_and_roll: done (1); for (s2i = str2cts; s2i->si_key; s2i++) - if (!strcasecmp (ci->ci_type, s2i->si_key)) + if (!mh_strcasecmp (ci->ci_type, s2i->si_key)) break; if (!s2i->si_key && !uprf (ci->ci_type, "X-")) s2i++; @@ -2986,7 +3066,7 @@ rock_and_roll: */ switch (ct->c_type = s2i->si_val) { case CT_MESSAGE: - if (!strcasecmp (ci->ci_subtype, "rfc822")) { + if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) { ct->c_encoding = CE_7BIT; goto call_init; } @@ -3022,7 +3102,7 @@ call_init: /* check directive against the list of MIME types */ for (s2i = str2cts; s2i->si_key; s2i++) - if (!strcasecmp (ci->ci_type, s2i->si_key)) + if (!mh_strcasecmp (ci->ci_type, s2i->si_key)) break; /* @@ -3043,10 +3123,10 @@ call_init: /* NOTREACHED */ case CT_MESSAGE: - if (!strcasecmp (ci->ci_subtype, "partial")) + if (!mh_strcasecmp (ci->ci_subtype, "partial")) adios (NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype); - if (!strcasecmp (ci->ci_subtype, "external-body")) + if (!mh_strcasecmp (ci->ci_subtype, "external-body")) adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype); use_forw: @@ -3153,7 +3233,7 @@ use_forw: * Message directive * #forw [+folder] [msgs] */ - if (!strcasecmp (ci->ci_type, "forw")) { + if (!mh_strcasecmp (ci->ci_type, "forw")) { int msgnum; char *folder, *arguments[MAXARGS]; struct msgs *mp; @@ -3174,7 +3254,7 @@ use_forw: if (folder) adios (NULL, "only one folder per #forw directive"); else - folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF); + folder = pluspath (cp); } } @@ -3259,7 +3339,7 @@ use_forw: /* * #end */ - if (!strcasecmp (ci->ci_type, "end")) { + if (!mh_strcasecmp (ci->ci_type, "end")) { free_content (ct); *ctp = NULL; return DONE; @@ -3268,14 +3348,14 @@ use_forw: /* * #begin [ alternative | parallel ] */ - if (!strcasecmp (ci->ci_type, "begin")) { + if (!mh_strcasecmp (ci->ci_type, "begin")) { if (!ci->ci_magic) { vrsn = MULTI_MIXED; cp = SubMultiPart[vrsn - 1].kv_key; - } else if (!strcasecmp (ci->ci_magic, "alternative")) { + } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) { vrsn = MULTI_ALTERNATE; cp = SubMultiPart[vrsn - 1].kv_key; - } else if (!strcasecmp (ci->ci_magic, "parallel")) { + } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) { vrsn = MULTI_PARALLEL; cp = SubMultiPart[vrsn - 1].kv_key; } else if (uprf (ci->ci_magic, "digest")) { @@ -3866,10 +3946,10 @@ build_headers (CT ct) /* * Skip the output of Content-Type, parameters, content - * description, and Content-ID if the content is of type - * "message" and the rfc934 compatibility flag is set - * (which means we are inside multipart/digest and the - * switch -rfc934mode was given). + * description and disposition, and Content-ID if the + * content is of type "message" and the rfc934 compatibility + * flag is set (which means we are inside multipart/digest + * and the switch -rfc934mode was given). */ if (ct->c_type == CT_MESSAGE && ct->c_rfc934) goto skip_headers; @@ -3893,7 +3973,7 @@ build_headers (CT ct) * the end of the Content-Type line. */ for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (mailbody && !strcasecmp (*ap, "body")) + if (mailbody && !mh_strcasecmp (*ap, "body")) continue; vp = add (";", vp); @@ -3931,9 +4011,9 @@ build_headers (CT ct) add_header (ct, np, vp); /* - * output the Content-ID + * output the Content-ID, unless disabled by -nocontentid */ - if (ct->c_id) { + if (contentidsw && ct->c_id) { np = add (ID_FIELD, NULL); vp = concat (" ", ct->c_id, NULL); add_header (ct, np, vp); @@ -3948,6 +4028,15 @@ build_headers (CT ct) add_header (ct, np, vp); } + /* + * output the Content-Disposition + */ + if (ct->c_dispo) { + np = add (DISPO_FIELD, NULL); + vp = concat (" ", ct->c_dispo, NULL); + add_header (ct, np, vp); + } + skip_headers: /* * If this is the internal content structure for a @@ -4226,3 +4315,88 @@ invalid_digest: return OK; } + + +/* Make sure that buf contains at least one appearance of name, + followed by =. If not, insert both name and value, just after + first semicolon, if any. Note that name should not contain a + trailing =. And quotes will be added around the value. Typical + usage: make sure that a Content-Disposition header contains + filename="foo". If it doesn't and value does, use value from + that. */ +static char * +incl_name_value (char *buf, char *name, char *value) { + char *newbuf = buf; + + /* Assume that name is non-null. */ + if (buf && value) { + char *name_plus_equal = concat (name, "=", NULL); + + if (! strstr (buf, name_plus_equal)) { + char *insertion; + char *cp; + char *prefix, *suffix; + + /* Trim trailing space, esp. newline. */ + for (cp = &buf[strlen (buf) - 1]; + cp >= buf && isspace (*cp); + --cp) { + *cp = '\0'; + } + + insertion = concat ("; ", name, "=", "\"", value, "\"", NULL); + + /* Insert at first semicolon, if any. If none, append to + end. */ + prefix = add (buf, NULL); + if ((cp = strchr (prefix, ';'))) { + suffix = concat (cp, NULL); + *cp = '\0'; + newbuf = concat (prefix, insertion, suffix, "\n", NULL); + free (suffix); + } else { + /* Append to end. */ + newbuf = concat (buf, insertion, "\n", NULL); + } + + free (prefix); + free (insertion); + free (buf); + } + + free (name_plus_equal); + } + + return newbuf; +} + + +/* Extract just name_suffix="foo", if any, from value. If there isn't + one, return the entire value. Note that, for example, a name_suffix + of name will match filename="foo", and return foo. */ +static char * +extract_name_value (char *name_suffix, char *value) { + char *extracted_name_value = value; + char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL); + char *name_suffix_equals = strstr (value, name_suffix_plus_quote); + char *cp; + + free (name_suffix_plus_quote); + if (name_suffix_equals) { + char *name_suffix_begin; + + /* Find first \". */ + for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */; + name_suffix_begin = ++cp; + /* Find second \". */ + for (; *cp != '"'; ++cp) /* empty */; + + extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1); + memcpy (extracted_name_value, + name_suffix_begin, + cp - name_suffix_begin); + extracted_name_value[cp - name_suffix_begin] = '\0'; + } + + return extracted_name_value; +}