3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern pid_t xpid; /* mhshowsbr.c */
28 extern int rcachesw; /* mhcachesbr.c */
29 extern int wcachesw; /* mhcachesbr.c */
31 int checksw = 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * Structures for TEXT messages
42 struct k2v SubText[] = {
43 { "plain", TEXT_PLAIN },
44 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
45 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
46 { NULL, TEXT_UNKNOWN } /* this one must be last! */
49 /* Charset[] removed -- yozo. Mon Oct 8 01:03:41 JST 2012 */
52 * Structures for MULTIPART messages
54 struct k2v SubMultiPart[] = {
55 { "mixed", MULTI_MIXED },
56 { "alternative", MULTI_ALTERNATE },
57 { "digest", MULTI_DIGEST },
58 { "parallel", MULTI_PARALLEL },
59 { NULL, MULTI_UNKNOWN } /* this one must be last! */
63 * Structures for MESSAGE messages
65 struct k2v SubMessage[] = {
66 { "rfc822", MESSAGE_RFC822 },
67 { "partial", MESSAGE_PARTIAL },
68 { "external-body", MESSAGE_EXTERNAL },
69 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
73 * Structure for APPLICATION messages
75 struct k2v SubApplication[] = {
76 { "octet-stream", APPLICATION_OCTETS },
77 { "postscript", APPLICATION_POSTSCRIPT },
78 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
83 int find_cache (CT, int, int *, char *, char *, int);
86 int part_ok (CT, int);
87 int type_ok (CT, int);
88 void content_error (char *, CT, char *, ...);
91 void free_content (CT);
92 void free_encoding (CT, int);
97 static CT get_content (FILE *, char *, int);
98 static int get_comment (CT, unsigned char **, int);
100 static int InitGeneric (CT);
101 static int InitText (CT);
102 static int InitMultiPart (CT);
103 static void reverse_parts (CT);
104 static int InitMessage (CT);
105 static int InitApplication (CT);
106 static int init_encoding (CT, OpenCEFunc);
107 static unsigned long size_encoding (CT);
108 static int InitBase64 (CT);
109 static int openBase64 (CT, char **);
110 static int InitQuoted (CT);
111 static int openQuoted (CT, char **);
112 static int Init7Bit (CT);
113 static int openExternal (CT, CT, CE, char **, int *);
114 static int InitFile (CT);
115 static int openFile (CT, char **);
116 static int InitFTP (CT);
117 static int openFTP (CT, char **);
118 static int InitMail (CT);
119 static int openMail (CT, char **);
120 static int readDigest (CT, char *);
122 struct str2init str2cts[] = {
123 { "application", CT_APPLICATION, InitApplication },
124 { "audio", CT_AUDIO, InitGeneric },
125 { "image", CT_IMAGE, InitGeneric },
126 { "message", CT_MESSAGE, InitMessage },
127 { "multipart", CT_MULTIPART, InitMultiPart },
128 { "text", CT_TEXT, InitText },
129 { "video", CT_VIDEO, InitGeneric },
130 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
131 { NULL, CT_UNKNOWN, NULL },
134 struct str2init str2ces[] = {
135 { "base64", CE_BASE64, InitBase64 },
136 { "quoted-printable", CE_QUOTED, InitQuoted },
137 { "8bit", CE_8BIT, Init7Bit },
138 { "7bit", CE_7BIT, Init7Bit },
139 { "binary", CE_BINARY, Init7Bit },
140 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
141 { NULL, CE_UNKNOWN, NULL },
145 * NOTE WELL: si_key MUST NOT have value of NOTOK
147 * si_key is 1 if access method is anonymous.
149 struct str2init str2methods[] = {
150 { "afs", 1, InitFile },
151 { "anon-ftp", 1, InitFTP },
152 { "ftp", 0, InitFTP },
153 { "local-file", 0, InitFile },
154 { "mail-server", 0, InitMail },
160 pidcheck (int status)
162 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
173 * Main entry point for parsing a MIME message or file.
174 * It returns the Content structure for the top level
175 * entity in the file.
179 parse_mime (char *file)
187 * Check if file is actually standard input
189 if ((is_stdin = !(strcmp (file, "-")))) {
190 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
192 advise("mhparse", "unable to create temporary file");
195 file = add (tfile, NULL);
198 while (fgets (buffer, sizeof(buffer), stdin))
202 if (ferror (stdin)) {
204 advise ("stdin", "error reading");
209 advise (file, "error writing");
212 fseek (fp, 0L, SEEK_SET);
213 } else if ((fp = fopen (file, "r")) == NULL) {
214 advise (file, "unable to read");
218 if (!(ct = get_content (fp, file, 1))) {
221 advise (NULL, "unable to decode %s", file);
226 ct->c_unlink = 1; /* temp file to remove */
230 if (ct->c_end == 0L) {
231 fseek (fp, 0L, SEEK_END);
232 ct->c_end = ftell (fp);
235 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
247 * Main routine for reading/parsing the headers
248 * of a message content.
250 * toplevel = 1 # we are at the top level of the message
251 * toplevel = 0 # we are inside message type or multipart type
252 * # other than multipart/digest
253 * toplevel = -1 # we are inside multipart/digest
254 * NB: on failure we will fclose(in)!
258 get_content (FILE *in, char *file, int toplevel)
261 char buf[BUFSIZ], name[NAMESZ];
266 /* allocate the content structure */
267 if (!(ct = (CT) calloc (1, sizeof(*ct))))
268 adios (NULL, "out of memory");
271 ct->c_file = add (file, NULL);
272 ct->c_begin = ftell (ct->c_fp) + 1;
275 * Parse the header fields for this
276 * content into a linked list.
278 for (compnum = 1, state = FLD;;) {
279 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
285 /* get copies of the buffers */
286 np = add (name, NULL);
287 vp = add (buf, NULL);
289 /* if necessary, get rest of field */
290 while (state == FLDPLUS) {
291 state = m_getfld (state, name, buf, sizeof(buf), in);
292 vp = add (buf, vp); /* add to previous value */
295 /* Now add the header data to the list */
296 add_header (ct, np, vp);
298 /* continue, if this isn't the last header field */
299 if (state != FLDEOF) {
300 ct->c_begin = ftell (in) + 1;
307 ct->c_begin = ftell (in) - strlen (buf);
311 ct->c_begin = ftell (in);
316 adios (NULL, "message format error in component #%d", compnum);
319 adios (NULL, "getfld() returned %d", state);
322 /* break out of the loop */
327 * Read the content headers. We will parse the
328 * MIME related header fields into their various
329 * structures and set internal flags related to
330 * content type/subtype, etc.
333 hp = ct->c_first_hf; /* start at first header field */
335 /* Get MIME-Version field */
336 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
339 unsigned char *cp, *dp;
342 advise (NULL, "message %s has multiple %s: fields",
343 ct->c_file, VRSN_FIELD);
346 ct->c_vrsn = add (hp->value, NULL);
348 /* Now, cleanup this field */
351 while (isspace (*cp))
353 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
355 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
360 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
362 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
365 for (dp = cp; istoken (*dp); dp++)
369 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
372 admonish (NULL, "message %s has unknown value for %s: field (%s)",
373 ct->c_file, VRSN_FIELD, cp);
376 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
377 /* Get Content-Type field */
378 struct str2init *s2i;
379 CI ci = &ct->c_ctinfo;
381 /* Check if we've already seen a Content-Type header */
383 advise (NULL, "message %s has multiple %s: fields",
384 ct->c_file, TYPE_FIELD);
388 /* Parse the Content-Type field */
389 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
393 * Set the Init function and the internal
394 * flag for this content type.
396 for (s2i = str2cts; s2i->si_key; s2i++)
397 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
399 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
401 ct->c_type = s2i->si_val;
402 ct->c_ctinitfnx = s2i->si_init;
404 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
405 /* Get Content-Transfer-Encoding field */
407 unsigned char *cp, *dp;
408 struct str2init *s2i;
411 * Check if we've already seen the
412 * Content-Transfer-Encoding field
415 advise (NULL, "message %s has multiple %s: fields",
416 ct->c_file, ENCODING_FIELD);
420 /* get copy of this field */
421 ct->c_celine = cp = add (hp->value, NULL);
423 while (isspace (*cp))
425 for (dp = cp; istoken (*dp); dp++)
431 * Find the internal flag and Init function
432 * for this transfer encoding.
434 for (s2i = str2ces; s2i->si_key; s2i++)
435 if (!mh_strcasecmp (cp, s2i->si_key))
437 if (!s2i->si_key && !uprf (cp, "X-"))
440 ct->c_encoding = s2i->si_val;
442 /* Call the Init function for this encoding */
443 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
446 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
447 /* Get Content-MD5 field */
448 unsigned char *cp, *dp;
454 if (ct->c_digested) {
455 advise (NULL, "message %s has multiple %s: fields",
456 ct->c_file, MD5_FIELD);
460 ep = cp = add (hp->value, NULL); /* get a copy */
462 while (isspace (*cp))
464 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
466 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
471 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
473 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
478 for (dp = cp; *dp && !isspace (*dp); dp++)
486 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
487 /* Get Content-ID field */
488 ct->c_id = add (hp->value, ct->c_id);
490 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
491 /* Get Content-Description field */
492 ct->c_descr = add (hp->value, ct->c_descr);
494 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
495 /* Get Content-Disposition field */
496 ct->c_dispo = add (hp->value, ct->c_dispo);
500 hp = hp->next; /* next header field */
504 * Check if we saw a Content-Type field.
505 * If not, then assign a default value for
506 * it, and the Init function.
510 * If we are inside a multipart/digest message,
511 * so default type is message/rfc822
514 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
516 ct->c_type = CT_MESSAGE;
517 ct->c_ctinitfnx = InitMessage;
520 * Else default type is text/plain
522 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
524 ct->c_type = CT_TEXT;
525 ct->c_ctinitfnx = InitText;
529 /* Use default Transfer-Encoding, if necessary */
531 ct->c_encoding = CE_7BIT;
544 * small routine to add header field to list
548 add_header (CT ct, char *name, char *value)
552 /* allocate header field structure */
553 hp = mh_xmalloc (sizeof(*hp));
555 /* link data into header structure */
560 /* link header structure into the list */
561 if (ct->c_first_hf == NULL) {
562 ct->c_first_hf = hp; /* this is the first */
565 ct->c_last_hf->next = hp; /* add it to the end */
573 /* Make sure that buf contains at least one appearance of name,
574 followed by =. If not, insert both name and value, just after
575 first semicolon, if any. Note that name should not contain a
576 trailing =. And quotes will be added around the value. Typical
577 usage: make sure that a Content-Disposition header contains
578 filename="foo". If it doesn't and value does, use value from
581 incl_name_value (unsigned char *buf, char *name, char *value) {
584 /* Assume that name is non-null. */
586 char *name_plus_equal = concat (name, "=", NULL);
588 if (! strstr (buf, name_plus_equal)) {
591 char *prefix, *suffix;
593 /* Trim trailing space, esp. newline. */
594 for (cp = &buf[strlen (buf) - 1];
595 cp >= buf && isspace (*cp);
600 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
602 /* Insert at first semicolon, if any. If none, append to
604 prefix = add (buf, NULL);
605 if ((cp = strchr (prefix, ';'))) {
606 suffix = concat (cp, NULL);
608 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
612 newbuf = concat (buf, insertion, "\n", NULL);
620 free (name_plus_equal);
626 /* Extract just name_suffix="foo", if any, from value. If there isn't
627 one, return the entire value. Note that, for example, a name_suffix
628 of name will match filename="foo", and return foo. */
630 extract_name_value (char *name_suffix, char *value) {
631 char *extracted_name_value = value;
632 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
633 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
636 free (name_suffix_plus_quote);
637 if (name_suffix_equals) {
638 char *name_suffix_begin;
641 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
642 name_suffix_begin = ++cp;
643 /* Find second \". */
644 for (; *cp != '"'; ++cp) /* empty */;
646 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
647 memcpy (extracted_name_value,
649 cp - name_suffix_begin);
650 extracted_name_value[cp - name_suffix_begin] = '\0';
653 return extracted_name_value;
657 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
658 * directives. Fills in the information of the CTinfo structure.
661 get_ctinfo (unsigned char *cp, CT ct, int magic)
670 i = strlen (invo_name) + 2;
672 /* store copy of Content-Type line */
673 cp = ct->c_ctline = add (cp, NULL);
675 while (isspace (*cp)) /* trim leading spaces */
678 /* change newlines to spaces */
679 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
682 /* trim trailing spaces */
683 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
689 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
691 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
694 for (dp = cp; istoken (*dp); dp++)
697 ci->ci_type = add (cp, NULL); /* store content type */
701 advise (NULL, "invalid %s: field in message %s (empty type)",
702 TYPE_FIELD, ct->c_file);
706 /* down case the content type string */
707 for (dp = ci->ci_type; *dp; dp++)
708 if (isalpha(*dp) && isupper (*dp))
711 while (isspace (*cp))
714 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
719 ci->ci_subtype = add ("", NULL);
724 while (isspace (*cp))
727 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
730 for (dp = cp; istoken (*dp); dp++)
733 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
736 if (!*ci->ci_subtype) {
738 "invalid %s: field in message %s (empty subtype for \"%s\")",
739 TYPE_FIELD, ct->c_file, ci->ci_type);
743 /* down case the content subtype string */
744 for (dp = ci->ci_subtype; *dp; dp++)
745 if (isalpha(*dp) && isupper (*dp))
749 while (isspace (*cp))
752 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
756 * Parse attribute/value pairs given with Content-Type
758 ep = (ap = ci->ci_attrs) + NPARMS;
765 "too many parameters in message %s's %s: field (%d max)",
766 ct->c_file, TYPE_FIELD, NPARMS);
771 while (isspace (*cp))
774 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
779 "extraneous trailing ';' in message %s's %s: parameter list",
780 ct->c_file, TYPE_FIELD);
784 /* down case the attribute name */
785 for (dp = cp; istoken (*dp); dp++)
786 if (isalpha(*dp) && isupper (*dp))
789 for (up = dp; isspace (*dp);)
791 if (dp == cp || *dp != '=') {
793 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
794 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
798 vp = (*ap = add (cp, NULL)) + (up - cp);
800 for (dp++; isspace (*dp);)
803 /* now add the attribute value */
804 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
807 for (cp = ++dp, dp = vp;;) {
812 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
813 ct->c_file, TYPE_FIELD, i, i, "", *ap);
818 if ((c = *cp++) == '\0')
833 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
839 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
840 ct->c_file, TYPE_FIELD, i, i, "", *ap);
845 while (isspace (*cp))
848 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
853 * Get any <Content-Id> given in buffer
855 if (magic && *cp == '<') {
860 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
861 advise (NULL, "invalid ID in message %s", ct->c_file);
867 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
873 while (isspace (*cp))
878 * Get any [Content-Description] given in buffer.
880 if (magic && *cp == '[') {
882 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
886 advise (NULL, "invalid description in message %s", ct->c_file);
894 ct->c_descr = concat (ct->c_descr, "\n", NULL);
900 while (isspace (*cp))
905 * Get any {Content-Disposition} given in buffer.
907 if (magic && *cp == '{') {
909 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
913 advise (NULL, "invalid disposition in message %s", ct->c_file);
921 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
927 while (isspace (*cp))
932 * Check if anything is left over
936 ci->ci_magic = add (cp, NULL);
938 /* If there is a Content-Disposition header and it doesn't
939 have a *filename=, extract it from the magic contents.
940 The r1bindex call skips any leading directory
944 incl_name_value (ct->c_dispo,
946 r1bindex (extract_name_value ("name",
953 "extraneous information in message %s's %s: field\n%*.*s(%s)",
954 ct->c_file, TYPE_FIELD, i, i, "", cp);
962 get_comment (CT ct, unsigned char **ap, int istype)
967 char c, buffer[BUFSIZ], *dp;
979 advise (NULL, "invalid comment in message %s's %s: field",
980 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
985 if ((c = *cp++) == '\0')
1008 if ((dp = ci->ci_comment)) {
1009 ci->ci_comment = concat (dp, " ", buffer, NULL);
1012 ci->ci_comment = add (buffer, NULL);
1016 while (isspace (*cp))
1027 * Handles content types audio, image, and video.
1028 * There's not much to do right here.
1036 return OK; /* not much to do here */
1047 char buffer[BUFSIZ];
1049 char **ap, **ep, *cp;
1052 CI ci = &ct->c_ctinfo;
1054 /* check for missing subtype */
1055 if (!*ci->ci_subtype)
1056 ci->ci_subtype = add ("plain", ci->ci_subtype);
1059 for (kv = SubText; kv->kv_key; kv++)
1060 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1062 ct->c_subtype = kv->kv_value;
1064 /* allocate text character set structure */
1065 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1066 adios (NULL, "out of memory");
1067 ct->c_ctparams = (void *) t;
1069 /* scan for charset parameter */
1070 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1071 if (!mh_strcasecmp (*ap, "charset"))
1074 /* check if content specified a character set */
1077 t->tx_charset = CHARSET_SPECIFIED;
1079 t->tx_charset = CHARSET_UNSPECIFIED;
1083 * If we can not handle character set natively,
1084 * then check profile for string to modify the
1085 * terminal or display method.
1087 * termproc is for mhshow, though mhlist -debug prints it, too.
1089 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1090 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1091 if ((cp = context_find (buffer)))
1092 ct->c_termproc = getcpy (cp);
1104 InitMultiPart (CT ct)
1108 unsigned char *cp, *dp;
1110 char *bp, buffer[BUFSIZ];
1111 struct multipart *m;
1113 struct part *part, **next;
1114 CI ci = &ct->c_ctinfo;
1119 * The encoding for multipart messages must be either
1120 * 7bit, 8bit, or binary (per RFC2045).
1122 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1123 && ct->c_encoding != CE_BINARY) {
1125 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1126 ci->ci_type, ci->ci_subtype, ct->c_file);
1131 for (kv = SubMultiPart; kv->kv_key; kv++)
1132 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1134 ct->c_subtype = kv->kv_value;
1137 * Check for "boundary" parameter, which is
1138 * required for multipart messages.
1141 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1142 if (!mh_strcasecmp (*ap, "boundary")) {
1148 /* complain if boundary parameter is missing */
1151 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1152 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1156 /* allocate primary structure for multipart info */
1157 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1158 adios (NULL, "out of memory");
1159 ct->c_ctparams = (void *) m;
1161 /* check if boundary parameter contains only whitespace characters */
1162 for (cp = bp; isspace (*cp); cp++)
1165 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1166 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1170 /* remove trailing whitespace from boundary parameter */
1171 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1176 /* record boundary separators */
1177 m->mp_start = concat (bp, "\n", NULL);
1178 m->mp_stop = concat (bp, "--\n", NULL);
1180 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1181 advise (ct->c_file, "unable to open for reading");
1185 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1187 next = &m->mp_parts;
1191 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1195 pos += strlen (buffer);
1196 if (buffer[0] != '-' || buffer[1] != '-')
1199 if (strcmp (buffer + 2, m->mp_start))
1202 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1203 adios (NULL, "out of memory");
1205 next = &part->mp_next;
1207 if (!(p = get_content (fp, ct->c_file,
1208 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1215 fseek (fp, pos, SEEK_SET);
1218 if (strcmp (buffer + 2, m->mp_start) == 0) {
1222 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1223 if (p->c_end < p->c_begin)
1224 p->c_begin = p->c_end;
1229 if (strcmp (buffer + 2, m->mp_stop) == 0)
1235 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1236 if (!inout && part) {
1238 p->c_end = ct->c_end;
1240 if (p->c_begin >= p->c_end) {
1241 for (next = &m->mp_parts; *next != part;
1242 next = &((*next)->mp_next))
1246 free ((char *) part);
1251 /* reverse the order of the parts for multipart/alternative */
1252 if (ct->c_subtype == MULTI_ALTERNATE)
1256 * label all subparts with part number, and
1257 * then initialize the content of the subpart.
1262 char partnam[BUFSIZ];
1265 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1266 pp = partnam + strlen (partnam);
1271 for (part = m->mp_parts, partnum = 1; part;
1272 part = part->mp_next, partnum++) {
1275 sprintf (pp, "%d", partnum);
1276 p->c_partno = add (partnam, NULL);
1278 /* initialize the content of the subparts */
1279 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1294 * reverse the order of the parts of a multipart
1298 reverse_parts (CT ct)
1301 struct multipart *m;
1302 struct part **base, **bmp, **next, *part;
1304 m = (struct multipart *) ct->c_ctparams;
1306 /* if only one part, just return */
1307 if (!m->mp_parts || !m->mp_parts->mp_next)
1310 /* count number of parts */
1312 for (part = m->mp_parts; part; part = part->mp_next)
1315 /* allocate array of pointers to the parts */
1316 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1317 adios (NULL, "out of memory");
1320 /* point at all the parts */
1321 for (part = m->mp_parts; part; part = part->mp_next)
1325 /* reverse the order of the parts */
1326 next = &m->mp_parts;
1327 for (bmp--; bmp >= base; bmp--) {
1330 next = &part->mp_next;
1334 /* free array of pointers */
1335 free ((char *) base);
1347 CI ci = &ct->c_ctinfo;
1349 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1351 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1352 ci->ci_type, ci->ci_subtype, ct->c_file);
1356 /* check for missing subtype */
1357 if (!*ci->ci_subtype)
1358 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1361 for (kv = SubMessage; kv->kv_key; kv++)
1362 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1364 ct->c_subtype = kv->kv_value;
1366 switch (ct->c_subtype) {
1367 case MESSAGE_RFC822:
1370 case MESSAGE_PARTIAL:
1375 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1376 adios (NULL, "out of memory");
1377 ct->c_ctparams = (void *) p;
1379 /* scan for parameters "id", "number", and "total" */
1380 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1381 if (!mh_strcasecmp (*ap, "id")) {
1382 p->pm_partid = add (*ep, NULL);
1385 if (!mh_strcasecmp (*ap, "number")) {
1386 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1387 || p->pm_partno < 1) {
1390 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1391 *ap, ci->ci_type, ci->ci_subtype,
1392 ct->c_file, TYPE_FIELD);
1397 if (!mh_strcasecmp (*ap, "total")) {
1398 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1407 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1409 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1410 ci->ci_type, ci->ci_subtype,
1411 ct->c_file, TYPE_FIELD);
1417 case MESSAGE_EXTERNAL:
1424 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1425 adios (NULL, "out of memory");
1426 ct->c_ctparams = (void *) e;
1429 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1430 advise (ct->c_file, "unable to open for reading");
1434 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1436 if (!(p = get_content (fp, ct->c_file, 0))) {
1444 if ((exresult = params_external (ct, 0)) != NOTOK
1445 && p->c_ceopenfnx == openMail) {
1449 if ((size = ct->c_end - p->c_begin) <= 0) {
1451 content_error (NULL, ct,
1452 "empty body for access-type=mail-server");
1456 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1457 fseek (p->c_fp, p->c_begin, SEEK_SET);
1459 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1461 adios ("failed", "fread");
1464 adios (NULL, "unexpected EOF from fread");
1467 bp += cc, size -= cc;
1474 p->c_end = p->c_begin;
1479 if (exresult == NOTOK)
1481 if (e->eb_flags == NOTOK)
1484 switch (p->c_type) {
1489 if (p->c_subtype != MESSAGE_RFC822)
1493 e->eb_partno = ct->c_partno;
1495 (*p->c_ctinitfnx) (p);
1510 params_external (CT ct, int composing)
1513 struct exbody *e = (struct exbody *) ct->c_ctparams;
1514 CI ci = &ct->c_ctinfo;
1516 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1517 if (!mh_strcasecmp (*ap, "access-type")) {
1518 struct str2init *s2i;
1519 CT p = e->eb_content;
1521 for (s2i = str2methods; s2i->si_key; s2i++)
1522 if (!mh_strcasecmp (*ep, s2i->si_key))
1526 e->eb_flags = NOTOK;
1527 p->c_encoding = CE_EXTERNAL;
1530 e->eb_access = s2i->si_key;
1531 e->eb_flags = s2i->si_val;
1532 p->c_encoding = CE_EXTERNAL;
1534 /* Call the Init function for this external type */
1535 if ((*s2i->si_init)(p) == NOTOK)
1539 if (!mh_strcasecmp (*ap, "name")) {
1543 if (!mh_strcasecmp (*ap, "permission")) {
1544 e->eb_permission = *ep;
1547 if (!mh_strcasecmp (*ap, "site")) {
1551 if (!mh_strcasecmp (*ap, "directory")) {
1555 if (!mh_strcasecmp (*ap, "mode")) {
1559 if (!mh_strcasecmp (*ap, "size")) {
1560 sscanf (*ep, "%lu", &e->eb_size);
1563 if (!mh_strcasecmp (*ap, "server")) {
1567 if (!mh_strcasecmp (*ap, "subject")) {
1568 e->eb_subject = *ep;
1571 if (composing && !mh_strcasecmp (*ap, "body")) {
1572 e->eb_body = getcpy (*ep);
1577 if (!e->eb_access) {
1579 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1580 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1593 InitApplication (CT ct)
1596 CI ci = &ct->c_ctinfo;
1599 for (kv = SubApplication; kv->kv_key; kv++)
1600 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1602 ct->c_subtype = kv->kv_value;
1609 * TRANSFER ENCODINGS
1613 init_encoding (CT ct, OpenCEFunc openfnx)
1617 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1618 adios (NULL, "out of memory");
1621 ct->c_ceopenfnx = openfnx;
1622 ct->c_ceclosefnx = close_encoding;
1623 ct->c_cesizefnx = size_encoding;
1630 close_encoding (CT ct)
1634 if (!(ce = ct->c_cefile))
1644 static unsigned long
1645 size_encoding (CT ct)
1653 if (!(ce = ct->c_cefile))
1654 return (ct->c_end - ct->c_begin);
1656 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1657 return (long) st.st_size;
1660 if (stat (ce->ce_file, &st) != NOTOK)
1661 return (long) st.st_size;
1666 if (ct->c_encoding == CE_EXTERNAL)
1667 return (ct->c_end - ct->c_begin);
1670 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1671 return (ct->c_end - ct->c_begin);
1673 if (fstat (fd, &st) != NOTOK)
1674 size = (long) st.st_size;
1678 (*ct->c_ceclosefnx) (ct);
1687 static unsigned char b642nib[0x80] = {
1688 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1689 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1690 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1691 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1692 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1693 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1694 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1695 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1696 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1697 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1698 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1699 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1700 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1701 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1702 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1703 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1710 return init_encoding (ct, openBase64);
1715 openBase64 (CT ct, char **file)
1717 int bitno, cc, digested;
1718 int fd, len, skip, own_ct_fp = 0;
1720 unsigned char value, b;
1721 unsigned char *cp, *ep;
1722 char buffer[BUFSIZ];
1723 /* sbeck -- handle suffixes */
1730 fseek (ce->ce_fp, 0L, SEEK_SET);
1735 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1736 content_error (ce->ce_file, ct, "unable to fopen for reading");
1742 if (*file == NULL) {
1743 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1746 ce->ce_file = add (*file, NULL);
1750 /* sbeck@cise.ufl.edu -- handle suffixes */
1752 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1753 invo_name, ci->ci_type, ci->ci_subtype);
1754 cp = context_find (buffer);
1755 if (cp == NULL || *cp == '\0') {
1756 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1758 cp = context_find (buffer);
1760 if (cp != NULL && *cp != '\0') {
1761 if (ce->ce_unlink) {
1762 /* Temporary file already exists, so we rename to
1763 version with extension. */
1764 char *file_org = strdup(ce->ce_file);
1765 ce->ce_file = add (cp, ce->ce_file);
1766 if (rename(file_org, ce->ce_file)) {
1767 adios (ce->ce_file, "unable to rename %s to ", file_org);
1772 ce->ce_file = add (cp, ce->ce_file);
1776 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1777 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1781 if ((len = ct->c_end - ct->c_begin) < 0)
1782 adios (NULL, "internal error(1)");
1785 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1786 content_error (ct->c_file, ct, "unable to open for reading");
1792 if ((digested = ct->c_digested))
1793 MD5Init (&mdContext);
1799 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1801 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1803 content_error (ct->c_file, ct, "error reading from");
1807 content_error (NULL, ct, "premature eof");
1815 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1820 if (skip || (*cp & 0x80)
1821 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1823 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1825 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1828 content_error (NULL, ct,
1829 "invalid BASE64 encoding -- continuing");
1833 bits |= value << bitno;
1835 if ((bitno -= 6) < 0) {
1836 b = (bits >> 16) & 0xff;
1837 putc ((char) b, ce->ce_fp);
1839 MD5Update (&mdContext, &b, 1);
1841 b = (bits >> 8) & 0xff;
1842 putc ((char) b, ce->ce_fp);
1844 MD5Update (&mdContext, &b, 1);
1847 putc ((char) b, ce->ce_fp);
1849 MD5Update (&mdContext, &b, 1);
1853 if (ferror (ce->ce_fp)) {
1854 content_error (ce->ce_file, ct,
1855 "error writing to");
1858 bitno = 18, bits = 0L, skip = 0;
1864 goto self_delimiting;
1873 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1875 content_error (NULL, ct, "invalid BASE64 encoding");
1880 fseek (ct->c_fp, 0L, SEEK_SET);
1882 if (fflush (ce->ce_fp)) {
1883 content_error (ce->ce_file, ct, "error writing to");
1888 unsigned char digest[16];
1890 MD5Final (digest, &mdContext);
1891 if (memcmp((char *) digest, (char *) ct->c_digest,
1892 sizeof(digest) / sizeof(digest[0])))
1893 content_error (NULL, ct,
1894 "content integrity suspect (digest mismatch) -- continuing");
1897 fprintf (stderr, "content integrity confirmed\n");
1900 fseek (ce->ce_fp, 0L, SEEK_SET);
1903 *file = ce->ce_file;
1908 return fileno (ce->ce_fp);
1915 free_encoding (ct, 0);
1924 static char hex2nib[0x80] = {
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, 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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1932 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1947 return init_encoding (ct, openQuoted);
1952 openQuoted (CT ct, char **file)
1954 int cc, digested, len, quoted, own_ct_fp = 0;
1955 unsigned char *cp, *ep;
1956 char buffer[BUFSIZ];
1959 /* sbeck -- handle suffixes */
1965 fseek (ce->ce_fp, 0L, SEEK_SET);
1970 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1971 content_error (ce->ce_file, ct, "unable to fopen for reading");
1977 if (*file == NULL) {
1978 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1981 ce->ce_file = add (*file, NULL);
1985 /* sbeck@cise.ufl.edu -- handle suffixes */
1987 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1988 invo_name, ci->ci_type, ci->ci_subtype);
1989 cp = context_find (buffer);
1990 if (cp == NULL || *cp == '\0') {
1991 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1993 cp = context_find (buffer);
1995 if (cp != NULL && *cp != '\0') {
1996 if (ce->ce_unlink) {
1997 /* Temporary file already exists, so we rename to
1998 version with extension. */
1999 char *file_org = strdup(ce->ce_file);
2000 ce->ce_file = add (cp, ce->ce_file);
2001 if (rename(file_org, ce->ce_file)) {
2002 adios (ce->ce_file, "unable to rename %s to ", file_org);
2007 ce->ce_file = add (cp, ce->ce_file);
2011 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2012 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2016 if ((len = ct->c_end - ct->c_begin) < 0)
2017 adios (NULL, "internal error(2)");
2020 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2021 content_error (ct->c_file, ct, "unable to open for reading");
2027 if ((digested = ct->c_digested))
2028 MD5Init (&mdContext);
2035 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2037 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2038 content_error (NULL, ct, "premature eof");
2042 if ((cc = strlen (buffer)) > len)
2046 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2051 for (; cp < ep; cp++) {
2053 /* in an escape sequence */
2055 /* at byte 1 of an escape sequence */
2056 mask = hex2nib[*cp & 0x7f];
2057 /* next is byte 2 */
2060 /* at byte 2 of an escape sequence */
2062 mask |= hex2nib[*cp & 0x7f];
2063 putc (mask, ce->ce_fp);
2065 MD5Update (&mdContext, &mask, 1);
2066 if (ferror (ce->ce_fp)) {
2067 content_error (ce->ce_file, ct, "error writing to");
2070 /* finished escape sequence; next may be literal or a new
2071 * escape sequence */
2074 /* on to next byte */
2078 /* not in an escape sequence */
2080 /* starting an escape sequence, or invalid '='? */
2081 if (cp + 1 < ep && cp[1] == '\n') {
2082 /* "=\n" soft line break, eat the \n */
2086 if (cp + 1 >= ep || cp + 2 >= ep) {
2087 /* We don't have 2 bytes left, so this is an invalid
2088 * escape sequence; just show the raw bytes (below). */
2089 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2090 /* Next 2 bytes are hex digits, making this a valid escape
2091 * sequence; let's decode it (above). */
2095 /* One or both of the next 2 is out of range, making this
2096 * an invalid escape sequence; just show the raw bytes
2101 /* Just show the raw byte. */
2102 putc (*cp, ce->ce_fp);
2105 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2107 MD5Update (&mdContext, (unsigned char *) cp, 1);
2110 if (ferror (ce->ce_fp)) {
2111 content_error (ce->ce_file, ct, "error writing to");
2117 content_error (NULL, ct,
2118 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2122 fseek (ct->c_fp, 0L, SEEK_SET);
2124 if (fflush (ce->ce_fp)) {
2125 content_error (ce->ce_file, ct, "error writing to");
2130 unsigned char digest[16];
2132 MD5Final (digest, &mdContext);
2133 if (memcmp((char *) digest, (char *) ct->c_digest,
2134 sizeof(digest) / sizeof(digest[0])))
2135 content_error (NULL, ct,
2136 "content integrity suspect (digest mismatch) -- continuing");
2139 fprintf (stderr, "content integrity confirmed\n");
2142 fseek (ce->ce_fp, 0L, SEEK_SET);
2145 *file = ce->ce_file;
2150 return fileno (ce->ce_fp);
2153 free_encoding (ct, 0);
2169 if (init_encoding (ct, open7Bit) == NOTOK)
2172 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2178 open7Bit (CT ct, char **file)
2180 int cc, fd, len, own_ct_fp = 0;
2181 char buffer[BUFSIZ];
2182 /* sbeck -- handle suffixes */
2189 fseek (ce->ce_fp, 0L, SEEK_SET);
2194 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2195 content_error (ce->ce_file, ct, "unable to fopen for reading");
2201 if (*file == NULL) {
2202 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2205 ce->ce_file = add (*file, NULL);
2209 /* sbeck@cise.ufl.edu -- handle suffixes */
2211 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2212 invo_name, ci->ci_type, ci->ci_subtype);
2213 cp = context_find (buffer);
2214 if (cp == NULL || *cp == '\0') {
2215 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2217 cp = context_find (buffer);
2219 if (cp != NULL && *cp != '\0') {
2220 if (ce->ce_unlink) {
2221 /* Temporary file already exists, so we rename to
2222 version with extension. */
2223 char *file_org = strdup(ce->ce_file);
2224 ce->ce_file = add (cp, ce->ce_file);
2225 if (rename(file_org, ce->ce_file)) {
2226 adios (ce->ce_file, "unable to rename %s to ", file_org);
2231 ce->ce_file = add (cp, ce->ce_file);
2235 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2236 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2240 if (ct->c_type == CT_MULTIPART) {
2242 CI ci = &ct->c_ctinfo;
2245 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2246 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2247 + 1 + strlen (ci->ci_subtype);
2248 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2249 putc (';', ce->ce_fp);
2252 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2254 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2255 fputs ("\n\t", ce->ce_fp);
2258 putc (' ', ce->ce_fp);
2261 fprintf (ce->ce_fp, "%s", buffer);
2265 if (ci->ci_comment) {
2266 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2267 fputs ("\n\t", ce->ce_fp);
2271 putc (' ', ce->ce_fp);
2274 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2277 fprintf (ce->ce_fp, "\n");
2279 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2281 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2283 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2284 fprintf (ce->ce_fp, "\n");
2287 if ((len = ct->c_end - ct->c_begin) < 0)
2288 adios (NULL, "internal error(3)");
2291 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2292 content_error (ct->c_file, ct, "unable to open for reading");
2298 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2300 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2302 content_error (ct->c_file, ct, "error reading from");
2306 content_error (NULL, ct, "premature eof");
2314 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2315 if (ferror (ce->ce_fp)) {
2316 content_error (ce->ce_file, ct, "error writing to");
2321 fseek (ct->c_fp, 0L, SEEK_SET);
2323 if (fflush (ce->ce_fp)) {
2324 content_error (ce->ce_file, ct, "error writing to");
2328 fseek (ce->ce_fp, 0L, SEEK_SET);
2331 *file = ce->ce_file;
2336 return fileno (ce->ce_fp);
2339 free_encoding (ct, 0);
2353 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2355 char cachefile[BUFSIZ];
2358 fseek (ce->ce_fp, 0L, SEEK_SET);
2363 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2364 content_error (ce->ce_file, ct, "unable to fopen for reading");
2370 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2371 cachefile, sizeof(cachefile)) != NOTOK) {
2372 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2373 ce->ce_file = getcpy (cachefile);
2377 admonish (cachefile, "unable to fopen for reading");
2384 *file = ce->ce_file;
2385 *fd = fileno (ce->ce_fp);
2396 return init_encoding (ct, openFile);
2401 openFile (CT ct, char **file)
2404 char cachefile[BUFSIZ];
2405 struct exbody *e = ct->c_ctexbody;
2406 CE ce = ct->c_cefile;
2408 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2420 content_error (NULL, ct, "missing name parameter");
2424 ce->ce_file = getcpy (e->eb_name);
2427 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2428 content_error (ce->ce_file, ct, "unable to fopen for reading");
2432 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2433 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2434 cachefile, sizeof(cachefile)) != NOTOK) {
2438 mask = umask (cachetype ? ~m_gmprot () : 0222);
2439 if ((fp = fopen (cachefile, "w"))) {
2441 char buffer[BUFSIZ];
2442 FILE *gp = ce->ce_fp;
2444 fseek (gp, 0L, SEEK_SET);
2446 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2448 fwrite (buffer, sizeof(*buffer), cc, fp);
2452 admonish (ce->ce_file, "error reading");
2457 admonish (cachefile, "error writing");
2465 fseek (ce->ce_fp, 0L, SEEK_SET);
2466 *file = ce->ce_file;
2467 return fileno (ce->ce_fp);
2477 return init_encoding (ct, openFTP);
2482 openFTP (CT ct, char **file)
2484 int cachetype, caching, fd;
2486 char *bp, *ftp, *user, *pass;
2487 char buffer[BUFSIZ], cachefile[BUFSIZ];
2490 static char *username = NULL;
2491 static char *password = NULL;
2496 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2502 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2513 if (!e->eb_name || !e->eb_site) {
2514 content_error (NULL, ct, "missing %s parameter",
2515 e->eb_name ? "site": "name");
2522 pidcheck (pidwait (xpid, NOTOK));
2526 /* Get the buffer ready to go */
2528 buflen = sizeof(buffer);
2531 * Construct the query message for user
2533 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2539 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2545 snprintf (bp, buflen, "\n using %sFTP from site %s",
2546 e->eb_flags ? "anonymous " : "", e->eb_site);
2551 if (e->eb_size > 0) {
2552 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2557 snprintf (bp, buflen, "? ");
2560 * Now, check the answer
2562 if (!getanswer (buffer))
2567 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2571 ruserpass (e->eb_site, &username, &password);
2576 ce->ce_unlink = (*file == NULL);
2578 cachefile[0] = '\0';
2579 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2580 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2581 cachefile, sizeof(cachefile)) != NOTOK) {
2582 if (*file == NULL) {
2589 ce->ce_file = add (*file, NULL);
2591 ce->ce_file = add (cachefile, NULL);
2593 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2595 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2596 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2601 int child_id, i, vecp;
2605 vec[vecp++] = r1bindex (ftp, '/');
2606 vec[vecp++] = e->eb_site;
2609 vec[vecp++] = e->eb_dir;
2610 vec[vecp++] = e->eb_name;
2611 vec[vecp++] = ce->ce_file,
2612 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2613 ? "ascii" : "binary";
2618 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2622 adios ("fork", "unable to");
2626 close (fileno (ce->ce_fp));
2628 fprintf (stderr, "unable to exec ");
2634 if (pidXwait (child_id, NULL)) {
2635 username = password = NULL;
2645 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2650 mask = umask (cachetype ? ~m_gmprot () : 0222);
2651 if ((fp = fopen (cachefile, "w"))) {
2653 FILE *gp = ce->ce_fp;
2655 fseek (gp, 0L, SEEK_SET);
2657 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2659 fwrite (buffer, sizeof(*buffer), cc, fp);
2663 admonish (ce->ce_file, "error reading");
2668 admonish (cachefile, "error writing");
2677 fseek (ce->ce_fp, 0L, SEEK_SET);
2678 *file = ce->ce_file;
2679 return fileno (ce->ce_fp);
2690 return init_encoding (ct, openMail);
2695 openMail (CT ct, char **file)
2697 int child_id, fd, i, vecp;
2699 char *bp, buffer[BUFSIZ], *vec[7];
2700 struct exbody *e = ct->c_ctexbody;
2701 CE ce = ct->c_cefile;
2703 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2714 if (!e->eb_server) {
2715 content_error (NULL, ct, "missing server parameter");
2722 pidcheck (pidwait (xpid, NOTOK));
2726 /* Get buffer ready to go */
2728 buflen = sizeof(buffer);
2730 /* Now, construct query message */
2731 snprintf (bp, buflen, "Retrieve content");
2737 snprintf (bp, buflen, " %s", e->eb_partno);
2743 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2745 e->eb_subject ? e->eb_subject : e->eb_body);
2747 /* Now, check answer */
2748 if (!getanswer (buffer))
2752 vec[vecp++] = r1bindex (mailproc, '/');
2753 vec[vecp++] = e->eb_server;
2754 vec[vecp++] = "-subject";
2755 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2756 vec[vecp++] = "-body";
2757 vec[vecp++] = e->eb_body;
2760 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2764 advise ("fork", "unable to");
2768 execvp (mailproc, vec);
2769 fprintf (stderr, "unable to exec ");
2775 if (pidXwait (child_id, NULL) == OK)
2776 advise (NULL, "request sent");
2780 if (*file == NULL) {
2781 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2784 ce->ce_file = add (*file, NULL);
2788 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2789 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2793 /* showproc is for mhshow and mhstore, though mhlist -debug
2794 * prints it, too. */
2796 free (ct->c_showproc);
2797 ct->c_showproc = add ("true", NULL);
2799 fseek (ce->ce_fp, 0L, SEEK_SET);
2800 *file = ce->ce_file;
2801 return fileno (ce->ce_fp);
2806 readDigest (CT ct, char *cp)
2811 unsigned char *dp, value, *ep;
2817 for (ep = (dp = ct->c_digest)
2818 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2823 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2825 fprintf (stderr, "invalid BASE64 encoding\n");
2829 bits |= value << bitno;
2831 if ((bitno -= 6) < 0) {
2832 if (dp + (3 - skip) > ep)
2833 goto invalid_digest;
2834 *dp++ = (bits >> 16) & 0xff;
2836 *dp++ = (bits >> 8) & 0xff;
2838 *dp++ = bits & 0xff;
2848 goto self_delimiting;
2853 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2863 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2871 fprintf (stderr, "MD5 digest=");
2872 for (dp = ct->c_digest; dp < ep; dp++)
2873 fprintf (stderr, "%02x", *dp & 0xff);
2874 fprintf (stderr, "\n");