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)
214 * Main entry point for parsing a MIME message or file.
215 * It returns the Content structure for the top level
216 * entity in the file.
220 parse_mime (char *file)
228 * Check if file is actually standard input
230 if ((is_stdin = !(strcmp (file, "-")))) {
231 file = add (m_tmpfil (invo_name), NULL);
232 if ((fp = fopen (file, "w+")) == NULL) {
233 advise (file, "unable to fopen for writing and reading");
237 while (fgets (buffer, sizeof(buffer), stdin))
241 if (ferror (stdin)) {
243 advise ("stdin", "error reading");
248 advise (file, "error writing");
251 fseek (fp, 0L, SEEK_SET);
252 } else if ((fp = fopen (file, "r")) == NULL) {
253 advise (file, "unable to read");
257 if (!(ct = get_content (fp, file, 1))) {
261 advise (NULL, "unable to decode %s", file);
266 ct->c_unlink = 1; /* temp file to remove */
270 if (ct->c_end == 0L) {
271 fseek (fp, 0L, SEEK_END);
272 ct->c_end = ftell (fp);
275 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
287 * Main routine for reading/parsing the headers
288 * of a message content.
290 * toplevel = 1 # we are at the top level of the message
291 * toplevel = 0 # we are inside message type or multipart type
292 * # other than multipart/digest
293 * toplevel = -1 # we are inside multipart/digest
297 get_content (FILE *in, char *file, int toplevel)
300 char buf[BUFSIZ], name[NAMESZ];
305 /* allocate the content structure */
306 if (!(ct = (CT) calloc (1, sizeof(*ct))))
307 adios (NULL, "out of memory");
310 ct->c_file = add (file, NULL);
311 ct->c_begin = ftell (ct->c_fp) + 1;
314 * Parse the header fields for this
315 * content into a linked list.
317 for (compnum = 1, state = FLD;;) {
318 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
324 /* get copies of the buffers */
325 np = add (name, NULL);
326 vp = add (buf, NULL);
328 /* if necessary, get rest of field */
329 while (state == FLDPLUS) {
330 state = m_getfld (state, name, buf, sizeof(buf), in);
331 vp = add (buf, vp); /* add to previous value */
334 /* Now add the header data to the list */
335 add_header (ct, np, vp);
337 /* continue, if this isn't the last header field */
338 if (state != FLDEOF) {
339 ct->c_begin = ftell (in) + 1;
346 ct->c_begin = ftell (in) - strlen (buf);
350 ct->c_begin = ftell (in);
355 adios (NULL, "message format error in component #%d", compnum);
358 adios (NULL, "getfld() returned %d", state);
361 /* break out of the loop */
366 * Read the content headers. We will parse the
367 * MIME related header fields into their various
368 * structures and set internal flags related to
369 * content type/subtype, etc.
372 hp = ct->c_first_hf; /* start at first header field */
374 /* Get MIME-Version field */
375 if (!strcasecmp (hp->name, VRSN_FIELD)) {
380 advise (NULL, "message %s has multiple %s: fields",
381 ct->c_file, VRSN_FIELD);
384 ct->c_vrsn = add (hp->value, NULL);
386 /* Now, cleanup this field */
389 while (isspace (*cp))
391 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
393 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
398 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
400 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
403 for (dp = cp; istoken (*dp); dp++)
407 ucmp = !strcasecmp (cp, VRSN_VALUE);
410 admonish (NULL, "message %s has unknown value for %s: field (%s)",
411 ct->c_file, VRSN_FIELD, cp);
414 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
415 /* Get Content-Type field */
416 struct str2init *s2i;
417 CI ci = &ct->c_ctinfo;
419 /* Check if we've already seen a Content-Type header */
421 advise (NULL, "message %s has multiple %s: fields",
422 ct->c_file, TYPE_FIELD);
426 /* Parse the Content-Type field */
427 if (get_ctinfo (hp->value, ct) == NOTOK)
431 * Set the Init function and the internal
432 * flag for this content type.
434 for (s2i = str2cts; s2i->si_key; s2i++)
435 if (!strcasecmp (ci->ci_type, s2i->si_key))
437 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
439 ct->c_type = s2i->si_val;
440 ct->c_ctinitfnx = s2i->si_init;
442 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
443 /* Get Content-Transfer-Encoding field */
445 struct str2init *s2i;
448 * Check if we've already seen the
449 * Content-Transfer-Encoding field
452 advise (NULL, "message %s has multiple %s: fields",
453 ct->c_file, ENCODING_FIELD);
457 /* get copy of this field */
458 ct->c_celine = cp = add (hp->value, NULL);
460 while (isspace (*cp))
462 for (dp = cp; istoken (*dp); dp++)
468 * Find the internal flag and Init function
469 * for this transfer encoding.
471 for (s2i = str2ces; s2i->si_key; s2i++)
472 if (!strcasecmp (cp, s2i->si_key))
474 if (!s2i->si_key && !uprf (cp, "X-"))
477 ct->c_encoding = s2i->si_val;
479 /* Call the Init function for this encoding */
480 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
483 else if (!strcasecmp (hp->name, MD5_FIELD)) {
484 /* Get Content-MD5 field */
490 if (ct->c_digested) {
491 advise (NULL, "message %s has multiple %s: fields",
492 ct->c_file, MD5_FIELD);
496 ep = cp = add (hp->value, NULL); /* get a copy */
498 while (isspace (*cp))
500 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
502 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
507 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
509 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
514 for (dp = cp; *dp && !isspace (*dp); dp++)
522 else if (!strcasecmp (hp->name, ID_FIELD)) {
523 /* Get Content-ID field */
524 ct->c_id = add (hp->value, ct->c_id);
526 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
527 /* Get Content-Description field */
528 ct->c_descr = add (hp->value, ct->c_descr);
532 hp = hp->next; /* next header field */
536 * Check if we saw a Content-Type field.
537 * If not, then assign a default value for
538 * it, and the Init function.
542 * If we are inside a multipart/digest message,
543 * so default type is message/rfc822
546 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
548 ct->c_type = CT_MESSAGE;
549 ct->c_ctinitfnx = InitMessage;
552 * Else default type is text/plain
554 if (get_ctinfo ("text/plain", ct) == NOTOK)
556 ct->c_type = CT_TEXT;
557 ct->c_ctinitfnx = InitText;
561 /* Use default Transfer-Encoding, if necessary */
563 ct->c_encoding = CE_7BIT;
576 * small routine to add header field to list
580 add_header (CT ct, char *name, char *value)
584 /* allocate header field structure */
585 if (!(hp = malloc (sizeof(*hp))))
586 adios (NULL, "out of memory");
588 /* link data into header structure */
593 /* link header structure into the list */
594 if (ct->c_first_hf == NULL) {
595 ct->c_first_hf = hp; /* this is the first */
598 ct->c_last_hf->next = hp; /* add it to the end */
607 * Parse Content-Type line and fill in the
608 * information of the CTinfo structure.
612 get_ctinfo (char *cp, CT ct)
615 char *dp, **ap, **ep;
620 i = strlen (invo_name) + 2;
622 /* store copy of Content-Type line */
623 cp = ct->c_ctline = add (cp, NULL);
625 while (isspace (*cp)) /* trim leading spaces */
628 /* change newlines to spaces */
629 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
632 /* trim trailing spaces */
633 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
639 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
641 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
644 for (dp = cp; istoken (*dp); dp++)
647 ci->ci_type = add (cp, NULL); /* store content type */
651 advise (NULL, "invalid %s: field in message %s (empty type)",
652 TYPE_FIELD, ct->c_file);
656 /* down case the content type string */
657 for (dp = ci->ci_type; *dp; dp++)
658 if (isalpha(*dp) && isupper (*dp))
661 while (isspace (*cp))
664 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
668 ci->ci_subtype = add ("", NULL);
673 while (isspace (*cp))
676 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
679 for (dp = cp; istoken (*dp); dp++)
682 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
685 if (!*ci->ci_subtype) {
687 "invalid %s: field in message %s (empty subtype for \"%s\")",
688 TYPE_FIELD, ct->c_file, ci->ci_type);
692 /* down case the content subtype string */
693 for (dp = ci->ci_subtype; *dp; dp++)
694 if (isalpha(*dp) && isupper (*dp))
698 while (isspace (*cp))
701 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
705 * Parse attribute/value pairs given with Content-Type
707 ep = (ap = ci->ci_attrs) + NPARMS;
713 "too many parameters in message %s's %s: field (%d max)",
714 ct->c_file, TYPE_FIELD, NPARMS);
719 while (isspace (*cp))
722 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
727 "extraneous trailing ';' in message %s's %s: parameter list",
728 ct->c_file, TYPE_FIELD);
732 /* down case the attribute name */
733 for (dp = cp; istoken (*dp); dp++)
734 if (isalpha(*dp) && isupper (*dp))
737 for (up = dp; isspace (*dp);)
739 if (dp == cp || *dp != '=') {
741 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
742 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
746 vp = (*ap = add (cp, NULL)) + (up - cp);
748 for (dp++; isspace (*dp);)
751 /* now add the attribute value */
752 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
755 for (cp = ++dp, dp = vp;;) {
760 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
761 ct->c_file, TYPE_FIELD, i, i, "", *ap);
766 if ((c = *cp++) == '\0')
781 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
787 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
788 ct->c_file, TYPE_FIELD, i, i, "", *ap);
793 while (isspace (*cp))
796 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
801 * Check if anything is left over
804 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
805 ct->c_file, TYPE_FIELD, i, i, "", cp);
813 get_comment (CT ct, char **ap, int istype)
817 char c, buffer[BUFSIZ], *dp;
829 advise (NULL, "invalid comment in message %s's %s: field",
830 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
835 if ((c = *cp++) == '\0')
858 if ((dp = ci->ci_comment)) {
859 ci->ci_comment = concat (dp, " ", buffer, NULL);
862 ci->ci_comment = add (buffer, NULL);
866 while (isspace (*cp))
877 * Handles content types audio, image, and video.
878 * There's not much to do right here.
884 return OK; /* not much to do here */
897 char **ap, **ep, *cp;
900 CI ci = &ct->c_ctinfo;
902 /* check for missing subtype */
903 if (!*ci->ci_subtype)
904 ci->ci_subtype = add ("plain", ci->ci_subtype);
907 for (kv = SubText; kv->kv_key; kv++)
908 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
910 ct->c_subtype = kv->kv_value;
912 /* allocate text structure */
913 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
914 adios (NULL, "out of memory");
915 ct->c_ctparams = (void *) t;
917 /* scan for charset parameter */
918 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
919 if (!strcasecmp (*ap, "charset"))
925 chset = "US-ASCII"; /* default for text */
927 /* match character set, or set to unknown */
928 for (kv = Charset; kv->kv_key; kv++)
929 if (!strcasecmp (chset, kv->kv_key))
931 t->tx_charset = kv->kv_value;
934 * If we can not handle character set natively,
935 * then check profile for string to modify the
936 * terminal or display method.
938 if (!check_charset (chset, strlen (chset))) {
939 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
940 if ((cp = context_find (buffer)))
941 ct->c_termproc = getcpy (cp);
953 InitMultiPart (CT ct)
957 char *cp, *dp, **ap, **ep;
958 char *bp, buffer[BUFSIZ];
961 struct part *part, **next;
962 CI ci = &ct->c_ctinfo;
967 * The encoding for multipart messages must be either
968 * 7bit, 8bit, or binary (per RFC2045).
970 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
971 && ct->c_encoding != CE_BINARY) {
973 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
974 ci->ci_type, ci->ci_subtype, ct->c_file);
979 for (kv = SubMultiPart; kv->kv_key; kv++)
980 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
982 ct->c_subtype = kv->kv_value;
985 * Check for "boundary" parameter, which is
986 * required for multipart messages.
988 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
989 if (!strcasecmp (*ap, "boundary")) {
995 /* complain if boundary parameter is missing */
998 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
999 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1003 /* allocate primary structure for multipart info */
1004 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1005 adios (NULL, "out of memory");
1006 ct->c_ctparams = (void *) m;
1008 /* check if boundary parameter contains only whitespace characters */
1009 for (cp = bp; isspace (*cp); cp++)
1012 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1013 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1017 /* remove trailing whitespace from boundary parameter */
1018 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1023 /* record boundary separators */
1024 m->mp_start = concat (bp, "\n", NULL);
1025 m->mp_stop = concat (bp, "--\n", NULL);
1027 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1028 advise (ct->c_file, "unable to open for reading");
1032 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1034 next = &m->mp_parts;
1038 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1042 pos += strlen (buffer);
1043 if (buffer[0] != '-' || buffer[1] != '-')
1046 if (strcmp (buffer + 2, m->mp_start))
1049 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1050 adios (NULL, "out of memory");
1052 next = &part->mp_next;
1054 if (!(p = get_content (fp, ct->c_file,
1055 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1063 fseek (fp, pos, SEEK_SET);
1066 if (strcmp (buffer + 2, m->mp_start) == 0) {
1070 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1071 if (p->c_end < p->c_begin)
1072 p->c_begin = p->c_end;
1077 if (strcmp (buffer + 2, m->mp_stop) == 0)
1083 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1084 if (!inout && part) {
1086 p->c_end = ct->c_end;
1088 if (p->c_begin >= p->c_end) {
1089 for (next = &m->mp_parts; *next != part;
1090 next = &((*next)->mp_next))
1094 free ((char *) part);
1099 /* reverse the order of the parts for multipart/alternative */
1100 if (ct->c_subtype == MULTI_ALTERNATE)
1104 * label all subparts with part number, and
1105 * then initialize the content of the subpart.
1110 char partnam[BUFSIZ];
1113 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1114 pp = partnam + strlen (partnam);
1119 for (part = m->mp_parts, partnum = 1; part;
1120 part = part->mp_next, partnum++) {
1123 sprintf (pp, "%d", partnum);
1124 p->c_partno = add (partnam, NULL);
1126 /* initialize the content of the subparts */
1127 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1142 * reverse the order of the parts of a multipart
1146 reverse_parts (CT ct)
1149 struct multipart *m;
1150 struct part **base, **bmp, **next, *part;
1152 m = (struct multipart *) ct->c_ctparams;
1154 /* if only one part, just return */
1155 if (!m->mp_parts || !m->mp_parts->mp_next)
1158 /* count number of parts */
1160 for (part = m->mp_parts; part; part = part->mp_next)
1163 /* allocate array of pointers to the parts */
1164 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1165 adios (NULL, "out of memory");
1168 /* point at all the parts */
1169 for (part = m->mp_parts; part; part = part->mp_next)
1173 /* reverse the order of the parts */
1174 next = &m->mp_parts;
1175 for (bmp--; bmp >= base; bmp--) {
1178 next = &part->mp_next;
1182 /* free array of pointers */
1183 free ((char *) base);
1195 CI ci = &ct->c_ctinfo;
1197 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1199 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1200 ci->ci_type, ci->ci_subtype, ct->c_file);
1204 /* check for missing subtype */
1205 if (!*ci->ci_subtype)
1206 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1209 for (kv = SubMessage; kv->kv_key; kv++)
1210 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1212 ct->c_subtype = kv->kv_value;
1214 switch (ct->c_subtype) {
1215 case MESSAGE_RFC822:
1218 case MESSAGE_PARTIAL:
1223 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1224 adios (NULL, "out of memory");
1225 ct->c_ctparams = (void *) p;
1227 /* scan for parameters "id", "number", and "total" */
1228 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1229 if (!strcasecmp (*ap, "id")) {
1230 p->pm_partid = add (*ep, NULL);
1233 if (!strcasecmp (*ap, "number")) {
1234 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1235 || p->pm_partno < 1) {
1238 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1239 *ap, ci->ci_type, ci->ci_subtype,
1240 ct->c_file, TYPE_FIELD);
1245 if (!strcasecmp (*ap, "total")) {
1246 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1255 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1257 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1258 ci->ci_type, ci->ci_subtype,
1259 ct->c_file, TYPE_FIELD);
1265 case MESSAGE_EXTERNAL:
1272 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1273 adios (NULL, "out of memory");
1274 ct->c_ctparams = (void *) e;
1277 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1278 advise (ct->c_file, "unable to open for reading");
1282 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1284 if (!(p = get_content (fp, ct->c_file, 0))) {
1293 if ((exresult = params_external (ct, 0)) != NOTOK
1294 && p->c_ceopenfnx == openMail) {
1298 if ((size = ct->c_end - p->c_begin) <= 0) {
1300 content_error (NULL, ct,
1301 "empty body for access-type=mail-server");
1305 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1306 adios (NULL, "out of memory");
1307 fseek (p->c_fp, p->c_begin, SEEK_SET);
1309 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1311 adios ("failed", "fread");
1314 adios (NULL, "unexpected EOF from fread");
1317 bp += cc, size -= cc;
1324 p->c_end = p->c_begin;
1329 if (exresult == NOTOK)
1331 if (e->eb_flags == NOTOK)
1334 switch (p->c_type) {
1339 if (p->c_subtype != MESSAGE_RFC822)
1343 e->eb_partno = ct->c_partno;
1345 (*p->c_ctinitfnx) (p);
1360 params_external (CT ct, int composing)
1363 struct exbody *e = (struct exbody *) ct->c_ctparams;
1364 CI ci = &ct->c_ctinfo;
1366 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1367 if (!strcasecmp (*ap, "access-type")) {
1368 struct str2init *s2i;
1369 CT p = e->eb_content;
1371 for (s2i = str2methods; s2i->si_key; s2i++)
1372 if (!strcasecmp (*ep, s2i->si_key))
1376 e->eb_flags = NOTOK;
1377 p->c_encoding = CE_EXTERNAL;
1380 e->eb_access = s2i->si_key;
1381 e->eb_flags = s2i->si_val;
1382 p->c_encoding = CE_EXTERNAL;
1384 /* Call the Init function for this external type */
1385 if ((*s2i->si_init)(p) == NOTOK)
1389 if (!strcasecmp (*ap, "name")) {
1393 if (!strcasecmp (*ap, "permission")) {
1394 e->eb_permission = *ep;
1397 if (!strcasecmp (*ap, "site")) {
1401 if (!strcasecmp (*ap, "directory")) {
1405 if (!strcasecmp (*ap, "mode")) {
1409 if (!strcasecmp (*ap, "size")) {
1410 sscanf (*ep, "%lu", &e->eb_size);
1413 if (!strcasecmp (*ap, "server")) {
1417 if (!strcasecmp (*ap, "subject")) {
1418 e->eb_subject = *ep;
1421 if (composing && !strcasecmp (*ap, "body")) {
1422 e->eb_body = getcpy (*ep);
1427 if (!e->eb_access) {
1429 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1430 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1443 InitApplication (CT ct)
1446 CI ci = &ct->c_ctinfo;
1449 for (kv = SubApplication; kv->kv_key; kv++)
1450 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1452 ct->c_subtype = kv->kv_value;
1459 * TRANSFER ENCODINGS
1463 init_encoding (CT ct, OpenCEFunc openfnx)
1467 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1468 adios (NULL, "out of memory");
1471 ct->c_ceopenfnx = openfnx;
1472 ct->c_ceclosefnx = close_encoding;
1473 ct->c_cesizefnx = size_encoding;
1480 close_encoding (CT ct)
1484 if (!(ce = ct->c_cefile))
1494 static unsigned long
1495 size_encoding (CT ct)
1503 if (!(ce = ct->c_cefile))
1504 return (ct->c_end - ct->c_begin);
1506 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1507 return (long) st.st_size;
1510 if (stat (ce->ce_file, &st) != NOTOK)
1511 return (long) st.st_size;
1516 if (ct->c_encoding == CE_EXTERNAL)
1517 return (ct->c_end - ct->c_begin);
1520 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1521 return (ct->c_end - ct->c_begin);
1523 if (fstat (fd, &st) != NOTOK)
1524 size = (long) st.st_size;
1528 (*ct->c_ceclosefnx) (ct);
1537 static unsigned char b642nib[0x80] = {
1538 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1540 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1541 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1544 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1545 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1546 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1547 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1548 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1549 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1550 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1551 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1552 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1553 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1560 return init_encoding (ct, openBase64);
1565 openBase64 (CT ct, char **file)
1567 int bitno, cc, digested;
1570 unsigned char value, *b, *b1, *b2, *b3;
1571 char *cp, *ep, buffer[BUFSIZ];
1575 b = (unsigned char *) &bits;
1576 b1 = &b[endian > 0 ? 1 : 2];
1577 b2 = &b[endian > 0 ? 2 : 1];
1578 b3 = &b[endian > 0 ? 3 : 0];
1582 fseek (ce->ce_fp, 0L, SEEK_SET);
1587 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1588 content_error (ce->ce_file, ct, "unable to fopen for reading");
1594 if (*file == NULL) {
1595 ce->ce_file = add (m_scratch ("", tmp), NULL);
1598 ce->ce_file = add (*file, NULL);
1602 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1603 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1607 if ((len = ct->c_end - ct->c_begin) < 0)
1608 adios (NULL, "internal error(1)");
1610 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1611 content_error (ct->c_file, ct, "unable to open for reading");
1615 if ((digested = ct->c_digested))
1616 MD5Init (&mdContext);
1622 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1624 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1626 content_error (ct->c_file, ct, "error reading from");
1630 content_error (NULL, ct, "premature eof");
1638 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1643 if (skip || (*cp & 0x80)
1644 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1646 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1648 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1651 content_error (NULL, ct,
1652 "invalid BASE64 encoding -- continuing");
1656 bits |= value << bitno;
1658 if ((bitno -= 6) < 0) {
1659 putc ((char) *b1, ce->ce_fp);
1661 MD5Update (&mdContext, b1, 1);
1663 putc ((char) *b2, ce->ce_fp);
1665 MD5Update (&mdContext, b2, 1);
1667 putc ((char) *b3, ce->ce_fp);
1669 MD5Update (&mdContext, b3, 1);
1673 if (ferror (ce->ce_fp)) {
1674 content_error (ce->ce_file, ct,
1675 "error writing to");
1678 bitno = 18, bits = 0L, skip = 0;
1684 goto self_delimiting;
1693 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1695 content_error (NULL, ct, "invalid BASE64 encoding");
1700 fseek (ct->c_fp, 0L, SEEK_SET);
1702 if (fflush (ce->ce_fp)) {
1703 content_error (ce->ce_file, ct, "error writing to");
1708 unsigned char digest[16];
1710 MD5Final (digest, &mdContext);
1711 if (memcmp((char *) digest, (char *) ct->c_digest,
1712 sizeof(digest) / sizeof(digest[0])))
1713 content_error (NULL, ct,
1714 "content integrity suspect (digest mismatch) -- continuing");
1717 fprintf (stderr, "content integrity confirmed\n");
1720 fseek (ce->ce_fp, 0L, SEEK_SET);
1723 *file = ce->ce_file;
1724 return fileno (ce->ce_fp);
1727 free_encoding (ct, 0);
1736 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1743 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1744 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1745 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1746 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1747 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1748 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1749 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1750 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1759 return init_encoding (ct, openQuoted);
1764 openQuoted (CT ct, char **file)
1766 int cc, digested, len, quoted;
1768 char buffer[BUFSIZ];
1775 fseek (ce->ce_fp, 0L, SEEK_SET);
1780 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1781 content_error (ce->ce_file, ct, "unable to fopen for reading");
1787 if (*file == NULL) {
1788 ce->ce_file = add (m_scratch ("", tmp), NULL);
1791 ce->ce_file = add (*file, NULL);
1795 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1796 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1800 if ((len = ct->c_end - ct->c_begin) < 0)
1801 adios (NULL, "internal error(2)");
1803 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1804 content_error (ct->c_file, ct, "unable to open for reading");
1808 if ((digested = ct->c_digested))
1809 MD5Init (&mdContext);
1816 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1820 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1821 content_error (NULL, ct, "premature eof");
1825 if ((cc = strlen (buffer)) > len)
1829 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1834 for (; cp < ep; cp++) {
1837 if (!isxdigit (*cp)) {
1839 dp = "expecting hexidecimal-digit";
1840 goto invalid_encoding;
1843 mask |= hex2nib[*cp & 0x7f];
1844 putc (mask, ce->ce_fp);
1846 MD5Update (&mdContext, &mask, 1);
1850 putc (*cp, ce->ce_fp);
1852 MD5Update (&mdContext, (unsigned char *) ":", 1);
1856 if (!isxdigit (*cp))
1858 mask = hex2nib[*cp & 0x7f];
1864 if (ferror (ce->ce_fp)) {
1865 content_error (ce->ce_file, ct, "error writing to");
1874 if (*cp < '!' || *cp > '~') {
1876 dp = "expecting character in range [!..~]";
1879 i = strlen (invo_name) + 2;
1880 content_error (NULL, ct,
1881 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1889 putc (*cp, ce->ce_fp);
1892 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1894 MD5Update (&mdContext, (unsigned char *) cp, 1);
1896 if (ferror (ce->ce_fp)) {
1897 content_error (ce->ce_file, ct, "error writing to");
1903 if (*++cp != '\n') {
1912 content_error (NULL, ct,
1913 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1917 fseek (ct->c_fp, 0L, SEEK_SET);
1919 if (fflush (ce->ce_fp)) {
1920 content_error (ce->ce_file, ct, "error writing to");
1925 unsigned char digest[16];
1927 MD5Final (digest, &mdContext);
1928 if (memcmp((char *) digest, (char *) ct->c_digest,
1929 sizeof(digest) / sizeof(digest[0])))
1930 content_error (NULL, ct,
1931 "content integrity suspect (digest mismatch) -- continuing");
1934 fprintf (stderr, "content integrity confirmed\n");
1937 fseek (ce->ce_fp, 0L, SEEK_SET);
1940 *file = ce->ce_file;
1941 return fileno (ce->ce_fp);
1944 free_encoding (ct, 0);
1956 if (init_encoding (ct, open7Bit) == NOTOK)
1959 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1965 open7Bit (CT ct, char **file)
1968 char buffer[BUFSIZ];
1973 fseek (ce->ce_fp, 0L, SEEK_SET);
1978 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1979 content_error (ce->ce_file, ct, "unable to fopen for reading");
1985 if (*file == NULL) {
1986 ce->ce_file = add (m_scratch ("", tmp), NULL);
1989 ce->ce_file = add (*file, NULL);
1993 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1994 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1998 if (ct->c_type == CT_MULTIPART) {
2000 CI ci = &ct->c_ctinfo;
2003 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2004 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2005 + 1 + strlen (ci->ci_subtype);
2006 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2007 putc (';', ce->ce_fp);
2010 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2012 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2013 fputs ("\n\t", ce->ce_fp);
2016 putc (' ', ce->ce_fp);
2019 fprintf (ce->ce_fp, "%s", buffer);
2023 if (ci->ci_comment) {
2024 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2025 fputs ("\n\t", ce->ce_fp);
2029 putc (' ', ce->ce_fp);
2032 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2035 fprintf (ce->ce_fp, "\n");
2037 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2039 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2040 fprintf (ce->ce_fp, "\n");
2043 if ((len = ct->c_end - ct->c_begin) < 0)
2044 adios (NULL, "internal error(3)");
2046 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2047 content_error (ct->c_file, ct, "unable to open for reading");
2051 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2053 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2055 content_error (ct->c_file, ct, "error reading from");
2059 content_error (NULL, ct, "premature eof");
2067 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2068 if (ferror (ce->ce_fp)) {
2069 content_error (ce->ce_file, ct, "error writing to");
2074 fseek (ct->c_fp, 0L, SEEK_SET);
2076 if (fflush (ce->ce_fp)) {
2077 content_error (ce->ce_file, ct, "error writing to");
2081 fseek (ce->ce_fp, 0L, SEEK_SET);
2084 *file = ce->ce_file;
2085 return fileno (ce->ce_fp);
2088 free_encoding (ct, 0);
2098 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2100 char cachefile[BUFSIZ];
2103 fseek (ce->ce_fp, 0L, SEEK_SET);
2108 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2109 content_error (ce->ce_file, ct, "unable to fopen for reading");
2115 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2116 cachefile, sizeof(cachefile)) != NOTOK) {
2117 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2118 ce->ce_file = getcpy (cachefile);
2122 admonish (cachefile, "unable to fopen for reading");
2129 *file = ce->ce_file;
2130 *fd = fileno (ce->ce_fp);
2141 return init_encoding (ct, openFile);
2146 openFile (CT ct, char **file)
2149 char cachefile[BUFSIZ];
2150 struct exbody *e = ct->c_ctexbody;
2151 CE ce = ct->c_cefile;
2153 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2165 content_error (NULL, ct, "missing name parameter");
2169 ce->ce_file = getcpy (e->eb_name);
2172 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2173 content_error (ce->ce_file, ct, "unable to fopen for reading");
2177 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2178 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2179 cachefile, sizeof(cachefile)) != NOTOK) {
2183 mask = umask (cachetype ? ~m_gmprot () : 0222);
2184 if ((fp = fopen (cachefile, "w"))) {
2186 char buffer[BUFSIZ];
2187 FILE *gp = ce->ce_fp;
2189 fseek (gp, 0L, SEEK_SET);
2191 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2193 fwrite (buffer, sizeof(*buffer), cc, fp);
2197 admonish (ce->ce_file, "error reading");
2202 admonish (cachefile, "error writing");
2210 fseek (ce->ce_fp, 0L, SEEK_SET);
2211 *file = ce->ce_file;
2212 return fileno (ce->ce_fp);
2222 return init_encoding (ct, openFTP);
2227 openFTP (CT ct, char **file)
2229 int cachetype, caching, fd;
2231 char *bp, *ftp, *user, *pass;
2232 char buffer[BUFSIZ], cachefile[BUFSIZ];
2235 static char *username = NULL;
2236 static char *password = NULL;
2241 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2249 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2260 if (!e->eb_name || !e->eb_site) {
2261 content_error (NULL, ct, "missing %s parameter",
2262 e->eb_name ? "site": "name");
2269 pidcheck (pidwait (xpid, NOTOK));
2273 /* Get the buffer ready to go */
2275 buflen = sizeof(buffer);
2278 * Construct the query message for user
2280 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2286 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2292 snprintf (bp, buflen, "\n using %sFTP from site %s",
2293 e->eb_flags ? "anonymous " : "", e->eb_site);
2298 if (e->eb_size > 0) {
2299 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2304 snprintf (bp, buflen, "? ");
2307 * Now, check the answer
2309 if (!getanswer (buffer))
2314 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2317 ruserpass (e->eb_site, &username, &password);
2322 ce->ce_unlink = (*file == NULL);
2324 cachefile[0] = '\0';
2325 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2326 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2327 cachefile, sizeof(cachefile)) != NOTOK) {
2328 if (*file == NULL) {
2335 ce->ce_file = add (*file, NULL);
2337 ce->ce_file = add (cachefile, NULL);
2339 ce->ce_file = add (m_scratch ("", tmp), NULL);
2341 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2342 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2350 int child_id, i, vecp;
2354 vec[vecp++] = r1bindex (ftp, '/');
2355 vec[vecp++] = e->eb_site;
2358 vec[vecp++] = e->eb_dir;
2359 vec[vecp++] = e->eb_name;
2360 vec[vecp++] = ce->ce_file,
2361 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2362 ? "ascii" : "binary";
2367 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2371 adios ("fork", "unable to");
2375 close (fileno (ce->ce_fp));
2377 fprintf (stderr, "unable to exec ");
2383 if (pidXwait (child_id, NULL)) {
2387 username = password = NULL;
2396 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2398 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2405 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2410 mask = umask (cachetype ? ~m_gmprot () : 0222);
2411 if ((fp = fopen (cachefile, "w"))) {
2413 FILE *gp = ce->ce_fp;
2415 fseek (gp, 0L, SEEK_SET);
2417 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2419 fwrite (buffer, sizeof(*buffer), cc, fp);
2423 admonish (ce->ce_file, "error reading");
2428 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");