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.
1047 return OK; /* not much to do here */
1058 char buffer[BUFSIZ];
1060 char **ap, **ep, *cp;
1063 CI ci = &ct->c_ctinfo;
1065 /* check for missing subtype */
1066 if (!*ci->ci_subtype)
1067 ci->ci_subtype = add ("plain", ci->ci_subtype);
1070 for (kv = SubText; kv->kv_key; kv++)
1071 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1073 ct->c_subtype = kv->kv_value;
1075 /* allocate text character set structure */
1076 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1077 adios (NULL, "out of memory");
1078 ct->c_ctparams = (void *) t;
1080 /* scan for charset parameter */
1081 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1082 if (!mh_strcasecmp (*ap, "charset"))
1085 /* check if content specified a character set */
1087 /* match character set or set to CHARSET_UNKNOWN */
1088 for (kv = Charset; kv->kv_key; kv++) {
1089 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1094 t->tx_charset = kv->kv_value;
1096 t->tx_charset = CHARSET_UNSPECIFIED;
1100 * If we can not handle character set natively,
1101 * then check profile for string to modify the
1102 * terminal or display method.
1104 * termproc is for mhshow, though mhlist -debug prints it, too.
1106 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1107 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1108 if ((cp = context_find (buffer)))
1109 ct->c_termproc = getcpy (cp);
1121 InitMultiPart (CT ct)
1125 unsigned char *cp, *dp;
1127 char *bp, buffer[BUFSIZ];
1128 struct multipart *m;
1130 struct part *part, **next;
1131 CI ci = &ct->c_ctinfo;
1136 * The encoding for multipart messages must be either
1137 * 7bit, 8bit, or binary (per RFC2045).
1139 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1140 && ct->c_encoding != CE_BINARY) {
1142 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1143 ci->ci_type, ci->ci_subtype, ct->c_file);
1148 for (kv = SubMultiPart; kv->kv_key; kv++)
1149 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1151 ct->c_subtype = kv->kv_value;
1154 * Check for "boundary" parameter, which is
1155 * required for multipart messages.
1158 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1159 if (!mh_strcasecmp (*ap, "boundary")) {
1165 /* complain if boundary parameter is missing */
1168 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1169 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1173 /* allocate primary structure for multipart info */
1174 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1175 adios (NULL, "out of memory");
1176 ct->c_ctparams = (void *) m;
1178 /* check if boundary parameter contains only whitespace characters */
1179 for (cp = bp; isspace (*cp); cp++)
1182 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1183 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1187 /* remove trailing whitespace from boundary parameter */
1188 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1193 /* record boundary separators */
1194 m->mp_start = concat (bp, "\n", NULL);
1195 m->mp_stop = concat (bp, "--\n", NULL);
1197 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1198 advise (ct->c_file, "unable to open for reading");
1202 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1204 next = &m->mp_parts;
1208 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1212 pos += strlen (buffer);
1213 if (buffer[0] != '-' || buffer[1] != '-')
1216 if (strcmp (buffer + 2, m->mp_start))
1219 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1220 adios (NULL, "out of memory");
1222 next = &part->mp_next;
1224 if (!(p = get_content (fp, ct->c_file,
1225 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1232 fseek (fp, pos, SEEK_SET);
1235 if (strcmp (buffer + 2, m->mp_start) == 0) {
1239 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1240 if (p->c_end < p->c_begin)
1241 p->c_begin = p->c_end;
1246 if (strcmp (buffer + 2, m->mp_stop) == 0)
1252 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1253 if (!inout && part) {
1255 p->c_end = ct->c_end;
1257 if (p->c_begin >= p->c_end) {
1258 for (next = &m->mp_parts; *next != part;
1259 next = &((*next)->mp_next))
1263 free ((char *) part);
1268 /* reverse the order of the parts for multipart/alternative */
1269 if (ct->c_subtype == MULTI_ALTERNATE)
1273 * label all subparts with part number, and
1274 * then initialize the content of the subpart.
1279 char partnam[BUFSIZ];
1282 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1283 pp = partnam + strlen (partnam);
1288 for (part = m->mp_parts, partnum = 1; part;
1289 part = part->mp_next, partnum++) {
1292 sprintf (pp, "%d", partnum);
1293 p->c_partno = add (partnam, NULL);
1295 /* initialize the content of the subparts */
1296 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1311 * reverse the order of the parts of a multipart
1315 reverse_parts (CT ct)
1318 struct multipart *m;
1319 struct part **base, **bmp, **next, *part;
1321 m = (struct multipart *) ct->c_ctparams;
1323 /* if only one part, just return */
1324 if (!m->mp_parts || !m->mp_parts->mp_next)
1327 /* count number of parts */
1329 for (part = m->mp_parts; part; part = part->mp_next)
1332 /* allocate array of pointers to the parts */
1333 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1334 adios (NULL, "out of memory");
1337 /* point at all the parts */
1338 for (part = m->mp_parts; part; part = part->mp_next)
1342 /* reverse the order of the parts */
1343 next = &m->mp_parts;
1344 for (bmp--; bmp >= base; bmp--) {
1347 next = &part->mp_next;
1351 /* free array of pointers */
1352 free ((char *) base);
1364 CI ci = &ct->c_ctinfo;
1366 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1368 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1369 ci->ci_type, ci->ci_subtype, ct->c_file);
1373 /* check for missing subtype */
1374 if (!*ci->ci_subtype)
1375 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1378 for (kv = SubMessage; kv->kv_key; kv++)
1379 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1381 ct->c_subtype = kv->kv_value;
1383 switch (ct->c_subtype) {
1384 case MESSAGE_RFC822:
1387 case MESSAGE_PARTIAL:
1392 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1393 adios (NULL, "out of memory");
1394 ct->c_ctparams = (void *) p;
1396 /* scan for parameters "id", "number", and "total" */
1397 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1398 if (!mh_strcasecmp (*ap, "id")) {
1399 p->pm_partid = add (*ep, NULL);
1402 if (!mh_strcasecmp (*ap, "number")) {
1403 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1404 || p->pm_partno < 1) {
1407 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1408 *ap, ci->ci_type, ci->ci_subtype,
1409 ct->c_file, TYPE_FIELD);
1414 if (!mh_strcasecmp (*ap, "total")) {
1415 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1424 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1426 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1427 ci->ci_type, ci->ci_subtype,
1428 ct->c_file, TYPE_FIELD);
1434 case MESSAGE_EXTERNAL:
1441 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1442 adios (NULL, "out of memory");
1443 ct->c_ctparams = (void *) e;
1446 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1447 advise (ct->c_file, "unable to open for reading");
1451 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1453 if (!(p = get_content (fp, ct->c_file, 0))) {
1461 if ((exresult = params_external (ct, 0)) != NOTOK
1462 && p->c_ceopenfnx == openMail) {
1466 if ((size = ct->c_end - p->c_begin) <= 0) {
1468 content_error (NULL, ct,
1469 "empty body for access-type=mail-server");
1473 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1474 fseek (p->c_fp, p->c_begin, SEEK_SET);
1476 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1478 adios ("failed", "fread");
1481 adios (NULL, "unexpected EOF from fread");
1484 bp += cc, size -= cc;
1491 p->c_end = p->c_begin;
1496 if (exresult == NOTOK)
1498 if (e->eb_flags == NOTOK)
1501 switch (p->c_type) {
1506 if (p->c_subtype != MESSAGE_RFC822)
1510 e->eb_partno = ct->c_partno;
1512 (*p->c_ctinitfnx) (p);
1527 params_external (CT ct, int composing)
1530 struct exbody *e = (struct exbody *) ct->c_ctparams;
1531 CI ci = &ct->c_ctinfo;
1533 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1534 if (!mh_strcasecmp (*ap, "access-type")) {
1535 struct str2init *s2i;
1536 CT p = e->eb_content;
1538 for (s2i = str2methods; s2i->si_key; s2i++)
1539 if (!mh_strcasecmp (*ep, s2i->si_key))
1543 e->eb_flags = NOTOK;
1544 p->c_encoding = CE_EXTERNAL;
1547 e->eb_access = s2i->si_key;
1548 e->eb_flags = s2i->si_val;
1549 p->c_encoding = CE_EXTERNAL;
1551 /* Call the Init function for this external type */
1552 if ((*s2i->si_init)(p) == NOTOK)
1556 if (!mh_strcasecmp (*ap, "name")) {
1560 if (!mh_strcasecmp (*ap, "permission")) {
1561 e->eb_permission = *ep;
1564 if (!mh_strcasecmp (*ap, "site")) {
1568 if (!mh_strcasecmp (*ap, "directory")) {
1572 if (!mh_strcasecmp (*ap, "mode")) {
1576 if (!mh_strcasecmp (*ap, "size")) {
1577 sscanf (*ep, "%lu", &e->eb_size);
1580 if (!mh_strcasecmp (*ap, "server")) {
1584 if (!mh_strcasecmp (*ap, "subject")) {
1585 e->eb_subject = *ep;
1588 if (composing && !mh_strcasecmp (*ap, "body")) {
1589 e->eb_body = getcpy (*ep);
1594 if (!e->eb_access) {
1596 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1597 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1610 InitApplication (CT ct)
1613 CI ci = &ct->c_ctinfo;
1616 for (kv = SubApplication; kv->kv_key; kv++)
1617 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1619 ct->c_subtype = kv->kv_value;
1626 * TRANSFER ENCODINGS
1630 init_encoding (CT ct, OpenCEFunc openfnx)
1634 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1635 adios (NULL, "out of memory");
1638 ct->c_ceopenfnx = openfnx;
1639 ct->c_ceclosefnx = close_encoding;
1640 ct->c_cesizefnx = size_encoding;
1647 close_encoding (CT ct)
1651 if (!(ce = ct->c_cefile))
1661 static unsigned long
1662 size_encoding (CT ct)
1670 if (!(ce = ct->c_cefile))
1671 return (ct->c_end - ct->c_begin);
1673 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1674 return (long) st.st_size;
1677 if (stat (ce->ce_file, &st) != NOTOK)
1678 return (long) st.st_size;
1683 if (ct->c_encoding == CE_EXTERNAL)
1684 return (ct->c_end - ct->c_begin);
1687 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1688 return (ct->c_end - ct->c_begin);
1690 if (fstat (fd, &st) != NOTOK)
1691 size = (long) st.st_size;
1695 (*ct->c_ceclosefnx) (ct);
1704 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1711 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1712 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1713 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1714 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1715 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1716 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1717 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1718 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1719 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1720 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1727 return init_encoding (ct, openBase64);
1732 openBase64 (CT ct, char **file)
1734 int bitno, cc, digested;
1737 unsigned char value, *b, *b1, *b2, *b3;
1738 unsigned char *cp, *ep;
1739 char buffer[BUFSIZ];
1740 /* sbeck -- handle suffixes */
1745 b = (unsigned char *) &bits;
1746 b1 = &b[endian > 0 ? 1 : 2];
1747 b2 = &b[endian > 0 ? 2 : 1];
1748 b3 = &b[endian > 0 ? 3 : 0];
1752 fseek (ce->ce_fp, 0L, SEEK_SET);
1757 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1758 content_error (ce->ce_file, ct, "unable to fopen for reading");
1764 if (*file == NULL) {
1765 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1768 ce->ce_file = add (*file, NULL);
1772 /* sbeck@cise.ufl.edu -- handle suffixes */
1774 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1775 invo_name, ci->ci_type, ci->ci_subtype);
1776 cp = context_find (buffer);
1777 if (cp == NULL || *cp == '\0') {
1778 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1780 cp = context_find (buffer);
1782 if (cp != NULL && *cp != '\0') {
1783 if (ce->ce_unlink) {
1784 // Temporary file already exists, so we rename to
1785 // version with extension.
1786 char *file_org = strdup(ce->ce_file);
1787 ce->ce_file = add (cp, ce->ce_file);
1788 if (rename(file_org, ce->ce_file)) {
1789 adios (ce->ce_file, "unable to rename %s to ", file_org);
1794 ce->ce_file = add (cp, ce->ce_file);
1798 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1799 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1803 if ((len = ct->c_end - ct->c_begin) < 0)
1804 adios (NULL, "internal error(1)");
1806 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1807 content_error (ct->c_file, ct, "unable to open for reading");
1811 if ((digested = ct->c_digested))
1812 MD5Init (&mdContext);
1818 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1820 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1822 content_error (ct->c_file, ct, "error reading from");
1826 content_error (NULL, ct, "premature eof");
1834 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1839 if (skip || (*cp & 0x80)
1840 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1842 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1844 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1847 content_error (NULL, ct,
1848 "invalid BASE64 encoding -- continuing");
1852 bits |= value << bitno;
1854 if ((bitno -= 6) < 0) {
1855 putc ((char) *b1, ce->ce_fp);
1857 MD5Update (&mdContext, b1, 1);
1859 putc ((char) *b2, ce->ce_fp);
1861 MD5Update (&mdContext, b2, 1);
1863 putc ((char) *b3, ce->ce_fp);
1865 MD5Update (&mdContext, b3, 1);
1869 if (ferror (ce->ce_fp)) {
1870 content_error (ce->ce_file, ct,
1871 "error writing to");
1874 bitno = 18, bits = 0L, skip = 0;
1880 goto self_delimiting;
1889 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1891 content_error (NULL, ct, "invalid BASE64 encoding");
1896 fseek (ct->c_fp, 0L, SEEK_SET);
1898 if (fflush (ce->ce_fp)) {
1899 content_error (ce->ce_file, ct, "error writing to");
1904 unsigned char digest[16];
1906 MD5Final (digest, &mdContext);
1907 if (memcmp((char *) digest, (char *) ct->c_digest,
1908 sizeof(digest) / sizeof(digest[0])))
1909 content_error (NULL, ct,
1910 "content integrity suspect (digest mismatch) -- continuing");
1913 fprintf (stderr, "content integrity confirmed\n");
1916 fseek (ce->ce_fp, 0L, SEEK_SET);
1919 *file = ce->ce_file;
1920 return fileno (ce->ce_fp);
1923 free_encoding (ct, 0);
1932 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1940 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1944 0x00, 0x00, 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
1955 return init_encoding (ct, openQuoted);
1960 openQuoted (CT ct, char **file)
1962 int cc, digested, len, quoted;
1963 unsigned char *cp, *ep;
1964 char buffer[BUFSIZ];
1967 /* sbeck -- handle suffixes */
1973 fseek (ce->ce_fp, 0L, SEEK_SET);
1978 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1979 content_error (ce->ce_file, ct, "unable to fopen for reading");
1985 if (*file == NULL) {
1986 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1989 ce->ce_file = add (*file, NULL);
1993 /* sbeck@cise.ufl.edu -- handle suffixes */
1995 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1996 invo_name, ci->ci_type, ci->ci_subtype);
1997 cp = context_find (buffer);
1998 if (cp == NULL || *cp == '\0') {
1999 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2001 cp = context_find (buffer);
2003 if (cp != NULL && *cp != '\0') {
2004 if (ce->ce_unlink) {
2005 // Temporary file already exists, so we rename to
2006 // version with extension.
2007 char *file_org = strdup(ce->ce_file);
2008 ce->ce_file = add (cp, ce->ce_file);
2009 if (rename(file_org, ce->ce_file)) {
2010 adios (ce->ce_file, "unable to rename %s to ", file_org);
2015 ce->ce_file = add (cp, ce->ce_file);
2019 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2020 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2024 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2025 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2029 if ((len = ct->c_end - ct->c_begin) < 0)
2030 adios (NULL, "internal error(2)");
2032 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2033 content_error (ct->c_file, ct, "unable to open for reading");
2037 if ((digested = ct->c_digested))
2038 MD5Init (&mdContext);
2045 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2047 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2048 content_error (NULL, ct, "premature eof");
2052 if ((cc = strlen (buffer)) > len)
2056 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2061 for (; cp < ep; cp++) {
2063 /* in an escape sequence */
2065 /* at byte 1 of an escape sequence */
2066 mask = hex2nib[*cp & 0x7f];
2067 /* next is byte 2 */
2070 /* at byte 2 of an escape sequence */
2072 mask |= hex2nib[*cp & 0x7f];
2073 putc (mask, ce->ce_fp);
2075 MD5Update (&mdContext, &mask, 1);
2076 if (ferror (ce->ce_fp)) {
2077 content_error (ce->ce_file, ct, "error writing to");
2080 /* finished escape sequence; next may be literal or a new
2081 * escape sequence */
2084 /* on to next byte */
2088 /* not in an escape sequence */
2090 /* starting an escape sequence, or invalid '='? */
2091 if (cp + 1 < ep && cp[1] == '\n') {
2092 /* "=\n" soft line break, eat the \n */
2096 if (cp + 1 >= ep || cp + 2 >= ep) {
2097 /* We don't have 2 bytes left, so this is an invalid
2098 * escape sequence; just show the raw bytes (below). */
2099 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2100 /* Next 2 bytes are hex digits, making this a valid escape
2101 * sequence; let's decode it (above). */
2105 /* One or both of the next 2 is out of range, making this
2106 * an invalid escape sequence; just show the raw bytes
2111 /* Just show the raw byte. */
2112 putc (*cp, ce->ce_fp);
2115 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2117 MD5Update (&mdContext, (unsigned char *) cp, 1);
2120 if (ferror (ce->ce_fp)) {
2121 content_error (ce->ce_file, ct, "error writing to");
2127 content_error (NULL, ct,
2128 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2132 fseek (ct->c_fp, 0L, SEEK_SET);
2134 if (fflush (ce->ce_fp)) {
2135 content_error (ce->ce_file, ct, "error writing to");
2140 unsigned char digest[16];
2142 MD5Final (digest, &mdContext);
2143 if (memcmp((char *) digest, (char *) ct->c_digest,
2144 sizeof(digest) / sizeof(digest[0])))
2145 content_error (NULL, ct,
2146 "content integrity suspect (digest mismatch) -- continuing");
2149 fprintf (stderr, "content integrity confirmed\n");
2152 fseek (ce->ce_fp, 0L, SEEK_SET);
2155 *file = ce->ce_file;
2156 return fileno (ce->ce_fp);
2159 free_encoding (ct, 0);
2171 if (init_encoding (ct, open7Bit) == NOTOK)
2174 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2180 open7Bit (CT ct, char **file)
2183 char buffer[BUFSIZ];
2184 /* sbeck -- handle suffixes */
2191 fseek (ce->ce_fp, 0L, SEEK_SET);
2196 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2197 content_error (ce->ce_file, ct, "unable to fopen for reading");
2203 if (*file == NULL) {
2204 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2207 ce->ce_file = add (*file, NULL);
2211 /* sbeck@cise.ufl.edu -- handle suffixes */
2213 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2214 invo_name, ci->ci_type, ci->ci_subtype);
2215 cp = context_find (buffer);
2216 if (cp == NULL || *cp == '\0') {
2217 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2219 cp = context_find (buffer);
2221 if (cp != NULL && *cp != '\0') {
2222 if (ce->ce_unlink) {
2223 // Temporary file already exists, so we rename to
2224 // version with extension.
2225 char *file_org = strdup(ce->ce_file);
2226 ce->ce_file = add (cp, ce->ce_file);
2227 if (rename(file_org, ce->ce_file)) {
2228 adios (ce->ce_file, "unable to rename %s to ", file_org);
2233 ce->ce_file = add (cp, ce->ce_file);
2237 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2238 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2242 if (ct->c_type == CT_MULTIPART) {
2244 CI ci = &ct->c_ctinfo;
2247 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2248 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2249 + 1 + strlen (ci->ci_subtype);
2250 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2251 putc (';', ce->ce_fp);
2254 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2256 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2257 fputs ("\n\t", ce->ce_fp);
2260 putc (' ', ce->ce_fp);
2263 fprintf (ce->ce_fp, "%s", buffer);
2267 if (ci->ci_comment) {
2268 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2269 fputs ("\n\t", ce->ce_fp);
2273 putc (' ', ce->ce_fp);
2276 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2279 fprintf (ce->ce_fp, "\n");
2281 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2283 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2285 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2286 fprintf (ce->ce_fp, "\n");
2289 if ((len = ct->c_end - ct->c_begin) < 0)
2290 adios (NULL, "internal error(3)");
2292 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2293 content_error (ct->c_file, ct, "unable to open for reading");
2297 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2299 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2301 content_error (ct->c_file, ct, "error reading from");
2305 content_error (NULL, ct, "premature eof");
2313 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2314 if (ferror (ce->ce_fp)) {
2315 content_error (ce->ce_file, ct, "error writing to");
2320 fseek (ct->c_fp, 0L, SEEK_SET);
2322 if (fflush (ce->ce_fp)) {
2323 content_error (ce->ce_file, ct, "error writing to");
2327 fseek (ce->ce_fp, 0L, SEEK_SET);
2330 *file = ce->ce_file;
2331 return fileno (ce->ce_fp);
2334 free_encoding (ct, 0);
2344 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2346 char cachefile[BUFSIZ];
2349 fseek (ce->ce_fp, 0L, SEEK_SET);
2354 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2355 content_error (ce->ce_file, ct, "unable to fopen for reading");
2361 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2362 cachefile, sizeof(cachefile)) != NOTOK) {
2363 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2364 ce->ce_file = getcpy (cachefile);
2368 admonish (cachefile, "unable to fopen for reading");
2375 *file = ce->ce_file;
2376 *fd = fileno (ce->ce_fp);
2387 return init_encoding (ct, openFile);
2392 openFile (CT ct, char **file)
2395 char cachefile[BUFSIZ];
2396 struct exbody *e = ct->c_ctexbody;
2397 CE ce = ct->c_cefile;
2399 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2411 content_error (NULL, ct, "missing name parameter");
2415 ce->ce_file = getcpy (e->eb_name);
2418 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2419 content_error (ce->ce_file, ct, "unable to fopen for reading");
2423 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2424 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2425 cachefile, sizeof(cachefile)) != NOTOK) {
2429 mask = umask (cachetype ? ~m_gmprot () : 0222);
2430 if ((fp = fopen (cachefile, "w"))) {
2432 char buffer[BUFSIZ];
2433 FILE *gp = ce->ce_fp;
2435 fseek (gp, 0L, SEEK_SET);
2437 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2439 fwrite (buffer, sizeof(*buffer), cc, fp);
2443 admonish (ce->ce_file, "error reading");
2448 admonish (cachefile, "error writing");
2456 fseek (ce->ce_fp, 0L, SEEK_SET);
2457 *file = ce->ce_file;
2458 return fileno (ce->ce_fp);
2468 return init_encoding (ct, openFTP);
2473 openFTP (CT ct, char **file)
2475 int cachetype, caching, fd;
2477 char *bp, *ftp, *user, *pass;
2478 char buffer[BUFSIZ], cachefile[BUFSIZ];
2481 static char *username = NULL;
2482 static char *password = NULL;
2487 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2493 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2504 if (!e->eb_name || !e->eb_site) {
2505 content_error (NULL, ct, "missing %s parameter",
2506 e->eb_name ? "site": "name");
2513 pidcheck (pidwait (xpid, NOTOK));
2517 /* Get the buffer ready to go */
2519 buflen = sizeof(buffer);
2522 * Construct the query message for user
2524 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2530 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2536 snprintf (bp, buflen, "\n using %sFTP from site %s",
2537 e->eb_flags ? "anonymous " : "", e->eb_site);
2542 if (e->eb_size > 0) {
2543 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2548 snprintf (bp, buflen, "? ");
2551 * Now, check the answer
2553 if (!getanswer (buffer))
2558 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2561 ruserpass (e->eb_site, &username, &password);
2566 ce->ce_unlink = (*file == NULL);
2568 cachefile[0] = '\0';
2569 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2570 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2571 cachefile, sizeof(cachefile)) != NOTOK) {
2572 if (*file == NULL) {
2579 ce->ce_file = add (*file, NULL);
2581 ce->ce_file = add (cachefile, NULL);
2583 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2585 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2586 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2591 int child_id, i, vecp;
2595 vec[vecp++] = r1bindex (ftp, '/');
2596 vec[vecp++] = e->eb_site;
2599 vec[vecp++] = e->eb_dir;
2600 vec[vecp++] = e->eb_name;
2601 vec[vecp++] = ce->ce_file,
2602 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2603 ? "ascii" : "binary";
2608 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2612 adios ("fork", "unable to");
2616 close (fileno (ce->ce_fp));
2618 fprintf (stderr, "unable to exec ");
2624 if (pidXwait (child_id, NULL)) {
2625 username = password = NULL;
2635 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2640 mask = umask (cachetype ? ~m_gmprot () : 0222);
2641 if ((fp = fopen (cachefile, "w"))) {
2643 FILE *gp = ce->ce_fp;
2645 fseek (gp, 0L, SEEK_SET);
2647 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2649 fwrite (buffer, sizeof(*buffer), cc, fp);
2653 admonish (ce->ce_file, "error reading");
2658 admonish (cachefile, "error writing");
2667 fseek (ce->ce_fp, 0L, SEEK_SET);
2668 *file = ce->ce_file;
2669 return fileno (ce->ce_fp);
2680 return init_encoding (ct, openMail);
2685 openMail (CT ct, char **file)
2687 int child_id, fd, i, vecp;
2689 char *bp, buffer[BUFSIZ], *vec[7];
2690 struct exbody *e = ct->c_ctexbody;
2691 CE ce = ct->c_cefile;
2693 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2704 if (!e->eb_server) {
2705 content_error (NULL, ct, "missing server parameter");
2712 pidcheck (pidwait (xpid, NOTOK));
2716 /* Get buffer ready to go */
2718 buflen = sizeof(buffer);
2720 /* Now, construct query message */
2721 snprintf (bp, buflen, "Retrieve content");
2727 snprintf (bp, buflen, " %s", e->eb_partno);
2733 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2735 e->eb_subject ? e->eb_subject : e->eb_body);
2737 /* Now, check answer */
2738 if (!getanswer (buffer))
2742 vec[vecp++] = r1bindex (mailproc, '/');
2743 vec[vecp++] = e->eb_server;
2744 vec[vecp++] = "-subject";
2745 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2746 vec[vecp++] = "-body";
2747 vec[vecp++] = e->eb_body;
2750 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2754 advise ("fork", "unable to");
2758 execvp (mailproc, vec);
2759 fprintf (stderr, "unable to exec ");
2765 if (pidXwait (child_id, NULL) == OK)
2766 advise (NULL, "request sent");
2770 if (*file == NULL) {
2771 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2774 ce->ce_file = add (*file, NULL);
2778 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2779 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2783 /* showproc is for mhshow and mhstore, though mhlist -debug
2784 * prints it, too. */
2786 free (ct->c_showproc);
2787 ct->c_showproc = add ("true", NULL);
2789 fseek (ce->ce_fp, 0L, SEEK_SET);
2790 *file = ce->ce_file;
2791 return fileno (ce->ce_fp);
2796 readDigest (CT ct, char *cp)
2801 unsigned char *dp, value, *ep;
2802 unsigned char *b, *b1, *b2, *b3;
2804 b = (unsigned char *) &bits,
2805 b1 = &b[endian > 0 ? 1 : 2],
2806 b2 = &b[endian > 0 ? 2 : 1],
2807 b3 = &b[endian > 0 ? 3 : 0];
2812 for (ep = (dp = ct->c_digest)
2813 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2818 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2820 fprintf (stderr, "invalid BASE64 encoding\n");
2824 bits |= value << bitno;
2826 if ((bitno -= 6) < 0) {
2827 if (dp + (3 - skip) > ep)
2828 goto invalid_digest;
2843 goto self_delimiting;
2848 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2858 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2866 fprintf (stderr, "MD5 digest=");
2867 for (dp = ct->c_digest; dp < ep; dp++)
2868 fprintf (stderr, "%02x", *dp & 0xff);
2869 fprintf (stderr, "\n");