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)
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)) {
381 unsigned char *cp, *dp;
384 advise (NULL, "message %s has multiple %s: fields",
385 ct->c_file, VRSN_FIELD);
388 ct->c_vrsn = add (hp->value, NULL);
390 /* Now, cleanup this field */
393 while (isspace (*cp))
395 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
397 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
402 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
404 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
407 for (dp = cp; istoken (*dp); dp++)
411 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
414 admonish (NULL, "message %s has unknown value for %s: field (%s)",
415 ct->c_file, VRSN_FIELD, cp);
418 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
419 /* Get Content-Type field */
420 struct str2init *s2i;
421 CI ci = &ct->c_ctinfo;
423 /* Check if we've already seen a Content-Type header */
425 advise (NULL, "message %s has multiple %s: fields",
426 ct->c_file, TYPE_FIELD);
430 /* Parse the Content-Type field */
431 if (get_ctinfo (hp->value, ct) == NOTOK)
435 * Set the Init function and the internal
436 * flag for this content type.
438 for (s2i = str2cts; s2i->si_key; s2i++)
439 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
441 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
443 ct->c_type = s2i->si_val;
444 ct->c_ctinitfnx = s2i->si_init;
446 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
447 /* Get Content-Transfer-Encoding field */
449 unsigned char *cp, *dp;
450 struct str2init *s2i;
453 * Check if we've already seen the
454 * Content-Transfer-Encoding field
457 advise (NULL, "message %s has multiple %s: fields",
458 ct->c_file, ENCODING_FIELD);
462 /* get copy of this field */
463 ct->c_celine = cp = add (hp->value, NULL);
465 while (isspace (*cp))
467 for (dp = cp; istoken (*dp); dp++)
473 * Find the internal flag and Init function
474 * for this transfer encoding.
476 for (s2i = str2ces; s2i->si_key; s2i++)
477 if (!mh_strcasecmp (cp, s2i->si_key))
479 if (!s2i->si_key && !uprf (cp, "X-"))
482 ct->c_encoding = s2i->si_val;
484 /* Call the Init function for this encoding */
485 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
488 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
489 /* Get Content-MD5 field */
490 unsigned char *cp, *dp;
496 if (ct->c_digested) {
497 advise (NULL, "message %s has multiple %s: fields",
498 ct->c_file, MD5_FIELD);
502 ep = cp = add (hp->value, NULL); /* get a copy */
504 while (isspace (*cp))
506 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
508 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
513 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
515 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
520 for (dp = cp; *dp && !isspace (*dp); dp++)
528 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
529 /* Get Content-ID field */
530 ct->c_id = add (hp->value, ct->c_id);
532 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
533 /* Get Content-Description field */
534 ct->c_descr = add (hp->value, ct->c_descr);
538 hp = hp->next; /* next header field */
542 * Check if we saw a Content-Type field.
543 * If not, then assign a default value for
544 * it, and the Init function.
548 * If we are inside a multipart/digest message,
549 * so default type is message/rfc822
552 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
554 ct->c_type = CT_MESSAGE;
555 ct->c_ctinitfnx = InitMessage;
558 * Else default type is text/plain
560 if (get_ctinfo ("text/plain", ct) == NOTOK)
562 ct->c_type = CT_TEXT;
563 ct->c_ctinitfnx = InitText;
567 /* Use default Transfer-Encoding, if necessary */
569 ct->c_encoding = CE_7BIT;
582 * small routine to add header field to list
586 add_header (CT ct, char *name, char *value)
590 /* allocate header field structure */
591 hp = mh_xmalloc (sizeof(*hp));
593 /* link data into header structure */
598 /* link header structure into the list */
599 if (ct->c_first_hf == NULL) {
600 ct->c_first_hf = hp; /* this is the first */
603 ct->c_last_hf->next = hp; /* add it to the end */
612 * Parse Content-Type line and fill in the
613 * information of the CTinfo structure.
617 get_ctinfo (unsigned char *cp, CT ct)
626 i = strlen (invo_name) + 2;
628 /* store copy of Content-Type line */
629 cp = ct->c_ctline = add (cp, NULL);
631 while (isspace (*cp)) /* trim leading spaces */
634 /* change newlines to spaces */
635 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
638 /* trim trailing spaces */
639 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
645 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
647 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
650 for (dp = cp; istoken (*dp); dp++)
653 ci->ci_type = add (cp, NULL); /* store content type */
657 advise (NULL, "invalid %s: field in message %s (empty type)",
658 TYPE_FIELD, ct->c_file);
662 /* down case the content type string */
663 for (dp = ci->ci_type; *dp; dp++)
664 if (isalpha(*dp) && isupper (*dp))
667 while (isspace (*cp))
670 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
674 ci->ci_subtype = add ("", NULL);
679 while (isspace (*cp))
682 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
685 for (dp = cp; istoken (*dp); dp++)
688 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
691 if (!*ci->ci_subtype) {
693 "invalid %s: field in message %s (empty subtype for \"%s\")",
694 TYPE_FIELD, ct->c_file, ci->ci_type);
698 /* down case the content subtype string */
699 for (dp = ci->ci_subtype; *dp; dp++)
700 if (isalpha(*dp) && isupper (*dp))
704 while (isspace (*cp))
707 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
711 * Parse attribute/value pairs given with Content-Type
713 ep = (ap = ci->ci_attrs) + NPARMS;
720 "too many parameters in message %s's %s: field (%d max)",
721 ct->c_file, TYPE_FIELD, NPARMS);
726 while (isspace (*cp))
729 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
734 "extraneous trailing ';' in message %s's %s: parameter list",
735 ct->c_file, TYPE_FIELD);
739 /* down case the attribute name */
740 for (dp = cp; istoken (*dp); dp++)
741 if (isalpha(*dp) && isupper (*dp))
744 for (up = dp; isspace (*dp);)
746 if (dp == cp || *dp != '=') {
748 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
749 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
753 vp = (*ap = add (cp, NULL)) + (up - cp);
755 for (dp++; isspace (*dp);)
758 /* now add the attribute value */
759 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
762 for (cp = ++dp, dp = vp;;) {
767 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
768 ct->c_file, TYPE_FIELD, i, i, "", *ap);
773 if ((c = *cp++) == '\0')
788 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
794 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
795 ct->c_file, TYPE_FIELD, i, i, "", *ap);
800 while (isspace (*cp))
803 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
808 * Check if anything is left over
811 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
812 ct->c_file, TYPE_FIELD, i, i, "", cp);
820 get_comment (CT ct, unsigned char **ap, int istype)
825 char c, buffer[BUFSIZ], *dp;
837 advise (NULL, "invalid comment in message %s's %s: field",
838 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
843 if ((c = *cp++) == '\0')
866 if ((dp = ci->ci_comment)) {
867 ci->ci_comment = concat (dp, " ", buffer, NULL);
870 ci->ci_comment = add (buffer, NULL);
874 while (isspace (*cp))
885 * Handles content types audio, image, and video.
886 * There's not much to do right here.
892 return OK; /* not much to do here */
905 char **ap, **ep, *cp;
908 CI ci = &ct->c_ctinfo;
910 /* check for missing subtype */
911 if (!*ci->ci_subtype)
912 ci->ci_subtype = add ("plain", ci->ci_subtype);
915 for (kv = SubText; kv->kv_key; kv++)
916 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
918 ct->c_subtype = kv->kv_value;
920 /* allocate text structure */
921 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
922 adios (NULL, "out of memory");
923 ct->c_ctparams = (void *) t;
925 /* scan for charset parameter */
926 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
927 if (!mh_strcasecmp (*ap, "charset"))
933 chset = "US-ASCII"; /* default for text */
935 /* match character set, or set to unknown */
936 for (kv = Charset; kv->kv_key; kv++)
937 if (!mh_strcasecmp (chset, kv->kv_key))
939 t->tx_charset = kv->kv_value;
942 * If we can not handle character set natively,
943 * then check profile for string to modify the
944 * terminal or display method.
946 if (!check_charset (chset, strlen (chset))) {
947 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
948 if ((cp = context_find (buffer)))
949 ct->c_termproc = getcpy (cp);
961 InitMultiPart (CT ct)
965 unsigned char *cp, *dp;
967 char *bp, buffer[BUFSIZ];
970 struct part *part, **next;
971 CI ci = &ct->c_ctinfo;
976 * The encoding for multipart messages must be either
977 * 7bit, 8bit, or binary (per RFC2045).
979 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
980 && ct->c_encoding != CE_BINARY) {
982 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
983 ci->ci_type, ci->ci_subtype, ct->c_file);
988 for (kv = SubMultiPart; kv->kv_key; kv++)
989 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
991 ct->c_subtype = kv->kv_value;
994 * Check for "boundary" parameter, which is
995 * required for multipart messages.
998 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
999 if (!mh_strcasecmp (*ap, "boundary")) {
1005 /* complain if boundary parameter is missing */
1008 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1009 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1013 /* allocate primary structure for multipart info */
1014 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1015 adios (NULL, "out of memory");
1016 ct->c_ctparams = (void *) m;
1018 /* check if boundary parameter contains only whitespace characters */
1019 for (cp = bp; isspace (*cp); cp++)
1022 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1023 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1027 /* remove trailing whitespace from boundary parameter */
1028 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1033 /* record boundary separators */
1034 m->mp_start = concat (bp, "\n", NULL);
1035 m->mp_stop = concat (bp, "--\n", NULL);
1037 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1038 advise (ct->c_file, "unable to open for reading");
1042 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1044 next = &m->mp_parts;
1048 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1052 pos += strlen (buffer);
1053 if (buffer[0] != '-' || buffer[1] != '-')
1056 if (strcmp (buffer + 2, m->mp_start))
1059 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1060 adios (NULL, "out of memory");
1062 next = &part->mp_next;
1064 if (!(p = get_content (fp, ct->c_file,
1065 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1072 fseek (fp, pos, SEEK_SET);
1075 if (strcmp (buffer + 2, m->mp_start) == 0) {
1079 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1080 if (p->c_end < p->c_begin)
1081 p->c_begin = p->c_end;
1086 if (strcmp (buffer + 2, m->mp_stop) == 0)
1092 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1093 if (!inout && part) {
1095 p->c_end = ct->c_end;
1097 if (p->c_begin >= p->c_end) {
1098 for (next = &m->mp_parts; *next != part;
1099 next = &((*next)->mp_next))
1103 free ((char *) part);
1108 /* reverse the order of the parts for multipart/alternative */
1109 if (ct->c_subtype == MULTI_ALTERNATE)
1113 * label all subparts with part number, and
1114 * then initialize the content of the subpart.
1119 char partnam[BUFSIZ];
1122 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1123 pp = partnam + strlen (partnam);
1128 for (part = m->mp_parts, partnum = 1; part;
1129 part = part->mp_next, partnum++) {
1132 sprintf (pp, "%d", partnum);
1133 p->c_partno = add (partnam, NULL);
1135 /* initialize the content of the subparts */
1136 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1151 * reverse the order of the parts of a multipart
1155 reverse_parts (CT ct)
1158 struct multipart *m;
1159 struct part **base, **bmp, **next, *part;
1161 m = (struct multipart *) ct->c_ctparams;
1163 /* if only one part, just return */
1164 if (!m->mp_parts || !m->mp_parts->mp_next)
1167 /* count number of parts */
1169 for (part = m->mp_parts; part; part = part->mp_next)
1172 /* allocate array of pointers to the parts */
1173 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1174 adios (NULL, "out of memory");
1177 /* point at all the parts */
1178 for (part = m->mp_parts; part; part = part->mp_next)
1182 /* reverse the order of the parts */
1183 next = &m->mp_parts;
1184 for (bmp--; bmp >= base; bmp--) {
1187 next = &part->mp_next;
1191 /* free array of pointers */
1192 free ((char *) base);
1204 CI ci = &ct->c_ctinfo;
1206 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1208 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1209 ci->ci_type, ci->ci_subtype, ct->c_file);
1213 /* check for missing subtype */
1214 if (!*ci->ci_subtype)
1215 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1218 for (kv = SubMessage; kv->kv_key; kv++)
1219 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1221 ct->c_subtype = kv->kv_value;
1223 switch (ct->c_subtype) {
1224 case MESSAGE_RFC822:
1227 case MESSAGE_PARTIAL:
1232 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1233 adios (NULL, "out of memory");
1234 ct->c_ctparams = (void *) p;
1236 /* scan for parameters "id", "number", and "total" */
1237 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1238 if (!mh_strcasecmp (*ap, "id")) {
1239 p->pm_partid = add (*ep, NULL);
1242 if (!mh_strcasecmp (*ap, "number")) {
1243 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1244 || p->pm_partno < 1) {
1247 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1248 *ap, ci->ci_type, ci->ci_subtype,
1249 ct->c_file, TYPE_FIELD);
1254 if (!mh_strcasecmp (*ap, "total")) {
1255 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1264 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1266 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1267 ci->ci_type, ci->ci_subtype,
1268 ct->c_file, TYPE_FIELD);
1274 case MESSAGE_EXTERNAL:
1281 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1282 adios (NULL, "out of memory");
1283 ct->c_ctparams = (void *) e;
1286 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1287 advise (ct->c_file, "unable to open for reading");
1291 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1293 if (!(p = get_content (fp, ct->c_file, 0))) {
1301 if ((exresult = params_external (ct, 0)) != NOTOK
1302 && p->c_ceopenfnx == openMail) {
1306 if ((size = ct->c_end - p->c_begin) <= 0) {
1308 content_error (NULL, ct,
1309 "empty body for access-type=mail-server");
1313 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1314 fseek (p->c_fp, p->c_begin, SEEK_SET);
1316 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1318 adios ("failed", "fread");
1321 adios (NULL, "unexpected EOF from fread");
1324 bp += cc, size -= cc;
1331 p->c_end = p->c_begin;
1336 if (exresult == NOTOK)
1338 if (e->eb_flags == NOTOK)
1341 switch (p->c_type) {
1346 if (p->c_subtype != MESSAGE_RFC822)
1350 e->eb_partno = ct->c_partno;
1352 (*p->c_ctinitfnx) (p);
1367 params_external (CT ct, int composing)
1370 struct exbody *e = (struct exbody *) ct->c_ctparams;
1371 CI ci = &ct->c_ctinfo;
1373 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1374 if (!mh_strcasecmp (*ap, "access-type")) {
1375 struct str2init *s2i;
1376 CT p = e->eb_content;
1378 for (s2i = str2methods; s2i->si_key; s2i++)
1379 if (!mh_strcasecmp (*ep, s2i->si_key))
1383 e->eb_flags = NOTOK;
1384 p->c_encoding = CE_EXTERNAL;
1387 e->eb_access = s2i->si_key;
1388 e->eb_flags = s2i->si_val;
1389 p->c_encoding = CE_EXTERNAL;
1391 /* Call the Init function for this external type */
1392 if ((*s2i->si_init)(p) == NOTOK)
1396 if (!mh_strcasecmp (*ap, "name")) {
1400 if (!mh_strcasecmp (*ap, "permission")) {
1401 e->eb_permission = *ep;
1404 if (!mh_strcasecmp (*ap, "site")) {
1408 if (!mh_strcasecmp (*ap, "directory")) {
1412 if (!mh_strcasecmp (*ap, "mode")) {
1416 if (!mh_strcasecmp (*ap, "size")) {
1417 sscanf (*ep, "%lu", &e->eb_size);
1420 if (!mh_strcasecmp (*ap, "server")) {
1424 if (!mh_strcasecmp (*ap, "subject")) {
1425 e->eb_subject = *ep;
1428 if (composing && !mh_strcasecmp (*ap, "body")) {
1429 e->eb_body = getcpy (*ep);
1434 if (!e->eb_access) {
1436 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1437 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1450 InitApplication (CT ct)
1453 CI ci = &ct->c_ctinfo;
1456 for (kv = SubApplication; kv->kv_key; kv++)
1457 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1459 ct->c_subtype = kv->kv_value;
1466 * TRANSFER ENCODINGS
1470 init_encoding (CT ct, OpenCEFunc openfnx)
1474 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1475 adios (NULL, "out of memory");
1478 ct->c_ceopenfnx = openfnx;
1479 ct->c_ceclosefnx = close_encoding;
1480 ct->c_cesizefnx = size_encoding;
1487 close_encoding (CT ct)
1491 if (!(ce = ct->c_cefile))
1501 static unsigned long
1502 size_encoding (CT ct)
1510 if (!(ce = ct->c_cefile))
1511 return (ct->c_end - ct->c_begin);
1513 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1514 return (long) st.st_size;
1517 if (stat (ce->ce_file, &st) != NOTOK)
1518 return (long) st.st_size;
1523 if (ct->c_encoding == CE_EXTERNAL)
1524 return (ct->c_end - ct->c_begin);
1527 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1528 return (ct->c_end - ct->c_begin);
1530 if (fstat (fd, &st) != NOTOK)
1531 size = (long) st.st_size;
1535 (*ct->c_ceclosefnx) (ct);
1544 static unsigned char b642nib[0x80] = {
1545 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1551 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1552 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1553 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1554 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1555 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1556 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1557 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1558 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1559 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1560 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1567 return init_encoding (ct, openBase64);
1572 openBase64 (CT ct, char **file)
1574 int bitno, cc, digested;
1577 unsigned char value, *b, *b1, *b2, *b3;
1578 unsigned char *cp, *ep;
1579 char buffer[BUFSIZ];
1580 /* sbeck -- handle prefixes */
1585 b = (unsigned char *) &bits;
1586 b1 = &b[endian > 0 ? 1 : 2];
1587 b2 = &b[endian > 0 ? 2 : 1];
1588 b3 = &b[endian > 0 ? 3 : 0];
1592 fseek (ce->ce_fp, 0L, SEEK_SET);
1597 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1598 content_error (ce->ce_file, ct, "unable to fopen for reading");
1604 if (*file == NULL) {
1605 ce->ce_file = add (m_scratch ("", tmp), NULL);
1608 ce->ce_file = add (*file, NULL);
1612 /* sbeck@cise.ufl.edu -- handle suffixes */
1614 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1615 invo_name, ci->ci_type, ci->ci_subtype);
1616 cp = context_find (buffer);
1617 if (cp == NULL || *cp == '\0') {
1618 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1620 cp = context_find (buffer);
1622 if (cp != NULL && *cp != '\0')
1623 ce->ce_file = add (cp, ce->ce_file);
1625 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1626 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1630 if ((len = ct->c_end - ct->c_begin) < 0)
1631 adios (NULL, "internal error(1)");
1633 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1634 content_error (ct->c_file, ct, "unable to open for reading");
1638 if ((digested = ct->c_digested))
1639 MD5Init (&mdContext);
1645 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1647 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1649 content_error (ct->c_file, ct, "error reading from");
1653 content_error (NULL, ct, "premature eof");
1661 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1666 if (skip || (*cp & 0x80)
1667 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1669 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1671 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1674 content_error (NULL, ct,
1675 "invalid BASE64 encoding -- continuing");
1679 bits |= value << bitno;
1681 if ((bitno -= 6) < 0) {
1682 putc ((char) *b1, ce->ce_fp);
1684 MD5Update (&mdContext, b1, 1);
1686 putc ((char) *b2, ce->ce_fp);
1688 MD5Update (&mdContext, b2, 1);
1690 putc ((char) *b3, ce->ce_fp);
1692 MD5Update (&mdContext, b3, 1);
1696 if (ferror (ce->ce_fp)) {
1697 content_error (ce->ce_file, ct,
1698 "error writing to");
1701 bitno = 18, bits = 0L, skip = 0;
1707 goto self_delimiting;
1716 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1718 content_error (NULL, ct, "invalid BASE64 encoding");
1723 fseek (ct->c_fp, 0L, SEEK_SET);
1725 if (fflush (ce->ce_fp)) {
1726 content_error (ce->ce_file, ct, "error writing to");
1731 unsigned char digest[16];
1733 MD5Final (digest, &mdContext);
1734 if (memcmp((char *) digest, (char *) ct->c_digest,
1735 sizeof(digest) / sizeof(digest[0])))
1736 content_error (NULL, ct,
1737 "content integrity suspect (digest mismatch) -- continuing");
1740 fprintf (stderr, "content integrity confirmed\n");
1743 fseek (ce->ce_fp, 0L, SEEK_SET);
1746 *file = ce->ce_file;
1747 return fileno (ce->ce_fp);
1750 free_encoding (ct, 0);
1759 static char hex2nib[0x80] = {
1760 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1767 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1768 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1772 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1773 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1774 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1775 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1782 return init_encoding (ct, openQuoted);
1787 openQuoted (CT ct, char **file)
1789 int cc, digested, len, quoted;
1790 unsigned char *cp, *ep;
1791 char buffer[BUFSIZ];
1794 /* sbeck -- handle prefixes */
1800 fseek (ce->ce_fp, 0L, SEEK_SET);
1805 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1806 content_error (ce->ce_file, ct, "unable to fopen for reading");
1812 if (*file == NULL) {
1813 ce->ce_file = add (m_scratch ("", tmp), NULL);
1816 ce->ce_file = add (*file, NULL);
1820 /* sbeck@cise.ufl.edu -- handle suffixes */
1822 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1823 invo_name, ci->ci_type, ci->ci_subtype);
1824 cp = context_find (buffer);
1825 if (cp == NULL || *cp == '\0') {
1826 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1828 cp = context_find (buffer);
1830 if (cp != NULL && *cp != '\0')
1831 ce->ce_file = add (cp, ce->ce_file);
1833 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1834 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1838 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1839 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1843 if ((len = ct->c_end - ct->c_begin) < 0)
1844 adios (NULL, "internal error(2)");
1846 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1847 content_error (ct->c_file, ct, "unable to open for reading");
1851 if ((digested = ct->c_digested))
1852 MD5Init (&mdContext);
1859 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1863 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1864 content_error (NULL, ct, "premature eof");
1868 if ((cc = strlen (buffer)) > len)
1872 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1877 for (; cp < ep; cp++) {
1880 if (!isxdigit (*cp)) {
1882 dp = "expecting hexidecimal-digit";
1883 goto invalid_encoding;
1886 mask |= hex2nib[*cp & 0x7f];
1887 putc (mask, ce->ce_fp);
1889 MD5Update (&mdContext, &mask, 1);
1893 putc (*cp, ce->ce_fp);
1895 MD5Update (&mdContext, (unsigned char *) ":", 1);
1899 if (!isxdigit (*cp))
1901 mask = hex2nib[*cp & 0x7f];
1907 if (ferror (ce->ce_fp)) {
1908 content_error (ce->ce_file, ct, "error writing to");
1917 if (*cp < '!' || *cp > '~') {
1919 dp = "expecting character in range [!..~]";
1922 i = strlen (invo_name) + 2;
1923 content_error (NULL, ct,
1924 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1932 putc (*cp, ce->ce_fp);
1935 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1937 MD5Update (&mdContext, (unsigned char *) cp, 1);
1939 if (ferror (ce->ce_fp)) {
1940 content_error (ce->ce_file, ct, "error writing to");
1946 if (*++cp != '\n') {
1955 content_error (NULL, ct,
1956 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1960 fseek (ct->c_fp, 0L, SEEK_SET);
1962 if (fflush (ce->ce_fp)) {
1963 content_error (ce->ce_file, ct, "error writing to");
1968 unsigned char digest[16];
1970 MD5Final (digest, &mdContext);
1971 if (memcmp((char *) digest, (char *) ct->c_digest,
1972 sizeof(digest) / sizeof(digest[0])))
1973 content_error (NULL, ct,
1974 "content integrity suspect (digest mismatch) -- continuing");
1977 fprintf (stderr, "content integrity confirmed\n");
1980 fseek (ce->ce_fp, 0L, SEEK_SET);
1983 *file = ce->ce_file;
1984 return fileno (ce->ce_fp);
1987 free_encoding (ct, 0);
1999 if (init_encoding (ct, open7Bit) == NOTOK)
2002 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2008 open7Bit (CT ct, char **file)
2011 char buffer[BUFSIZ];
2012 /* sbeck -- handle prefixes */
2019 fseek (ce->ce_fp, 0L, SEEK_SET);
2024 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2025 content_error (ce->ce_file, ct, "unable to fopen for reading");
2031 if (*file == NULL) {
2032 ce->ce_file = add (m_scratch ("", tmp), NULL);
2035 ce->ce_file = add (*file, NULL);
2039 /* sbeck@cise.ufl.edu -- handle suffixes */
2041 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2042 invo_name, ci->ci_type, ci->ci_subtype);
2043 cp = context_find (buffer);
2044 if (cp == NULL || *cp == '\0') {
2045 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2047 cp = context_find (buffer);
2049 if (cp != NULL && *cp != '\0')
2050 ce->ce_file = add (cp, ce->ce_file);
2052 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2053 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2057 if (ct->c_type == CT_MULTIPART) {
2059 CI ci = &ct->c_ctinfo;
2062 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2063 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2064 + 1 + strlen (ci->ci_subtype);
2065 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2066 putc (';', ce->ce_fp);
2069 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2071 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2072 fputs ("\n\t", ce->ce_fp);
2075 putc (' ', ce->ce_fp);
2078 fprintf (ce->ce_fp, "%s", buffer);
2082 if (ci->ci_comment) {
2083 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2084 fputs ("\n\t", ce->ce_fp);
2088 putc (' ', ce->ce_fp);
2091 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2094 fprintf (ce->ce_fp, "\n");
2096 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2098 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2099 fprintf (ce->ce_fp, "\n");
2102 if ((len = ct->c_end - ct->c_begin) < 0)
2103 adios (NULL, "internal error(3)");
2105 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2106 content_error (ct->c_file, ct, "unable to open for reading");
2110 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2112 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2114 content_error (ct->c_file, ct, "error reading from");
2118 content_error (NULL, ct, "premature eof");
2126 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2127 if (ferror (ce->ce_fp)) {
2128 content_error (ce->ce_file, ct, "error writing to");
2133 fseek (ct->c_fp, 0L, SEEK_SET);
2135 if (fflush (ce->ce_fp)) {
2136 content_error (ce->ce_file, ct, "error writing to");
2140 fseek (ce->ce_fp, 0L, SEEK_SET);
2143 *file = ce->ce_file;
2144 return fileno (ce->ce_fp);
2147 free_encoding (ct, 0);
2157 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2159 char cachefile[BUFSIZ];
2162 fseek (ce->ce_fp, 0L, SEEK_SET);
2167 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2168 content_error (ce->ce_file, ct, "unable to fopen for reading");
2174 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2175 cachefile, sizeof(cachefile)) != NOTOK) {
2176 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2177 ce->ce_file = getcpy (cachefile);
2181 admonish (cachefile, "unable to fopen for reading");
2188 *file = ce->ce_file;
2189 *fd = fileno (ce->ce_fp);
2200 return init_encoding (ct, openFile);
2205 openFile (CT ct, char **file)
2208 char cachefile[BUFSIZ];
2209 struct exbody *e = ct->c_ctexbody;
2210 CE ce = ct->c_cefile;
2212 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2224 content_error (NULL, ct, "missing name parameter");
2228 ce->ce_file = getcpy (e->eb_name);
2231 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2232 content_error (ce->ce_file, ct, "unable to fopen for reading");
2236 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2237 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2238 cachefile, sizeof(cachefile)) != NOTOK) {
2242 mask = umask (cachetype ? ~m_gmprot () : 0222);
2243 if ((fp = fopen (cachefile, "w"))) {
2245 char buffer[BUFSIZ];
2246 FILE *gp = ce->ce_fp;
2248 fseek (gp, 0L, SEEK_SET);
2250 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2252 fwrite (buffer, sizeof(*buffer), cc, fp);
2256 admonish (ce->ce_file, "error reading");
2261 admonish (cachefile, "error writing");
2269 fseek (ce->ce_fp, 0L, SEEK_SET);
2270 *file = ce->ce_file;
2271 return fileno (ce->ce_fp);
2281 return init_encoding (ct, openFTP);
2286 openFTP (CT ct, char **file)
2288 int cachetype, caching, fd;
2290 char *bp, *ftp, *user, *pass;
2291 char buffer[BUFSIZ], cachefile[BUFSIZ];
2294 static char *username = NULL;
2295 static char *password = NULL;
2300 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2308 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2319 if (!e->eb_name || !e->eb_site) {
2320 content_error (NULL, ct, "missing %s parameter",
2321 e->eb_name ? "site": "name");
2328 pidcheck (pidwait (xpid, NOTOK));
2332 /* Get the buffer ready to go */
2334 buflen = sizeof(buffer);
2337 * Construct the query message for user
2339 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2345 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2351 snprintf (bp, buflen, "\n using %sFTP from site %s",
2352 e->eb_flags ? "anonymous " : "", e->eb_site);
2357 if (e->eb_size > 0) {
2358 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2363 snprintf (bp, buflen, "? ");
2366 * Now, check the answer
2368 if (!getanswer (buffer))
2373 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2376 ruserpass (e->eb_site, &username, &password);
2381 ce->ce_unlink = (*file == NULL);
2383 cachefile[0] = '\0';
2384 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2385 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2386 cachefile, sizeof(cachefile)) != NOTOK) {
2387 if (*file == NULL) {
2394 ce->ce_file = add (*file, NULL);
2396 ce->ce_file = add (cachefile, NULL);
2398 ce->ce_file = add (m_scratch ("", tmp), NULL);
2400 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2401 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2409 int child_id, i, vecp;
2413 vec[vecp++] = r1bindex (ftp, '/');
2414 vec[vecp++] = e->eb_site;
2417 vec[vecp++] = e->eb_dir;
2418 vec[vecp++] = e->eb_name;
2419 vec[vecp++] = ce->ce_file,
2420 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2421 ? "ascii" : "binary";
2426 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2430 adios ("fork", "unable to");
2434 close (fileno (ce->ce_fp));
2436 fprintf (stderr, "unable to exec ");
2442 if (pidXwait (child_id, NULL)) {
2446 username = password = NULL;
2455 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2457 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2464 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2469 mask = umask (cachetype ? ~m_gmprot () : 0222);
2470 if ((fp = fopen (cachefile, "w"))) {
2472 FILE *gp = ce->ce_fp;
2474 fseek (gp, 0L, SEEK_SET);
2476 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2478 fwrite (buffer, sizeof(*buffer), cc, fp);
2482 admonish (ce->ce_file, "error reading");
2487 admonish (cachefile, "error writing");
2496 fseek (ce->ce_fp, 0L, SEEK_SET);
2497 *file = ce->ce_file;
2498 return fileno (ce->ce_fp);
2509 return init_encoding (ct, openMail);
2514 openMail (CT ct, char **file)
2516 int child_id, fd, i, vecp;
2518 char *bp, buffer[BUFSIZ], *vec[7];
2519 struct exbody *e = ct->c_ctexbody;
2520 CE ce = ct->c_cefile;
2522 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2533 if (!e->eb_server) {
2534 content_error (NULL, ct, "missing server parameter");
2541 pidcheck (pidwait (xpid, NOTOK));
2545 /* Get buffer ready to go */
2547 buflen = sizeof(buffer);
2549 /* Now, construct query message */
2550 snprintf (bp, buflen, "Retrieve content");
2556 snprintf (bp, buflen, " %s", e->eb_partno);
2562 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2564 e->eb_subject ? e->eb_subject : e->eb_body);
2566 /* Now, check answer */
2567 if (!getanswer (buffer))
2571 vec[vecp++] = r1bindex (mailproc, '/');
2572 vec[vecp++] = e->eb_server;
2573 vec[vecp++] = "-subject";
2574 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2575 vec[vecp++] = "-body";
2576 vec[vecp++] = e->eb_body;
2579 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2583 advise ("fork", "unable to");
2587 execvp (mailproc, vec);
2588 fprintf (stderr, "unable to exec ");
2594 if (pidXwait (child_id, NULL) == OK)
2595 advise (NULL, "request sent");
2599 if (*file == NULL) {
2600 ce->ce_file = add (m_scratch ("", tmp), NULL);
2603 ce->ce_file = add (*file, NULL);
2607 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2608 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2613 free (ct->c_showproc);
2614 ct->c_showproc = add ("true", NULL);
2616 fseek (ce->ce_fp, 0L, SEEK_SET);
2617 *file = ce->ce_file;
2618 return fileno (ce->ce_fp);
2623 readDigest (CT ct, char *cp)
2628 unsigned char *dp, value, *ep;
2629 unsigned char *b, *b1, *b2, *b3;
2631 b = (unsigned char *) &bits,
2632 b1 = &b[endian > 0 ? 1 : 2],
2633 b2 = &b[endian > 0 ? 2 : 1],
2634 b3 = &b[endian > 0 ? 3 : 0];
2639 for (ep = (dp = ct->c_digest)
2640 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2645 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2647 fprintf (stderr, "invalid BASE64 encoding\n");
2651 bits |= value << bitno;
2653 if ((bitno -= 6) < 0) {
2654 if (dp + (3 - skip) > ep)
2655 goto invalid_digest;
2670 goto self_delimiting;
2675 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2685 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2693 fprintf (stderr, "MD5 digest=");
2694 for (dp = ct->c_digest; dp < ep; dp++)
2695 fprintf (stderr, "%02x", *dp & 0xff);
2696 fprintf (stderr, "\n");