]> diplodocus.org Git - nmh/blobdiff - uip/mhbuildsbr.c
Merge branch 'tmpfiles'
[nmh] / uip / mhbuildsbr.c
index 7d187b395ed87bdad075c3ed4a0a21a14d03dbd0..70747c85ee93a35fca37d17d698e61367ee9bd38 100644 (file)
@@ -19,8 +19,6 @@
 #include <fcntl.h>
 #include <h/signals.h>
 #include <h/md5.h>
-#include <errno.h>
-#include <signal.h>
 #include <h/mts.h>
 #include <h/tws.h>
 #include <h/mime.h>
@@ -44,16 +42,21 @@ extern int contentidsw;
 extern int rcachesw;   /* mhcachesbr.c */
 extern int wcachesw;   /* mhcachesbr.c */
 
-/*
- * Directory to place tmp files.  This must
- * be set before these routines are called.
- */
-char *tmp;
-
 pid_t xpid = 0;
 
 static char prefix[] = "----- =_aaaaaaaaaa";
 
+struct attach_list {
+    char *filename;
+    struct attach_list *next;
+};
+
+/*
+ * Maximum size of URL token in message/external-body
+ */
+
+#define MAXURLTOKEN 40
+
 
 /* mhmisc.c */
 void content_error (char *, CT, char *, ...);
@@ -62,21 +65,16 @@ void content_error (char *, CT, char *, ...);
 int find_cache (CT, int, int *, char *, char *, int);
 
 /* mhfree.c */
-void free_content (CT);
 void free_ctinfo (CT);
 void free_encoding (CT, int);
 
-/*
- * prototypes
- */
-CT build_mime (char *, int);
-
 /*
  * static prototypes
  */
 static int init_decoded_content (CT);
+static void setup_attach_content(CT, char *);
 static char *fgetstr (char *, int, FILE *);
-static int user_content (FILE *, char *, char *, CT *);
+static int user_content (FILE *, char *, CT *);
 static void set_id (CT, int);
 static int compose_content (CT);
 static int scan_content (CT);
@@ -125,7 +123,8 @@ static void directive_pop(void)
  */
 
 CT
