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)) {
1096 t->tx_charset = kv->kv_value;
1098 t->tx_charset = CHARSET_UNSPECIFIED;
1102 * If we can not handle character set natively,
1103 * then check profile for string to modify the
1104 * terminal or display method.
1106 * termproc is for mhshow, though mhlist -debug prints it, too.
1108 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1109 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1110 if ((cp = context_find (buffer)))
1111 ct->c_termproc = getcpy (cp);
1123 InitMultiPart (CT ct)
1127 unsigned char *cp, *dp;
1129 char *bp, buffer[BUFSIZ];
1130 struct multipart *m;
1132 struct part *part, **next;
1133 CI ci = &ct->c_ctinfo;
1138 * The encoding for multipart messages must be either
1139 * 7bit, 8bit, or binary (per RFC2045).
1141 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1142 && ct->c_encoding != CE_BINARY) {
1144 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1145 ci->ci_type, ci->ci_subtype, ct->c_file);
1150 for (kv = SubMultiPart; kv->kv_key; kv++)
1151 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1153 ct->c_subtype = kv->kv_value;
1156 * Check for "boundary" parameter, which is
1157 * required for multipart messages.
1160 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1161 if (!mh_strcasecmp (*ap, "boundary")) {
1167 /* complain if boundary parameter is missing */
1170 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1171 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1175 /* allocate primary structure for multipart info */
1176 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1177 adios (NULL, "out of memory");
1178 ct->c_ctparams = (void *) m;
1180 /* check if boundary parameter contains only whitespace characters */
1181 for (cp = bp; isspace (*cp); cp++)
1184 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1185 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1189 /* remove trailing whitespace from boundary parameter */
1190 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1195 /* record boundary separators */
1196 m->mp_start = concat (bp, "\n", NULL);
1197 m->mp_stop = concat (bp, "--\n", NULL);
1199 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1200 advise (ct->c_file, "unable to open for reading");
1204 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1206 next = &m->mp_parts;
1210 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1214 pos += strlen (buffer);
1215 if (buffer[0] != '-' || buffer[1] != '-')
1218 if (strcmp (buffer + 2, m->mp_start))
1221 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1222 adios (NULL, "out of memory");
1224 next = &part->mp_next;
1226 if (!(p = get_content (fp, ct->c_file,
1227 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1234 fseek (fp, pos, SEEK_SET);
1237 if (strcmp (buffer + 2, m->mp_start) == 0) {
1241 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1242 if (p->c_end < p->c_begin)
1243 p->c_begin = p->c_end;
1248 if (strcmp (buffer + 2, m->mp_stop) == 0)
1254 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1255 if (!inout && part) {
1257 p->c_end = ct->c_end;
1259 if (p->c_begin >= p->c_end) {
1260 for (next = &m->mp_parts; *next != part;
1261 next = &((*next)->mp_next))
1265 free ((char *) part);
1270 /* reverse the order of the parts for multipart/alternative */
1271 if (ct->c_subtype == MULTI_ALTERNATE)
1275 * label all subparts with part number, and
1276 * then initialize the content of the subpart.
1281 char partnam[BUFSIZ];
1284 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1285 pp = partnam + strlen (partnam);
1290 for (part = m->mp_parts, partnum = 1; part;
1291 part = part->mp_next, partnum++) {
1294 sprintf (pp, "%d", partnum);
1295 p->c_partno = add (partnam, NULL);
1297 /* initialize the content of the subparts */
1298 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1313 * reverse the order of the parts of a multipart
1317 reverse_parts (CT ct)
1320 struct multipart *m;
1321 struct part **base, **bmp, **next, *part;
1323 m = (struct multipart *) ct->c_ctparams;
1325 /* if only one part, just return */
1326 if (!m->mp_parts || !m->mp_parts->mp_next)
1329 /* count number of parts */
1331 for (part = m->mp_parts; part; part = part->mp_next)
1334 /* allocate array of pointers to the parts */
1335 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1336 adios (NULL, "out of memory");
1339 /* point at all the parts */
1340 for (part = m->mp_parts; part; part = part->mp_next)
1344 /* reverse the order of the parts */
1345 next = &m->mp_parts;
1346 for (bmp--; bmp >= base; bmp--) {
1349 next = &part->mp_next;
1353 /* free array of pointers */
1354 free ((char *) base);
1366 CI ci = &ct->c_ctinfo;
1368 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1370 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1371 ci->ci_type, ci->ci_subtype, ct->c_file);
1375 /* check for missing subtype */
1376 if (!*ci->ci_subtype)
1377 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1380 for (kv = SubMessage; kv->kv_key; kv++)
1381 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1383 ct->c_subtype = kv->kv_value;
1385 switch (ct->c_subtype) {
1386 case MESSAGE_RFC822:
1389 case MESSAGE_PARTIAL:
1394 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1395 adios (NULL, "out of memory");
1396 ct->c_ctparams = (void *) p;
1398 /* scan for parameters "id", "number", and "total" */
1399 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1400 if (!mh_strcasecmp (*ap, "id")) {
1401 p->pm_partid = add (*ep, NULL);
1404 if (!mh_strcasecmp (*ap, "number")) {
1405 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1406 || p->pm_partno < 1) {
1409 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1410 *ap, ci->ci_type, ci->ci_subtype,
1411 ct->c_file, TYPE_FIELD);
1416 if (!mh_strcasecmp (*ap, "total")) {
1417 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1426 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1428 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1429 ci->ci_type, ci->ci_subtype,
1430 ct->c_file, TYPE_FIELD);
1436 case MESSAGE_EXTERNAL:
1443 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1444 adios (NULL, "out of memory");
1445 ct->c_ctparams = (void *) e;
1448 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1449 advise (ct->c_file, "unable to open for reading");
1453 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1455 if (!(p = get_content (fp, ct->c_file, 0))) {
1463 if ((exresult = params_external (ct, 0)) != NOTOK
1464 && p->c_ceopenfnx == openMail) {
1468 if ((size = ct->c_end - p->c_begin) <= 0) {
1470 content_error (NULL, ct,
1471 "empty body for access-type=mail-server");
1475 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1476 fseek (p->c_fp, p->c_begin, SEEK_SET);
1478 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1480 adios ("failed", "fread");
1483 adios (NULL, "unexpected EOF from fread");
1486 bp += cc, size -= cc;
1493 p->c_end = p->c_begin;
1498 if (exresult == NOTOK)
1500 if (e->eb_flags == NOTOK)
1503 switch (p->c_type) {
1508 if (p->c_subtype != MESSAGE_RFC822)
1512 e->eb_partno = ct->c_partno;
1514 (*p->c_ctinitfnx) (p);
1529 params_external (CT ct, int composing)
1532 struct exbody *e = (struct exbody *) ct->c_ctparams;
1533 CI ci = &ct->c_ctinfo;
1535 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1536 if (!mh_strcasecmp (*ap, "access-type")) {
1537 struct str2init *s2i;
1538 CT p = e->eb_content;
1540 for (s2i = str2methods; s2i->si_key; s2i++)
1541 if (!mh_strcasecmp (*ep, s2i->si_key))
1545 e->eb_flags = NOTOK;
1546 p->c_encoding = CE_EXTERNAL;
1549 e->eb_access = s2i->si_key;
1550 e->eb_flags = s2i->si_val;
1551 p->c_encoding = CE_EXTERNAL;
1553 /* Call the Init function for this external type */
1554 if ((*s2i->si_init)(p) == NOTOK)
1558 if (!mh_strcasecmp (*ap, "name")) {
1562 if (!mh_strcasecmp (*ap, "permission")) {
1563 e->eb_permission = *ep;
1566 if (!mh_strcasecmp (*ap, "site")) {
1570 if (!mh_strcasecmp (*ap, "directory")) {
1574 if (!mh_strcasecmp (*ap, "mode")) {
1578 if (!mh_strcasecmp (*ap, "size")) {
1579 sscanf (*ep, "%lu", &e->eb_size);
1582 if (!mh_strcasecmp (*ap, "server")) {
1586 if (!mh_strcasecmp (*ap, "subject")) {
1587 e->eb_subject = *ep;
1590 if (composing && !mh_strcasecmp (*ap, "body")) {
1591 e->eb_body = getcpy (*ep);
1596 if (!e->eb_access) {
1598 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1599 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1612 InitApplication (CT ct)
1615 CI ci = &ct->c_ctinfo;
1618 for (kv = SubApplication; kv->kv_key; kv++)
1619 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1621 ct->c_subtype = kv->kv_value;
1628 * TRANSFER ENCODINGS
1632 init_encoding (CT ct, OpenCEFunc openfnx)
1636 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1637 adios (NULL, "out of memory");
1640 ct->c_ceopenfnx = openfnx;
1641 ct->c_ceclosefnx = close_encoding;
1642 ct->c_cesizefnx = size_encoding;
1649 close_encoding (CT ct)
1653 if (!(ce = ct->c_cefile))
1663 static unsigned long
1664 size_encoding (CT ct)
1672 if (!(ce = ct->c_cefile))
1673 return (ct->c_end - ct->c_begin);
1675 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1676 return (long) st.st_size;
1679 if (stat (ce->ce_file, &st) != NOTOK)
1680 return (long) st.st_size;
1685 if (ct->c_encoding == CE_EXTERNAL)
1686 return (ct->c_end - ct->c_begin);
1689 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1690 return (ct->c_end - ct->c_begin);
1692 if (fstat (fd, &st) != NOTOK)
1693 size = (long) st.st_size;
1697 (*ct->c_ceclosefnx) (ct);
1706 static unsigned char b642nib[0x80] = {
1707 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1713 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1714 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1715 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1716 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1717 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1718 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1719 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1720 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1721 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1722 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1729 return init_encoding (ct, openBase64);
1734 openBase64 (CT ct, char **file)
1736 int bitno, cc, digested;
1739 unsigned char value, *b, *b1, *b2, *b3;
1740 unsigned char *cp, *ep;
1741 char buffer[BUFSIZ];
1742 /* sbeck -- handle suffixes */
1747 b = (unsigned char *) &bits;
1748 b1 = &b[endian > 0 ? 1 : 2];
1749 b2 = &b[endian > 0 ? 2 : 1];
1750 b3 = &b[endian > 0 ? 3 : 0];
1754 fseek (ce->ce_fp, 0L, SEEK_SET);
1759 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1760 content_error (ce->ce_file, ct, "unable to fopen for reading");
1766 if (*file == NULL) {
1767 ce->ce_file = add (m_scratch ("", tmp), NULL);
1770 ce->ce_file = add (*file, NULL);
1774 /* sbeck@cise.ufl.edu -- handle suffixes */
1776 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1777 invo_name, ci->ci_type, ci->ci_subtype);
1778 cp = context_find (buffer);
1779 if (cp == NULL || *cp == '\0') {
1780 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1782 cp = context_find (buffer);
1784 if (cp != NULL && *cp != '\0')
1785 ce->ce_file = add (cp, ce->ce_file);
1787 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1788 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1792 if ((len = ct->c_end - ct->c_begin) < 0)
1793 adios (NULL, "internal error(1)");
1795 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1796 content_error (ct->c_file, ct, "unable to open for reading");
1800 if ((digested = ct->c_digested))
1801 MD5Init (&mdContext);
1807 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1809 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1811 content_error (ct->c_file, ct, "error reading from");
1815 content_error (NULL, ct, "premature eof");
1823 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1828 if (skip || (*cp & 0x80)
1829 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1831 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1833 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1836 content_error (NULL, ct,
1837 "invalid BASE64 encoding -- continuing");
1841 bits |= value << bitno;
1843 if ((bitno -= 6) < 0) {
1844 putc ((char) *b1, ce->ce_fp);
1846 MD5Update (&mdContext, b1, 1);
1848 putc ((char) *b2, ce->ce_fp);
1850 MD5Update (&mdContext, b2, 1);
1852 putc ((char) *b3, ce->ce_fp);
1854 MD5Update (&mdContext, b3, 1);
1858 if (ferror (ce->ce_fp)) {
1859 content_error (ce->ce_file, ct,
1860 "error writing to");
1863 bitno = 18, bits = 0L, skip = 0;
1869 goto self_delimiting;
1878 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1880 content_error (NULL, ct, "invalid BASE64 encoding");
1885 fseek (ct->c_fp, 0L, SEEK_SET);
1887 if (fflush (ce->ce_fp)) {
1888 content_error (ce->ce_file, ct, "error writing to");
1893 unsigned char digest[16];
1895 MD5Final (digest, &mdContext);
1896 if (memcmp((char *) digest, (char *) ct->c_digest,
1897 sizeof(digest) / sizeof(digest[0])))
1898 content_error (NULL, ct,
1899 "content integrity suspect (digest mismatch) -- continuing");
1902 fprintf (stderr, "content integrity confirmed\n");
1905 fseek (ce->ce_fp, 0L, SEEK_SET);
1908 *file = ce->ce_file;
1909 return fileno (ce->ce_fp);
1912 free_encoding (ct, 0);
1921 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1926 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1927 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1928 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1929 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1930 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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
1944 return init_encoding (ct, openQuoted);
1949 openQuoted (CT ct, char **file)
1951 int cc, digested, len, quoted;
1952 unsigned char *cp, *ep;
1953 char buffer[BUFSIZ];
1956 /* sbeck -- handle suffixes */
1962 fseek (ce->ce_fp, 0L, SEEK_SET);
1967 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1968 content_error (ce->ce_file, ct, "unable to fopen for reading");
1974 if (*file == NULL) {
1975 ce->ce_file = add (m_scratch ("", tmp), NULL);
1978 ce->ce_file = add (*file, NULL);
1982 /* sbeck@cise.ufl.edu -- handle suffixes */
1984 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1985 invo_name, ci->ci_type, ci->ci_subtype);
1986 cp = context_find (buffer);
1987 if (cp == NULL || *cp == '\0') {
1988 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1990 cp = context_find (buffer);
1992 if (cp != NULL && *cp != '\0')
1993 ce->ce_file = add (cp, ce->ce_file);
1995 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1996 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2000 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2001 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2005 if ((len = ct->c_end - ct->c_begin) < 0)
2006 adios (NULL, "internal error(2)");
2008 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2009 content_error (ct->c_file, ct, "unable to open for reading");
2013 if ((digested = ct->c_digested))
2014 MD5Init (&mdContext);
2021 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2023 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2024 content_error (NULL, ct, "premature eof");
2028 if ((cc = strlen (buffer)) > len)
2032 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2037 for (; cp < ep; cp++) {
2039 /* in an escape sequence */
2041 /* at byte 1 of an escape sequence */
2042 mask = hex2nib[*cp & 0x7f];
2043 /* next is byte 2 */
2046 /* at byte 2 of an escape sequence */
2048 mask |= hex2nib[*cp & 0x7f];
2049 putc (mask, ce->ce_fp);
2051 MD5Update (&mdContext, &mask, 1);
2052 if (ferror (ce->ce_fp)) {
2053 content_error (ce->ce_file, ct, "error writing to");
2056 /* finished escape sequence; next may be literal or a new
2057 * escape sequence */
2060 /* on to next byte */
2064 /* not in an escape sequence */
2066 /* starting an escape sequence, or invalid '='? */
2067 if (cp + 1 < ep && cp[1] == '\n') {
2068 /* "=\n" soft line break, eat the \n */
2072 if (cp + 1 >= ep || cp + 2 >= ep) {
2073 /* We don't have 2 bytes left, so this is an invalid
2074 * escape sequence; just show the raw bytes (below). */
2075 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2076 /* Next 2 bytes are hex digits, making this a valid escape
2077 * sequence; let's decode it (above). */
2081 /* One or both of the next 2 is out of range, making this
2082 * an invalid escape sequence; just show the raw bytes
2087 /* Just show the raw byte. */
2088 putc (*cp, ce->ce_fp);
2091 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2093 MD5Update (&mdContext, (unsigned char *) cp, 1);
2096 if (ferror (ce->ce_fp)) {
2097 content_error (ce->ce_file, ct, "error writing to");
2103 content_error (NULL, ct,
2104 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2108 fseek (ct->c_fp, 0L, SEEK_SET);
2110 if (fflush (ce->ce_fp)) {
2111 content_error (ce->ce_file, ct, "error writing to");
2116 unsigned char digest[16];
2118 MD5Final (digest, &mdContext);
2119 if (memcmp((char *) digest, (char *) ct->c_digest,
2120 sizeof(digest) / sizeof(digest[0])))
2121 content_error (NULL, ct,
2122 "content integrity suspect (digest mismatch) -- continuing");
2125 fprintf (stderr, "content integrity confirmed\n");
2128 fseek (ce->ce_fp, 0L, SEEK_SET);
2131 *file = ce->ce_file;
2132 return fileno (ce->ce_fp);
2135 free_encoding (ct, 0);
2147 if (init_encoding (ct, open7Bit) == NOTOK)
2150 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2156 open7Bit (CT ct, char **file)
2159 char buffer[BUFSIZ];
2160 /* sbeck -- handle suffixes */
2167 fseek (ce->ce_fp, 0L, SEEK_SET);
2172 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2173 content_error (ce->ce_file, ct, "unable to fopen for reading");
2179 if (*file == NULL) {
2180 ce->ce_file = add (m_scratch ("", tmp), NULL);
2183 ce->ce_file = add (*file, NULL);
2187 /* sbeck@cise.ufl.edu -- handle suffixes */
2189 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2190 invo_name, ci->ci_type, ci->ci_subtype);
2191 cp = context_find (buffer);
2192 if (cp == NULL || *cp == '\0') {
2193 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2195 cp = context_find (buffer);
2197 if (cp != NULL && *cp != '\0')
2198 ce->ce_file = add (cp, ce->ce_file);
2200 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2201 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2205 if (ct->c_type == CT_MULTIPART) {
2207 CI ci = &ct->c_ctinfo;
2210 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2211 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2212 + 1 + strlen (ci->ci_subtype);
2213 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2214 putc (';', ce->ce_fp);
2217 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2219 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2220 fputs ("\n\t", ce->ce_fp);
2223 putc (' ', ce->ce_fp);
2226 fprintf (ce->ce_fp, "%s", buffer);
2230 if (ci->ci_comment) {
2231 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2232 fputs ("\n\t", ce->ce_fp);
2236 putc (' ', ce->ce_fp);
2239 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2242 fprintf (ce->ce_fp, "\n");
2244 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2246 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2248 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2249 fprintf (ce->ce_fp, "\n");
2252 if ((len = ct->c_end - ct->c_begin) < 0)
2253 adios (NULL, "internal error(3)");
2255 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2256 content_error (ct->c_file, ct, "unable to open for reading");
2260 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2262 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2264 content_error (ct->c_file, ct, "error reading from");
2268 content_error (NULL, ct, "premature eof");
2276 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2277 if (ferror (ce->ce_fp)) {
2278 content_error (ce->ce_file, ct, "error writing to");
2283 fseek (ct->c_fp, 0L, SEEK_SET);
2285 if (fflush (ce->ce_fp)) {
2286 content_error (ce->ce_file, ct, "error writing to");
2290 fseek (ce->ce_fp, 0L, SEEK_SET);
2293 *file = ce->ce_file;
2294 return fileno (ce->ce_fp);
2297 free_encoding (ct, 0);
2307 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2309 char cachefile[BUFSIZ];
2312 fseek (ce->ce_fp, 0L, SEEK_SET);
2317 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2318 content_error (ce->ce_file, ct, "unable to fopen for reading");
2324 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2325 cachefile, sizeof(cachefile)) != NOTOK) {
2326 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2327 ce->ce_file = getcpy (cachefile);
2331 admonish (cachefile, "unable to fopen for reading");
2338 *file = ce->ce_file;
2339 *fd = fileno (ce->ce_fp);
2350 return init_encoding (ct, openFile);
2355 openFile (CT ct, char **file)
2358 char cachefile[BUFSIZ];
2359 struct exbody *e = ct->c_ctexbody;
2360 CE ce = ct->c_cefile;
2362 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2374 content_error (NULL, ct, "missing name parameter");
2378 ce->ce_file = getcpy (e->eb_name);
2381 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2382 content_error (ce->ce_file, ct, "unable to fopen for reading");
2386 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2387 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2388 cachefile, sizeof(cachefile)) != NOTOK) {
2392 mask = umask (cachetype ? ~m_gmprot () : 0222);
2393 if ((fp = fopen (cachefile, "w"))) {
2395 char buffer[BUFSIZ];
2396 FILE *gp = ce->ce_fp;
2398 fseek (gp, 0L, SEEK_SET);
2400 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2402 fwrite (buffer, sizeof(*buffer), cc, fp);
2406 admonish (ce->ce_file, "error reading");
2411 admonish (cachefile, "error writing");
2419 fseek (ce->ce_fp, 0L, SEEK_SET);
2420 *file = ce->ce_file;
2421 return fileno (ce->ce_fp);
2431 return init_encoding (ct, openFTP);
2436 openFTP (CT ct, char **file)
2438 int cachetype, caching, fd;
2440 char *bp, *ftp, *user, *pass;
2441 char buffer[BUFSIZ], cachefile[BUFSIZ];
2444 static char *username = NULL;
2445 static char *password = NULL;
2450 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2458 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2469 if (!e->eb_name || !e->eb_site) {
2470 content_error (NULL, ct, "missing %s parameter",
2471 e->eb_name ? "site": "name");
2478 pidcheck (pidwait (xpid, NOTOK));
2482 /* Get the buffer ready to go */
2484 buflen = sizeof(buffer);
2487 * Construct the query message for user
2489 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2495 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2501 snprintf (bp, buflen, "\n using %sFTP from site %s",
2502 e->eb_flags ? "anonymous " : "", e->eb_site);
2507 if (e->eb_size > 0) {
2508 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2513 snprintf (bp, buflen, "? ");
2516 * Now, check the answer
2518 if (!getanswer (buffer))
2523 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2526 ruserpass (e->eb_site, &username, &password);
2531 ce->ce_unlink = (*file == NULL);
2533 cachefile[0] = '\0';
2534 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2535 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2536 cachefile, sizeof(cachefile)) != NOTOK) {
2537 if (*file == NULL) {
2544 ce->ce_file = add (*file, NULL);
2546 ce->ce_file = add (cachefile, NULL);
2548 ce->ce_file = add (m_scratch ("", tmp), NULL);
2550 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2551 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2559 int child_id, i, vecp;
2563 vec[vecp++] = r1bindex (ftp, '/');
2564 vec[vecp++] = e->eb_site;
2567 vec[vecp++] = e->eb_dir;
2568 vec[vecp++] = e->eb_name;
2569 vec[vecp++] = ce->ce_file,
2570 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2571 ? "ascii" : "binary";
2576 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2580 adios ("fork", "unable to");
2584 close (fileno (ce->ce_fp));
2586 fprintf (stderr, "unable to exec ");
2592 if (pidXwait (child_id, NULL)) {
2596 username = password = NULL;
2605 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2607 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2614 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2619 mask = umask (cachetype ? ~m_gmprot () : 0222);
2620 if ((fp = fopen (cachefile, "w"))) {
2622 FILE *gp = ce->ce_fp;
2624 fseek (gp, 0L, SEEK_SET);
2626 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2628 fwrite (buffer, sizeof(*buffer), cc, fp);
2632 admonish (ce->ce_file, "error reading");
2637 admonish (cachefile, "error writing");
2646 fseek (ce->ce_fp, 0L, SEEK_SET);
2647 *file = ce->ce_file;
2648 return fileno (ce->ce_fp);
2659 return init_encoding (ct, openMail);
2664 openMail (CT ct, char **file)
2666 int child_id, fd, i, vecp;
2668 char *bp, buffer[BUFSIZ], *vec[7];
2669 struct exbody *e = ct->c_ctexbody;
2670 CE ce = ct->c_cefile;
2672 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2683 if (!e->eb_server) {
2684 content_error (NULL, ct, "missing server parameter");
2691 pidcheck (pidwait (xpid, NOTOK));
2695 /* Get buffer ready to go */
2697 buflen = sizeof(buffer);
2699 /* Now, construct query message */
2700 snprintf (bp, buflen, "Retrieve content");
2706 snprintf (bp, buflen, " %s", e->eb_partno);
2712 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2714 e->eb_subject ? e->eb_subject : e->eb_body);
2716 /* Now, check answer */
2717 if (!getanswer (buffer))
2721 vec[vecp++] = r1bindex (mailproc, '/');
2722 vec[vecp++] = e->eb_server;
2723 vec[vecp++] = "-subject";
2724 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2725 vec[vecp++] = "-body";
2726 vec[vecp++] = e->eb_body;
2729 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2733 advise ("fork", "unable to");
2737 execvp (mailproc, vec);
2738 fprintf (stderr, "unable to exec ");
2744 if (pidXwait (child_id, NULL) == OK)
2745 advise (NULL, "request sent");
2749 if (*file == NULL) {
2750 ce->ce_file = add (m_scratch ("", tmp), NULL);
2753 ce->ce_file = add (*file, NULL);
2757 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2758 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2762 /* showproc is for mhshow and mhstore, though mhlist -debug
2763 * prints it, too. */
2765 free (ct->c_showproc);
2766 ct->c_showproc = add ("true", NULL);
2768 fseek (ce->ce_fp, 0L, SEEK_SET);
2769 *file = ce->ce_file;
2770 return fileno (ce->ce_fp);
2775 readDigest (CT ct, char *cp)
2780 unsigned char *dp, value, *ep;
2781 unsigned char *b, *b1, *b2, *b3;
2783 b = (unsigned char *) &bits,
2784 b1 = &b[endian > 0 ? 1 : 2],
2785 b2 = &b[endian > 0 ? 2 : 1],
2786 b3 = &b[endian > 0 ? 3 : 0];
2791 for (ep = (dp = ct->c_digest)
2792 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2797 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2799 fprintf (stderr, "invalid BASE64 encoding\n");
2803 bits |= value << bitno;
2805 if ((bitno -= 6) < 0) {
2806 if (dp + (3 - skip) > ep)
2807 goto invalid_digest;
2822 goto self_delimiting;
2827 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2837 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2845 fprintf (stderr, "MD5 digest=");
2846 for (dp = ct->c_digest; dp < ep; dp++)
2847 fprintf (stderr, "%02x", *dp & 0xff);
2848 fprintf (stderr, "\n");