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>
26 extern int endian; /* mhmisc.c */
28 extern pid_t xpid; /* mhshowsbr.c */
31 extern int rcachesw; /* mhcachesbr.c */
32 extern int wcachesw; /* mhcachesbr.c */
34 int checksw = 0; /* check Content-MD5 field */
37 * Directory to place temp files. This must
38 * be set before these routines are called.
43 * Structures for TEXT messages
45 struct k2v SubText[] = {
46 { "plain", TEXT_PLAIN },
47 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
48 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
49 { NULL, TEXT_UNKNOWN } /* this one must be last! */
52 struct k2v Charset[] = {
53 { "us-ascii", CHARSET_USASCII },
54 { "iso-8859-1", CHARSET_LATIN },
55 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
59 * Structures for MULTIPART messages
61 struct k2v SubMultiPart[] = {
62 { "mixed", MULTI_MIXED },
63 { "alternative", MULTI_ALTERNATE },
64 { "digest", MULTI_DIGEST },
65 { "parallel", MULTI_PARALLEL },
66 { NULL, MULTI_UNKNOWN } /* this one must be last! */
70 * Structures for MESSAGE messages
72 struct k2v SubMessage[] = {
73 { "rfc822", MESSAGE_RFC822 },
74 { "partial", MESSAGE_PARTIAL },
75 { "external-body", MESSAGE_EXTERNAL },
76 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
80 * Structure for APPLICATION messages
82 struct k2v SubApplication[] = {
83 { "octet-stream", APPLICATION_OCTETS },
84 { "postscript", APPLICATION_POSTSCRIPT },
85 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
90 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
93 int find_cache (CT, int, int *, char *, char *, int);
96 int part_ok (CT, int);
97 int type_ok (CT, int);
98 int make_intermediates (char *);
99 void content_error (char *, CT, char *, ...);
102 void free_content (CT);
103 void free_encoding (CT, int);
108 static CT get_content (FILE *, char *, int);
109 static int get_comment (CT, unsigned char **, int);
111 static int InitGeneric (CT);
112 static int InitText (CT);
113 static int InitMultiPart (CT);
114 static void reverse_parts (CT);
115 static int InitMessage (CT);
116 static int InitApplication (CT);
117 static int init_encoding (CT, OpenCEFunc);
118 static unsigned long size_encoding (CT);
119 static int InitBase64 (CT);
120 static int openBase64 (CT, char **);
121 static int InitQuoted (CT);
122 static int openQuoted (CT, char **);
123 static int Init7Bit (CT);
124 static int openExternal (CT, CT, CE, char **, int *);
125 static int InitFile (CT);
126 static int openFile (CT, char **);
127 static int InitFTP (CT);
128 static int openFTP (CT, char **);
129 static int InitMail (CT);
130 static int openMail (CT, char **);
131 static int readDigest (CT, char *);
133 struct str2init str2cts[] = {
134 { "application", CT_APPLICATION, InitApplication },
135 { "audio", CT_AUDIO, InitGeneric },
136 { "image", CT_IMAGE, InitGeneric },
137 { "message", CT_MESSAGE, InitMessage },
138 { "multipart", CT_MULTIPART, InitMultiPart },
139 { "text", CT_TEXT, InitText },
140 { "video", CT_VIDEO, InitGeneric },
141 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
142 { NULL, CT_UNKNOWN, NULL },
145 struct str2init str2ces[] = {
146 { "base64", CE_BASE64, InitBase64 },
147 { "quoted-printable", CE_QUOTED, InitQuoted },
148 { "8bit", CE_8BIT, Init7Bit },
149 { "7bit", CE_7BIT, Init7Bit },
150 { "binary", CE_BINARY, Init7Bit },
151 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
152 { NULL, CE_UNKNOWN, NULL },
156 * NOTE WELL: si_key MUST NOT have value of NOTOK
158 * si_key is 1 if access method is anonymous.
160 struct str2init str2methods[] = {
161 { "afs", 1, InitFile },
162 { "anon-ftp", 1, InitFTP },
163 { "ftp", 0, InitFTP },
164 { "local-file", 0, InitFile },
165 { "mail-server", 0, InitMail },
171 pidcheck (int status)
173 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
184 * Main entry point for parsing a MIME message or file.
185 * It returns the Content structure for the top level
186 * entity in the file.
190 parse_mime (char *file)
198 * Check if file is actually standard input
200 if ((is_stdin = !(strcmp (file, "-")))) {
201 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
203 advise("mhparse", "unable to create temporary file");
206 file = add (tfile, NULL);
209 while (fgets (buffer, sizeof(buffer), stdin))
213 if (ferror (stdin)) {
215 advise ("stdin", "error reading");
220 advise (file, "error writing");
223 fseek (fp, 0L, SEEK_SET);
224 } else if ((fp = fopen (file, "r")) == NULL) {
225 advise (file, "unable to read");
229 if (!(ct = get_content (fp, file, 1))) {
232 advise (NULL, "unable to decode %s", file);
237 ct->c_unlink = 1; /* temp file to remove */
241 if (ct->c_end == 0L) {
242 fseek (fp, 0L, SEEK_END);
243 ct->c_end = ftell (fp);
246 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
258 * Main routine for reading/parsing the headers
259 * of a message content.
261 * toplevel = 1 # we are at the top level of the message
262 * toplevel = 0 # we are inside message type or multipart type
263 * # other than multipart/digest
264 * toplevel = -1 # we are inside multipart/digest
265 * NB: on failure we will fclose(in)!
269 get_content (FILE *in, char *file, int toplevel)
272 char buf[BUFSIZ], name[NAMESZ];
277 /* allocate the content structure */
278 if (!(ct = (CT) calloc (1, sizeof(*ct))))
279 adios (NULL, "out of memory");
282 ct->c_file = add (file, NULL);
283 ct->c_begin = ftell (ct->c_fp) + 1;
286 * Parse the header fields for this
287 * content into a linked list.
289 for (compnum = 1, state = FLD;;) {
290 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
296 /* get copies of the buffers */
297 np = add (name, NULL);
298 vp = add (buf, NULL);
300 /* if necessary, get rest of field */
301 while (state == FLDPLUS) {
302 state = m_getfld (state, name, buf, sizeof(buf), in);
303 vp = add (buf, vp); /* add to previous value */
306 /* Now add the header data to the list */
307 add_header (ct, np, vp);
309 /* continue, if this isn't the last header field */
310 if (state != FLDEOF) {
311 ct->c_begin = ftell (in) + 1;
318 ct->c_begin = ftell (in) - strlen (buf);
322 ct->c_begin = ftell (in);
327 adios (NULL, "message format error in component #%d", compnum);
330 adios (NULL, "getfld() returned %d", state);
333 /* break out of the loop */
338 * Read the content headers. We will parse the
339 * MIME related header fields into their various
340 * structures and set internal flags related to
341 * content type/subtype, etc.
344 hp = ct->c_first_hf; /* start at first header field */
346 /* Get MIME-Version field */
347 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
350 unsigned char *cp, *dp;
353 advise (NULL, "message %s has multiple %s: fields",
354 ct->c_file, VRSN_FIELD);
357 ct->c_vrsn = add (hp->value, NULL);
359 /* Now, cleanup this field */
362 while (isspace (*cp))
364 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
366 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
371 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
373 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
376 for (dp = cp; istoken (*dp); dp++)
380 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
383 admonish (NULL, "message %s has unknown value for %s: field (%s)",
384 ct->c_file, VRSN_FIELD, cp);
387 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
388 /* Get Content-Type field */
389 struct str2init *s2i;
390 CI ci = &ct->c_ctinfo;
392 /* Check if we've already seen a Content-Type header */
394 advise (NULL, "message %s has multiple %s: fields",
395 ct->c_file, TYPE_FIELD);
399 /* Parse the Content-Type field */
400 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
404 * Set the Init function and the internal
405 * flag for this content type.
407 for (s2i = str2cts; s2i->si_key; s2i++)
408 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
410 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
412 ct->c_type = s2i->si_val;
413 ct->c_ctinitfnx = s2i->si_init;
415 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
416 /* Get Content-Transfer-Encoding field */
418 unsigned char *cp, *dp;
419 struct str2init *s2i;
422 * Check if we've already seen the
423 * Content-Transfer-Encoding field
426 advise (NULL, "message %s has multiple %s: fields",
427 ct->c_file, ENCODING_FIELD);
431 /* get copy of this field */
432 ct->c_celine = cp = add (hp->value, NULL);
434 while (isspace (*cp))
436 for (dp = cp; istoken (*dp); dp++)
442 * Find the internal flag and Init function
443 * for this transfer encoding.
445 for (s2i = str2ces; s2i->si_key; s2i++)
446 if (!mh_strcasecmp (cp, s2i->si_key))
448 if (!s2i->si_key && !uprf (cp, "X-"))
451 ct->c_encoding = s2i->si_val;
453 /* Call the Init function for this encoding */
454 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
457 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
458 /* Get Content-MD5 field */
459 unsigned char *cp, *dp;
465 if (ct->c_digested) {
466 advise (NULL, "message %s has multiple %s: fields",
467 ct->c_file, MD5_FIELD);
471 ep = cp = add (hp->value, NULL); /* get a copy */
473 while (isspace (*cp))
475 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
477 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
482 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
484 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
489 for (dp = cp; *dp && !isspace (*dp); dp++)
497 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
498 /* Get Content-ID field */
499 ct->c_id = add (hp->value, ct->c_id);
501 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
502 /* Get Content-Description field */
503 ct->c_descr = add (hp->value, ct->c_descr);
505 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
506 /* Get Content-Disposition field */
507 ct->c_dispo = add (hp->value, ct->c_dispo);
511 hp = hp->next; /* next header field */
515 * Check if we saw a Content-Type field.
516 * If not, then assign a default value for
517 * it, and the Init function.
521 * If we are inside a multipart/digest message,
522 * so default type is message/rfc822
525 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
527 ct->c_type = CT_MESSAGE;
528 ct->c_ctinitfnx = InitMessage;
531 * Else default type is text/plain
533 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
535 ct->c_type = CT_TEXT;
536 ct->c_ctinitfnx = InitText;
540 /* Use default Transfer-Encoding, if necessary */
542 ct->c_encoding = CE_7BIT;
555 * small routine to add header field to list
559 add_header (CT ct, char *name, char *value)
563 /* allocate header field structure */
564 hp = mh_xmalloc (sizeof(*hp));
566 /* link data into header structure */
571 /* link header structure into the list */
572 if (ct->c_first_hf == NULL) {
573 ct->c_first_hf = hp; /* this is the first */
576 ct->c_last_hf->next = hp; /* add it to the end */
584 /* Make sure that buf contains at least one appearance of name,
585 followed by =. If not, insert both name and value, just after
586 first semicolon, if any. Note that name should not contain a
587 trailing =. And quotes will be added around the value. Typical
588 usage: make sure that a Content-Disposition header contains
589 filename="foo". If it doesn't and value does, use value from
592 incl_name_value (unsigned char *buf, char *name, char *value) {
595 /* Assume that name is non-null. */
597 char *name_plus_equal = concat (name, "=", NULL);
599 if (! strstr (buf, name_plus_equal)) {
602 char *prefix, *suffix;
604 /* Trim trailing space, esp. newline. */
605 for (cp = &buf[strlen (buf) - 1];
606 cp >= buf && isspace (*cp);
611 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
613 /* Insert at first semicolon, if any. If none, append to
615 prefix = add (buf, NULL);
616 if ((cp = strchr (prefix, ';'))) {
617 suffix = concat (cp, NULL);
619 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
623 newbuf = concat (buf, insertion, "\n", NULL);
631 free (name_plus_equal);
637 /* Extract just name_suffix="foo", if any, from value. If there isn't
638 one, return the entire value. Note that, for example, a name_suffix
639 of name will match filename="foo", and return foo. */
641 extract_name_value (char *name_suffix, char *value) {
642 char *extracted_name_value = value;
643 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
644 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
647 free (name_suffix_plus_quote);
648 if (name_suffix_equals) {
649 char *name_suffix_begin;
652 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
653 name_suffix_begin = ++cp;
654 /* Find second \". */
655 for (; *cp != '"'; ++cp) /* empty */;
657 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
658 memcpy (extracted_name_value,
660 cp - name_suffix_begin);
661 extracted_name_value[cp - name_suffix_begin] = '\0';
664 return extracted_name_value;
668 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
669 * directives. Fills in the information of the CTinfo structure.
672 get_ctinfo (unsigned char *cp, CT ct, int magic)
681 i = strlen (invo_name) + 2;
683 /* store copy of Content-Type line */
684 cp = ct->c_ctline = add (cp, NULL);
686 while (isspace (*cp)) /* trim leading spaces */
689 /* change newlines to spaces */
690 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
693 /* trim trailing spaces */
694 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
700 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
702 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
705 for (dp = cp; istoken (*dp); dp++)
708 ci->ci_type = add (cp, NULL); /* store content type */
712 advise (NULL, "invalid %s: field in message %s (empty type)",
713 TYPE_FIELD, ct->c_file);
717 /* down case the content type string */
718 for (dp = ci->ci_type; *dp; dp++)
719 if (isalpha(*dp) && isupper (*dp))
722 while (isspace (*cp))
725 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
730 ci->ci_subtype = add ("", NULL);
735 while (isspace (*cp))
738 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
741 for (dp = cp; istoken (*dp); dp++)
744 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
747 if (!*ci->ci_subtype) {
749 "invalid %s: field in message %s (empty subtype for \"%s\")",
750 TYPE_FIELD, ct->c_file, ci->ci_type);
754 /* down case the content subtype string */
755 for (dp = ci->ci_subtype; *dp; dp++)
756 if (isalpha(*dp) && isupper (*dp))
760 while (isspace (*cp))
763 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
767 * Parse attribute/value pairs given with Content-Type
769 ep = (ap = ci->ci_attrs) + NPARMS;
776 "too many parameters in message %s's %s: field (%d max)",
777 ct->c_file, TYPE_FIELD, NPARMS);
782 while (isspace (*cp))
785 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
790 "extraneous trailing ';' in message %s's %s: parameter list",
791 ct->c_file, TYPE_FIELD);
795 /* down case the attribute name */
796 for (dp = cp; istoken (*dp); dp++)
797 if (isalpha(*dp) && isupper (*dp))
800 for (up = dp; isspace (*dp);)
802 if (dp == cp || *dp != '=') {
804 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
805 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
809 vp = (*ap = add (cp, NULL)) + (up - cp);
811 for (dp++; isspace (*dp);)
814 /* now add the attribute value */
815 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
818 for (cp = ++dp, dp = vp;;) {
823 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
824 ct->c_file, TYPE_FIELD, i, i, "", *ap);
829 if ((c = *cp++) == '\0')
844 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
850 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
851 ct->c_file, TYPE_FIELD, i, i, "", *ap);
856 while (isspace (*cp))
859 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
864 * Get any <Content-Id> given in buffer
866 if (magic && *cp == '<') {
871 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
872 advise (NULL, "invalid ID in message %s", ct->c_file);
878 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
884 while (isspace (*cp))
889 * Get any [Content-Description] given in buffer.
891 if (magic && *cp == '[') {
893 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
897 advise (NULL, "invalid description in message %s", ct->c_file);
905 ct->c_descr = concat (ct->c_descr, "\n", NULL);
911 while (isspace (*cp))
916 * Get any {Content-Disposition} given in buffer.
918 if (magic && *cp == '{') {
920 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
924 advise (NULL, "invalid disposition in message %s", ct->c_file);
932 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
938 while (isspace (*cp))
943 * Check if anything is left over
947 ci->ci_magic = add (cp, NULL);
949 /* If there is a Content-Disposition header and it doesn't
950 have a *filename=, extract it from the magic contents.
951 The r1bindex call skips any leading directory
955 incl_name_value (ct->c_dispo,
957 r1bindex (extract_name_value ("name",
964 "extraneous information in message %s's %s: field\n%*.*s(%s)",
965 ct->c_file, TYPE_FIELD, i, i, "", cp);
973 get_comment (CT ct, unsigned char **ap, int istype)
978 char c, buffer[BUFSIZ], *dp;
990 advise (NULL, "invalid comment in message %s's %s: field",
991 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
996 if ((c = *cp++) == '\0')
1019 if ((dp = ci->ci_comment)) {
1020 ci->ci_comment = concat (dp, " ", buffer, NULL);
1023 ci->ci_comment = add (buffer, NULL);
1027 while (isspace (*cp))
1038 * Handles content types audio, image, and video.
1039 * There's not much to do right here.
1045 return OK; /* not much to do here */
1056 char buffer[BUFSIZ];
1058 char **ap, **ep, *cp;
1061 CI ci = &ct->c_ctinfo;
1063 /* check for missing subtype */
1064 if (!*ci->ci_subtype)
1065 ci->ci_subtype = add ("plain", ci->ci_subtype);
1068 for (kv = SubText; kv->kv_key; kv++)
1069 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1071 ct->c_subtype = kv->kv_value;
1073 /* allocate text character set structure */
1074 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1075 adios (NULL, "out of memory");
1076 ct->c_ctparams = (void *) t;
1078 /* scan for charset parameter */
1079 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1080 if (!mh_strcasecmp (*ap, "charset"))
1083 /* check if content specified a character set */
1085 /* match character set or set to CHARSET_UNKNOWN */
1086 for (kv = Charset; kv->kv_key; kv++) {
1087 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1092 t->tx_charset = kv->kv_value;
1094 t->tx_charset = CHARSET_UNSPECIFIED;
1098 * If we can not handle character set natively,
1099 * then check profile for string to modify the
1100 * terminal or display method.
1102 * termproc is for mhshow, though mhlist -debug prints it, too.
1104 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1105 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1106 if ((cp = context_find (buffer)))
1107 ct->c_termproc = getcpy (cp);
1119 InitMultiPart (CT ct)
1123 unsigned char *cp, *dp;
1125 char *bp, buffer[BUFSIZ];
1126 struct multipart *m;
1128 struct part *part, **next;
1129 CI ci = &ct->c_ctinfo;
1134 * The encoding for multipart messages must be either
1135 * 7bit, 8bit, or binary (per RFC2045).
1137 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1138 && ct->c_encoding != CE_BINARY) {
1140 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1141 ci->ci_type, ci->ci_subtype, ct->c_file);
1146 for (kv = SubMultiPart; kv->kv_key; kv++)
1147 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1149 ct->c_subtype = kv->kv_value;
1152 * Check for "boundary" parameter, which is
1153 * required for multipart messages.
1156 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1157 if (!mh_strcasecmp (*ap, "boundary")) {
1163 /* complain if boundary parameter is missing */
1166 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1167 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1171 /* allocate primary structure for multipart info */
1172 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1173 adios (NULL, "out of memory");
1174 ct->c_ctparams = (void *) m;
1176 /* check if boundary parameter contains only whitespace characters */
1177 for (cp = bp; isspace (*cp); cp++)
1180 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1181 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1185 /* remove trailing whitespace from boundary parameter */
1186 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1191 /* record boundary separators */
1192 m->mp_start = concat (bp, "\n", NULL);
1193 m->mp_stop = concat (bp, "--\n", NULL);
1195 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1196 advise (ct->c_file, "unable to open for reading");
1200 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1202 next = &m->mp_parts;
1206 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1210 pos += strlen (buffer);
1211 if (buffer[0] != '-' || buffer[1] != '-')
1214 if (strcmp (buffer + 2, m->mp_start))
1217 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1218 adios (NULL, "out of memory");
1220 next = &part->mp_next;
1222 if (!(p = get_content (fp, ct->c_file,
1223 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1230 fseek (fp, pos, SEEK_SET);
1233 if (strcmp (buffer + 2, m->mp_start) == 0) {
1237 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1238 if (p->c_end < p->c_begin)
1239 p->c_begin = p->c_end;
1244 if (strcmp (buffer + 2, m->mp_stop) == 0)
1250 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1251 if (!inout && part) {
1253 p->c_end = ct->c_end;
1255 if (p->c_begin >= p->c_end) {
1256 for (next = &m->mp_parts; *next != part;
1257 next = &((*next)->mp_next))
1261 free ((char *) part);
1266 /* reverse the order of the parts for multipart/alternative */
1267 if (ct->c_subtype == MULTI_ALTERNATE)
1271 * label all subparts with part number, and
1272 * then initialize the content of the subpart.
1277 char partnam[BUFSIZ];
1280 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1281 pp = partnam + strlen (partnam);
1286 for (part = m->mp_parts, partnum = 1; part;
1287 part = part->mp_next, partnum++) {
1290 sprintf (pp, "%d", partnum);
1291 p->c_partno = add (partnam, NULL);
1293 /* initialize the content of the subparts */
1294 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1309 * reverse the order of the parts of a multipart
1313 reverse_parts (CT ct)
1316 struct multipart *m;
1317 struct part **base, **bmp, **next, *part;
1319 m = (struct multipart *) ct->c_ctparams;
1321 /* if only one part, just return */
1322 if (!m->mp_parts || !m->mp_parts->mp_next)
1325 /* count number of parts */
1327 for (part = m->mp_parts; part; part = part->mp_next)
1330 /* allocate array of pointers to the parts */
1331 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1332 adios (NULL, "out of memory");
1335 /* point at all the parts */
1336 for (part = m->mp_parts; part; part = part->mp_next)
1340 /* reverse the order of the parts */
1341 next = &m->mp_parts;
1342 for (bmp--; bmp >= base; bmp--) {
1345 next = &part->mp_next;
1349 /* free array of pointers */
1350 free ((char *) base);
1362 CI ci = &ct->c_ctinfo;
1364 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1366 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1367 ci->ci_type, ci->ci_subtype, ct->c_file);
1371 /* check for missing subtype */
1372 if (!*ci->ci_subtype)
1373 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1376 for (kv = SubMessage; kv->kv_key; kv++)
1377 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1379 ct->c_subtype = kv->kv_value;
1381 switch (ct->c_subtype) {
1382 case MESSAGE_RFC822:
1385 case MESSAGE_PARTIAL:
1390 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1391 adios (NULL, "out of memory");
1392 ct->c_ctparams = (void *) p;
1394 /* scan for parameters "id", "number", and "total" */
1395 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1396 if (!mh_strcasecmp (*ap, "id")) {
1397 p->pm_partid = add (*ep, NULL);
1400 if (!mh_strcasecmp (*ap, "number")) {
1401 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1402 || p->pm_partno < 1) {
1405 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1406 *ap, ci->ci_type, ci->ci_subtype,
1407 ct->c_file, TYPE_FIELD);
1412 if (!mh_strcasecmp (*ap, "total")) {
1413 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1422 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1424 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1425 ci->ci_type, ci->ci_subtype,
1426 ct->c_file, TYPE_FIELD);
1432 case MESSAGE_EXTERNAL:
1439 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1440 adios (NULL, "out of memory");
1441 ct->c_ctparams = (void *) e;
1444 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1445 advise (ct->c_file, "unable to open for reading");
1449 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1451 if (!(p = get_content (fp, ct->c_file, 0))) {
1459 if ((exresult = params_external (ct, 0)) != NOTOK
1460 && p->c_ceopenfnx == openMail) {
1464 if ((size = ct->c_end - p->c_begin) <= 0) {
1466 content_error (NULL, ct,
1467 "empty body for access-type=mail-server");
1471 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1472 fseek (p->c_fp, p->c_begin, SEEK_SET);
1474 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1476 adios ("failed", "fread");
1479 adios (NULL, "unexpected EOF from fread");
1482 bp += cc, size -= cc;
1489 p->c_end = p->c_begin;
1494 if (exresult == NOTOK)
1496 if (e->eb_flags == NOTOK)
1499 switch (p->c_type) {
1504 if (p->c_subtype != MESSAGE_RFC822)
1508 e->eb_partno = ct->c_partno;
1510 (*p->c_ctinitfnx) (p);
1525 params_external (CT ct, int composing)
1528 struct exbody *e = (struct exbody *) ct->c_ctparams;
1529 CI ci = &ct->c_ctinfo;
1531 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1532 if (!mh_strcasecmp (*ap, "access-type")) {
1533 struct str2init *s2i;
1534 CT p = e->eb_content;
1536 for (s2i = str2methods; s2i->si_key; s2i++)
1537 if (!mh_strcasecmp (*ep, s2i->si_key))
1541 e->eb_flags = NOTOK;
1542 p->c_encoding = CE_EXTERNAL;
1545 e->eb_access = s2i->si_key;
1546 e->eb_flags = s2i->si_val;
1547 p->c_encoding = CE_EXTERNAL;
1549 /* Call the Init function for this external type */
1550 if ((*s2i->si_init)(p) == NOTOK)
1554 if (!mh_strcasecmp (*ap, "name")) {
1558 if (!mh_strcasecmp (*ap, "permission")) {
1559 e->eb_permission = *ep;
1562 if (!mh_strcasecmp (*ap, "site")) {
1566 if (!mh_strcasecmp (*ap, "directory")) {
1570 if (!mh_strcasecmp (*ap, "mode")) {
1574 if (!mh_strcasecmp (*ap, "size")) {
1575 sscanf (*ep, "%lu", &e->eb_size);
1578 if (!mh_strcasecmp (*ap, "server")) {
1582 if (!mh_strcasecmp (*ap, "subject")) {
1583 e->eb_subject = *ep;
1586 if (composing && !mh_strcasecmp (*ap, "body")) {
1587 e->eb_body = getcpy (*ep);
1592 if (!e->eb_access) {
1594 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1595 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1608 InitApplication (CT ct)
1611 CI ci = &ct->c_ctinfo;
1614 for (kv = SubApplication; kv->kv_key; kv++)
1615 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1617 ct->c_subtype = kv->kv_value;
1624 * TRANSFER ENCODINGS
1628 init_encoding (CT ct, OpenCEFunc openfnx)
1632 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1633 adios (NULL, "out of memory");
1636 ct->c_ceopenfnx = openfnx;
1637 ct->c_ceclosefnx = close_encoding;
1638 ct->c_cesizefnx = size_encoding;
1645 close_encoding (CT ct)
1649 if (!(ce = ct->c_cefile))
1659 static unsigned long
1660 size_encoding (CT ct)
1668 if (!(ce = ct->c_cefile))
1669 return (ct->c_end - ct->c_begin);
1671 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1672 return (long) st.st_size;
1675 if (stat (ce->ce_file, &st) != NOTOK)
1676 return (long) st.st_size;
1681 if (ct->c_encoding == CE_EXTERNAL)
1682 return (ct->c_end - ct->c_begin);
1685 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1686 return (ct->c_end - ct->c_begin);
1688 if (fstat (fd, &st) != NOTOK)
1689 size = (long) st.st_size;
1693 (*ct->c_ceclosefnx) (ct);
1702 static unsigned char b642nib[0x80] = {
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1707 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1709 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1710 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1712 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1713 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1714 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1715 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1716 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1717 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1718 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1725 return init_encoding (ct, openBase64);
1730 openBase64 (CT ct, char **file)
1732 int bitno, cc, digested;
1735 unsigned char value, *b, *b1, *b2, *b3;
1736 unsigned char *cp, *ep;
1737 char buffer[BUFSIZ];
1738 /* sbeck -- handle suffixes */
1743 b = (unsigned char *) &bits;
1744 b1 = &b[endian > 0 ? 1 : 2];
1745 b2 = &b[endian > 0 ? 2 : 1];
1746 b3 = &b[endian > 0 ? 3 : 0];
1750 fseek (ce->ce_fp, 0L, SEEK_SET);
1755 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1756 content_error (ce->ce_file, ct, "unable to fopen for reading");
1762 if (*file == NULL) {
1763 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1766 ce->ce_file = add (*file, NULL);
1770 /* sbeck@cise.ufl.edu -- handle suffixes */
1772 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1773 invo_name, ci->ci_type, ci->ci_subtype);
1774 cp = context_find (buffer);
1775 if (cp == NULL || *cp == '\0') {
1776 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1778 cp = context_find (buffer);
1780 if (cp != NULL && *cp != '\0') {
1781 if (ce->ce_unlink) {
1782 // Temporary file already exists, so we rename to
1783 // version with extension.
1784 char *file_org = strdup(ce->ce_file);
1785 ce->ce_file = add (cp, ce->ce_file);
1786 if (rename(file_org, ce->ce_file)) {
1787 adios (ce->ce_file, "unable to rename %s to ", file_org);
1792 ce->ce_file = add (cp, ce->ce_file);
1796 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1797 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1801 if ((len = ct->c_end - ct->c_begin) < 0)
1802 adios (NULL, "internal error(1)");
1804 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1805 content_error (ct->c_file, ct, "unable to open for reading");
1809 if ((digested = ct->c_digested))
1810 MD5Init (&mdContext);
1816 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1818 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1820 content_error (ct->c_file, ct, "error reading from");
1824 content_error (NULL, ct, "premature eof");
1832 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1837 if (skip || (*cp & 0x80)
1838 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1840 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1842 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1845 content_error (NULL, ct,
1846 "invalid BASE64 encoding -- continuing");
1850 bits |= value << bitno;
1852 if ((bitno -= 6) < 0) {
1853 putc ((char) *b1, ce->ce_fp);
1855 MD5Update (&mdContext, b1, 1);
1857 putc ((char) *b2, ce->ce_fp);
1859 MD5Update (&mdContext, b2, 1);
1861 putc ((char) *b3, ce->ce_fp);
1863 MD5Update (&mdContext, b3, 1);
1867 if (ferror (ce->ce_fp)) {
1868 content_error (ce->ce_file, ct,
1869 "error writing to");
1872 bitno = 18, bits = 0L, skip = 0;
1878 goto self_delimiting;
1887 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1889 content_error (NULL, ct, "invalid BASE64 encoding");
1894 fseek (ct->c_fp, 0L, SEEK_SET);
1896 if (fflush (ce->ce_fp)) {
1897 content_error (ce->ce_file, ct, "error writing to");
1902 unsigned char digest[16];
1904 MD5Final (digest, &mdContext);
1905 if (memcmp((char *) digest, (char *) ct->c_digest,
1906 sizeof(digest) / sizeof(digest[0])))
1907 content_error (NULL, ct,
1908 "content integrity suspect (digest mismatch) -- continuing");
1911 fprintf (stderr, "content integrity confirmed\n");
1914 fseek (ce->ce_fp, 0L, SEEK_SET);
1917 *file = ce->ce_file;
1918 return fileno (ce->ce_fp);
1921 free_encoding (ct, 0);
1930 static char hex2nib[0x80] = {
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1938 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 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
1953 return init_encoding (ct, openQuoted);
1958 openQuoted (CT ct, char **file)
1960 int cc, digested, len, quoted;
1961 unsigned char *cp, *ep;
1962 char buffer[BUFSIZ];
1965 /* sbeck -- handle suffixes */
1971 fseek (ce->ce_fp, 0L, SEEK_SET);
1976 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1977 content_error (ce->ce_file, ct, "unable to fopen for reading");
1983 if (*file == NULL) {
1984 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1987 ce->ce_file = add (*file, NULL);
1991 /* sbeck@cise.ufl.edu -- handle suffixes */
1993 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1994 invo_name, ci->ci_type, ci->ci_subtype);
1995 cp = context_find (buffer);
1996 if (cp == NULL || *cp == '\0') {
1997 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1999 cp = context_find (buffer);
2001 if (cp != NULL && *cp != '\0') {
2002 if (ce->ce_unlink) {
2003 // Temporary file already exists, so we rename to
2004 // version with extension.
2005 char *file_org = strdup(ce->ce_file);
2006 ce->ce_file = add (cp, ce->ce_file);
2007 if (rename(file_org, ce->ce_file)) {
2008 adios (ce->ce_file, "unable to rename %s to ", file_org);
2013 ce->ce_file = add (cp, ce->ce_file);
2017 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2018 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2022 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2023 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2027 if ((len = ct->c_end - ct->c_begin) < 0)
2028 adios (NULL, "internal error(2)");
2030 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2031 content_error (ct->c_file, ct, "unable to open for reading");
2035 if ((digested = ct->c_digested))
2036 MD5Init (&mdContext);
2043 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2045 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2046 content_error (NULL, ct, "premature eof");
2050 if ((cc = strlen (buffer)) > len)
2054 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2059 for (; cp < ep; cp++) {
2061 /* in an escape sequence */
2063 /* at byte 1 of an escape sequence */
2064 mask = hex2nib[*cp & 0x7f];
2065 /* next is byte 2 */
2068 /* at byte 2 of an escape sequence */
2070 mask |= hex2nib[*cp & 0x7f];
2071 putc (mask, ce->ce_fp);
2073 MD5Update (&mdContext, &mask, 1);
2074 if (ferror (ce->ce_fp)) {
2075 content_error (ce->ce_file, ct, "error writing to");
2078 /* finished escape sequence; next may be literal or a new
2079 * escape sequence */
2082 /* on to next byte */
2086 /* not in an escape sequence */
2088 /* starting an escape sequence, or invalid '='? */
2089 if (cp + 1 < ep && cp[1] == '\n') {
2090 /* "=\n" soft line break, eat the \n */
2094 if (cp + 1 >= ep || cp + 2 >= ep) {
2095 /* We don't have 2 bytes left, so this is an invalid
2096 * escape sequence; just show the raw bytes (below). */
2097 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2098 /* Next 2 bytes are hex digits, making this a valid escape
2099 * sequence; let's decode it (above). */
2103 /* One or both of the next 2 is out of range, making this
2104 * an invalid escape sequence; just show the raw bytes
2109 /* Just show the raw byte. */
2110 putc (*cp, ce->ce_fp);
2113 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2115 MD5Update (&mdContext, (unsigned char *) cp, 1);
2118 if (ferror (ce->ce_fp)) {
2119 content_error (ce->ce_file, ct, "error writing to");
2125 content_error (NULL, ct,
2126 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2130 fseek (ct->c_fp, 0L, SEEK_SET);
2132 if (fflush (ce->ce_fp)) {
2133 content_error (ce->ce_file, ct, "error writing to");
2138 unsigned char digest[16];
2140 MD5Final (digest, &mdContext);
2141 if (memcmp((char *) digest, (char *) ct->c_digest,
2142 sizeof(digest) / sizeof(digest[0])))
2143 content_error (NULL, ct,
2144 "content integrity suspect (digest mismatch) -- continuing");
2147 fprintf (stderr, "content integrity confirmed\n");
2150 fseek (ce->ce_fp, 0L, SEEK_SET);
2153 *file = ce->ce_file;
2154 return fileno (ce->ce_fp);
2157 free_encoding (ct, 0);
2169 if (init_encoding (ct, open7Bit) == NOTOK)
2172 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2178 open7Bit (CT ct, char **file)
2181 char buffer[BUFSIZ];
2182 /* sbeck -- handle suffixes */
2189 fseek (ce->ce_fp, 0L, SEEK_SET);
2194 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2195 content_error (ce->ce_file, ct, "unable to fopen for reading");
2201 if (*file == NULL) {
2202 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2205 ce->ce_file = add (*file, NULL);
2209 /* sbeck@cise.ufl.edu -- handle suffixes */
2211 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2212 invo_name, ci->ci_type, ci->ci_subtype);
2213 cp = context_find (buffer);
2214 if (cp == NULL || *cp == '\0') {
2215 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2217 cp = context_find (buffer);
2219 if (cp != NULL && *cp != '\0') {
2220 if (ce->ce_unlink) {
2221 // Temporary file already exists, so we rename to
2222 // version with extension.
2223 char *file_org = strdup(ce->ce_file);
2224 ce->ce_file = add (cp, ce->ce_file);
2225 if (rename(file_org, ce->ce_file)) {
2226 adios (ce->ce_file, "unable to rename %s to ", file_org);
2231 ce->ce_file = add (cp, ce->ce_file);
2235 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2236 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2240 if (ct->c_type == CT_MULTIPART) {
2242 CI ci = &ct->c_ctinfo;
2245 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2246 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2247 + 1 + strlen (ci->ci_subtype);
2248 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2249 putc (';', ce->ce_fp);
2252 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2254 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2255 fputs ("\n\t", ce->ce_fp);
2258 putc (' ', ce->ce_fp);
2261 fprintf (ce->ce_fp, "%s", buffer);
2265 if (ci->ci_comment) {
2266 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2267 fputs ("\n\t", ce->ce_fp);
2271 putc (' ', ce->ce_fp);
2274 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2277 fprintf (ce->ce_fp, "\n");
2279 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2281 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2283 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2284 fprintf (ce->ce_fp, "\n");
2287 if ((len = ct->c_end - ct->c_begin) < 0)
2288 adios (NULL, "internal error(3)");
2290 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2291 content_error (ct->c_file, ct, "unable to open for reading");
2295 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2297 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2299 content_error (ct->c_file, ct, "error reading from");
2303 content_error (NULL, ct, "premature eof");
2311 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2312 if (ferror (ce->ce_fp)) {
2313 content_error (ce->ce_file, ct, "error writing to");
2318 fseek (ct->c_fp, 0L, SEEK_SET);
2320 if (fflush (ce->ce_fp)) {
2321 content_error (ce->ce_file, ct, "error writing to");
2325 fseek (ce->ce_fp, 0L, SEEK_SET);
2328 *file = ce->ce_file;
2329 return fileno (ce->ce_fp);
2332 free_encoding (ct, 0);
2342 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2344 char cachefile[BUFSIZ];
2347 fseek (ce->ce_fp, 0L, SEEK_SET);
2352 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2353 content_error (ce->ce_file, ct, "unable to fopen for reading");
2359 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2360 cachefile, sizeof(cachefile)) != NOTOK) {
2361 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2362 ce->ce_file = getcpy (cachefile);
2366 admonish (cachefile, "unable to fopen for reading");
2373 *file = ce->ce_file;
2374 *fd = fileno (ce->ce_fp);
2385 return init_encoding (ct, openFile);
2390 openFile (CT ct, char **file)
2393 char cachefile[BUFSIZ];
2394 struct exbody *e = ct->c_ctexbody;
2395 CE ce = ct->c_cefile;
2397 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2409 content_error (NULL, ct, "missing name parameter");
2413 ce->ce_file = getcpy (e->eb_name);
2416 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2417 content_error (ce->ce_file, ct, "unable to fopen for reading");
2421 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2422 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2423 cachefile, sizeof(cachefile)) != NOTOK) {
2427 mask = umask (cachetype ? ~m_gmprot () : 0222);
2428 if ((fp = fopen (cachefile, "w"))) {
2430 char buffer[BUFSIZ];
2431 FILE *gp = ce->ce_fp;
2433 fseek (gp, 0L, SEEK_SET);
2435 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2437 fwrite (buffer, sizeof(*buffer), cc, fp);
2441 admonish (ce->ce_file, "error reading");
2446 admonish (cachefile, "error writing");
2454 fseek (ce->ce_fp, 0L, SEEK_SET);
2455 *file = ce->ce_file;
2456 return fileno (ce->ce_fp);
2466 return init_encoding (ct, openFTP);
2471 openFTP (CT ct, char **file)
2473 int cachetype, caching, fd;
2475 char *bp, *ftp, *user, *pass;
2476 char buffer[BUFSIZ], cachefile[BUFSIZ];
2479 static char *username = NULL;
2480 static char *password = NULL;
2485 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2491 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2502 if (!e->eb_name || !e->eb_site) {
2503 content_error (NULL, ct, "missing %s parameter",
2504 e->eb_name ? "site": "name");
2511 pidcheck (pidwait (xpid, NOTOK));
2515 /* Get the buffer ready to go */
2517 buflen = sizeof(buffer);
2520 * Construct the query message for user
2522 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2528 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2534 snprintf (bp, buflen, "\n using %sFTP from site %s",
2535 e->eb_flags ? "anonymous " : "", e->eb_site);
2540 if (e->eb_size > 0) {
2541 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2546 snprintf (bp, buflen, "? ");
2549 * Now, check the answer
2551 if (!getanswer (buffer))
2556 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2559 ruserpass (e->eb_site, &username, &password);
2564 ce->ce_unlink = (*file == NULL);
2566 cachefile[0] = '\0';
2567 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2568 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2569 cachefile, sizeof(cachefile)) != NOTOK) {
2570 if (*file == NULL) {
2577 ce->ce_file = add (*file, NULL);
2579 ce->ce_file = add (cachefile, NULL);
2581 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2583 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2584 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2589 int child_id, i, vecp;
2593 vec[vecp++] = r1bindex (ftp, '/');
2594 vec[vecp++] = e->eb_site;
2597 vec[vecp++] = e->eb_dir;
2598 vec[vecp++] = e->eb_name;
2599 vec[vecp++] = ce->ce_file,
2600 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2601 ? "ascii" : "binary";
2606 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2610 adios ("fork", "unable to");
2614 close (fileno (ce->ce_fp));
2616 fprintf (stderr, "unable to exec ");
2622 if (pidXwait (child_id, NULL)) {
2623 username = password = NULL;
2633 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2638 mask = umask (cachetype ? ~m_gmprot () : 0222);
2639 if ((fp = fopen (cachefile, "w"))) {
2641 FILE *gp = ce->ce_fp;
2643 fseek (gp, 0L, SEEK_SET);
2645 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2647 fwrite (buffer, sizeof(*buffer), cc, fp);
2651 admonish (ce->ce_file, "error reading");
2656 admonish (cachefile, "error writing");
2665 fseek (ce->ce_fp, 0L, SEEK_SET);
2666 *file = ce->ce_file;
2667 return fileno (ce->ce_fp);
2678 return init_encoding (ct, openMail);
2683 openMail (CT ct, char **file)
2685 int child_id, fd, i, vecp;
2687 char *bp, buffer[BUFSIZ], *vec[7];
2688 struct exbody *e = ct->c_ctexbody;
2689 CE ce = ct->c_cefile;
2691 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2702 if (!e->eb_server) {
2703 content_error (NULL, ct, "missing server parameter");
2710 pidcheck (pidwait (xpid, NOTOK));
2714 /* Get buffer ready to go */
2716 buflen = sizeof(buffer);
2718 /* Now, construct query message */
2719 snprintf (bp, buflen, "Retrieve content");
2725 snprintf (bp, buflen, " %s", e->eb_partno);
2731 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2733 e->eb_subject ? e->eb_subject : e->eb_body);
2735 /* Now, check answer */
2736 if (!getanswer (buffer))
2740 vec[vecp++] = r1bindex (mailproc, '/');
2741 vec[vecp++] = e->eb_server;
2742 vec[vecp++] = "-subject";
2743 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2744 vec[vecp++] = "-body";
2745 vec[vecp++] = e->eb_body;
2748 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2752 advise ("fork", "unable to");
2756 execvp (mailproc, vec);
2757 fprintf (stderr, "unable to exec ");
2763 if (pidXwait (child_id, NULL) == OK)
2764 advise (NULL, "request sent");
2768 if (*file == NULL) {
2769 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2772 ce->ce_file = add (*file, NULL);
2776 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2777 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2781 /* showproc is for mhshow and mhstore, though mhlist -debug
2782 * prints it, too. */
2784 free (ct->c_showproc);
2785 ct->c_showproc = add ("true", NULL);
2787 fseek (ce->ce_fp, 0L, SEEK_SET);
2788 *file = ce->ce_file;
2789 return fileno (ce->ce_fp);
2794 readDigest (CT ct, char *cp)
2799 unsigned char *dp, value, *ep;
2800 unsigned char *b, *b1, *b2, *b3;
2802 b = (unsigned char *) &bits,
2803 b1 = &b[endian > 0 ? 1 : 2],
2804 b2 = &b[endian > 0 ? 2 : 1],
2805 b3 = &b[endian > 0 ? 3 : 0];
2810 for (ep = (dp = ct->c_digest)
2811 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2816 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2818 fprintf (stderr, "invalid BASE64 encoding\n");
2822 bits |= value << bitno;
2824 if ((bitno -= 6) < 0) {
2825 if (dp + (3 - skip) > ep)
2826 goto invalid_digest;
2841 goto self_delimiting;
2846 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2856 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2864 fprintf (stderr, "MD5 digest=");
2865 for (dp = ct->c_digest; dp < ep; dp++)
2866 fprintf (stderr, "%02x", *dp & 0xff);
2867 fprintf (stderr, "\n");