3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
20 #include <h/mhparse.h>
23 #ifdef HAVE_SYS_WAIT_H
24 # include <sys/wait.h>
30 extern int endian; /* mhmisc.c */
32 extern pid_t xpid; /* mhshowsbr.c */
35 extern int rcachesw; /* mhcachesbr.c */
36 extern int wcachesw; /* mhcachesbr.c */
38 int checksw = 0; /* check Content-MD5 field */
41 * Directory to place temp files. This must
42 * be set before these routines are called.
47 * Structures for TEXT messages
49 struct k2v SubText[] = {
50 { "plain", TEXT_PLAIN },
51 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
52 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
53 { NULL, TEXT_UNKNOWN } /* this one must be last! */
56 struct k2v Charset[] = {
57 { "us-ascii", CHARSET_USASCII },
58 { "iso-8859-1", CHARSET_LATIN },
59 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
63 * Structures for MULTIPART messages
65 struct k2v SubMultiPart[] = {
66 { "mixed", MULTI_MIXED },
67 { "alternative", MULTI_ALTERNATE },
68 { "digest", MULTI_DIGEST },
69 { "parallel", MULTI_PARALLEL },
70 { NULL, MULTI_UNKNOWN } /* this one must be last! */
74 * Structures for MESSAGE messages
76 struct k2v SubMessage[] = {
77 { "rfc822", MESSAGE_RFC822 },
78 { "partial", MESSAGE_PARTIAL },
79 { "external-body", MESSAGE_EXTERNAL },
80 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
84 * Structure for APPLICATION messages
86 struct k2v SubApplication[] = {
87 { "octet-stream", APPLICATION_OCTETS },
88 { "postscript", APPLICATION_POSTSCRIPT },
89 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
94 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
97 int find_cache (CT, int, int *, char *, char *, int);
100 int part_ok (CT, int);
101 int type_ok (CT, int);
102 int make_intermediates (char *);
103 void content_error (char *, CT, char *, ...);
106 void free_content (CT);
107 void free_encoding (CT, int);
112 static CT get_content (FILE *, char *, int);
113 static int get_comment (CT, unsigned char **, int);
115 static int InitGeneric (CT);
116 static int InitText (CT);
117 static int InitMultiPart (CT);
118 static void reverse_parts (CT);
119 static int InitMessage (CT);
120 static int InitApplication (CT);
121 static int init_encoding (CT, OpenCEFunc);
122 static unsigned long size_encoding (CT);
123 static int InitBase64 (CT);
124 static int openBase64 (CT, char **);
125 static int InitQuoted (CT);
126 static int openQuoted (CT, char **);
127 static int Init7Bit (CT);
128 static int openExternal (CT, CT, CE, char **, int *);
129 static int InitFile (CT);
130 static int openFile (CT, char **);
131 static int InitFTP (CT);
132 static int openFTP (CT, char **);
133 static int InitMail (CT);
134 static int openMail (CT, char **);
135 static int readDigest (CT, char *);
137 struct str2init str2cts[] = {
138 { "application", CT_APPLICATION, InitApplication },
139 { "audio", CT_AUDIO, InitGeneric },
140 { "image", CT_IMAGE, InitGeneric },
141 { "message", CT_MESSAGE, InitMessage },
142 { "multipart", CT_MULTIPART, InitMultiPart },
143 { "text", CT_TEXT, InitText },
144 { "video", CT_VIDEO, InitGeneric },
145 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
146 { NULL, CT_UNKNOWN, NULL },
149 struct str2init str2ces[] = {
150 { "base64", CE_BASE64, InitBase64 },
151 { "quoted-printable", CE_QUOTED, InitQuoted },
152 { "8bit", CE_8BIT, Init7Bit },
153 { "7bit", CE_7BIT, Init7Bit },
154 { "binary", CE_BINARY, Init7Bit },
155 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
156 { NULL, CE_UNKNOWN, NULL },
160 * NOTE WELL: si_key MUST NOT have value of NOTOK
162 * si_key is 1 if access method is anonymous.
164 struct str2init str2methods[] = {
165 { "afs", 1, InitFile },
166 { "anon-ftp", 1, InitFTP },
167 { "ftp", 0, InitFTP },
168 { "local-file", 0, InitFile },
169 { "mail-server", 0, InitMail },
175 pidcheck (int status)
177 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
188 * Main entry point for parsing a MIME message or file.
189 * It returns the Content structure for the top level
190 * entity in the file.
194 parse_mime (char *file)
202 * Check if file is actually standard input
204 if ((is_stdin = !(strcmp (file, "-")))) {
205 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
207 advise("mhparse", "unable to create temporary file");
210 file = add (tfile, NULL);
213 while (fgets (buffer, sizeof(buffer), stdin))
217 if (ferror (stdin)) {
219 advise ("stdin", "error reading");
224 advise (file, "error writing");
227 fseek (fp, 0L, SEEK_SET);
228 } else if ((fp = fopen (file, "r")) == NULL) {
229 advise (file, "unable to read");
233 if (!(ct = get_content (fp, file, 1))) {
236 advise (NULL, "unable to decode %s", file);
241 ct->c_unlink = 1; /* temp file to remove */
245 if (ct->c_end == 0L) {
246 fseek (fp, 0L, SEEK_END);
247 ct->c_end = ftell (fp);
250 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
262 * Main routine for reading/parsing the headers
263 * of a message content.
265 * toplevel = 1 # we are at the top level of the message
266 * toplevel = 0 # we are inside message type or multipart type
267 * # other than multipart/digest
268 * toplevel = -1 # we are inside multipart/digest
269 * NB: on failure we will fclose(in)!
273 get_content (FILE *in, char *file, int toplevel)
276 char buf[BUFSIZ], name[NAMESZ];
281 /* allocate the content structure */
282 if (!(ct = (CT) calloc (1, sizeof(*ct))))
283 adios (NULL, "out of memory");
286 ct->c_file = add (file, NULL);
287 ct->c_begin = ftell (ct->c_fp) + 1;
290 * Parse the header fields for this
291 * content into a linked list.
293 for (compnum = 1, state = FLD;;) {
294 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
300 /* get copies of the buffers */
301 np = add (name, NULL);
302 vp = add (buf, NULL);
304 /* if necessary, get rest of field */
305 while (state == FLDPLUS) {
306 state = m_getfld (state, name, buf, sizeof(buf), in);
307 vp = add (buf, vp); /* add to previous value */
310 /* Now add the header data to the list */
311 add_header (ct, np, vp);
313 /* continue, if this isn't the last header field */
314 if (state != FLDEOF) {
315 ct->c_begin = ftell (in) + 1;
322 ct->c_begin = ftell (in) - strlen (buf);
326 ct->c_begin = ftell (in);
331 adios (NULL, "message format error in component #%d", compnum);
334 adios (NULL, "getfld() returned %d", state);
337 /* break out of the loop */
342 * Read the content headers. We will parse the
343 * MIME related header fields into their various
344 * structures and set internal flags related to
345 * content type/subtype, etc.
348 hp = ct->c_first_hf; /* start at first header field */
350 /* Get MIME-Version field */
351 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
354 unsigned char *cp, *dp;
357 advise (NULL, "message %s has multiple %s: fields",
358 ct->c_file, VRSN_FIELD);
361 ct->c_vrsn = add (hp->value, NULL);
363 /* Now, cleanup this field */
366 while (isspace (*cp))
368 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
370 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
375 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
377 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
380 for (dp = cp; istoken (*dp); dp++)
384 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
387 admonish (NULL, "message %s has unknown value for %s: field (%s)",
388 ct->c_file, VRSN_FIELD, cp);
391 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
392 /* Get Content-Type field */
393 struct str2init *s2i;
394 CI ci = &ct->c_ctinfo;
396 /* Check if we've already seen a Content-Type header */
398 advise (NULL, "message %s has multiple %s: fields",
399 ct->c_file, TYPE_FIELD);
403 /* Parse the Content-Type field */
404 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
408 * Set the Init function and the internal
409 * flag for this content type.
411 for (s2i = str2cts; s2i->si_key; s2i++)
412 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
414 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
416 ct->c_type = s2i->si_val;
417 ct->c_ctinitfnx = s2i->si_init;
419 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
420 /* Get Content-Transfer-Encoding field */
422 unsigned char *cp, *dp;
423 struct str2init *s2i;
426 * Check if we've already seen the
427 * Content-Transfer-Encoding field
430 advise (NULL, "message %s has multiple %s: fields",
431 ct->c_file, ENCODING_FIELD);
435 /* get copy of this field */
436 ct->c_celine = cp = add (hp->value, NULL);
438 while (isspace (*cp))
440 for (dp = cp; istoken (*dp); dp++)
446 * Find the internal flag and Init function
447 * for this transfer encoding.
449 for (s2i = str2ces; s2i->si_key; s2i++)
450 if (!mh_strcasecmp (cp, s2i->si_key))
452 if (!s2i->si_key && !uprf (cp, "X-"))
455 ct->c_encoding = s2i->si_val;
457 /* Call the Init function for this encoding */
458 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
461 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
462 /* Get Content-MD5 field */
463 unsigned char *cp, *dp;
469 if (ct->c_digested) {
470 advise (NULL, "message %s has multiple %s: fields",
471 ct->c_file, MD5_FIELD);
475 ep = cp = add (hp->value, NULL); /* get a copy */
477 while (isspace (*cp))
479 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
481 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
486 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
488 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
493 for (dp = cp; *dp && !isspace (*dp); dp++)
501 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
502 /* Get Content-ID field */
503 ct->c_id = add (hp->value, ct->c_id);
505 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
506 /* Get Content-Description field */
507 ct->c_descr = add (hp->value, ct->c_descr);
509 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
510 /* Get Content-Disposition field */
511 ct->c_dispo = add (hp->value, ct->c_dispo);
515 hp = hp->next; /* next header field */
519 * Check if we saw a Content-Type field.
520 * If not, then assign a default value for
521 * it, and the Init function.
525 * If we are inside a multipart/digest message,
526 * so default type is message/rfc822
529 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
531 ct->c_type = CT_MESSAGE;
532 ct->c_ctinitfnx = InitMessage;
535 * Else default type is text/plain
537 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
539 ct->c_type = CT_TEXT;
540 ct->c_ctinitfnx = InitText;
544 /* Use default Transfer-Encoding, if necessary */
546 ct->c_encoding = CE_7BIT;
559 * small routine to add header field to list
563 add_header (CT ct, char *name, char *value)
567 /* allocate header field structure */
568 hp = mh_xmalloc (sizeof(*hp));
570 /* link data into header structure */
575 /* link header structure into the list */
576 if (ct->c_first_hf == NULL) {
577 ct->c_first_hf = hp; /* this is the first */
580 ct->c_last_hf->next = hp; /* add it to the end */
588 /* Make sure that buf contains at least one appearance of name,
589 followed by =. If not, insert both name and value, just after
590 first semicolon, if any. Note that name should not contain a
591 trailing =. And quotes will be added around the value. Typical
592 usage: make sure that a Content-Disposition header contains
593 filename="foo". If it doesn't and value does, use value from
596 incl_name_value (unsigned char *buf, char *name, char *value) {
599 /* Assume that name is non-null. */
601 char *name_plus_equal = concat (name, "=", NULL);
603 if (! strstr (buf, name_plus_equal)) {
606 char *prefix, *suffix;
608 /* Trim trailing space, esp. newline. */
609 for (cp = &buf[strlen (buf) - 1];
610 cp >= buf && isspace (*cp);
615 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
617 /* Insert at first semicolon, if any. If none, append to
619 prefix = add (buf, NULL);
620 if ((cp = strchr (prefix, ';'))) {
621 suffix = concat (cp, NULL);
623 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
627 newbuf = concat (buf, insertion, "\n", NULL);
635 free (name_plus_equal);
641 /* Extract just name_suffix="foo", if any, from value. If there isn't
642 one, return the entire value. Note that, for example, a name_suffix
643 of name will match filename="foo", and return foo. */
645 extract_name_value (char *name_suffix, char *value) {
646 char *extracted_name_value = value;
647 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
648 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
651 free (name_suffix_plus_quote);
652 if (name_suffix_equals) {
653 char *name_suffix_begin;
656 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
657 name_suffix_begin = ++cp;
658 /* Find second \". */
659 for (; *cp != '"'; ++cp) /* empty */;
661 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
662 memcpy (extracted_name_value,
664 cp - name_suffix_begin);
665 extracted_name_value[cp - name_suffix_begin] = '\0';
668 return extracted_name_value;
672 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
673 * directives. Fills in the information of the CTinfo structure.
676 get_ctinfo (unsigned char *cp, CT ct, int magic)
685 i = strlen (invo_name) + 2;
687 /* store copy of Content-Type line */
688 cp = ct->c_ctline = add (cp, NULL);
690 while (isspace (*cp)) /* trim leading spaces */
693 /* change newlines to spaces */
694 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
697 /* trim trailing spaces */
698 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
704 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
706 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
709 for (dp = cp; istoken (*dp); dp++)
712 ci->ci_type = add (cp, NULL); /* store content type */
716 advise (NULL, "invalid %s: field in message %s (empty type)",
717 TYPE_FIELD, ct->c_file);
721 /* down case the content type string */
722 for (dp = ci->ci_type; *dp; dp++)
723 if (isalpha(*dp) && isupper (*dp))
726 while (isspace (*cp))
729 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
734 ci->ci_subtype = add ("", NULL);
739 while (isspace (*cp))
742 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
745 for (dp = cp; istoken (*dp); dp++)
748 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
751 if (!*ci->ci_subtype) {
753 "invalid %s: field in message %s (empty subtype for \"%s\")",
754 TYPE_FIELD, ct->c_file, ci->ci_type);
758 /* down case the content subtype string */
759 for (dp = ci->ci_subtype; *dp; dp++)
760 if (isalpha(*dp) && isupper (*dp))
764 while (isspace (*cp))
767 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
771 * Parse attribute/value pairs given with Content-Type
773 ep = (ap = ci->ci_attrs) + NPARMS;
780 "too many parameters in message %s's %s: field (%d max)",
781 ct->c_file, TYPE_FIELD, NPARMS);
786 while (isspace (*cp))
789 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
794 "extraneous trailing ';' in message %s's %s: parameter list",
795 ct->c_file, TYPE_FIELD);
799 /* down case the attribute name */
800 for (dp = cp; istoken (*dp); dp++)
801 if (isalpha(*dp) && isupper (*dp))
804 for (up = dp; isspace (*dp);)
806 if (dp == cp || *dp != '=') {
808 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
809 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
813 vp = (*ap = add (cp, NULL)) + (up - cp);
815 for (dp++; isspace (*dp);)
818 /* now add the attribute value */
819 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
822 for (cp = ++dp, dp = vp;;) {
827 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
828 ct->c_file, TYPE_FIELD, i, i, "", *ap);
833 if ((c = *cp++) == '\0')
848 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
854 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
855 ct->c_file, TYPE_FIELD, i, i, "", *ap);
860 while (isspace (*cp))
863 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
868 * Get any <Content-Id> given in buffer
870 if (magic && *cp == '<') {
875 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
876 advise (NULL, "invalid ID in message %s", ct->c_file);
882 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
888 while (isspace (*cp))
893 * Get any [Content-Description] given in buffer.
895 if (magic && *cp == '[') {
897 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
901 advise (NULL, "invalid description in message %s", ct->c_file);
909 ct->c_descr = concat (ct->c_descr, "\n", NULL);
915 while (isspace (*cp))
920 * Get any {Content-Disposition} given in buffer.
922 if (magic && *cp == '{') {
924 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
928 advise (NULL, "invalid disposition in message %s", ct->c_file);
936 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
942 while (isspace (*cp))
947 * Check if anything is left over
951 ci->ci_magic = add (cp, NULL);
953 /* If there is a Content-Disposition header and it doesn't
954 have a *filename=, extract it from the magic contents.
955 The r1bindex call skips any leading directory
959 incl_name_value (ct->c_dispo,
961 r1bindex (extract_name_value ("name",
968 "extraneous information in message %s's %s: field\n%*.*s(%s)",
969 ct->c_file, TYPE_FIELD, i, i, "", cp);
977 get_comment (CT ct, unsigned char **ap, int istype)
982 char c, buffer[BUFSIZ], *dp;
994 advise (NULL, "invalid comment in message %s's %s: field",
995 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1000 if ((c = *cp++) == '\0')
1023 if ((dp = ci->ci_comment)) {
1024 ci->ci_comment = concat (dp, " ", buffer, NULL);
1027 ci->ci_comment = add (buffer, NULL);
1031 while (isspace (*cp))
1042 * Handles content types audio, image, and video.
1043 * There's not much to do right here.
1049 return OK; /* not much to do here */
1060 char buffer[BUFSIZ];
1062 char **ap, **ep, *cp;
1065 CI ci = &ct->c_ctinfo;
1067 /* check for missing subtype */
1068 if (!*ci->ci_subtype)
1069 ci->ci_subtype = add ("plain", ci->ci_subtype);
1072 for (kv = SubText; kv->kv_key; kv++)
1073 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1075 ct->c_subtype = kv->kv_value;
1077 /* allocate text character set structure */
1078 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1079 adios (NULL, "out of memory");
1080 ct->c_ctparams = (void *) t;
1082 /* scan for charset parameter */
1083 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1084 if (!mh_strcasecmp (*ap, "charset"))
1087 /* check if content specified a character set */
1089 /* match character set or set to CHARSET_UNKNOWN */
1090 for (kv = Charset; kv->kv_key; kv++) {
1091 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1096 t->tx_charset = kv->kv_value;
1098 t->tx_charset = CHARSET_UNSPECIFIED;
1102 * If we can not handle character set natively,
1103 * then check profile for string to modify the
1104 * terminal or display method.
1106 * termproc is for mhshow, though mhlist -debug prints it, too.
1108 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1109 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1110 if ((cp = context_find (buffer)))
1111 ct->c_termproc = getcpy (cp);
1123 InitMultiPart (CT ct)
1127 unsigned char *cp, *dp;
1129 char *bp, buffer[BUFSIZ];
1130 struct multipart *m;
1132 struct part *part, **next;
1133 CI ci = &ct->c_ctinfo;
1138 * The encoding for multipart messages must be either
1139 * 7bit, 8bit, or binary (per RFC2045).
1141 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1142 && ct->c_encoding != CE_BINARY) {
1144 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1145 ci->ci_type, ci->ci_subtype, ct->c_file);
1150 for (kv = SubMultiPart; kv->kv_key; kv++)
1151 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1153 ct->c_subtype = kv->kv_value;
1156 * Check for "boundary" parameter, which is
1157 * required for multipart messages.
1160 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1161 if (!mh_strcasecmp (*ap, "boundary")) {
1167 /* complain if boundary parameter is missing */
1170 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1171 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1175 /* allocate primary structure for multipart info */
1176 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1177 adios (NULL, "out of memory");
1178 ct->c_ctparams = (void *) m;
1180 /* check if boundary parameter contains only whitespace characters */
1181 for (cp = bp; isspace (*cp); cp++)
1184 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1185 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1189 /* remove trailing whitespace from boundary parameter */
1190 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1195 /* record boundary separators */
1196 m->mp_start = concat (bp, "\n", NULL);
1197 m->mp_stop = concat (bp, "--\n", NULL);
1199 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1200 advise (ct->c_file, "unable to open for reading");
1204 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1206 next = &m->mp_parts;
1210 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1214 pos += strlen (buffer);
1215 if (buffer[0] != '-' || buffer[1] != '-')
1218 if (strcmp (buffer + 2, m->mp_start))
1221 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1222 adios (NULL, "out of memory");
1224 next = &part->mp_next;
1226 if (!(p = get_content (fp, ct->c_file,
1227 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1234 fseek (fp, pos, SEEK_SET);
1237 if (strcmp (buffer + 2, m->mp_start) == 0) {
1241 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1242 if (p->c_end < p->c_begin)
1243 p->c_begin = p->c_end;
1248 if (strcmp (buffer + 2, m->mp_stop) == 0)
1254 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1255 if (!inout && part) {
1257 p->c_end = ct->c_end;
1259 if (p->c_begin >= p->c_end) {
1260 for (next = &m->mp_parts; *next != part;
1261 next = &((*next)->mp_next))
1265 free ((char *) part);
1270 /* reverse the order of the parts for multipart/alternative */
1271 if (ct->c_subtype == MULTI_ALTERNATE)
1275 * label all subparts with part number, and
1276 * then initialize the content of the subpart.
1281 char partnam[BUFSIZ];
1284 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1285 pp = partnam + strlen (partnam);
1290 for (part = m->mp_parts, partnum = 1; part;
1291 part = part->mp_next, partnum++) {
1294 sprintf (pp, "%d", partnum);
1295 p->c_partno = add (partnam, NULL);
1297 /* initialize the content of the subparts */
1298 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1313 * reverse the order of the parts of a multipart
1317 reverse_parts (CT ct)
1320 struct multipart *m;
1321 struct part **base, **bmp, **next, *part;
1323 m = (struct multipart *) ct->c_ctparams;
1325 /* if only one part, just return */
1326 if (!m->mp_parts || !m->mp_parts->mp_next)
1329 /* count number of parts */
1331 for (part = m->mp_parts; part; part = part->mp_next)
1334 /* allocate array of pointers to the parts */
1335 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1336 adios (NULL, "out of memory");
1339 /* point at all the parts */
1340 for (part = m->mp_parts; part; part = part->mp_next)
1344 /* reverse the order of the parts */
1345 next = &m->mp_parts;
1346 for (bmp--; bmp >= base; bmp--) {
1349 next = &part->mp_next;
1353 /* free array of pointers */
1354 free ((char *) base);
1366 CI ci = &ct->c_ctinfo;
1368 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1370 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1371 ci->ci_type, ci->ci_subtype, ct->c_file);
1375 /* check for missing subtype */
1376 if (!*ci->ci_subtype)
1377 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1380 for (kv = SubMessage; kv->kv_key; kv++)
1381 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1383 ct->c_subtype = kv->kv_value;
1385 switch (ct->c_subtype) {
1386 case MESSAGE_RFC822:
1389 case MESSAGE_PARTIAL:
1394 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1395 adios (NULL, "out of memory");
1396 ct->c_ctparams = (void *) p;
1398 /* scan for parameters "id", "number", and "total" */
1399 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1400 if (!mh_strcasecmp (*ap, "id")) {
1401 p->pm_partid = add (*ep, NULL);
1404 if (!mh_strcasecmp (*ap, "number")) {
1405 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1406 || p->pm_partno < 1) {
1409 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1410 *ap, ci->ci_type, ci->ci_subtype,
1411 ct->c_file, TYPE_FIELD);
1416 if (!mh_strcasecmp (*ap, "total")) {
1417 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1426 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1428 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1429 ci->ci_type, ci->ci_subtype,
1430 ct->c_file, TYPE_FIELD);
1436 case MESSAGE_EXTERNAL:
1443 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1444 adios (NULL, "out of memory");
1445 ct->c_ctparams = (void *) e;
1448 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1449 advise (ct->c_file, "unable to open for reading");
1453 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1455 if (!(p = get_content (fp, ct->c_file, 0))) {
1463 if ((exresult = params_external (ct, 0)) != NOTOK
1464 && p->c_ceopenfnx == openMail) {
1468 if ((size = ct->c_end - p->c_begin) <= 0) {
1470 content_error (NULL, ct,
1471 "empty body for access-type=mail-server");
1475 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1476 fseek (p->c_fp, p->c_begin, SEEK_SET);
1478 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1480 adios ("failed", "fread");
1483 adios (NULL, "unexpected EOF from fread");
1486 bp += cc, size -= cc;
1493 p->c_end = p->c_begin;
1498 if (exresult == NOTOK)
1500 if (e->eb_flags == NOTOK)
1503 switch (p->c_type) {
1508 if (p->c_subtype != MESSAGE_RFC822)
1512 e->eb_partno = ct->c_partno;
1514 (*p->c_ctinitfnx) (p);
1529 params_external (CT ct, int composing)
1532 struct exbody *e = (struct exbody *) ct->c_ctparams;
1533 CI ci = &ct->c_ctinfo;
1535 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1536 if (!mh_strcasecmp (*ap, "access-type")) {
1537 struct str2init *s2i;
1538 CT p = e->eb_content;
1540 for (s2i = str2methods; s2i->si_key; s2i++)
1541 if (!mh_strcasecmp (*ep, s2i->si_key))
1545 e->eb_flags = NOTOK;
1546 p->c_encoding = CE_EXTERNAL;
1549 e->eb_access = s2i->si_key;
1550 e->eb_flags = s2i->si_val;
1551 p->c_encoding = CE_EXTERNAL;
1553 /* Call the Init function for this external type */
1554 if ((*s2i->si_init)(p) == NOTOK)
1558 if (!mh_strcasecmp (*ap, "name")) {
1562 if (!mh_strcasecmp (*ap, "permission")) {
1563 e->eb_permission = *ep;
1566 if (!mh_strcasecmp (*ap, "site")) {
1570 if (!mh_strcasecmp (*ap, "directory")) {
1574 if (!mh_strcasecmp (*ap, "mode")) {
1578 if (!mh_strcasecmp (*ap, "size")) {
1579 sscanf (*ep, "%lu", &e->eb_size);
1582 if (!mh_strcasecmp (*ap, "server")) {
1586 if (!mh_strcasecmp (*ap, "subject")) {
1587 e->eb_subject = *ep;
1590 if (composing && !mh_strcasecmp (*ap, "body")) {
1591 e->eb_body = getcpy (*ep);
1596 if (!e->eb_access) {
1598 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1599 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1612 InitApplication (CT ct)
1615 CI ci = &ct->c_ctinfo;
1618 for (kv = SubApplication; kv->kv_key; kv++)
1619 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1621 ct->c_subtype = kv->kv_value;
1628 * TRANSFER ENCODINGS
1632 init_encoding (CT ct, OpenCEFunc openfnx)
1636 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1637 adios (NULL, "out of memory");
1640 ct->c_ceopenfnx = openfnx;
1641 ct->c_ceclosefnx = close_encoding;
1642 ct->c_cesizefnx = size_encoding;
1649 close_encoding (CT ct)
1653 if (!(ce = ct->c_cefile))
1663 static unsigned long
1664 size_encoding (CT ct)
1672 if (!(ce = ct->c_cefile))
1673 return (ct->c_end - ct->c_begin);
1675 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1676 return (long) st.st_size;
1679 if (stat (ce->ce_file, &st) != NOTOK)
1680 return (long) st.st_size;
1685 if (ct->c_encoding == CE_EXTERNAL)
1686 return (ct->c_end - ct->c_begin);
1689 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1690 return (ct->c_end - ct->c_begin);
1692 if (fstat (fd, &st) != NOTOK)
1693 size = (long) st.st_size;
1697 (*ct->c_ceclosefnx) (ct);
1706 static unsigned char b642nib[0x80] = {
1707 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1713 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1714 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1715 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1716 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1717 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1718 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1719 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1720 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1721 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1722 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1729 return init_encoding (ct, openBase64);
1734 openBase64 (CT ct, char **file)
1736 int bitno, cc, digested;
1739 unsigned char value, *b, *b1, *b2, *b3;
1740 unsigned char *cp, *ep;
1741 char buffer[BUFSIZ];
1742 /* sbeck -- handle suffixes */
1747 b = (unsigned char *) &bits;
1748 b1 = &b[endian > 0 ? 1 : 2];
1749 b2 = &b[endian > 0 ? 2 : 1];
1750 b3 = &b[endian > 0 ? 3 : 0];
1754 fseek (ce->ce_fp, 0L, SEEK_SET);
1759 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1760 content_error (ce->ce_file, ct, "unable to fopen for reading");
1766 if (*file == NULL) {
1767 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1770 ce->ce_file = add (*file, NULL);
1774 /* sbeck@cise.ufl.edu -- handle suffixes */
1776 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1777 invo_name, ci->ci_type, ci->ci_subtype);
1778 cp = context_find (buffer);
1779 if (cp == NULL || *cp == '\0') {
1780 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1782 cp = context_find (buffer);
1784 if (cp != NULL && *cp != '\0') {
1785 if (ce->ce_unlink) {
1786 // Temporary file already exists, so we rename to
1787 // version with extension.
1788 char *file_org = strdup(ce->ce_file);
1789 ce->ce_file = add (cp, ce->ce_file);
1790 if (rename(file_org, ce->ce_file)) {
1791 adios (ce->ce_file, "unable to rename %s to ", file_org);
1796 ce->ce_file = add (cp, ce->ce_file);
1800 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1801 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1805 if ((len = ct->c_end - ct->c_begin) < 0)
1806 adios (NULL, "internal error(1)");
1808 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1809 content_error (ct->c_file, ct, "unable to open for reading");
1813 if ((digested = ct->c_digested))
1814 MD5Init (&mdContext);
1820 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1822 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1824 content_error (ct->c_file, ct, "error reading from");
1828 content_error (NULL, ct, "premature eof");
1836 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1841 if (skip || (*cp & 0x80)
1842 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1844 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1846 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1849 content_error (NULL, ct,
1850 "invalid BASE64 encoding -- continuing");
1854 bits |= value << bitno;
1856 if ((bitno -= 6) < 0) {
1857 putc ((char) *b1, ce->ce_fp);
1859 MD5Update (&mdContext, b1, 1);
1861 putc ((char) *b2, ce->ce_fp);
1863 MD5Update (&mdContext, b2, 1);
1865 putc ((char) *b3, ce->ce_fp);
1867 MD5Update (&mdContext, b3, 1);
1871 if (ferror (ce->ce_fp)) {
1872 content_error (ce->ce_file, ct,
1873 "error writing to");
1876 bitno = 18, bits = 0L, skip = 0;
1882 goto self_delimiting;
1891 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1893 content_error (NULL, ct, "invalid BASE64 encoding");
1898 fseek (ct->c_fp, 0L, SEEK_SET);
1900 if (fflush (ce->ce_fp)) {
1901 content_error (ce->ce_file, ct, "error writing to");
1906 unsigned char digest[16];
1908 MD5Final (digest, &mdContext);
1909 if (memcmp((char *) digest, (char *) ct->c_digest,
1910 sizeof(digest) / sizeof(digest[0])))
1911 content_error (NULL, ct,
1912 "content integrity suspect (digest mismatch) -- continuing");
1915 fprintf (stderr, "content integrity confirmed\n");
1918 fseek (ce->ce_fp, 0L, SEEK_SET);
1921 *file = ce->ce_file;
1922 return fileno (ce->ce_fp);
1925 free_encoding (ct, 0);
1934 static char hex2nib[0x80] = {
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1942 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1957 return init_encoding (ct, openQuoted);
1962 openQuoted (CT ct, char **file)
1964 int cc, digested, len, quoted;
1965 unsigned char *cp, *ep;
1966 char buffer[BUFSIZ];
1969 /* sbeck -- handle suffixes */
1975 fseek (ce->ce_fp, 0L, SEEK_SET);
1980 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1981 content_error (ce->ce_file, ct, "unable to fopen for reading");
1987 if (*file == NULL) {
1988 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1991 ce->ce_file = add (*file, NULL);
1995 /* sbeck@cise.ufl.edu -- handle suffixes */
1997 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1998 invo_name, ci->ci_type, ci->ci_subtype);
1999 cp = context_find (buffer);
2000 if (cp == NULL || *cp == '\0') {
2001 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2003 cp = context_find (buffer);
2005 if (cp != NULL && *cp != '\0') {
2006 if (ce->ce_unlink) {
2007 // Temporary file already exists, so we rename to
2008 // version with extension.
2009 char *file_org = strdup(ce->ce_file);
2010 ce->ce_file = add (cp, ce->ce_file);
2011 if (rename(file_org, ce->ce_file)) {
2012 adios (ce->ce_file, "unable to rename %s to ", file_org);
2017 ce->ce_file = add (cp, ce->ce_file);
2021 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2022 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2026 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2027 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2031 if ((len = ct->c_end - ct->c_begin) < 0)
2032 adios (NULL, "internal error(2)");
2034 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2035 content_error (ct->c_file, ct, "unable to open for reading");
2039 if ((digested = ct->c_digested))
2040 MD5Init (&mdContext);
2047 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2049 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2050 content_error (NULL, ct, "premature eof");
2054 if ((cc = strlen (buffer)) > len)
2058 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2063 for (; cp < ep; cp++) {
2065 /* in an escape sequence */
2067 /* at byte 1 of an escape sequence */
2068 mask = hex2nib[*cp & 0x7f];
2069 /* next is byte 2 */
2072 /* at byte 2 of an escape sequence */
2074 mask |= hex2nib[*cp & 0x7f];
2075 putc (mask, ce->ce_fp);
2077 MD5Update (&mdContext, &mask, 1);
2078 if (ferror (ce->ce_fp)) {
2079 content_error (ce->ce_file, ct, "error writing to");
2082 /* finished escape sequence; next may be literal or a new
2083 * escape sequence */
2086 /* on to next byte */
2090 /* not in an escape sequence */
2092 /* starting an escape sequence, or invalid '='? */
2093 if (cp + 1 < ep && cp[1] == '\n') {
2094 /* "=\n" soft line break, eat the \n */
2098 if (cp + 1 >= ep || cp + 2 >= ep) {
2099 /* We don't have 2 bytes left, so this is an invalid
2100 * escape sequence; just show the raw bytes (below). */
2101 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2102 /* Next 2 bytes are hex digits, making this a valid escape
2103 * sequence; let's decode it (above). */
2107 /* One or both of the next 2 is out of range, making this
2108 * an invalid escape sequence; just show the raw bytes
2113 /* Just show the raw byte. */
2114 putc (*cp, ce->ce_fp);
2117 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2119 MD5Update (&mdContext, (unsigned char *) cp, 1);
2122 if (ferror (ce->ce_fp)) {
2123 content_error (ce->ce_file, ct, "error writing to");
2129 content_error (NULL, ct,
2130 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2134 fseek (ct->c_fp, 0L, SEEK_SET);
2136 if (fflush (ce->ce_fp)) {
2137 content_error (ce->ce_file, ct, "error writing to");
2142 unsigned char digest[16];
2144 MD5Final (digest, &mdContext);
2145 if (memcmp((char *) digest, (char *) ct->c_digest,
2146 sizeof(digest) / sizeof(digest[0])))
2147 content_error (NULL, ct,
2148 "content integrity suspect (digest mismatch) -- continuing");
2151 fprintf (stderr, "content integrity confirmed\n");
2154 fseek (ce->ce_fp, 0L, SEEK_SET);
2157 *file = ce->ce_file;
2158 return fileno (ce->ce_fp);
2161 free_encoding (ct, 0);
2173 if (init_encoding (ct, open7Bit) == NOTOK)
2176 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2182 open7Bit (CT ct, char **file)
2185 char buffer[BUFSIZ];
2186 /* sbeck -- handle suffixes */
2193 fseek (ce->ce_fp, 0L, SEEK_SET);
2198 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2199 content_error (ce->ce_file, ct, "unable to fopen for reading");
2205 if (*file == NULL) {
2206 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2209 ce->ce_file = add (*file, NULL);
2213 /* sbeck@cise.ufl.edu -- handle suffixes */
2215 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2216 invo_name, ci->ci_type, ci->ci_subtype);
2217 cp = context_find (buffer);
2218 if (cp == NULL || *cp == '\0') {
2219 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2221 cp = context_find (buffer);
2223 if (cp != NULL && *cp != '\0') {
2224 if (ce->ce_unlink) {
2225 // Temporary file already exists, so we rename to
2226 // version with extension.
2227 char *file_org = strdup(ce->ce_file);
2228 ce->ce_file = add (cp, ce->ce_file);
2229 if (rename(file_org, ce->ce_file)) {
2230 adios (ce->ce_file, "unable to rename %s to ", file_org);
2235 ce->ce_file = add (cp, ce->ce_file);
2239 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2240 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2244 if (ct->c_type == CT_MULTIPART) {
2246 CI ci = &ct->c_ctinfo;
2249 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2250 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2251 + 1 + strlen (ci->ci_subtype);
2252 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2253 putc (';', ce->ce_fp);
2256 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2258 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2259 fputs ("\n\t", ce->ce_fp);
2262 putc (' ', ce->ce_fp);
2265 fprintf (ce->ce_fp, "%s", buffer);
2269 if (ci->ci_comment) {
2270 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2271 fputs ("\n\t", ce->ce_fp);
2275 putc (' ', ce->ce_fp);
2278 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2281 fprintf (ce->ce_fp, "\n");
2283 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2285 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2287 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2288 fprintf (ce->ce_fp, "\n");
2291 if ((len = ct->c_end - ct->c_begin) < 0)
2292 adios (NULL, "internal error(3)");
2294 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2295 content_error (ct->c_file, ct, "unable to open for reading");
2299 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2301 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2303 content_error (ct->c_file, ct, "error reading from");
2307 content_error (NULL, ct, "premature eof");
2315 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2316 if (ferror (ce->ce_fp)) {
2317 content_error (ce->ce_file, ct, "error writing to");
2322 fseek (ct->c_fp, 0L, SEEK_SET);
2324 if (fflush (ce->ce_fp)) {
2325 content_error (ce->ce_file, ct, "error writing to");
2329 fseek (ce->ce_fp, 0L, SEEK_SET);
2332 *file = ce->ce_file;
2333 return fileno (ce->ce_fp);
2336 free_encoding (ct, 0);
2346 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2348 char cachefile[BUFSIZ];
2351 fseek (ce->ce_fp, 0L, SEEK_SET);
2356 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2357 content_error (ce->ce_file, ct, "unable to fopen for reading");
2363 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2364 cachefile, sizeof(cachefile)) != NOTOK) {
2365 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2366 ce->ce_file = getcpy (cachefile);
2370 admonish (cachefile, "unable to fopen for reading");
2377 *file = ce->ce_file;
2378 *fd = fileno (ce->ce_fp);
2389 return init_encoding (ct, openFile);
2394 openFile (CT ct, char **file)
2397 char cachefile[BUFSIZ];
2398 struct exbody *e = ct->c_ctexbody;
2399 CE ce = ct->c_cefile;
2401 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2413 content_error (NULL, ct, "missing name parameter");
2417 ce->ce_file = getcpy (e->eb_name);
2420 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2421 content_error (ce->ce_file, ct, "unable to fopen for reading");
2425 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2426 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2427 cachefile, sizeof(cachefile)) != NOTOK) {
2431 mask = umask (cachetype ? ~m_gmprot () : 0222);
2432 if ((fp = fopen (cachefile, "w"))) {
2434 char buffer[BUFSIZ];
2435 FILE *gp = ce->ce_fp;
2437 fseek (gp, 0L, SEEK_SET);
2439 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2441 fwrite (buffer, sizeof(*buffer), cc, fp);
2445 admonish (ce->ce_file, "error reading");
2450 admonish (cachefile, "error writing");
2458 fseek (ce->ce_fp, 0L, SEEK_SET);
2459 *file = ce->ce_file;
2460 return fileno (ce->ce_fp);
2470 return init_encoding (ct, openFTP);
2475 openFTP (CT ct, char **file)
2477 int cachetype, caching, fd;
2479 char *bp, *ftp, *user, *pass;
2480 char buffer[BUFSIZ], cachefile[BUFSIZ];
2483 static char *username = NULL;
2484 static char *password = NULL;
2489 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2497 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2508 if (!e->eb_name || !e->eb_site) {
2509 content_error (NULL, ct, "missing %s parameter",
2510 e->eb_name ? "site": "name");
2517 pidcheck (pidwait (xpid, NOTOK));
2521 /* Get the buffer ready to go */
2523 buflen = sizeof(buffer);
2526 * Construct the query message for user
2528 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2534 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2540 snprintf (bp, buflen, "\n using %sFTP from site %s",
2541 e->eb_flags ? "anonymous " : "", e->eb_site);
2546 if (e->eb_size > 0) {
2547 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2552 snprintf (bp, buflen, "? ");
2555 * Now, check the answer
2557 if (!getanswer (buffer))
2562 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2565 ruserpass (e->eb_site, &username, &password);
2570 ce->ce_unlink = (*file == NULL);
2572 cachefile[0] = '\0';
2573 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2574 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2575 cachefile, sizeof(cachefile)) != NOTOK) {
2576 if (*file == NULL) {
2583 ce->ce_file = add (*file, NULL);
2585 ce->ce_file = add (cachefile, NULL);
2587 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2589 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2590 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2598 int child_id, i, vecp;
2602 vec[vecp++] = r1bindex (ftp, '/');
2603 vec[vecp++] = e->eb_site;
2606 vec[vecp++] = e->eb_dir;
2607 vec[vecp++] = e->eb_name;
2608 vec[vecp++] = ce->ce_file,
2609 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2610 ? "ascii" : "binary";
2615 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2619 adios ("fork", "unable to");
2623 close (fileno (ce->ce_fp));
2625 fprintf (stderr, "unable to exec ");
2631 if (pidXwait (child_id, NULL)) {
2635 username = password = NULL;
2644 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2646 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2653 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2658 mask = umask (cachetype ? ~m_gmprot () : 0222);
2659 if ((fp = fopen (cachefile, "w"))) {
2661 FILE *gp = ce->ce_fp;
2663 fseek (gp, 0L, SEEK_SET);
2665 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2667 fwrite (buffer, sizeof(*buffer), cc, fp);
2671 admonish (ce->ce_file, "error reading");
2676 admonish (cachefile, "error writing");
2685 fseek (ce->ce_fp, 0L, SEEK_SET);
2686 *file = ce->ce_file;
2687 return fileno (ce->ce_fp);
2698 return init_encoding (ct, openMail);
2703 openMail (CT ct, char **file)
2705 int child_id, fd, i, vecp;
2707 char *bp, buffer[BUFSIZ], *vec[7];
2708 struct exbody *e = ct->c_ctexbody;
2709 CE ce = ct->c_cefile;
2711 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2722 if (!e->eb_server) {
2723 content_error (NULL, ct, "missing server parameter");
2730 pidcheck (pidwait (xpid, NOTOK));
2734 /* Get buffer ready to go */
2736 buflen = sizeof(buffer);
2738 /* Now, construct query message */
2739 snprintf (bp, buflen, "Retrieve content");
2745 snprintf (bp, buflen, " %s", e->eb_partno);
2751 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2753 e->eb_subject ? e->eb_subject : e->eb_body);
2755 /* Now, check answer */
2756 if (!getanswer (buffer))
2760 vec[vecp++] = r1bindex (mailproc, '/');
2761 vec[vecp++] = e->eb_server;
2762 vec[vecp++] = "-subject";
2763 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2764 vec[vecp++] = "-body";
2765 vec[vecp++] = e->eb_body;
2768 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2772 advise ("fork", "unable to");
2776 execvp (mailproc, vec);
2777 fprintf (stderr, "unable to exec ");
2783 if (pidXwait (child_id, NULL) == OK)
2784 advise (NULL, "request sent");
2788 if (*file == NULL) {
2789 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2792 ce->ce_file = add (*file, NULL);
2796 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2797 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2801 /* showproc is for mhshow and mhstore, though mhlist -debug
2802 * prints it, too. */
2804 free (ct->c_showproc);
2805 ct->c_showproc = add ("true", NULL);
2807 fseek (ce->ce_fp, 0L, SEEK_SET);
2808 *file = ce->ce_file;
2809 return fileno (ce->ce_fp);
2814 readDigest (CT ct, char *cp)
2819 unsigned char *dp, value, *ep;
2820 unsigned char *b, *b1, *b2, *b3;
2822 b = (unsigned char *) &bits,
2823 b1 = &b[endian > 0 ? 1 : 2],
2824 b2 = &b[endian > 0 ? 2 : 1],
2825 b3 = &b[endian > 0 ? 3 : 0];
2830 for (ep = (dp = ct->c_digest)
2831 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2836 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2838 fprintf (stderr, "invalid BASE64 encoding\n");
2842 bits |= value << bitno;
2844 if ((bitno -= 6) < 0) {
2845 if (dp + (3 - skip) > ep)
2846 goto invalid_digest;
2861 goto self_delimiting;
2866 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2876 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2884 fprintf (stderr, "MD5 digest=");
2885 for (dp = ct->c_digest; dp < ep; dp++)
2886 fprintf (stderr, "%02x", *dp & 0xff);
2887 fprintf (stderr, "\n");