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);
1864 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1865 content_error (NULL, ct, "premature eof");
1869 if ((cc = strlen (buffer)) > len)
1873 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1878 for (; cp < ep; cp++) {
1881 if (!isxdigit (*cp)) {
1883 dp = "expecting hexidecimal-digit";
1884 goto invalid_encoding;
1887 mask |= hex2nib[*cp & 0x7f];
1888 putc (mask, ce->ce_fp);
1890 MD5Update (&mdContext, &mask, 1);
1894 putc (*cp, ce->ce_fp);
1896 MD5Update (&mdContext, (unsigned char *) ":", 1);
1900 if (!isxdigit (*cp))
1902 mask = hex2nib[*cp & 0x7f];
1908 if (ferror (ce->ce_fp)) {
1909 content_error (ce->ce_file, ct, "error writing to");
1918 if (*cp < '!' || *cp > '~') {
1920 dp = "expecting character in range [!..~]";
1923 i = strlen (invo_name) + 2;
1924 content_error (NULL, ct,
1925 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1933 putc (*cp, ce->ce_fp);
1936 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1938 MD5Update (&mdContext, (unsigned char *) cp, 1);
1940 if (ferror (ce->ce_fp)) {
1941 content_error (ce->ce_file, ct, "error writing to");
1947 if (*++cp != '\n') {
1956 content_error (NULL, ct,
1957 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1961 fseek (ct->c_fp, 0L, SEEK_SET);
1963 if (fflush (ce->ce_fp)) {
1964 content_error (ce->ce_file, ct, "error writing to");
1969 unsigned char digest[16];
1971 MD5Final (digest, &mdContext);
1972 if (memcmp((char *) digest, (char *) ct->c_digest,
1973 sizeof(digest) / sizeof(digest[0])))
1974 content_error (NULL, ct,
1975 "content integrity suspect (digest mismatch) -- continuing");
1978 fprintf (stderr, "content integrity confirmed\n");
1981 fseek (ce->ce_fp, 0L, SEEK_SET);
1984 *file = ce->ce_file;
1985 return fileno (ce->ce_fp);
1988 free_encoding (ct, 0);
2000 if (init_encoding (ct, open7Bit) == NOTOK)
2003 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2009 open7Bit (CT ct, char **file)
2012 char buffer[BUFSIZ];
2013 /* sbeck -- handle prefixes */
2020 fseek (ce->ce_fp, 0L, SEEK_SET);
2025 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2026 content_error (ce->ce_file, ct, "unable to fopen for reading");
2032 if (*file == NULL) {
2033 ce->ce_file = add (m_scratch ("", tmp), NULL);
2036 ce->ce_file = add (*file, NULL);
2040 /* sbeck@cise.ufl.edu -- handle suffixes */
2042 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2043 invo_name, ci->ci_type, ci->ci_subtype);
2044 cp = context_find (buffer);
2045 if (cp == NULL || *cp == '\0') {
2046 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2048 cp = context_find (buffer);
2050 if (cp != NULL && *cp != '\0')
2051 ce->ce_file = add (cp, ce->ce_file);
2053 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2054 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2058 if (ct->c_type == CT_MULTIPART) {
2060 CI ci = &ct->c_ctinfo;
2063 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2064 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2065 + 1 + strlen (ci->ci_subtype);
2066 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2067 putc (';', ce->ce_fp);
2070 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2072 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2073 fputs ("\n\t", ce->ce_fp);
2076 putc (' ', ce->ce_fp);
2079 fprintf (ce->ce_fp, "%s", buffer);
2083 if (ci->ci_comment) {
2084 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2085 fputs ("\n\t", ce->ce_fp);
2089 putc (' ', ce->ce_fp);
2092 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2095 fprintf (ce->ce_fp, "\n");
2097 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2099 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2100 fprintf (ce->ce_fp, "\n");
2103 if ((len = ct->c_end - ct->c_begin) < 0)
2104 adios (NULL, "internal error(3)");
2106 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2107 content_error (ct->c_file, ct, "unable to open for reading");
2111 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2113 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2115 content_error (ct->c_file, ct, "error reading from");
2119 content_error (NULL, ct, "premature eof");
2127 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2128 if (ferror (ce->ce_fp)) {
2129 content_error (ce->ce_file, ct, "error writing to");
2134 fseek (ct->c_fp, 0L, SEEK_SET);
2136 if (fflush (ce->ce_fp)) {
2137 content_error (ce->ce_file, ct, "error writing to");
2141 fseek (ce->ce_fp, 0L, SEEK_SET);
2144 *file = ce->ce_file;
2145 return fileno (ce->ce_fp);
2148 free_encoding (ct, 0);
2158 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2160 char cachefile[BUFSIZ];
2163 fseek (ce->ce_fp, 0L, SEEK_SET);
2168 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2169 content_error (ce->ce_file, ct, "unable to fopen for reading");
2175 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2176 cachefile, sizeof(cachefile)) != NOTOK) {
2177 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2178 ce->ce_file = getcpy (cachefile);
2182 admonish (cachefile, "unable to fopen for reading");
2189 *file = ce->ce_file;
2190 *fd = fileno (ce->ce_fp);
2201 return init_encoding (ct, openFile);
2206 openFile (CT ct, char **file)
2209 char cachefile[BUFSIZ];
2210 struct exbody *e = ct->c_ctexbody;
2211 CE ce = ct->c_cefile;
2213 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2225 content_error (NULL, ct, "missing name parameter");
2229 ce->ce_file = getcpy (e->eb_name);
2232 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2233 content_error (ce->ce_file, ct, "unable to fopen for reading");
2237 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2238 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2239 cachefile, sizeof(cachefile)) != NOTOK) {
2243 mask = umask (cachetype ? ~m_gmprot () : 0222);
2244 if ((fp = fopen (cachefile, "w"))) {
2246 char buffer[BUFSIZ];
2247 FILE *gp = ce->ce_fp;
2249 fseek (gp, 0L, SEEK_SET);
2251 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2253 fwrite (buffer, sizeof(*buffer), cc, fp);
2257 admonish (ce->ce_file, "error reading");
2262 admonish (cachefile, "error writing");
2270 fseek (ce->ce_fp, 0L, SEEK_SET);
2271 *file = ce->ce_file;
2272 return fileno (ce->ce_fp);
2282 return init_encoding (ct, openFTP);
2287 openFTP (CT ct, char **file)
2289 int cachetype, caching, fd;
2291 char *bp, *ftp, *user, *pass;
2292 char buffer[BUFSIZ], cachefile[BUFSIZ];
2295 static char *username = NULL;
2296 static char *password = NULL;
2301 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2309 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2320 if (!e->eb_name || !e->eb_site) {
2321 content_error (NULL, ct, "missing %s parameter",
2322 e->eb_name ? "site": "name");
2329 pidcheck (pidwait (xpid, NOTOK));
2333 /* Get the buffer ready to go */
2335 buflen = sizeof(buffer);
2338 * Construct the query message for user
2340 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2346 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2352 snprintf (bp, buflen, "\n using %sFTP from site %s",
2353 e->eb_flags ? "anonymous " : "", e->eb_site);
2358 if (e->eb_size > 0) {
2359 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2364 snprintf (bp, buflen, "? ");
2367 * Now, check the answer
2369 if (!getanswer (buffer))
2374 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2377 ruserpass (e->eb_site, &username, &password);
2382 ce->ce_unlink = (*file == NULL);
2384 cachefile[0] = '\0';
2385 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2386 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2387 cachefile, sizeof(cachefile)) != NOTOK) {
2388 if (*file == NULL) {
2395 ce->ce_file = add (*file, NULL);
2397 ce->ce_file = add (cachefile, NULL);
2399 ce->ce_file = add (m_scratch ("", tmp), NULL);
2401 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2402 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2410 int child_id, i, vecp;
2414 vec[vecp++] = r1bindex (ftp, '/');
2415 vec[vecp++] = e->eb_site;
2418 vec[vecp++] = e->eb_dir;
2419 vec[vecp++] = e->eb_name;
2420 vec[vecp++] = ce->ce_file,
2421 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2422 ? "ascii" : "binary";
2427 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2431 adios ("fork", "unable to");
2435 close (fileno (ce->ce_fp));
2437 fprintf (stderr, "unable to exec ");
2443 if (pidXwait (child_id, NULL)) {
2447 username = password = NULL;
2456 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2458 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2465 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2470 mask = umask (cachetype ? ~m_gmprot () : 0222);
2471 if ((fp = fopen (cachefile, "w"))) {
2473 FILE *gp = ce->ce_fp;
2475 fseek (gp, 0L, SEEK_SET);
2477 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2479 fwrite (buffer, sizeof(*buffer), cc, fp);
2483 admonish (ce->ce_file, "error reading");
2488 admonish (cachefile, "error writing");
2497 fseek (ce->ce_fp, 0L, SEEK_SET);
2498 *file = ce->ce_file;
2499 return fileno (ce->ce_fp);
2510 return init_encoding (ct, openMail);
2515 openMail (CT ct, char **file)
2517 int child_id, fd, i, vecp;
2519 char *bp, buffer[BUFSIZ], *vec[7];
2520 struct exbody *e = ct->c_ctexbody;
2521 CE ce = ct->c_cefile;
2523 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2534 if (!e->eb_server) {
2535 content_error (NULL, ct, "missing server parameter");
2542 pidcheck (pidwait (xpid, NOTOK));
2546 /* Get buffer ready to go */
2548 buflen = sizeof(buffer);
2550 /* Now, construct query message */
2551 snprintf (bp, buflen, "Retrieve content");
2557 snprintf (bp, buflen, " %s", e->eb_partno);
2563 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2565 e->eb_subject ? e->eb_subject : e->eb_body);
2567 /* Now, check answer */
2568 if (!getanswer (buffer))
2572 vec[vecp++] = r1bindex (mailproc, '/');
2573 vec[vecp++] = e->eb_server;
2574 vec[vecp++] = "-subject";
2575 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2576 vec[vecp++] = "-body";
2577 vec[vecp++] = e->eb_body;
2580 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2584 advise ("fork", "unable to");
2588 execvp (mailproc, vec);
2589 fprintf (stderr, "unable to exec ");
2595 if (pidXwait (child_id, NULL) == OK)
2596 advise (NULL, "request sent");
2600 if (*file == NULL) {
2601 ce->ce_file = add (m_scratch ("", tmp), NULL);
2604 ce->ce_file = add (*file, NULL);
2608 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2609 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2614 free (ct->c_showproc);
2615 ct->c_showproc = add ("true", NULL);
2617 fseek (ce->ce_fp, 0L, SEEK_SET);
2618 *file = ce->ce_file;
2619 return fileno (ce->ce_fp);
2624 readDigest (CT ct, char *cp)
2629 unsigned char *dp, value, *ep;
2630 unsigned char *b, *b1, *b2, *b3;
2632 b = (unsigned char *) &bits,
2633 b1 = &b[endian > 0 ? 1 : 2],
2634 b2 = &b[endian > 0 ? 2 : 1],
2635 b3 = &b[endian > 0 ? 3 : 0];
2640 for (ep = (dp = ct->c_digest)
2641 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2646 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2648 fprintf (stderr, "invalid BASE64 encoding\n");
2652 bits |= value << bitno;
2654 if ((bitno -= 6) < 0) {
2655 if (dp + (3 - skip) > ep)
2656 goto invalid_digest;
2671 goto self_delimiting;
2676 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2686 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2694 fprintf (stderr, "MD5 digest=");
2695 for (dp = ct->c_digest; dp < ep; dp++)
2696 fprintf (stderr, "%02x", *dp & 0xff);
2697 fprintf (stderr, "\n");