X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/58d22c00deb7bc9c771c5293079f6f89bc28274a..f98955f258440b73a698f6cb83f2623ff4da2ff1:/uip/mhfixmsg.c diff --git a/uip/mhfixmsg.c b/uip/mhfixmsg.c index 5814eaee..cd38983c 100644 --- a/uip/mhfixmsg.c +++ b/uip/mhfixmsg.c @@ -14,7 +14,7 @@ #include #define MHFIXMSG_SWITCHES \ - X("decodetext 8bit|7bit", 0, DECODETEXTSW) \ + X("decodetext 8bit|7bit|binary", 0, DECODETEXTSW) \ X("nodecodetext", 0, NDECODETEXTSW) \ X("decodetypes", 0, DECODETYPESW) \ X("crlflinebreaks", 0, CRLFLINEBREAKSSW) \ @@ -27,8 +27,8 @@ X("noreplacetextplain", 0, NREPLACETEXTPLAINSW) \ X("fixboundary", 0, FIXBOUNDARYSW) \ X("nofixboundary", 0, NFIXBOUNDARYSW) \ - X("fixcte", 0, FIXCTESW) \ - X("nofixcte", 0, NFIXCTESW) \ + X("fixcte", 0, FIXCOMPOSITECTESW) \ + X("nofixcte", 0, NFIXCOMPOSITECTESW) \ X("fixtype mimetype", 0, FIXTYPESW) \ X("file file", 0, FILESW) \ X("outfile file", 0, OUTFILESW) \ @@ -78,7 +78,7 @@ void freects_done (int) NORETURN; */ typedef struct fix_transformations { int fixboundary; - int fixcte; + int fixcompositecte; svector_t fixtypes; int reformat; int replacetextplain; @@ -97,7 +97,7 @@ static int replace_boundary (CT, char *, char *); static int fix_types (CT, svector_t, int *); static char *replace_substring (char **, const char *, const char *); static char *remove_parameter (char *, const char *); -static int fix_multipart_cte (CT, int *); +static int fix_composite_cte (CT, int *); static int set_ce (CT, int); static int ensure_text_plain (CT *, CT, int *, int); static int find_textplain_sibling (CT, int, int *); @@ -108,7 +108,6 @@ static CT divide_part (CT); static void copy_ctinfo (CI, CI); static int decode_part (CT); static int reformat_part (CT, char *, char *, char *, int); -static int charset_encoding (CT); static CT build_multipart_alt (CT, CT, int, int); static int boundary_in_content (FILE **, char *, const char *); static void transfer_noncontent_headers (CT, CT); @@ -117,8 +116,13 @@ static int decode_text_parts (CT, int, const char *, int *); static int should_decode(const char *, const char *, const char *); static int content_encoding (CT, const char **); static int strip_crs (CT, int *); +static void update_cte (CT); +static int least_restrictive_encoding (CT); +static int less_restrictive (int, int); static int convert_charsets (CT, char *, int *); static int fix_always (CT, int *); +static int fix_filename_param (char *, char *, PM *, PM *); +static int fix_filename_encoding (CT); static int write_content (CT, const char *, char *, int, int); static void set_text_ctparams(CT, char *, int); static int remove_file (const char *); @@ -140,7 +144,7 @@ main (int argc, char **argv) { int chgflag = 1; int status = OK; fix_transformations fx; - fx.reformat = fx.fixcte = fx.fixboundary = 1; + fx.reformat = fx.fixcompositecte = fx.fixboundary = 1; fx.fixtypes = NULL; fx.replacetextplain = 0; fx.decodetext = CE_8BIT; @@ -148,7 +152,7 @@ main (int argc, char **argv) { fx.lf_line_endings = 0; fx.textcharset = NULL; - if (nmh_init(argv[0], 1)) { return 1; } + if (nmh_init(argv[0], 2)) { return 1; } done = freects_done; @@ -184,6 +188,8 @@ main (int argc, char **argv) { fx.decodetext = CE_8BIT; } else if (! strcasecmp (cp, "7bit")) { fx.decodetext = CE_7BIT; + } else if (! strcasecmp (cp, "binary")) { + fx.decodetext = CE_BINARY; } else { adios (NULL, "invalid argument to %s", argp[-2]); } @@ -218,11 +224,11 @@ main (int argc, char **argv) { case NFIXBOUNDARYSW: fx.fixboundary = 0; continue; - case FIXCTESW: - fx.fixcte = 1; + case FIXCOMPOSITECTESW: + fx.fixcompositecte = 1; continue; - case NFIXCTESW: - fx.fixcte = 0; + case NFIXCOMPOSITECTESW: + fx.fixcompositecte = 0; continue; case FIXTYPESW: if (! (cp = *argp++) || (*cp == '-' && cp[1])) { @@ -352,9 +358,7 @@ main (int argc, char **argv) { } } - if (! (cts = (CT *) mh_xcalloc ((size_t) 2, sizeof *cts))) { - adios (NULL, "out of memory"); - } + cts = mh_xcalloc(2, sizeof *cts); ctp = cts; if ((ct = parse_mime (file))) { @@ -409,10 +413,7 @@ main (int argc, char **argv) { } seq_setprev (mp); /* set the previous-sequence */ - if (! (cts = - (CT *) mh_xcalloc ((size_t) (mp->numsel + 1), sizeof *cts))) { - adios (NULL, "out of memory"); - } + cts = mh_xcalloc(mp->numsel + 1, sizeof *cts); ctp = cts; for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) { @@ -479,6 +480,9 @@ main (int argc, char **argv) { } +/* + * Apply transformations to one message. + */ int mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) { /* Store input filename in case one of the transformations, i.e., @@ -511,15 +515,17 @@ mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) { if (status == OK && fx->fixtypes != NULL) { status = fix_types (*ctp, fx->fixtypes, &message_mods); } - if (status == OK && fx->fixcte) { - status = fix_multipart_cte (*ctp, &message_mods); + if (status == OK && fx->fixcompositecte) { + status = fix_composite_cte (*ctp, &message_mods); } if (status == OK && fx->reformat) { status = ensure_text_plain (ctp, NULL, &message_mods, fx->replacetextplain); } if (status == OK && fx->decodetext) { - status = decode_text_parts (*ctp, fx->decodetext, fx->decodetypes, &message_mods); + status = decode_text_parts (*ctp, fx->decodetext, fx->decodetypes, + &message_mods); + update_cte (*ctp); } if (status == OK && fx->textcharset != NULL) { status = convert_charsets (*ctp, fx->textcharset, &message_mods); @@ -564,8 +570,10 @@ mhfixmsgsbr (CT *ctp, const fix_transformations *fx, char *outfile) { } -/* Copy input message to output. Assumes not modifying in place, so this - might be running as part of a pipeline. */ +/* + * Copy input message to output. Assumes not modifying in place, so this + * might be running as part of a pipeline. + */ static int copy_input_to_output (const char *input_filename, const char *output_filename) { int in = open (input_filename, O_RDONLY); @@ -587,6 +595,9 @@ copy_input_to_output (const char *input_filename, const char *output_filename) { } +/* + * Fix mismatched outer level boundary. + */ static int fix_boundary (CT *ct, int *message_mods) { struct multipart *mp; @@ -660,6 +671,9 @@ fix_boundary (CT *ct, int *message_mods) { } +/* + * Find boundary at end of multipart. + */ static int get_multipart_boundary (CT ct, char **part_boundary) { char buffer[BUFSIZ]; @@ -741,7 +755,9 @@ get_multipart_boundary (CT ct, char **part_boundary) { } -/* Open and copy ct->c_file to file, replacing the multipart boundary. */ +/* + * Open and copy ct->c_file to file, replacing the multipart boundary. + */ static int replace_boundary (CT ct, char *file, char *boundary) { FILE *fpin, *fpout; @@ -847,6 +863,9 @@ replace_boundary (CT ct, char *file, char *boundary) { } +/* + * Fix Content-Type header to reflect the content of its part. + */ static int fix_types (CT ct, svector_t fixtypes, int *message_mods) { int status = OK; @@ -905,10 +924,10 @@ fix_types (CT ct, svector_t fixtypes, int *message_mods) { * c_ctline */ /* Extract type and subtype from type/subtype. */ - ct_type = getcpy (ct_type_subtype); + ct_type = mh_xstrdup(ct_type_subtype); if ((cp = strchr (ct_type, '/'))) { *cp = '\0'; - ct_subtype = getcpy (++cp); + ct_subtype = mh_xstrdup(++cp); } else { advise (NULL, "missing / in MIME type of %s %s", ct->c_file, ct->c_partno); @@ -959,6 +978,10 @@ fix_types (CT ct, svector_t fixtypes, int *message_mods) { return status; } + +/* + * Replace a substring, allocating space to hold the new one. + */ char * replace_substring (char **str, const char *old, const char *new) { char *cp; @@ -968,7 +991,7 @@ replace_substring (char **str, const char *old, const char *new) { char *prefix, *new_str; if (cp - *str) { - prefix = getcpy (*str); + prefix = mh_xstrdup(*str); *(prefix + (cp - *str)) = '\0'; new_str = concat (prefix, new, remainder, NULL); free (prefix); @@ -984,6 +1007,7 @@ replace_substring (char **str, const char *old, const char *new) { } } + /* * Remove a name=value parameter, given just its name, from a header value. */ @@ -1030,14 +1054,17 @@ remove_parameter (char *str, const char *name) { return str; } + +/* + * Fix Content-Transfer-Encoding of composite,, e.g., message or multipart, part. + * According to RFC 2045 Sec. 6.4, it must be 7bit, 8bit, or binary. Set it to + * 8 bit. + */ static int -fix_multipart_cte (CT ct, int *message_mods) { +fix_composite_cte (CT ct, int *message_mods) { int status = OK; - if (ct->c_type == CT_MULTIPART) { - struct multipart *m; - struct part *part; - + if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART) { if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT && ct->c_encoding != CE_BINARY) { HF hf; @@ -1051,8 +1078,9 @@ fix_multipart_cte (CT ct, int *message_mods) { if (! strncasecmp (name, ENCODING_FIELD, strlen (ENCODING_FIELD))) { char *prefix = "Nmh-REPLACED-INVALID-"; - HF h = mh_xmalloc (sizeof *h); + HF h; + NEW(h); h->name = add (hf->name, NULL); h->hf_encoding = hf->hf_encoding; h->next = hf->next; @@ -1083,11 +1111,16 @@ fix_multipart_cte (CT ct, int *message_mods) { set_ce (ct, CE_8BIT); } - m = (struct multipart *) ct->c_ctparams; - for (part = m->mp_parts; part; part = part->mp_next) { - if (fix_multipart_cte (part->mp_part, message_mods) != OK) { - status = NOTOK; - break; + if (ct->c_type == CT_MULTIPART) { + struct multipart *m; + struct part *part; + + m = (struct multipart *) ct->c_ctparams; + for (part = m->mp_parts; part; part = part->mp_next) { + if (fix_composite_cte (part->mp_part, message_mods) != OK) { + status = NOTOK; + break; + } } } } @@ -1096,6 +1129,9 @@ fix_multipart_cte (CT ct, int *message_mods) { } +/* + * Set content encoding. + */ static int set_ce (CT ct, int encoding) { const char *ce = ce_str (encoding); @@ -1120,6 +1156,10 @@ set_ce (CT ct, int encoding) { ct->c_cefile.ce_file to the name of the file containing the contents. */ + if (ct->c_ceclosefnx) { + (*ct->c_ceclosefnx) (ct); + } + /* Restore the cefile. */ ct->c_cefile = decoded_content_info; @@ -1146,7 +1186,9 @@ set_ce (CT ct, int encoding) { } -/* Make sure each text part has a corresponding text/plain part. */ +/* + * Make sure each text part has a corresponding text/plain part. + */ static int ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) { int status = OK; @@ -1235,7 +1277,7 @@ ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) { HF hf; parent->c_subtype = MULTI_ALTERNATE; - parent->c_ctinfo.ci_subtype = getcpy ("alternative"); + parent->c_ctinfo.ci_subtype = mh_xstrdup("alternative"); if (! replace_substring (&parent->c_ctline, "/related", "/alternative")) { advise (NULL, @@ -1306,7 +1348,9 @@ ensure_text_plain (CT *ct, CT parent, int *message_mods, int replacetextplain) { } -/* See if there is a sibling text/plain. */ +/* + * See if there is a sibling text/plain, and return its subpart number. + */ static int find_textplain_sibling (CT parent, int replacetextplain, int *new_subpart_number) { @@ -1346,11 +1390,15 @@ find_textplain_sibling (CT parent, int replacetextplain, } +/* + * Insert a new text/plain part. + */ static int insert_new_text_plain_part (CT ct, int new_subpart_number, CT parent) { struct multipart *mp = (struct multipart *) parent->c_ctparams; - struct part *new_part = mh_xmalloc (sizeof *new_part); + struct part *new_part; + NEW(new_part); if ((new_part->mp_part = build_text_plain_part (ct))) { char buffer[16]; snprintf (buffer, sizeof buffer, "%d", new_subpart_number); @@ -1371,6 +1419,9 @@ insert_new_text_plain_part (CT ct, int new_subpart_number, CT parent) { } +/* + * Create a text/plain part to go along with non-plain sibling part. + */ static CT build_text_plain_part (CT encoded_part) { CT tp_part = divide_part (encoded_part); @@ -1405,7 +1456,9 @@ build_text_plain_part (CT encoded_part) { } -/* Slip new text/plain part into a new multipart/alternative. */ +/* + * Slip new text/plain part into a new multipart/alternative. + */ static int insert_into_new_mp_alt (CT *ct, int *message_mods) { CT tp_part = build_text_plain_part (*ct); @@ -1442,13 +1495,15 @@ insert_into_new_mp_alt (CT *ct, int *message_mods) { return status; } + +/* + * Clone a MIME part. + */ static CT divide_part (CT ct) { CT new_part; - if ((new_part = (CT) mh_xcalloc (1, sizeof *new_part)) == NULL) - adios (NULL, "out of memory"); - + NEW0(new_part); /* Just copy over what is needed for decoding. c_vrsn and c_celine aren't necessary. */ new_part->c_file = add (ct->c_file, NULL); @@ -1471,6 +1526,9 @@ divide_part (CT ct) { } +/* + * Copy the content info from one part to another. + */ static void copy_ctinfo (CI dest, CI src) { PM s_pm, d_pm; @@ -1481,10 +1539,12 @@ copy_ctinfo (CI dest, CI src) { for (s_pm = src->ci_first_pm; s_pm; s_pm = s_pm->pm_next) { d_pm = add_param(&dest->ci_first_pm, &dest->ci_last_pm, s_pm->pm_name, s_pm->pm_value, 0); - if (s_pm->pm_charset) - d_pm->pm_charset = getcpy(s_pm->pm_charset); - if (s_pm->pm_lang) - d_pm->pm_lang = getcpy(s_pm->pm_lang); + if (s_pm->pm_charset) { + d_pm->pm_charset = mh_xstrdup(s_pm->pm_charset); + } + if (s_pm->pm_lang) { + d_pm->pm_lang = mh_xstrdup(s_pm->pm_lang); + } } dest->ci_comment = src->ci_comment ? add (src->ci_comment, NULL) : NULL; @@ -1492,6 +1552,9 @@ copy_ctinfo (CI dest, CI src) { } +/* + * Decode content. + */ static int decode_part (CT ct) { char *tmp_decoded; @@ -1513,11 +1576,15 @@ decode_part (CT ct) { } -/* Some of the arguments aren't really needed now, but maybe will - be in the future for other than text types. */ +/* + * Reformat content as plain text. + * Some of the arguments aren't really needed now, but maybe will + * be in the future for other than text types. + */ static int reformat_part (CT ct, char *file, char *type, char *subtype, int c_type) { int output_subtype, output_encoding; + const char *reason = NULL; char *cp, *cf; int status; @@ -1563,8 +1630,8 @@ reformat_part (CT ct, char *file, char *type, char *subtype, int c_type) { /* Set subtype to 0, which is always an UNKNOWN subtype. */ output_subtype = 0; } - output_encoding = charset_encoding (ct); + output_encoding = content_encoding (ct, &reason); if (set_ct_type (ct, c_type, output_subtype, output_encoding) == OK) { ct->c_cefile.ce_file = file; ct->c_cefile.ce_unlink = 1; @@ -1577,18 +1644,9 @@ reformat_part (CT ct, char *file, char *type, char *subtype, int c_type) { } -/* Identifies 7bit or 8bit content based on charset. */ -static int -charset_encoding (CT ct) { - char *ct_charset = content_charset (ct); - int encoding = strcasecmp (ct_charset, "US-ASCII") ? CE_8BIT : CE_7BIT; - - free (ct_charset); - - return encoding; -} - - +/* + * Fill in a multipart/alternative part. + */ static CT build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) { char *boundary_prefix = "----=_nmh-multipart"; @@ -1600,8 +1658,7 @@ build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) { struct multipart *m; const struct str2init *ctinit; - if ((ct = (CT) mh_xcalloc (1, sizeof *ct)) == NULL) - adios (NULL, "out of memory"); + NEW0(ct); /* Set up the multipart/alternative part. These fields of *ct were initialized to 0 by mh_xcalloc(): @@ -1703,14 +1760,12 @@ build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) { add_param(&ct->c_ctinfo.ci_first_pm, &ct->c_ctinfo.ci_last_pm, "boundary", boundary, 0); - p = (struct part *) mh_xmalloc (sizeof *p); - p->mp_next = (struct part *) mh_xmalloc (sizeof *p->mp_next); + NEW(p); + NEW(p->mp_next); p->mp_next->mp_next = NULL; p->mp_next->mp_part = first_alt; - if ((m = (struct multipart *) mh_xcalloc (1, sizeof (struct multipart))) == - NULL) - adios (NULL, "out of memory"); + NEW0(m); m->mp_start = concat (boundary, "\n", NULL); m->mp_stop = concat (boundary, "--\n", NULL); m->mp_parts = p; @@ -1722,7 +1777,9 @@ build_multipart_alt (CT first_alt, CT new_part, int type, int subtype) { } -/* Check that the boundary does not appear in the content. */ +/* + * Check that the boundary does not appear in the content. + */ static int boundary_in_content (FILE **fp, char *file, const char *boundary) { char buffer[BUFSIZ]; @@ -1747,7 +1804,9 @@ boundary_in_content (FILE **fp, char *file, const char *boundary) { } -/* Remove all non-Content headers. */ +/* + * Remove all non-Content headers. + */ static void transfer_noncontent_headers (CT old, CT new) { HF hp, hp_prev; @@ -1789,6 +1848,9 @@ transfer_noncontent_headers (CT old, CT new) { } +/* + * Set content type. + */ static int set_ct_type (CT ct, int type, int subtype, int encoding) { char *typename = ct_type_str (type); @@ -1843,8 +1905,15 @@ set_ct_type (CT ct, int type, int subtype, int encoding) { } +/* + * It's not necessary to update the charset parameter of a Content-Type + * header for a text part. According to RFC 2045 Sec. 6.4, the body + * (content) was originally in the specified charset, "and will be in + * that character set again after decoding." + */ static int -decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mods) { +decode_text_parts (CT ct, int encoding, const char *decodetypes, + int *message_mods) { int status = OK; int lf_line_endings = 0; @@ -1856,7 +1925,8 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo /* Should check to see if the body for this part is encoded? For now, it gets passed along as-is by InitMultiPart(). */ for (part = m->mp_parts; status == OK && part; part = part->mp_next) { - status = decode_text_parts (part->mp_part, encoding, decodetypes, message_mods); + status = decode_text_parts (part->mp_part, encoding, decodetypes, + message_mods); } break; } @@ -1865,7 +1935,8 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo if (ct->c_subtype == MESSAGE_EXTERNAL) { struct exbody *e = (struct exbody *) ct->c_ctparams; - status = decode_text_parts (e->eb_content, encoding, decodetypes, message_mods); + status = decode_text_parts (e->eb_content, encoding, decodetypes, + message_mods); } break; @@ -1916,12 +1987,13 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo ct->c_cefile.ce_file = NULL; } else { int enc; + if (ct_encoding == CE_BINARY) { enc = CE_BINARY; } else if (ct_encoding == CE_8BIT && encoding == CE_7BIT) { enc = CE_QUOTED; } else { - enc = charset_encoding (ct); + enc = ct_encoding; } if (set_ce (ct, enc) == OK) { ++*message_mods; @@ -1958,8 +2030,10 @@ decode_text_parts (CT ct, int encoding, const char *decodetypes, int *message_mo } -/* Determine if the part with type[/subtype] should be decoded, according to - decodetypes (which came from the -decodetypes switch). */ +/* + * Determine if the part with type[/subtype] should be decoded, according to + * decodetypes (which came from the -decodetypes switch). + */ static int should_decode(const char *decodetypes, const char *type, const char *subtype) { /* Quick search for matching type[/subtype] in decodetypes: bracket @@ -1989,10 +2063,12 @@ should_decode(const char *decodetypes, const char *type, const char *subtype) { } -/* See if the decoded content is 7bit, 8bit, or binary. It's binary - if it has any NUL characters, a CR not followed by a LF, or lines - greater than 998 characters in length. If binary, reason is set - to a string explaining why. */ +/* + * See if the decoded content is 7bit, 8bit, or binary. It's binary + * if it has any NUL characters, a CR not followed by a LF, or lines + * greater than 998 characters in length. If binary, reason is set + * to a string explaining why. + */ static int content_encoding (CT ct, const char **reason) { CE ce = &ct->c_cefile; @@ -2048,6 +2124,9 @@ content_encoding (CT ct, const char **reason) { } +/* + * Strip carriage returns from content. + */ static int strip_crs (CT ct, int *message_mods) { char *charset = content_charset (ct); @@ -2199,6 +2278,106 @@ strip_crs (CT ct, int *message_mods) { } +/* + * Add/update, if necessary, the message C-T-E, based on the least restrictive + * of the part C-T-E's. + */ +static void +update_cte (CT ct) { + const int least_restrictive_enc = least_restrictive_encoding (ct); + + if (least_restrictive_enc != CE_UNKNOWN && + least_restrictive_enc != CE_7BIT) { + char *cte = concat (" ", ce_str (least_restrictive_enc), "\n", NULL); + HF hf; + int found_cte = 0; + + /* Update/add Content-Transfer-Encoding header field. */ + for (hf = ct->c_first_hf; hf; hf = hf->next) { + if (! strcasecmp (ENCODING_FIELD, hf->name)) { + found_cte = 1; + free (hf->value); + hf->value = cte; + } + } + if (! found_cte) { + add_header (ct, add (ENCODING_FIELD, NULL), cte); + } + } +} + + +/* + * Find the least restrictive encoding (7bit, 8bit, binary) of the parts + * within a message. + */ +static int +least_restrictive_encoding (CT ct) { + int encoding = CE_UNKNOWN; + + switch (ct->c_type) { + case CT_MULTIPART: { + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part; + + for (part = m->mp_parts; part; part = part->mp_next) { + const int part_encoding = + least_restrictive_encoding (part->mp_part); + + if (less_restrictive (encoding, part_encoding)) { + encoding = part_encoding; + } + } + break; + } + + case CT_MESSAGE: + if (ct->c_subtype == MESSAGE_EXTERNAL) { + struct exbody *e = (struct exbody *) ct->c_ctparams; + const int part_encoding = + least_restrictive_encoding (e->eb_content); + + if (less_restrictive (encoding, part_encoding)) { + encoding = part_encoding; + } + } + break; + + default: { + if (less_restrictive (encoding, ct->c_encoding)) { + encoding = ct->c_encoding; + } + }} + + return encoding; +} + + +/* + * Return whether the second encoding is less restrictive than the first, where + * "less restrictive" is in the sense used by RFC 2045 Secs. 6.1 and 6.4. So, + * CE_BINARY is less restrictive than CE_8BIT and + * CE_8BIT is less restrictive than CE_7BIT. + */ +static int +less_restrictive (int encoding, int second_encoding) { + switch (second_encoding) { + case CE_BINARY: + return encoding != CE_BINARY; + case CE_8BIT: + return encoding != CE_BINARY && encoding != CE_8BIT; + case CE_7BIT: + return encoding != CE_BINARY && encoding != CE_8BIT && + encoding != CE_7BIT; + default : + return 0; + } +} + + +/* + * Convert character set of each part. + */ static int convert_charsets (CT ct, char *dest_charset, int *message_mods) { int status = OK; @@ -2258,8 +2437,11 @@ convert_charsets (CT ct, char *dest_charset, int *message_mods) { /* * Fix various problems that aren't handled elsewhere. These * are fixed unconditionally: there are no switches to disable - * them. (Currently, "problems" is just one: an extraneous - * semicolon at the end of a header parameter list.) + * them. Currently, "problems" are these: + * 1) remove extraneous semicolon at the end of a header parameter list + * 2) replace RFC 2047 encoding with RFC 2231 encoding of name and + * filename parameters in Content-Type and Content-Disposition + * headers, respectively. */ static int fix_always (CT ct, int *message_mods) { @@ -2287,6 +2469,10 @@ fix_always (CT ct, int *message_mods) { default: { HF hf; + if (ct->c_first_hf) { + fix_filename_encoding (ct); + } + for (hf = ct->c_first_hf; hf; hf = hf->next) { size_t len = strlen (hf->value); @@ -2336,6 +2522,103 @@ fix_always (CT ct, int *message_mods) { } +/* + * Factor out common code for loops in fix_filename_encoding(). + */ +static int +fix_filename_param (char *name, char *value, PM *first_pm, PM *last_pm) { + size_t value_len; + int fixed = 0; + + if (((value_len = strlen (value)) > 0) && + strncmp (value, "=?", 2) == 0 && + strncmp (&value[value_len - 2], "?=", 2) == 0) { + /* Looks like an RFC 2047 encoded parameter. */ + char decoded[PATH_MAX + 1]; + + if (decode_rfc2047 (value, decoded, sizeof decoded)) { + /* Encode using RFC 2231. */ + replace_param (first_pm, last_pm, name, decoded, 0); + fixed = 1; + } else { + advise (NULL, "failed to decode %s parameter %s", name, value); + } + } + + return fixed; +} + + +/* + * Replace RFC 2047 encoding with RFC 2231 encoding of name and + * filename parameters in Content-Type and Content-Disposition + * headers, respectively. + */ +static int +fix_filename_encoding (CT ct) { + PM pm; + HF hf; + int fixed = 0; + + for (pm = ct->c_ctinfo.ci_first_pm; pm; pm = pm->pm_next) { + if (pm->pm_name && pm->pm_value && + strcasecmp (pm->pm_name, "name") == 0) { + fixed = fix_filename_param (pm->pm_name, pm->pm_value, + &ct->c_ctinfo.ci_first_pm, + &ct->c_ctinfo.ci_last_pm); + } + } + + for (pm = ct->c_dispo_first; pm; pm = pm->pm_next) { + if (pm->pm_name && pm->pm_value && + strcasecmp (pm->pm_name, "filename") == 0) { + fixed = fix_filename_param (pm->pm_name, pm->pm_value, + &ct->c_dispo_first, + &ct->c_dispo_last); + } + } + + /* Fix hf values to correspond. */ + for (hf = ct->c_first_hf; fixed && hf; hf = hf->next) { + enum { OTHER, TYPE_HEADER, DISPO_HEADER } field = OTHER; + + if (strcasecmp (hf->name, TYPE_FIELD) == 0) { + field = TYPE_HEADER; + } else if (strcasecmp (hf->name, DISPO_FIELD) == 0) { + field = DISPO_HEADER; + } + + if (field != OTHER) { + const char *const semicolon_loc = strchr (hf->value, ';'); + + if (semicolon_loc) { + const size_t len = + strlen (hf->name) + 1 + semicolon_loc - hf->value; + const char *const params = + output_params (len, + field == TYPE_HEADER + ? ct->c_ctinfo.ci_first_pm + : ct->c_dispo_first, + NULL, 0); + const char *const new_params = concat (params, "\n", NULL); + + replace_substring (&hf->value, semicolon_loc, new_params); + free ((char *) new_params); + free ((char *) params); + } else { + advise (NULL, "did not find semicolon in %s:%s\n", + hf->name, hf->value); + } + } + } + + return OK; +} + + +/* + * Output content in input file to output file. + */ static int write_content (CT ct, const char *input_filename, char *outfile, int modify_inplace, int message_mods) { @@ -2411,8 +2694,8 @@ write_content (CT ct, const char *input_filename, char *outfile, int modify_inpl /* - * parse_mime() does not set lf_line_endings in struct text, so use this function to do it. - * It touches the parts the decodetypes identifies. + * parse_mime() does not set lf_line_endings in struct text, so use this + * function to do it. It touches the parts the decodetypes identifies. */ static void set_text_ctparams(CT ct, char *decodetypes, int lf_line_endings) { @@ -2438,9 +2721,7 @@ set_text_ctparams(CT ct, char *decodetypes, int lf_line_endings) { default: if (should_decode(decodetypes, ct->c_ctinfo.ci_type, ct->c_ctinfo.ci_subtype)) { if (ct->c_ctparams == NULL) { - if ((ct->c_ctparams = (struct text *) mh_xcalloc (1, sizeof (struct text))) == NULL) { - adios (NULL, "out of memory"); - } + ct->c_ctparams = mh_xcalloc(1, sizeof (struct text)); } ((struct text *) ct->c_ctparams)->lf_line_endings = lf_line_endings; } @@ -2469,6 +2750,9 @@ remove_file (const char *file) { } +/* + * Output formatted message to user. + */ static void report (char *what, char *partno, char *filename, char *message, ...) { va_list args;