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];
1571 /* sbeck -- handle prefixes */
1576 b = (unsigned char *) &bits;
1577 b1 = &b[endian > 0 ? 1 : 2];
1578 b2 = &b[endian > 0 ? 2 : 1];
1579 b3 = &b[endian > 0 ? 3 : 0];
1583 fseek (ce->ce_fp, 0L, SEEK_SET);
1588 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1589 content_error (ce->ce_file, ct, "unable to fopen for reading");
1595 if (*file == NULL) {
1596 ce->ce_file = add (m_scratch ("", tmp), NULL);
1599 ce->ce_file = add (*file, NULL);
1603 /* sbeck@cise.ufl.edu -- handle suffixes */
1605 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1606 invo_name, ci->ci_type, ci->ci_subtype);
1607 cp = context_find (buffer);
1608 if (cp == NULL || *cp == '\0') {
1609 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1611 cp = context_find (buffer);
1613 if (cp != NULL && *cp != '\0')
1614 ce->ce_file = add (cp, ce->ce_file);
1616 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1617 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1621 if ((len = ct->c_end - ct->c_begin) < 0)
1622 adios (NULL, "internal error(1)");
1624 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1625 content_error (ct->c_file, ct, "unable to open for reading");
1629 if ((digested = ct->c_digested))
1630 MD5Init (&mdContext);
1636 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1638 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1640 content_error (ct->c_file, ct, "error reading from");
1644 content_error (NULL, ct, "premature eof");
1652 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1657 if (skip || (*cp & 0x80)
1658 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1660 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1662 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1665 content_error (NULL, ct,
1666 "invalid BASE64 encoding -- continuing");
1670 bits |= value << bitno;
1672 if ((bitno -= 6) < 0) {
1673 putc ((char) *b1, ce->ce_fp);
1675 MD5Update (&mdContext, b1, 1);
1677 putc ((char) *b2, ce->ce_fp);
1679 MD5Update (&mdContext, b2, 1);
1681 putc ((char) *b3, ce->ce_fp);
1683 MD5Update (&mdContext, b3, 1);
1687 if (ferror (ce->ce_fp)) {
1688 content_error (ce->ce_file, ct,
1689 "error writing to");
1692 bitno = 18, bits = 0L, skip = 0;
1698 goto self_delimiting;
1707 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1709 content_error (NULL, ct, "invalid BASE64 encoding");
1714 fseek (ct->c_fp, 0L, SEEK_SET);
1716 if (fflush (ce->ce_fp)) {
1717 content_error (ce->ce_file, ct, "error writing to");
1722 unsigned char digest[16];
1724 MD5Final (digest, &mdContext);
1725 if (memcmp((char *) digest, (char *) ct->c_digest,
1726 sizeof(digest) / sizeof(digest[0])))
1727 content_error (NULL, ct,
1728 "content integrity suspect (digest mismatch) -- continuing");
1731 fprintf (stderr, "content integrity confirmed\n");
1734 fseek (ce->ce_fp, 0L, SEEK_SET);
1737 *file = ce->ce_file;
1738 return fileno (ce->ce_fp);
1741 free_encoding (ct, 0);
1750 static char hex2nib[0x80] = {
1751 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1752 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1753 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1754 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1756 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1757 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1758 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1759 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1773 return init_encoding (ct, openQuoted);
1778 openQuoted (CT ct, char **file)
1780 int cc, digested, len, quoted;
1782 char buffer[BUFSIZ];
1785 /* sbeck -- handle prefixes */
1791 fseek (ce->ce_fp, 0L, SEEK_SET);
1796 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1797 content_error (ce->ce_file, ct, "unable to fopen for reading");
1803 if (*file == NULL) {
1804 ce->ce_file = add (m_scratch ("", tmp), NULL);
1807 ce->ce_file = add (*file, NULL);
1811 /* sbeck@cise.ufl.edu -- handle suffixes */
1813 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1814 invo_name, ci->ci_type, ci->ci_subtype);
1815 cp = context_find (buffer);
1816 if (cp == NULL || *cp == '\0') {
1817 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1819 cp = context_find (buffer);
1821 if (cp != NULL && *cp != '\0')
1822 ce->ce_file = add (cp, ce->ce_file);
1824 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1825 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1829 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1830 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1834 if ((len = ct->c_end - ct->c_begin) < 0)
1835 adios (NULL, "internal error(2)");
1837 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1838 content_error (ct->c_file, ct, "unable to open for reading");
1842 if ((digested = ct->c_digested))
1843 MD5Init (&mdContext);
1850 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1854 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1855 content_error (NULL, ct, "premature eof");
1859 if ((cc = strlen (buffer)) > len)
1863 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1868 for (; cp < ep; cp++) {
1871 if (!isxdigit (*cp)) {
1873 dp = "expecting hexidecimal-digit";
1874 goto invalid_encoding;
1877 mask |= hex2nib[*cp & 0x7f];
1878 putc (mask, ce->ce_fp);
1880 MD5Update (&mdContext, &mask, 1);
1884 putc (*cp, ce->ce_fp);
1886 MD5Update (&mdContext, (unsigned char *) ":", 1);
1890 if (!isxdigit (*cp))
1892 mask = hex2nib[*cp & 0x7f];
1898 if (ferror (ce->ce_fp)) {
1899 content_error (ce->ce_file, ct, "error writing to");
1908 if (*cp < '!' || *cp > '~') {
1910 dp = "expecting character in range [!..~]";
1913 i = strlen (invo_name) + 2;
1914 content_error (NULL, ct,
1915 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1923 putc (*cp, ce->ce_fp);
1926 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1928 MD5Update (&mdContext, (unsigned char *) cp, 1);
1930 if (ferror (ce->ce_fp)) {
1931 content_error (ce->ce_file, ct, "error writing to");
1937 if (*++cp != '\n') {
1946 content_error (NULL, ct,
1947 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1951 fseek (ct->c_fp, 0L, SEEK_SET);
1953 if (fflush (ce->ce_fp)) {
1954 content_error (ce->ce_file, ct, "error writing to");
1959 unsigned char digest[16];
1961 MD5Final (digest, &mdContext);
1962 if (memcmp((char *) digest, (char *) ct->c_digest,
1963 sizeof(digest) / sizeof(digest[0])))
1964 content_error (NULL, ct,
1965 "content integrity suspect (digest mismatch) -- continuing");
1968 fprintf (stderr, "content integrity confirmed\n");
1971 fseek (ce->ce_fp, 0L, SEEK_SET);
1974 *file = ce->ce_file;
1975 return fileno (ce->ce_fp);
1978 free_encoding (ct, 0);
1990 if (init_encoding (ct, open7Bit) == NOTOK)
1993 ct->c_cesizefnx = NULL; /* no need to decode for real size */
1999 open7Bit (CT ct, char **file)
2002 char buffer[BUFSIZ];
2003 /* sbeck -- handle prefixes */
2010 fseek (ce->ce_fp, 0L, SEEK_SET);
2015 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2016 content_error (ce->ce_file, ct, "unable to fopen for reading");
2022 if (*file == NULL) {
2023 ce->ce_file = add (m_scratch ("", tmp), NULL);
2026 ce->ce_file = add (*file, NULL);
2030 /* sbeck@cise.ufl.edu -- handle suffixes */
2032 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2033 invo_name, ci->ci_type, ci->ci_subtype);
2034 cp = context_find (buffer);
2035 if (cp == NULL || *cp == '\0') {
2036 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2038 cp = context_find (buffer);
2040 if (cp != NULL && *cp != '\0')
2041 ce->ce_file = add (cp, ce->ce_file);
2043 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2044 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2048 if (ct->c_type == CT_MULTIPART) {
2050 CI ci = &ct->c_ctinfo;
2053 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2054 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2055 + 1 + strlen (ci->ci_subtype);
2056 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2057 putc (';', ce->ce_fp);
2060 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2062 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2063 fputs ("\n\t", ce->ce_fp);
2066 putc (' ', ce->ce_fp);
2069 fprintf (ce->ce_fp, "%s", buffer);
2073 if (ci->ci_comment) {
2074 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2075 fputs ("\n\t", ce->ce_fp);
2079 putc (' ', ce->ce_fp);
2082 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2085 fprintf (ce->ce_fp, "\n");
2087 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2089 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2090 fprintf (ce->ce_fp, "\n");
2093 if ((len = ct->c_end - ct->c_begin) < 0)
2094 adios (NULL, "internal error(3)");
2096 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2097 content_error (ct->c_file, ct, "unable to open for reading");
2101 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2103 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2105 content_error (ct->c_file, ct, "error reading from");
2109 content_error (NULL, ct, "premature eof");
2117 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2118 if (ferror (ce->ce_fp)) {
2119 content_error (ce->ce_file, ct, "error writing to");
2124 fseek (ct->c_fp, 0L, SEEK_SET);
2126 if (fflush (ce->ce_fp)) {
2127 content_error (ce->ce_file, ct, "error writing to");
2131 fseek (ce->ce_fp, 0L, SEEK_SET);
2134 *file = ce->ce_file;
2135 return fileno (ce->ce_fp);
2138 free_encoding (ct, 0);
2148 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2150 char cachefile[BUFSIZ];
2153 fseek (ce->ce_fp, 0L, SEEK_SET);
2158 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2159 content_error (ce->ce_file, ct, "unable to fopen for reading");
2165 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2166 cachefile, sizeof(cachefile)) != NOTOK) {
2167 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2168 ce->ce_file = getcpy (cachefile);
2172 admonish (cachefile, "unable to fopen for reading");
2179 *file = ce->ce_file;
2180 *fd = fileno (ce->ce_fp);
2191 return init_encoding (ct, openFile);
2196 openFile (CT ct, char **file)
2199 char cachefile[BUFSIZ];
2200 struct exbody *e = ct->c_ctexbody;
2201 CE ce = ct->c_cefile;
2203 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2215 content_error (NULL, ct, "missing name parameter");
2219 ce->ce_file = getcpy (e->eb_name);
2222 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2223 content_error (ce->ce_file, ct, "unable to fopen for reading");
2227 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2228 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2229 cachefile, sizeof(cachefile)) != NOTOK) {
2233 mask = umask (cachetype ? ~m_gmprot () : 0222);
2234 if ((fp = fopen (cachefile, "w"))) {
2236 char buffer[BUFSIZ];
2237 FILE *gp = ce->ce_fp;
2239 fseek (gp, 0L, SEEK_SET);
2241 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2243 fwrite (buffer, sizeof(*buffer), cc, fp);
2247 admonish (ce->ce_file, "error reading");
2252 admonish (cachefile, "error writing");
2260 fseek (ce->ce_fp, 0L, SEEK_SET);
2261 *file = ce->ce_file;
2262 return fileno (ce->ce_fp);
2272 return init_encoding (ct, openFTP);
2277 openFTP (CT ct, char **file)
2279 int cachetype, caching, fd;
2281 char *bp, *ftp, *user, *pass;
2282 char buffer[BUFSIZ], cachefile[BUFSIZ];
2285 static char *username = NULL;
2286 static char *password = NULL;
2291 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2299 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2310 if (!e->eb_name || !e->eb_site) {
2311 content_error (NULL, ct, "missing %s parameter",
2312 e->eb_name ? "site": "name");
2319 pidcheck (pidwait (xpid, NOTOK));
2323 /* Get the buffer ready to go */
2325 buflen = sizeof(buffer);
2328 * Construct the query message for user
2330 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2336 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2342 snprintf (bp, buflen, "\n using %sFTP from site %s",
2343 e->eb_flags ? "anonymous " : "", e->eb_site);
2348 if (e->eb_size > 0) {
2349 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2354 snprintf (bp, buflen, "? ");
2357 * Now, check the answer
2359 if (!getanswer (buffer))
2364 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2367 ruserpass (e->eb_site, &username, &password);
2372 ce->ce_unlink = (*file == NULL);
2374 cachefile[0] = '\0';
2375 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2376 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2377 cachefile, sizeof(cachefile)) != NOTOK) {
2378 if (*file == NULL) {
2385 ce->ce_file = add (*file, NULL);
2387 ce->ce_file = add (cachefile, NULL);
2389 ce->ce_file = add (m_scratch ("", tmp), NULL);
2391 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2392 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2400 int child_id, i, vecp;
2404 vec[vecp++] = r1bindex (ftp, '/');
2405 vec[vecp++] = e->eb_site;
2408 vec[vecp++] = e->eb_dir;
2409 vec[vecp++] = e->eb_name;
2410 vec[vecp++] = ce->ce_file,
2411 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2412 ? "ascii" : "binary";
2417 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2421 adios ("fork", "unable to");
2425 close (fileno (ce->ce_fp));
2427 fprintf (stderr, "unable to exec ");
2433 if (pidXwait (child_id, NULL)) {
2437 username = password = NULL;
2446 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2448 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2455 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2460 mask = umask (cachetype ? ~m_gmprot () : 0222);
2461 if ((fp = fopen (cachefile, "w"))) {
2463 FILE *gp = ce->ce_fp;
2465 fseek (gp, 0L, SEEK_SET);
2467 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2469 fwrite (buffer, sizeof(*buffer), cc, fp);
2473 admonish (ce->ce_file, "error reading");
2478 admonish (cachefile, "error writing");
2487 fseek (ce->ce_fp, 0L, SEEK_SET);
2488 *file = ce->ce_file;
2489 return fileno (ce->ce_fp);
2500 return init_encoding (ct, openMail);
2505 openMail (CT ct, char **file)
2507 int child_id, fd, i, vecp;
2509 char *bp, buffer[BUFSIZ], *vec[7];
2510 struct exbody *e = ct->c_ctexbody;
2511 CE ce = ct->c_cefile;
2513 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2524 if (!e->eb_server) {
2525 content_error (NULL, ct, "missing server parameter");
2532 pidcheck (pidwait (xpid, NOTOK));
2536 /* Get buffer ready to go */
2538 buflen = sizeof(buffer);
2540 /* Now, construct query message */
2541 snprintf (bp, buflen, "Retrieve content");
2547 snprintf (bp, buflen, " %s", e->eb_partno);
2553 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2555 e->eb_subject ? e->eb_subject : e->eb_body);
2557 /* Now, check answer */
2558 if (!getanswer (buffer))
2562 vec[vecp++] = r1bindex (mailproc, '/');
2563 vec[vecp++] = e->eb_server;
2564 vec[vecp++] = "-subject";
2565 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2566 vec[vecp++] = "-body";
2567 vec[vecp++] = e->eb_body;
2570 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2574 advise ("fork", "unable to");
2578 execvp (mailproc, vec);
2579 fprintf (stderr, "unable to exec ");
2585 if (pidXwait (child_id, NULL) == OK)
2586 advise (NULL, "request sent");
2590 if (*file == NULL) {
2591 ce->ce_file = add (m_scratch ("", tmp), NULL);
2594 ce->ce_file = add (*file, NULL);
2598 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2599 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2604 free (ct->c_showproc);
2605 ct->c_showproc = add ("true", NULL);
2607 fseek (ce->ce_fp, 0L, SEEK_SET);
2608 *file = ce->ce_file;
2609 return fileno (ce->ce_fp);
2614 readDigest (CT ct, char *cp)
2619 unsigned char *dp, value, *ep;
2620 unsigned char *b, *b1, *b2, *b3;
2622 b = (unsigned char *) &bits,
2623 b1 = &b[endian > 0 ? 1 : 2],
2624 b2 = &b[endian > 0 ? 2 : 1],
2625 b3 = &b[endian > 0 ? 3 : 0];
2630 for (ep = (dp = ct->c_digest)
2631 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2636 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2638 fprintf (stderr, "invalid BASE64 encoding\n");
2642 bits |= value << bitno;
2644 if ((bitno -= 6) < 0) {
2645 if (dp + (3 - skip) > ep)
2646 goto invalid_digest;
2661 goto self_delimiting;
2666 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2676 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2684 fprintf (stderr, "MD5 digest=");
2685 for (dp = ct->c_digest; dp < ep; dp++)
2686 fprintf (stderr, "%02x", *dp & 0xff);
2687 fprintf (stderr, "\n");