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
{
char *cp = *header_attrp;
PM pm;
+ struct sectlist {
+ char *value;
+ int index;
+ int len;
+ struct sectlist *next;
+ } *sp, *sp2;
+ struct parmlist {
+ char *name;
+ char *charset;
+ char *lang;
+ struct sectlist *sechead;
+ struct parmlist *next;
+ } *pp, *pp2, *phead = NULL;
while (*cp == ';') {
- char *dp, *vp, *up, c;
+ char *dp, *vp, *up, *nameptr, *valptr, *charset = NULL, *lang = NULL;
+ int encoded = 0, partial = 0, len = 0, index = 0;
cp++;
while (isspace ((unsigned char) *cp))
return NOTOK;
}
- pm = mh_xmalloc(sizeof(*pm));
- memset(pm, 0, sizeof(*pm));
+ /*
+ * To handle RFC 2231, we have to deal with the following extensions:
+ *
+ * name*=encoded-value
+ * name*<N>=part-N-of-a-parameter-value
+ * name*<N>*=encoded-part-N-of-a-parameter-value
+ *
+ * So the rule is:
+ * If there's a * right before the equal sign, it's encoded.
+ * If there's a * and one or more digits, then it's section N.
+ *
+ * Remember we can have one or the other, or both. cp points to
+ * beginning of name, up points past the last character in the
+ * parameter name.
+ */
+
+ for (vp = cp; vp < up; vp++) {
+ if (*vp == '*' && vp < up - 1) {
+ partial = 1;
+ continue;
+ } else if (*vp == '*' && vp == up - 1) {
+ encoded = 1;
+ } else if (partial) {
+ if (isdigit((unsigned char) *vp))
+ index = *vp - '0' + index * 10;
+ else {
+ advise (NULL, "invalid parameter index in message %s's "
+ "%s: field\n%*s(parameter %s)", filename,
+ fieldname, strlen(invo_name) + 2, "", cp);
+ return NOTOK;
+ }
+ } else {
+ len++;
+ }
+ }
+
+ /*
+ * Break out the parameter name and value sections and allocate
+ * memory for each.
+ */
+
+ nameptr = mh_xmalloc(len + 1);
+ strncpy(nameptr, cp, len);
+ nameptr[len] = '\0';
- /* This is all mega-bozo and needs cleanup */
- vp = (pm->pm_name = add (cp, NULL)) + (up - cp);
- *vp = '\0';
for (dp++; isspace ((unsigned char) *dp);)
dp++;
- /* Now store the attribute value. */
+ if (encoded) {
+ /*
+ * Single quotes delimit the character set and language tag.
+ * They are required on the first section (or a complete
+ * parameter).
+ */
+ if (index == 0) {
+ vp = dp;
+ while (*vp != '\'' && !isspace((unsigned char) *vp) &&
+ *vp != '\0')
+ vp++;
+ if (*vp == '\'') {
+ if (vp != dp) {
+ len = vp - dp;
+ charset = mh_xmalloc(len + 1);
+ strncpy(charset, dp, len);
+ charset[len] = '\0';
+ } else {
+ charset = NULL;
+ }
+ vp++;
+ } else {
+ advise(NULL, "missing charset in message %s's %s: "
+ "field\n%*s(parameter %s)", filename, fieldname,
+ strlen(invo_name) + 2, "", nameptr);
+ free(nameptr);
+ return NOTOK;
+ }
+ dp = 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';
+ } else {
+ lang = NULL;
+ }
+ 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;
+ }
+
+ /*
+ * At this point vp should be pointing at the beginning
+ * of the encoded value/section. Continue until we reach
+ * the end or get whitespace. But first, calculate the
+ * length so we can allocate the correct buffer size.
+ */
+
+ for (vp = dp, len = 0; istoken(*vp); vp++) {
+ if (*vp == '%') {
+ if (*(vp + 1) == '\0' ||
+ !isxdigit((unsigned char) *(vp + 1)) ||
+ *(vp + 2) == '\0' ||
+ !isxdigit((unsigned char) *(vp + 2))) {
+ advise(NULL, "invalid encoded sequence in message "
+ "%s's %s: field\n%*s(parameter %s)",
+ filename, fieldname, strlen(invo_name) + 2,
+ "", nameptr);
+ free(nameptr);
+ if (charset)
+ free(charset);
+ if (lang)
+ free(lang);
+ return NOTOK;
+ }
+ vp += 2;
+ }
+ len++;
+ }
- vp = pm->pm_name + (dp - cp);
+ up = valptr = mh_xmalloc(len + 1);
- if (*dp == '"') {
- for (cp = ++dp, dp = vp;;) {
- switch (c = *cp++) {
+ for (vp = dp; istoken(*vp); vp++) {
+ if (*vp == '%') {
+ *up++ = decode_qp(*(vp + 1), *(vp + 2));
+ vp += 2;
+ } else {
+ *up++ = *vp;
+ }
+ }
+
+ *up = '\0';
+ cp = vp;
+ } else {
+ /*
+ * A "normal" string. If it's got a leading quote, then we
+ * strip the quotes out. Otherwise go until we reach the end
+ * or get whitespace. Note we scan it twice; once to get the
+ * length, then the second time copies it into the destination
+ * buffer.
+ */
+
+ len = 0;
+
+ if (*dp == '"') {
+ for (cp = dp + 1;;) {
+ switch (*cp++) {
case '\0':
bad_quote:
advise (NULL,
"invalid quoted-string in message %s's %s: "
"field\n%*s(parameter %s)",
filename, fieldname, strlen(invo_name) + 2, "",
- pm->pm_name);
+ nameptr);
+ free(nameptr);
+ if (charset)
+ free(charset);
+ if (lang)
+ free(lang);
return NOTOK;
+ case '"':
+ break;
case '\\':
- *dp++ = c;
- if ((c = *cp++) == '\0')
+ if (*++cp == '\0')
goto bad_quote;
- /* else fall... */
-
+ /* FALL THROUGH */
default:
- *dp++ = c;
+ len++;
continue;
+ }
+ break;
+ }
- case '"':
- *dp = '\0';
+ } else {
+ for (cp = dp; istoken (*cp); cp++) {
+ len++;
+ }
+ }
+
+ valptr = mh_xmalloc(len + 1);
+
+ if (*dp == '"') {
+ int i;
+ for (cp = dp + 1, vp = valptr, i = 0; i < len; i++) {
+ if (*cp == '\\') {
+ cp++;
+ }
+ *vp++ = *cp++;
+ }
+ cp++;
+ } else {
+ strncpy(valptr, cp = dp, len);
+ cp += len;
+ }
+
+ valptr[len] = '\0';
+ }
+
+ /*
+ * If 'partial' is set, we don't allocate a parameter now. We
+ * put it on the parameter linked list to be reassembled later.
+ *
+ * "phead" points to a list of all parameters we need to reassemble.
+ * Each parameter has a list of sections. We insert the sections in
+ * order.
+ */
+
+ if (partial) {
+ for (pp = phead; pp != NULL; pp = pp->next) {
+ if (strcasecmp(nameptr, pp->name) == 0)
+ break;
+ }
+
+ if (pp == NULL) {
+ pp = mh_xmalloc(sizeof(*pp));
+ memset(pp, 0, sizeof(*pp));
+ pp->name = nameptr;
+ pp->next = phead;
+ phead = pp;
+ }
+
+ /*
+ * Insert this into the section linked list
+ */
+
+ sp = mh_xmalloc(sizeof(*sp));
+ memset(sp, 0, sizeof(*sp));
+ sp->value = valptr;
+ sp->index = index;
+ sp->len = len;
+
+ if (pp->sechead == NULL || pp->sechead->index > index) {
+ sp->next = pp->sechead;
+ pp->sechead = sp;
+ } else {
+ for (sp2 = pp->sechead; sp2 != NULL; sp2 = sp2->next) {
+ if (sp2->index == sp->index) {
+ advise (NULL, "duplicate index (%d) in message "
+ "%s's %s: field\n%*s(parameter %s)", sp->index,
+ filename, fieldname, strlen(invo_name) + 2, "",
+ nameptr);
+ return NOTOK;
+ }
+ if (sp2->index < sp->index &&
+ (sp2->next == NULL || sp2->next->index > sp->index)) {
+ sp->next = sp2->next;
+ sp2->next = sp;
break;
+ }
+ }
+
+ if (sp2 == NULL) {
+ advise(NULL, "Internal error: cannot insert partial "
+ "param in message %s's %s: field\n%*s(parameter %s)",
+ filename, fieldname, strlen(invo_name) + 2, "",
+ nameptr);
+ return NOTOK;
}
- break;
+ }
+
+ /*
+ * Save our charset and lang tags.
+ */
+
+ if (index == 0 && encoded) {
+ if (pp->charset)
+ free(pp->charset);
+ pp->charset = charset;
+ if (pp->lang)
+ free(pp->lang);
+ pp->lang = lang;
}
} else {
- for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
- continue;
- *dp = '\0';
- }
- pm->pm_value = getcpy(vp);
- if (!*vp) {
- advise (NULL,
- "invalid parameter in message %s's %s: "
- "field\n%*s(parameter %s)",
- filename, fieldname, strlen(invo_name) + 2, "",
- pm->pm_name);
- return NOTOK;
+ pm = add_param(param_head, param_tail, nameptr, valptr, 1);
+ pm->pm_charset = charset;
+ pm->pm_lang = lang;
}
while (isspace ((unsigned char) *cp))
get_comment (filename, fieldname, &cp, commentp) == NOTOK) {
return NOTOK;
}
+ }
- if (*param_head == NULL) {
- *param_head = pm;
- *param_tail = pm;
- } else {
- (*param_tail)->pm_next = pm;
- *param_tail = pm;
+ /*
+ * 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. Return pointer is
+ * only valid until the next call to content_charset().
+ */
+
+char *
+content_charset (CT ct) {
+ static char *ret_charset = NULL;
+
+ if (ret_charset != NULL) {
+ free(ret_charset);
+ }
+
+ ret_charset = get_param(ct->c_ctinfo.ci_first_pm, "charset", '?', 0);
+
+ return ret_charset ? ret_charset : "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
*/
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_name = getcpy(name);
- pm->pm_value = getcpy(value);
+ pm->pm_name = nocopy ? name : getcpy(name);
+ pm->pm_value = nocopy ? value : getcpy(value);
if (*first) {
(*last)->pm_next = pm;
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.
int utf8;
iconv_t cd;
ICONV_CONST char *p;
+#else /* HAVE_ICONV */
+ char *p;
#endif /* HAVE_ICONV */
+
char *q;
/*
*q = '\0';
return buffer;
-#endif /* HAVE_ICONV */
noiconv:
+#endif /* HAVE_ICONV */
+
/*
* Take everything non-ASCII and substituite the replacement character
*/