]> diplodocus.org Git - nmh/blobdiff - uip/mhparse.c
Went through the sources and checked where etcpath() is
[nmh] / uip / mhparse.c
index dd7c83de2936aeebb6e764ff1050bf9d02443916..08eaa9ab33428c9af1b096c8e1f3233b6364fde2 100644 (file)
@@ -104,12 +104,12 @@ void free_encoding (CT, int);
  * static prototypes
  */
 static CT get_content (FILE *, char *, int);
  * static prototypes
  */
 static CT get_content (FILE *, char *, int);
-static int get_comment (CT, char **, int);
+static int get_comment (const char *, CI, char **, int);
 
 static int InitGeneric (CT);
 static int InitText (CT);
 static int InitMultiPart (CT);
 
 static int InitGeneric (CT);
 static int InitText (CT);
 static int InitMultiPart (CT);
-static void reverse_parts (CT);
+void reverse_parts (CT);
 static int InitMessage (CT);
 static int InitApplication (CT);
 static int init_encoding (CT, OpenCEFunc);
 static int InitMessage (CT);
 static int InitApplication (CT);
 static int init_encoding (CT, OpenCEFunc);
@@ -128,6 +128,8 @@ static int InitMail (CT);
 static int openMail (CT, char **);
 static int readDigest (CT, char *);
 static int get_leftover_mp_content (CT, int);
 static int openMail (CT, char **);
 static int readDigest (CT, char *);
 static int get_leftover_mp_content (CT, int);
+static int InitURL (CT);
+static int openURL (CT, char **);
 
 struct str2init str2cts[] = {
     { "application", CT_APPLICATION, InitApplication },
 
 struct str2init str2cts[] = {
     { "application", CT_APPLICATION, InitApplication },
@@ -162,6 +164,7 @@ struct str2init str2methods[] = {
     { "ftp",         0,        InitFTP },
     { "local-file",  0,        InitFile },
     { "mail-server", 0,        InitMail },
     { "ftp",         0,        InitFTP },
     { "local-file",  0,        InitFile },
     { "mail-server", 0,        InitMail },
+    { "url",         0, InitURL },
     { NULL,          0, NULL }
 };
 
     { NULL,          0, NULL }
 };
 
@@ -368,7 +371,8 @@ get_content (FILE *in, char *file, int toplevel)
            if (debugsw)
                fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
 
            if (debugsw)
                fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
 
-           if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
+           if (*cp == '('  &&
+                get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK)
                goto out;
 
            for (dp = cp; istoken (*dp); dp++)
                goto out;
 
            for (dp = cp; istoken (*dp); dp++)
@@ -477,7 +481,8 @@ get_content (FILE *in, char *file, int toplevel)
            if (debugsw)
                fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
 
            if (debugsw)
                fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
 
-           if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
+           if (*cp == '('  &&
+                get_comment (ct->c_file, &ct->c_ctinfo, &cp, 0) == NOTOK) {
                free (ep);
                goto out;
            }
                free (ep);
                goto out;
            }
