From: Ken Hornstein Date: Wed, 12 Feb 2014 04:17:00 +0000 (-0500) Subject: Beginnings of RFC 2231 support. Does not compile (yet). X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/3b11b987c2e48062f9db46cf6006faf9b1bbf454?ds=inline Beginnings of RFC 2231 support. Does not compile (yet). --- diff --git a/h/mhparse.h b/h/mhparse.h index 58c8a8a7..3ff7c70f 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -52,7 +52,7 @@ struct Parameter { char *pm_value; /* Parameter value */ char *pm_charset; /* Parameter character set (optional) */ char *pm_lang; /* Parameter language tag (optional) */ - PM *pm_next; /* Pointer to next element */ + PM pm_next; /* Pointer to next element */ }; /* @@ -62,8 +62,8 @@ struct Parameter { struct CTinfo { char *ci_type; /* content type */ char *ci_subtype; /* content subtype */ - PM *ci_first_pm; /* Pointer to first MIME parameter */ - PM *ci_last_pm; /* Pointer to last MIME parameter */ + PM ci_first_pm; /* Pointer to first MIME parameter */ + PM ci_last_pm; /* Pointer to last MIME parameter */ char *ci_comment; /* RFC-822 comments */ char *ci_magic; }; @@ -112,8 +112,8 @@ struct Content { char *c_descr; /* Content-Description: */ char *c_dispo; /* Content-Disposition: */ char *c_dispo_type; /* Type of Content-Disposition */ - PM *c_dispo_first; /* Pointer to first disposition parm */ - PM *c_dispo_last; /* Pointer to last disposition parm */ + PM c_dispo_first; /* Pointer to first disposition parm */ + PM c_dispo_last; /* Pointer to last disposition parm */ char *c_partno; /* within multipart content */ /* Content-Type info */ @@ -343,4 +343,20 @@ const char *ce_str (int); const struct str2init *get_ce_method (const char *); int parse_header_attrs (const char *, int, char **, CI, int *); +/* + * Given a linked list of parameters, build an output string for them. This + * string is designed to be concatenated on an already-built header. + * + * Arguments are: + * + * initialwidth - Current width of the header. Used to compute when to wrap + * parameters on the first line. The following lines will + * be prefixed by a tab (\t) character. + * params - Pointer to head of linked list of parameters. + * + * Returns a pointer to the resulting parameter string. This string must + * be free()'d by the caller. Returns NULL on error. + */ +char *output_params(size_t initialwidth, PM params); + extern int checksw; /* Add Content-MD5 field */ diff --git a/h/mime.h b/h/mime.h index d152094a..fad1cf88 100644 --- a/h/mime.h +++ b/h/mime.h @@ -35,6 +35,19 @@ && (c) != '/' && (c) != '[' && (c) != ']' \ && (c) != '?' && (c) != '=') +/* + * Definitions for RFC 2231 encoding + */ +#define istspecial(c) ((c) == '(' || (c) == ')' || (c) == '<' || (c) == '>' \ + || (c) == '@' || (c) == ',' || (c) == ';' \ + || (c) == ':' || (c) == '\\' || (c) == '"' \ + || (c) == '/' || (c) == '[' || (c) == ']' \ + || (c) == '?' || (c) == '=') + +#define isparamencode(c) (!isascii((unsigned char) c) || \ + iscntrl((unsigned char) c) || istspecial(c) || \ + (c) == '*' || (c) == '\'' || (c) == '%') + #define MAXTEXTPERLN 78 #define MAXLONGLINE 998 #define CPERLIN 76 diff --git a/uip/mhoutsbr.c b/uip/mhoutsbr.c index 88f610dd..7c3cbe99 100644 --- a/uip/mhoutsbr.c +++ b/uip/mhoutsbr.c @@ -81,11 +81,12 @@ output_content (CT ct, FILE *out) { int result = 0; CI ci = &ct->c_ctinfo; - char *boundary = ci->ci_values[0], **ap, **vp; + PM pm; + char *boundary = ""; - for (ap = ci->ci_attrs, vp = ci->ci_values; *ap; ++ap, ++vp) { - if (! strcasecmp ("boundary", *ap)) { - boundary = *vp; + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { + if (! strcasecmp ("boundary", pm->pm_name)) { + boundary = pm->pm_value; break; } } @@ -163,7 +164,7 @@ output_content (CT ct, FILE *out) body, don't emit the newline that would appear between the headers and body. In that case, the call to write8Bit() shouldn't be needed, but is harmless. */ - if (ct->c_ctinfo.ci_attrs[0] != NULL || + if (ct->c_ctinfo.ci_first_pm != NULL || ct->c_begin != ct->c_end) { putc ('\n', out); } @@ -226,7 +227,8 @@ output_headers (CT ct, FILE *out) static int writeExternalBody (CT ct, FILE *out) { - char **ap, **ep, *cp; + char *cp; + PM pm; struct exbody *e = (struct exbody *) ct->c_ctparams; putc ('\n', out); @@ -246,17 +248,17 @@ writeExternalBody (CT ct, FILE *out) continue; case 'N': - for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++) - if (!strcasecmp (*ap, "name")) { - fprintf (out, "%s", *ep); + for (pm = ci2->ci_first_pm; pm; pm = pm->pm_next) + if (!strcasecmp (pm->pm_name, "name")) { + fprintf (out, "%s", pm->pm_value); break; } continue; case 'T': fprintf (out, "%s/%s", ci2->ci_type, ci2->ci_subtype); - for (ap = ci2->ci_attrs, ep = ci2->ci_values; *ap; ap++, ep++) - fprintf (out, "; %s=\"%s\"", *ap, *ep); + for (pm = ci2->ci_first_pm; pm; pm = pm->pm_next) + fprintf (out, "; %s=\"%s\"", pm->pm_name, pm->pm_value); continue; case 'n': diff --git a/uip/mhparse.c b/uip/mhparse.c index 8af26906..4509e764 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -135,6 +135,7 @@ static int readDigest (CT, char *); static int get_leftover_mp_content (CT, int); static int InitURL (CT); static int openURL (CT, char **); +static size_t param_len(PM, int, size_t, int *); struct str2init str2cts[] = { { "application", CT_APPLICATION, InitApplication }, @@ -1007,7 +1008,8 @@ InitText (CT ct) { char buffer[BUFSIZ]; char *chset = NULL; - char **ap, **ep, *cp; + char *cp; + PM pm; struct k2v *kv; struct text *t; CI ci = &ct->c_ctinfo; @@ -1028,13 +1030,13 @@ InitText (CT ct) ct->c_ctparams = (void *) t; /* scan for charset parameter */ - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) - if (!strcasecmp (*ap, "charset")) + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) + if (!strcasecmp (pm->pm_name, "charset")) break; /* check if content specified a character set */ - if (*ap) { - chset = *ep; + if (pm) { + chset = pm->pm_value; t->tx_charset = CHARSET_SPECIFIED; } else { t->tx_charset = CHARSET_UNSPECIFIED; @@ -1066,7 +1068,8 @@ InitMultiPart (CT ct) { int inout; long last, pos; - char *cp, *dp, **ap, **ep; + char *cp, *dp; + PM pm; char *bp, buffer[BUFSIZ]; struct multipart *m; struct k2v *kv; @@ -1111,15 +1114,15 @@ InitMultiPart (CT ct) * required for multipart messages. */ bp = 0; - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "boundary")) { - bp = *ep; + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { + if (!strcasecmp (pm->pm_name, "boundary")) { + bp = pm->pm_value; break; } } /* complain if boundary parameter is missing */ - if (!*ap) { + if (!pm) { advise (NULL, "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field", ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD); @@ -1324,7 +1327,7 @@ InitMessage (CT ct) case MESSAGE_PARTIAL: { - char **ap, **ep; + PM pm; struct partial *p; if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL) @@ -1332,25 +1335,25 @@ InitMessage (CT ct) ct->c_ctparams = (void *) p; /* scan for parameters "id", "number", and "total" */ - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "id")) { - p->pm_partid = add (*ep, NULL); + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { + if (!strcasecmp (pm->pm_name, "id")) { + p->pm_partid = add (pm->pm_value, NULL); continue; } - if (!strcasecmp (*ap, "number")) { - if (sscanf (*ep, "%d", &p->pm_partno) != 1 + if (!strcasecmp (pm->pm_name, "number")) { + if (sscanf (pm->pm_value, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) { invalid_param: advise (NULL, "invalid %s parameter for \"%s/%s\" type in message %s's %s field", - *ap, ci->ci_type, ci->ci_subtype, + pm->pm_name, ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD); return NOTOK; } continue; } - if (!strcasecmp (*ap, "total")) { - if (sscanf (*ep, "%d", &p->pm_maxno) != 1 + if (!strcasecmp (pm->pm_name, "total")) { + if (sscanf (pm->pm_value, "%d", &p->pm_maxno) != 1 || p->pm_maxno < 1) goto invalid_param; continue; @@ -1465,21 +1468,21 @@ no_body: int params_external (CT ct, int composing) { - char **ap, **ep; + PM pm; struct exbody *e = (struct exbody *) ct->c_ctparams; CI ci = &ct->c_ctinfo; ct->c_ceopenfnx = NULL; - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { - if (!strcasecmp (*ap, "access-type")) { + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { + if (!strcasecmp (pm->pm_name, "access-type")) { struct str2init *s2i; CT p = e->eb_content; for (s2i = str2methods; s2i->si_key; s2i++) - if (!strcasecmp (*ep, s2i->si_key)) + if (!strcasecmp (pm->pm_value, s2i->si_key)) break; if (!s2i->si_key) { - e->eb_access = *ep; + e->eb_access = pm->pm_value; e->eb_flags = NOTOK; p->c_encoding = CE_EXTERNAL; continue; @@ -1493,46 +1496,46 @@ params_external (CT ct, int composing) return NOTOK; continue; } - if (!strcasecmp (*ap, "name")) { - e->eb_name = *ep; + if (!strcasecmp (pm->pm_name, "name")) { + e->eb_name = pm->pm_value; continue; } - if (!strcasecmp (*ap, "permission")) { - e->eb_permission = *ep; + if (!strcasecmp (pm->pm_name, "permission")) { + e->eb_permission = pm->pm_value; continue; } - if (!strcasecmp (*ap, "site")) { - e->eb_site = *ep; + if (!strcasecmp (pm->pm_name, "site")) { + e->eb_site = pm->pm_value; continue; } - if (!strcasecmp (*ap, "directory")) { - e->eb_dir = *ep; + if (!strcasecmp (pm->pm_name, "directory")) { + e->eb_dir = pm->pm_value; continue; } - if (!strcasecmp (*ap, "mode")) { - e->eb_mode = *ep; + if (!strcasecmp (pm->pm_name, "mode")) { + e->eb_mode = pm->pm_value; continue; } - if (!strcasecmp (*ap, "size")) { - sscanf (*ep, "%lu", &e->eb_size); + if (!strcasecmp (pm->pm_name, "size")) { + sscanf (pm->pm_value, "%lu", &e->eb_size); continue; } - if (!strcasecmp (*ap, "server")) { - e->eb_server = *ep; + if (!strcasecmp (pm->pm_name, "server")) { + e->eb_server = pm->pm_value; continue; } - if (!strcasecmp (*ap, "subject")) { - e->eb_subject = *ep; + if (!strcasecmp (pm->pm_name, "subject")) { + e->eb_subject = pm->pm_value; continue; } - if (!strcasecmp (*ap, "url")) { + if (!strcasecmp (pm->pm_name, "url")) { /* * According to RFC 2017, we have to remove all whitespace from * the URL */ - char *u, *p = *ep; - e->eb_url = u = mh_xmalloc(strlen(*ep) + 1); + char *u, *p = pm->pm_value; + e->eb_url = u = mh_xmalloc(strlen(pm->pm_value) + 1); for (; *p != '\0'; p++) { if (! isspace((unsigned char) *p)) @@ -1542,8 +1545,8 @@ params_external (CT ct, int composing) *u = '\0'; continue; } - if (composing && !strcasecmp (*ap, "body")) { - e->eb_body = getcpy (*ep); + if (composing && !strcasecmp (pm->pm_name, "body")) { + e->eb_body = getcpy (pm->pm_value); continue; } } @@ -2206,18 +2209,18 @@ open7Bit (CT ct, char **file) } if (ct->c_type == CT_MULTIPART) { - char **ap, **ep; + PM pm; CI ci = &ct->c_ctinfo; len = 0; fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype); len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type) + 1 + strlen (ci->ci_subtype); - for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) { + for (pm = ci->ci_first_pm; pm; pm = pm->pm_next) { putc (';', ce->ce_fp); len++; - snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep); + snprintf (buffer, sizeof(buffer), "%s=\"%s\"", , *ep); if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) { fputs ("\n\t", ce->ce_fp); @@ -3351,3 +3354,124 @@ bad_quote: *header_attrp = cp; return OK; } + +/* + * Create a string based on a list of output parameters. Assume that this + * parameter string will be appended to an existing header, so start out + * with the separator (;). Perform RFC 2231 encoding when necessary. + */ + +char * +output_params(size_t initialwidth, PM params) +{ + char *paramout = NULL; + char line[CPERLIN + 1], *q; + int curlen, index, eightbit; + size_t valoff; + + while (params != NULL) { + index = 0; + valoff = 0; + q = line; + + curlen = param_len(params, index, valoff, &eightbit); + + /* + * If this won't fit on the line, start a new one. Save room in + * case we need a semicolon on the end + */ + + if (initialwidth + curlen > CPERLIN - 1) { + paramout = add(";\n\t", paramout); + initialwidth = 8; + } + + /* + * Loop until we get a parameter that fits within a line. + */ + + while (initialwidth + curlen > CPERLIN - 1) { + int curvallen = strlen(params->pm_value + valoff) - + (initialwidth + curlen - (CPERLIN - 1)); + + /* + * curvallen holds how many characters we take from this + * current value. Make sure it's at least 1. + */ + + if (curvallen < 1) + curvallen = 1; + } + params = params->pm_next; + } + + return paramout; +} + +/* + * Calculate the size of a parameter. Include any necessary encoding. + * Start the length computation from where "offset" is marked. + */ + +static size_t +param_len(PM pm, int index, size_t valueoff, int *eightbit) +{ + char *start = pm->pm_value + valueoff, *p; + size_t len = 0; + + *eightbit = 0; + + /* + * Add up the length. First, start with the parameter name, and include + * the equal sign. + */ + + len += strlen(pm->pm_name) + 1; + + /* + * Scan the parameter value. If we find an 8-bit character, then + * we need to compute the locale name for the length. + */ + + for (p = start; *p != '\0'; p++) { + if (! isascii((unsigned char) *p)) { + *eightbit = 1; + break; + } + } + + /* + * If we've got 8-bit character, put the locale on the front (if we're + * doing part 0. Also compute the length of the string based on the + * encoding we need to do. + */ + + if (*eightbit) { + len++; /* For the encoding we need to do */ + if (index == 0) { + len += strlen(write_charset_8bit()) + 2; /* Plus extra '' */ + } else { + /* + * We know we definitely need to include an index. + * This will get the length wrong if we have more than 99 + * sections. I can live with that. + */ + len += 2; /* * */ + if (index > 9) + len++; + } + for (p = start; *p != '\0'; p++) { + if (isparamencode(*p)) + len += 3; + else + len++; + } + } else { + /* + * Much simpler! Don't forget opening and closing quotes. + */ + len += strlen(start) + 2; + } + + return len; +}