* mhbuildsbr.c -- routines to expand/translate MIME composition files
*
* $Id$
+ *
+ * This code is Copyright (c) 2002, by the authors of nmh. See the
+ * COPYRIGHT file in the root directory of the nmh distribution for
+ * complete copyright information.
*/
/*
#include <h/md5.h>
#include <errno.h>
#include <signal.h>
-#include <zotnet/mts/mts.h>
-#include <zotnet/tws/tws.h>
+#include <h/mts.h>
+#include <h/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
+#include <h/utils.h>
#ifdef TIME_WITH_SYS_TIME
# include <sys/time.h>
#endif
-extern int errno;
-
extern int debugsw;
extern int verbosw;
extern int ebcdicsw;
extern int listsw;
extern int rfc934sw;
+extern int contentidsw;
extern int endian; /* mhmisc.c */
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
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;
HF hp;
/* allocate header field structure */
- if (!(hp = malloc (sizeof(*hp))))
- adios (NULL, "out of memory");
+ hp = mh_xmalloc (sizeof(*hp));
/* link data into header structure */
hp->name = name;
}
/*
+ * 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)",
- ct->c_file, TYPE_FIELD, i, i, "", cp);
+ ct->c_file, TYPE_FIELD, i, i, "", cp);
}
return OK;
goto no_body;
}
- if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
- adios (NULL, "out of memory");
+ e->eb_body = bp = mh_xmalloc ((unsigned) size);
fseek (p->c_fp, p->c_begin, SEEK_SET);
while (size > 0)
switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
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");
}
}
}
+ 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);
/*
* 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;
add_header (ct, np, vp);
/*
- * output the Content-ID
+ * output the Content-ID, unless disabled by -nocontentid
*/
- if (ct->c_id) {
+ if (contentidsw && ct->c_id) {
np = add (ID_FIELD, NULL);
vp = concat (" ", ct->c_id, NULL);
add_header (ct, np, vp);
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
return OK;
}
+
+
+/* Make sure that buf contains at least one appearance of name,
+ followed by =. If not, insert both name and value, just after
+ first semicolon, if any. 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 *insertion;
+ char *cp;
+ char *prefix, *suffix;
+
+ /* Trim trailing space, esp. newline. */
+ for (cp = &buf[strlen (buf) - 1];
+ cp >= buf && isspace (*cp);
+ --cp) {
+ *cp = '\0';
+ }
+
+ insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
+
+ /* Insert at first semicolon, if any. If none, append to
+ end. */
+ prefix = add (buf, NULL);
+ if ((cp = strchr (prefix, ';'))) {
+ suffix = concat (cp, NULL);
+ *cp = '\0';
+ newbuf = concat (prefix, insertion, suffix, "\n", NULL);
+ free (suffix);
+ } else {
+ /* Append to end. */
+ newbuf = concat (buf, insertion, "\n", NULL);
+ }
+
+ free (prefix);
+ free (insertion);
+ free (buf);
+ }
+
+ 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;
+}