@@ -667,9 +672,10 @@ int
 get_ctinfo (char *cp, CT ct, int magic)
 {
     int        i;
 get_ctinfo (char *cp, CT ct, int magic)
 {
     int        i;
-    char *dp, **ap, **ep;
+    char *dp;
     char c;
     CI ci;
     char c;
     CI ci;
+    int status;
 
     ci = &ct->c_ctinfo;
     i = strlen (invo_name) + 2;
 
     ci = &ct->c_ctinfo;
     i = strlen (invo_name) + 2;
@@ -693,7 +699,7 @@ get_ctinfo (char *cp, CT ct, int magic)
     if (debugsw)
        fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
 
     if (debugsw)
        fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
 
-    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+    if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
        return NOTOK;
 
     for (dp = cp; istoken (*dp); dp++)
        return NOTOK;
 
     for (dp = cp; istoken (*dp); dp++)
@@ -716,7 +722,7 @@ get_ctinfo (char *cp, CT ct, int magic)
     while (isspace ((unsigned char) *cp))
        cp++;
 
     while (isspace ((unsigned char) *cp))
        cp++;
 
-    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+    if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
        return NOTOK;
 
     if (*cp != '/') {
        return NOTOK;
 
     if (*cp != '/') {
@@ -729,7 +735,7 @@ get_ctinfo (char *cp, CT ct, int magic)
     while (isspace ((unsigned char) *cp))
        cp++;
 
     while (isspace ((unsigned char) *cp))
        cp++;
 
-    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+    if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
        return NOTOK;
 
     for (dp = cp; istoken (*dp); dp++)
        return NOTOK;
 
     for (dp = cp; istoken (*dp); dp++)
@@ -754,103 +760,11 @@ magic_skip:
     while (isspace ((unsigned char) *cp))
        cp++;
 
     while (isspace ((unsigned char) *cp))
        cp++;
 
-    if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
+    if (*cp == '(' && get_comment (ct->c_file, &ct->c_ctinfo, &cp, 1) == NOTOK)
        return NOTOK;
 
        return NOTOK;
 
-    /*
-     * Parse attribute/value pairs given with Content-Type
-     */
-    ep = (ap = ci->ci_attrs) + NPARMS;
-    while (*cp == ';') {
-       char *vp, *up;
-
-       if (ap >= ep) {
-           advise (NULL,
-                   "too many parameters in message %s's %s: field (%d max)",
-                   ct->c_file, TYPE_FIELD, NPARMS);
-           return NOTOK;
-       }
-
-       cp++;
-       while (isspace ((unsigned char) *cp))
-           cp++;
-
-       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
-           return NOTOK;
-
-       if (*cp == 0) {
-           advise (NULL,
-                   "extraneous trailing ';' in message %s's %s: parameter list",
-                   ct->c_file, TYPE_FIELD);
-           return OK;
-       }
-
-       /* down case the attribute name */
-       for (dp = cp; istoken ((unsigned char) *dp); dp++)
-           if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp))
-               *dp = tolower ((unsigned char) *dp);
-
-       for (up = dp; isspace ((unsigned char) *dp);)
-           dp++;
-       if (dp == cp || *dp != '=') {
-           advise (NULL,
-                   "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
-                   ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
-           return NOTOK;
-       }
-
-       vp = (*ap = add (cp, NULL)) + (up - cp);
-       *vp = '\0';
-       for (dp++; isspace ((unsigned char) *dp);)
-           dp++;
-
-       /* now add the attribute value */
-       ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
-
-       if (*dp == '"') {
-           for (cp = ++dp, dp = vp;;) {
-               switch (c = *cp++) {
-                   case '\0':
-bad_quote:
-                       advise (NULL,
-                               "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
-                               ct->c_file, TYPE_FIELD, i, i, "", *ap);
-                       return NOTOK;
-
-                   case '\\':
-                       *dp++ = c;
-                       if ((c = *cp++) == '\0')
-                           goto bad_quote;
-                       /* else fall... */
-
-                   default:
-                       *dp++ = c;
-                       continue;
-
-                   case '"':
-                       *dp = '\0';
-                       break;
-               }
-               break;
-           }
-       } else {
-           for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
-               continue;
-           *dp = '\0';
-       }
-       if (!*vp) {
-           advise (NULL,
-                   "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
-                   ct->c_file, TYPE_FIELD, i, i, "", *ap);
-           return NOTOK;
-       }
-       ap++;
-
-       while (isspace ((unsigned char) *cp))
-           cp++;
-
-       if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
-           return NOTOK;
+    if (parse_header_attrs (ct->c_file, i, &cp, ci, &status) == NOTOK) {
+       return status;
     }
 
     /*
     }
 
     /*
@@ -963,14 +877,12 @@ bad_quote:
 
 
 static int
 
 
 static int
-get_comment (CT ct, char **ap, int istype)
+get_comment (const char *filename, CI ci, char **ap, int istype)
 {
     int i;
     char *bp, *cp;
     char c, buffer[BUFSIZ], *dp;
 {
     int i;
     char *bp, *cp;
     char c, buffer[BUFSIZ], *dp;
-    CI ci;
 
 
-    ci = &ct->c_ctinfo;
     cp = *ap;
     bp = buffer;
     cp++;
     cp = *ap;
     bp = buffer;
     cp++;
@@ -980,7 +892,7 @@ get_comment (CT ct, char **ap, int istype)
        case '\0':
 invalid:
        advise (NULL, "invalid comment in message %s's %s: field",
        case '\0':
 invalid:
        advise (NULL, "invalid comment in message %s's %s: field",
-               ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
+               filename, istype ? TYPE_FIELD : VRSN_FIELD);
        return NOTOK;
 
        case '\\':
        return NOTOK;
 
        case '\\':
@@ -1247,11 +1159,11 @@ end_part:
        }
     }
 
        }
     }
 
-    if (suppress_bogus_mp_content_warning) {
-        bogus_mp_content = 1;
-    } else {
+    if (! suppress_bogus_mp_content_warning) {
         advise (NULL, "bogus multipart content in message %s", ct->c_file);
     }
         advise (NULL, "bogus multipart content in message %s", ct->c_file);
     }
+    bogus_mp_content = 1;
+
     if (!inout && part) {
        p = part->mp_part;
        p->c_end = ct->c_end;
     if (!inout && part) {
        p = part->mp_part;
        p->c_end = ct->c_end;
@@ -1313,48 +1225,23 @@ last_part:
 
 
 /*
 
 
 /*
- * reverse the order of the parts of a multipart
+ * reverse the order of the parts of a multipart/alternative
  */
 
  */
 
-static void
+void
 reverse_parts (CT ct)
 {
 reverse_parts (CT ct)
 {
-    int i;
-    struct multipart *m;
-    struct part **base, **bmp, **next, *part;
-
-    m = (struct multipart *) ct->c_ctparams;
-
-    /* if only one part, just return */
-    if (!m->mp_parts || !m->mp_parts->mp_next)
-       return;
-
-    /* count number of parts */
-    i = 0;
-    for (part = m->mp_parts; part; part = part->mp_next)
-       i++;
-
-    /* allocate array of pointers to the parts */
-    if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
-       adios (NULL, "out of memory");
-    bmp = base;
-
-    /* point at all the parts */
-    for (part = m->mp_parts; part; part = part->mp_next)
-       *bmp++ = part;
-    *bmp = NULL;
+    struct multipart *m = (struct multipart *) ct->c_ctparams;
+    struct part *part;
+    struct part *next;
 
 
-    /* reverse the order of the parts */
-    next = &m->mp_parts;
-    for (bmp--; bmp >= base; bmp--) {
-       part = *bmp;
-       *next = part;
-       next = &part->mp_next;
+    /* Reverse the order of its parts by walking the mp_parts list
+       and pushing each node to the front. */
+    for (part = m->mp_parts, m->mp_parts = NULL; part; part = next) {
+        next = part->mp_next;
+        part->mp_next = m->mp_parts;
+        m->mp_parts = part;
     }
     }
-    *next = NULL;
-
-    /* free array of pointers */
-    free ((char *) base);
 }
 
 
 }
 
 
@@ -1463,6 +1350,7 @@ invalid_param:
                e->eb_parent = ct;
                e->eb_content = p;
                p->c_ctexbody = e;
                e->eb_parent = ct;
                e->eb_content = p;
                p->c_ctexbody = e;
+               p->c_ceopenfnx = NULL;
                if ((exresult = params_external (ct, 0)) != NOTOK
                        && p->c_ceopenfnx == openMail) {
                    int cc, size;
                if ((exresult = params_external (ct, 0)) != NOTOK
                        && p->c_ceopenfnx == openMail) {
                    int cc, size;
@@ -1535,6 +1423,7 @@ params_external (CT ct, int composing)
     struct exbody *e = (struct exbody *) ct->c_ctparams;
     CI ci = &ct->c_ctinfo;
 
     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 (!mh_strcasecmp (*ap, "access-type")) {
            struct str2init *s2i;
     for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
        if (!mh_strcasecmp (*ap, "access-type")) {
            struct str2init *s2i;
@@ -1590,6 +1479,23 @@ params_external (CT ct, int composing)
            e->eb_subject = *ep;
            continue;
        }
            e->eb_subject = *ep;
            continue;
        }
+       if (!mh_strcasecmp (*ap, "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);
+
+           for (; *p != '\0'; p++) {
+               if (! isspace((unsigned char) *p))
+                   *u++ = *p;
+           }
+
+           *u = '\0';
+           continue;
+       }
        if (composing && !mh_strcasecmp (*ap, "body")) {
            e->eb_body = getcpy (*ep);
            continue;
        if (composing && !mh_strcasecmp (*ap, "body")) {
            e->eb_body = getcpy (*ep);
            continue;
@@ -1634,12 +1540,6 @@ InitApplication (CT ct)
 static int
 init_encoding (CT ct, OpenCEFunc openfnx)
 {
 static int
 init_encoding (CT ct, OpenCEFunc openfnx)
 {
-    CE ce;
-
-    if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
-       adios (NULL, "out of memory");
-
-    ct->c_cefile     = ce;
     ct->c_ceopenfnx  = openfnx;
     ct->c_ceclosefnx = close_encoding;
     ct->c_cesizefnx  = size_encoding;
     ct->c_ceopenfnx  = openfnx;
     ct->c_ceclosefnx = close_encoding;
     ct->c_cesizefnx  = size_encoding;
@@ -1651,10 +1551,7 @@ init_encoding (CT ct, OpenCEFunc openfnx)
 void
 close_encoding (CT ct)
 {
 void
 close_encoding (CT ct)
 {
-    CE ce;
-
-    if (!(ce = ct->c_cefile))
-       return;
+    CE ce = &ct->c_cefile;
 
     if (ce->ce_fp) {
        fclose (ce->ce_fp);
 
     if (ce->ce_fp) {
        fclose (ce->ce_fp);
@@ -1669,12 +1566,9 @@ size_encoding (CT ct)
     int        fd;
     unsigned long size;
     char *file;
     int        fd;
     unsigned long size;
     char *file;
-    CE ce;
+    CE ce = &ct->c_cefile;
     struct stat st;
 
     struct stat st;
 
-    if (!(ce = ct->c_cefile))
-       return (ct->c_end - ct->c_begin);
-
     if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
        return (long) st.st_size;
 
     if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
        return (long) st.st_size;
 
@@ -1743,10 +1637,9 @@ openBase64 (CT ct, char **file)
     char *cp, *ep, buffer[BUFSIZ];
     /* sbeck -- handle suffixes */
     CI ci;
     char *cp, *ep, buffer[BUFSIZ];
     /* sbeck -- handle suffixes */
     CI ci;
-    CE ce;
+    CE ce = &ct->c_cefile;
     MD5_CTX mdContext;
 
     MD5_CTX mdContext;
 
-    ce = ct->c_cefile;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
@@ -1976,12 +1869,11 @@ openQuoted (CT ct, char **file)
     char *cp, *ep;
     char buffer[BUFSIZ];
     unsigned char mask;
     char *cp, *ep;
     char buffer[BUFSIZ];
     unsigned char mask;
-    CE ce;
+    CE ce = &ct->c_cefile;
     /* sbeck -- handle suffixes */
     CI ci;
     MD5_CTX mdContext;
 
     /* sbeck -- handle suffixes */
     CI ci;
     MD5_CTX mdContext;
 
-    ce = ct->c_cefile;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
@@ -2204,9 +2096,8 @@ open7Bit (CT ct, char **file)
     /* sbeck -- handle suffixes */
     char *cp;
     CI ci;
     /* sbeck -- handle suffixes */
     char *cp;
     CI ci;
-    CE ce;
+    CE ce = &ct->c_cefile;
 
 
-    ce = ct->c_cefile;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
     if (ce->ce_fp) {
        fseek (ce->ce_fp, 0L, SEEK_SET);
        goto ready_to_go;
@@ -2425,7 +2316,7 @@ openFile (CT ct, char **file)
     int        fd, cachetype;
     char cachefile[BUFSIZ];
     struct exbody *e = ct->c_ctexbody;
     int        fd, cachetype;
     char cachefile[BUFSIZ];
     struct exbody *e = ct->c_ctexbody;
-    CE ce = ct->c_cefile;
+    CE ce = &ct->c_cefile;
 
     switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
        case NOTOK:
 
     switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
        case NOTOK:
@@ -2508,12 +2399,11 @@ openFTP (CT ct, char **file)
     char *bp, *ftp, *user, *pass;
     char buffer[BUFSIZ], cachefile[BUFSIZ];
     struct exbody *e;
     char *bp, *ftp, *user, *pass;
     char buffer[BUFSIZ], cachefile[BUFSIZ];
     struct exbody *e;
-    CE ce;
+    CE ce = &ct->c_cefile;
     static char *username = NULL;
     static char *password = NULL;
 
     e  = ct->c_ctexbody;
     static char *username = NULL;
     static char *password = NULL;
 
     e  = ct->c_ctexbody;
-    ce = ct->c_cefile;
 
     if ((ftp = context_find (nmhaccessftp)) && !*ftp)
        ftp = NULL;
 
     if ((ftp = context_find (nmhaccessftp)) && !*ftp)
        ftp = NULL;
@@ -2720,7 +2610,7 @@ openMail (CT ct, char **file)
     int len, buflen;
     char *bp, buffer[BUFSIZ], *vec[7];
     struct exbody *e = ct->c_ctexbody;
     int len, buflen;
     char *bp, buffer[BUFSIZ], *vec[7];
     struct exbody *e = ct->c_ctexbody;
-    CE ce = ct->c_cefile;
+    CE ce = &ct->c_cefile;
 
     switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
        case NOTOK:
 
     switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
        case NOTOK:
@@ -2824,6 +2714,141 @@ openMail (CT ct, char **file)
 }
 
 
 }
 
 
+/*
+ * URL
+ */
+
+static int
+InitURL (CT ct)
+{
+    return init_encoding (ct, openURL);
+}
+
+
+static int
+openURL (CT ct, char **file)
+{
+    struct exbody *e = ct->c_ctexbody;
+    CE ce = &ct->c_cefile;
+    char *urlprog, *program;
+    char buffer[BUFSIZ], cachefile[BUFSIZ];
+    int fd, caching, cachetype;
+    struct msgs_array args = { 0, 0, NULL};
+    pid_t child_id;
+
+    if ((urlprog = context_find(nmhaccessurl)) && *urlprog == '\0')
+       urlprog = NULL;
+
+    if (! urlprog) {
+       content_error(NULL, ct, "No entry for nmh-access-url in profile");
+       return NOTOK;
+    }
+
+    switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
+       case NOTOK:
+           return NOTOK;
+
+       case OK:
+           break;
+
+       case DONE:
+           return fd;
+    }
+
+    if (!e->eb_url) {
+       content_error(NULL, ct, "missing url parameter");
+       return NOTOK;
+    }
+
+    if (xpid) {
+       if (xpid < 0)
+           xpid = -xpid;
+       pidcheck (pidwait (xpid, NOTOK));
+       xpid = 0;
+    }
+
+    ce->ce_unlink = (*file == NULL);
+    caching = 0;
+    cachefile[0] = '\0';
+
+    if (find_cache(NULL, wcachesw, &cachetype, e->eb_content->c_id,
+                  cachefile, sizeof(cachefile)) != NOTOK) {
+       if (*file == NULL) {
+           ce->ce_unlink = 0;
+           caching = 1;
+       }
+    }
+
+    if (*file)
+       ce->ce_file = add(*file, NULL);
+    else if (caching)
+       ce->ce_file = add(cachefile, NULL);
+    else
+       ce->ce_file = add(m_mktemp(tmp, NULL, NULL), NULL);
+
+    if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
+       content_error(ce->ce_file, ct, "unable to fopen for read/writing");
+       return NOTOK;
+    }
+
+    switch (child_id = fork()) {
+    case NOTOK:
+       adios ("fork", "unable to");
+       /* NOTREACHED */
+
+    case OK:
+       argsplit_msgarg(&args, urlprog, &program);
+       app_msgarg(&args, e->eb_url);
+       app_msgarg(&args, NULL);
+       dup2(fileno(ce->ce_fp), 1);
+       close(fileno(ce->ce_fp));
+       execvp(program, args.msgs);
+       fprintf(stderr, "Unable to exec ");
+       perror(program);
+       _exit(-1);
+       /* NOTREACHED */
+
+    default:
+       if (pidXwait(child_id, NULL)) {
+           ce->ce_unlink = 1;
+           return NOTOK;
+       }
+    }
+
+    if (cachefile[0]) {
+       if (caching)
+           chmod(cachefile, cachetype ? m_gmprot() : 0444);
+       else {
+           int mask;
+           FILE *fp;
+
+           mask = umask (cachetype ? ~m_gmprot() : 0222);
+           if ((fp = fopen(cachefile, "w"))) {
+               int cc;
+               FILE *gp = ce->ce_fp;
+
+               fseeko(gp, 0, SEEK_SET);
+
+               while ((cc = fread(buffer, sizeof(*buffer),
+                                  sizeof(buffer), gp)) > 0)
+                   fwrite(buffer, sizeof(*buffer), cc, fp);
+
+               fflush(fp);
+
+               if (ferror(gp)) {
+                   admonish(ce->ce_file, "error reading");
+                   unlink(cachefile);
+               }
+           }
+           umask(mask);
+       }
+    }
+
+    fseeko(ce->ce_fp, 0, SEEK_SET);
+    *file = ce->ce_file;
+    return fd;
+}
+
 static int
 readDigest (CT ct, char *cp)
 {
 static int
 readDigest (CT ct, char *cp)
 {
@@ -3004,3 +3029,253 @@ get_leftover_mp_content (CT ct, int before /* or after */) {
 
     return OK;
 }
 
     return OK;
 }
+
+
+char *
+ct_type_str (int type) {
+    switch (type) {
+    case CT_APPLICATION:
+        return "application";
+    case CT_AUDIO:
+        return "audio";
+    case CT_IMAGE:
+        return "image";
+    case CT_MESSAGE:
+        return "message";
+    case CT_MULTIPART:
+        return "multipart";
+    case CT_TEXT:
+        return "text";
+    case CT_VIDEO:
+        return "video";
+    case CT_EXTENSION:
+        return "extension";
+    default:
+        return "unknown_type";
+    }
+}
+
+
+char *
+ct_subtype_str (int type, int subtype) {
+    switch (type) {
+    case CT_APPLICATION:
+        switch (subtype) {
+        case APPLICATION_OCTETS:
+            return "octets";
+        case APPLICATION_POSTSCRIPT:
+            return "postscript";
+        default:
+            return "unknown_app_subtype";
+        }
+    case CT_MESSAGE:
+        switch (subtype) {
+        case MESSAGE_RFC822:
+            return "rfc822";
+        case MESSAGE_PARTIAL:
+            return "partial";
+        case MESSAGE_EXTERNAL:
+            return "external";
+        default:
+            return "unknown_msg_subtype";
+        }
+    case CT_MULTIPART:
+        switch (subtype) {
+        case MULTI_MIXED:
+            return "mixed";
+        case MULTI_ALTERNATE:
+            return "alternative";
+        case MULTI_DIGEST:
+            return "digest";
+        case MULTI_PARALLEL:
+            return "parallel";
+        default:
+            return "unknown_multipart_subtype";
+        }
+    case CT_TEXT:
+        switch (subtype) {
+        case TEXT_PLAIN:
+            return "plain";
+        case TEXT_RICHTEXT:
+            return "richtext";
+        case TEXT_ENRICHED:
+            return "enriched";
+        default:
+            return "unknown_text_subtype";
+        }
+    default:
+        return "unknown_type";
+    }
+}
+
+
+/* Find the content type and InitFunc for the CT. */
+const struct str2init *
+get_ct_init (int type) {
+    const struct str2init *sp;
+
+    for (sp = str2cts; sp->si_key; ++sp) {
+        if (type == sp->si_val) {
+            return sp;
+        }
+    }
+
+    return NULL;
+}
+
+const char *
+ce_str (int encoding) {
+    switch (encoding) {
+    case CE_BASE64:
+        return "base64";
+    case CE_QUOTED:
+        return "quoted";
+    case CE_8BIT:
+        return "8bit";
+    case CE_7BIT:
+        return "7bit";
+    case CE_BINARY:
+        return "binary";
+    case CE_EXTENSION:
+        return "extension";
+    case CE_EXTERNAL:
+        return "external";
+    default:
+        return "unknown";
+    }
+}
+
+/* Find the content type and InitFunc for the content encoding method. */
+const struct str2init *
+get_ce_method (const char *method) {
+    struct str2init *sp;
+
+    for (sp = str2ces; sp->si_key; ++sp) {
+        if (! strcasecmp (method, sp->si_key)) {
+            return sp;
+        }
+    }
+
+    return NULL;
+}
+
+int
+parse_header_attrs (const char *filename, int len, char **header_attrp, CI ci,
+                    int *status) {
+    char **attr = ci->ci_attrs;
+    char *cp = *header_attrp;
+
+    while (*cp == ';') {
+       char *dp, *vp, *up, c;
+
+        /* Relies on knowledge of this declaration:
+         *   char *ci_attrs[NPARMS + 2];
+         */
+       if (attr >= ci->ci_attrs + sizeof ci->ci_attrs/sizeof (char *) - 2) {
+           advise (NULL,
+                   "too many parameters in message %s's %s: field (%d max)",
+                   filename, TYPE_FIELD, NPARMS);
+           *status = NOTOK;
+           return NOTOK;
+       }
+
+       cp++;
+       while (isspace ((unsigned char) *cp))
+           cp++;
+
+       if (*cp == '('  &&
+            get_comment (filename, ci, &cp, 1) == NOTOK) {
+           *status = NOTOK;
+           return NOTOK;
+        }
+
+       if (*cp == 0) {
+           advise (NULL,
+                   "extraneous trailing ';' in message %s's %s: "
+                    "parameter list",
+                   filename, TYPE_FIELD);
+           *status = OK;
+           return NOTOK;
+       }
+
+       /* down case the attribute name */
+       for (dp = cp; istoken ((unsigned char) *dp); dp++)
+           if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp))
+               *dp = tolower ((unsigned char) *dp);
+
+       for (up = dp; isspace ((unsigned char) *dp);)
+           dp++;
+       if (dp == cp || *dp != '=') {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: "
+                    "field\n%*.*sparameter %s (error detected at offset %d)",
+                   filename, TYPE_FIELD, len, len, "", cp, dp - cp);
+           *status = NOTOK;
+           return NOTOK;
+       }
+
+       vp = (*attr = add (cp, NULL)) + (up - cp);
+       *vp = '\0';
+       for (dp++; isspace ((unsigned char) *dp);)
+           dp++;
+
+       /* Now store the attribute value. */
+       ci->ci_values[attr - ci->ci_attrs] = vp = *attr + (dp - cp);
+
+       if (*dp == '"') {
+           for (cp = ++dp, dp = vp;;) {
+               switch (c = *cp++) {
+                   case '\0':
+bad_quote:
+                       advise (NULL,
+                               "invalid quoted-string in message %s's %s: "
+                                "field\n%*.*s(parameter %s)",
+                               filename, TYPE_FIELD, len, len, "", *attr);
+                       *status = NOTOK;
+                       return NOTOK;
+
+                   case '\\':
+                       *dp++ = c;
+                       if ((c = *cp++) == '\0')
+                           goto bad_quote;
+                       /* else fall... */
+
+                   default:
+                       *dp++ = c;
+                       continue;
+
+                   case '"':
+                       *dp = '\0';
+                       break;
+               }
+               break;
+           }
+       } else {
+           for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
+               continue;
+           *dp = '\0';
+       }
+       if (!*vp) {
+           advise (NULL,
+                   "invalid parameter in message %s's %s: "
+                    "field\n%*.*s(parameter %s)",
+                   filename, TYPE_FIELD, len, len, "", *attr);
+           *status = NOTOK;
+           return NOTOK;
+       }
+
+       while (isspace ((unsigned char) *cp))
+           cp++;
+
+       if (*cp == '('  &&
+            get_comment (filename, ci, &cp, 1) == NOTOK) {
+           *status = NOTOK;
+           return NOTOK;
+        }
+
+        ++attr;
+    }
+
+    *header_attrp = cp;
+    return OK;
+}