-build_mime (char *infile, int directives)
+build_mime (char *infile, int autobuild, int dist, int directives,
+           int header_encoding)
 {
     int        compnum, state;
     char buf[BUFSIZ], name[NAMESZ];
@@ -134,7 +133,9 @@ build_mime (char *infile, int directives)
     struct part **pp;
     CT ct;
     FILE *in;
+    HF hp;
     m_getfld_state_t gstate = 0;
+    struct attach_list *attach_head = NULL, *attach_tail = NULL, *at_entry;
 
     directive_init(directives);
 
@@ -162,6 +163,7 @@ build_mime (char *infile, int directives)
      * draft into the linked list of header fields for
      * the new MIME message.
      */
+    m_getfld_track_filepos (&gstate, in);
     for (compnum = 1;;) {
        int bufsz = sizeof buf;
        switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) {
@@ -169,16 +171,19 @@ build_mime (char *infile, int directives)
        case FLDPLUS:
            compnum++;
 
-           /* abort if draft has Mime-Version header field */
-           if (!mh_strcasecmp (name, VRSN_FIELD))
-               adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
-
-           /* abort if draft has Content-Transfer-Encoding header field */
-           if (!mh_strcasecmp (name, ENCODING_FIELD))
-               adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
+           /* abort if draft has Mime-Version or C-T-E header field */
+           if (strcasecmp (name, VRSN_FIELD) == 0 ||
+               strcasecmp (name, ENCODING_FIELD) == 0) {
+               if (autobuild) {
+                   fclose(in);
+                   return NULL;
+               } else {
+                   adios (NULL, "draft shouldn't contain %s: field", name);
+               }
+           }
 
            /* ignore any Content-Type fields in the header */
-           if (!mh_strcasecmp (name, TYPE_FIELD)) {
+           if (!strcasecmp (name, TYPE_FIELD)) {
                while (state == FLDPLUS) {
                    bufsz = sizeof buf;
                    state = m_getfld (&gstate, name, buf, &bufsz, in);
@@ -197,19 +202,56 @@ build_mime (char *infile, int directives)
                vp = add (buf, vp);     /* add to previous value */
            }
 
-           /* Now add the header data to the list */
-           add_header (ct, np, vp);
+           /*
+            * Now add the header data to the list, unless it's an attach
+            * header; in that case, add it to our attach list
+            */
+
+           if (strcasecmp(ATTACH_FIELD, np) == 0) {
+               struct attach_list *entry;
+               char *s = vp, *e = vp + strlen(vp) - 1;
+               free(np);
+
+               /*
+                * Make sure we can find the start of this filename.
+                * If it's blank, we skip completely.  Otherwise, strip
+                * off any leading spaces and trailing newlines.
+                */
+
+               while (isspace((unsigned char) *s))
+                   s++;
+
+               while (e > s && *e == '\n')
+                   *e-- = '\0';
+
+               if (*s == '\0') {
+                   free(vp);
+                   goto finish_field;
+               }
+
+               entry = mh_xmalloc(sizeof(*entry));
+               entry->filename = getcpy(s);
+               entry->next = NULL;
+               free(vp);
+
+               if (attach_tail) {
+                   attach_tail->next = entry;
+                   attach_tail = entry;
+               } else {
+                   attach_head = attach_tail = entry;
+               }
+           } else {
+               add_header (ct, np, vp);
+           }
 
 finish_field:
            /* if this wasn't the last header field, then continue */
            continue;
 
-       case FILEEOF:
-           adios (NULL, "draft has empty body -- no directives!");
-           /* NOTREACHED */
-
        case BODY:
            fseek (in, (long) (-strlen (buf)), SEEK_CUR);
+           /* fall through */
+       case FILEEOF:
            break;
 
        case LENERR:
@@ -223,13 +265,27 @@ finish_field:
     }
     m_getfld_state_destroy (&gstate);
 
+    /*
+     * Iterate through the list of headers and call the function to MIME-ify
+     * them if required.
+     */
+
+    for (hp = ct->c_first_hf; hp != NULL; hp = hp->next) {
+       if (encode_rfc2047(hp->name, &hp->value, header_encoding, NULL)) {
+           adios(NULL, "Unable to encode header \"%s\"", hp->name);
+       }
+    }
+
     /*
      * Now add the MIME-Version header field
      * to the list of header fields.
      */
-    np = add (VRSN_FIELD, NULL);
-    vp = concat (" ", VRSN_VALUE, "\n", NULL);
-    add_header (ct, np, vp);
+
+    if (! dist) {
+       np = add (VRSN_FIELD, NULL);
+       vp = concat (" ", VRSN_VALUE, "\n", NULL);
+       add_header (ct, np, vp);
+    }
 
     /*
      * We initally assume we will find multiple contents in the
@@ -255,7 +311,7 @@ finish_field:
        struct part *part;
        CT p;
 
-       if (user_content (in, infile, buf, &p) == DONE) {
+       if (user_content (in, buf, &p) == DONE) {
            admonish (NULL, "ignoring spurious #end");
            continue;
        }
@@ -269,16 +325,94 @@ finish_field:
        part->mp_part = p;
     }
 
+    /*
+     * Add any Attach headers to the list of MIME parts at the end of the
+     * message.
+     */
+
+    for (at_entry = attach_head; at_entry; ) {
+       struct attach_list *at_prev = at_entry;
+       struct part *part;
+       CT p;
+
+       if (access(at_entry->filename, R_OK) != 0) {
+           adios("reading", "Unable to open %s for", at_entry->filename);
+       }
+
+       if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+           adios(NULL, "out of memory");
+
+       init_decoded_content(p);
+
+       /*
+        * Initialize our content structure based on the filename,
+        * and fill in all of the relevant fields.  Also place MIME
+        * parameters in the attributes array.
+        */
+
+       setup_attach_content(p, at_entry->filename);
+
+       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+           adios (NULL, "out of memory");
+       *pp = part;
+       pp = &part->mp_next;
+       part->mp_part = p;
+
+       at_entry = at_entry->next;
+       free(at_prev->filename);
+       free(at_prev);
+    }
+
+    /*
+     * To allow for empty message bodies, if we've found NO content at all
+     * yet cook up an empty text/plain part.
+     */
+
+    if (!m->mp_parts) {
+       CT p;
+       struct part *part;
+       struct text *t;
+
+       if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
+           adios(NULL, "out of memory");
+
+       init_decoded_content(p);
+
+       if (get_ctinfo ("text/plain", p, 0) == NOTOK)
+           done (1);
+
+       p->c_type = CT_TEXT;
+       p->c_subtype = TEXT_PLAIN;
+       p->c_encoding = CE_7BIT;
+       p->c_file = getcpy(infile);
+       /*
+        * Sigh.  ce_file contains the "decoded" contents of this part.
+        * So this seems like the best option available since we're going
+        * to call scan_content() on this.
+        */
+       p->c_cefile.ce_file = getcpy("/dev/null");
+       p->c_begin = ftell(in);
+       p->c_end = ftell(in);
+
+       if ((t = (struct text *) calloc (1, sizeof (*t))) == NULL)
+           adios (NULL, "out of memory");
+
+       t->tx_charset = CHARSET_SPECIFIED;
+       p->c_ctparams = t;
+
+       if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
+           adios (NULL, "out of memory");
+       *pp = part;
+       pp = &part->mp_next;
+       part->mp_part = p;
+    }
+
     /*
      * close the composition draft since
      * it's not needed any longer.
      */
     fclose (in);
 
-    /* check if any contents were found */
-    if (!m->mp_parts)
-       adios (NULL, "no content directives found");
-
     /*
      * If only one content was found, then remove and
      * free the outer multipart content.
@@ -327,7 +461,8 @@ finish_field:
     }
 
     /* Build the rest of the header field structures */
-    build_headers (ct);
+    if (! dist)
+       build_headers (ct);
 
     return ct;
 }
@@ -341,12 +476,6 @@ finish_field:
 static int
 init_decoded_content (CT ct)
 {
-    CE ce;
-
-    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
-       adios (NULL, "out of memory");
-
-    ct->c_cefile     = ce;
     ct->c_ceopenfnx  = open7Bit;       /* since unencoded */
     ct->c_ceclosefnx = close_encoding;
     ct->c_cesizefnx  = NULL;           /* since unencoded */
@@ -399,11 +528,10 @@ fgetstr (char *s, int n, FILE *stream)
  */
 
 static int
-user_content (FILE *in, char *file, char *buf, CT *ctp)
+user_content (FILE *in, char *buf, CT *ctp)
 {
     int        extrnal, vrsn;
-    unsigned char *cp;
-    char **ap;
+    char *cp, **ap;
     char buffer[BUFSIZ];
     struct multipart *m;
     struct part **pp;
@@ -425,7 +553,7 @@ user_content (FILE *in, char *file, char *buf, CT *ctp)
 
     /* allocate basic structure for handling decoded content */
     init_decoded_content (ct);
-    ce = ct->c_cefile;
+    ce = &ct->c_cefile;
 
     ci = &ct->c_ctinfo;
     set_id (ct, 0);
@@ -446,8 +574,10 @@ user_content (FILE *in, char *file, char *buf, CT *ctp)
        FILE *out;
         char *cp;
 
-        cp = m_mktemp2(NULL, invo_name, NULL, &out);
-        if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
+       if ((cp = m_mktemp2(NULL, invo_name, NULL, &out)) == NULL) {
+           adios("mhbuildsbr", "unable to create temporary file in %s",
+                 get_temp_dir());
+       }
 
        /* use a temp file to collect the plain text lines */
        ce->ce_file = add (cp, NULL);
@@ -542,7 +672,7 @@ rock_and_roll:
            done (1);
 
        for (s2i = str2cts; s2i->si_key; s2i++)
-           if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
+           if (!strcasecmp (ci->ci_type, s2i->si_key))
                break;
        if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
            s2i++;
@@ -552,7 +682,7 @@ rock_and_roll:
         */
        switch (ct->c_type = s2i->si_val) {
        case CT_MESSAGE:
-           if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
+           if (!strcasecmp (ci->ci_subtype, "rfc822")) {
                ct->c_encoding = CE_7BIT;
                goto call_init;
            }
@@ -588,7 +718,7 @@ call_init:
 
     /* check directive against the list of MIME types */
     for (s2i = str2cts; s2i->si_key; s2i++)
-       if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
+       if (!strcasecmp (ci->ci_type, s2i->si_key))
            break;
 
     /*
@@ -609,10 +739,10 @@ call_init:
            /* NOTREACHED */
 
        case CT_MESSAGE:
-           if (!mh_strcasecmp (ci->ci_subtype, "partial"))
+           if (!strcasecmp (ci->ci_subtype, "partial"))
                adios (NULL, "sorry, \"#%s/%s\" isn't supported",
                       ci->ci_type, ci->ci_subtype);
-           if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
+           if (!strcasecmp (ci->ci_subtype, "external-body"))
                adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
                       ci->ci_type, ci->ci_subtype);
 use_forw:
@@ -676,7 +806,7 @@ use_forw:
        if (ci->ci_magic) {
            /* check if specifies command to execute */
            if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
-               for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
+               for (cp = ci->ci_magic + 1; isspace ((unsigned char) *cp); cp++)
                    continue;
                if (!*cp)
                    adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
@@ -719,7 +849,7 @@ use_forw:
      * Message directive
      * #forw [+folder] [msgs]
      */
-    if (!mh_strcasecmp (ci->ci_type, "forw")) {
+    if (!strcasecmp (ci->ci_type, "forw")) {
        int msgnum;
        char *folder, *arguments[MAXARGS];
        struct msgs *mp;
@@ -748,7 +878,7 @@ use_forw:
        if (!folder)
            folder = add (getfolder (1), NULL);
 
-       if (!(mp = folder_read (folder)))
+       if (!(mp = folder_read (folder, 0)))
            adios (NULL, "unable to read folder %s", folder);
        for (ap = arguments; *ap; ap++) {
            cp = *ap;
@@ -786,7 +916,7 @@ use_forw:
                    if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
                        adios (NULL, "out of memory");
                    init_decoded_content (p);
-                   pe = p->c_cefile;
+                   pe = &p->c_cefile;
                    if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
                        done (1);
                    p->c_type = CT_MESSAGE;
@@ -825,7 +955,7 @@ use_forw:
     /*
      * #end
      */
-    if (!mh_strcasecmp (ci->ci_type, "end")) {
+    if (!strcasecmp (ci->ci_type, "end")) {
        free_content (ct);
        *ctp = NULL;
        return DONE;
@@ -834,14 +964,14 @@ use_forw:
     /*
      * #begin [ alternative | parallel ]
      */
-    if (!mh_strcasecmp (ci->ci_type, "begin")) {
+    if (!strcasecmp (ci->ci_type, "begin")) {
        if (!ci->ci_magic) {
            vrsn = MULTI_MIXED;
            cp = SubMultiPart[vrsn - 1].kv_key;
-       } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
+       } else if (!strcasecmp (ci->ci_magic, "alternative")) {
            vrsn = MULTI_ALTERNATE;
            cp = SubMultiPart[vrsn - 1].kv_key;
-       } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
+       } else if (!strcasecmp (ci->ci_magic, "parallel")) {
            vrsn = MULTI_PARALLEL;
            cp = SubMultiPart[vrsn - 1].kv_key;
        } else if (uprf (ci->ci_magic, "digest")) {
@@ -867,7 +997,7 @@ use_forw:
            struct part *part;
            CT p;
 
-           if (user_content (in, file, buffer, &p) == DONE) {
+           if (user_content (in, buffer, &p) == DONE) {
                if (!m->mp_parts)
                    adios (NULL, "empty \"#begin ... #end\" sequence");
                return OK;
@@ -921,7 +1051,7 @@ set_id (CT ct, int top)
 static int
 compose_content (CT ct)
 {
-    CE ce = ct->c_cefile;
+    CE ce = &ct->c_cefile;
 
     switch (ct->c_type) {
     case CT_MULTIPART:
@@ -1006,10 +1136,10 @@ compose_content (CT ct)
            if (!(cp = ci->ci_magic))
                adios (NULL, "internal error(5)");
 
-            tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
-            if (tfile == NULL) {
-                adios("mhbuildsbr", "unable to create temporary file");
-            }
+           if ((tfile = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) {
+               adios("mhbuildsbr", "unable to create temporary file in %s",
+                     get_temp_dir());
+           }
            ce->ce_file = add (tfile, NULL);
            ce->ce_unlink = 1;
 
@@ -1095,7 +1225,7 @@ raw:
            if ((out = fopen (ce->ce_file, "w")) == NULL)
                adios (ce->ce_file, "unable to open for writing");
 
-           for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
+           for (i = 0; (child_id = fork()) == NOTOK && i > 5; i++)
                sleep (5);
            switch (child_id) {
            case NOTOK:
@@ -1155,10 +1285,10 @@ scan_content (CT ct)
     int checklinelen = 0, linelen = 0;   /* check for 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          */
-    unsigned char *cp = NULL, buffer[BUFSIZ];
+    char *cp = NULL, buffer[BUFSIZ];
     struct text *t = NULL;
     FILE *in = NULL;
-    CE ce = ct->c_cefile;
+    CE ce = &ct->c_cefile;
 
     /*
      * handle multipart by scanning all subparts
@@ -1250,7 +1380,7 @@ scan_content (CT ct)
             */
            if (check8bit) {
                for (cp = buffer; *cp; cp++) {
-                   if (!isascii (*cp)) {
+                   if (!isascii ((unsigned char) *cp)) {
                        contains8bit = 1;
                        check8bit = 0;  /* no need to keep checking */
                    }
@@ -1268,7 +1398,7 @@ scan_content (CT ct)
            /*
             * Check if line ends with a space.
             */
-           if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
+           if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace ((unsigned char) *cp)) {
                linespace = 1;
                checklinespace = 0;     /* no need to keep checking */
            }
@@ -1279,10 +1409,10 @@ scan_content (CT ct)
             */
            if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
                for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
-                   if (!isspace (*cp))
+                   if (!isspace ((unsigned char) *cp))
                        break;
                *++cp = '\0';
-               if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
+               if (!strncmp(buffer + 2, prefix, len) && isdigit((unsigned char) buffer[2 + len])) {
                    boundaryclash = 1;
                    checkboundary = 0;  /* no need to keep checking */
                }
@@ -1361,7 +1491,7 @@ scan_content (CT ct)
 static int
 build_headers (CT ct)
 {
-    int        cc, mailbody, len;
+    int        cc, mailbody, extbody, len;
     char **ap, **ep;
     char *np, *vp, buffer[BUFSIZ];
     CI ci = &ct->c_ctinfo;
@@ -1403,21 +1533,56 @@ build_headers (CT ct)
     len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
                + strlen (ci->ci_subtype) + 3;
 
-    mailbody = ct->c_type == CT_MESSAGE
-       && ct->c_subtype == MESSAGE_EXTERNAL
-       && ((struct exbody *) ct->c_ctparams)->eb_body;
+    extbody = ct->c_type == CT_MESSAGE && ct->c_subtype == MESSAGE_EXTERNAL;
+    mailbody = extbody && ((struct exbody *) ct->c_ctparams)->eb_body;
 
     /*
      * 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 && !mh_strcasecmp (*ap, "body"))
+       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;
+           }
+
+           vp = add (value, vp);
+           vp = add ("\"", vp);
+           len += strlen(value) + 1;
+           continue;
+       }
+
        snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
        if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
            vp = add ("\n\t", vp);
@@ -1592,7 +1757,7 @@ calculate_digest (CT ct, int asciiP)
     unsigned char digest[16];
     unsigned char outbuf[25];
     MD5_CTX mdContext;
-    CE ce = ct->c_cefile;
+    CE ce = &ct->c_cefile;
     char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
     FILE *in;
 
@@ -1641,7 +1806,8 @@ calculate_digest (CT ct, int asciiP)
     }
 
     /* encode the digest using base64 */
-    for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
+    for (dp = digest, op = (char *) outbuf,
+                               cc = sizeof(digest) / sizeof(digest[0]);
                cc > 0; cc -= 3, op += 4) {
        unsigned long bits;
        char *bp;
@@ -1669,3 +1835,104 @@ calculate_digest (CT ct, int asciiP)
     vp = concat (" ", outbuf, "\n", NULL);
     return vp;
 }
+
+/*
+ * Set things up for the content structure for file "filename" that
+ * we want to attach
+ */
+
+static void
+setup_attach_content(CT ct, char *filename)
+{
+    char *type, **ap, **ep, *simplename = r1bindex(filename, '/');
+    struct str2init *s2i;
+
+    if (! (type = mime_type(filename))) {
+       adios(NULL, "Unable to determine MIME type of \"%s\"", filename);
+    }
+
+    /*
+     * Parse the Content-Type.  get_ctinfo() parses MIME parameters, but
+     * since we're just feeding it a MIME type we have to add those ourself.
+     * Map that to a valid content-type label and call any initialization
+     * function.
+     */
+
+    if (get_ctinfo(type, ct, 0) == NOTOK)
+       done(1);
+
+    free(type);
+
+    for (s2i = str2cts; s2i->si_key; s2i++)
+       if (strcasecmp(ct->c_ctinfo.ci_type, s2i->si_key) == 0)
+           break;
+    if (!s2i->si_key && !uprf(ct->c_ctinfo.ci_type, "X-"))
+       s2i++;
+
+    /*
+     * Make sure the type isn't incompatible with what we can handle
+     */
+
+    switch (ct->c_type = s2i->si_val) {
+    case CT_MULTIPART:
+       adios (NULL, "multipart types must be specified by mhbuild directives");
+       /* NOTREACHED */
+
+    case CT_MESSAGE:
+       if (strcasecmp(ct->c_ctinfo.ci_subtype, "partial") == 0)
+           adios(NULL, "Sorry, %s/%s isn't supported", ct->c_ctinfo.ci_type,
+                 ct->c_ctinfo.ci_subtype);
+       if (strcasecmp(ct->c_ctinfo.ci_subtype, "external-body") == 0)
+           adios(NULL, "external-body messages must be specified "
+                 "by mhbuild directives");
+       /* Fall through */
+
+    default:
+       /*
+        * This sets the subtype, if it's significant
+        */
+       if ((ct->c_ctinitfnx = s2i->si_init))
+           (*ct->c_ctinitfnx)(ct);
+       break;
+    }
+
+    /*
+     * Feed in a few attributes; specifically, the name attribute, the
+     * 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);
+           break;
+       }
+    }
+
+    if (*ap == NULL) {
+       *ap = getcpy("name");
+       *ep = getcpy(simplename);
+    }
+
+    ct->c_descr = getcpy(simplename);
+    ct->c_descr = add("\n", ct->c_descr);
+    ct->c_cefile.ce_file = getcpy(filename);
+
+    /*
+     * If it's a text/calendar, we need to make sure it's an inline,
+     * otherwise it won't work with some calendar programs.  Otherwise
+     * assume attachment
+     */
+
+    if (strcasecmp(ct->c_ctinfo.ci_type, "text") == 0 &&
+       strcasecmp(ct->c_ctinfo.ci_subtype, "calendar") == 0) {
+       ct->c_dispo = getcpy("inline; filename=\"");
+    } else {
+       ct->c_dispo = getcpy("attachment; filename=\"");
+    }
+
+    ct->c_dispo = add(simplename, ct->c_dispo);
+    ct->c_dispo = add("\"\n", ct->c_dispo);
+}