3 * mhparse.c -- routines to parse the contents of MIME messages
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
14 #include <h/signals.h>
22 #include <h/mhparse.h>
25 #ifdef HAVE_SYS_WAIT_H
26 # include <sys/wait.h>
32 extern int endian; /* mhmisc.c */
34 extern pid_t xpid; /* mhshowsbr.c */
37 extern int rcachesw; /* mhcachesbr.c */
38 extern int wcachesw; /* mhcachesbr.c */
40 int checksw = 0; /* check Content-MD5 field */
43 * Directory to place temp files. This must
44 * be set before these routines are called.
49 * Structures for TEXT messages
51 struct k2v SubText[] = {
52 { "plain", TEXT_PLAIN },
53 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
54 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
55 { NULL, TEXT_UNKNOWN } /* this one must be last! */
58 struct k2v Charset[] = {
59 { "us-ascii", CHARSET_USASCII },
60 { "iso-8859-1", CHARSET_LATIN },
61 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
65 * Structures for MULTIPART messages
67 struct k2v SubMultiPart[] = {
68 { "mixed", MULTI_MIXED },
69 { "alternative", MULTI_ALTERNATE },
70 { "digest", MULTI_DIGEST },
71 { "parallel", MULTI_PARALLEL },
72 { NULL, MULTI_UNKNOWN } /* this one must be last! */
76 * Structures for MESSAGE messages
78 struct k2v SubMessage[] = {
79 { "rfc822", MESSAGE_RFC822 },
80 { "partial", MESSAGE_PARTIAL },
81 { "external-body", MESSAGE_EXTERNAL },
82 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
86 * Structure for APPLICATION messages
88 struct k2v SubApplication[] = {
89 { "octet-stream", APPLICATION_OCTETS },
90 { "postscript", APPLICATION_POSTSCRIPT },
91 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
96 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
99 int find_cache (CT, int, int *, char *, char *, int);
102 int part_ok (CT, int);
103 int type_ok (CT, int);
104 int make_intermediates (char *);
105 void content_error (char *, CT, char *, ...);
108 void free_content (CT);
109 void free_encoding (CT, int);
114 static CT get_content (FILE *, char *, int);
115 static int get_comment (CT, unsigned char **, int);
117 static int InitGeneric (CT);
118 static int InitText (CT);
119 static int InitMultiPart (CT);
120 static void reverse_parts (CT);
121 static int InitMessage (CT);
122 static int InitApplication (CT);
123 static int init_encoding (CT, OpenCEFunc);
124 static unsigned long size_encoding (CT);
125 static int InitBase64 (CT);
126 static int openBase64 (CT, char **);
127 static int InitQuoted (CT);
128 static int openQuoted (CT, char **);
129 static int Init7Bit (CT);
130 static int openExternal (CT, CT, CE, char **, int *);
131 static int InitFile (CT);
132 static int openFile (CT, char **);
133 static int InitFTP (CT);
134 static int openFTP (CT, char **);
135 static int InitMail (CT);
136 static int openMail (CT, char **);
137 static int readDigest (CT, char *);
139 struct str2init str2cts[] = {
140 { "application", CT_APPLICATION, InitApplication },
141 { "audio", CT_AUDIO, InitGeneric },
142 { "image", CT_IMAGE, InitGeneric },
143 { "message", CT_MESSAGE, InitMessage },
144 { "multipart", CT_MULTIPART, InitMultiPart },
145 { "text", CT_TEXT, InitText },
146 { "video", CT_VIDEO, InitGeneric },
147 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
148 { NULL, CT_UNKNOWN, NULL },
151 struct str2init str2ces[] = {
152 { "base64", CE_BASE64, InitBase64 },
153 { "quoted-printable", CE_QUOTED, InitQuoted },
154 { "8bit", CE_8BIT, Init7Bit },
155 { "7bit", CE_7BIT, Init7Bit },
156 { "binary", CE_BINARY, Init7Bit },
157 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
158 { NULL, CE_UNKNOWN, NULL },
162 * NOTE WELL: si_key MUST NOT have value of NOTOK
164 * si_key is 1 if access method is anonymous.
166 struct str2init str2methods[] = {
167 { "afs", 1, InitFile },
168 { "anon-ftp", 1, InitFTP },
169 { "ftp", 0, InitFTP },
170 { "local-file", 0, InitFile },
171 { "mail-server", 0, InitMail },
177 pidcheck (int status)
179 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
190 * Main entry point for parsing a MIME message or file.
191 * It returns the Content structure for the top level
192 * entity in the file.
196 parse_mime (char *file)
204 * Check if file is actually standard input
206 if ((is_stdin = !(strcmp (file, "-")))) {
207 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
209 advise("mhparse", "unable to create temporary file");
212 file = add (tfile, NULL);
215 while (fgets (buffer, sizeof(buffer), stdin))
219 if (ferror (stdin)) {
221 advise ("stdin", "error reading");
226 advise (file, "error writing");
229 fseek (fp, 0L, SEEK_SET);
230 } else if ((fp = fopen (file, "r")) == NULL) {
231 advise (file, "unable to read");
235 if (!(ct = get_content (fp, file, 1))) {
238 advise (NULL, "unable to decode %s", file);
243 ct->c_unlink = 1; /* temp file to remove */
247 if (ct->c_end == 0L) {
248 fseek (fp, 0L, SEEK_END);
249 ct->c_end = ftell (fp);
252 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
264 * Main routine for reading/parsing the headers
265 * of a message content.
267 * toplevel = 1 # we are at the top level of the message
268 * toplevel = 0 # we are inside message type or multipart type
269 * # other than multipart/digest
270 * toplevel = -1 # we are inside multipart/digest
271 * NB: on failure we will fclose(in)!
275 get_content (FILE *in, char *file, int toplevel)
278 char buf[BUFSIZ], name[NAMESZ];
283 /* allocate the content structure */
284 if (!(ct = (CT) calloc (1, sizeof(*ct))))
285 adios (NULL, "out of memory");
288 ct->c_file = add (file, NULL);
289 ct->c_begin = ftell (ct->c_fp) + 1;
292 * Parse the header fields for this
293 * content into a linked list.
295 for (compnum = 1, state = FLD;;) {
296 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
302 /* get copies of the buffers */
303 np = add (name, NULL);
304 vp = add (buf, NULL);
306 /* if necessary, get rest of field */
307 while (state == FLDPLUS) {
308 state = m_getfld (state, name, buf, sizeof(buf), in);
309 vp = add (buf, vp); /* add to previous value */
312 /* Now add the header data to the list */
313 add_header (ct, np, vp);
315 /* continue, if this isn't the last header field */
316 if (state != FLDEOF) {
317 ct->c_begin = ftell (in) + 1;
324 ct->c_begin = ftell (in) - strlen (buf);
328 ct->c_begin = ftell (in);
333 adios (NULL, "message format error in component #%d", compnum);
336 adios (NULL, "getfld() returned %d", state);
339 /* break out of the loop */
344 * Read the content headers. We will parse the
345 * MIME related header fields into their various
346 * structures and set internal flags related to
347 * content type/subtype, etc.
350 hp = ct->c_first_hf; /* start at first header field */
352 /* Get MIME-Version field */
353 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
356 unsigned char *cp, *dp;
359 advise (NULL, "message %s has multiple %s: fields",
360 ct->c_file, VRSN_FIELD);
363 ct->c_vrsn = add (hp->value, NULL);
365 /* Now, cleanup this field */
368 while (isspace (*cp))
370 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
372 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
377 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
379 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
382 for (dp = cp; istoken (*dp); dp++)
386 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
389 admonish (NULL, "message %s has unknown value for %s: field (%s)",
390 ct->c_file, VRSN_FIELD, cp);
393 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
394 /* Get Content-Type field */
395 struct str2init *s2i;
396 CI ci = &ct->c_ctinfo;
398 /* Check if we've already seen a Content-Type header */
400 advise (NULL, "message %s has multiple %s: fields",
401 ct->c_file, TYPE_FIELD);
405 /* Parse the Content-Type field */
406 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
410 * Set the Init function and the internal
411 * flag for this content type.
413 for (s2i = str2cts; s2i->si_key; s2i++)
414 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
416 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
418 ct->c_type = s2i->si_val;
419 ct->c_ctinitfnx = s2i->si_init;
421 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
422 /* Get Content-Transfer-Encoding field */
424 unsigned char *cp, *dp;
425 struct str2init *s2i;
428 * Check if we've already seen the
429 * Content-Transfer-Encoding field
432 advise (NULL, "message %s has multiple %s: fields",
433 ct->c_file, ENCODING_FIELD);
437 /* get copy of this field */
438 ct->c_celine = cp = add (hp->value, NULL);
440 while (isspace (*cp))
442 for (dp = cp; istoken (*dp); dp++)
448 * Find the internal flag and Init function
449 * for this transfer encoding.
451 for (s2i = str2ces; s2i->si_key; s2i++)
452 if (!mh_strcasecmp (cp, s2i->si_key))
454 if (!s2i->si_key && !uprf (cp, "X-"))
457 ct->c_encoding = s2i->si_val;
459 /* Call the Init function for this encoding */
460 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
463 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
464 /* Get Content-MD5 field */
465 unsigned char *cp, *dp;
471 if (ct->c_digested) {
472 advise (NULL, "message %s has multiple %s: fields",
473 ct->c_file, MD5_FIELD);
477 ep = cp = add (hp->value, NULL); /* get a copy */
479 while (isspace (*cp))
481 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
483 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
488 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
490 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
495 for (dp = cp; *dp && !isspace (*dp); dp++)
503 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
504 /* Get Content-ID field */
505 ct->c_id = add (hp->value, ct->c_id);
507 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
508 /* Get Content-Description field */
509 ct->c_descr = add (hp->value, ct->c_descr);
511 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
512 /* Get Content-Disposition field */
513 ct->c_dispo = add (hp->value, ct->c_dispo);
517 hp = hp->next; /* next header field */
521 * Check if we saw a Content-Type field.
522 * If not, then assign a default value for
523 * it, and the Init function.
527 * If we are inside a multipart/digest message,
528 * so default type is message/rfc822
531 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
533 ct->c_type = CT_MESSAGE;
534 ct->c_ctinitfnx = InitMessage;
537 * Else default type is text/plain
539 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
541 ct->c_type = CT_TEXT;
542 ct->c_ctinitfnx = InitText;
546 /* Use default Transfer-Encoding, if necessary */
548 ct->c_encoding = CE_7BIT;
561 * small routine to add header field to list
565 add_header (CT ct, char *name, char *value)
569 /* allocate header field structure */
570 hp = mh_xmalloc (sizeof(*hp));
572 /* link data into header structure */
577 /* link header structure into the list */
578 if (ct->c_first_hf == NULL) {
579 ct->c_first_hf = hp; /* this is the first */
582 ct->c_last_hf->next = hp; /* add it to the end */
590 /* Make sure that buf contains at least one appearance of name,
591 followed by =. If not, insert both name and value, just after
592 first semicolon, if any. Note that name should not contain a
593 trailing =. And quotes will be added around the value. Typical
594 usage: make sure that a Content-Disposition header contains
595 filename="foo". If it doesn't and value does, use value from
598 incl_name_value (unsigned char *buf, char *name, char *value) {
601 /* Assume that name is non-null. */
603 char *name_plus_equal = concat (name, "=", NULL);
605 if (! strstr (buf, name_plus_equal)) {
608 char *prefix, *suffix;
610 /* Trim trailing space, esp. newline. */
611 for (cp = &buf[strlen (buf) - 1];
612 cp >= buf && isspace (*cp);
617 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
619 /* Insert at first semicolon, if any. If none, append to
621 prefix = add (buf, NULL);
622 if ((cp = strchr (prefix, ';'))) {
623 suffix = concat (cp, NULL);
625 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
629 newbuf = concat (buf, insertion, "\n", NULL);
637 free (name_plus_equal);
643 /* Extract just name_suffix="foo", if any, from value. If there isn't
644 one, return the entire value. Note that, for example, a name_suffix
645 of name will match filename="foo", and return foo. */
647 extract_name_value (char *name_suffix, char *value) {
648 char *extracted_name_value = value;
649 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
650 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
653 free (name_suffix_plus_quote);
654 if (name_suffix_equals) {
655 char *name_suffix_begin;
658 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
659 name_suffix_begin = ++cp;
660 /* Find second \". */
661 for (; *cp != '"'; ++cp) /* empty */;
663 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
664 memcpy (extracted_name_value,
666 cp - name_suffix_begin);
667 extracted_name_value[cp - name_suffix_begin] = '\0';
670 return extracted_name_value;
674 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
675 * directives. Fills in the information of the CTinfo structure.
678 get_ctinfo (unsigned char *cp, CT ct, int magic)
687 i = strlen (invo_name) + 2;
689 /* store copy of Content-Type line */
690 cp = ct->c_ctline = add (cp, NULL);
692 while (isspace (*cp)) /* trim leading spaces */
695 /* change newlines to spaces */
696 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
699 /* trim trailing spaces */
700 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
706 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
708 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
711 for (dp = cp; istoken (*dp); dp++)
714 ci->ci_type = add (cp, NULL); /* store content type */
718 advise (NULL, "invalid %s: field in message %s (empty type)",
719 TYPE_FIELD, ct->c_file);
723 /* down case the content type string */
724 for (dp = ci->ci_type; *dp; dp++)
725 if (isalpha(*dp) && isupper (*dp))
728 while (isspace (*cp))
731 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
736 ci->ci_subtype = add ("", NULL);
741 while (isspace (*cp))
744 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
747 for (dp = cp; istoken (*dp); dp++)
750 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
753 if (!*ci->ci_subtype) {
755 "invalid %s: field in message %s (empty subtype for \"%s\")",
756 TYPE_FIELD, ct->c_file, ci->ci_type);
760 /* down case the content subtype string */
761 for (dp = ci->ci_subtype; *dp; dp++)
762 if (isalpha(*dp) && isupper (*dp))
766 while (isspace (*cp))
769 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
773 * Parse attribute/value pairs given with Content-Type
775 ep = (ap = ci->ci_attrs) + NPARMS;
782 "too many parameters in message %s's %s: field (%d max)",
783 ct->c_file, TYPE_FIELD, NPARMS);
788 while (isspace (*cp))
791 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
796 "extraneous trailing ';' in message %s's %s: parameter list",
797 ct->c_file, TYPE_FIELD);
801 /* down case the attribute name */
802 for (dp = cp; istoken (*dp); dp++)
803 if (isalpha(*dp) && isupper (*dp))
806 for (up = dp; isspace (*dp);)
808 if (dp == cp || *dp != '=') {
810 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
811 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
815 vp = (*ap = add (cp, NULL)) + (up - cp);
817 for (dp++; isspace (*dp);)
820 /* now add the attribute value */
821 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
824 for (cp = ++dp, dp = vp;;) {
829 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
830 ct->c_file, TYPE_FIELD, i, i, "", *ap);
835 if ((c = *cp++) == '\0')
850 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
856 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
857 ct->c_file, TYPE_FIELD, i, i, "", *ap);
862 while (isspace (*cp))
865 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
870 * Get any <Content-Id> given in buffer
872 if (magic && *cp == '<') {
877 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
878 advise (NULL, "invalid ID in message %s", ct->c_file);
884 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
890 while (isspace (*cp))
895 * Get any [Content-Description] given in buffer.
897 if (magic && *cp == '[') {
899 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
903 advise (NULL, "invalid description in message %s", ct->c_file);
911 ct->c_descr = concat (ct->c_descr, "\n", NULL);
917 while (isspace (*cp))
922 * Get any {Content-Disposition} given in buffer.
924 if (magic && *cp == '{') {
926 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
930 advise (NULL, "invalid disposition in message %s", ct->c_file);
938 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
944 while (isspace (*cp))
949 * Check if anything is left over
953 ci->ci_magic = add (cp, NULL);
955 /* If there is a Content-Disposition header and it doesn't
956 have a *filename=, extract it from the magic contents.
957 The r1bindex call skips any leading directory
961 incl_name_value (ct->c_dispo,
963 r1bindex (extract_name_value ("name",
970 "extraneous information in message %s's %s: field\n%*.*s(%s)",
971 ct->c_file, TYPE_FIELD, i, i, "", cp);
979 get_comment (CT ct, unsigned char **ap, int istype)
984 char c, buffer[BUFSIZ], *dp;
996 advise (NULL, "invalid comment in message %s's %s: field",
997 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1002 if ((c = *cp++) == '\0')
1025 if ((dp = ci->ci_comment)) {
1026 ci->ci_comment = concat (dp, " ", buffer, NULL);
1029 ci->ci_comment = add (buffer, NULL);
1033 while (isspace (*cp))
1044 * Handles content types audio, image, and video.
1045 * There's not much to do right here.
1051 return OK; /* not much to do here */
1062 char buffer[BUFSIZ];
1064 char **ap, **ep, *cp;
1067 CI ci = &ct->c_ctinfo;
1069 /* check for missing subtype */
1070 if (!*ci->ci_subtype)
1071 ci->ci_subtype = add ("plain", ci->ci_subtype);
1074 for (kv = SubText; kv->kv_key; kv++)
1075 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1077 ct->c_subtype = kv->kv_value;
1079 /* allocate text character set structure */
1080 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1081 adios (NULL, "out of memory");
1082 ct->c_ctparams = (void *) t;
1084 /* scan for charset parameter */
1085 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1086 if (!mh_strcasecmp (*ap, "charset"))
1089 /* check if content specified a character set */
1091 /* match character set or set to CHARSET_UNKNOWN */
1092 for (kv = Charset; kv->kv_key; kv++) {
1093 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1098 t->tx_charset = kv->kv_value;
1100 t->tx_charset = CHARSET_UNSPECIFIED;
1104 * If we can not handle character set natively,
1105 * then check profile for string to modify the
1106 * terminal or display method.
1108 * termproc is for mhshow, though mhlist -debug prints it, too.
1110 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1111 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1112 if ((cp = context_find (buffer)))
1113 ct->c_termproc = getcpy (cp);
1125 InitMultiPart (CT ct)
1129 unsigned char *cp, *dp;
1131 char *bp, buffer[BUFSIZ];
1132 struct multipart *m;
1134 struct part *part, **next;
1135 CI ci = &ct->c_ctinfo;
1140 * The encoding for multipart messages must be either
1141 * 7bit, 8bit, or binary (per RFC2045).
1143 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1144 && ct->c_encoding != CE_BINARY) {
1146 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1147 ci->ci_type, ci->ci_subtype, ct->c_file);
1152 for (kv = SubMultiPart; kv->kv_key; kv++)
1153 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1155 ct->c_subtype = kv->kv_value;
1158 * Check for "boundary" parameter, which is
1159 * required for multipart messages.
1162 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1163 if (!mh_strcasecmp (*ap, "boundary")) {
1169 /* complain if boundary parameter is missing */
1172 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1173 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1177 /* allocate primary structure for multipart info */
1178 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1179 adios (NULL, "out of memory");
1180 ct->c_ctparams = (void *) m;
1182 /* check if boundary parameter contains only whitespace characters */
1183 for (cp = bp; isspace (*cp); cp++)
1186 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1187 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1191 /* remove trailing whitespace from boundary parameter */
1192 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1197 /* record boundary separators */
1198 m->mp_start = concat (bp, "\n", NULL);
1199 m->mp_stop = concat (bp, "--\n", NULL);
1201 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1202 advise (ct->c_file, "unable to open for reading");
1206 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1208 next = &m->mp_parts;
1212 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1216 pos += strlen (buffer);
1217 if (buffer[0] != '-' || buffer[1] != '-')
1220 if (strcmp (buffer + 2, m->mp_start))
1223 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1224 adios (NULL, "out of memory");
1226 next = &part->mp_next;
1228 if (!(p = get_content (fp, ct->c_file,
1229 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1236 fseek (fp, pos, SEEK_SET);
1239 if (strcmp (buffer + 2, m->mp_start) == 0) {
1243 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1244 if (p->c_end < p->c_begin)
1245 p->c_begin = p->c_end;
1250 if (strcmp (buffer + 2, m->mp_stop) == 0)
1256 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1257 if (!inout && part) {
1259 p->c_end = ct->c_end;
1261 if (p->c_begin >= p->c_end) {
1262 for (next = &m->mp_parts; *next != part;
1263 next = &((*next)->mp_next))
1267 free ((char *) part);
1272 /* reverse the order of the parts for multipart/alternative */
1273 if (ct->c_subtype == MULTI_ALTERNATE)
1277 * label all subparts with part number, and
1278 * then initialize the content of the subpart.
1283 char partnam[BUFSIZ];
1286 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1287 pp = partnam + strlen (partnam);
1292 for (part = m->mp_parts, partnum = 1; part;
1293 part = part->mp_next, partnum++) {
1296 sprintf (pp, "%d", partnum);
1297 p->c_partno = add (partnam, NULL);
1299 /* initialize the content of the subparts */
1300 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1315 * reverse the order of the parts of a multipart
1319 reverse_parts (CT ct)
1322 struct multipart *m;
1323 struct part **base, **bmp, **next, *part;
1325 m = (struct multipart *) ct->c_ctparams;
1327 /* if only one part, just return */
1328 if (!m->mp_parts || !m->mp_parts->mp_next)
1331 /* count number of parts */
1333 for (part = m->mp_parts; part; part = part->mp_next)
1336 /* allocate array of pointers to the parts */
1337 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1338 adios (NULL, "out of memory");
1341 /* point at all the parts */
1342 for (part = m->mp_parts; part; part = part->mp_next)
1346 /* reverse the order of the parts */
1347 next = &m->mp_parts;
1348 for (bmp--; bmp >= base; bmp--) {
1351 next = &part->mp_next;
1355 /* free array of pointers */
1356 free ((char *) base);
1368 CI ci = &ct->c_ctinfo;
1370 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1372 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1373 ci->ci_type, ci->ci_subtype, ct->c_file);
1377 /* check for missing subtype */
1378 if (!*ci->ci_subtype)
1379 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1382 for (kv = SubMessage; kv->kv_key; kv++)
1383 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1385 ct->c_subtype = kv->kv_value;
1387 switch (ct->c_subtype) {
1388 case MESSAGE_RFC822:
1391 case MESSAGE_PARTIAL:
1396 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1397 adios (NULL, "out of memory");
1398 ct->c_ctparams = (void *) p;
1400 /* scan for parameters "id", "number", and "total" */
1401 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1402 if (!mh_strcasecmp (*ap, "id")) {
1403 p->pm_partid = add (*ep, NULL);
1406 if (!mh_strcasecmp (*ap, "number")) {
1407 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1408 || p->pm_partno < 1) {
1411 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1412 *ap, ci->ci_type, ci->ci_subtype,
1413 ct->c_file, TYPE_FIELD);
1418 if (!mh_strcasecmp (*ap, "total")) {
1419 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1428 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1430 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1431 ci->ci_type, ci->ci_subtype,
1432 ct->c_file, TYPE_FIELD);
1438 case MESSAGE_EXTERNAL:
1445 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1446 adios (NULL, "out of memory");
1447 ct->c_ctparams = (void *) e;
1450 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1451 advise (ct->c_file, "unable to open for reading");
1455 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1457 if (!(p = get_content (fp, ct->c_file, 0))) {
1465 if ((exresult = params_external (ct, 0)) != NOTOK
1466 && p->c_ceopenfnx == openMail) {
1470 if ((size = ct->c_end - p->c_begin) <= 0) {
1472 content_error (NULL, ct,
1473 "empty body for access-type=mail-server");
1477 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1478 fseek (p->c_fp, p->c_begin, SEEK_SET);
1480 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1482 adios ("failed", "fread");
1485 adios (NULL, "unexpected EOF from fread");
1488 bp += cc, size -= cc;
1495 p->c_end = p->c_begin;
1500 if (exresult == NOTOK)
1502 if (e->eb_flags == NOTOK)
1505 switch (p->c_type) {
1510 if (p->c_subtype != MESSAGE_RFC822)
1514 e->eb_partno = ct->c_partno;
1516 (*p->c_ctinitfnx) (p);
1531 params_external (CT ct, int composing)
1534 struct exbody *e = (struct exbody *) ct->c_ctparams;
1535 CI ci = &ct->c_ctinfo;
1537 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1538 if (!mh_strcasecmp (*ap, "access-type")) {
1539 struct str2init *s2i;
1540 CT p = e->eb_content;
1542 for (s2i = str2methods; s2i->si_key; s2i++)
1543 if (!mh_strcasecmp (*ep, s2i->si_key))
1547 e->eb_flags = NOTOK;
1548 p->c_encoding = CE_EXTERNAL;
1551 e->eb_access = s2i->si_key;
1552 e->eb_flags = s2i->si_val;
1553 p->c_encoding = CE_EXTERNAL;
1555 /* Call the Init function for this external type */
1556 if ((*s2i->si_init)(p) == NOTOK)
1560 if (!mh_strcasecmp (*ap, "name")) {
1564 if (!mh_strcasecmp (*ap, "permission")) {
1565 e->eb_permission = *ep;
1568 if (!mh_strcasecmp (*ap, "site")) {
1572 if (!mh_strcasecmp (*ap, "directory")) {
1576 if (!mh_strcasecmp (*ap, "mode")) {
1580 if (!mh_strcasecmp (*ap, "size")) {
1581 sscanf (*ep, "%lu", &e->eb_size);
1584 if (!mh_strcasecmp (*ap, "server")) {
1588 if (!mh_strcasecmp (*ap, "subject")) {
1589 e->eb_subject = *ep;
1592 if (composing && !mh_strcasecmp (*ap, "body")) {
1593 e->eb_body = getcpy (*ep);
1598 if (!e->eb_access) {
1600 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1601 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1614 InitApplication (CT ct)
1617 CI ci = &ct->c_ctinfo;
1620 for (kv = SubApplication; kv->kv_key; kv++)
1621 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1623 ct->c_subtype = kv->kv_value;
1630 * TRANSFER ENCODINGS
1634 init_encoding (CT ct, OpenCEFunc openfnx)
1638 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1639 adios (NULL, "out of memory");
1642 ct->c_ceopenfnx = openfnx;
1643 ct->c_ceclosefnx = close_encoding;
1644 ct->c_cesizefnx = size_encoding;
1651 close_encoding (CT ct)
1655 if (!(ce = ct->c_cefile))
1665 static unsigned long
1666 size_encoding (CT ct)
1674 if (!(ce = ct->c_cefile))
1675 return (ct->c_end - ct->c_begin);
1677 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1678 return (long) st.st_size;
1681 if (stat (ce->ce_file, &st) != NOTOK)
1682 return (long) st.st_size;
1687 if (ct->c_encoding == CE_EXTERNAL)
1688 return (ct->c_end - ct->c_begin);
1691 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1692 return (ct->c_end - ct->c_begin);
1694 if (fstat (fd, &st) != NOTOK)
1695 size = (long) st.st_size;
1699 (*ct->c_ceclosefnx) (ct);
1708 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1713 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1714 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1715 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1716 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1717 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1718 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1719 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1720 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1721 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1722 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1723 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1724 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1731 return init_encoding (ct, openBase64);
1736 openBase64 (CT ct, char **file)
1738 int bitno, cc, digested;
1741 unsigned char value, *b, *b1, *b2, *b3;
1742 unsigned char *cp, *ep;
1743 char buffer[BUFSIZ];
1744 /* sbeck -- handle suffixes */
1749 b = (unsigned char *) &bits;
1750 b1 = &b[endian > 0 ? 1 : 2];
1751 b2 = &b[endian > 0 ? 2 : 1];
1752 b3 = &b[endian > 0 ? 3 : 0];
1756 fseek (ce->ce_fp, 0L, SEEK_SET);
1761 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1762 content_error (ce->ce_file, ct, "unable to fopen for reading");
1768 if (*file == NULL) {
1769 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1772 ce->ce_file = add (*file, NULL);
1776 /* sbeck@cise.ufl.edu -- handle suffixes */
1778 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1779 invo_name, ci->ci_type, ci->ci_subtype);
1780 cp = context_find (buffer);
1781 if (cp == NULL || *cp == '\0') {
1782 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1784 cp = context_find (buffer);
1786 if (cp != NULL && *cp != '\0') {
1787 if (ce->ce_unlink) {
1788 // Temporary file already exists, so we rename to
1789 // version with extension.
1790 char *file_org = strdup(ce->ce_file);
1791 ce->ce_file = add (cp, ce->ce_file);
1792 if (rename(file_org, ce->ce_file)) {
1793 adios (ce->ce_file, "unable to rename %s to ", file_org);
1798 ce->ce_file = add (cp, ce->ce_file);
1802 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1803 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1807 if ((len = ct->c_end - ct->c_begin) < 0)
1808 adios (NULL, "internal error(1)");
1810 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1811 content_error (ct->c_file, ct, "unable to open for reading");
1815 if ((digested = ct->c_digested))
1816 MD5Init (&mdContext);
1822 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1824 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1826 content_error (ct->c_file, ct, "error reading from");
1830 content_error (NULL, ct, "premature eof");
1838 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1843 if (skip || (*cp & 0x80)
1844 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1846 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1848 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1851 content_error (NULL, ct,
1852 "invalid BASE64 encoding -- continuing");
1856 bits |= value << bitno;
1858 if ((bitno -= 6) < 0) {
1859 putc ((char) *b1, ce->ce_fp);
1861 MD5Update (&mdContext, b1, 1);
1863 putc ((char) *b2, ce->ce_fp);
1865 MD5Update (&mdContext, b2, 1);
1867 putc ((char) *b3, ce->ce_fp);
1869 MD5Update (&mdContext, b3, 1);
1873 if (ferror (ce->ce_fp)) {
1874 content_error (ce->ce_file, ct,
1875 "error writing to");
1878 bitno = 18, bits = 0L, skip = 0;
1884 goto self_delimiting;
1893 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1895 content_error (NULL, ct, "invalid BASE64 encoding");
1900 fseek (ct->c_fp, 0L, SEEK_SET);
1902 if (fflush (ce->ce_fp)) {
1903 content_error (ce->ce_file, ct, "error writing to");
1908 unsigned char digest[16];
1910 MD5Final (digest, &mdContext);
1911 if (memcmp((char *) digest, (char *) ct->c_digest,
1912 sizeof(digest) / sizeof(digest[0])))
1913 content_error (NULL, ct,
1914 "content integrity suspect (digest mismatch) -- continuing");
1917 fprintf (stderr, "content integrity confirmed\n");
1920 fseek (ce->ce_fp, 0L, SEEK_SET);
1923 *file = ce->ce_file;
1924 return fileno (ce->ce_fp);
1927 free_encoding (ct, 0);
1936 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1944 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1951 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1952 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1959 return init_encoding (ct, openQuoted);
1964 openQuoted (CT ct, char **file)
1966 int cc, digested, len, quoted;
1967 unsigned char *cp, *ep;
1968 char buffer[BUFSIZ];
1971 /* sbeck -- handle suffixes */
1977 fseek (ce->ce_fp, 0L, SEEK_SET);
1982 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1983 content_error (ce->ce_file, ct, "unable to fopen for reading");
1989 if (*file == NULL) {
1990 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1993 ce->ce_file = add (*file, NULL);
1997 /* sbeck@cise.ufl.edu -- handle suffixes */
1999 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2000 invo_name, ci->ci_type, ci->ci_subtype);
2001 cp = context_find (buffer);
2002 if (cp == NULL || *cp == '\0') {
2003 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2005 cp = context_find (buffer);
2007 if (cp != NULL && *cp != '\0') {
2008 if (ce->ce_unlink) {
2009 // Temporary file already exists, so we rename to
2010 // version with extension.
2011 char *file_org = strdup(ce->ce_file);
2012 ce->ce_file = add (cp, ce->ce_file);
2013 if (rename(file_org, ce->ce_file)) {
2014 adios (ce->ce_file, "unable to rename %s to ", file_org);
2019 ce->ce_file = add (cp, ce->ce_file);
2023 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2024 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2028 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2029 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2033 if ((len = ct->c_end - ct->c_begin) < 0)
2034 adios (NULL, "internal error(2)");
2036 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2037 content_error (ct->c_file, ct, "unable to open for reading");
2041 if ((digested = ct->c_digested))
2042 MD5Init (&mdContext);
2049 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2051 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2052 content_error (NULL, ct, "premature eof");
2056 if ((cc = strlen (buffer)) > len)
2060 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2065 for (; cp < ep; cp++) {
2067 /* in an escape sequence */
2069 /* at byte 1 of an escape sequence */
2070 mask = hex2nib[*cp & 0x7f];
2071 /* next is byte 2 */
2074 /* at byte 2 of an escape sequence */
2076 mask |= hex2nib[*cp & 0x7f];
2077 putc (mask, ce->ce_fp);
2079 MD5Update (&mdContext, &mask, 1);
2080 if (ferror (ce->ce_fp)) {
2081 content_error (ce->ce_file, ct, "error writing to");
2084 /* finished escape sequence; next may be literal or a new
2085 * escape sequence */
2088 /* on to next byte */
2092 /* not in an escape sequence */
2094 /* starting an escape sequence, or invalid '='? */
2095 if (cp + 1 < ep && cp[1] == '\n') {
2096 /* "=\n" soft line break, eat the \n */
2100 if (cp + 1 >= ep || cp + 2 >= ep) {
2101 /* We don't have 2 bytes left, so this is an invalid
2102 * escape sequence; just show the raw bytes (below). */
2103 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2104 /* Next 2 bytes are hex digits, making this a valid escape
2105 * sequence; let's decode it (above). */
2109 /* One or both of the next 2 is out of range, making this
2110 * an invalid escape sequence; just show the raw bytes
2115 /* Just show the raw byte. */
2116 putc (*cp, ce->ce_fp);
2119 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2121 MD5Update (&mdContext, (unsigned char *) cp, 1);
2124 if (ferror (ce->ce_fp)) {
2125 content_error (ce->ce_file, ct, "error writing to");
2131 content_error (NULL, ct,
2132 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2136 fseek (ct->c_fp, 0L, SEEK_SET);
2138 if (fflush (ce->ce_fp)) {
2139 content_error (ce->ce_file, ct, "error writing to");
2144 unsigned char digest[16];
2146 MD5Final (digest, &mdContext);
2147 if (memcmp((char *) digest, (char *) ct->c_digest,
2148 sizeof(digest) / sizeof(digest[0])))
2149 content_error (NULL, ct,
2150 "content integrity suspect (digest mismatch) -- continuing");
2153 fprintf (stderr, "content integrity confirmed\n");
2156 fseek (ce->ce_fp, 0L, SEEK_SET);
2159 *file = ce->ce_file;
2160 return fileno (ce->ce_fp);
2163 free_encoding (ct, 0);
2175 if (init_encoding (ct, open7Bit) == NOTOK)
2178 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2184 open7Bit (CT ct, char **file)
2187 char buffer[BUFSIZ];
2188 /* sbeck -- handle suffixes */
2195 fseek (ce->ce_fp, 0L, SEEK_SET);
2200 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2201 content_error (ce->ce_file, ct, "unable to fopen for reading");
2207 if (*file == NULL) {
2208 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2211 ce->ce_file = add (*file, NULL);
2215 /* sbeck@cise.ufl.edu -- handle suffixes */
2217 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2218 invo_name, ci->ci_type, ci->ci_subtype);
2219 cp = context_find (buffer);
2220 if (cp == NULL || *cp == '\0') {
2221 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2223 cp = context_find (buffer);
2225 if (cp != NULL && *cp != '\0') {
2226 if (ce->ce_unlink) {
2227 // Temporary file already exists, so we rename to
2228 // version with extension.
2229 char *file_org = strdup(ce->ce_file);
2230 ce->ce_file = add (cp, ce->ce_file);
2231 if (rename(file_org, ce->ce_file)) {
2232 adios (ce->ce_file, "unable to rename %s to ", file_org);
2237 ce->ce_file = add (cp, ce->ce_file);
2241 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2242 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2246 if (ct->c_type == CT_MULTIPART) {
2248 CI ci = &ct->c_ctinfo;
2251 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2252 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2253 + 1 + strlen (ci->ci_subtype);
2254 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2255 putc (';', ce->ce_fp);
2258 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2260 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2261 fputs ("\n\t", ce->ce_fp);
2264 putc (' ', ce->ce_fp);
2267 fprintf (ce->ce_fp, "%s", buffer);
2271 if (ci->ci_comment) {
2272 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2273 fputs ("\n\t", ce->ce_fp);
2277 putc (' ', ce->ce_fp);
2280 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2283 fprintf (ce->ce_fp, "\n");
2285 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2287 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2289 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2290 fprintf (ce->ce_fp, "\n");
2293 if ((len = ct->c_end - ct->c_begin) < 0)
2294 adios (NULL, "internal error(3)");
2296 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2297 content_error (ct->c_file, ct, "unable to open for reading");
2301 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2303 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2305 content_error (ct->c_file, ct, "error reading from");
2309 content_error (NULL, ct, "premature eof");
2317 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2318 if (ferror (ce->ce_fp)) {
2319 content_error (ce->ce_file, ct, "error writing to");
2324 fseek (ct->c_fp, 0L, SEEK_SET);
2326 if (fflush (ce->ce_fp)) {
2327 content_error (ce->ce_file, ct, "error writing to");
2331 fseek (ce->ce_fp, 0L, SEEK_SET);
2334 *file = ce->ce_file;
2335 return fileno (ce->ce_fp);
2338 free_encoding (ct, 0);
2348 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2350 char cachefile[BUFSIZ];
2353 fseek (ce->ce_fp, 0L, SEEK_SET);
2358 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2359 content_error (ce->ce_file, ct, "unable to fopen for reading");
2365 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2366 cachefile, sizeof(cachefile)) != NOTOK) {
2367 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2368 ce->ce_file = getcpy (cachefile);
2372 admonish (cachefile, "unable to fopen for reading");
2379 *file = ce->ce_file;
2380 *fd = fileno (ce->ce_fp);
2391 return init_encoding (ct, openFile);
2396 openFile (CT ct, char **file)
2399 char cachefile[BUFSIZ];
2400 struct exbody *e = ct->c_ctexbody;
2401 CE ce = ct->c_cefile;
2403 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2415 content_error (NULL, ct, "missing name parameter");
2419 ce->ce_file = getcpy (e->eb_name);
2422 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2423 content_error (ce->ce_file, ct, "unable to fopen for reading");
2427 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2428 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2429 cachefile, sizeof(cachefile)) != NOTOK) {
2433 mask = umask (cachetype ? ~m_gmprot () : 0222);
2434 if ((fp = fopen (cachefile, "w"))) {
2436 char buffer[BUFSIZ];
2437 FILE *gp = ce->ce_fp;
2439 fseek (gp, 0L, SEEK_SET);
2441 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2443 fwrite (buffer, sizeof(*buffer), cc, fp);
2447 admonish (ce->ce_file, "error reading");
2452 admonish (cachefile, "error writing");
2460 fseek (ce->ce_fp, 0L, SEEK_SET);
2461 *file = ce->ce_file;
2462 return fileno (ce->ce_fp);
2472 return init_encoding (ct, openFTP);
2477 openFTP (CT ct, char **file)
2479 int cachetype, caching, fd;
2481 char *bp, *ftp, *user, *pass;
2482 char buffer[BUFSIZ], cachefile[BUFSIZ];
2485 static char *username = NULL;
2486 static char *password = NULL;
2491 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2499 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2510 if (!e->eb_name || !e->eb_site) {
2511 content_error (NULL, ct, "missing %s parameter",
2512 e->eb_name ? "site": "name");
2519 pidcheck (pidwait (xpid, NOTOK));
2523 /* Get the buffer ready to go */
2525 buflen = sizeof(buffer);
2528 * Construct the query message for user
2530 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2536 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2542 snprintf (bp, buflen, "\n using %sFTP from site %s",
2543 e->eb_flags ? "anonymous " : "", e->eb_site);
2548 if (e->eb_size > 0) {
2549 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2554 snprintf (bp, buflen, "? ");
2557 * Now, check the answer
2559 if (!getanswer (buffer))
2564 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2567 ruserpass (e->eb_site, &username, &password);
2572 ce->ce_unlink = (*file == NULL);
2574 cachefile[0] = '\0';
2575 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2576 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2577 cachefile, sizeof(cachefile)) != NOTOK) {
2578 if (*file == NULL) {
2585 ce->ce_file = add (*file, NULL);
2587 ce->ce_file = add (cachefile, NULL);
2589 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2591 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2592 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2600 int child_id, i, vecp;
2604 vec[vecp++] = r1bindex (ftp, '/');
2605 vec[vecp++] = e->eb_site;
2608 vec[vecp++] = e->eb_dir;
2609 vec[vecp++] = e->eb_name;
2610 vec[vecp++] = ce->ce_file,
2611 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2612 ? "ascii" : "binary";
2617 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2621 adios ("fork", "unable to");
2625 close (fileno (ce->ce_fp));
2627 fprintf (stderr, "unable to exec ");
2633 if (pidXwait (child_id, NULL)) {
2637 username = password = NULL;
2646 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2648 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2655 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2660 mask = umask (cachetype ? ~m_gmprot () : 0222);
2661 if ((fp = fopen (cachefile, "w"))) {
2663 FILE *gp = ce->ce_fp;
2665 fseek (gp, 0L, SEEK_SET);
2667 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2669 fwrite (buffer, sizeof(*buffer), cc, fp);
2673 admonish (ce->ce_file, "error reading");
2678 admonish (cachefile, "error writing");
2687 fseek (ce->ce_fp, 0L, SEEK_SET);
2688 *file = ce->ce_file;
2689 return fileno (ce->ce_fp);
2700 return init_encoding (ct, openMail);
2705 openMail (CT ct, char **file)
2707 int child_id, fd, i, vecp;
2709 char *bp, buffer[BUFSIZ], *vec[7];
2710 struct exbody *e = ct->c_ctexbody;
2711 CE ce = ct->c_cefile;
2713 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2724 if (!e->eb_server) {
2725 content_error (NULL, ct, "missing server parameter");
2732 pidcheck (pidwait (xpid, NOTOK));
2736 /* Get buffer ready to go */
2738 buflen = sizeof(buffer);
2740 /* Now, construct query message */
2741 snprintf (bp, buflen, "Retrieve content");
2747 snprintf (bp, buflen, " %s", e->eb_partno);
2753 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2755 e->eb_subject ? e->eb_subject : e->eb_body);
2757 /* Now, check answer */
2758 if (!getanswer (buffer))
2762 vec[vecp++] = r1bindex (mailproc, '/');
2763 vec[vecp++] = e->eb_server;
2764 vec[vecp++] = "-subject";
2765 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2766 vec[vecp++] = "-body";
2767 vec[vecp++] = e->eb_body;
2770 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2774 advise ("fork", "unable to");
2778 execvp (mailproc, vec);
2779 fprintf (stderr, "unable to exec ");
2785 if (pidXwait (child_id, NULL) == OK)
2786 advise (NULL, "request sent");
2790 if (*file == NULL) {
2791 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2794 ce->ce_file = add (*file, NULL);
2798 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2799 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2803 /* showproc is for mhshow and mhstore, though mhlist -debug
2804 * prints it, too. */
2806 free (ct->c_showproc);
2807 ct->c_showproc = add ("true", NULL);
2809 fseek (ce->ce_fp, 0L, SEEK_SET);
2810 *file = ce->ce_file;
2811 return fileno (ce->ce_fp);
2816 readDigest (CT ct, char *cp)
2821 unsigned char *dp, value, *ep;
2822 unsigned char *b, *b1, *b2, *b3;
2824 b = (unsigned char *) &bits,
2825 b1 = &b[endian > 0 ? 1 : 2],
2826 b2 = &b[endian > 0 ? 2 : 1],
2827 b3 = &b[endian > 0 ? 3 : 0];
2832 for (ep = (dp = ct->c_digest)
2833 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2838 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2840 fprintf (stderr, "invalid BASE64 encoding\n");
2844 bits |= value << bitno;
2846 if ((bitno -= 6) < 0) {
2847 if (dp + (3 - skip) > ep)
2848 goto invalid_digest;
2863 goto self_delimiting;
2868 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2878 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2886 fprintf (stderr, "MD5 digest=");
2887 for (dp = ct->c_digest; dp < ep; dp++)
2888 fprintf (stderr, "%02x", *dp & 0xff);
2889 fprintf (stderr, "\n");