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 * Structure for mapping types to their internal flags
57 * Structures for TEXT messages
59 static struct k2v SubText[] = {
60 { "plain", TEXT_PLAIN },
61 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
62 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
63 { NULL, TEXT_UNKNOWN } /* this one must be last! */
66 static struct k2v Charset[] = {
67 { "us-ascii", CHARSET_USASCII },
68 { "iso-8859-1", CHARSET_LATIN },
69 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
73 * Structures for MULTIPART messages
75 static struct k2v SubMultiPart[] = {
76 { "mixed", MULTI_MIXED },
77 { "alternative", MULTI_ALTERNATE },
78 { "digest", MULTI_DIGEST },
79 { "parallel", MULTI_PARALLEL },
80 { NULL, MULTI_UNKNOWN } /* this one must be last! */
84 * Structures for MESSAGE messages
86 static struct k2v SubMessage[] = {
87 { "rfc822", MESSAGE_RFC822 },
88 { "partial", MESSAGE_PARTIAL },
89 { "external-body", MESSAGE_EXTERNAL },
90 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
94 * Structure for APPLICATION messages
96 static struct k2v SubApplication[] = {
97 { "octet-stream", APPLICATION_OCTETS },
98 { "postscript", APPLICATION_POSTSCRIPT },
99 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
104 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
107 int find_cache (CT, int, int *, char *, char *, int);
110 int part_ok (CT, int);
111 int type_ok (CT, int);
112 int make_intermediates (char *);
113 void content_error (char *, CT, char *, ...);
116 void free_content (CT);
117 void free_encoding (CT, int);
123 CT parse_mime (char *);
128 static CT get_content (FILE *, char *, int);
129 static int add_header (CT, char *, char *);
130 static int get_ctinfo (unsigned char *, CT);
131 static int get_comment (CT, unsigned char **, int);
132 static int InitGeneric (CT);
133 static int InitText (CT);
134 static int InitMultiPart (CT);
135 static void reverse_parts (CT);
136 static int InitMessage (CT);
137 static int params_external (CT, int);
138 static int InitApplication (CT);
139 static int init_encoding (CT, OpenCEFunc);
140 static void close_encoding (CT);
141 static unsigned long size_encoding (CT);
142 static int InitBase64 (CT);
143 static int openBase64 (CT, char **);
144 static int InitQuoted (CT);
145 static int openQuoted (CT, char **);
146 static int Init7Bit (CT);
147 static int open7Bit (CT, char **);
148 static int openExternal (CT, CT, CE, char **, int *);
149 static int InitFile (CT);
150 static int openFile (CT, char **);
151 static int InitFTP (CT);
152 static int openFTP (CT, char **);
153 static int InitMail (CT);
154 static int openMail (CT, char **);
155 static int readDigest (CT, char *);
158 * Structures for mapping (content) types to
159 * the functions to handle them.
167 static struct str2init str2cts[] = {
168 { "application", CT_APPLICATION, InitApplication },
169 { "audio", CT_AUDIO, InitGeneric },
170 { "image", CT_IMAGE, InitGeneric },
171 { "message", CT_MESSAGE, InitMessage },
172 { "multipart", CT_MULTIPART, InitMultiPart },
173 { "text", CT_TEXT, InitText },
174 { "video", CT_VIDEO, InitGeneric },
175 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
176 { NULL, CT_UNKNOWN, NULL },
179 static struct str2init str2ces[] = {
180 { "base64", CE_BASE64, InitBase64 },
181 { "quoted-printable", CE_QUOTED, InitQuoted },
182 { "8bit", CE_8BIT, Init7Bit },
183 { "7bit", CE_7BIT, Init7Bit },
184 { "binary", CE_BINARY, Init7Bit },
185 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
186 { NULL, CE_UNKNOWN, NULL },
190 * NOTE WELL: si_key MUST NOT have value of NOTOK
192 * si_key is 1 if access method is anonymous.
194 static struct str2init str2methods[] = {
195 { "afs", 1, InitFile },
196 { "anon-ftp", 1, InitFTP },
197 { "ftp", 0, InitFTP },
198 { "local-file", 0, InitFile },
199 { "mail-server", 0, InitMail },
205 pidcheck (int status)
207 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
218 * Main entry point for parsing a MIME message or file.
219 * It returns the Content structure for the top level
220 * entity in the file.
224 parse_mime (char *file)
232 * Check if file is actually standard input
234 if ((is_stdin = !(strcmp (file, "-")))) {
235 file = add (m_tmpfil (invo_name), NULL);
236 if ((fp = fopen (file, "w+")) == NULL) {
237 advise (file, "unable to fopen for writing and reading");
241 while (fgets (buffer, sizeof(buffer), stdin))
245 if (ferror (stdin)) {
247 advise ("stdin", "error reading");
252 advise (file, "error writing");
255 fseek (fp, 0L, SEEK_SET);
256 } else if ((fp = fopen (file, "r")) == NULL) {
257 advise (file, "unable to read");
261 if (!(ct = get_content (fp, file, 1))) {
264 advise (NULL, "unable to decode %s", file);
269 ct->c_unlink = 1; /* temp file to remove */
273 if (ct->c_end == 0L) {
274 fseek (fp, 0L, SEEK_END);
275 ct->c_end = ftell (fp);
278 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
290 * Main routine for reading/parsing the headers
291 * of a message content.
293 * toplevel = 1 # we are at the top level of the message
294 * toplevel = 0 # we are inside message type or multipart type
295 * # other than multipart/digest
296 * toplevel = -1 # we are inside multipart/digest
297 * NB: on failure we will fclose(in)!
301 get_content (FILE *in, char *file, int toplevel)
304 char buf[BUFSIZ], name[NAMESZ];
309 /* allocate the content structure */
310 if (!(ct = (CT) calloc (1, sizeof(*ct))))
311 adios (NULL, "out of memory");
314 ct->c_file = add (file, NULL);
315 ct->c_begin = ftell (ct->c_fp) + 1;
318 * Parse the header fields for this
319 * content into a linked list.
321 for (compnum = 1, state = FLD;;) {
322 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
328 /* get copies of the buffers */
329 np = add (name, NULL);
330 vp = add (buf, NULL);
332 /* if necessary, get rest of field */
333 while (state == FLDPLUS) {
334 state = m_getfld (state, name, buf, sizeof(buf), in);
335 vp = add (buf, vp); /* add to previous value */
338 /* Now add the header data to the list */
339 add_header (ct, np, vp);
341 /* continue, if this isn't the last header field */
342 if (state != FLDEOF) {
343 ct->c_begin = ftell (in) + 1;
350 ct->c_begin = ftell (in) - strlen (buf);
354 ct->c_begin = ftell (in);
359 adios (NULL, "message format error in component #%d", compnum);
362 adios (NULL, "getfld() returned %d", state);
365 /* break out of the loop */
370 * Read the content headers. We will parse the
371 * MIME related header fields into their various
372 * structures and set internal flags related to
373 * content type/subtype, etc.
376 hp = ct->c_first_hf; /* start at first header field */
378 /* Get MIME-Version field */
379 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
382 unsigned char *cp, *dp;
385 advise (NULL, "message %s has multiple %s: fields",
386 ct->c_file, VRSN_FIELD);
389 ct->c_vrsn = add (hp->value, NULL);
391 /* Now, cleanup this field */
394 while (isspace (*cp))
396 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
398 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
403 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
405 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
408 for (dp = cp; istoken (*dp); dp++)
412 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
415 admonish (NULL, "message %s has unknown value for %s: field (%s)",
416 ct->c_file, VRSN_FIELD, cp);
419 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
420 /* Get Content-Type field */
421 struct str2init *s2i;
422 CI ci = &ct->c_ctinfo;
424 /* Check if we've already seen a Content-Type header */
426 advise (NULL, "message %s has multiple %s: fields",
427 ct->c_file, TYPE_FIELD);
431 /* Parse the Content-Type field */
432 if (get_ctinfo (hp->value, ct) == NOTOK)
436 * Set the Init function and the internal
437 * flag for this content type.
439 for (s2i = str2cts; s2i->si_key; s2i++)
440 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
442 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
444 ct->c_type = s2i->si_val;
445 ct->c_ctinitfnx = s2i->si_init;
447 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
448 /* Get Content-Transfer-Encoding field */
450 unsigned char *cp, *dp;
451 struct str2init *s2i;
454 * Check if we've already seen the
455 * Content-Transfer-Encoding field
458 advise (NULL, "message %s has multiple %s: fields",
459 ct->c_file, ENCODING_FIELD);
463 /* get copy of this field */
464 ct->c_celine = cp = add (hp->value, NULL);
466 while (isspace (*cp))
468 for (dp = cp; istoken (*dp); dp++)
474 * Find the internal flag and Init function
475 * for this transfer encoding.
477 for (s2i = str2ces; s2i->si_key; s2i++)
478 if (!mh_strcasecmp (cp, s2i->si_key))
480 if (!s2i->si_key && !uprf (cp, "X-"))
483 ct->c_encoding = s2i->si_val;
485 /* Call the Init function for this encoding */
486 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
489 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
490 /* Get Content-MD5 field */
491 unsigned char *cp, *dp;
497 if (ct->c_digested) {
498 advise (NULL, "message %s has multiple %s: fields",
499 ct->c_file, MD5_FIELD);
503 ep = cp = add (hp->value, NULL); /* get a copy */
505 while (isspace (*cp))
507 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
509 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
514 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
516 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
521 for (dp = cp; *dp && !isspace (*dp); dp++)
529 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
530 /* Get Content-ID field */
531 ct->c_id = add (hp->value, ct->c_id);
533 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
534 /* Get Content-Description field */
535 ct->c_descr = add (hp->value, ct->c_descr);
539 hp = hp->next; /* next header field */
543 * Check if we saw a Content-Type field.
544 * If not, then assign a default value for
545 * it, and the Init function.
549 * If we are inside a multipart/digest message,
550 * so default type is message/rfc822
553 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
555 ct->c_type = CT_MESSAGE;
556 ct->c_ctinitfnx = InitMessage;
559 * Else default type is text/plain
561 if (get_ctinfo ("text/plain", ct) == NOTOK)
563 ct->c_type = CT_TEXT;
564 ct->c_ctinitfnx = InitText;
568 /* Use default Transfer-Encoding, if necessary */
570 ct->c_encoding = CE_7BIT;
583 * small routine to add header field to list
587 add_header (CT ct, char *name, char *value)
591 /* allocate header field structure */
592 hp = mh_xmalloc (sizeof(*hp));
594 /* link data into header structure */
599 /* link header structure into the list */
600 if (ct->c_first_hf == NULL) {
601 ct->c_first_hf = hp; /* this is the first */
604 ct->c_last_hf->next = hp; /* add it to the end */
613 * Parse Content-Type line and fill in the
614 * information of the CTinfo structure.
618 get_ctinfo (unsigned char *cp, CT ct)
627 i = strlen (invo_name) + 2;
629 /* store copy of Content-Type line */
630 cp = ct->c_ctline = add (cp, NULL);
632 while (isspace (*cp)) /* trim leading spaces */
635 /* change newlines to spaces */
636 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
639 /* trim trailing spaces */
640 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
646 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
648 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
651 for (dp = cp; istoken (*dp); dp++)
654 ci->ci_type = add (cp, NULL); /* store content type */
658 advise (NULL, "invalid %s: field in message %s (empty type)",
659 TYPE_FIELD, ct->c_file);
663 /* down case the content type string */
664 for (dp = ci->ci_type; *dp; dp++)
665 if (isalpha(*dp) && isupper (*dp))
668 while (isspace (*cp))
671 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
675 ci->ci_subtype = add ("", NULL);
680 while (isspace (*cp))
683 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
686 for (dp = cp; istoken (*dp); dp++)
689 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
692 if (!*ci->ci_subtype) {
694 "invalid %s: field in message %s (empty subtype for \"%s\")",
695 TYPE_FIELD, ct->c_file, ci->ci_type);
699 /* down case the content subtype string */
700 for (dp = ci->ci_subtype; *dp; dp++)
701 if (isalpha(*dp) && isupper (*dp))
705 while (isspace (*cp))
708 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
712 * Parse attribute/value pairs given with Content-Type
714 ep = (ap = ci->ci_attrs) + NPARMS;
721 "too many parameters in message %s's %s: field (%d max)",
722 ct->c_file, TYPE_FIELD, NPARMS);
727 while (isspace (*cp))
730 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
735 "extraneous trailing ';' in message %s's %s: parameter list",
736 ct->c_file, TYPE_FIELD);
740 /* down case the attribute name */
741 for (dp = cp; istoken (*dp); dp++)
742 if (isalpha(*dp) && isupper (*dp))
745 for (up = dp; isspace (*dp);)
747 if (dp == cp || *dp != '=') {
749 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
750 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
754 vp = (*ap = add (cp, NULL)) + (up - cp);
756 for (dp++; isspace (*dp);)
759 /* now add the attribute value */
760 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
763 for (cp = ++dp, dp = vp;;) {
768 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
769 ct->c_file, TYPE_FIELD, i, i, "", *ap);
774 if ((c = *cp++) == '\0')
789 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
795 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
796 ct->c_file, TYPE_FIELD, i, i, "", *ap);
801 while (isspace (*cp))
804 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
809 * Check if anything is left over
812 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
813 ct->c_file, TYPE_FIELD, i, i, "", cp);
821 get_comment (CT ct, unsigned char **ap, int istype)
826 char c, buffer[BUFSIZ], *dp;
838 advise (NULL, "invalid comment in message %s's %s: field",
839 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
844 if ((c = *cp++) == '\0')
867 if ((dp = ci->ci_comment)) {
868 ci->ci_comment = concat (dp, " ", buffer, NULL);
871 ci->ci_comment = add (buffer, NULL);
875 while (isspace (*cp))
886 * Handles content types audio, image, and video.
887 * There's not much to do right here.
893 return OK; /* not much to do here */
906 char **ap, **ep, *cp;
909 CI ci = &ct->c_ctinfo;
911 /* check for missing subtype */
912 if (!*ci->ci_subtype)
913 ci->ci_subtype = add ("plain", ci->ci_subtype);
916 for (kv = SubText; kv->kv_key; kv++)
917 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
919 ct->c_subtype = kv->kv_value;
921 /* allocate text structure */
922 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
923 adios (NULL, "out of memory");
924 ct->c_ctparams = (void *) t;
926 /* scan for charset parameter */
927 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
928 if (!mh_strcasecmp (*ap, "charset"))
934 chset = "US-ASCII"; /* default for text */
936 /* match character set, or set to unknown */
937 for (kv = Charset; kv->kv_key; kv++)
938 if (!mh_strcasecmp (chset, kv->kv_key))
940 t->tx_charset = kv->kv_value;
943 * If we can not handle character set natively,
944 * then check profile for string to modify the
945 * terminal or display method.
947 if (!check_charset (chset, strlen (chset))) {
948 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
949 if ((cp = context_find (buffer)))
950 ct->c_termproc = getcpy (cp);
962 InitMultiPart (CT ct)
966 unsigned char *cp, *dp;
968 char *bp, buffer[BUFSIZ];
971 struct part *part, **next;
972 CI ci = &ct->c_ctinfo;
977 * The encoding for multipart messages must be either
978 * 7bit, 8bit, or binary (per RFC2045).
980 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
981 && ct->c_encoding != CE_BINARY) {
983 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
984 ci->ci_type, ci->ci_subtype, ct->c_file);
989 for (kv = SubMultiPart; kv->kv_key; kv++)
990 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
992 ct->c_subtype = kv->kv_value;
995 * Check for "boundary" parameter, which is
996 * required for multipart messages.
999 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1000 if (!mh_strcasecmp (*ap, "boundary")) {
1006 /* complain if boundary parameter is missing */
1009 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1010 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1014 /* allocate primary structure for multipart info */
1015 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1016 adios (NULL, "out of memory");
1017 ct->c_ctparams = (void *) m;
1019 /* check if boundary parameter contains only whitespace characters */
1020 for (cp = bp; isspace (*cp); cp++)
1023 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1024 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1028 /* remove trailing whitespace from boundary parameter */
1029 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1034 /* record boundary separators */
1035 m->mp_start = concat (bp, "\n", NULL);
1036 m->mp_stop = concat (bp, "--\n", NULL);
1038 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1039 advise (ct->c_file, "unable to open for reading");
1043 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1045 next = &m->mp_parts;
1049 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1053 pos += strlen (buffer);
1054 if (buffer[0] != '-' || buffer[1] != '-')
1057 if (strcmp (buffer + 2, m->mp_start))
1060 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1061 adios (NULL, "out of memory");
1063 next = &part->mp_next;
1065 if (!(p = get_content (fp, ct->c_file,
1066 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1073 fseek (fp, pos, SEEK_SET);
1076 if (strcmp (buffer + 2, m->mp_start) == 0) {
1080 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1081 if (p->c_end < p->c_begin)
1082 p->c_begin = p->c_end;
1087 if (strcmp (buffer + 2, m->mp_stop) == 0)
1093 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1094 if (!inout && part) {
1096 p->c_end = ct->c_end;
1098 if (p->c_begin >= p->c_end) {
1099 for (next = &m->mp_parts; *next != part;
1100 next = &((*next)->mp_next))
1104 free ((char *) part);
1109 /* reverse the order of the parts for multipart/alternative */
1110 if (ct->c_subtype == MULTI_ALTERNATE)
1114 * label all subparts with part number, and
1115 * then initialize the content of the subpart.
1120 char partnam[BUFSIZ];
1123 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1124 pp = partnam + strlen (partnam);
1129 for (part = m->mp_parts, partnum = 1; part;
1130 part = part->mp_next, partnum++) {
1133 sprintf (pp, "%d", partnum);
1134 p->c_partno = add (partnam, NULL);
1136 /* initialize the content of the subparts */
1137 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1152 * reverse the order of the parts of a multipart
1156 reverse_parts (CT ct)
1159 struct multipart *m;
1160 struct part **base, **bmp, **next, *part;
1162 m = (struct multipart *) ct->c_ctparams;
1164 /* if only one part, just return */
1165 if (!m->mp_parts || !m->mp_parts->mp_next)
1168 /* count number of parts */
1170 for (part = m->mp_parts; part; part = part->mp_next)
1173 /* allocate array of pointers to the parts */
1174 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1175 adios (NULL, "out of memory");
1178 /* point at all the parts */
1179 for (part = m->mp_parts; part; part = part->mp_next)
1183 /* reverse the order of the parts */
1184 next = &m->mp_parts;
1185 for (bmp--; bmp >= base; bmp--) {
1188 next = &part->mp_next;
1192 /* free array of pointers */
1193 free ((char *) base);
1205 CI ci = &ct->c_ctinfo;
1207 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1209 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1210 ci->ci_type, ci->ci_subtype, ct->c_file);
1214 /* check for missing subtype */
1215 if (!*ci->ci_subtype)
1216 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1219 for (kv = SubMessage; kv->kv_key; kv++)
1220 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1222 ct->c_subtype = kv->kv_value;
1224 switch (ct->c_subtype) {
1225 case MESSAGE_RFC822:
1228 case MESSAGE_PARTIAL:
1233 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1234 adios (NULL, "out of memory");
1235 ct->c_ctparams = (void *) p;
1237 /* scan for parameters "id", "number", and "total" */
1238 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1239 if (!mh_strcasecmp (*ap, "id")) {
1240 p->pm_partid = add (*ep, NULL);
1243 if (!mh_strcasecmp (*ap, "number")) {
1244 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1245 || p->pm_partno < 1) {
1248 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1249 *ap, ci->ci_type, ci->ci_subtype,
1250 ct->c_file, TYPE_FIELD);
1255 if (!mh_strcasecmp (*ap, "total")) {
1256 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1265 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1267 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1268 ci->ci_type, ci->ci_subtype,
1269 ct->c_file, TYPE_FIELD);
1275 case MESSAGE_EXTERNAL:
1282 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1283 adios (NULL, "out of memory");
1284 ct->c_ctparams = (void *) e;
1287 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1288 advise (ct->c_file, "unable to open for reading");
1292 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1294 if (!(p = get_content (fp, ct->c_file, 0))) {
1302 if ((exresult = params_external (ct, 0)) != NOTOK
1303 && p->c_ceopenfnx == openMail) {
1307 if ((size = ct->c_end - p->c_begin) <= 0) {
1309 content_error (NULL, ct,
1310 "empty body for access-type=mail-server");
1314 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1315 fseek (p->c_fp, p->c_begin, SEEK_SET);
1317 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1319 adios ("failed", "fread");
1322 adios (NULL, "unexpected EOF from fread");
1325 bp += cc, size -= cc;
1332 p->c_end = p->c_begin;
1337 if (exresult == NOTOK)
1339 if (e->eb_flags == NOTOK)
1342 switch (p->c_type) {
1347 if (p->c_subtype != MESSAGE_RFC822)
1351 e->eb_partno = ct->c_partno;
1353 (*p->c_ctinitfnx) (p);
1368 params_external (CT ct, int composing)
1371 struct exbody *e = (struct exbody *) ct->c_ctparams;
1372 CI ci = &ct->c_ctinfo;
1374 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1375 if (!mh_strcasecmp (*ap, "access-type")) {
1376 struct str2init *s2i;
1377 CT p = e->eb_content;
1379 for (s2i = str2methods; s2i->si_key; s2i++)
1380 if (!mh_strcasecmp (*ep, s2i->si_key))
1384 e->eb_flags = NOTOK;
1385 p->c_encoding = CE_EXTERNAL;
1388 e->eb_access = s2i->si_key;
1389 e->eb_flags = s2i->si_val;
1390 p->c_encoding = CE_EXTERNAL;
1392 /* Call the Init function for this external type */
1393 if ((*s2i->si_init)(p) == NOTOK)
1397 if (!mh_strcasecmp (*ap, "name")) {
1401 if (!mh_strcasecmp (*ap, "permission")) {
1402 e->eb_permission = *ep;
1405 if (!mh_strcasecmp (*ap, "site")) {
1409 if (!mh_strcasecmp (*ap, "directory")) {
1413 if (!mh_strcasecmp (*ap, "mode")) {
1417 if (!mh_strcasecmp (*ap, "size")) {
1418 sscanf (*ep, "%lu", &e->eb_size);
1421 if (!mh_strcasecmp (*ap, "server")) {
1425 if (!mh_strcasecmp (*ap, "subject")) {
1426 e->eb_subject = *ep;
1429 if (composing && !mh_strcasecmp (*ap, "body")) {
1430 e->eb_body = getcpy (*ep);
1435 if (!e->eb_access) {
1437 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1438 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1451 InitApplication (CT ct)
1454 CI ci = &ct->c_ctinfo;
1457 for (kv = SubApplication; kv->kv_key; kv++)
1458 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1460 ct->c_subtype = kv->kv_value;
1467 * TRANSFER ENCODINGS
1471 init_encoding (CT ct, OpenCEFunc openfnx)
1475 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1476 adios (NULL, "out of memory");
1479 ct->c_ceopenfnx = openfnx;
1480 ct->c_ceclosefnx = close_encoding;
1481 ct->c_cesizefnx = size_encoding;
1488 close_encoding (CT ct)
1492 if (!(ce = ct->c_cefile))
1502 static unsigned long
1503 size_encoding (CT ct)
1511 if (!(ce = ct->c_cefile))
1512 return (ct->c_end - ct->c_begin);
1514 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1515 return (long) st.st_size;
1518 if (stat (ce->ce_file, &st) != NOTOK)
1519 return (long) st.st_size;
1524 if (ct->c_encoding == CE_EXTERNAL)
1525 return (ct->c_end - ct->c_begin);
1528 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1529 return (ct->c_end - ct->c_begin);
1531 if (fstat (fd, &st) != NOTOK)
1532 size = (long) st.st_size;
1536 (*ct->c_ceclosefnx) (ct);
1545 static unsigned char b642nib[0x80] = {
1546 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1547 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1548 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1549 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1550 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1551 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1552 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1553 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1554 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1555 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1556 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1557 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1558 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1559 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1560 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1561 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1568 return init_encoding (ct, openBase64);
1573 openBase64 (CT ct, char **file)
1575 int bitno, cc, digested;
1578 unsigned char value, *b, *b1, *b2, *b3;
1579 unsigned char *cp, *ep;
1580 char buffer[BUFSIZ];
1581 /* sbeck -- handle prefixes */
1586 b = (unsigned char *) &bits;
1587 b1 = &b[endian > 0 ? 1 : 2];
1588 b2 = &b[endian > 0 ? 2 : 1];
1589 b3 = &b[endian > 0 ? 3 : 0];
1593 fseek (ce->ce_fp, 0L, SEEK_SET);
1598 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1599 content_error (ce->ce_file, ct, "unable to fopen for reading");
1605 if (*file == NULL) {
1606 ce->ce_file = add (m_scratch ("", tmp), NULL);
1609 ce->ce_file = add (*file, NULL);
1613 /* sbeck@cise.ufl.edu -- handle suffixes */
1615 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1616 invo_name, ci->ci_type, ci->ci_subtype);
1617 cp = context_find (buffer);
1618 if (cp == NULL || *cp == '\0') {
1619 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1621 cp = context_find (buffer);
1623 if (cp != NULL && *cp != '\0')
1624 ce->ce_file = add (cp, ce->ce_file);
1626 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1627 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1631 if ((len = ct->c_end - ct->c_begin) < 0)
1632 adios (NULL, "internal error(1)");
1634 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1635 content_error (ct->c_file, ct, "unable to open for reading");
1639 if ((digested = ct->c_digested))
1640 MD5Init (&mdContext);
1646 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1648 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1650 content_error (ct->c_file, ct, "error reading from");
1654 content_error (NULL, ct, "premature eof");
1662 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1667 if (skip || (*cp & 0x80)
1668 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1670 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1672 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1675 content_error (NULL, ct,
1676 "invalid BASE64 encoding -- continuing");
1680 bits |= value << bitno;
1682 if ((bitno -= 6) < 0) {
1683 putc ((char) *b1, ce->ce_fp);
1685 MD5Update (&mdContext, b1, 1);
1687 putc ((char) *b2, ce->ce_fp);
1689 MD5Update (&mdContext, b2, 1);
1691 putc ((char) *b3, ce->ce_fp);
1693 MD5Update (&mdContext, b3, 1);
1697 if (ferror (ce->ce_fp)) {
1698 content_error (ce->ce_file, ct,
1699 "error writing to");
1702 bitno = 18, bits = 0L, skip = 0;
1708 goto self_delimiting;
1717 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1719 content_error (NULL, ct, "invalid BASE64 encoding");
1724 fseek (ct->c_fp, 0L, SEEK_SET);
1726 if (fflush (ce->ce_fp)) {
1727 content_error (ce->ce_file, ct, "error writing to");
1732 unsigned char digest[16];
1734 MD5Final (digest, &mdContext);
1735 if (memcmp((char *) digest, (char *) ct->c_digest,
1736 sizeof(digest) / sizeof(digest[0])))
1737 content_error (NULL, ct,
1738 "content integrity suspect (digest mismatch) -- continuing");
1741 fprintf (stderr, "content integrity confirmed\n");
1744 fseek (ce->ce_fp, 0L, SEEK_SET);
1747 *file = ce->ce_file;
1748 return fileno (ce->ce_fp);
1751 free_encoding (ct, 0);
1760 static char hex2nib[0x80] = {
1761 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1762 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1767 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1768 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1769 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1772 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1773 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1774 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1775 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1776 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1783 return init_encoding (ct, openQuoted);
1788 openQuoted (CT ct, char **file)
1790 int cc, digested, len, quoted;
1791 unsigned char *cp, *ep;
1792 char buffer[BUFSIZ];
1795 /* sbeck -- handle prefixes */
1801 fseek (ce->ce_fp, 0L, SEEK_SET);
1806 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1807 content_error (ce->ce_file, ct, "unable to fopen for reading");
1813 if (*file == NULL) {
1814 ce->ce_file = add (m_scratch ("", tmp), NULL);
1817 ce->ce_file = add (*file, NULL);
1821 /* sbeck@cise.ufl.edu -- handle suffixes */
1823 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1824 invo_name, ci->ci_type, ci->ci_subtype);
1825 cp = context_find (buffer);
1826 if (cp == NULL || *cp == '\0') {
1827 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1829 cp = context_find (buffer);
1831 if (cp != NULL && *cp != '\0')
1832 ce->ce_file = add (cp, ce->ce_file);
1834 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1835 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1839 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1840 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1844 if ((len = ct->c_end - ct->c_begin) < 0)
1845 adios (NULL, "internal error(2)");
1847 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1848 content_error (ct->c_file, ct, "unable to open for reading");
1852 if ((digested = ct->c_digested))
1853 MD5Init (&mdContext);
1860 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1862 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1863 content_error (NULL, ct, "premature eof");
1867 if ((cc = strlen (buffer)) > len)
1871 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1876 for (; cp < ep; cp++) {
1878 /* in an escape sequence */
1880 /* at byte 1 of an escape sequence */
1881 mask = hex2nib[*cp & 0x7f];
1882 /* next is byte 2 */
1885 /* at byte 2 of an escape sequence */
1887 mask |= hex2nib[*cp & 0x7f];
1888 putc (mask, ce->ce_fp);
1890 MD5Update (&mdContext, &mask, 1);
1891 if (ferror (ce->ce_fp)) {
1892 content_error (ce->ce_file, ct, "error writing to");
1895 /* finished escape sequence; next may be literal or a new
1896 * escape sequence */
1899 /* on to next byte */
1903 /* not in an escape sequence */
1905 /* starting an escape sequence, or invalid '='? */
1906 if (cp + 1 < ep && cp[1] == '\n') {
1907 /* "=\n" soft line break, eat the \n */
1911 if (cp + 1 >= ep || cp + 2 >= ep) {
1912 /* We don't have 2 bytes left, so this is an invalid
1913 * escape sequence; just show the raw bytes (below). */
1914 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
1915 /* Next 2 bytes are hex digits, making this a valid escape
1916 * sequence; let's decode it (above). */
1920 /* One or both of the next 2 is out of range, making this
1921 * an invalid escape sequence; just show the raw bytes
1926 /* Just show the raw byte. */
1927 putc (*cp, ce->ce_fp);
1930 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1932 MD5Update (&mdContext, (unsigned char *) cp, 1);
1935 if (ferror (ce->ce_fp)) {
1936 content_error (ce->ce_file, ct, "error writing to");
1942 content_error (NULL, ct,
1943 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1947 fseek (ct->c_fp, 0L, SEEK_SET);
1949 if (fflush (ce->ce_fp)) {
1950 content_error (ce->ce_file, ct, "error writing to");
1955 unsigned char digest[16];
1957 MD5Final (digest, &mdContext);
1958 if (memcmp((char *) digest, (char *) ct->c_digest,
1959 sizeof(digest) / sizeof(digest[0])))
1960 content_error (NULL, ct,
1961 "content integrity suspect (digest mismatch) -- continuing");
1964 fprintf (stderr, "content integrity confirmed\n");
1967 fseek (ce->ce_fp, 0L, SEEK_SET);
1970 *file = ce->ce_file;
1971 return fileno (ce->ce_fp);
1974 free_encoding (ct, 0);
1986 if (init_encoding (ct, open7Bit) == NOTOK)
1989 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1995 open7Bit (CT ct, char **file)
1998 char buffer[BUFSIZ];
1999 /* sbeck -- handle prefixes */
2006 fseek (ce->ce_fp, 0L, SEEK_SET);
2011 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2012 content_error (ce->ce_file, ct, "unable to fopen for reading");
2018 if (*file == NULL) {
2019 ce->ce_file = add (m_scratch ("", tmp), NULL);
2022 ce->ce_file = add (*file, NULL);
2026 /* sbeck@cise.ufl.edu -- handle suffixes */
2028 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2029 invo_name, ci->ci_type, ci->ci_subtype);
2030 cp = context_find (buffer);
2031 if (cp == NULL || *cp == '\0') {
2032 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2034 cp = context_find (buffer);
2036 if (cp != NULL && *cp != '\0')
2037 ce->ce_file = add (cp, ce->ce_file);
2039 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2040 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2044 if (ct->c_type == CT_MULTIPART) {
2046 CI ci = &ct->c_ctinfo;
2049 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2050 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2051 + 1 + strlen (ci->ci_subtype);
2052 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2053 putc (';', ce->ce_fp);
2056 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2058 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2059 fputs ("\n\t", ce->ce_fp);
2062 putc (' ', ce->ce_fp);
2065 fprintf (ce->ce_fp, "%s", buffer);
2069 if (ci->ci_comment) {
2070 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2071 fputs ("\n\t", ce->ce_fp);
2075 putc (' ', ce->ce_fp);
2078 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2081 fprintf (ce->ce_fp, "\n");
2083 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2085 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2086 fprintf (ce->ce_fp, "\n");
2089 if ((len = ct->c_end - ct->c_begin) < 0)
2090 adios (NULL, "internal error(3)");
2092 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2093 content_error (ct->c_file, ct, "unable to open for reading");
2097 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2099 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2101 content_error (ct->c_file, ct, "error reading from");
2105 content_error (NULL, ct, "premature eof");
2113 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2114 if (ferror (ce->ce_fp)) {
2115 content_error (ce->ce_file, ct, "error writing to");
2120 fseek (ct->c_fp, 0L, SEEK_SET);
2122 if (fflush (ce->ce_fp)) {
2123 content_error (ce->ce_file, ct, "error writing to");
2127 fseek (ce->ce_fp, 0L, SEEK_SET);
2130 *file = ce->ce_file;
2131 return fileno (ce->ce_fp);
2134 free_encoding (ct, 0);
2144 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2146 char cachefile[BUFSIZ];
2149 fseek (ce->ce_fp, 0L, SEEK_SET);
2154 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2155 content_error (ce->ce_file, ct, "unable to fopen for reading");
2161 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2162 cachefile, sizeof(cachefile)) != NOTOK) {
2163 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2164 ce->ce_file = getcpy (cachefile);
2168 admonish (cachefile, "unable to fopen for reading");
2175 *file = ce->ce_file;
2176 *fd = fileno (ce->ce_fp);
2187 return init_encoding (ct, openFile);
2192 openFile (CT ct, char **file)
2195 char cachefile[BUFSIZ];
2196 struct exbody *e = ct->c_ctexbody;
2197 CE ce = ct->c_cefile;
2199 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2211 content_error (NULL, ct, "missing name parameter");
2215 ce->ce_file = getcpy (e->eb_name);
2218 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2219 content_error (ce->ce_file, ct, "unable to fopen for reading");
2223 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2224 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2225 cachefile, sizeof(cachefile)) != NOTOK) {
2229 mask = umask (cachetype ? ~m_gmprot () : 0222);
2230 if ((fp = fopen (cachefile, "w"))) {
2232 char buffer[BUFSIZ];
2233 FILE *gp = ce->ce_fp;
2235 fseek (gp, 0L, SEEK_SET);
2237 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2239 fwrite (buffer, sizeof(*buffer), cc, fp);
2243 admonish (ce->ce_file, "error reading");
2248 admonish (cachefile, "error writing");
2256 fseek (ce->ce_fp, 0L, SEEK_SET);
2257 *file = ce->ce_file;
2258 return fileno (ce->ce_fp);
2268 return init_encoding (ct, openFTP);
2273 openFTP (CT ct, char **file)
2275 int cachetype, caching, fd;
2277 char *bp, *ftp, *user, *pass;
2278 char buffer[BUFSIZ], cachefile[BUFSIZ];
2281 static char *username = NULL;
2282 static char *password = NULL;
2287 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2295 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2306 if (!e->eb_name || !e->eb_site) {
2307 content_error (NULL, ct, "missing %s parameter",
2308 e->eb_name ? "site": "name");
2315 pidcheck (pidwait (xpid, NOTOK));
2319 /* Get the buffer ready to go */
2321 buflen = sizeof(buffer);
2324 * Construct the query message for user
2326 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2332 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2338 snprintf (bp, buflen, "\n using %sFTP from site %s",
2339 e->eb_flags ? "anonymous " : "", e->eb_site);
2344 if (e->eb_size > 0) {
2345 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2350 snprintf (bp, buflen, "? ");
2353 * Now, check the answer
2355 if (!getanswer (buffer))
2360 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2363 ruserpass (e->eb_site, &username, &password);
2368 ce->ce_unlink = (*file == NULL);
2370 cachefile[0] = '\0';
2371 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2372 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2373 cachefile, sizeof(cachefile)) != NOTOK) {
2374 if (*file == NULL) {
2381 ce->ce_file = add (*file, NULL);
2383 ce->ce_file = add (cachefile, NULL);
2385 ce->ce_file = add (m_scratch ("", tmp), NULL);
2387 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2388 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2396 int child_id, i, vecp;
2400 vec[vecp++] = r1bindex (ftp, '/');
2401 vec[vecp++] = e->eb_site;
2404 vec[vecp++] = e->eb_dir;
2405 vec[vecp++] = e->eb_name;
2406 vec[vecp++] = ce->ce_file,
2407 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2408 ? "ascii" : "binary";
2413 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2417 adios ("fork", "unable to");
2421 close (fileno (ce->ce_fp));
2423 fprintf (stderr, "unable to exec ");
2429 if (pidXwait (child_id, NULL)) {
2433 username = password = NULL;
2442 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2444 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2451 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2456 mask = umask (cachetype ? ~m_gmprot () : 0222);
2457 if ((fp = fopen (cachefile, "w"))) {
2459 FILE *gp = ce->ce_fp;
2461 fseek (gp, 0L, SEEK_SET);
2463 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2465 fwrite (buffer, sizeof(*buffer), cc, fp);
2469 admonish (ce->ce_file, "error reading");
2474 admonish (cachefile, "error writing");
2483 fseek (ce->ce_fp, 0L, SEEK_SET);
2484 *file = ce->ce_file;
2485 return fileno (ce->ce_fp);
2496 return init_encoding (ct, openMail);
2501 openMail (CT ct, char **file)
2503 int child_id, fd, i, vecp;
2505 char *bp, buffer[BUFSIZ], *vec[7];
2506 struct exbody *e = ct->c_ctexbody;
2507 CE ce = ct->c_cefile;
2509 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2520 if (!e->eb_server) {
2521 content_error (NULL, ct, "missing server parameter");
2528 pidcheck (pidwait (xpid, NOTOK));
2532 /* Get buffer ready to go */
2534 buflen = sizeof(buffer);
2536 /* Now, construct query message */
2537 snprintf (bp, buflen, "Retrieve content");
2543 snprintf (bp, buflen, " %s", e->eb_partno);
2549 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2551 e->eb_subject ? e->eb_subject : e->eb_body);
2553 /* Now, check answer */
2554 if (!getanswer (buffer))
2558 vec[vecp++] = r1bindex (mailproc, '/');
2559 vec[vecp++] = e->eb_server;
2560 vec[vecp++] = "-subject";
2561 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2562 vec[vecp++] = "-body";
2563 vec[vecp++] = e->eb_body;
2566 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2570 advise ("fork", "unable to");
2574 execvp (mailproc, vec);
2575 fprintf (stderr, "unable to exec ");
2581 if (pidXwait (child_id, NULL) == OK)
2582 advise (NULL, "request sent");
2586 if (*file == NULL) {
2587 ce->ce_file = add (m_scratch ("", tmp), NULL);
2590 ce->ce_file = add (*file, NULL);
2594 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2595 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2600 free (ct->c_showproc);
2601 ct->c_showproc = add ("true", NULL);
2603 fseek (ce->ce_fp, 0L, SEEK_SET);
2604 *file = ce->ce_file;
2605 return fileno (ce->ce_fp);
2610 readDigest (CT ct, char *cp)
2615 unsigned char *dp, value, *ep;
2616 unsigned char *b, *b1, *b2, *b3;
2618 b = (unsigned char *) &bits,
2619 b1 = &b[endian > 0 ? 1 : 2],
2620 b2 = &b[endian > 0 ? 2 : 1],
2621 b3 = &b[endian > 0 ? 3 : 0];
2626 for (ep = (dp = ct->c_digest)
2627 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2632 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2634 fprintf (stderr, "invalid BASE64 encoding\n");
2638 bits |= value << bitno;
2640 if ((bitno -= 6) < 0) {
2641 if (dp + (3 - skip) > ep)
2642 goto invalid_digest;
2657 goto self_delimiting;
2662 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2672 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2680 fprintf (stderr, "MD5 digest=");
2681 for (dp = ct->c_digest; dp < ep; dp++)
2682 fprintf (stderr, "%02x", *dp & 0xff);
2683 fprintf (stderr, "\n");