X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/6bc64765f773ce75454ec1592a86779e3547fe46..d6e398f9c:/uip/mhbuildsbr.c diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 70747c85..6186cfe4 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -32,7 +32,6 @@ extern int debugsw; -extern int verbosw; extern int listsw; extern int rfc934sw; @@ -42,8 +41,6 @@ extern int contentidsw; extern int rcachesw; /* mhcachesbr.c */ extern int wcachesw; /* mhcachesbr.c */ -pid_t xpid = 0; - static char prefix[] = "----- =_aaaaaaaaaa"; struct attach_list { @@ -76,9 +73,9 @@ static void setup_attach_content(CT, char *); static char *fgetstr (char *, int, FILE *); static int user_content (FILE *, char *, CT *); static void set_id (CT, int); -static int compose_content (CT); -static int scan_content (CT); -static int build_headers (CT); +static int compose_content (CT, int); +static int scan_content (CT, size_t); +static int build_headers (CT, int); static char *calculate_digest (CT, int); @@ -124,7 +121,7 @@ static void directive_pop(void) CT build_mime (char *infile, int autobuild, int dist, int directives, - int header_encoding) + int header_encoding, size_t maxunencoded, int verbose) { int compnum, state; char buf[BUFSIZ], name[NAMESZ]; @@ -439,7 +436,7 @@ finish_field: * Fill out, or expand directives. Parse and execute * commands specified by profile composition strings. */ - compose_content (ct); + compose_content (ct, verbose); if ((cp = strchr(prefix, 'a')) == NULL) adios (NULL, "internal error(4)"); @@ -449,7 +446,7 @@ finish_field: * check if prefix for multipart boundary clashes with * any of the contents. */ - while (scan_content (ct) == NOTOK) { + while (scan_content (ct, maxunencoded) == NOTOK) { if (*cp < 'z') { (*cp)++; } else { @@ -462,7 +459,7 @@ finish_field: /* Build the rest of the header field structures */ if (! dist) - build_headers (ct); + build_headers (ct, header_encoding); return ct; } @@ -1049,7 +1046,7 @@ set_id (CT ct, int top) */ static int -compose_content (CT ct) +compose_content (CT ct, int verbose) { CE ce = &ct->c_cefile; @@ -1075,7 +1072,7 @@ compose_content (CT ct) sprintf (pp, "%d", partnum); p->c_partno = add (partnam, NULL); - if (compose_content (p) == NOTOK) + if (compose_content (p, verbose) == NOTOK) return NOTOK; } @@ -1127,7 +1124,7 @@ compose_content (CT ct) if (!ce->ce_file) { pid_t child_id; int i, xstdout, len, buflen; - char *bp, **ap, *cp; + char *bp, *cp; char *vec[4], buffer[BUFSIZ]; FILE *out; CI ci = &ct->c_ctinfo; @@ -1159,11 +1156,12 @@ compose_content (CT ct) case 'a': { /* insert parameters from directive */ - char **ep; char *s = ""; + PM pm; - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep); + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { + snprintf (bp, buflen, "%s%s=\"%s\"", s, + pm->pm_name, get_param_value(pm, '?')); len = strlen (bp); bp += len; buflen -= len; @@ -1211,7 +1209,7 @@ raw: } } - if (verbosw) + if (verbose) printf ("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer); @@ -1278,11 +1276,12 @@ raw: */ static int -scan_content (CT ct) +scan_content (CT ct, size_t maxunencoded) { int len; - int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */ - int checklinelen = 0, linelen = 0; /* check for long lines */ + int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */ + int checklinelen = 0, linelen = 0; /* check for long lines */ + int checkllinelen = 0; /* check for extra-long lines */ int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */ int checklinespace = 0, linespace = 0; /* check if any line ends with space */ char *cp = NULL, buffer[BUFSIZ]; @@ -1304,7 +1303,7 @@ scan_content (CT ct) for (part = m->mp_parts; part; part = part->mp_next) { CT p = part->mp_part; - if (scan_content (p) == NOTOK) /* choose encoding for subpart */ + if (scan_content (p, maxunencoded) == NOTOK) /* choose encoding for subpart */ return NOTOK; /* if necessary, enlarge encoding for enclosing multipart */ @@ -1318,58 +1317,81 @@ scan_content (CT ct) } /* - * Decide what to check while scanning this content. + * Decide what to check while scanning this content. Note that + * for text content we always check for 8bit characters if the + * charset is unspecified, because that controls whether or not the + * character set is us-ascii or retrieved from the locale. */ - switch (ct->c_type) { - case CT_TEXT: - check8bit = 1; + + if (ct->c_type == CT_TEXT) { + t = (struct text *) ct->c_ctparams; + if (t->tx_charset == CHARSET_UNSPECIFIED) + check8bit = 1; + } + + switch (ct->c_reqencoding) { + case CE_8BIT: + checkllinelen = 1; checkboundary = 1; - if (ct->c_subtype == TEXT_PLAIN) { - checklinelen = 0; - checklinespace = 0; - } else { + break; + case CE_QUOTED: + checkboundary = 1; + break; + case CE_BASE64: + break; + case CE_UNKNOWN: + /* Use the default rules based on content-type */ + switch (ct->c_type) { + case CT_TEXT: + checkboundary = 1; checklinelen = 1; - checklinespace = 1; - } + if (ct->c_subtype == TEXT_PLAIN) { + checklinespace = 0; + } else { + checklinespace = 1; + } break; - case CT_APPLICATION: - check8bit = 1; - checklinelen = 1; - checklinespace = 1; - checkboundary = 1; + case CT_APPLICATION: + check8bit = 1; + checklinelen = 1; + checklinespace = 1; + checkboundary = 1; break; - case CT_MESSAGE: - check8bit = 0; - checklinelen = 0; - checklinespace = 0; + case CT_MESSAGE: + check8bit = 0; + checklinelen = 0; + checklinespace = 0; - /* don't check anything for message/external */ - if (ct->c_subtype == MESSAGE_EXTERNAL) - checkboundary = 0; - else - checkboundary = 1; - break; + /* don't check anything for message/external */ + if (ct->c_subtype == MESSAGE_EXTERNAL) + checkboundary = 0; + else + checkboundary = 1; + break; - case CT_AUDIO: - case CT_IMAGE: - case CT_VIDEO: - /* - * Don't check anything for these types, - * since we are forcing use of base64. - */ - check8bit = 0; - checklinelen = 0; - checklinespace = 0; - checkboundary = 0; - break; + case CT_AUDIO: + case CT_IMAGE: + case CT_VIDEO: + /* + * Don't check anything for these types, + * since we are forcing use of base64, unless + * the content-type was specified by a mhbuild directive. + */ + check8bit = 0; + checklinelen = 0; + checklinespace = 0; + checkboundary = 0; + break; + } } /* * Scan the unencoded content */ - if (check8bit || checklinelen || checklinespace || checkboundary) { + if (check8bit || checklinelen || checklinespace || checkboundary || + checkllinelen) { if ((in = fopen (ce->ce_file, "r")) == NULL) adios (ce->ce_file, "unable to open for reading"); len = strlen (prefix); @@ -1390,11 +1412,23 @@ scan_content (CT ct) /* * Check line length. */ - if (checklinelen && (strlen (buffer) > CPERLIN + 1)) { + if (checklinelen && (strlen (buffer) > maxunencoded + 1)) { linelen = 1; checklinelen = 0; /* no need to keep checking */ } + /* + * RFC 5322 specifies that a message cannot contain a line + * greater than 998 characters (excluding the CRLF). If we + * get one of those lines and linelen is NOT set, then abort. + */ + + if (checkllinelen && !linelen && + (strlen(buffer) > MAXLONGLINE + 1)) { + adios(NULL, "Line in content exceeds maximum line limit (%d)", + MAXLONGLINE); + } + /* * Check if line ends with a space. */ @@ -1422,61 +1456,58 @@ scan_content (CT ct) } /* - * Decide which transfer encoding to use. + * If the content is text and didn't specify a character set, + * we need to figure out which one was used. */ - switch (ct->c_type) { - case CT_TEXT: - /* - * If the text content didn't specify a character - * set, we need to figure out which one was used. - */ + + if (ct->c_type == CT_TEXT) { t = (struct text *) ct->c_ctparams; if (t->tx_charset == CHARSET_UNSPECIFIED) { CI ci = &ct->c_ctinfo; - char **ap, **ep; - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) - continue; - - if (contains8bit) { - *ap = concat ("charset=", write_charset_8bit(), NULL); - } else { - *ap = add ("charset=us-ascii", NULL); - } + add_param(&ci->ci_first_pm, &ci->ci_last_pm, "charset", + contains8bit ? write_charset_8bit() : "us-ascii", 0); t->tx_charset = CHARSET_SPECIFIED; - - cp = strchr(*ap++, '='); - *ap = NULL; - *cp++ = '\0'; - *ep = cp; } + } - if (contains8bit || linelen || linespace || checksw) - ct->c_encoding = CE_QUOTED; - else - ct->c_encoding = CE_7BIT; - break; + /* + * Decide which transfer encoding to use. + */ - case CT_APPLICATION: - /* For application type, use base64, except when postscript */ - if (contains8bit || linelen || linespace || checksw) - ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT) - ? CE_QUOTED : CE_BASE64; - else - ct->c_encoding = CE_7BIT; - break; + if (ct->c_reqencoding != CE_UNKNOWN) + ct->c_encoding = ct->c_reqencoding; + else + switch (ct->c_type) { + case CT_TEXT: + if (contains8bit && !linelen && !linespace && !checksw) + ct->c_encoding = CE_8BIT; + else if (contains8bit || linelen || linespace || checksw) + ct->c_encoding = CE_QUOTED; + else + ct->c_encoding = CE_7BIT; + break; - case CT_MESSAGE: - ct->c_encoding = CE_7BIT; - break; + case CT_APPLICATION: + /* For application type, use base64, except when postscript */ + if (contains8bit || linelen || linespace || checksw) + ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT) + ? CE_QUOTED : CE_BASE64; + else + ct->c_encoding = CE_7BIT; + break; - case CT_AUDIO: - case CT_IMAGE: - case CT_VIDEO: - /* For audio, image, and video contents, just use base64 */ - ct->c_encoding = CE_BASE64; - break; - } + case CT_MESSAGE: + ct->c_encoding = CE_7BIT; + break; + + case CT_AUDIO: + case CT_IMAGE: + case CT_VIDEO: + /* For audio, image, and video contents, just use base64 */ + ct->c_encoding = CE_BASE64; + break; + } return (boundaryclash ? NOTOK : OK); } @@ -1489,10 +1520,9 @@ scan_content (CT ct) */ static int -build_headers (CT ct) +build_headers (CT ct, int header_encoding) { int cc, mailbody, extbody, len; - char **ap, **ep; char *np, *vp, buffer[BUFSIZ]; CI ci = &ct->c_ctinfo; @@ -1501,16 +1531,10 @@ build_headers (CT ct) * boundary to the list of attribute/value pairs. */ if (ct->c_type == CT_MULTIPART) { - char *cp; static int level = 0; /* store nesting level */ - ap = ci->ci_attrs; - ep = ci->ci_values; - snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++); - cp = strchr(*ap++ = add (buffer, NULL), '='); - *ap = NULL; - *cp++ = '\0'; - *ep = cp; + snprintf (buffer, sizeof(buffer), "%s%d", prefix, level++); + add_param(&ci->ci_first_pm, &ci->ci_last_pm, "boundary", buffer, 0); } /* @@ -1540,59 +1564,16 @@ build_headers (CT ct) * Append the attribute/value pairs to * the end of the Content-Type line. */ - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (mailbody && !strcasecmp (*ap, "body")) - continue; - - vp = add (";", vp); - len++; - - /* - * According to RFC 2017, if we have a URL longer than 40 characters - * we have to break it across multiple lines - */ - - if (extbody && strcasecmp (*ap, "url") == 0) { - char *value = *ep; - - /* 7 here refers to " url=\"\"" */ - if (len + 1 + (cc = (min(MAXURLTOKEN, strlen(value)) + 7)) >= - CPERLIN) { - vp = add ("\n\t", vp); - len = 8; - } else { - vp = add (" ", vp); - len++; - } - - vp = add ("url=\"", vp); - len += 5; - while (strlen(value) > MAXURLTOKEN) { - strncpy(buffer, value, MAXURLTOKEN); - buffer[MAXURLTOKEN] = '\0'; - vp = add (buffer, vp); - vp = add ("\n\t", vp); - value += MAXURLTOKEN; - len = 8; - } + if (ci->ci_first_pm) { + char *s = output_params(len, ci->ci_first_pm, &len, mailbody); - vp = add (value, vp); - vp = add ("\"", vp); - len += strlen(value) + 1; - continue; - } + if (!s) + adios(NULL, "Internal error: failed outputting Content-Type " + "parameters"); - snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); - if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { - vp = add ("\n\t", vp); - len = 8; - } else { - vp = add (" ", vp); - len++; - } - vp = add (buffer, vp); - len += cc; + vp = add (s, vp); + free(s); } /* @@ -1622,23 +1603,34 @@ build_headers (CT ct) vp = concat (" ", ct->c_id, NULL); add_header (ct, np, vp); } - /* * output the Content-Description */ if (ct->c_descr) { np = add (DESCR_FIELD, NULL); vp = concat (" ", ct->c_descr, NULL); + if (encode_rfc2047(DESCR_FIELD, &vp, header_encoding, NULL)) + adios(NULL, "Unable to encode %s header", DESCR_FIELD); add_header (ct, np, vp); } /* - * output the Content-Disposition + * output the Content-Disposition. If it's NULL but c_dispo_type is + * set, then we need to build it. */ if (ct->c_dispo) { np = add (DISPO_FIELD, NULL); vp = concat (" ", ct->c_dispo, NULL); add_header (ct, np, vp); + } else if (ct->c_dispo_type) { + vp = concat (" ", ct->c_dispo_type, NULL); + len = strlen(DISPO_FIELD) + strlen(vp) + 1; + np = output_params(len, ct->c_dispo_first, NULL, 0); + vp = add(np, vp); + vp = add("\n", vp); + if (np) + free(np); + add_header (ct, getcpy(DISPO_FIELD), vp); } skip_headers: @@ -1722,7 +1714,7 @@ skip_headers: CT p; p = part->mp_part; - build_headers (p); + build_headers (p, header_encoding); } } break; @@ -1732,7 +1724,7 @@ skip_headers: struct exbody *e; e = (struct exbody *) ct->c_ctparams; - build_headers (e->eb_content); + build_headers (e->eb_content, header_encoding); } break; @@ -1844,8 +1836,9 @@ calculate_digest (CT ct, int asciiP) static void setup_attach_content(CT ct, char *filename) { - char *type, **ap, **ep, *simplename = r1bindex(filename, '/'); + char *type, *simplename = r1bindex(filename, '/'); struct str2init *s2i; + PM pm; if (! (type = mime_type(filename))) { adios(NULL, "Unable to determine MIME type of \"%s\"", filename); @@ -1901,20 +1894,18 @@ setup_attach_content(CT ct, char *filename) * content-description, and the content-disposition. */ - for (ap = ct->c_ctinfo.ci_attrs, ep = ct->c_ctinfo.ci_values; *ap; - ap++, ep++) { - if (strcasecmp(*ap, "name") == 0) { - if (*ep) - free(*ep); - *ep = getcpy(simplename); + for (pm = ct->c_ctinfo.ci_first_pm; pm; pm = pm->pm_next) { + if (strcasecmp(pm->pm_name, "name") == 0) { + if (pm->pm_value) + free(pm->pm_value); + pm->pm_value = getcpy(simplename); break; } } - if (*ap == NULL) { - *ap = getcpy("name"); - *ep = getcpy(simplename); - } + if (pm == NULL) + add_param(&ct->c_ctinfo.ci_first_pm, &ct->c_ctinfo.ci_last_pm, + "name", simplename, 0); ct->c_descr = getcpy(simplename); ct->c_descr = add("\n", ct->c_descr); @@ -1928,11 +1919,10 @@ setup_attach_content(CT ct, char *filename) if (strcasecmp(ct->c_ctinfo.ci_type, "text") == 0 && strcasecmp(ct->c_ctinfo.ci_subtype, "calendar") == 0) { - ct->c_dispo = getcpy("inline; filename=\""); + ct->c_dispo_type = getcpy("inline"); } else { - ct->c_dispo = getcpy("attachment; filename=\""); + ct->c_dispo_type = getcpy("attachment"); } - ct->c_dispo = add(simplename, ct->c_dispo); - ct->c_dispo = add("\"\n", ct->c_dispo); + add_param(&ct->c_dispo_first, &ct->c_dispo_last, "filename", simplename, 0); }