X-Git-Url: https://diplodocus.org/git/nmh/blobdiff_plain/636b3bab53e3fc2154bb86fb02aeecf433cc977a..64cc8937ff11ffd37736f27ceed5f4ff93161ca7:/uip/mhparse.c?ds=inline diff --git a/uip/mhparse.c b/uip/mhparse.c index f5ff85c2..4a70610e 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -2,8 +2,6 @@ /* * mhparse.c -- routines to parse the contents of MIME messages * - * $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. @@ -14,7 +12,6 @@ #include #include #include -#include #include #include #include @@ -22,15 +19,9 @@ #include #include -#ifdef HAVE_SYS_WAIT_H -# include -#endif - extern int debugsw; -extern int endian; /* mhmisc.c */ - extern pid_t xpid; /* mhshowsbr.c */ /* cache policies */ @@ -45,6 +36,16 @@ int checksw = 0; /* check Content-MD5 field */ */ char *tmp; +/* + * These are for mhfixmsg to: + * 1) Instruct parser not to detect invalid Content-Transfer-Encoding + * in a multipart. + * 2) Suppress the warning about bogus multipart content, and report it. + */ +int skip_mp_cte_check; +int suppress_bogus_mp_content_warning; +int bogus_mp_content; + /* * Structures for TEXT messages */ @@ -55,11 +56,7 @@ struct k2v SubText[] = { { NULL, TEXT_UNKNOWN } /* this one must be last! */ }; -struct k2v Charset[] = { - { "us-ascii", CHARSET_USASCII }, - { "iso-8859-1", CHARSET_LATIN }, - { NULL, CHARSET_UNKNOWN } /* this one must be last! */ -}; +/* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */ /* * Structures for MULTIPART messages @@ -92,32 +89,27 @@ struct k2v SubApplication[] = { }; -/* ftpsbr.c */ -int ftp_get (char *, char *, char *, char *, char *, char *, int, int); - /* mhcachesbr.c */ int find_cache (CT, int, int *, char *, char *, int); /* mhmisc.c */ int part_ok (CT, int); int type_ok (CT, int); -int make_intermediates (char *); void content_error (char *, CT, char *, ...); /* mhfree.c */ -void free_content (CT); void free_encoding (CT, int); /* * static prototypes */ static CT get_content (FILE *, char *, int); -static int get_comment (CT, unsigned char **, int); +static int get_comment (CT, char **, int); 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); @@ -135,6 +127,9 @@ static int openFTP (CT, char **); static int InitMail (CT); 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 }, @@ -169,6 +164,7 @@ struct str2init str2methods[] = { { "ftp", 0, InitFTP }, { "local-file", 0, InitFile }, { "mail-server", 0, InitMail }, + { "url", 0, InitURL }, { NULL, 0, NULL } }; @@ -204,12 +200,14 @@ parse_mime (char *file) * Check if file is actually standard input */ if ((is_stdin = !(strcmp (file, "-")))) { - file = add (m_tmpfil (invo_name), NULL); - if ((fp = fopen (file, "w+")) == NULL) { - advise (file, "unable to fopen for writing and reading"); - return NULL; - } + char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp); + if (tfile == NULL) { + advise("mhparse", "unable to create temporary file"); + return NULL; + } + file = add (tfile, NULL); chmod (file, 0600); + while (fgets (buffer, sizeof(buffer), stdin)) fputs (buffer, fp); fflush (fp); @@ -277,6 +275,7 @@ get_content (FILE *in, char *file, int toplevel) char *np, *vp; CT ct; HF hp; + m_getfld_state_t gstate = 0; /* allocate the content structure */ if (!(ct = (CT) calloc (1, sizeof(*ct)))) @@ -290,11 +289,12 @@ get_content (FILE *in, char *file, int toplevel) * Parse the header fields for this * content into a linked list. */ - for (compnum = 1, state = FLD;;) { - switch (state = m_getfld (state, name, buf, sizeof(buf), in)) { + m_getfld_track_filepos (&gstate, in); + for (compnum = 1;;) { + int bufsz = sizeof buf; + switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) { case FLD: case FLDPLUS: - case FLDEOF: compnum++; /* get copies of the buffers */ @@ -303,22 +303,19 @@ get_content (FILE *in, char *file, int toplevel) /* if necessary, get rest of field */ while (state == FLDPLUS) { - state = m_getfld (state, name, buf, sizeof(buf), in); + bufsz = sizeof buf; + state = m_getfld (&gstate, name, buf, &bufsz, in); vp = add (buf, vp); /* add to previous value */ } /* Now add the header data to the list */ add_header (ct, np, vp); - /* continue, if this isn't the last header field */ - if (state != FLDEOF) { - ct->c_begin = ftell (in) + 1; - continue; - } - /* else fall... */ + /* continue, to see if this isn't the last header field */ + ct->c_begin = ftell (in) + 1; + continue; case BODY: - case BODYEOF: ct->c_begin = ftell (in) - strlen (buf); break; @@ -337,6 +334,7 @@ get_content (FILE *in, char *file, int toplevel) /* break out of the loop */ break; } + m_getfld_state_destroy (&gstate); /* * Read the content headers. We will parse the @@ -350,8 +348,7 @@ get_content (FILE *in, char *file, int toplevel) /* Get MIME-Version field */ if (!mh_strcasecmp (hp->name, VRSN_FIELD)) { int ucmp; - char c; - unsigned char *cp, *dp; + char c, *cp, *dp; if (ct->c_vrsn) { advise (NULL, "message %s has multiple %s: fields", @@ -363,12 +360,12 @@ get_content (FILE *in, char *file, int toplevel) /* Now, cleanup this field */ cp = ct->c_vrsn; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) *dp++ = ' '; for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) - if (!isspace (*dp)) + if (!isspace ((unsigned char) *dp)) break; *++dp = '\0'; if (debugsw) @@ -418,8 +415,7 @@ get_content (FILE *in, char *file, int toplevel) } else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) { /* Get Content-Transfer-Encoding field */ - char c; - unsigned char *cp, *dp; + char c, *cp, *dp; struct str2init *s2i; /* @@ -435,7 +431,7 @@ get_content (FILE *in, char *file, int toplevel) /* get copy of this field */ ct->c_celine = cp = add (hp->value, NULL); - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; for (dp = cp; istoken (*dp); dp++) continue; @@ -460,8 +456,7 @@ get_content (FILE *in, char *file, int toplevel) } else if (!mh_strcasecmp (hp->name, MD5_FIELD)) { /* Get Content-MD5 field */ - unsigned char *cp, *dp; - char *ep; + char *cp, *dp, *ep; if (!checksw) goto next_header; @@ -474,12 +469,12 @@ get_content (FILE *in, char *file, int toplevel) ep = cp = add (hp->value, NULL); /* get a copy */ - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n')) *dp++ = ' '; for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) - if (!isspace (*dp)) + if (!isspace ((unsigned char) *dp)) break; *++dp = '\0'; if (debugsw) @@ -490,7 +485,7 @@ get_content (FILE *in, char *file, int toplevel) goto out; } - for (dp = cp; *dp && !isspace (*dp); dp++) + for (dp = cp; *dp && !isspace ((unsigned char) *dp); dp++) continue; *dp = '\0'; @@ -593,7 +588,7 @@ add_header (CT ct, char *name, char *value) filename="foo". If it doesn't and value does, use value from that. */ static char * -incl_name_value (unsigned char *buf, char *name, char *value) { +incl_name_value (char *buf, char *name, char *value) { char *newbuf = buf; /* Assume that name is non-null. */ @@ -602,12 +597,11 @@ incl_name_value (unsigned char *buf, char *name, char *value) { if (! strstr (buf, name_plus_equal)) { char *insertion; - unsigned char *cp; - char *prefix, *suffix; + char *cp, *prefix, *suffix; /* Trim trailing space, esp. newline. */ for (cp = &buf[strlen (buf) - 1]; - cp >= buf && isspace (*cp); + cp >= buf && isspace ((unsigned char) *cp); --cp) { *cp = '\0'; } @@ -673,11 +667,10 @@ extract_name_value (char *name_suffix, char *value) { * directives. Fills in the information of the CTinfo structure. */ int -get_ctinfo (unsigned char *cp, CT ct, int magic) +get_ctinfo (char *cp, CT ct, int magic) { int i; - unsigned char *dp; - char **ap, **ep; + char *dp, **ap, **ep; char c; CI ci; @@ -687,7 +680,7 @@ get_ctinfo (unsigned char *cp, CT ct, int magic) /* store copy of Content-Type line */ cp = ct->c_ctline = add (cp, NULL); - while (isspace (*cp)) /* trim leading spaces */ + while (isspace ((unsigned char) *cp)) /* trim leading spaces */ cp++; /* change newlines to spaces */ @@ -696,7 +689,7 @@ get_ctinfo (unsigned char *cp, CT ct, int magic) /* trim trailing spaces */ for (dp = cp + strlen (cp) - 1; dp >= cp; dp--) - if (!isspace (*dp)) + if (!isspace ((unsigned char) *dp)) break; *++dp = '\0'; @@ -720,10 +713,10 @@ get_ctinfo (unsigned char *cp, CT ct, int magic) /* down case the content type string */ for (dp = ci->ci_type; *dp; dp++) - if (isalpha(*dp) && isupper (*dp)) - *dp = tolower (*dp); + if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp)) + *dp = tolower ((unsigned char) *dp); - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) @@ -736,7 +729,7 @@ get_ctinfo (unsigned char *cp, CT ct, int magic) } cp++; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) @@ -757,11 +750,11 @@ get_ctinfo (unsigned char *cp, CT ct, int magic) /* down case the content subtype string */ for (dp = ci->ci_subtype; *dp; dp++) - if (isalpha(*dp) && isupper (*dp)) - *dp = tolower (*dp); + if (isalpha((unsigned char) *dp) && isupper ((unsigned char) *dp)) + *dp = tolower ((unsigned char) *dp); magic_skip: - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) @@ -772,8 +765,7 @@ magic_skip: */ ep = (ap = ci->ci_attrs) + NPARMS; while (*cp == ';') { - char *vp; - unsigned char *up; + char *vp, *up; if (ap >= ep) { advise (NULL, @@ -783,7 +775,7 @@ magic_skip: } cp++; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) @@ -797,11 +789,11 @@ magic_skip: } /* down case the attribute name */ - for (dp = cp; istoken (*dp); dp++) - if (isalpha(*dp) && isupper (*dp)) - *dp = tolower (*dp); + 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 (*dp);) + for (up = dp; isspace ((unsigned char) *dp);) dp++; if (dp == cp || *dp != '=') { advise (NULL, @@ -812,7 +804,7 @@ magic_skip: vp = (*ap = add (cp, NULL)) + (up - cp); *vp = '\0'; - for (dp++; isspace (*dp);) + for (dp++; isspace ((unsigned char) *dp);) dp++; /* now add the attribute value */ @@ -857,7 +849,7 @@ bad_quote: } ap++; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK) @@ -885,7 +877,7 @@ bad_quote: *dp++ = c; cp = dp; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; } @@ -912,7 +904,7 @@ bad_quote: *dp++ = c; cp = dp; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; } @@ -939,7 +931,7 @@ bad_quote: *dp++ = c; cp = dp; - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; } @@ -974,11 +966,10 @@ bad_quote: static int -get_comment (CT ct, unsigned char **ap, int istype) +get_comment (CT ct, char **ap, int istype) { int i; - char *bp; - unsigned char *cp; + char *bp, *cp; char c, buffer[BUFSIZ], *dp; CI ci; @@ -1028,7 +1019,7 @@ invalid: } } - while (isspace (*cp)) + while (isspace ((unsigned char) *cp)) cp++; *ap = cp; @@ -1046,6 +1037,8 @@ invalid: static int InitGeneric (CT ct) { + NMH_UNUSED (ct); + return OK; /* not much to do here */ } @@ -1058,7 +1051,7 @@ static int InitText (CT ct) { char buffer[BUFSIZ]; - char *chset; + char *chset = NULL; char **ap, **ep, *cp; struct k2v *kv; struct text *t; @@ -1086,11 +1079,8 @@ InitText (CT ct) /* check if content specified a character set */ if (*ap) { - /* match character set or set to CHARSET_UNKNOWN */ - for (kv = Charset; kv->kv_key; kv++) - if (!mh_strcasecmp (*ep, kv->kv_key)) - break; - t->tx_charset = kv->kv_value; + chset = *ep; + t->tx_charset = CHARSET_SPECIFIED; } else { t->tx_charset = CHARSET_UNSPECIFIED; } @@ -1121,8 +1111,7 @@ InitMultiPart (CT ct) { int inout; long last, pos; - unsigned char *cp, *dp; - char **ap, **ep; + char *cp, *dp, **ap, **ep; char *bp, buffer[BUFSIZ]; struct multipart *m; struct k2v *kv; @@ -1135,11 +1124,24 @@ InitMultiPart (CT ct) * The encoding for multipart messages must be either * 7bit, 8bit, or binary (per RFC2045). */ - if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT - && ct->c_encoding != CE_BINARY) { + if (! skip_mp_cte_check && ct->c_encoding != CE_7BIT && + ct->c_encoding != CE_8BIT && ct->c_encoding != CE_BINARY) { + /* Copy the Content-Transfer-Encoding header field body so we can + remove any trailing whitespace and leading blanks from it. */ + char *cte = add (ct->c_celine ? ct->c_celine : "(null)", NULL); + + bp = cte + strlen (cte) - 1; + while (bp >= cte && isspace ((unsigned char) *bp)) *bp-- = '\0'; + for (bp = cte; *bp && isblank ((unsigned char) *bp); ++bp) continue; + admonish (NULL, - "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary", - ci->ci_type, ci->ci_subtype, ct->c_file); + "\"%s/%s\" type in message %s must be encoded in\n" + "7bit, 8bit, or binary, per RFC 2045 (6.4). One workaround " + "is to\nmanually edit the file and change the \"%s\"\n" + "Content-Transfer-Encoding to one of those. For now", + ci->ci_type, ci->ci_subtype, ct->c_file, bp); + free (cte); + return NOTOK; } @@ -1175,7 +1177,7 @@ InitMultiPart (CT ct) ct->c_ctparams = (void *) m; /* check if boundary parameter contains only whitespace characters */ - for (cp = bp; isspace (*cp); cp++) + for (cp = bp; isspace ((unsigned char) *cp); cp++) continue; if (!*cp) { advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field", @@ -1185,7 +1187,7 @@ InitMultiPart (CT ct) /* remove trailing whitespace from boundary parameter */ for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--) - if (!isspace (*dp)) + if (!isspace ((unsigned char) *dp)) break; *++dp = '\0'; @@ -1248,7 +1250,11 @@ end_part: } } - advise (NULL, "bogus multipart content in message %s", ct->c_file); + if (! suppress_bogus_mp_content_warning) { + 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; @@ -1300,6 +1306,9 @@ last_part: } } + get_leftover_mp_content (ct, 1); + get_leftover_mp_content (ct, 0); + fclose (ct->c_fp); ct->c_fp = NULL; return OK; @@ -1307,48 +1316,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) { - 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++; + struct multipart *m = (struct multipart *) ct->c_ctparams; + struct part *part; + struct part *next; - /* 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; - - /* 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); } @@ -1457,6 +1441,7 @@ invalid_param: 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; @@ -1529,6 +1514,7 @@ params_external (CT ct, int composing) 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; @@ -1584,6 +1570,23 @@ params_external (CT ct, int composing) 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; @@ -1731,21 +1734,15 @@ static int openBase64 (CT ct, char **file) { int bitno, cc, digested; - int fd, len, skip; - unsigned long bits; - unsigned char value, *b, *b1, *b2, *b3; - unsigned char *cp, *ep; - char buffer[BUFSIZ]; + int fd, len, skip, own_ct_fp = 0; + uint32_t bits; + unsigned char value, b; + char *cp, *ep, buffer[BUFSIZ]; /* sbeck -- handle suffixes */ CI ci; CE ce; MD5_CTX mdContext; - b = (unsigned char *) &bits; - b1 = &b[endian > 0 ? 1 : 2]; - b2 = &b[endian > 0 ? 2 : 1]; - b3 = &b[endian > 0 ? 3 : 0]; - ce = ct->c_cefile; if (ce->ce_fp) { fseek (ce->ce_fp, 0L, SEEK_SET); @@ -1761,7 +1758,7 @@ openBase64 (CT ct, char **file) } if (*file == NULL) { - ce->ce_file = add (m_scratch ("", tmp), NULL); + ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1778,8 +1775,21 @@ openBase64 (CT ct, char **file) ci->ci_type); cp = context_find (buffer); } - if (cp != NULL && *cp != '\0') - ce->ce_file = add (cp, ce->ce_file); + if (cp != NULL && *cp != '\0') { + if (ce->ce_unlink) { + /* Temporary file already exists, so we rename to + version with extension. */ + char *file_org = strdup(ce->ce_file); + ce->ce_file = add (cp, ce->ce_file); + if (rename(file_org, ce->ce_file)) { + adios (ce->ce_file, "unable to rename %s to ", file_org); + } + free(file_org); + + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); @@ -1789,9 +1799,12 @@ openBase64 (CT ct, char **file) if ((len = ct->c_end - ct->c_begin) < 0) adios (NULL, "internal error(1)"); - if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { - content_error (ct->c_file, ct, "unable to open for reading"); - return NOTOK; + if (! ct->c_fp) { + if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) { + content_error (ct->c_file, ct, "unable to open for reading"); + return NOTOK; + } + own_ct_fp = 1; } if ((digested = ct->c_digested)) @@ -1820,13 +1833,13 @@ openBase64 (CT ct, char **file) for (ep = (cp = buffer) + cc; cp < ep; cp++) { switch (*cp) { default: - if (isspace (*cp)) + if (isspace ((unsigned char) *cp)) break; - if (skip || (*cp & 0x80) - || (value = b642nib[*cp & 0x7f]) > 0x3f) { + if (skip || (((unsigned char) *cp) & 0x80) + || (value = b642nib[((unsigned char) *cp) & 0x7f]) > 0x3f) { if (debugsw) { fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", - *cp, + (unsigned char) *cp, (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip); } @@ -1838,17 +1851,20 @@ openBase64 (CT ct, char **file) bits |= value << bitno; test_end: if ((bitno -= 6) < 0) { - putc ((char) *b1, ce->ce_fp); + b = (bits >> 16) & 0xff; + putc ((char) b, ce->ce_fp); if (digested) - MD5Update (&mdContext, b1, 1); + MD5Update (&mdContext, &b, 1); if (skip < 2) { - putc ((char) *b2, ce->ce_fp); + b = (bits >> 8) & 0xff; + putc ((char) b, ce->ce_fp); if (digested) - MD5Update (&mdContext, b2, 1); + MD5Update (&mdContext, &b, 1); if (skip < 1) { - putc ((char) *b3, ce->ce_fp); + b = bits & 0xff; + putc ((char) b, ce->ce_fp); if (digested) - MD5Update (&mdContext, b3, 1); + MD5Update (&mdContext, &b, 1); } } @@ -1903,9 +1919,17 @@ self_delimiting: ready_to_go: *file = ce->ce_file; + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } return fileno (ce->ce_fp); clean_up: + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } free_encoding (ct, 0); return NOTOK; } @@ -1945,8 +1969,8 @@ InitQuoted (CT ct) static int openQuoted (CT ct, char **file) { - int cc, digested, len, quoted; - unsigned char *cp, *ep; + int cc, digested, len, quoted, own_ct_fp = 0; + char *cp, *ep; char buffer[BUFSIZ]; unsigned char mask; CE ce; @@ -1969,7 +1993,7 @@ openQuoted (CT ct, char **file) } if (*file == NULL) { - ce->ce_file = add (m_scratch ("", tmp), NULL); + ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -1986,12 +2010,20 @@ openQuoted (CT ct, char **file) ci->ci_type); cp = context_find (buffer); } - if (cp != NULL && *cp != '\0') - ce->ce_file = add (cp, ce->ce_file); - - if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { - content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); - return NOTOK; + if (cp != NULL && *cp != '\0') { + if (ce->ce_unlink) { + /* Temporary file already exists, so we rename to + version with extension. */ + char *file_org = strdup(ce->ce_file); + ce->ce_file = add (cp, ce->ce_file); + if (rename(file_org, ce->ce_file)) { + adios (ce->ce_file, "unable to rename %s to ", file_org); + } + free(file_org); + + } else { + ce->ce_file = add (cp, ce->ce_file); + } } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { @@ -2002,9 +2034,12 @@ openQuoted (CT ct, char **file) if ((len = ct->c_end - ct->c_begin) < 0) adios (NULL, "internal error(2)"); - if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { - content_error (ct->c_file, ct, "unable to open for reading"); - return NOTOK; + if (! ct->c_fp) { + if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) { + content_error (ct->c_file, ct, "unable to open for reading"); + return NOTOK; + } + own_ct_fp = 1; } if ((digested = ct->c_digested)) @@ -2027,7 +2062,7 @@ openQuoted (CT ct, char **file) len -= cc; for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--) - if (!isspace (*ep)) + if (!isspace ((unsigned char) *ep)) break; *++ep = '\n', ep++; @@ -2036,13 +2071,13 @@ openQuoted (CT ct, char **file) /* in an escape sequence */ if (quoted == 1) { /* at byte 1 of an escape sequence */ - mask = hex2nib[*cp & 0x7f]; + mask = hex2nib[((unsigned char) *cp) & 0x7f]; /* next is byte 2 */ quoted = 2; } else { /* at byte 2 of an escape sequence */ mask <<= 4; - mask |= hex2nib[*cp & 0x7f]; + mask |= hex2nib[((unsigned char) *cp) & 0x7f]; putc (mask, ce->ce_fp); if (digested) MD5Update (&mdContext, &mask, 1); @@ -2069,7 +2104,8 @@ openQuoted (CT ct, char **file) if (cp + 1 >= ep || cp + 2 >= ep) { /* We don't have 2 bytes left, so this is an invalid * escape sequence; just show the raw bytes (below). */ - } else if (isxdigit (cp[1]) && isxdigit (cp[2])) { + } else if (isxdigit ((unsigned char) cp[1]) && + isxdigit ((unsigned char) cp[2])) { /* Next 2 bytes are hex digits, making this a valid escape * sequence; let's decode it (above). */ quoted = 1; @@ -2126,10 +2162,18 @@ openQuoted (CT ct, char **file) ready_to_go: *file = ce->ce_file; + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } return fileno (ce->ce_fp); clean_up: free_encoding (ct, 0); + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } return NOTOK; } @@ -2152,7 +2196,7 @@ Init7Bit (CT ct) int open7Bit (CT ct, char **file) { - int cc, fd, len; + int cc, fd, len, own_ct_fp = 0; char buffer[BUFSIZ]; /* sbeck -- handle suffixes */ char *cp; @@ -2174,7 +2218,7 @@ open7Bit (CT ct, char **file) } if (*file == NULL) { - ce->ce_file = add (m_scratch ("", tmp), NULL); + ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -2191,8 +2235,21 @@ open7Bit (CT ct, char **file) ci->ci_type); cp = context_find (buffer); } - if (cp != NULL && *cp != '\0') - ce->ce_file = add (cp, ce->ce_file); + if (cp != NULL && *cp != '\0') { + if (ce->ce_unlink) { + /* Temporary file already exists, so we rename to + version with extension. */ + char *file_org = strdup(ce->ce_file); + ce->ce_file = add (cp, ce->ce_file); + if (rename(file_org, ce->ce_file)) { + adios (ce->ce_file, "unable to rename %s to ", file_org); + } + free(file_org); + + } else { + ce->ce_file = add (cp, ce->ce_file); + } + } if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) { content_error (ce->ce_file, ct, "unable to fopen for reading/writing"); @@ -2249,9 +2306,12 @@ open7Bit (CT ct, char **file) if ((len = ct->c_end - ct->c_begin) < 0) adios (NULL, "internal error(3)"); - if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) { - content_error (ct->c_file, ct, "unable to open for reading"); - return NOTOK; + if (! ct->c_fp) { + if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) { + content_error (ct->c_file, ct, "unable to open for reading"); + return NOTOK; + } + own_ct_fp = 1; } lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET); @@ -2288,10 +2348,18 @@ open7Bit (CT ct, char **file) ready_to_go: *file = ce->ce_file; + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } return fileno (ce->ce_fp); clean_up: free_encoding (ct, 0); + if (own_ct_fp) { + fclose (ct->c_fp); + ct->c_fp = NULL; + } return NOTOK; } @@ -2447,10 +2515,8 @@ openFTP (CT ct, char **file) if ((ftp = context_find (nmhaccessftp)) && !*ftp) ftp = NULL; -#ifndef BUILTIN_FTP if (!ftp) return NOTOK; -#endif switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) { case NOTOK: @@ -2517,7 +2583,8 @@ openFTP (CT ct, char **file) if (e->eb_flags) { user = "anonymous"; - snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ()); + snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), + LocalName (1)); pass = buffer; } else { ruserpass (e->eb_site, &username, &password); @@ -2542,16 +2609,13 @@ openFTP (CT ct, char **file) else if (caching) ce->ce_file = add (cachefile, NULL); else - ce->ce_file = add (m_scratch ("", tmp), NULL); + 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 reading/writing"); return NOTOK; } -#ifdef BUILTIN_FTP - if (ftp) -#endif { int child_id, i, vecp; char *vec[9]; @@ -2570,7 +2634,7 @@ openFTP (CT ct, char **file) fflush (stdout); - 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: @@ -2587,9 +2651,6 @@ openFTP (CT ct, char **file) default: if (pidXwait (child_id, NULL)) { -#ifdef BUILTIN_FTP -losing_ftp: -#endif username = password = NULL; ce->ce_unlink = 1; return NOTOK; @@ -2597,14 +2658,6 @@ losing_ftp: break; } } -#ifdef BUILTIN_FTP - else - if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name, - ce->ce_file, - e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0) - == NOTOK) - goto losing_ftp; -#endif if (cachefile[0]) { if (caching) @@ -2723,7 +2776,7 @@ openMail (CT ct, char **file) vec[vecp++] = e->eb_body; vec[vecp] = NULL; - 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: @@ -2744,7 +2797,7 @@ openMail (CT ct, char **file) } if (*file == NULL) { - ce->ce_file = add (m_scratch ("", tmp), NULL); + ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL); ce->ce_unlink = 1; } else { ce->ce_file = add (*file, NULL); @@ -2768,19 +2821,149 @@ 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) { int bitno, skip; - unsigned long bits; + uint32_t bits; char *bp = cp; unsigned char *dp, value, *ep; - unsigned char *b, *b1, *b2, *b3; - b = (unsigned char *) &bits, - b1 = &b[endian > 0 ? 1 : 2], - b2 = &b[endian > 0 ? 2 : 1], - b3 = &b[endian > 0 ? 3 : 0]; bitno = 18; bits = 0L; skip = 0; @@ -2802,11 +2985,11 @@ test_end: if ((bitno -= 6) < 0) { if (dp + (3 - skip) > ep) goto invalid_digest; - *dp++ = *b1; + *dp++ = (bits >> 16) & 0xff; if (skip < 2) { - *dp++ = *b2; + *dp++ = (bits >> 8) & 0xff; if (skip < 1) - *dp++ = *b3; + *dp++ = bits & 0xff; } bitno = 18; bits = 0L; @@ -2847,3 +3030,238 @@ invalid_digest: return OK; } + + +/* Multipart parts might have content before the first subpart and/or + after the last subpart that hasn't been stored anywhere else, so do + that. */ +int +get_leftover_mp_content (CT ct, int before /* or after */) { + struct multipart *m = (struct multipart *) ct->c_ctparams; + char *boundary; + int found_boundary = 0; + char buffer[BUFSIZ]; + int max = BUFSIZ; + int read = 0; + char *content = NULL; + + if (! m) return NOTOK; + + if (before) { + if (! m->mp_parts || ! m->mp_parts->mp_part) return NOTOK; + + /* Isolate the beginning of this part to the beginning of the + first subpart and save any content between them. */ + fseeko (ct->c_fp, ct->c_begin, SEEK_SET); + max = m->mp_parts->mp_part->c_begin - ct->c_begin; + boundary = concat ("--", m->mp_start, NULL); + } else { + struct part *last_subpart = NULL; + struct part *subpart; + + /* Go to the last subpart to get its end position. */ + for (subpart = m->mp_parts; subpart; subpart = subpart->mp_next) { + last_subpart = subpart; + } + + if (last_subpart == NULL) return NOTOK; + + /* Isolate the end of the last subpart to the end of this part + and save any content between them. */ + fseeko (ct->c_fp, last_subpart->mp_part->c_end, SEEK_SET); + max = ct->c_end - last_subpart->mp_part->c_end; + boundary = concat ("--", m->mp_stop, NULL); + } + + /* Back up by 1 to pick up the newline. */ + while (fgets (buffer, sizeof(buffer) - 1, ct->c_fp)) { + read += strlen (buffer); + /* Don't look beyond beginning of first subpart (before) or + next part (after). */ + if (read > max) buffer[read-max] = '\0'; + + if (before) { + if (! strcmp (buffer, boundary)) { + found_boundary = 1; + } + } else { + if (! found_boundary && ! strcmp (buffer, boundary)) { + found_boundary = 1; + continue; + } + } + + if ((before && ! found_boundary) || (! before && found_boundary)) { + if (content) { + char *old_content = content; + content = concat (content, buffer, NULL); + free (old_content); + } else { + content = before + ? concat ("\n", buffer, NULL) + : concat (buffer, NULL); + } + } + + if (before) { + if (found_boundary || read > max) break; + } else { + if (read > max) break; + } + } + + /* Skip the newline if that's all there is. */ + if (content) { + char *cp; + + /* Remove trailing newline, except at EOF. */ + if ((before || ! feof (ct->c_fp)) && + (cp = content + strlen (content)) > content && + *--cp == '\n') { + *cp = '\0'; + } + + if (strlen (content) > 1) { + if (before) { + m->mp_content_before = content; + } else { + m->mp_content_after = content; + } + } else { + free (content); + } + } + + free (boundary); + + 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; +}