From: David Levine Date: Sat, 18 Feb 2006 16:26:37 +0000 (+0000) Subject: Added support for optional Content_Disposition header in mhbuild directive.s X-Git-Tag: nmh-1_3_RC1~59 X-Git-Url: http://git.marmaro.de/?p=mmh;a=commitdiff_plain;h=1513f7668c494c4583141d6115669b7198c14556;hp=4586e3f4cffbf0f1c3cdd5804d01da16ae714cfe Added support for optional Content_Disposition header in mhbuild directive.s --- diff --git a/ChangeLog b/ChangeLog index 48c9806..8245154 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2006-02-18 David Levine + + * h/mime.h, h/mhparse.h, uip/mhbuildsbr.c, uip/mhfree.c, + man/mhbuild.man, docs/TODO: added support for an optional + Content-Disposition header in mhbuild (only). Its contents + are supplied between {}, positioned after the optional [], in + a mhbuild directive. If the contents do not contain a + "filename=" parameter, and the directive has a filename, or + something else that ends with "name=", then that will be used + to add a "filename=" parameter to the header. + 2006-02-12 David Levine * docs/TODO: added RFC2183 to reference of RFC1806 for diff --git a/docs/TODO b/docs/TODO index c4b8409..97d4ef6 100644 --- a/docs/TODO +++ b/docs/TODO @@ -106,7 +106,6 @@ MHBUILD ------- * add ability to specify Content-Transfer-Encoding in composition drafts. -* add support for Content-Disposition header (rfc1806, rfc2183). * remove the code for caching from mhbuild. MHL diff --git a/h/mhparse.h b/h/mhparse.h index f99a1c7..812b78f 100644 --- a/h/mhparse.h +++ b/h/mhparse.h @@ -90,6 +90,7 @@ struct Content { char *c_celine; /* Content-Transfer-Encoding: */ char *c_id; /* Content-ID: */ char *c_descr; /* Content-Description: */ + char *c_dispo; /* Content-Disposition: */ char *c_partno; /* within multipart content */ /* Content-Type info */ diff --git a/h/mime.h b/h/mime.h index b741b2f..1a85b42 100644 --- a/h/mime.h +++ b/h/mime.h @@ -12,6 +12,7 @@ #define ENCODING_FIELD "Content-Transfer-Encoding" #define ID_FIELD "Content-ID" #define DESCR_FIELD "Content-Description" +#define DISPO_FIELD "Content-Disposition" #define MD5_FIELD "Content-MD5" #define isatom(c) (!isspace (c) && !iscntrl (c) && (c) != '(' \ diff --git a/man/mhbuild.man b/man/mhbuild.man index bd1484c..8dd7458 100644 --- a/man/mhbuild.man +++ b/man/mhbuild.man @@ -212,6 +212,7 @@ separated accordingly. For example, type=tar; \\ conversions=compress \\ [this is the nmh distribution] \\ + {application; filename="nmh.tar.gz"} \\ name="nmh.tar.gz"; \\ directory="/pub/nmh"; \\ site="ftp.math.gatech.edu"; \\ @@ -223,7 +224,8 @@ separated accordingly. For example, You must give a description string to separate the content parameters from the external-parameters (although this string may be empty). This description string is specified by enclosing it within -\*(lq[]\*(rq. +\*(lq[]\*(rq. A disposition string, to appear in a +Content-Disposition header, may appear in the optional \*(lq{}\*(rq. .PP These parameters are of the form: .PP @@ -311,6 +313,29 @@ character. This description will be copied into the .fi .RE .PP +Similarly, a disposition string may optionally be provided between +\*(lq{\*(rq and \*(lq}\*(rq characters; it will be copied into the +\*(lqContent-Disposition\*(rq header when the directive is processed. +If a disposition string is provided that does not contain a filename +parameter, and a filename is provided in the directive, it will be +added to the \*(lqContent-Disposition\*(rq header. For example, the +following directive: +.PP +.RS 5 +.nf +#text/plain; charset=iso-8859-1 <>{attachment} /tmp/summary.txt +.fi +.RE +.PP +creates these message part headers: +.PP +.RS 5 +.nf +Content-Type: text/plain; charset="iso-8859-1" +Content-Disposition: attachment; filename="summary.txt" +.fi +.RE +.PP By default, .B mhbuild will generate a unique \*(lqContent-ID:\*(rq for each directive, @@ -555,6 +580,7 @@ directive ::= "#" type "/" subtype [ "(" comment ")" ] [ "<" id ">" ] [ "[" description "]" ] + [ "{" disposition "}" ] [ filename ] EOL @@ -563,18 +589,21 @@ directive ::= "#" type "/" subtype [ "(" comment ")" ] [ "<" id ">" ] [ "[" description "]" ] + [ "{" disposition "}" ] external-parameters EOL | "#forw" [ "<" id ">" ] [ "[" description "]" ] + [ "{" disposition "}" ] [ "+"folder ] [ 0*msg ] EOL | "#begin" [ "<" id ">" ] [ "[" description "]" ] + [ "{" disposition "}" ] [ "alternative" | "parallel" | something-else ] @@ -591,6 +620,7 @@ plaintext ::= [ "Content-Description:" 0*(";" attribute "=" value) [ "(" comment ")" ] [ "[" description "]" ] + [ "{" disposition "}" ] EOL 1*line [ "#" EOL ] diff --git a/uip/mhbuildsbr.c b/uip/mhbuildsbr.c index 83a18b5..25924e9 100644 --- a/uip/mhbuildsbr.c +++ b/uip/mhbuildsbr.c @@ -186,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 @@ -651,6 +653,16 @@ get_content (FILE *in, char *file, int toplevel) goto got_header; } + /* Get Content-Disposition field */ + if (!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)) { char *cp, *dp, *ep; @@ -1058,11 +1070,51 @@ 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)", @@ -2311,6 +2363,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"); } @@ -2950,6 +3004,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); @@ -3868,10 +3945,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; @@ -3950,6 +4027,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 @@ -4228,3 +4314,67 @@ invalid_digest: return OK; } + + +/* Make sure that buf contains at least one appearance of name, + followed by =. If not, append both name and value. 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 *appendage; + char *cp; + + /* Trim trailing space, esp. newline. */ + for (cp = &buf[strlen (buf) - 1]; cp >= buf && isspace (*cp); --cp) { + *cp = '\0'; + } + + appendage = concat ("; ", name, "=", "\"", value, "\"\n", NULL); + newbuf = add (appendage, buf); + free (appendage); + } + + 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; +} diff --git a/uip/mhfree.c b/uip/mhfree.c index 9d064ba..abaeb2b 100644 --- a/uip/mhfree.c +++ b/uip/mhfree.c @@ -101,6 +101,8 @@ free_content (CT ct) free (ct->c_id); if (ct->c_descr) free (ct->c_descr); + if (ct->c_dispo) + free (ct->c_dispo); if (ct->c_file) { if (ct->c_unlink)