3 * mhparse.c -- routines to parse the contents of MIME messages
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
14 #include <h/signals.h>
22 #include <h/mhparse.h>
25 #ifdef HAVE_SYS_WAIT_H
26 # include <sys/wait.h>
32 extern int endian; /* mhmisc.c */
34 extern pid_t xpid; /* mhshowsbr.c */
37 extern int rcachesw; /* mhcachesbr.c */
38 extern int wcachesw; /* mhcachesbr.c */
40 int checksw = 0; /* check Content-MD5 field */
43 * Directory to place temp files. This must
44 * be set before these routines are called.
49 * Structures for TEXT messages
51 struct k2v SubText[] = {
52 { "plain", TEXT_PLAIN },
53 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
54 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
55 { NULL, TEXT_UNKNOWN } /* this one must be last! */
58 struct k2v Charset[] = {
59 { "us-ascii", CHARSET_USASCII },
60 { "iso-8859-1", CHARSET_LATIN },
61 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
65 * Structures for MULTIPART messages
67 struct k2v SubMultiPart[] = {
68 { "mixed", MULTI_MIXED },
69 { "alternative", MULTI_ALTERNATE },
70 { "digest", MULTI_DIGEST },
71 { "parallel", MULTI_PARALLEL },
72 { NULL, MULTI_UNKNOWN } /* this one must be last! */
76 * Structures for MESSAGE messages
78 struct k2v SubMessage[] = {
79 { "rfc822", MESSAGE_RFC822 },
80 { "partial", MESSAGE_PARTIAL },
81 { "external-body", MESSAGE_EXTERNAL },
82 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
86 * Structure for APPLICATION messages
88 struct k2v SubApplication[] = {
89 { "octet-stream", APPLICATION_OCTETS },
90 { "postscript", APPLICATION_POSTSCRIPT },
91 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
96 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
99 int find_cache (CT, int, int *, char *, char *, int);
102 int part_ok (CT, int);
103 int type_ok (CT, int);
104 int make_intermediates (char *);
105 void content_error (char *, CT, char *, ...);
108 void free_content (CT);
109 void free_encoding (CT, int);
114 static CT get_content (FILE *, char *, int);
115 static int get_comment (CT, unsigned char **, int);
117 static int InitGeneric (CT);
118 static int InitText (CT);
119 static int InitMultiPart (CT);
120 static void reverse_parts (CT);
121 static int InitMessage (CT);
122 static int InitApplication (CT);
123 static int init_encoding (CT, OpenCEFunc);
124 static unsigned long size_encoding (CT);
125 static int InitBase64 (CT);
126 static int openBase64 (CT, char **);
127 static int InitQuoted (CT);
128 static int openQuoted (CT, char **);
129 static int Init7Bit (CT);
130 static int openExternal (CT, CT, CE, char **, int *);
131 static int InitFile (CT);
132 static int openFile (CT, char **);
133 static int InitFTP (CT);
134 static int openFTP (CT, char **);
135 static int InitMail (CT);
136 static int openMail (CT, char **);
137 static int readDigest (CT, char *);
139 struct str2init str2cts[] = {
140 { "application", CT_APPLICATION, InitApplication },
141 { "audio", CT_AUDIO, InitGeneric },
142 { "image", CT_IMAGE, InitGeneric },
143 { "message", CT_MESSAGE, InitMessage },
144 { "multipart", CT_MULTIPART, InitMultiPart },
145 { "text", CT_TEXT, InitText },
146 { "video", CT_VIDEO, InitGeneric },
147 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
148 { NULL, CT_UNKNOWN, NULL },
151 struct str2init str2ces[] = {
152 { "base64", CE_BASE64, InitBase64 },
153 { "quoted-printable", CE_QUOTED, InitQuoted },
154 { "8bit", CE_8BIT, Init7Bit },
155 { "7bit", CE_7BIT, Init7Bit },
156 { "binary", CE_BINARY, Init7Bit },
157 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
158 { NULL, CE_UNKNOWN, NULL },
162 * NOTE WELL: si_key MUST NOT have value of NOTOK
164 * si_key is 1 if access method is anonymous.
166 struct str2init str2methods[] = {
167 { "afs", 1, InitFile },
168 { "anon-ftp", 1, InitFTP },
169 { "ftp", 0, InitFTP },
170 { "local-file", 0, InitFile },
171 { "mail-server", 0, InitMail },
177 pidcheck (int status)
179 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
190 * Main entry point for parsing a MIME message or file.
191 * It returns the Content structure for the top level
192 * entity in the file.
196 parse_mime (char *file)
204 * Check if file is actually standard input
206 if ((is_stdin = !(strcmp (file, "-")))) {
207 file = add (m_tmpfil (invo_name), NULL);
208 if ((fp = fopen (file, "w+")) == NULL) {
209 advise (file, "unable to fopen for writing and reading");
213 while (fgets (buffer, sizeof(buffer), stdin))
217 if (ferror (stdin)) {
219 advise ("stdin", "error reading");
224 advise (file, "error writing");
227 fseek (fp, 0L, SEEK_SET);
228 } else if ((fp = fopen (file, "r")) == NULL) {
229 advise (file, "unable to read");
233 if (!(ct = get_content (fp, file, 1))) {
236 advise (NULL, "unable to decode %s", file);
241 ct->c_unlink = 1; /* temp file to remove */
245 if (ct->c_end == 0L) {
246 fseek (fp, 0L, SEEK_END);
247 ct->c_end = ftell (fp);
250 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
262 * Main routine for reading/parsing the headers
263 * of a message content.
265 * toplevel = 1 # we are at the top level of the message
266 * toplevel = 0 # we are inside message type or multipart type
267 * # other than multipart/digest
268 * toplevel = -1 # we are inside multipart/digest
269 * NB: on failure we will fclose(in)!
273 get_content (FILE *in, char *file, int toplevel)
276 char buf[BUFSIZ], name[NAMESZ];
281 /* allocate the content structure */
282 if (!(ct = (CT) calloc (1, sizeof(*ct))))
283 adios (NULL, "out of memory");
286 ct->c_file = add (file, NULL);
287 ct->c_begin = ftell (ct->c_fp) + 1;
290 * Parse the header fields for this
291 * content into a linked list.
293 for (compnum = 1, state = FLD;;) {
294 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
300 /* get copies of the buffers */
301 np = add (name, NULL);
302 vp = add (buf, NULL);
304 /* if necessary, get rest of field */
305 while (state == FLDPLUS) {
306 state = m_getfld (state, name, buf, sizeof(buf), in);
307 vp = add (buf, vp); /* add to previous value */
310 /* Now add the header data to the list */
311 add_header (ct, np, vp);
313 /* continue, if this isn't the last header field */
314 if (state != FLDEOF) {
315 ct->c_begin = ftell (in) + 1;
322 ct->c_begin = ftell (in) - strlen (buf);
326 ct->c_begin = ftell (in);
331 adios (NULL, "message format error in component #%d", compnum);
334 adios (NULL, "getfld() returned %d", state);
337 /* break out of the loop */
342 * Read the content headers. We will parse the
343 * MIME related header fields into their various
344 * structures and set internal flags related to
345 * content type/subtype, etc.
348 hp = ct->c_first_hf; /* start at first header field */
350 /* Get MIME-Version field */
351 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
354 unsigned char *cp, *dp;
357 advise (NULL, "message %s has multiple %s: fields",
358 ct->c_file, VRSN_FIELD);
361 ct->c_vrsn = add (hp->value, NULL);
363 /* Now, cleanup this field */
366 while (isspace (*cp))
368 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
370 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
375 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
377 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
380 for (dp = cp; istoken (*dp); dp++)
384 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
387 admonish (NULL, "message %s has unknown value for %s: field (%s)",
388 ct->c_file, VRSN_FIELD, cp);
391 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
392 /* Get Content-Type field */
393 struct str2init *s2i;
394 CI ci = &ct->c_ctinfo;
396 /* Check if we've already seen a Content-Type header */
398 advise (NULL, "message %s has multiple %s: fields",
399 ct->c_file, TYPE_FIELD);
403 /* Parse the Content-Type field */
404 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
408 * Set the Init function and the internal
409 * flag for this content type.
411 for (s2i = str2cts; s2i->si_key; s2i++)
412 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
414 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
416 ct->c_type = s2i->si_val;
417 ct->c_ctinitfnx = s2i->si_init;
419 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
420 /* Get Content-Transfer-Encoding field */
422 unsigned char *cp, *dp;
423 struct str2init *s2i;
426 * Check if we've already seen the
427 * Content-Transfer-Encoding field
430 advise (NULL, "message %s has multiple %s: fields",
431 ct->c_file, ENCODING_FIELD);
435 /* get copy of this field */
436 ct->c_celine = cp = add (hp->value, NULL);
438 while (isspace (*cp))
440 for (dp = cp; istoken (*dp); dp++)
446 * Find the internal flag and Init function
447 * for this transfer encoding.
449 for (s2i = str2ces; s2i->si_key; s2i++)
450 if (!mh_strcasecmp (cp, s2i->si_key))
452 if (!s2i->si_key && !uprf (cp, "X-"))
455 ct->c_encoding = s2i->si_val;
457 /* Call the Init function for this encoding */
458 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
461 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
462 /* Get Content-MD5 field */
463 unsigned char *cp, *dp;
469 if (ct->c_digested) {
470 advise (NULL, "message %s has multiple %s: fields",
471 ct->c_file, MD5_FIELD);
475 ep = cp = add (hp->value, NULL); /* get a copy */
477 while (isspace (*cp))
479 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
481 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
486 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
488 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
493 for (dp = cp; *dp && !isspace (*dp); dp++)
501 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
502 /* Get Content-ID field */
503 ct->c_id = add (hp->value, ct->c_id);
505 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
506 /* Get Content-Description field */
507 ct->c_descr = add (hp->value, ct->c_descr);
509 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
510 /* Get Content-Disposition field */
511 ct->c_dispo = add (hp->value, ct->c_dispo);
515 hp = hp->next; /* next header field */
519 * Check if we saw a Content-Type field.
520 * If not, then assign a default value for
521 * it, and the Init function.
525 * If we are inside a multipart/digest message,
526 * so default type is message/rfc822
529 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
531 ct->c_type = CT_MESSAGE;
532 ct->c_ctinitfnx = InitMessage;
535 * Else default type is text/plain
537 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
539 ct->c_type = CT_TEXT;
540 ct->c_ctinitfnx = InitText;
544 /* Use default Transfer-Encoding, if necessary */
546 ct->c_encoding = CE_7BIT;
559 * small routine to add header field to list
563 add_header (CT ct, char *name, char *value)
567 /* allocate header field structure */
568 hp = mh_xmalloc (sizeof(*hp));
570 /* link data into header structure */
575 /* link header structure into the list */
576 if (ct->c_first_hf == NULL) {
577 ct->c_first_hf = hp; /* this is the first */
580 ct->c_last_hf->next = hp; /* add it to the end */
588 /* Make sure that buf contains at least one appearance of name,
589 followed by =. If not, insert both name and value, just after
590 first semicolon, if any. Note that name should not contain a
591 trailing =. And quotes will be added around the value. Typical
592 usage: make sure that a Content-Disposition header contains
593 filename="foo". If it doesn't and value does, use value from
596 incl_name_value (unsigned char *buf, char *name, char *value) {
599 /* Assume that name is non-null. */
601 char *name_plus_equal = concat (name, "=", NULL);
603 if (! strstr (buf, name_plus_equal)) {
606 char *prefix, *suffix;
608 /* Trim trailing space, esp. newline. */
609 for (cp = &buf[strlen (buf) - 1];
610 cp >= buf && isspace (*cp);
615 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
617 /* Insert at first semicolon, if any. If none, append to
619 prefix = add (buf, NULL);
620 if ((cp = strchr (prefix, ';'))) {
621 suffix = concat (cp, NULL);
623 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
627 newbuf = concat (buf, insertion, "\n", NULL);
635 free (name_plus_equal);
641 /* Extract just name_suffix="foo", if any, from value. If there isn't
642 one, return the entire value. Note that, for example, a name_suffix
643 of name will match filename="foo", and return foo. */
645 extract_name_value (char *name_suffix, char *value) {
646 char *extracted_name_value = value;
647 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
648 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
651 free (name_suffix_plus_quote);
652 if (name_suffix_equals) {
653 char *name_suffix_begin;
656 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
657 name_suffix_begin = ++cp;
658 /* Find second \". */
659 for (; *cp != '"'; ++cp) /* empty */;
661 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
662 memcpy (extracted_name_value,
664 cp - name_suffix_begin);
665 extracted_name_value[cp - name_suffix_begin] = '\0';
668 return extracted_name_value;
672 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
673 * directives. Fills in the information of the CTinfo structure.
676 get_ctinfo (unsigned char *cp, CT ct, int magic)
685 i = strlen (invo_name) + 2;
687 /* store copy of Content-Type line */
688 cp = ct->c_ctline = add (cp, NULL);
690 while (isspace (*cp)) /* trim leading spaces */
693 /* change newlines to spaces */
694 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
697 /* trim trailing spaces */
698 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
704 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
706 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
709 for (dp = cp; istoken (*dp); dp++)
712 ci->ci_type = add (cp, NULL); /* store content type */
716 advise (NULL, "invalid %s: field in message %s (empty type)",
717 TYPE_FIELD, ct->c_file);
721 /* down case the content type string */
722 for (dp = ci->ci_type; *dp; dp++)
723 if (isalpha(*dp) && isupper (*dp))
726 while (isspace (*cp))
729 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
734 ci->ci_subtype = add ("", NULL);
739 while (isspace (*cp))
742 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
745 for (dp = cp; istoken (*dp); dp++)
748 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
751 if (!*ci->ci_subtype) {
753 "invalid %s: field in message %s (empty subtype for \"%s\")",
754 TYPE_FIELD, ct->c_file, ci->ci_type);
758 /* down case the content subtype string */
759 for (dp = ci->ci_subtype; *dp; dp++)
760 if (isalpha(*dp) && isupper (*dp))
764 while (isspace (*cp))
767 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
771 * Parse attribute/value pairs given with Content-Type
773 ep = (ap = ci->ci_attrs) + NPARMS;
780 "too many parameters in message %s's %s: field (%d max)",
781 ct->c_file, TYPE_FIELD, NPARMS);
786 while (isspace (*cp))
789 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
794 "extraneous trailing ';' in message %s's %s: parameter list",
795 ct->c_file, TYPE_FIELD);
799 /* down case the attribute name */
800 for (dp = cp; istoken (*dp); dp++)
801 if (isalpha(*dp) && isupper (*dp))
804 for (up = dp; isspace (*dp);)
806 if (dp == cp || *dp != '=') {
808 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
809 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
813 vp = (*ap = add (cp, NULL)) + (up - cp);
815 for (dp++; isspace (*dp);)
818 /* now add the attribute value */
819 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
822 for (cp = ++dp, dp = vp;;) {
827 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
828 ct->c_file, TYPE_FIELD, i, i, "", *ap);
833 if ((c = *cp++) == '\0')
848 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
854 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
855 ct->c_file, TYPE_FIELD, i, i, "", *ap);
860 while (isspace (*cp))
863 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
868 * Get any <Content-Id> given in buffer
870 if (magic && *cp == '<') {
875 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
876 advise (NULL, "invalid ID in message %s", ct->c_file);
882 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
888 while (isspace (*cp))
893 * Get any [Content-Description] given in buffer.
895 if (magic && *cp == '[') {
897 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
901 advise (NULL, "invalid description in message %s", ct->c_file);
909 ct->c_descr = concat (ct->c_descr, "\n", NULL);
915 while (isspace (*cp))
920 * Get any {Content-Disposition} given in buffer.
922 if (magic && *cp == '{') {
924 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
928 advise (NULL, "invalid disposition in message %s", ct->c_file);
936 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
942 while (isspace (*cp))
947 * Check if anything is left over
951 ci->ci_magic = add (cp, NULL);
953 /* If there is a Content-Disposition header and it doesn't
954 have a *filename=, extract it from the magic contents.
955 The r1bindex call skips any leading directory
959 incl_name_value (ct->c_dispo,
961 r1bindex (extract_name_value ("name",
968 "extraneous information in message %s's %s: field\n%*.*s(%s)",
969 ct->c_file, TYPE_FIELD, i, i, "", cp);
977 get_comment (CT ct, unsigned char **ap, int istype)
982 char c, buffer[BUFSIZ], *dp;
994 advise (NULL, "invalid comment in message %s's %s: field",
995 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1000 if ((c = *cp++) == '\0')
1023 if ((dp = ci->ci_comment)) {
1024 ci->ci_comment = concat (dp, " ", buffer, NULL);
1027 ci->ci_comment = add (buffer, NULL);
1031 while (isspace (*cp))
1042 * Handles content types audio, image, and video.
1043 * There's not much to do right here.
1049 return OK; /* not much to do here */
1060 char buffer[BUFSIZ];
1062 char **ap, **ep, *cp;
1065 CI ci = &ct->c_ctinfo;
1067 /* check for missing subtype */
1068 if (!*ci->ci_subtype)
1069 ci->ci_subtype = add ("plain", ci->ci_subtype);
1072 for (kv = SubText; kv->kv_key; kv++)
1073 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1075 ct->c_subtype = kv->kv_value;
1077 /* allocate text character set structure */
1078 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1079 adios (NULL, "out of memory");
1080 ct->c_ctparams = (void *) t;
1082 /* scan for charset parameter */
1083 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1084 if (!mh_strcasecmp (*ap, "charset"))
1087 /* check if content specified a character set */
1089 /* match character set or set to CHARSET_UNKNOWN */
1090 for (kv = Charset; kv->kv_key; kv++)
1091 if (!mh_strcasecmp (*ep, kv->kv_key))
1093 t->tx_charset = kv->kv_value;
1095 t->tx_charset = CHARSET_UNSPECIFIED;
1099 * If we can not handle character set natively,
1100 * then check profile for string to modify the
1101 * terminal or display method.
1103 * termproc is for mhshow, though mhlist -debug prints it, too.
1105 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1106 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1107 if ((cp = context_find (buffer)))
1108 ct->c_termproc = getcpy (cp);
1120 InitMultiPart (CT ct)
1124 unsigned char *cp, *dp;
1126 char *bp, buffer[BUFSIZ];
1127 struct multipart *m;
1129 struct part *part, **next;
1130 CI ci = &ct->c_ctinfo;
1135 * The encoding for multipart messages must be either
1136 * 7bit, 8bit, or binary (per RFC2045).
1138 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1139 && ct->c_encoding != CE_BINARY) {
1141 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1142 ci->ci_type, ci->ci_subtype, ct->c_file);
1147 for (kv = SubMultiPart; kv->kv_key; kv++)
1148 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1150 ct->c_subtype = kv->kv_value;
1153 * Check for "boundary" parameter, which is
1154 * required for multipart messages.
1157 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1158 if (!mh_strcasecmp (*ap, "boundary")) {
1164 /* complain if boundary parameter is missing */
1167 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1168 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1172 /* allocate primary structure for multipart info */
1173 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1174 adios (NULL, "out of memory");
1175 ct->c_ctparams = (void *) m;
1177 /* check if boundary parameter contains only whitespace characters */
1178 for (cp = bp; isspace (*cp); cp++)
1181 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1182 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1186 /* remove trailing whitespace from boundary parameter */
1187 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1192 /* record boundary separators */
1193 m->mp_start = concat (bp, "\n", NULL);
1194 m->mp_stop = concat (bp, "--\n", NULL);
1196 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1197 advise (ct->c_file, "unable to open for reading");
1201 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1203 next = &m->mp_parts;
1207 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1211 pos += strlen (buffer);
1212 if (buffer[0] != '-' || buffer[1] != '-')
1215 if (strcmp (buffer + 2, m->mp_start))
1218 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1219 adios (NULL, "out of memory");
1221 next = &part->mp_next;
1223 if (!(p = get_content (fp, ct->c_file,
1224 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1231 fseek (fp, pos, SEEK_SET);
1234 if (strcmp (buffer + 2, m->mp_start) == 0) {
1238 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1239 if (p->c_end < p->c_begin)
1240 p->c_begin = p->c_end;
1245 if (strcmp (buffer + 2, m->mp_stop) == 0)
1251 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1252 if (!inout && part) {
1254 p->c_end = ct->c_end;
1256 if (p->c_begin >= p->c_end) {
1257 for (next = &m->mp_parts; *next != part;
1258 next = &((*next)->mp_next))
1262 free ((char *) part);
1267 /* reverse the order of the parts for multipart/alternative */
1268 if (ct->c_subtype == MULTI_ALTERNATE)
1272 * label all subparts with part number, and
1273 * then initialize the content of the subpart.
1278 char partnam[BUFSIZ];
1281 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1282 pp = partnam + strlen (partnam);
1287 for (part = m->mp_parts, partnum = 1; part;
1288 part = part->mp_next, partnum++) {
1291 sprintf (pp, "%d", partnum);
1292 p->c_partno = add (partnam, NULL);
1294 /* initialize the content of the subparts */
1295 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1310 * reverse the order of the parts of a multipart
1314 reverse_parts (CT ct)
1317 struct multipart *m;
1318 struct part **base, **bmp, **next, *part;
1320 m = (struct multipart *) ct->c_ctparams;
1322 /* if only one part, just return */
1323 if (!m->mp_parts || !m->mp_parts->mp_next)
1326 /* count number of parts */
1328 for (part = m->mp_parts; part; part = part->mp_next)
1331 /* allocate array of pointers to the parts */
1332 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1333 adios (NULL, "out of memory");
1336 /* point at all the parts */
1337 for (part = m->mp_parts; part; part = part->mp_next)
1341 /* reverse the order of the parts */
1342 next = &m->mp_parts;
1343 for (bmp--; bmp >= base; bmp--) {
1346 next = &part->mp_next;
1350 /* free array of pointers */
1351 free ((char *) base);
1363 CI ci = &ct->c_ctinfo;
1365 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1367 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1368 ci->ci_type, ci->ci_subtype, ct->c_file);
1372 /* check for missing subtype */
1373 if (!*ci->ci_subtype)
1374 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1377 for (kv = SubMessage; kv->kv_key; kv++)
1378 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1380 ct->c_subtype = kv->kv_value;
1382 switch (ct->c_subtype) {
1383 case MESSAGE_RFC822:
1386 case MESSAGE_PARTIAL:
1391 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1392 adios (NULL, "out of memory");
1393 ct->c_ctparams = (void *) p;
1395 /* scan for parameters "id", "number", and "total" */
1396 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1397 if (!mh_strcasecmp (*ap, "id")) {
1398 p->pm_partid = add (*ep, NULL);
1401 if (!mh_strcasecmp (*ap, "number")) {
1402 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1403 || p->pm_partno < 1) {
1406 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1407 *ap, ci->ci_type, ci->ci_subtype,
1408 ct->c_file, TYPE_FIELD);
1413 if (!mh_strcasecmp (*ap, "total")) {
1414 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1423 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1425 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1426 ci->ci_type, ci->ci_subtype,
1427 ct->c_file, TYPE_FIELD);
1433 case MESSAGE_EXTERNAL:
1440 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1441 adios (NULL, "out of memory");
1442 ct->c_ctparams = (void *) e;
1445 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1446 advise (ct->c_file, "unable to open for reading");
1450 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1452 if (!(p = get_content (fp, ct->c_file, 0))) {
1460 if ((exresult = params_external (ct, 0)) != NOTOK
1461 && p->c_ceopenfnx == openMail) {
1465 if ((size = ct->c_end - p->c_begin) <= 0) {
1467 content_error (NULL, ct,
1468 "empty body for access-type=mail-server");
1472 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1473 fseek (p->c_fp, p->c_begin, SEEK_SET);
1475 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1477 adios ("failed", "fread");
1480 adios (NULL, "unexpected EOF from fread");
1483 bp += cc, size -= cc;
1490 p->c_end = p->c_begin;
1495 if (exresult == NOTOK)
1497 if (e->eb_flags == NOTOK)
1500 switch (p->c_type) {
1505 if (p->c_subtype != MESSAGE_RFC822)
1509 e->eb_partno = ct->c_partno;
1511 (*p->c_ctinitfnx) (p);
1526 params_external (CT ct, int composing)
1529 struct exbody *e = (struct exbody *) ct->c_ctparams;
1530 CI ci = &ct->c_ctinfo;
1532 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1533 if (!mh_strcasecmp (*ap, "access-type")) {
1534 struct str2init *s2i;
1535 CT p = e->eb_content;
1537 for (s2i = str2methods; s2i->si_key; s2i++)
1538 if (!mh_strcasecmp (*ep, s2i->si_key))
1542 e->eb_flags = NOTOK;
1543 p->c_encoding = CE_EXTERNAL;
1546 e->eb_access = s2i->si_key;
1547 e->eb_flags = s2i->si_val;
1548 p->c_encoding = CE_EXTERNAL;
1550 /* Call the Init function for this external type */
1551 if ((*s2i->si_init)(p) == NOTOK)
1555 if (!mh_strcasecmp (*ap, "name")) {
1559 if (!mh_strcasecmp (*ap, "permission")) {
1560 e->eb_permission = *ep;
1563 if (!mh_strcasecmp (*ap, "site")) {
1567 if (!mh_strcasecmp (*ap, "directory")) {
1571 if (!mh_strcasecmp (*ap, "mode")) {
1575 if (!mh_strcasecmp (*ap, "size")) {
1576 sscanf (*ep, "%lu", &e->eb_size);
1579 if (!mh_strcasecmp (*ap, "server")) {
1583 if (!mh_strcasecmp (*ap, "subject")) {
1584 e->eb_subject = *ep;
1587 if (composing && !mh_strcasecmp (*ap, "body")) {
1588 e->eb_body = getcpy (*ep);
1593 if (!e->eb_access) {
1595 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1596 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1609 InitApplication (CT ct)
1612 CI ci = &ct->c_ctinfo;
1615 for (kv = SubApplication; kv->kv_key; kv++)
1616 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1618 ct->c_subtype = kv->kv_value;
1625 * TRANSFER ENCODINGS
1629 init_encoding (CT ct, OpenCEFunc openfnx)
1633 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1634 adios (NULL, "out of memory");
1637 ct->c_ceopenfnx = openfnx;
1638 ct->c_ceclosefnx = close_encoding;
1639 ct->c_cesizefnx = size_encoding;
1646 close_encoding (CT ct)
1650 if (!(ce = ct->c_cefile))
1660 static unsigned long
1661 size_encoding (CT ct)
1669 if (!(ce = ct->c_cefile))
1670 return (ct->c_end - ct->c_begin);
1672 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1673 return (long) st.st_size;
1676 if (stat (ce->ce_file, &st) != NOTOK)
1677 return (long) st.st_size;
1682 if (ct->c_encoding == CE_EXTERNAL)
1683 return (ct->c_end - ct->c_begin);
1686 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1687 return (ct->c_end - ct->c_begin);
1689 if (fstat (fd, &st) != NOTOK)
1690 size = (long) st.st_size;
1694 (*ct->c_ceclosefnx) (ct);
1703 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1710 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1711 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1713 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1714 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1715 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1716 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1717 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1718 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1719 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1726 return init_encoding (ct, openBase64);
1731 openBase64 (CT ct, char **file)
1733 int bitno, cc, digested;
1736 unsigned char value, *b, *b1, *b2, *b3;
1737 unsigned char *cp, *ep;
1738 char buffer[BUFSIZ];
1739 /* sbeck -- handle suffixes */
1744 b = (unsigned char *) &bits;
1745 b1 = &b[endian > 0 ? 1 : 2];
1746 b2 = &b[endian > 0 ? 2 : 1];
1747 b3 = &b[endian > 0 ? 3 : 0];
1751 fseek (ce->ce_fp, 0L, SEEK_SET);
1756 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1757 content_error (ce->ce_file, ct, "unable to fopen for reading");
1763 if (*file == NULL) {
1764 ce->ce_file = add (m_scratch ("", tmp), NULL);
1767 ce->ce_file = add (*file, NULL);
1771 /* sbeck@cise.ufl.edu -- handle suffixes */
1773 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1774 invo_name, ci->ci_type, ci->ci_subtype);
1775 cp = context_find (buffer);
1776 if (cp == NULL || *cp == '\0') {
1777 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1779 cp = context_find (buffer);
1781 if (cp != NULL && *cp != '\0')
1782 ce->ce_file = add (cp, ce->ce_file);
1784 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1785 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1789 if ((len = ct->c_end - ct->c_begin) < 0)
1790 adios (NULL, "internal error(1)");
1792 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1793 content_error (ct->c_file, ct, "unable to open for reading");
1797 if ((digested = ct->c_digested))
1798 MD5Init (&mdContext);
1804 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1806 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1808 content_error (ct->c_file, ct, "error reading from");
1812 content_error (NULL, ct, "premature eof");
1820 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1825 if (skip || (*cp & 0x80)
1826 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1828 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1830 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1833 content_error (NULL, ct,
1834 "invalid BASE64 encoding -- continuing");
1838 bits |= value << bitno;
1840 if ((bitno -= 6) < 0) {
1841 putc ((char) *b1, ce->ce_fp);
1843 MD5Update (&mdContext, b1, 1);
1845 putc ((char) *b2, ce->ce_fp);
1847 MD5Update (&mdContext, b2, 1);
1849 putc ((char) *b3, ce->ce_fp);
1851 MD5Update (&mdContext, b3, 1);
1855 if (ferror (ce->ce_fp)) {
1856 content_error (ce->ce_file, ct,
1857 "error writing to");
1860 bitno = 18, bits = 0L, skip = 0;
1866 goto self_delimiting;
1875 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1877 content_error (NULL, ct, "invalid BASE64 encoding");
1882 fseek (ct->c_fp, 0L, SEEK_SET);
1884 if (fflush (ce->ce_fp)) {
1885 content_error (ce->ce_file, ct, "error writing to");
1890 unsigned char digest[16];
1892 MD5Final (digest, &mdContext);
1893 if (memcmp((char *) digest, (char *) ct->c_digest,
1894 sizeof(digest) / sizeof(digest[0])))
1895 content_error (NULL, ct,
1896 "content integrity suspect (digest mismatch) -- continuing");
1899 fprintf (stderr, "content integrity confirmed\n");
1902 fseek (ce->ce_fp, 0L, SEEK_SET);
1905 *file = ce->ce_file;
1906 return fileno (ce->ce_fp);
1909 free_encoding (ct, 0);
1918 static char hex2nib[0x80] = {
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1925 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1926 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1927 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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
1941 return init_encoding (ct, openQuoted);
1946 openQuoted (CT ct, char **file)
1948 int cc, digested, len, quoted;
1949 unsigned char *cp, *ep;
1950 char buffer[BUFSIZ];
1953 /* sbeck -- handle suffixes */
1959 fseek (ce->ce_fp, 0L, SEEK_SET);
1964 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1965 content_error (ce->ce_file, ct, "unable to fopen for reading");
1971 if (*file == NULL) {
1972 ce->ce_file = add (m_scratch ("", tmp), NULL);
1975 ce->ce_file = add (*file, NULL);
1979 /* sbeck@cise.ufl.edu -- handle suffixes */
1981 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1982 invo_name, ci->ci_type, ci->ci_subtype);
1983 cp = context_find (buffer);
1984 if (cp == NULL || *cp == '\0') {
1985 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1987 cp = context_find (buffer);
1989 if (cp != NULL && *cp != '\0')
1990 ce->ce_file = add (cp, ce->ce_file);
1992 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1993 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1997 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1998 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2002 if ((len = ct->c_end - ct->c_begin) < 0)
2003 adios (NULL, "internal error(2)");
2005 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2006 content_error (ct->c_file, ct, "unable to open for reading");
2010 if ((digested = ct->c_digested))
2011 MD5Init (&mdContext);
2018 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2020 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2021 content_error (NULL, ct, "premature eof");
2025 if ((cc = strlen (buffer)) > len)
2029 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2034 for (; cp < ep; cp++) {
2036 /* in an escape sequence */
2038 /* at byte 1 of an escape sequence */
2039 mask = hex2nib[*cp & 0x7f];
2040 /* next is byte 2 */
2043 /* at byte 2 of an escape sequence */
2045 mask |= hex2nib[*cp & 0x7f];
2046 putc (mask, ce->ce_fp);
2048 MD5Update (&mdContext, &mask, 1);
2049 if (ferror (ce->ce_fp)) {
2050 content_error (ce->ce_file, ct, "error writing to");
2053 /* finished escape sequence; next may be literal or a new
2054 * escape sequence */
2057 /* on to next byte */
2061 /* not in an escape sequence */
2063 /* starting an escape sequence, or invalid '='? */
2064 if (cp + 1 < ep && cp[1] == '\n') {
2065 /* "=\n" soft line break, eat the \n */
2069 if (cp + 1 >= ep || cp + 2 >= ep) {
2070 /* We don't have 2 bytes left, so this is an invalid
2071 * escape sequence; just show the raw bytes (below). */
2072 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2073 /* Next 2 bytes are hex digits, making this a valid escape
2074 * sequence; let's decode it (above). */
2078 /* One or both of the next 2 is out of range, making this
2079 * an invalid escape sequence; just show the raw bytes
2084 /* Just show the raw byte. */
2085 putc (*cp, ce->ce_fp);
2088 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2090 MD5Update (&mdContext, (unsigned char *) cp, 1);
2093 if (ferror (ce->ce_fp)) {
2094 content_error (ce->ce_file, ct, "error writing to");
2100 content_error (NULL, ct,
2101 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2105 fseek (ct->c_fp, 0L, SEEK_SET);
2107 if (fflush (ce->ce_fp)) {
2108 content_error (ce->ce_file, ct, "error writing to");
2113 unsigned char digest[16];
2115 MD5Final (digest, &mdContext);
2116 if (memcmp((char *) digest, (char *) ct->c_digest,
2117 sizeof(digest) / sizeof(digest[0])))
2118 content_error (NULL, ct,
2119 "content integrity suspect (digest mismatch) -- continuing");
2122 fprintf (stderr, "content integrity confirmed\n");
2125 fseek (ce->ce_fp, 0L, SEEK_SET);
2128 *file = ce->ce_file;
2129 return fileno (ce->ce_fp);
2132 free_encoding (ct, 0);
2144 if (init_encoding (ct, open7Bit) == NOTOK)
2147 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2153 open7Bit (CT ct, char **file)
2156 char buffer[BUFSIZ];
2157 /* sbeck -- handle suffixes */
2164 fseek (ce->ce_fp, 0L, SEEK_SET);
2169 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2170 content_error (ce->ce_file, ct, "unable to fopen for reading");
2176 if (*file == NULL) {
2177 ce->ce_file = add (m_scratch ("", tmp), NULL);
2180 ce->ce_file = add (*file, NULL);
2184 /* sbeck@cise.ufl.edu -- handle suffixes */
2186 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2187 invo_name, ci->ci_type, ci->ci_subtype);
2188 cp = context_find (buffer);
2189 if (cp == NULL || *cp == '\0') {
2190 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2192 cp = context_find (buffer);
2194 if (cp != NULL && *cp != '\0')
2195 ce->ce_file = add (cp, ce->ce_file);
2197 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2198 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2202 if (ct->c_type == CT_MULTIPART) {
2204 CI ci = &ct->c_ctinfo;
2207 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2208 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2209 + 1 + strlen (ci->ci_subtype);
2210 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2211 putc (';', ce->ce_fp);
2214 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2216 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2217 fputs ("\n\t", ce->ce_fp);
2220 putc (' ', ce->ce_fp);
2223 fprintf (ce->ce_fp, "%s", buffer);
2227 if (ci->ci_comment) {
2228 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2229 fputs ("\n\t", ce->ce_fp);
2233 putc (' ', ce->ce_fp);
2236 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2239 fprintf (ce->ce_fp, "\n");
2241 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2243 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2245 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2246 fprintf (ce->ce_fp, "\n");
2249 if ((len = ct->c_end - ct->c_begin) < 0)
2250 adios (NULL, "internal error(3)");
2252 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2253 content_error (ct->c_file, ct, "unable to open for reading");
2257 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2259 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2261 content_error (ct->c_file, ct, "error reading from");
2265 content_error (NULL, ct, "premature eof");
2273 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2274 if (ferror (ce->ce_fp)) {
2275 content_error (ce->ce_file, ct, "error writing to");
2280 fseek (ct->c_fp, 0L, SEEK_SET);
2282 if (fflush (ce->ce_fp)) {
2283 content_error (ce->ce_file, ct, "error writing to");
2287 fseek (ce->ce_fp, 0L, SEEK_SET);
2290 *file = ce->ce_file;
2291 return fileno (ce->ce_fp);
2294 free_encoding (ct, 0);
2304 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2306 char cachefile[BUFSIZ];
2309 fseek (ce->ce_fp, 0L, SEEK_SET);
2314 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2315 content_error (ce->ce_file, ct, "unable to fopen for reading");
2321 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2322 cachefile, sizeof(cachefile)) != NOTOK) {
2323 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2324 ce->ce_file = getcpy (cachefile);
2328 admonish (cachefile, "unable to fopen for reading");
2335 *file = ce->ce_file;
2336 *fd = fileno (ce->ce_fp);
2347 return init_encoding (ct, openFile);
2352 openFile (CT ct, char **file)
2355 char cachefile[BUFSIZ];
2356 struct exbody *e = ct->c_ctexbody;
2357 CE ce = ct->c_cefile;
2359 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2371 content_error (NULL, ct, "missing name parameter");
2375 ce->ce_file = getcpy (e->eb_name);
2378 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2379 content_error (ce->ce_file, ct, "unable to fopen for reading");
2383 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2384 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2385 cachefile, sizeof(cachefile)) != NOTOK) {
2389 mask = umask (cachetype ? ~m_gmprot () : 0222);
2390 if ((fp = fopen (cachefile, "w"))) {
2392 char buffer[BUFSIZ];
2393 FILE *gp = ce->ce_fp;
2395 fseek (gp, 0L, SEEK_SET);
2397 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2399 fwrite (buffer, sizeof(*buffer), cc, fp);
2403 admonish (ce->ce_file, "error reading");
2408 admonish (cachefile, "error writing");
2416 fseek (ce->ce_fp, 0L, SEEK_SET);
2417 *file = ce->ce_file;
2418 return fileno (ce->ce_fp);
2428 return init_encoding (ct, openFTP);
2433 openFTP (CT ct, char **file)
2435 int cachetype, caching, fd;
2437 char *bp, *ftp, *user, *pass;
2438 char buffer[BUFSIZ], cachefile[BUFSIZ];
2441 static char *username = NULL;
2442 static char *password = NULL;
2447 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2455 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2466 if (!e->eb_name || !e->eb_site) {
2467 content_error (NULL, ct, "missing %s parameter",
2468 e->eb_name ? "site": "name");
2475 pidcheck (pidwait (xpid, NOTOK));
2479 /* Get the buffer ready to go */
2481 buflen = sizeof(buffer);
2484 * Construct the query message for user
2486 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2492 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2498 snprintf (bp, buflen, "\n using %sFTP from site %s",
2499 e->eb_flags ? "anonymous " : "", e->eb_site);
2504 if (e->eb_size > 0) {
2505 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2510 snprintf (bp, buflen, "? ");
2513 * Now, check the answer
2515 if (!getanswer (buffer))
2520 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2523 ruserpass (e->eb_site, &username, &password);
2528 ce->ce_unlink = (*file == NULL);
2530 cachefile[0] = '\0';
2531 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2532 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2533 cachefile, sizeof(cachefile)) != NOTOK) {
2534 if (*file == NULL) {
2541 ce->ce_file = add (*file, NULL);
2543 ce->ce_file = add (cachefile, NULL);
2545 ce->ce_file = add (m_scratch ("", tmp), NULL);
2547 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2548 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2556 int child_id, i, vecp;
2560 vec[vecp++] = r1bindex (ftp, '/');
2561 vec[vecp++] = e->eb_site;
2564 vec[vecp++] = e->eb_dir;
2565 vec[vecp++] = e->eb_name;
2566 vec[vecp++] = ce->ce_file,
2567 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2568 ? "ascii" : "binary";
2573 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2577 adios ("fork", "unable to");
2581 close (fileno (ce->ce_fp));
2583 fprintf (stderr, "unable to exec ");
2589 if (pidXwait (child_id, NULL)) {
2593 username = password = NULL;
2602 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2604 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2611 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2616 mask = umask (cachetype ? ~m_gmprot () : 0222);
2617 if ((fp = fopen (cachefile, "w"))) {
2619 FILE *gp = ce->ce_fp;
2621 fseek (gp, 0L, SEEK_SET);
2623 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2625 fwrite (buffer, sizeof(*buffer), cc, fp);
2629 admonish (ce->ce_file, "error reading");
2634 admonish (cachefile, "error writing");
2643 fseek (ce->ce_fp, 0L, SEEK_SET);
2644 *file = ce->ce_file;
2645 return fileno (ce->ce_fp);
2656 return init_encoding (ct, openMail);
2661 openMail (CT ct, char **file)
2663 int child_id, fd, i, vecp;
2665 char *bp, buffer[BUFSIZ], *vec[7];
2666 struct exbody *e = ct->c_ctexbody;
2667 CE ce = ct->c_cefile;
2669 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2680 if (!e->eb_server) {
2681 content_error (NULL, ct, "missing server parameter");
2688 pidcheck (pidwait (xpid, NOTOK));
2692 /* Get buffer ready to go */
2694 buflen = sizeof(buffer);
2696 /* Now, construct query message */
2697 snprintf (bp, buflen, "Retrieve content");
2703 snprintf (bp, buflen, " %s", e->eb_partno);
2709 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2711 e->eb_subject ? e->eb_subject : e->eb_body);
2713 /* Now, check answer */
2714 if (!getanswer (buffer))
2718 vec[vecp++] = r1bindex (mailproc, '/');
2719 vec[vecp++] = e->eb_server;
2720 vec[vecp++] = "-subject";
2721 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2722 vec[vecp++] = "-body";
2723 vec[vecp++] = e->eb_body;
2726 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2730 advise ("fork", "unable to");
2734 execvp (mailproc, vec);
2735 fprintf (stderr, "unable to exec ");
2741 if (pidXwait (child_id, NULL) == OK)
2742 advise (NULL, "request sent");
2746 if (*file == NULL) {
2747 ce->ce_file = add (m_scratch ("", tmp), NULL);
2750 ce->ce_file = add (*file, NULL);
2754 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2755 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2759 /* showproc is for mhshow and mhstore, though mhlist -debug
2760 * prints it, too. */
2762 free (ct->c_showproc);
2763 ct->c_showproc = add ("true", NULL);
2765 fseek (ce->ce_fp, 0L, SEEK_SET);
2766 *file = ce->ce_file;
2767 return fileno (ce->ce_fp);
2772 readDigest (CT ct, char *cp)
2777 unsigned char *dp, value, *ep;
2778 unsigned char *b, *b1, *b2, *b3;
2780 b = (unsigned char *) &bits,
2781 b1 = &b[endian > 0 ? 1 : 2],
2782 b2 = &b[endian > 0 ? 2 : 1],
2783 b3 = &b[endian > 0 ? 3 : 0];
2788 for (ep = (dp = ct->c_digest)
2789 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2794 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2796 fprintf (stderr, "invalid BASE64 encoding\n");
2800 bits |= value << bitno;
2802 if ((bitno -= 6) < 0) {
2803 if (dp + (3 - skip) > ep)
2804 goto invalid_digest;
2819 goto self_delimiting;
2824 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2834 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2842 fprintf (stderr, "MD5 digest=");
2843 for (dp = ct->c_digest; dp < ep; dp++)
2844 fprintf (stderr, "%02x", *dp & 0xff);
2845 fprintf (stderr, "\n");