3 * mhparse.c -- routines to parse the contents of MIME messages
10 #include <h/signals.h>
15 #include <zotnet/mts/mts.h>
16 #include <zotnet/tws/tws.h>
18 #include <h/mhparse.h>
20 #ifdef HAVE_SYS_WAIT_H
21 # include <sys/wait.h>
28 extern int endian; /* mhmisc.c */
30 extern pid_t xpid; /* mhshowsbr.c */
33 extern int rcachesw; /* mhcachesbr.c */
34 extern int wcachesw; /* mhcachesbr.c */
36 int checksw = 0; /* check Content-MD5 field */
39 * Directory to place temp files. This must
40 * be set before these routines are called.
45 * Structure for mapping types to their internal flags
53 * Structures for TEXT messages
55 static struct k2v SubText[] = {
56 { "plain", TEXT_PLAIN },
57 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
58 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
59 { NULL, TEXT_UNKNOWN } /* this one must be last! */
62 static struct k2v Charset[] = {
63 { "us-ascii", CHARSET_USASCII },
64 { "iso-8859-1", CHARSET_LATIN },
65 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
69 * Structures for MULTIPART messages
71 static struct k2v SubMultiPart[] = {
72 { "mixed", MULTI_MIXED },
73 { "alternative", MULTI_ALTERNATE },
74 { "digest", MULTI_DIGEST },
75 { "parallel", MULTI_PARALLEL },
76 { NULL, MULTI_UNKNOWN } /* this one must be last! */
80 * Structures for MESSAGE messages
82 static struct k2v SubMessage[] = {
83 { "rfc822", MESSAGE_RFC822 },
84 { "partial", MESSAGE_PARTIAL },
85 { "external-body", MESSAGE_EXTERNAL },
86 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
90 * Structure for APPLICATION messages
92 static struct k2v SubApplication[] = {
93 { "octet-stream", APPLICATION_OCTETS },
94 { "postscript", APPLICATION_POSTSCRIPT },
95 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
100 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
103 int find_cache (CT, int, int *, char *, char *, int);
106 int part_ok (CT, int);
107 int type_ok (CT, int);
108 int make_intermediates (char *);
109 void content_error (char *, CT, char *, ...);
112 void free_content (CT);
113 void free_encoding (CT, int);
119 CT parse_mime (char *);
124 static CT get_content (FILE *, char *, int);
125 static int add_header (CT, char *, char *);
126 static int get_ctinfo (char *, CT);
127 static int get_comment (CT, char **, int);
128 static int InitGeneric (CT);
129 static int InitText (CT);
130 static int InitMultiPart (CT);
131 static void reverse_parts (CT);
132 static int InitMessage (CT);
133 static int params_external (CT, int);
134 static int InitApplication (CT);
135 static int init_encoding (CT, OpenCEFunc);
136 static void close_encoding (CT);
137 static unsigned long size_encoding (CT);
138 static int InitBase64 (CT);
139 static int openBase64 (CT, char **);
140 static int InitQuoted (CT);
141 static int openQuoted (CT, char **);
142 static int Init7Bit (CT);
143 static int open7Bit (CT, char **);
144 static int openExternal (CT, CT, CE, char **, int *);
145 static int InitFile (CT);
146 static int openFile (CT, char **);
147 static int InitFTP (CT);
148 static int openFTP (CT, char **);
149 static int InitMail (CT);
150 static int openMail (CT, char **);
151 static int readDigest (CT, char *);
154 * Structures for mapping (content) types to
155 * the functions to handle them.
163 static struct str2init str2cts[] = {
164 { "application", CT_APPLICATION, InitApplication },
165 { "audio", CT_AUDIO, InitGeneric },
166 { "image", CT_IMAGE, InitGeneric },
167 { "message", CT_MESSAGE, InitMessage },
168 { "multipart", CT_MULTIPART, InitMultiPart },
169 { "text", CT_TEXT, InitText },
170 { "video", CT_VIDEO, InitGeneric },
171 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
172 { NULL, CT_UNKNOWN, NULL },
175 static struct str2init str2ces[] = {
176 { "base64", CE_BASE64, InitBase64 },
177 { "quoted-printable", CE_QUOTED, InitQuoted },
178 { "8bit", CE_8BIT, Init7Bit },
179 { "7bit", CE_7BIT, Init7Bit },
180 { "binary", CE_BINARY, NULL },
181 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
182 { NULL, CE_UNKNOWN, NULL },
186 * NOTE WELL: si_key MUST NOT have value of NOTOK
188 * si_key is 1 if access method is anonymous.
190 static struct str2init str2methods[] = {
191 { "afs", 1, InitFile },
192 { "anon-ftp", 1, InitFTP },
193 { "ftp", 0, InitFTP },
194 { "local-file", 0, InitFile },
195 { "mail-server", 0, InitMail },
201 pidcheck (int status)
203 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
213 * Main entry point for parsing a MIME message or file.
214 * It returns the Content structure for the top level
215 * entity in the file.
219 parse_mime (char *file)
227 * Check if file is actually standard input
229 if ((is_stdin = !(strcmp (file, "-")))) {
230 file = add (m_tmpfil (invo_name), NULL);
231 if ((fp = fopen (file, "w+")) == NULL) {
232 advise (file, "unable to fopen for writing and reading");
236 while (fgets (buffer, sizeof(buffer), stdin))
240 if (ferror (stdin)) {
242 advise ("stdin", "error reading");
247 advise (file, "error writing");
250 fseek (fp, 0L, SEEK_SET);
251 } else if ((fp = fopen (file, "r")) == NULL) {
252 advise (file, "unable to read");
256 if (!(ct = get_content (fp, file, 1))) {
260 advise (NULL, "unable to decode %s", file);
265 ct->c_unlink = 1; /* temp file to remove */
269 if (ct->c_end == 0L) {
270 fseek (fp, 0L, SEEK_END);
271 ct->c_end = ftell (fp);
274 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
286 * Main routine for reading/parsing the headers
287 * of a message content.
289 * toplevel = 1 # we are at the top level of the message
290 * toplevel = 0 # we are inside message type or multipart type
291 * # other than multipart/digest
292 * toplevel = -1 # we are inside multipart/digest
296 get_content (FILE *in, char *file, int toplevel)
299 char buf[BUFSIZ], name[NAMESZ];
304 /* allocate the content structure */
305 if (!(ct = (CT) calloc (1, sizeof(*ct))))
306 adios (NULL, "out of memory");
309 ct->c_file = add (file, NULL);
310 ct->c_begin = ftell (ct->c_fp) + 1;
313 * Parse the header fields for this
314 * content into a linked list.
316 for (compnum = 1, state = FLD;;) {
317 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
323 /* get copies of the buffers */
324 np = add (name, NULL);
325 vp = add (buf, NULL);
327 /* if necessary, get rest of field */
328 while (state == FLDPLUS) {
329 state = m_getfld (state, name, buf, sizeof(buf), in);
330 vp = add (buf, vp); /* add to previous value */
333 /* Now add the header data to the list */
334 add_header (ct, np, vp);
336 /* continue, if this isn't the last header field */
337 if (state != FLDEOF) {
338 ct->c_begin = ftell (in) + 1;
345 ct->c_begin = ftell (in) - strlen (buf);
349 ct->c_begin = ftell (in);
354 adios (NULL, "message format error in component #%d", compnum);
357 adios (NULL, "getfld() returned %d", state);
360 /* break out of the loop */
365 * Read the content headers. We will parse the
366 * MIME related header fields into their various
367 * structures and set internal flags related to
368 * content type/subtype, etc.
371 hp = ct->c_first_hf; /* start at first header field */
373 /* Get MIME-Version field */
374 if (!strcasecmp (hp->name, VRSN_FIELD)) {
379 advise (NULL, "message %s has multiple %s: fields",
380 ct->c_file, VRSN_FIELD);
383 ct->c_vrsn = add (hp->value, NULL);
385 /* Now, cleanup this field */
388 while (isspace (*cp))
390 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
392 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
397 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
399 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
402 for (dp = cp; istoken (*dp); dp++)
406 ucmp = !strcasecmp (cp, VRSN_VALUE);
409 admonish (NULL, "message %s has unknown value for %s: field (%s)",
410 ct->c_file, VRSN_FIELD, cp);
413 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
414 /* Get Content-Type field */
415 struct str2init *s2i;
416 CI ci = &ct->c_ctinfo;
418 /* Check if we've already seen a Content-Type header */
420 advise (NULL, "message %s has multiple %s: fields",
421 ct->c_file, TYPE_FIELD);
425 /* Parse the Content-Type field */
426 if (get_ctinfo (hp->value, ct) == NOTOK)
430 * Set the Init function and the internal
431 * flag for this content type.
433 for (s2i = str2cts; s2i->si_key; s2i++)
434 if (!strcasecmp (ci->ci_type, s2i->si_key))
436 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
438 ct->c_type = s2i->si_val;
439 ct->c_ctinitfnx = s2i->si_init;
441 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
442 /* Get Content-Transfer-Encoding field */
444 struct str2init *s2i;
447 * Check if we've already seen the
448 * Content-Transfer-Encoding field
451 advise (NULL, "message %s has multiple %s: fields",
452 ct->c_file, ENCODING_FIELD);
456 /* get copy of this field */
457 ct->c_celine = cp = add (hp->value, NULL);
459 while (isspace (*cp))
461 for (dp = cp; istoken (*dp); dp++)
467 * Find the internal flag and Init function
468 * for this transfer encoding.
470 for (s2i = str2ces; s2i->si_key; s2i++)
471 if (!strcasecmp (cp, s2i->si_key))
473 if (!s2i->si_key && !uprf (cp, "X-"))
476 ct->c_encoding = s2i->si_val;
478 /* Call the Init function for this encoding */
479 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
482 else if (!strcasecmp (hp->name, MD5_FIELD)) {
483 /* Get Content-MD5 field */
489 if (ct->c_digested) {
490 advise (NULL, "message %s has multiple %s: fields",
491 ct->c_file, MD5_FIELD);
495 ep = cp = add (hp->value, NULL); /* get a copy */
497 while (isspace (*cp))
499 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
501 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
506 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
508 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
513 for (dp = cp; *dp && !isspace (*dp); dp++)
521 else if (!strcasecmp (hp->name, ID_FIELD)) {
522 /* Get Content-ID field */
523 ct->c_id = add (hp->value, ct->c_id);
525 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
526 /* Get Content-Description field */
527 ct->c_descr = add (hp->value, ct->c_descr);
531 hp = hp->next; /* next header field */
535 * Check if we saw a Content-Type field.
536 * If not, then assign a default value for
537 * it, and the Init function.
541 * If we are inside a multipart/digest message,
542 * so default type is message/rfc822
545 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
547 ct->c_type = CT_MESSAGE;
548 ct->c_ctinitfnx = InitMessage;
551 * Else default type is text/plain
553 if (get_ctinfo ("text/plain", ct) == NOTOK)
555 ct->c_type = CT_TEXT;
556 ct->c_ctinitfnx = InitText;
560 /* Use default Transfer-Encoding, if necessary */
562 ct->c_encoding = CE_7BIT;
575 * small routine to add header field to list
579 add_header (CT ct, char *name, char *value)
583 /* allocate header field structure */
584 if (!(hp = malloc (sizeof(*hp))))
585 adios (NULL, "out of memory");
587 /* link data into header structure */
592 /* link header structure into the list */
593 if (ct->c_first_hf == NULL) {
594 ct->c_first_hf = hp; /* this is the first */
597 ct->c_last_hf->next = hp; /* add it to the end */
606 * Parse Content-Type line and fill in the
607 * information of the CTinfo structure.
611 get_ctinfo (char *cp, CT ct)
614 char *dp, **ap, **ep;
619 i = strlen (invo_name) + 2;
621 /* store copy of Content-Type line */
622 cp = ct->c_ctline = add (cp, NULL);
624 while (isspace (*cp)) /* trim leading spaces */
627 /* change newlines to spaces */
628 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
631 /* trim trailing spaces */
632 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
638 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
640 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
643 for (dp = cp; istoken (*dp); dp++)
646 ci->ci_type = add (cp, NULL); /* store content type */
650 advise (NULL, "invalid %s: field in message %s (empty type)",
651 TYPE_FIELD, ct->c_file);
655 /* down case the content type string */
656 for (dp = ci->ci_type; *dp; dp++)
657 if (isalpha(*dp) && isupper (*dp))
660 while (isspace (*cp))
663 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
667 ci->ci_subtype = add ("", NULL);
672 while (isspace (*cp))
675 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
678 for (dp = cp; istoken (*dp); dp++)
681 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
684 if (!*ci->ci_subtype) {
686 "invalid %s: field in message %s (empty subtype for \"%s\")",
687 TYPE_FIELD, ct->c_file, ci->ci_type);
691 /* down case the content subtype string */
692 for (dp = ci->ci_subtype; *dp; dp++)
693 if (isalpha(*dp) && isupper (*dp))
697 while (isspace (*cp))
700 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
704 * Parse attribute/value pairs given with Content-Type
706 ep = (ap = ci->ci_attrs) + NPARMS;
712 "too many parameters in message %s's %s: field (%d max)",
713 ct->c_file, TYPE_FIELD, NPARMS);
718 while (isspace (*cp))
721 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
726 "extraneous trailing ';' in message %s's %s: parameter list",
727 ct->c_file, TYPE_FIELD);
731 /* down case the attribute name */
732 for (dp = cp; istoken (*dp); dp++)
733 if (isalpha(*dp) && isupper (*dp))
736 for (up = dp; isspace (*dp);)
738 if (dp == cp || *dp != '=') {
740 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
741 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
745 vp = (*ap = add (cp, NULL)) + (up - cp);
747 for (dp++; isspace (*dp);)
750 /* now add the attribute value */
751 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
754 for (cp = ++dp, dp = vp;;) {
759 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
760 ct->c_file, TYPE_FIELD, i, i, "", *ap);
765 if ((c = *cp++) == '\0')
780 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
786 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
787 ct->c_file, TYPE_FIELD, i, i, "", *ap);
792 while (isspace (*cp))
795 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
800 * Check if anything is left over
803 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
804 ct->c_file, TYPE_FIELD, i, i, "", cp);
812 get_comment (CT ct, char **ap, int istype)
816 char c, buffer[BUFSIZ], *dp;
828 advise (NULL, "invalid comment in message %s's %s: field",
829 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
834 if ((c = *cp++) == '\0')
857 if ((dp = ci->ci_comment)) {
858 ci->ci_comment = concat (dp, " ", buffer, NULL);
861 ci->ci_comment = add (buffer, NULL);
865 while (isspace (*cp))
876 * Handles content types audio, image, and video.
877 * There's not much to do right here.
883 return OK; /* not much to do here */
896 char **ap, **ep, *cp;
899 CI ci = &ct->c_ctinfo;
901 /* check for missing subtype */
902 if (!*ci->ci_subtype)
903 ci->ci_subtype = add ("plain", ci->ci_subtype);
906 for (kv = SubText; kv->kv_key; kv++)
907 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
909 ct->c_subtype = kv->kv_value;
911 /* allocate text structure */
912 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
913 adios (NULL, "out of memory");
914 ct->c_ctparams = (void *) t;
916 /* scan for charset parameter */
917 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
918 if (!strcasecmp (*ap, "charset"))
924 chset = "US-ASCII"; /* default for text */
926 /* match character set, or set to unknown */
927 for (kv = Charset; kv->kv_key; kv++)
928 if (!strcasecmp (chset, kv->kv_key))
930 t->tx_charset = kv->kv_value;
933 * If we can not handle character set natively,
934 * then check profile for string to modify the
935 * terminal or display method.
937 if (!check_charset (chset, strlen (chset))) {
938 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
939 if ((cp = context_find (buffer)))
940 ct->c_termproc = getcpy (cp);
952 InitMultiPart (CT ct)
956 char *cp, *dp, **ap, **ep;
957 char *bp, buffer[BUFSIZ];
960 struct part *part, **next;
961 CI ci = &ct->c_ctinfo;
966 * The encoding for multipart messages must be either
967 * 7bit, 8bit, or binary (per RFC2045).
969 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
970 && ct->c_encoding != CE_BINARY) {
972 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
973 ci->ci_type, ci->ci_subtype, ct->c_file);
978 for (kv = SubMultiPart; kv->kv_key; kv++)
979 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
981 ct->c_subtype = kv->kv_value;
984 * Check for "boundary" parameter, which is
985 * required for multipart messages.
987 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
988 if (!strcasecmp (*ap, "boundary")) {
994 /* complain if boundary parameter is missing */
997 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
998 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1002 /* allocate primary structure for multipart info */
1003 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1004 adios (NULL, "out of memory");
1005 ct->c_ctparams = (void *) m;
1007 /* check if boundary parameter contains only whitespace characters */
1008 for (cp = bp; isspace (*cp); cp++)
1011 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1012 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1016 /* remove trailing whitespace from boundary parameter */
1017 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1022 /* record boundary separators */
1023 m->mp_start = concat (bp, "\n", NULL);
1024 m->mp_stop = concat (bp, "--\n", NULL);
1026 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1027 advise (ct->c_file, "unable to open for reading");
1031 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1033 next = &m->mp_parts;
1037 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1041 pos += strlen (buffer);
1042 if (buffer[0] != '-' || buffer[1] != '-')
1045 if (strcmp (buffer + 2, m->mp_start))
1048 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1049 adios (NULL, "out of memory");
1051 next = &part->mp_next;
1053 if (!(p = get_content (fp, ct->c_file,
1054 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1062 fseek (fp, pos, SEEK_SET);
1065 if (strcmp (buffer + 2, m->mp_start) == 0) {
1069 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1070 if (p->c_end < p->c_begin)
1071 p->c_begin = p->c_end;
1076 if (strcmp (buffer + 2, m->mp_stop) == 0)
1082 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1083 if (!inout && part) {
1085 p->c_end = ct->c_end;
1087 if (p->c_begin >= p->c_end) {
1088 for (next = &m->mp_parts; *next != part;
1089 next = &((*next)->mp_next))
1093 free ((char *) part);
1098 /* reverse the order of the parts for multipart/alternative */
1099 if (ct->c_subtype == MULTI_ALTERNATE)
1103 * label all subparts with part number, and
1104 * then initialize the content of the subpart.
1109 char partnam[BUFSIZ];
1112 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1113 pp = partnam + strlen (partnam);
1118 for (part = m->mp_parts, partnum = 1; part;
1119 part = part->mp_next, partnum++) {
1122 sprintf (pp, "%d", partnum);
1123 p->c_partno = add (partnam, NULL);
1125 /* initialize the content of the subparts */
1126 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1141 * reverse the order of the parts of a multipart
1145 reverse_parts (CT ct)
1148 struct multipart *m;
1149 struct part **base, **bmp, **next, *part;
1151 m = (struct multipart *) ct->c_ctparams;
1153 /* if only one part, just return */
1154 if (!m->mp_parts || !m->mp_parts->mp_next)
1157 /* count number of parts */
1159 for (part = m->mp_parts; part; part = part->mp_next)
1162 /* allocate array of pointers to the parts */
1163 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1164 adios (NULL, "out of memory");
1167 /* point at all the parts */
1168 for (part = m->mp_parts; part; part = part->mp_next)
1172 /* reverse the order of the parts */
1173 next = &m->mp_parts;
1174 for (bmp--; bmp >= base; bmp--) {
1177 next = &part->mp_next;
1181 /* free array of pointers */
1182 free ((char *) base);
1194 CI ci = &ct->c_ctinfo;
1196 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1198 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1199 ci->ci_type, ci->ci_subtype, ct->c_file);
1203 /* check for missing subtype */
1204 if (!*ci->ci_subtype)
1205 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1208 for (kv = SubMessage; kv->kv_key; kv++)
1209 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1211 ct->c_subtype = kv->kv_value;
1213 switch (ct->c_subtype) {
1214 case MESSAGE_RFC822:
1217 case MESSAGE_PARTIAL:
1222 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1223 adios (NULL, "out of memory");
1224 ct->c_ctparams = (void *) p;
1226 /* scan for parameters "id", "number", and "total" */
1227 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1228 if (!strcasecmp (*ap, "id")) {
1229 p->pm_partid = add (*ep, NULL);
1232 if (!strcasecmp (*ap, "number")) {
1233 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1234 || p->pm_partno < 1) {
1237 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1238 *ap, ci->ci_type, ci->ci_subtype,
1239 ct->c_file, TYPE_FIELD);
1244 if (!strcasecmp (*ap, "total")) {
1245 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1254 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1256 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1257 ci->ci_type, ci->ci_subtype,
1258 ct->c_file, TYPE_FIELD);
1264 case MESSAGE_EXTERNAL:
1271 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1272 adios (NULL, "out of memory");
1273 ct->c_ctparams = (void *) e;
1276 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1277 advise (ct->c_file, "unable to open for reading");
1281 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1283 if (!(p = get_content (fp, ct->c_file, 0))) {
1292 if ((exresult = params_external (ct, 0)) != NOTOK
1293 && p->c_ceopenfnx == openMail) {
1297 if ((size = ct->c_end - p->c_begin) <= 0) {
1299 content_error (NULL, ct,
1300 "empty body for access-type=mail-server");
1304 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1305 adios (NULL, "out of memory");
1306 fseek (p->c_fp, p->c_begin, SEEK_SET);
1308 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1310 adios ("failed", "fread");
1313 adios (NULL, "unexpected EOF from fread");
1316 bp += cc, size -= cc;
1323 p->c_end = p->c_begin;
1328 if (exresult == NOTOK)
1330 if (e->eb_flags == NOTOK)
1333 switch (p->c_type) {
1338 if (p->c_subtype != MESSAGE_RFC822)
1342 e->eb_partno = ct->c_partno;
1344 (*p->c_ctinitfnx) (p);
1359 params_external (CT ct, int composing)
1362 struct exbody *e = (struct exbody *) ct->c_ctparams;
1363 CI ci = &ct->c_ctinfo;
1365 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1366 if (!strcasecmp (*ap, "access-type")) {
1367 struct str2init *s2i;
1368 CT p = e->eb_content;
1370 for (s2i = str2methods; s2i->si_key; s2i++)
1371 if (!strcasecmp (*ep, s2i->si_key))
1375 e->eb_flags = NOTOK;
1376 p->c_encoding = CE_EXTERNAL;
1379 e->eb_access = s2i->si_key;
1380 e->eb_flags = s2i->si_val;
1381 p->c_encoding = CE_EXTERNAL;
1383 /* Call the Init function for this external type */
1384 if ((*s2i->si_init)(p) == NOTOK)
1388 if (!strcasecmp (*ap, "name")) {
1392 if (!strcasecmp (*ap, "permission")) {
1393 e->eb_permission = *ep;
1396 if (!strcasecmp (*ap, "site")) {
1400 if (!strcasecmp (*ap, "directory")) {
1404 if (!strcasecmp (*ap, "mode")) {
1408 if (!strcasecmp (*ap, "size")) {
1409 sscanf (*ep, "%lu", &e->eb_size);
1412 if (!strcasecmp (*ap, "server")) {
1416 if (!strcasecmp (*ap, "subject")) {
1417 e->eb_subject = *ep;
1420 if (composing && !strcasecmp (*ap, "body")) {
1421 e->eb_body = getcpy (*ep);
1426 if (!e->eb_access) {
1428 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1429 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1442 InitApplication (CT ct)
1445 CI ci = &ct->c_ctinfo;
1448 for (kv = SubApplication; kv->kv_key; kv++)
1449 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1451 ct->c_subtype = kv->kv_value;
1458 * TRANSFER ENCODINGS
1462 init_encoding (CT ct, OpenCEFunc openfnx)
1466 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1467 adios (NULL, "out of memory");
1470 ct->c_ceopenfnx = openfnx;
1471 ct->c_ceclosefnx = close_encoding;
1472 ct->c_cesizefnx = size_encoding;
1479 close_encoding (CT ct)
1483 if (!(ce = ct->c_cefile))
1493 static unsigned long
1494 size_encoding (CT ct)
1502 if (!(ce = ct->c_cefile))
1503 return (ct->c_end - ct->c_begin);
1505 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1506 return (long) st.st_size;
1509 if (stat (ce->ce_file, &st) != NOTOK)
1510 return (long) st.st_size;
1515 if (ct->c_encoding == CE_EXTERNAL)
1516 return (ct->c_end - ct->c_begin);
1519 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1520 return (ct->c_end - ct->c_begin);
1522 if (fstat (fd, &st) != NOTOK)
1523 size = (long) st.st_size;
1527 (*ct->c_ceclosefnx) (ct);
1536 static unsigned char b642nib[0x80] = {
1537 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
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, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1543 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1544 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1545 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1546 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1547 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1548 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1549 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1550 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1551 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1552 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1559 return init_encoding (ct, openBase64);
1564 openBase64 (CT ct, char **file)
1566 int bitno, cc, digested;
1569 unsigned char value, *b, *b1, *b2, *b3;
1570 char *cp, *ep, buffer[BUFSIZ];
1574 b = (unsigned char *) &bits;
1575 b1 = &b[endian > 0 ? 1 : 2];
1576 b2 = &b[endian > 0 ? 2 : 1];
1577 b3 = &b[endian > 0 ? 3 : 0];
1581 fseek (ce->ce_fp, 0L, SEEK_SET);
1586 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1587 content_error (ce->ce_file, ct, "unable to fopen for reading");
1593 if (*file == NULL) {
1594 ce->ce_file = add (m_scratch ("", tmp), NULL);
1597 ce->ce_file = add (*file, NULL);
1601 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1602 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1606 if ((len = ct->c_end - ct->c_begin) < 0)
1607 adios (NULL, "internal error(1)");
1609 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1610 content_error (ct->c_file, ct, "unable to open for reading");
1614 if ((digested = ct->c_digested))
1615 MD5Init (&mdContext);
1621 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1623 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1625 content_error (ct->c_file, ct, "error reading from");
1629 content_error (NULL, ct, "premature eof");
1637 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1642 if (skip || (*cp & 0x80)
1643 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1645 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1647 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1650 content_error (NULL, ct,
1651 "invalid BASE64 encoding -- continuing");
1655 bits |= value << bitno;
1657 if ((bitno -= 6) < 0) {
1658 putc ((char) *b1, ce->ce_fp);
1660 MD5Update (&mdContext, b1, 1);
1662 putc ((char) *b2, ce->ce_fp);
1664 MD5Update (&mdContext, b2, 1);
1666 putc ((char) *b3, ce->ce_fp);
1668 MD5Update (&mdContext, b3, 1);
1672 if (ferror (ce->ce_fp)) {
1673 content_error (ce->ce_file, ct,
1674 "error writing to");
1677 bitno = 18, bits = 0L, skip = 0;
1683 goto self_delimiting;
1692 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1694 content_error (NULL, ct, "invalid BASE64 encoding");
1699 fseek (ct->c_fp, 0L, SEEK_SET);
1701 if (fflush (ce->ce_fp)) {
1702 content_error (ce->ce_file, ct, "error writing to");
1707 unsigned char digest[16];
1709 MD5Final (digest, &mdContext);
1710 if (memcmp((char *) digest, (char *) ct->c_digest,
1711 sizeof(digest) / sizeof(digest[0])))
1712 content_error (NULL, ct,
1713 "content integrity suspect (digest mismatch) -- continuing");
1716 fprintf (stderr, "content integrity confirmed\n");
1719 fseek (ce->ce_fp, 0L, SEEK_SET);
1722 *file = ce->ce_file;
1723 return fileno (ce->ce_fp);
1726 free_encoding (ct, 0);
1735 static char hex2nib[0x80] = {
1736 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1737 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1738 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1739 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1740 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1741 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1742 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1743 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1744 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1745 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1746 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1747 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1748 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1749 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1750 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1758 return init_encoding (ct, openQuoted);
1763 openQuoted (CT ct, char **file)
1765 int cc, digested, len, quoted;
1767 char buffer[BUFSIZ];
1774 fseek (ce->ce_fp, 0L, SEEK_SET);
1779 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1780 content_error (ce->ce_file, ct, "unable to fopen for reading");
1786 if (*file == NULL) {
1787 ce->ce_file = add (m_scratch ("", tmp), NULL);
1790 ce->ce_file = add (*file, NULL);
1794 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1795 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1799 if ((len = ct->c_end - ct->c_begin) < 0)
1800 adios (NULL, "internal error(2)");
1802 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1803 content_error (ct->c_file, ct, "unable to open for reading");
1807 if ((digested = ct->c_digested))
1808 MD5Init (&mdContext);
1815 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1819 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1820 content_error (NULL, ct, "premature eof");
1824 if ((cc = strlen (buffer)) > len)
1828 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1833 for (; cp < ep; cp++) {
1836 if (!isxdigit (*cp)) {
1838 dp = "expecting hexidecimal-digit";
1839 goto invalid_encoding;
1842 mask |= hex2nib[*cp & 0x7f];
1843 putc (mask, ce->ce_fp);
1845 MD5Update (&mdContext, &mask, 1);
1849 putc (*cp, ce->ce_fp);
1851 MD5Update (&mdContext, (unsigned char *) ":", 1);
1855 if (!isxdigit (*cp))
1857 mask = hex2nib[*cp & 0x7f];
1863 if (ferror (ce->ce_fp)) {
1864 content_error (ce->ce_file, ct, "error writing to");
1873 if (*cp < '!' || *cp > '~') {
1875 dp = "expecting character in range [!..~]";
1878 i = strlen (invo_name) + 2;
1879 content_error (NULL, ct,
1880 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1888 putc (*cp, ce->ce_fp);
1891 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1893 MD5Update (&mdContext, (unsigned char *) cp, 1);
1895 if (ferror (ce->ce_fp)) {
1896 content_error (ce->ce_file, ct, "error writing to");
1902 if (*++cp != '\n') {
1911 content_error (NULL, ct,
1912 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1916 fseek (ct->c_fp, 0L, SEEK_SET);
1918 if (fflush (ce->ce_fp)) {
1919 content_error (ce->ce_file, ct, "error writing to");
1924 unsigned char digest[16];
1926 MD5Final (digest, &mdContext);
1927 if (memcmp((char *) digest, (char *) ct->c_digest,
1928 sizeof(digest) / sizeof(digest[0])))
1929 content_error (NULL, ct,
1930 "content integrity suspect (digest mismatch) -- continuing");
1933 fprintf (stderr, "content integrity confirmed\n");
1936 fseek (ce->ce_fp, 0L, SEEK_SET);
1939 *file = ce->ce_file;
1940 return fileno (ce->ce_fp);
1943 free_encoding (ct, 0);
1955 if (init_encoding (ct, open7Bit) == NOTOK)
1958 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1964 open7Bit (CT ct, char **file)
1967 char buffer[BUFSIZ];
1972 fseek (ce->ce_fp, 0L, SEEK_SET);
1977 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1978 content_error (ce->ce_file, ct, "unable to fopen for reading");
1984 if (*file == NULL) {
1985 ce->ce_file = add (m_scratch ("", tmp), NULL);
1988 ce->ce_file = add (*file, NULL);
1992 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1993 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1997 if (ct->c_type == CT_MULTIPART) {
1999 CI ci = &ct->c_ctinfo;
2002 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2003 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2004 + 1 + strlen (ci->ci_subtype);
2005 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2006 putc (';', ce->ce_fp);
2009 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2011 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2012 fputs ("\n\t", ce->ce_fp);
2015 putc (' ', ce->ce_fp);
2018 fprintf (ce->ce_fp, "%s", buffer);
2022 if (ci->ci_comment) {
2023 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2024 fputs ("\n\t", ce->ce_fp);
2028 putc (' ', ce->ce_fp);
2031 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2034 fprintf (ce->ce_fp, "\n");
2036 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2038 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2039 fprintf (ce->ce_fp, "\n");
2042 if ((len = ct->c_end - ct->c_begin) < 0)
2043 adios (NULL, "internal error(3)");
2045 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2046 content_error (ct->c_file, ct, "unable to open for reading");
2050 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2052 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2054 content_error (ct->c_file, ct, "error reading from");
2058 content_error (NULL, ct, "premature eof");
2066 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2067 if (ferror (ce->ce_fp)) {
2068 content_error (ce->ce_file, ct, "error writing to");
2073 fseek (ct->c_fp, 0L, SEEK_SET);
2075 if (fflush (ce->ce_fp)) {
2076 content_error (ce->ce_file, ct, "error writing to");
2080 fseek (ce->ce_fp, 0L, SEEK_SET);
2083 *file = ce->ce_file;
2084 return fileno (ce->ce_fp);
2087 free_encoding (ct, 0);
2097 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2099 char cachefile[BUFSIZ];
2102 fseek (ce->ce_fp, 0L, SEEK_SET);
2107 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2108 content_error (ce->ce_file, ct, "unable to fopen for reading");
2114 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2115 cachefile, sizeof(cachefile)) != NOTOK) {
2116 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2117 ce->ce_file = getcpy (cachefile);
2121 admonish (cachefile, "unable to fopen for reading");
2128 *file = ce->ce_file;
2129 *fd = fileno (ce->ce_fp);
2140 return init_encoding (ct, openFile);
2145 openFile (CT ct, char **file)
2148 char cachefile[BUFSIZ];
2149 struct exbody *e = ct->c_ctexbody;
2150 CE ce = ct->c_cefile;
2152 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2164 content_error (NULL, ct, "missing name parameter");
2168 ce->ce_file = getcpy (e->eb_name);
2171 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2172 content_error (ce->ce_file, ct, "unable to fopen for reading");
2176 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2177 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2178 cachefile, sizeof(cachefile)) != NOTOK) {
2182 mask = umask (cachetype ? ~m_gmprot () : 0222);
2183 if ((fp = fopen (cachefile, "w"))) {
2185 char buffer[BUFSIZ];
2186 FILE *gp = ce->ce_fp;
2188 fseek (gp, 0L, SEEK_SET);
2190 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2192 fwrite (buffer, sizeof(*buffer), cc, fp);
2196 admonish (ce->ce_file, "error reading");
2201 admonish (cachefile, "error writing");
2209 fseek (ce->ce_fp, 0L, SEEK_SET);
2210 *file = ce->ce_file;
2211 return fileno (ce->ce_fp);
2221 return init_encoding (ct, openFTP);
2226 openFTP (CT ct, char **file)
2228 int cachetype, caching, fd;
2230 char *bp, *ftp, *user, *pass;
2231 char buffer[BUFSIZ], cachefile[BUFSIZ];
2234 static char *username = NULL;
2235 static char *password = NULL;
2240 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2248 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2259 if (!e->eb_name || !e->eb_site) {
2260 content_error (NULL, ct, "missing %s parameter",
2261 e->eb_name ? "site": "name");
2268 pidcheck (pidwait (xpid, NOTOK));
2272 /* Get the buffer ready to go */
2274 buflen = sizeof(buffer);
2277 * Construct the query message for user
2279 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2285 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2291 snprintf (bp, buflen, "\n using %sFTP from site %s",
2292 e->eb_flags ? "anonymous " : "", e->eb_site);
2297 if (e->eb_size > 0) {
2298 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2303 snprintf (bp, buflen, "? ");
2306 * Now, check the answer
2308 if (!getanswer (buffer))
2313 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2316 ruserpass (e->eb_site, &username, &password);
2321 ce->ce_unlink = (*file == NULL);
2323 cachefile[0] = '\0';
2324 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2325 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2326 cachefile, sizeof(cachefile)) != NOTOK) {
2327 if (*file == NULL) {
2334 ce->ce_file = add (*file, NULL);
2336 ce->ce_file = add (cachefile, NULL);
2338 ce->ce_file = add (m_scratch ("", tmp), NULL);
2340 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2341 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2349 int child_id, i, vecp;
2353 vec[vecp++] = r1bindex (ftp, '/');
2354 vec[vecp++] = e->eb_site;
2357 vec[vecp++] = e->eb_dir;
2358 vec[vecp++] = e->eb_name;
2359 vec[vecp++] = ce->ce_file,
2360 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2361 ? "ascii" : "binary";
2366 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2370 adios ("fork", "unable to");
2374 close (fileno (ce->ce_fp));
2376 fprintf (stderr, "unable to exec ");
2382 if (pidXwait (child_id, NULL)) {
2386 username = password = NULL;
2395 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2397 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2404 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2409 mask = umask (cachetype ? ~m_gmprot () : 0222);
2410 if ((fp = fopen (cachefile, "w"))) {
2412 FILE *gp = ce->ce_fp;
2414 fseek (gp, 0L, SEEK_SET);
2416 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2418 fwrite (buffer, sizeof(*buffer), cc, fp);
2422 admonish (ce->ce_file, "error reading");
2427 admonish (cachefile, "error writing");
2436 fseek (ce->ce_fp, 0L, SEEK_SET);
2437 *file = ce->ce_file;
2438 return fileno (ce->ce_fp);
2449 return init_encoding (ct, openMail);
2454 openMail (CT ct, char **file)
2456 int child_id, fd, i, vecp;
2458 char *bp, buffer[BUFSIZ], *vec[7];
2459 struct exbody *e = ct->c_ctexbody;
2460 CE ce = ct->c_cefile;
2462 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2473 if (!e->eb_server) {
2474 content_error (NULL, ct, "missing server parameter");
2481 pidcheck (pidwait (xpid, NOTOK));
2485 /* Get buffer ready to go */
2487 buflen = sizeof(buffer);
2489 /* Now, construct query message */
2490 snprintf (bp, buflen, "Retrieve content");
2496 snprintf (bp, buflen, " %s", e->eb_partno);
2502 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2504 e->eb_subject ? e->eb_subject : e->eb_body);
2506 /* Now, check answer */
2507 if (!getanswer (buffer))
2511 vec[vecp++] = r1bindex (mailproc, '/');
2512 vec[vecp++] = e->eb_server;
2513 vec[vecp++] = "-subject";
2514 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2515 vec[vecp++] = "-body";
2516 vec[vecp++] = e->eb_body;
2519 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2523 advise ("fork", "unable to");
2527 execvp (mailproc, vec);
2528 fprintf (stderr, "unable to exec ");
2534 if (pidXwait (child_id, NULL) == OK)
2535 advise (NULL, "request sent");
2539 if (*file == NULL) {
2540 ce->ce_file = add (m_scratch ("", tmp), NULL);
2543 ce->ce_file = add (*file, NULL);
2547 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2548 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2553 free (ct->c_showproc);
2554 ct->c_showproc = add ("true", NULL);
2556 fseek (ce->ce_fp, 0L, SEEK_SET);
2557 *file = ce->ce_file;
2558 return fileno (ce->ce_fp);
2563 readDigest (CT ct, char *cp)
2568 unsigned char *dp, value, *ep;
2569 unsigned char *b, *b1, *b2, *b3;
2571 b = (unsigned char *) &bits,
2572 b1 = &b[endian > 0 ? 1 : 2],
2573 b2 = &b[endian > 0 ? 2 : 1],
2574 b3 = &b[endian > 0 ? 3 : 0];
2579 for (ep = (dp = ct->c_digest)
2580 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2585 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2587 fprintf (stderr, "invalid BASE64 encoding\n");
2591 bits |= value << bitno;
2593 if ((bitno -= 6) < 0) {
2594 if (dp + (3 - skip) > ep)
2595 goto invalid_digest;
2610 goto self_delimiting;
2615 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2625 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2633 fprintf (stderr, "MD5 digest=");
2634 for (dp = ct->c_digest; dp < ep; dp++)
2635 fprintf (stderr, "%02x", *dp & 0xff);
2636 fprintf (stderr, "\n");