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 (char *, CT);
131 static int get_comment (CT, 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)
217 * Main entry point for parsing a MIME message or file.
218 * It returns the Content structure for the top level
219 * entity in the file.
223 parse_mime (char *file)
231 * Check if file is actually standard input
233 if ((is_stdin = !(strcmp (file, "-")))) {
234 file = add (m_tmpfil (invo_name), NULL);
235 if ((fp = fopen (file, "w+")) == NULL) {
236 advise (file, "unable to fopen for writing and reading");
240 while (fgets (buffer, sizeof(buffer), stdin))
244 if (ferror (stdin)) {
246 advise ("stdin", "error reading");
251 advise (file, "error writing");
254 fseek (fp, 0L, SEEK_SET);
255 } else if ((fp = fopen (file, "r")) == NULL) {
256 advise (file, "unable to read");
260 if (!(ct = get_content (fp, file, 1))) {
263 advise (NULL, "unable to decode %s", file);
268 ct->c_unlink = 1; /* temp file to remove */
272 if (ct->c_end == 0L) {
273 fseek (fp, 0L, SEEK_END);
274 ct->c_end = ftell (fp);
277 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
289 * Main routine for reading/parsing the headers
290 * of a message content.
292 * toplevel = 1 # we are at the top level of the message
293 * toplevel = 0 # we are inside message type or multipart type
294 * # other than multipart/digest
295 * toplevel = -1 # we are inside multipart/digest
296 * NB: on failure we will fclose(in)!
300 get_content (FILE *in, char *file, int toplevel)
303 char buf[BUFSIZ], name[NAMESZ];
308 /* allocate the content structure */
309 if (!(ct = (CT) calloc (1, sizeof(*ct))))
310 adios (NULL, "out of memory");
313 ct->c_file = add (file, NULL);
314 ct->c_begin = ftell (ct->c_fp) + 1;
317 * Parse the header fields for this
318 * content into a linked list.
320 for (compnum = 1, state = FLD;;) {
321 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
327 /* get copies of the buffers */
328 np = add (name, NULL);
329 vp = add (buf, NULL);
331 /* if necessary, get rest of field */
332 while (state == FLDPLUS) {
333 state = m_getfld (state, name, buf, sizeof(buf), in);
334 vp = add (buf, vp); /* add to previous value */
337 /* Now add the header data to the list */
338 add_header (ct, np, vp);
340 /* continue, if this isn't the last header field */
341 if (state != FLDEOF) {
342 ct->c_begin = ftell (in) + 1;
349 ct->c_begin = ftell (in) - strlen (buf);
353 ct->c_begin = ftell (in);
358 adios (NULL, "message format error in component #%d", compnum);
361 adios (NULL, "getfld() returned %d", state);
364 /* break out of the loop */
369 * Read the content headers. We will parse the
370 * MIME related header fields into their various
371 * structures and set internal flags related to
372 * content type/subtype, etc.
375 hp = ct->c_first_hf; /* start at first header field */
377 /* Get MIME-Version field */
378 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
383 advise (NULL, "message %s has multiple %s: fields",
384 ct->c_file, VRSN_FIELD);
387 ct->c_vrsn = add (hp->value, NULL);
389 /* Now, cleanup this field */
392 while (isspace (*cp))
394 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
396 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
401 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
403 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
406 for (dp = cp; istoken (*dp); dp++)
410 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
413 admonish (NULL, "message %s has unknown value for %s: field (%s)",
414 ct->c_file, VRSN_FIELD, cp);
417 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
418 /* Get Content-Type field */
419 struct str2init *s2i;
420 CI ci = &ct->c_ctinfo;
422 /* Check if we've already seen a Content-Type header */
424 advise (NULL, "message %s has multiple %s: fields",
425 ct->c_file, TYPE_FIELD);
429 /* Parse the Content-Type field */
430 if (get_ctinfo (hp->value, ct) == NOTOK)
434 * Set the Init function and the internal
435 * flag for this content type.
437 for (s2i = str2cts; s2i->si_key; s2i++)
438 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
440 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
442 ct->c_type = s2i->si_val;
443 ct->c_ctinitfnx = s2i->si_init;
445 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
446 /* Get Content-Transfer-Encoding field */
448 struct str2init *s2i;
451 * Check if we've already seen the
452 * Content-Transfer-Encoding field
455 advise (NULL, "message %s has multiple %s: fields",
456 ct->c_file, ENCODING_FIELD);
460 /* get copy of this field */
461 ct->c_celine = cp = add (hp->value, NULL);
463 while (isspace (*cp))
465 for (dp = cp; istoken (*dp); dp++)
471 * Find the internal flag and Init function
472 * for this transfer encoding.
474 for (s2i = str2ces; s2i->si_key; s2i++)
475 if (!mh_strcasecmp (cp, s2i->si_key))
477 if (!s2i->si_key && !uprf (cp, "X-"))
480 ct->c_encoding = s2i->si_val;
482 /* Call the Init function for this encoding */
483 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
486 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
487 /* Get Content-MD5 field */
493 if (ct->c_digested) {
494 advise (NULL, "message %s has multiple %s: fields",
495 ct->c_file, MD5_FIELD);
499 ep = cp = add (hp->value, NULL); /* get a copy */
501 while (isspace (*cp))
503 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
505 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
510 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
512 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
517 for (dp = cp; *dp && !isspace (*dp); dp++)
525 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
526 /* Get Content-ID field */
527 ct->c_id = add (hp->value, ct->c_id);
529 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
530 /* Get Content-Description field */
531 ct->c_descr = add (hp->value, ct->c_descr);
535 hp = hp->next; /* next header field */
539 * Check if we saw a Content-Type field.
540 * If not, then assign a default value for
541 * it, and the Init function.
545 * If we are inside a multipart/digest message,
546 * so default type is message/rfc822
549 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
551 ct->c_type = CT_MESSAGE;
552 ct->c_ctinitfnx = InitMessage;
555 * Else default type is text/plain
557 if (get_ctinfo ("text/plain", ct) == NOTOK)
559 ct->c_type = CT_TEXT;
560 ct->c_ctinitfnx = InitText;
564 /* Use default Transfer-Encoding, if necessary */
566 ct->c_encoding = CE_7BIT;
579 * small routine to add header field to list
583 add_header (CT ct, char *name, char *value)
587 /* allocate header field structure */
588 hp = mh_xmalloc (sizeof(*hp));
590 /* link data into header structure */
595 /* link header structure into the list */
596 if (ct->c_first_hf == NULL) {
597 ct->c_first_hf = hp; /* this is the first */
600 ct->c_last_hf->next = hp; /* add it to the end */
609 * Parse Content-Type line and fill in the
610 * information of the CTinfo structure.
614 get_ctinfo (char *cp, CT ct)
617 char *dp, **ap, **ep;
622 i = strlen (invo_name) + 2;
624 /* store copy of Content-Type line */
625 cp = ct->c_ctline = add (cp, NULL);
627 while (isspace (*cp)) /* trim leading spaces */
630 /* change newlines to spaces */
631 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
634 /* trim trailing spaces */
635 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
641 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
643 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
646 for (dp = cp; istoken (*dp); dp++)
649 ci->ci_type = add (cp, NULL); /* store content type */
653 advise (NULL, "invalid %s: field in message %s (empty type)",
654 TYPE_FIELD, ct->c_file);
658 /* down case the content type string */
659 for (dp = ci->ci_type; *dp; dp++)
660 if (isalpha(*dp) && isupper (*dp))
663 while (isspace (*cp))
666 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
670 ci->ci_subtype = add ("", NULL);
675 while (isspace (*cp))
678 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
681 for (dp = cp; istoken (*dp); dp++)
684 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
687 if (!*ci->ci_subtype) {
689 "invalid %s: field in message %s (empty subtype for \"%s\")",
690 TYPE_FIELD, ct->c_file, ci->ci_type);
694 /* down case the content subtype string */
695 for (dp = ci->ci_subtype; *dp; dp++)
696 if (isalpha(*dp) && isupper (*dp))
700 while (isspace (*cp))
703 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
707 * Parse attribute/value pairs given with Content-Type
709 ep = (ap = ci->ci_attrs) + NPARMS;
715 "too many parameters in message %s's %s: field (%d max)",
716 ct->c_file, TYPE_FIELD, NPARMS);
721 while (isspace (*cp))
724 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
729 "extraneous trailing ';' in message %s's %s: parameter list",
730 ct->c_file, TYPE_FIELD);
734 /* down case the attribute name */
735 for (dp = cp; istoken (*dp); dp++)
736 if (isalpha(*dp) && isupper (*dp))
739 for (up = dp; isspace (*dp);)
741 if (dp == cp || *dp != '=') {
743 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
744 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
748 vp = (*ap = add (cp, NULL)) + (up - cp);
750 for (dp++; isspace (*dp);)
753 /* now add the attribute value */
754 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
757 for (cp = ++dp, dp = vp;;) {
762 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
763 ct->c_file, TYPE_FIELD, i, i, "", *ap);
768 if ((c = *cp++) == '\0')
783 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
789 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
790 ct->c_file, TYPE_FIELD, i, i, "", *ap);
795 while (isspace (*cp))
798 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
803 * Check if anything is left over
806 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
807 ct->c_file, TYPE_FIELD, i, i, "", cp);
815 get_comment (CT ct, char **ap, int istype)
819 char c, buffer[BUFSIZ], *dp;
831 advise (NULL, "invalid comment in message %s's %s: field",
832 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
837 if ((c = *cp++) == '\0')
860 if ((dp = ci->ci_comment)) {
861 ci->ci_comment = concat (dp, " ", buffer, NULL);
864 ci->ci_comment = add (buffer, NULL);
868 while (isspace (*cp))
879 * Handles content types audio, image, and video.
880 * There's not much to do right here.
886 return OK; /* not much to do here */
899 char **ap, **ep, *cp;
902 CI ci = &ct->c_ctinfo;
904 /* check for missing subtype */
905 if (!*ci->ci_subtype)
906 ci->ci_subtype = add ("plain", ci->ci_subtype);
909 for (kv = SubText; kv->kv_key; kv++)
910 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
912 ct->c_subtype = kv->kv_value;
914 /* allocate text structure */
915 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
916 adios (NULL, "out of memory");
917 ct->c_ctparams = (void *) t;
919 /* scan for charset parameter */
920 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
921 if (!mh_strcasecmp (*ap, "charset"))
927 chset = "US-ASCII"; /* default for text */
929 /* match character set, or set to unknown */
930 for (kv = Charset; kv->kv_key; kv++)
931 if (!mh_strcasecmp (chset, kv->kv_key))
933 t->tx_charset = kv->kv_value;
936 * If we can not handle character set natively,
937 * then check profile for string to modify the
938 * terminal or display method.
940 if (!check_charset (chset, strlen (chset))) {
941 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
942 if ((cp = context_find (buffer)))
943 ct->c_termproc = getcpy (cp);
955 InitMultiPart (CT ct)
959 char *cp, *dp, **ap, **ep;
960 char *bp, buffer[BUFSIZ];
963 struct part *part, **next;
964 CI ci = &ct->c_ctinfo;
969 * The encoding for multipart messages must be either
970 * 7bit, 8bit, or binary (per RFC2045).
972 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
973 && ct->c_encoding != CE_BINARY) {
975 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
976 ci->ci_type, ci->ci_subtype, ct->c_file);
981 for (kv = SubMultiPart; kv->kv_key; kv++)
982 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
984 ct->c_subtype = kv->kv_value;
987 * Check for "boundary" parameter, which is
988 * required for multipart messages.
991 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
992 if (!mh_strcasecmp (*ap, "boundary")) {
998 /* complain if boundary parameter is missing */
1001 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1002 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1006 /* allocate primary structure for multipart info */
1007 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1008 adios (NULL, "out of memory");
1009 ct->c_ctparams = (void *) m;
1011 /* check if boundary parameter contains only whitespace characters */
1012 for (cp = bp; isspace (*cp); cp++)
1015 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1016 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1020 /* remove trailing whitespace from boundary parameter */
1021 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1026 /* record boundary separators */
1027 m->mp_start = concat (bp, "\n", NULL);
1028 m->mp_stop = concat (bp, "--\n", NULL);
1030 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1031 advise (ct->c_file, "unable to open for reading");
1035 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1037 next = &m->mp_parts;
1041 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1045 pos += strlen (buffer);
1046 if (buffer[0] != '-' || buffer[1] != '-')
1049 if (strcmp (buffer + 2, m->mp_start))
1052 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1053 adios (NULL, "out of memory");
1055 next = &part->mp_next;
1057 if (!(p = get_content (fp, ct->c_file,
1058 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1065 fseek (fp, pos, SEEK_SET);
1068 if (strcmp (buffer + 2, m->mp_start) == 0) {
1072 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1073 if (p->c_end < p->c_begin)
1074 p->c_begin = p->c_end;
1079 if (strcmp (buffer + 2, m->mp_stop) == 0)
1085 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1086 if (!inout && part) {
1088 p->c_end = ct->c_end;
1090 if (p->c_begin >= p->c_end) {
1091 for (next = &m->mp_parts; *next != part;
1092 next = &((*next)->mp_next))
1096 free ((char *) part);
1101 /* reverse the order of the parts for multipart/alternative */
1102 if (ct->c_subtype == MULTI_ALTERNATE)
1106 * label all subparts with part number, and
1107 * then initialize the content of the subpart.
1112 char partnam[BUFSIZ];
1115 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1116 pp = partnam + strlen (partnam);
1121 for (part = m->mp_parts, partnum = 1; part;
1122 part = part->mp_next, partnum++) {
1125 sprintf (pp, "%d", partnum);
1126 p->c_partno = add (partnam, NULL);
1128 /* initialize the content of the subparts */
1129 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1144 * reverse the order of the parts of a multipart
1148 reverse_parts (CT ct)
1151 struct multipart *m;
1152 struct part **base, **bmp, **next, *part;
1154 m = (struct multipart *) ct->c_ctparams;
1156 /* if only one part, just return */
1157 if (!m->mp_parts || !m->mp_parts->mp_next)
1160 /* count number of parts */
1162 for (part = m->mp_parts; part; part = part->mp_next)
1165 /* allocate array of pointers to the parts */
1166 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1167 adios (NULL, "out of memory");
1170 /* point at all the parts */
1171 for (part = m->mp_parts; part; part = part->mp_next)
1175 /* reverse the order of the parts */
1176 next = &m->mp_parts;
1177 for (bmp--; bmp >= base; bmp--) {
1180 next = &part->mp_next;
1184 /* free array of pointers */
1185 free ((char *) base);
1197 CI ci = &ct->c_ctinfo;
1199 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1201 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1202 ci->ci_type, ci->ci_subtype, ct->c_file);
1206 /* check for missing subtype */
1207 if (!*ci->ci_subtype)
1208 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1211 for (kv = SubMessage; kv->kv_key; kv++)
1212 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1214 ct->c_subtype = kv->kv_value;
1216 switch (ct->c_subtype) {
1217 case MESSAGE_RFC822:
1220 case MESSAGE_PARTIAL:
1225 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1226 adios (NULL, "out of memory");
1227 ct->c_ctparams = (void *) p;
1229 /* scan for parameters "id", "number", and "total" */
1230 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1231 if (!mh_strcasecmp (*ap, "id")) {
1232 p->pm_partid = add (*ep, NULL);
1235 if (!mh_strcasecmp (*ap, "number")) {
1236 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1237 || p->pm_partno < 1) {
1240 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1241 *ap, ci->ci_type, ci->ci_subtype,
1242 ct->c_file, TYPE_FIELD);
1247 if (!mh_strcasecmp (*ap, "total")) {
1248 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1257 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1259 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1260 ci->ci_type, ci->ci_subtype,
1261 ct->c_file, TYPE_FIELD);
1267 case MESSAGE_EXTERNAL:
1274 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1275 adios (NULL, "out of memory");
1276 ct->c_ctparams = (void *) e;
1279 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1280 advise (ct->c_file, "unable to open for reading");
1284 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1286 if (!(p = get_content (fp, ct->c_file, 0))) {
1294 if ((exresult = params_external (ct, 0)) != NOTOK
1295 && p->c_ceopenfnx == openMail) {
1299 if ((size = ct->c_end - p->c_begin) <= 0) {
1301 content_error (NULL, ct,
1302 "empty body for access-type=mail-server");
1306 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1307 fseek (p->c_fp, p->c_begin, SEEK_SET);
1309 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1311 adios ("failed", "fread");
1314 adios (NULL, "unexpected EOF from fread");
1317 bp += cc, size -= cc;
1324 p->c_end = p->c_begin;
1329 if (exresult == NOTOK)
1331 if (e->eb_flags == NOTOK)
1334 switch (p->c_type) {
1339 if (p->c_subtype != MESSAGE_RFC822)
1343 e->eb_partno = ct->c_partno;
1345 (*p->c_ctinitfnx) (p);
1360 params_external (CT ct, int composing)
1363 struct exbody *e = (struct exbody *) ct->c_ctparams;
1364 CI ci = &ct->c_ctinfo;
1366 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1367 if (!mh_strcasecmp (*ap, "access-type")) {
1368 struct str2init *s2i;
1369 CT p = e->eb_content;
1371 for (s2i = str2methods; s2i->si_key; s2i++)
1372 if (!mh_strcasecmp (*ep, s2i->si_key))
1376 e->eb_flags = NOTOK;
1377 p->c_encoding = CE_EXTERNAL;
1380 e->eb_access = s2i->si_key;
1381 e->eb_flags = s2i->si_val;
1382 p->c_encoding = CE_EXTERNAL;
1384 /* Call the Init function for this external type */
1385 if ((*s2i->si_init)(p) == NOTOK)
1389 if (!mh_strcasecmp (*ap, "name")) {
1393 if (!mh_strcasecmp (*ap, "permission")) {
1394 e->eb_permission = *ep;
1397 if (!mh_strcasecmp (*ap, "site")) {
1401 if (!mh_strcasecmp (*ap, "directory")) {
1405 if (!mh_strcasecmp (*ap, "mode")) {
1409 if (!mh_strcasecmp (*ap, "size")) {
1410 sscanf (*ep, "%lu", &e->eb_size);
1413 if (!mh_strcasecmp (*ap, "server")) {
1417 if (!mh_strcasecmp (*ap, "subject")) {
1418 e->eb_subject = *ep;
1421 if (composing && !mh_strcasecmp (*ap, "body")) {
1422 e->eb_body = getcpy (*ep);
1427 if (!e->eb_access) {
1429 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1430 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1443 InitApplication (CT ct)
1446 CI ci = &ct->c_ctinfo;
1449 for (kv = SubApplication; kv->kv_key; kv++)
1450 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1452 ct->c_subtype = kv->kv_value;
1459 * TRANSFER ENCODINGS
1463 init_encoding (CT ct, OpenCEFunc openfnx)
1467 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1468 adios (NULL, "out of memory");
1471 ct->c_ceopenfnx = openfnx;
1472 ct->c_ceclosefnx = close_encoding;
1473 ct->c_cesizefnx = size_encoding;
1480 close_encoding (CT ct)
1484 if (!(ce = ct->c_cefile))
1494 static unsigned long
1495 size_encoding (CT ct)
1503 if (!(ce = ct->c_cefile))
1504 return (ct->c_end - ct->c_begin);
1506 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1507 return (long) st.st_size;
1510 if (stat (ce->ce_file, &st) != NOTOK)
1511 return (long) st.st_size;
1516 if (ct->c_encoding == CE_EXTERNAL)
1517 return (ct->c_end - ct->c_begin);
1520 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1521 return (ct->c_end - ct->c_begin);
1523 if (fstat (fd, &st) != NOTOK)
1524 size = (long) st.st_size;
1528 (*ct->c_ceclosefnx) (ct);
1537 static unsigned char b642nib[0x80] = {
1538 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1540 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1541 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1544 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1545 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1546 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1547 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1548 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1549 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1550 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1551 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1552 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1553 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1560 return init_encoding (ct, openBase64);
1565 openBase64 (CT ct, char **file)
1567 int bitno, cc, digested;
1570 unsigned char value, *b, *b1, *b2, *b3;
1571 char *cp, *ep, buffer[BUFSIZ];
1572 /* sbeck -- handle prefixes */
1577 b = (unsigned char *) &bits;
1578 b1 = &b[endian > 0 ? 1 : 2];
1579 b2 = &b[endian > 0 ? 2 : 1];
1580 b3 = &b[endian > 0 ? 3 : 0];
1584 fseek (ce->ce_fp, 0L, SEEK_SET);
1589 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1590 content_error (ce->ce_file, ct, "unable to fopen for reading");
1596 if (*file == NULL) {
1597 ce->ce_file = add (m_scratch ("", tmp), NULL);
1600 ce->ce_file = add (*file, NULL);
1604 /* sbeck@cise.ufl.edu -- handle suffixes */
1606 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1607 invo_name, ci->ci_type, ci->ci_subtype);
1608 cp = context_find (buffer);
1609 if (cp == NULL || *cp == '\0') {
1610 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1612 cp = context_find (buffer);
1614 if (cp != NULL && *cp != '\0')
1615 ce->ce_file = add (cp, ce->ce_file);
1617 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1618 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1622 if ((len = ct->c_end - ct->c_begin) < 0)
1623 adios (NULL, "internal error(1)");
1625 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1626 content_error (ct->c_file, ct, "unable to open for reading");
1630 if ((digested = ct->c_digested))
1631 MD5Init (&mdContext);
1637 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1639 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1641 content_error (ct->c_file, ct, "error reading from");
1645 content_error (NULL, ct, "premature eof");
1653 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1658 if (skip || (*cp & 0x80)
1659 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1661 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1663 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1666 content_error (NULL, ct,
1667 "invalid BASE64 encoding -- continuing");
1671 bits |= value << bitno;
1673 if ((bitno -= 6) < 0) {
1674 putc ((char) *b1, ce->ce_fp);
1676 MD5Update (&mdContext, b1, 1);
1678 putc ((char) *b2, ce->ce_fp);
1680 MD5Update (&mdContext, b2, 1);
1682 putc ((char) *b3, ce->ce_fp);
1684 MD5Update (&mdContext, b3, 1);
1688 if (ferror (ce->ce_fp)) {
1689 content_error (ce->ce_file, ct,
1690 "error writing to");
1693 bitno = 18, bits = 0L, skip = 0;
1699 goto self_delimiting;
1708 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1710 content_error (NULL, ct, "invalid BASE64 encoding");
1715 fseek (ct->c_fp, 0L, SEEK_SET);
1717 if (fflush (ce->ce_fp)) {
1718 content_error (ce->ce_file, ct, "error writing to");
1723 unsigned char digest[16];
1725 MD5Final (digest, &mdContext);
1726 if (memcmp((char *) digest, (char *) ct->c_digest,
1727 sizeof(digest) / sizeof(digest[0])))
1728 content_error (NULL, ct,
1729 "content integrity suspect (digest mismatch) -- continuing");
1732 fprintf (stderr, "content integrity confirmed\n");
1735 fseek (ce->ce_fp, 0L, SEEK_SET);
1738 *file = ce->ce_file;
1739 return fileno (ce->ce_fp);
1742 free_encoding (ct, 0);
1751 static char hex2nib[0x80] = {
1752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1753 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1754 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1756 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1757 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1758 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1759 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1760 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1767 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1774 return init_encoding (ct, openQuoted);
1779 openQuoted (CT ct, char **file)
1781 int cc, digested, len, quoted;
1783 char buffer[BUFSIZ];
1786 /* sbeck -- handle prefixes */
1792 fseek (ce->ce_fp, 0L, SEEK_SET);
1797 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1798 content_error (ce->ce_file, ct, "unable to fopen for reading");
1804 if (*file == NULL) {
1805 ce->ce_file = add (m_scratch ("", tmp), NULL);
1808 ce->ce_file = add (*file, NULL);
1812 /* sbeck@cise.ufl.edu -- handle suffixes */
1814 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1815 invo_name, ci->ci_type, ci->ci_subtype);
1816 cp = context_find (buffer);
1817 if (cp == NULL || *cp == '\0') {
1818 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1820 cp = context_find (buffer);
1822 if (cp != NULL && *cp != '\0')
1823 ce->ce_file = add (cp, ce->ce_file);
1825 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1826 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1830 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1831 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1835 if ((len = ct->c_end - ct->c_begin) < 0)
1836 adios (NULL, "internal error(2)");
1838 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1839 content_error (ct->c_file, ct, "unable to open for reading");
1843 if ((digested = ct->c_digested))
1844 MD5Init (&mdContext);
1851 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1855 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1856 content_error (NULL, ct, "premature eof");
1860 if ((cc = strlen (buffer)) > len)
1864 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1869 for (; cp < ep; cp++) {
1872 if (!isxdigit (*cp)) {
1874 dp = "expecting hexidecimal-digit";
1875 goto invalid_encoding;
1878 mask |= hex2nib[*cp & 0x7f];
1879 putc (mask, ce->ce_fp);
1881 MD5Update (&mdContext, &mask, 1);
1885 putc (*cp, ce->ce_fp);
1887 MD5Update (&mdContext, (unsigned char *) ":", 1);
1891 if (!isxdigit (*cp))
1893 mask = hex2nib[*cp & 0x7f];
1899 if (ferror (ce->ce_fp)) {
1900 content_error (ce->ce_file, ct, "error writing to");
1909 if (*cp < '!' || *cp > '~') {
1911 dp = "expecting character in range [!..~]";
1914 i = strlen (invo_name) + 2;
1915 content_error (NULL, ct,
1916 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1924 putc (*cp, ce->ce_fp);
1927 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1929 MD5Update (&mdContext, (unsigned char *) cp, 1);
1931 if (ferror (ce->ce_fp)) {
1932 content_error (ce->ce_file, ct, "error writing to");
1938 if (*++cp != '\n') {
1947 content_error (NULL, ct,
1948 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1952 fseek (ct->c_fp, 0L, SEEK_SET);
1954 if (fflush (ce->ce_fp)) {
1955 content_error (ce->ce_file, ct, "error writing to");
1960 unsigned char digest[16];
1962 MD5Final (digest, &mdContext);
1963 if (memcmp((char *) digest, (char *) ct->c_digest,
1964 sizeof(digest) / sizeof(digest[0])))
1965 content_error (NULL, ct,
1966 "content integrity suspect (digest mismatch) -- continuing");
1969 fprintf (stderr, "content integrity confirmed\n");
1972 fseek (ce->ce_fp, 0L, SEEK_SET);
1975 *file = ce->ce_file;
1976 return fileno (ce->ce_fp);
1979 free_encoding (ct, 0);
1991 if (init_encoding (ct, open7Bit) == NOTOK)
1994 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2000 open7Bit (CT ct, char **file)
2003 char buffer[BUFSIZ];
2004 /* sbeck -- handle prefixes */
2011 fseek (ce->ce_fp, 0L, SEEK_SET);
2016 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2017 content_error (ce->ce_file, ct, "unable to fopen for reading");
2023 if (*file == NULL) {
2024 ce->ce_file = add (m_scratch ("", tmp), NULL);
2027 ce->ce_file = add (*file, NULL);
2031 /* sbeck@cise.ufl.edu -- handle suffixes */
2033 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2034 invo_name, ci->ci_type, ci->ci_subtype);
2035 cp = context_find (buffer);
2036 if (cp == NULL || *cp == '\0') {
2037 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2039 cp = context_find (buffer);
2041 if (cp != NULL && *cp != '\0')
2042 ce->ce_file = add (cp, ce->ce_file);
2044 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2045 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2049 if (ct->c_type == CT_MULTIPART) {
2051 CI ci = &ct->c_ctinfo;
2054 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2055 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2056 + 1 + strlen (ci->ci_subtype);
2057 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2058 putc (';', ce->ce_fp);
2061 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2063 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2064 fputs ("\n\t", ce->ce_fp);
2067 putc (' ', ce->ce_fp);
2070 fprintf (ce->ce_fp, "%s", buffer);
2074 if (ci->ci_comment) {
2075 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2076 fputs ("\n\t", ce->ce_fp);
2080 putc (' ', ce->ce_fp);
2083 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2086 fprintf (ce->ce_fp, "\n");
2088 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2090 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2091 fprintf (ce->ce_fp, "\n");
2094 if ((len = ct->c_end - ct->c_begin) < 0)
2095 adios (NULL, "internal error(3)");
2097 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2098 content_error (ct->c_file, ct, "unable to open for reading");
2102 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2104 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2106 content_error (ct->c_file, ct, "error reading from");
2110 content_error (NULL, ct, "premature eof");
2118 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2119 if (ferror (ce->ce_fp)) {
2120 content_error (ce->ce_file, ct, "error writing to");
2125 fseek (ct->c_fp, 0L, SEEK_SET);
2127 if (fflush (ce->ce_fp)) {
2128 content_error (ce->ce_file, ct, "error writing to");
2132 fseek (ce->ce_fp, 0L, SEEK_SET);
2135 *file = ce->ce_file;
2136 return fileno (ce->ce_fp);
2139 free_encoding (ct, 0);
2149 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2151 char cachefile[BUFSIZ];
2154 fseek (ce->ce_fp, 0L, SEEK_SET);
2159 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2160 content_error (ce->ce_file, ct, "unable to fopen for reading");
2166 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2167 cachefile, sizeof(cachefile)) != NOTOK) {
2168 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2169 ce->ce_file = getcpy (cachefile);
2173 admonish (cachefile, "unable to fopen for reading");
2180 *file = ce->ce_file;
2181 *fd = fileno (ce->ce_fp);
2192 return init_encoding (ct, openFile);
2197 openFile (CT ct, char **file)
2200 char cachefile[BUFSIZ];
2201 struct exbody *e = ct->c_ctexbody;
2202 CE ce = ct->c_cefile;
2204 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2216 content_error (NULL, ct, "missing name parameter");
2220 ce->ce_file = getcpy (e->eb_name);
2223 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2224 content_error (ce->ce_file, ct, "unable to fopen for reading");
2228 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2229 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2230 cachefile, sizeof(cachefile)) != NOTOK) {
2234 mask = umask (cachetype ? ~m_gmprot () : 0222);
2235 if ((fp = fopen (cachefile, "w"))) {
2237 char buffer[BUFSIZ];
2238 FILE *gp = ce->ce_fp;
2240 fseek (gp, 0L, SEEK_SET);
2242 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2244 fwrite (buffer, sizeof(*buffer), cc, fp);
2248 admonish (ce->ce_file, "error reading");
2253 admonish (cachefile, "error writing");
2261 fseek (ce->ce_fp, 0L, SEEK_SET);
2262 *file = ce->ce_file;
2263 return fileno (ce->ce_fp);
2273 return init_encoding (ct, openFTP);
2278 openFTP (CT ct, char **file)
2280 int cachetype, caching, fd;
2282 char *bp, *ftp, *user, *pass;
2283 char buffer[BUFSIZ], cachefile[BUFSIZ];
2286 static char *username = NULL;
2287 static char *password = NULL;
2292 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2300 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2311 if (!e->eb_name || !e->eb_site) {
2312 content_error (NULL, ct, "missing %s parameter",
2313 e->eb_name ? "site": "name");
2320 pidcheck (pidwait (xpid, NOTOK));
2324 /* Get the buffer ready to go */
2326 buflen = sizeof(buffer);
2329 * Construct the query message for user
2331 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2337 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2343 snprintf (bp, buflen, "\n using %sFTP from site %s",
2344 e->eb_flags ? "anonymous " : "", e->eb_site);
2349 if (e->eb_size > 0) {
2350 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2355 snprintf (bp, buflen, "? ");
2358 * Now, check the answer
2360 if (!getanswer (buffer))
2365 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2368 ruserpass (e->eb_site, &username, &password);
2373 ce->ce_unlink = (*file == NULL);
2375 cachefile[0] = '\0';
2376 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2377 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2378 cachefile, sizeof(cachefile)) != NOTOK) {
2379 if (*file == NULL) {
2386 ce->ce_file = add (*file, NULL);
2388 ce->ce_file = add (cachefile, NULL);
2390 ce->ce_file = add (m_scratch ("", tmp), NULL);
2392 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2393 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2401 int child_id, i, vecp;
2405 vec[vecp++] = r1bindex (ftp, '/');
2406 vec[vecp++] = e->eb_site;
2409 vec[vecp++] = e->eb_dir;
2410 vec[vecp++] = e->eb_name;
2411 vec[vecp++] = ce->ce_file,
2412 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2413 ? "ascii" : "binary";
2418 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2422 adios ("fork", "unable to");
2426 close (fileno (ce->ce_fp));
2428 fprintf (stderr, "unable to exec ");
2434 if (pidXwait (child_id, NULL)) {
2438 username = password = NULL;
2447 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2449 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2456 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2461 mask = umask (cachetype ? ~m_gmprot () : 0222);
2462 if ((fp = fopen (cachefile, "w"))) {
2464 FILE *gp = ce->ce_fp;
2466 fseek (gp, 0L, SEEK_SET);
2468 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2470 fwrite (buffer, sizeof(*buffer), cc, fp);
2474 admonish (ce->ce_file, "error reading");
2479 admonish (cachefile, "error writing");
2488 fseek (ce->ce_fp, 0L, SEEK_SET);
2489 *file = ce->ce_file;
2490 return fileno (ce->ce_fp);
2501 return init_encoding (ct, openMail);
2506 openMail (CT ct, char **file)
2508 int child_id, fd, i, vecp;
2510 char *bp, buffer[BUFSIZ], *vec[7];
2511 struct exbody *e = ct->c_ctexbody;
2512 CE ce = ct->c_cefile;
2514 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2525 if (!e->eb_server) {
2526 content_error (NULL, ct, "missing server parameter");
2533 pidcheck (pidwait (xpid, NOTOK));
2537 /* Get buffer ready to go */
2539 buflen = sizeof(buffer);
2541 /* Now, construct query message */
2542 snprintf (bp, buflen, "Retrieve content");
2548 snprintf (bp, buflen, " %s", e->eb_partno);
2554 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2556 e->eb_subject ? e->eb_subject : e->eb_body);
2558 /* Now, check answer */
2559 if (!getanswer (buffer))
2563 vec[vecp++] = r1bindex (mailproc, '/');
2564 vec[vecp++] = e->eb_server;
2565 vec[vecp++] = "-subject";
2566 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2567 vec[vecp++] = "-body";
2568 vec[vecp++] = e->eb_body;
2571 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2575 advise ("fork", "unable to");
2579 execvp (mailproc, vec);
2580 fprintf (stderr, "unable to exec ");
2586 if (pidXwait (child_id, NULL) == OK)
2587 advise (NULL, "request sent");
2591 if (*file == NULL) {
2592 ce->ce_file = add (m_scratch ("", tmp), NULL);
2595 ce->ce_file = add (*file, NULL);
2599 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2600 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2605 free (ct->c_showproc);
2606 ct->c_showproc = add ("true", NULL);
2608 fseek (ce->ce_fp, 0L, SEEK_SET);
2609 *file = ce->ce_file;
2610 return fileno (ce->ce_fp);
2615 readDigest (CT ct, char *cp)
2620 unsigned char *dp, value, *ep;
2621 unsigned char *b, *b1, *b2, *b3;
2623 b = (unsigned char *) &bits,
2624 b1 = &b[endian > 0 ? 1 : 2],
2625 b2 = &b[endian > 0 ? 2 : 1],
2626 b3 = &b[endian > 0 ? 3 : 0];
2631 for (ep = (dp = ct->c_digest)
2632 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2637 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2639 fprintf (stderr, "invalid BASE64 encoding\n");
2643 bits |= value << bitno;
2645 if ((bitno -= 6) < 0) {
2646 if (dp + (3 - skip) > ep)
2647 goto invalid_digest;
2662 goto self_delimiting;
2667 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2677 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2685 fprintf (stderr, "MD5 digest=");
2686 for (dp = ct->c_digest; dp < ep; dp++)
2687 fprintf (stderr, "%02x", *dp & 0xff);
2688 fprintf (stderr, "\n");