3 * mhparse.c -- routines to parse the contents of MIME messages
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
14 #include <h/signals.h>
22 #include <h/mhparse.h>
24 #ifdef HAVE_SYS_WAIT_H
25 # include <sys/wait.h>
31 extern int endian; /* mhmisc.c */
33 extern pid_t xpid; /* mhshowsbr.c */
36 extern int rcachesw; /* mhcachesbr.c */
37 extern int wcachesw; /* mhcachesbr.c */
39 int checksw = 0; /* check Content-MD5 field */
42 * Directory to place temp files. This must
43 * be set before these routines are called.
48 * Structure for mapping types to their internal flags
56 * Structures for TEXT messages
58 static struct k2v SubText[] = {
59 { "plain", TEXT_PLAIN },
60 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
61 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
62 { NULL, TEXT_UNKNOWN } /* this one must be last! */
65 static struct k2v Charset[] = {
66 { "us-ascii", CHARSET_USASCII },
67 { "iso-8859-1", CHARSET_LATIN },
68 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
72 * Structures for MULTIPART messages
74 static struct k2v SubMultiPart[] = {
75 { "mixed", MULTI_MIXED },
76 { "alternative", MULTI_ALTERNATE },
77 { "digest", MULTI_DIGEST },
78 { "parallel", MULTI_PARALLEL },
79 { NULL, MULTI_UNKNOWN } /* this one must be last! */
83 * Structures for MESSAGE messages
85 static struct k2v SubMessage[] = {
86 { "rfc822", MESSAGE_RFC822 },
87 { "partial", MESSAGE_PARTIAL },
88 { "external-body", MESSAGE_EXTERNAL },
89 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
93 * Structure for APPLICATION messages
95 static struct k2v SubApplication[] = {
96 { "octet-stream", APPLICATION_OCTETS },
97 { "postscript", APPLICATION_POSTSCRIPT },
98 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
103 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
106 int find_cache (CT, int, int *, char *, char *, int);
109 int part_ok (CT, int);
110 int type_ok (CT, int);
111 int make_intermediates (char *);
112 void content_error (char *, CT, char *, ...);
115 void free_content (CT);
116 void free_encoding (CT, int);
122 CT parse_mime (char *);
127 static CT get_content (FILE *, char *, int);
128 static int add_header (CT, char *, char *);
129 static int get_ctinfo (char *, CT);
130 static int get_comment (CT, char **, int);
131 static int InitGeneric (CT);
132 static int InitText (CT);
133 static int InitMultiPart (CT);
134 static void reverse_parts (CT);
135 static int InitMessage (CT);
136 static int params_external (CT, int);
137 static int InitApplication (CT);
138 static int init_encoding (CT, OpenCEFunc);
139 static void close_encoding (CT);
140 static unsigned long size_encoding (CT);
141 static int InitBase64 (CT);
142 static int openBase64 (CT, char **);
143 static int InitQuoted (CT);
144 static int openQuoted (CT, char **);
145 static int Init7Bit (CT);
146 static int open7Bit (CT, char **);
147 static int openExternal (CT, CT, CE, char **, int *);
148 static int InitFile (CT);
149 static int openFile (CT, char **);
150 static int InitFTP (CT);
151 static int openFTP (CT, char **);
152 static int InitMail (CT);
153 static int openMail (CT, char **);
154 static int readDigest (CT, char *);
157 * Structures for mapping (content) types to
158 * the functions to handle them.
166 static struct str2init str2cts[] = {
167 { "application", CT_APPLICATION, InitApplication },
168 { "audio", CT_AUDIO, InitGeneric },
169 { "image", CT_IMAGE, InitGeneric },
170 { "message", CT_MESSAGE, InitMessage },
171 { "multipart", CT_MULTIPART, InitMultiPart },
172 { "text", CT_TEXT, InitText },
173 { "video", CT_VIDEO, InitGeneric },
174 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
175 { NULL, CT_UNKNOWN, NULL },
178 static struct str2init str2ces[] = {
179 { "base64", CE_BASE64, InitBase64 },
180 { "quoted-printable", CE_QUOTED, InitQuoted },
181 { "8bit", CE_8BIT, Init7Bit },
182 { "7bit", CE_7BIT, Init7Bit },
183 { "binary", CE_BINARY, Init7Bit },
184 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
185 { NULL, CE_UNKNOWN, NULL },
189 * NOTE WELL: si_key MUST NOT have value of NOTOK
191 * si_key is 1 if access method is anonymous.
193 static struct str2init str2methods[] = {
194 { "afs", 1, InitFile },
195 { "anon-ftp", 1, InitFTP },
196 { "ftp", 0, InitFTP },
197 { "local-file", 0, InitFile },
198 { "mail-server", 0, InitMail },
204 pidcheck (int status)
206 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
216 * Main entry point for parsing a MIME message or file.
217 * It returns the Content structure for the top level
218 * entity in the file.
222 parse_mime (char *file)
230 * Check if file is actually standard input
232 if ((is_stdin = !(strcmp (file, "-")))) {
233 file = add (m_tmpfil (invo_name), NULL);
234 if ((fp = fopen (file, "w+")) == NULL) {
235 advise (file, "unable to fopen for writing and reading");
239 while (fgets (buffer, sizeof(buffer), stdin))
243 if (ferror (stdin)) {
245 advise ("stdin", "error reading");
250 advise (file, "error writing");
253 fseek (fp, 0L, SEEK_SET);
254 } else if ((fp = fopen (file, "r")) == NULL) {
255 advise (file, "unable to read");
259 if (!(ct = get_content (fp, file, 1))) {
263 advise (NULL, "unable to decode %s", file);
268 ct->c_unlink = 1; /* temp file to remove */
272 if (ct->c_end == 0L) {
273 fseek (fp, 0L, SEEK_END);
274 ct->c_end = ftell (fp);
277 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
289 * Main routine for reading/parsing the headers
290 * of a message content.
292 * toplevel = 1 # we are at the top level of the message
293 * toplevel = 0 # we are inside message type or multipart type
294 * # other than multipart/digest
295 * toplevel = -1 # we are inside multipart/digest
299 get_content (FILE *in, char *file, int toplevel)
302 char buf[BUFSIZ], name[NAMESZ];
307 /* allocate the content structure */
308 if (!(ct = (CT) calloc (1, sizeof(*ct))))
309 adios (NULL, "out of memory");
312 ct->c_file = add (file, NULL);
313 ct->c_begin = ftell (ct->c_fp) + 1;
316 * Parse the header fields for this
317 * content into a linked list.
319 for (compnum = 1, state = FLD;;) {
320 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
326 /* get copies of the buffers */
327 np = add (name, NULL);
328 vp = add (buf, NULL);
330 /* if necessary, get rest of field */
331 while (state == FLDPLUS) {
332 state = m_getfld (state, name, buf, sizeof(buf), in);
333 vp = add (buf, vp); /* add to previous value */
336 /* Now add the header data to the list */
337 add_header (ct, np, vp);
339 /* continue, if this isn't the last header field */
340 if (state != FLDEOF) {
341 ct->c_begin = ftell (in) + 1;
348 ct->c_begin = ftell (in) - strlen (buf);
352 ct->c_begin = ftell (in);
357 adios (NULL, "message format error in component #%d", compnum);
360 adios (NULL, "getfld() returned %d", state);
363 /* break out of the loop */
368 * Read the content headers. We will parse the
369 * MIME related header fields into their various
370 * structures and set internal flags related to
371 * content type/subtype, etc.
374 hp = ct->c_first_hf; /* start at first header field */
376 /* Get MIME-Version field */
377 if (!strcasecmp (hp->name, VRSN_FIELD)) {
382 advise (NULL, "message %s has multiple %s: fields",
383 ct->c_file, VRSN_FIELD);
386 ct->c_vrsn = add (hp->value, NULL);
388 /* Now, cleanup this field */
391 while (isspace (*cp))
393 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
395 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
400 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
402 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
405 for (dp = cp; istoken (*dp); dp++)
409 ucmp = !strcasecmp (cp, VRSN_VALUE);
412 admonish (NULL, "message %s has unknown value for %s: field (%s)",
413 ct->c_file, VRSN_FIELD, cp);
416 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
417 /* Get Content-Type field */
418 struct str2init *s2i;
419 CI ci = &ct->c_ctinfo;
421 /* Check if we've already seen a Content-Type header */
423 advise (NULL, "message %s has multiple %s: fields",
424 ct->c_file, TYPE_FIELD);
428 /* Parse the Content-Type field */
429 if (get_ctinfo (hp->value, ct) == NOTOK)
433 * Set the Init function and the internal
434 * flag for this content type.
436 for (s2i = str2cts; s2i->si_key; s2i++)
437 if (!strcasecmp (ci->ci_type, s2i->si_key))
439 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
441 ct->c_type = s2i->si_val;
442 ct->c_ctinitfnx = s2i->si_init;
444 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
445 /* Get Content-Transfer-Encoding field */
447 struct str2init *s2i;
450 * Check if we've already seen the
451 * Content-Transfer-Encoding field
454 advise (NULL, "message %s has multiple %s: fields",
455 ct->c_file, ENCODING_FIELD);
459 /* get copy of this field */
460 ct->c_celine = cp = add (hp->value, NULL);
462 while (isspace (*cp))
464 for (dp = cp; istoken (*dp); dp++)
470 * Find the internal flag and Init function
471 * for this transfer encoding.
473 for (s2i = str2ces; s2i->si_key; s2i++)
474 if (!strcasecmp (cp, s2i->si_key))
476 if (!s2i->si_key && !uprf (cp, "X-"))
479 ct->c_encoding = s2i->si_val;
481 /* Call the Init function for this encoding */
482 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
485 else if (!strcasecmp (hp->name, MD5_FIELD)) {
486 /* Get Content-MD5 field */
492 if (ct->c_digested) {
493 advise (NULL, "message %s has multiple %s: fields",
494 ct->c_file, MD5_FIELD);
498 ep = cp = add (hp->value, NULL); /* get a copy */
500 while (isspace (*cp))
502 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
504 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
509 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
511 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
516 for (dp = cp; *dp && !isspace (*dp); dp++)
524 else if (!strcasecmp (hp->name, ID_FIELD)) {
525 /* Get Content-ID field */
526 ct->c_id = add (hp->value, ct->c_id);
528 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
529 /* Get Content-Description field */
530 ct->c_descr = add (hp->value, ct->c_descr);
534 hp = hp->next; /* next header field */
538 * Check if we saw a Content-Type field.
539 * If not, then assign a default value for
540 * it, and the Init function.
544 * If we are inside a multipart/digest message,
545 * so default type is message/rfc822
548 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
550 ct->c_type = CT_MESSAGE;
551 ct->c_ctinitfnx = InitMessage;
554 * Else default type is text/plain
556 if (get_ctinfo ("text/plain", ct) == NOTOK)
558 ct->c_type = CT_TEXT;
559 ct->c_ctinitfnx = InitText;
563 /* Use default Transfer-Encoding, if necessary */
565 ct->c_encoding = CE_7BIT;
578 * small routine to add header field to list
582 add_header (CT ct, char *name, char *value)
586 /* allocate header field structure */
587 if (!(hp = malloc (sizeof(*hp))))
588 adios (NULL, "out of memory");
590 /* link data into header structure */
595 /* link header structure into the list */
596 if (ct->c_first_hf == NULL) {
597 ct->c_first_hf = hp; /* this is the first */
600 ct->c_last_hf->next = hp; /* add it to the end */
609 * Parse Content-Type line and fill in the
610 * information of the CTinfo structure.
614 get_ctinfo (char *cp, CT ct)
617 char *dp, **ap, **ep;
622 i = strlen (invo_name) + 2;
624 /* store copy of Content-Type line */
625 cp = ct->c_ctline = add (cp, NULL);
627 while (isspace (*cp)) /* trim leading spaces */
630 /* change newlines to spaces */
631 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
634 /* trim trailing spaces */
635 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
641 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
643 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
646 for (dp = cp; istoken (*dp); dp++)
649 ci->ci_type = add (cp, NULL); /* store content type */
653 advise (NULL, "invalid %s: field in message %s (empty type)",
654 TYPE_FIELD, ct->c_file);
658 /* down case the content type string */
659 for (dp = ci->ci_type; *dp; dp++)
660 if (isalpha(*dp) && isupper (*dp))
663 while (isspace (*cp))
666 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
670 ci->ci_subtype = add ("", NULL);
675 while (isspace (*cp))
678 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
681 for (dp = cp; istoken (*dp); dp++)
684 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
687 if (!*ci->ci_subtype) {
689 "invalid %s: field in message %s (empty subtype for \"%s\")",
690 TYPE_FIELD, ct->c_file, ci->ci_type);
694 /* down case the content subtype string */
695 for (dp = ci->ci_subtype; *dp; dp++)
696 if (isalpha(*dp) && isupper (*dp))
700 while (isspace (*cp))
703 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
707 * Parse attribute/value pairs given with Content-Type
709 ep = (ap = ci->ci_attrs) + NPARMS;
715 "too many parameters in message %s's %s: field (%d max)",
716 ct->c_file, TYPE_FIELD, NPARMS);
721 while (isspace (*cp))
724 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
729 "extraneous trailing ';' in message %s's %s: parameter list",
730 ct->c_file, TYPE_FIELD);
734 /* down case the attribute name */
735 for (dp = cp; istoken (*dp); dp++)
736 if (isalpha(*dp) && isupper (*dp))
739 for (up = dp; isspace (*dp);)
741 if (dp == cp || *dp != '=') {
743 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
744 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
748 vp = (*ap = add (cp, NULL)) + (up - cp);
750 for (dp++; isspace (*dp);)
753 /* now add the attribute value */
754 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
757 for (cp = ++dp, dp = vp;;) {
762 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
763 ct->c_file, TYPE_FIELD, i, i, "", *ap);
768 if ((c = *cp++) == '\0')
783 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
789 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
790 ct->c_file, TYPE_FIELD, i, i, "", *ap);
795 while (isspace (*cp))
798 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
803 * Check if anything is left over
806 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
807 ct->c_file, TYPE_FIELD, i, i, "", cp);
815 get_comment (CT ct, char **ap, int istype)
819 char c, buffer[BUFSIZ], *dp;
831 advise (NULL, "invalid comment in message %s's %s: field",
832 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
837 if ((c = *cp++) == '\0')
860 if ((dp = ci->ci_comment)) {
861 ci->ci_comment = concat (dp, " ", buffer, NULL);
864 ci->ci_comment = add (buffer, NULL);
868 while (isspace (*cp))
879 * Handles content types audio, image, and video.
880 * There's not much to do right here.
886 return OK; /* not much to do here */
899 char **ap, **ep, *cp;
902 CI ci = &ct->c_ctinfo;
904 /* check for missing subtype */
905 if (!*ci->ci_subtype)
906 ci->ci_subtype = add ("plain", ci->ci_subtype);
909 for (kv = SubText; kv->kv_key; kv++)
910 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
912 ct->c_subtype = kv->kv_value;
914 /* allocate text structure */
915 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
916 adios (NULL, "out of memory");
917 ct->c_ctparams = (void *) t;
919 /* scan for charset parameter */
920 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
921 if (!strcasecmp (*ap, "charset"))
927 chset = "US-ASCII"; /* default for text */
929 /* match character set, or set to unknown */
930 for (kv = Charset; kv->kv_key; kv++)
931 if (!strcasecmp (chset, kv->kv_key))
933 t->tx_charset = kv->kv_value;
936 * If we can not handle character set natively,
937 * then check profile for string to modify the
938 * terminal or display method.
940 if (!check_charset (chset, strlen (chset))) {
941 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
942 if ((cp = context_find (buffer)))
943 ct->c_termproc = getcpy (cp);
955 InitMultiPart (CT ct)
959 char *cp, *dp, **ap, **ep;
960 char *bp, buffer[BUFSIZ];
963 struct part *part, **next;
964 CI ci = &ct->c_ctinfo;
969 * The encoding for multipart messages must be either
970 * 7bit, 8bit, or binary (per RFC2045).
972 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
973 && ct->c_encoding != CE_BINARY) {
975 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
976 ci->ci_type, ci->ci_subtype, ct->c_file);
981 for (kv = SubMultiPart; kv->kv_key; kv++)
982 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
984 ct->c_subtype = kv->kv_value;
987 * Check for "boundary" parameter, which is
988 * required for multipart messages.
991 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
992 if (!strcasecmp (*ap, "boundary")) {
998 /* complain if boundary parameter is missing */
1001 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1002 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1006 /* allocate primary structure for multipart info */
1007 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1008 adios (NULL, "out of memory");
1009 ct->c_ctparams = (void *) m;
1011 /* check if boundary parameter contains only whitespace characters */
1012 for (cp = bp; isspace (*cp); cp++)
1015 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1016 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1020 /* remove trailing whitespace from boundary parameter */
1021 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1026 /* record boundary separators */
1027 m->mp_start = concat (bp, "\n", NULL);
1028 m->mp_stop = concat (bp, "--\n", NULL);
1030 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1031 advise (ct->c_file, "unable to open for reading");
1035 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1037 next = &m->mp_parts;
1041 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1045 pos += strlen (buffer);
1046 if (buffer[0] != '-' || buffer[1] != '-')
1049 if (strcmp (buffer + 2, m->mp_start))
1052 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1053 adios (NULL, "out of memory");
1055 next = &part->mp_next;
1057 if (!(p = get_content (fp, ct->c_file,
1058 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1066 fseek (fp, pos, SEEK_SET);
1069 if (strcmp (buffer + 2, m->mp_start) == 0) {
1073 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1074 if (p->c_end < p->c_begin)
1075 p->c_begin = p->c_end;
1080 if (strcmp (buffer + 2, m->mp_stop) == 0)
1086 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1087 if (!inout && part) {
1089 p->c_end = ct->c_end;
1091 if (p->c_begin >= p->c_end) {
1092 for (next = &m->mp_parts; *next != part;
1093 next = &((*next)->mp_next))
1097 free ((char *) part);
1102 /* reverse the order of the parts for multipart/alternative */
1103 if (ct->c_subtype == MULTI_ALTERNATE)
1107 * label all subparts with part number, and
1108 * then initialize the content of the subpart.
1113 char partnam[BUFSIZ];
1116 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1117 pp = partnam + strlen (partnam);
1122 for (part = m->mp_parts, partnum = 1; part;
1123 part = part->mp_next, partnum++) {
1126 sprintf (pp, "%d", partnum);
1127 p->c_partno = add (partnam, NULL);
1129 /* initialize the content of the subparts */
1130 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1145 * reverse the order of the parts of a multipart
1149 reverse_parts (CT ct)
1152 struct multipart *m;
1153 struct part **base, **bmp, **next, *part;
1155 m = (struct multipart *) ct->c_ctparams;
1157 /* if only one part, just return */
1158 if (!m->mp_parts || !m->mp_parts->mp_next)
1161 /* count number of parts */
1163 for (part = m->mp_parts; part; part = part->mp_next)
1166 /* allocate array of pointers to the parts */
1167 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1168 adios (NULL, "out of memory");
1171 /* point at all the parts */
1172 for (part = m->mp_parts; part; part = part->mp_next)
1176 /* reverse the order of the parts */
1177 next = &m->mp_parts;
1178 for (bmp--; bmp >= base; bmp--) {
1181 next = &part->mp_next;
1185 /* free array of pointers */
1186 free ((char *) base);
1198 CI ci = &ct->c_ctinfo;
1200 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1202 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1203 ci->ci_type, ci->ci_subtype, ct->c_file);
1207 /* check for missing subtype */
1208 if (!*ci->ci_subtype)
1209 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1212 for (kv = SubMessage; kv->kv_key; kv++)
1213 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1215 ct->c_subtype = kv->kv_value;
1217 switch (ct->c_subtype) {
1218 case MESSAGE_RFC822:
1221 case MESSAGE_PARTIAL:
1226 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1227 adios (NULL, "out of memory");
1228 ct->c_ctparams = (void *) p;
1230 /* scan for parameters "id", "number", and "total" */
1231 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1232 if (!strcasecmp (*ap, "id")) {
1233 p->pm_partid = add (*ep, NULL);
1236 if (!strcasecmp (*ap, "number")) {
1237 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1238 || p->pm_partno < 1) {
1241 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1242 *ap, ci->ci_type, ci->ci_subtype,
1243 ct->c_file, TYPE_FIELD);
1248 if (!strcasecmp (*ap, "total")) {
1249 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1258 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1260 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1261 ci->ci_type, ci->ci_subtype,
1262 ct->c_file, TYPE_FIELD);
1268 case MESSAGE_EXTERNAL:
1275 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1276 adios (NULL, "out of memory");
1277 ct->c_ctparams = (void *) e;
1280 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1281 advise (ct->c_file, "unable to open for reading");
1285 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1287 if (!(p = get_content (fp, ct->c_file, 0))) {
1296 if ((exresult = params_external (ct, 0)) != NOTOK
1297 && p->c_ceopenfnx == openMail) {
1301 if ((size = ct->c_end - p->c_begin) <= 0) {
1303 content_error (NULL, ct,
1304 "empty body for access-type=mail-server");
1308 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1309 adios (NULL, "out of memory");
1310 fseek (p->c_fp, p->c_begin, SEEK_SET);
1312 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1314 adios ("failed", "fread");
1317 adios (NULL, "unexpected EOF from fread");
1320 bp += cc, size -= cc;
1327 p->c_end = p->c_begin;
1332 if (exresult == NOTOK)
1334 if (e->eb_flags == NOTOK)
1337 switch (p->c_type) {
1342 if (p->c_subtype != MESSAGE_RFC822)
1346 e->eb_partno = ct->c_partno;
1348 (*p->c_ctinitfnx) (p);
1363 params_external (CT ct, int composing)
1366 struct exbody *e = (struct exbody *) ct->c_ctparams;
1367 CI ci = &ct->c_ctinfo;
1369 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1370 if (!strcasecmp (*ap, "access-type")) {
1371 struct str2init *s2i;
1372 CT p = e->eb_content;
1374 for (s2i = str2methods; s2i->si_key; s2i++)
1375 if (!strcasecmp (*ep, s2i->si_key))
1379 e->eb_flags = NOTOK;
1380 p->c_encoding = CE_EXTERNAL;
1383 e->eb_access = s2i->si_key;
1384 e->eb_flags = s2i->si_val;
1385 p->c_encoding = CE_EXTERNAL;
1387 /* Call the Init function for this external type */
1388 if ((*s2i->si_init)(p) == NOTOK)
1392 if (!strcasecmp (*ap, "name")) {
1396 if (!strcasecmp (*ap, "permission")) {
1397 e->eb_permission = *ep;
1400 if (!strcasecmp (*ap, "site")) {
1404 if (!strcasecmp (*ap, "directory")) {
1408 if (!strcasecmp (*ap, "mode")) {
1412 if (!strcasecmp (*ap, "size")) {
1413 sscanf (*ep, "%lu", &e->eb_size);
1416 if (!strcasecmp (*ap, "server")) {
1420 if (!strcasecmp (*ap, "subject")) {
1421 e->eb_subject = *ep;
1424 if (composing && !strcasecmp (*ap, "body")) {
1425 e->eb_body = getcpy (*ep);
1430 if (!e->eb_access) {
1432 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1433 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1446 InitApplication (CT ct)
1449 CI ci = &ct->c_ctinfo;
1452 for (kv = SubApplication; kv->kv_key; kv++)
1453 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1455 ct->c_subtype = kv->kv_value;
1462 * TRANSFER ENCODINGS
1466 init_encoding (CT ct, OpenCEFunc openfnx)
1470 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1471 adios (NULL, "out of memory");
1474 ct->c_ceopenfnx = openfnx;
1475 ct->c_ceclosefnx = close_encoding;
1476 ct->c_cesizefnx = size_encoding;
1483 close_encoding (CT ct)
1487 if (!(ce = ct->c_cefile))
1497 static unsigned long
1498 size_encoding (CT ct)
1506 if (!(ce = ct->c_cefile))
1507 return (ct->c_end - ct->c_begin);
1509 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1510 return (long) st.st_size;
1513 if (stat (ce->ce_file, &st) != NOTOK)
1514 return (long) st.st_size;
1519 if (ct->c_encoding == CE_EXTERNAL)
1520 return (ct->c_end - ct->c_begin);
1523 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1524 return (ct->c_end - ct->c_begin);
1526 if (fstat (fd, &st) != NOTOK)
1527 size = (long) st.st_size;
1531 (*ct->c_ceclosefnx) (ct);
1540 static unsigned char b642nib[0x80] = {
1541 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1544 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1545 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1546 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1547 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1548 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1549 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1550 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1551 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1552 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1553 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1554 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1555 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1556 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1563 return init_encoding (ct, openBase64);
1568 openBase64 (CT ct, char **file)
1570 int bitno, cc, digested;
1573 unsigned char value, *b, *b1, *b2, *b3;
1574 char *cp, *ep, buffer[BUFSIZ];
1575 /* sbeck -- handle prefixes */
1580 b = (unsigned char *) &bits;
1581 b1 = &b[endian > 0 ? 1 : 2];
1582 b2 = &b[endian > 0 ? 2 : 1];
1583 b3 = &b[endian > 0 ? 3 : 0];
1587 fseek (ce->ce_fp, 0L, SEEK_SET);
1592 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1593 content_error (ce->ce_file, ct, "unable to fopen for reading");
1599 if (*file == NULL) {
1600 ce->ce_file = add (m_scratch ("", tmp), NULL);
1603 ce->ce_file = add (*file, NULL);
1607 /* sbeck@cise.ufl.edu -- handle suffixes */
1609 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1610 invo_name, ci->ci_type, ci->ci_subtype);
1611 cp = context_find (buffer);
1612 if (cp == NULL || *cp == '\0') {
1613 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1615 cp = context_find (buffer);
1617 if (cp != NULL && *cp != '\0')
1618 ce->ce_file = add (cp, ce->ce_file);
1620 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1621 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1625 if ((len = ct->c_end - ct->c_begin) < 0)
1626 adios (NULL, "internal error(1)");
1628 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1629 content_error (ct->c_file, ct, "unable to open for reading");
1633 if ((digested = ct->c_digested))
1634 MD5Init (&mdContext);
1640 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1642 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1644 content_error (ct->c_file, ct, "error reading from");
1648 content_error (NULL, ct, "premature eof");
1656 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1661 if (skip || (*cp & 0x80)
1662 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1664 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1666 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1669 content_error (NULL, ct,
1670 "invalid BASE64 encoding -- continuing");
1674 bits |= value << bitno;
1676 if ((bitno -= 6) < 0) {
1677 putc ((char) *b1, ce->ce_fp);
1679 MD5Update (&mdContext, b1, 1);
1681 putc ((char) *b2, ce->ce_fp);
1683 MD5Update (&mdContext, b2, 1);
1685 putc ((char) *b3, ce->ce_fp);
1687 MD5Update (&mdContext, b3, 1);
1691 if (ferror (ce->ce_fp)) {
1692 content_error (ce->ce_file, ct,
1693 "error writing to");
1696 bitno = 18, bits = 0L, skip = 0;
1702 goto self_delimiting;
1711 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1713 content_error (NULL, ct, "invalid BASE64 encoding");
1718 fseek (ct->c_fp, 0L, SEEK_SET);
1720 if (fflush (ce->ce_fp)) {
1721 content_error (ce->ce_file, ct, "error writing to");
1726 unsigned char digest[16];
1728 MD5Final (digest, &mdContext);
1729 if (memcmp((char *) digest, (char *) ct->c_digest,
1730 sizeof(digest) / sizeof(digest[0])))
1731 content_error (NULL, ct,
1732 "content integrity suspect (digest mismatch) -- continuing");
1735 fprintf (stderr, "content integrity confirmed\n");
1738 fseek (ce->ce_fp, 0L, SEEK_SET);
1741 *file = ce->ce_file;
1742 return fileno (ce->ce_fp);
1745 free_encoding (ct, 0);
1754 static char hex2nib[0x80] = {
1755 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1756 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1757 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1758 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1759 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1760 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1761 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1762 0x08, 0x09, 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,
1767 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1768 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1777 return init_encoding (ct, openQuoted);
1782 openQuoted (CT ct, char **file)
1784 int cc, digested, len, quoted;
1786 char buffer[BUFSIZ];
1789 /* sbeck -- handle prefixes */
1795 fseek (ce->ce_fp, 0L, SEEK_SET);
1800 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1801 content_error (ce->ce_file, ct, "unable to fopen for reading");
1807 if (*file == NULL) {
1808 ce->ce_file = add (m_scratch ("", tmp), NULL);
1811 ce->ce_file = add (*file, NULL);
1815 /* sbeck@cise.ufl.edu -- handle suffixes */
1817 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1818 invo_name, ci->ci_type, ci->ci_subtype);
1819 cp = context_find (buffer);
1820 if (cp == NULL || *cp == '\0') {
1821 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1823 cp = context_find (buffer);
1825 if (cp != NULL && *cp != '\0')
1826 ce->ce_file = add (cp, ce->ce_file);
1828 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1829 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1833 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1834 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1838 if ((len = ct->c_end - ct->c_begin) < 0)
1839 adios (NULL, "internal error(2)");
1841 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1842 content_error (ct->c_file, ct, "unable to open for reading");
1846 if ((digested = ct->c_digested))
1847 MD5Init (&mdContext);
1854 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1858 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1859 content_error (NULL, ct, "premature eof");
1863 if ((cc = strlen (buffer)) > len)
1867 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1872 for (; cp < ep; cp++) {
1875 if (!isxdigit (*cp)) {
1877 dp = "expecting hexidecimal-digit";
1878 goto invalid_encoding;
1881 mask |= hex2nib[*cp & 0x7f];
1882 putc (mask, ce->ce_fp);
1884 MD5Update (&mdContext, &mask, 1);
1888 putc (*cp, ce->ce_fp);
1890 MD5Update (&mdContext, (unsigned char *) ":", 1);
1894 if (!isxdigit (*cp))
1896 mask = hex2nib[*cp & 0x7f];
1902 if (ferror (ce->ce_fp)) {
1903 content_error (ce->ce_file, ct, "error writing to");
1912 if (*cp < '!' || *cp > '~') {
1914 dp = "expecting character in range [!..~]";
1917 i = strlen (invo_name) + 2;
1918 content_error (NULL, ct,
1919 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1927 putc (*cp, ce->ce_fp);
1930 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1932 MD5Update (&mdContext, (unsigned char *) cp, 1);
1934 if (ferror (ce->ce_fp)) {
1935 content_error (ce->ce_file, ct, "error writing to");
1941 if (*++cp != '\n') {
1950 content_error (NULL, ct,
1951 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1955 fseek (ct->c_fp, 0L, SEEK_SET);
1957 if (fflush (ce->ce_fp)) {
1958 content_error (ce->ce_file, ct, "error writing to");
1963 unsigned char digest[16];
1965 MD5Final (digest, &mdContext);
1966 if (memcmp((char *) digest, (char *) ct->c_digest,
1967 sizeof(digest) / sizeof(digest[0])))
1968 content_error (NULL, ct,
1969 "content integrity suspect (digest mismatch) -- continuing");
1972 fprintf (stderr, "content integrity confirmed\n");
1975 fseek (ce->ce_fp, 0L, SEEK_SET);
1978 *file = ce->ce_file;
1979 return fileno (ce->ce_fp);
1982 free_encoding (ct, 0);
1994 if (init_encoding (ct, open7Bit) == NOTOK)
1997 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2003 open7Bit (CT ct, char **file)
2006 char buffer[BUFSIZ];
2007 /* sbeck -- handle prefixes */
2014 fseek (ce->ce_fp, 0L, SEEK_SET);
2019 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2020 content_error (ce->ce_file, ct, "unable to fopen for reading");
2026 if (*file == NULL) {
2027 ce->ce_file = add (m_scratch ("", tmp), NULL);
2030 ce->ce_file = add (*file, NULL);
2034 /* sbeck@cise.ufl.edu -- handle suffixes */
2036 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2037 invo_name, ci->ci_type, ci->ci_subtype);
2038 cp = context_find (buffer);
2039 if (cp == NULL || *cp == '\0') {
2040 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2042 cp = context_find (buffer);
2044 if (cp != NULL && *cp != '\0')
2045 ce->ce_file = add (cp, ce->ce_file);
2047 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2048 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2052 if (ct->c_type == CT_MULTIPART) {
2054 CI ci = &ct->c_ctinfo;
2057 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2058 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2059 + 1 + strlen (ci->ci_subtype);
2060 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2061 putc (';', ce->ce_fp);
2064 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2066 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2067 fputs ("\n\t", ce->ce_fp);
2070 putc (' ', ce->ce_fp);
2073 fprintf (ce->ce_fp, "%s", buffer);
2077 if (ci->ci_comment) {
2078 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2079 fputs ("\n\t", ce->ce_fp);
2083 putc (' ', ce->ce_fp);
2086 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2089 fprintf (ce->ce_fp, "\n");
2091 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2093 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2094 fprintf (ce->ce_fp, "\n");
2097 if ((len = ct->c_end - ct->c_begin) < 0)
2098 adios (NULL, "internal error(3)");
2100 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2101 content_error (ct->c_file, ct, "unable to open for reading");
2105 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2107 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2109 content_error (ct->c_file, ct, "error reading from");
2113 content_error (NULL, ct, "premature eof");
2121 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2122 if (ferror (ce->ce_fp)) {
2123 content_error (ce->ce_file, ct, "error writing to");
2128 fseek (ct->c_fp, 0L, SEEK_SET);
2130 if (fflush (ce->ce_fp)) {
2131 content_error (ce->ce_file, ct, "error writing to");
2135 fseek (ce->ce_fp, 0L, SEEK_SET);
2138 *file = ce->ce_file;
2139 return fileno (ce->ce_fp);
2142 free_encoding (ct, 0);
2152 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2154 char cachefile[BUFSIZ];
2157 fseek (ce->ce_fp, 0L, SEEK_SET);
2162 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2163 content_error (ce->ce_file, ct, "unable to fopen for reading");
2169 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2170 cachefile, sizeof(cachefile)) != NOTOK) {
2171 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2172 ce->ce_file = getcpy (cachefile);
2176 admonish (cachefile, "unable to fopen for reading");
2183 *file = ce->ce_file;
2184 *fd = fileno (ce->ce_fp);
2195 return init_encoding (ct, openFile);
2200 openFile (CT ct, char **file)
2203 char cachefile[BUFSIZ];
2204 struct exbody *e = ct->c_ctexbody;
2205 CE ce = ct->c_cefile;
2207 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2219 content_error (NULL, ct, "missing name parameter");
2223 ce->ce_file = getcpy (e->eb_name);
2226 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2227 content_error (ce->ce_file, ct, "unable to fopen for reading");
2231 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2232 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2233 cachefile, sizeof(cachefile)) != NOTOK) {
2237 mask = umask (cachetype ? ~m_gmprot () : 0222);
2238 if ((fp = fopen (cachefile, "w"))) {
2240 char buffer[BUFSIZ];
2241 FILE *gp = ce->ce_fp;
2243 fseek (gp, 0L, SEEK_SET);
2245 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2247 fwrite (buffer, sizeof(*buffer), cc, fp);
2251 admonish (ce->ce_file, "error reading");
2256 admonish (cachefile, "error writing");
2264 fseek (ce->ce_fp, 0L, SEEK_SET);
2265 *file = ce->ce_file;
2266 return fileno (ce->ce_fp);
2276 return init_encoding (ct, openFTP);
2281 openFTP (CT ct, char **file)
2283 int cachetype, caching, fd;
2285 char *bp, *ftp, *user, *pass;
2286 char buffer[BUFSIZ], cachefile[BUFSIZ];
2289 static char *username = NULL;
2290 static char *password = NULL;
2295 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2303 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2314 if (!e->eb_name || !e->eb_site) {
2315 content_error (NULL, ct, "missing %s parameter",
2316 e->eb_name ? "site": "name");
2323 pidcheck (pidwait (xpid, NOTOK));
2327 /* Get the buffer ready to go */
2329 buflen = sizeof(buffer);
2332 * Construct the query message for user
2334 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2340 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2346 snprintf (bp, buflen, "\n using %sFTP from site %s",
2347 e->eb_flags ? "anonymous " : "", e->eb_site);
2352 if (e->eb_size > 0) {
2353 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2358 snprintf (bp, buflen, "? ");
2361 * Now, check the answer
2363 if (!getanswer (buffer))
2368 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2371 ruserpass (e->eb_site, &username, &password);
2376 ce->ce_unlink = (*file == NULL);
2378 cachefile[0] = '\0';
2379 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2380 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2381 cachefile, sizeof(cachefile)) != NOTOK) {
2382 if (*file == NULL) {
2389 ce->ce_file = add (*file, NULL);
2391 ce->ce_file = add (cachefile, NULL);
2393 ce->ce_file = add (m_scratch ("", tmp), NULL);
2395 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2396 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2404 int child_id, i, vecp;
2408 vec[vecp++] = r1bindex (ftp, '/');
2409 vec[vecp++] = e->eb_site;
2412 vec[vecp++] = e->eb_dir;
2413 vec[vecp++] = e->eb_name;
2414 vec[vecp++] = ce->ce_file,
2415 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2416 ? "ascii" : "binary";
2421 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2425 adios ("fork", "unable to");
2429 close (fileno (ce->ce_fp));
2431 fprintf (stderr, "unable to exec ");
2437 if (pidXwait (child_id, NULL)) {
2441 username = password = NULL;
2450 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2452 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2459 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2464 mask = umask (cachetype ? ~m_gmprot () : 0222);
2465 if ((fp = fopen (cachefile, "w"))) {
2467 FILE *gp = ce->ce_fp;
2469 fseek (gp, 0L, SEEK_SET);
2471 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2473 fwrite (buffer, sizeof(*buffer), cc, fp);
2477 admonish (ce->ce_file, "error reading");
2482 admonish (cachefile, "error writing");
2491 fseek (ce->ce_fp, 0L, SEEK_SET);
2492 *file = ce->ce_file;
2493 return fileno (ce->ce_fp);
2504 return init_encoding (ct, openMail);
2509 openMail (CT ct, char **file)
2511 int child_id, fd, i, vecp;
2513 char *bp, buffer[BUFSIZ], *vec[7];
2514 struct exbody *e = ct->c_ctexbody;
2515 CE ce = ct->c_cefile;
2517 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2528 if (!e->eb_server) {
2529 content_error (NULL, ct, "missing server parameter");
2536 pidcheck (pidwait (xpid, NOTOK));
2540 /* Get buffer ready to go */
2542 buflen = sizeof(buffer);
2544 /* Now, construct query message */
2545 snprintf (bp, buflen, "Retrieve content");
2551 snprintf (bp, buflen, " %s", e->eb_partno);
2557 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2559 e->eb_subject ? e->eb_subject : e->eb_body);
2561 /* Now, check answer */
2562 if (!getanswer (buffer))
2566 vec[vecp++] = r1bindex (mailproc, '/');
2567 vec[vecp++] = e->eb_server;
2568 vec[vecp++] = "-subject";
2569 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2570 vec[vecp++] = "-body";
2571 vec[vecp++] = e->eb_body;
2574 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2578 advise ("fork", "unable to");
2582 execvp (mailproc, vec);
2583 fprintf (stderr, "unable to exec ");
2589 if (pidXwait (child_id, NULL) == OK)
2590 advise (NULL, "request sent");
2594 if (*file == NULL) {
2595 ce->ce_file = add (m_scratch ("", tmp), NULL);
2598 ce->ce_file = add (*file, NULL);
2602 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2603 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2608 free (ct->c_showproc);
2609 ct->c_showproc = add ("true", NULL);
2611 fseek (ce->ce_fp, 0L, SEEK_SET);
2612 *file = ce->ce_file;
2613 return fileno (ce->ce_fp);
2618 readDigest (CT ct, char *cp)
2623 unsigned char *dp, value, *ep;
2624 unsigned char *b, *b1, *b2, *b3;
2626 b = (unsigned char *) &bits,
2627 b1 = &b[endian > 0 ? 1 : 2],
2628 b2 = &b[endian > 0 ? 2 : 1],
2629 b3 = &b[endian > 0 ? 3 : 0];
2634 for (ep = (dp = ct->c_digest)
2635 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2640 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2642 fprintf (stderr, "invalid BASE64 encoding\n");
2646 bits |= value << bitno;
2648 if ((bitno -= 6) < 0) {
2649 if (dp + (3 - skip) > ep)
2650 goto invalid_digest;
2665 goto self_delimiting;
2670 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2680 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2688 fprintf (stderr, "MD5 digest=");
2689 for (dp = ct->c_digest; dp < ep; dp++)
2690 fprintf (stderr, "%02x", *dp & 0xff);
2691 fprintf (stderr, "\n");