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>
32 extern int endian; /* mhmisc.c */
34 extern pid_t xpid; /* mhshowsbr.c */
37 extern int rcachesw; /* mhcachesbr.c */
38 extern int wcachesw; /* mhcachesbr.c */
40 int checksw = 0; /* check Content-MD5 field */
43 * Directory to place temp files. This must
44 * be set before these routines are called.
49 * Structure for mapping types to their internal flags
57 * Structures for TEXT messages
59 static struct k2v SubText[] = {
60 { "plain", TEXT_PLAIN },
61 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
62 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
63 { NULL, TEXT_UNKNOWN } /* this one must be last! */
66 static struct k2v Charset[] = {
67 { "us-ascii", CHARSET_USASCII },
68 { "iso-8859-1", CHARSET_LATIN },
69 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
73 * Structures for MULTIPART messages
75 static struct k2v SubMultiPart[] = {
76 { "mixed", MULTI_MIXED },
77 { "alternative", MULTI_ALTERNATE },
78 { "digest", MULTI_DIGEST },
79 { "parallel", MULTI_PARALLEL },
80 { NULL, MULTI_UNKNOWN } /* this one must be last! */
84 * Structures for MESSAGE messages
86 static struct k2v SubMessage[] = {
87 { "rfc822", MESSAGE_RFC822 },
88 { "partial", MESSAGE_PARTIAL },
89 { "external-body", MESSAGE_EXTERNAL },
90 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
94 * Structure for APPLICATION messages
96 static struct k2v SubApplication[] = {
97 { "octet-stream", APPLICATION_OCTETS },
98 { "postscript", APPLICATION_POSTSCRIPT },
99 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
104 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
107 int find_cache (CT, int, int *, char *, char *, int);
110 int part_ok (CT, int);
111 int type_ok (CT, int);
112 int make_intermediates (char *);
113 void content_error (char *, CT, char *, ...);
116 void free_content (CT);
117 void free_encoding (CT, int);
123 CT parse_mime (char *);
128 static CT get_content (FILE *, char *, int);
129 static int add_header (CT, char *, char *);
130 static int get_ctinfo (char *, CT);
131 static int get_comment (CT, char **, int);
132 static int InitGeneric (CT);
133 static int InitText (CT);
134 static int InitMultiPart (CT);
135 static void reverse_parts (CT);
136 static int InitMessage (CT);
137 static int params_external (CT, int);
138 static int InitApplication (CT);
139 static int init_encoding (CT, OpenCEFunc);
140 static void close_encoding (CT);
141 static unsigned long size_encoding (CT);
142 static int InitBase64 (CT);
143 static int openBase64 (CT, char **);
144 static int InitQuoted (CT);
145 static int openQuoted (CT, char **);
146 static int Init7Bit (CT);
147 static int open7Bit (CT, char **);
148 static int openExternal (CT, CT, CE, char **, int *);
149 static int InitFile (CT);
150 static int openFile (CT, char **);
151 static int InitFTP (CT);
152 static int openFTP (CT, char **);
153 static int InitMail (CT);
154 static int openMail (CT, char **);
155 static int readDigest (CT, char *);
158 * Structures for mapping (content) types to
159 * the functions to handle them.
167 static struct str2init str2cts[] = {
168 { "application", CT_APPLICATION, InitApplication },
169 { "audio", CT_AUDIO, InitGeneric },
170 { "image", CT_IMAGE, InitGeneric },
171 { "message", CT_MESSAGE, InitMessage },
172 { "multipart", CT_MULTIPART, InitMultiPart },
173 { "text", CT_TEXT, InitText },
174 { "video", CT_VIDEO, InitGeneric },
175 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
176 { NULL, CT_UNKNOWN, NULL },
179 static struct str2init str2ces[] = {
180 { "base64", CE_BASE64, InitBase64 },
181 { "quoted-printable", CE_QUOTED, InitQuoted },
182 { "8bit", CE_8BIT, Init7Bit },
183 { "7bit", CE_7BIT, Init7Bit },
184 { "binary", CE_BINARY, Init7Bit },
185 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
186 { NULL, CE_UNKNOWN, NULL },
190 * NOTE WELL: si_key MUST NOT have value of NOTOK
192 * si_key is 1 if access method is anonymous.
194 static struct str2init str2methods[] = {
195 { "afs", 1, InitFile },
196 { "anon-ftp", 1, InitFTP },
197 { "ftp", 0, InitFTP },
198 { "local-file", 0, InitFile },
199 { "mail-server", 0, InitMail },
205 pidcheck (int status)
207 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
217 * Main entry point for parsing a MIME message or file.
218 * It returns the Content structure for the top level
219 * entity in the file.
223 parse_mime (char *file)
231 * Check if file is actually standard input
233 if ((is_stdin = !(strcmp (file, "-")))) {
234 file = add (m_tmpfil (invo_name), NULL);
235 if ((fp = fopen (file, "w+")) == NULL) {
236 advise (file, "unable to fopen for writing and reading");
240 while (fgets (buffer, sizeof(buffer), stdin))
244 if (ferror (stdin)) {
246 advise ("stdin", "error reading");
251 advise (file, "error writing");
254 fseek (fp, 0L, SEEK_SET);
255 } else if ((fp = fopen (file, "r")) == NULL) {
256 advise (file, "unable to read");
260 if (!(ct = get_content (fp, file, 1))) {
264 advise (NULL, "unable to decode %s", file);
269 ct->c_unlink = 1; /* temp file to remove */
273 if (ct->c_end == 0L) {
274 fseek (fp, 0L, SEEK_END);
275 ct->c_end = ftell (fp);
278 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
290 * Main routine for reading/parsing the headers
291 * of a message content.
293 * toplevel = 1 # we are at the top level of the message
294 * toplevel = 0 # we are inside message type or multipart type
295 * # other than multipart/digest
296 * toplevel = -1 # we are inside multipart/digest
300 get_content (FILE *in, char *file, int toplevel)
303 char buf[BUFSIZ], name[NAMESZ];
308 /* allocate the content structure */
309 if (!(ct = (CT) calloc (1, sizeof(*ct))))
310 adios (NULL, "out of memory");
313 ct->c_file = add (file, NULL);
314 ct->c_begin = ftell (ct->c_fp) + 1;
317 * Parse the header fields for this
318 * content into a linked list.
320 for (compnum = 1, state = FLD;;) {
321 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
327 /* get copies of the buffers */
328 np = add (name, NULL);
329 vp = add (buf, NULL);
331 /* if necessary, get rest of field */
332 while (state == FLDPLUS) {
333 state = m_getfld (state, name, buf, sizeof(buf), in);
334 vp = add (buf, vp); /* add to previous value */
337 /* Now add the header data to the list */
338 add_header (ct, np, vp);
340 /* continue, if this isn't the last header field */
341 if (state != FLDEOF) {
342 ct->c_begin = ftell (in) + 1;
349 ct->c_begin = ftell (in) - strlen (buf);
353 ct->c_begin = ftell (in);
358 adios (NULL, "message format error in component #%d", compnum);
361 adios (NULL, "getfld() returned %d", state);
364 /* break out of the loop */
369 * Read the content headers. We will parse the
370 * MIME related header fields into their various
371 * structures and set internal flags related to
372 * content type/subtype, etc.
375 hp = ct->c_first_hf; /* start at first header field */
377 /* Get MIME-Version field */
378 if (!strcasecmp (hp->name, VRSN_FIELD)) {
383 advise (NULL, "message %s has multiple %s: fields",
384 ct->c_file, VRSN_FIELD);
387 ct->c_vrsn = add (hp->value, NULL);
389 /* Now, cleanup this field */
392 while (isspace (*cp))
394 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
396 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
401 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
403 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
406 for (dp = cp; istoken (*dp); dp++)
410 ucmp = !strcasecmp (cp, VRSN_VALUE);
413 admonish (NULL, "message %s has unknown value for %s: field (%s)",
414 ct->c_file, VRSN_FIELD, cp);
417 else if (!strcasecmp (hp->name, TYPE_FIELD)) {
418 /* Get Content-Type field */
419 struct str2init *s2i;
420 CI ci = &ct->c_ctinfo;
422 /* Check if we've already seen a Content-Type header */
424 advise (NULL, "message %s has multiple %s: fields",
425 ct->c_file, TYPE_FIELD);
429 /* Parse the Content-Type field */
430 if (get_ctinfo (hp->value, ct) == NOTOK)
434 * Set the Init function and the internal
435 * flag for this content type.
437 for (s2i = str2cts; s2i->si_key; s2i++)
438 if (!strcasecmp (ci->ci_type, s2i->si_key))
440 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
442 ct->c_type = s2i->si_val;
443 ct->c_ctinitfnx = s2i->si_init;
445 else if (!strcasecmp (hp->name, ENCODING_FIELD)) {
446 /* Get Content-Transfer-Encoding field */
448 struct str2init *s2i;
451 * Check if we've already seen the
452 * Content-Transfer-Encoding field
455 advise (NULL, "message %s has multiple %s: fields",
456 ct->c_file, ENCODING_FIELD);
460 /* get copy of this field */
461 ct->c_celine = cp = add (hp->value, NULL);
463 while (isspace (*cp))
465 for (dp = cp; istoken (*dp); dp++)
471 * Find the internal flag and Init function
472 * for this transfer encoding.
474 for (s2i = str2ces; s2i->si_key; s2i++)
475 if (!strcasecmp (cp, s2i->si_key))
477 if (!s2i->si_key && !uprf (cp, "X-"))
480 ct->c_encoding = s2i->si_val;
482 /* Call the Init function for this encoding */
483 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
486 else if (!strcasecmp (hp->name, MD5_FIELD)) {
487 /* Get Content-MD5 field */
493 if (ct->c_digested) {
494 advise (NULL, "message %s has multiple %s: fields",
495 ct->c_file, MD5_FIELD);
499 ep = cp = add (hp->value, NULL); /* get a copy */
501 while (isspace (*cp))
503 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
505 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
510 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
512 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
517 for (dp = cp; *dp && !isspace (*dp); dp++)
525 else if (!strcasecmp (hp->name, ID_FIELD)) {
526 /* Get Content-ID field */
527 ct->c_id = add (hp->value, ct->c_id);
529 else if (!strcasecmp (hp->name, DESCR_FIELD)) {
530 /* Get Content-Description field */
531 ct->c_descr = add (hp->value, ct->c_descr);
535 hp = hp->next; /* next header field */
539 * Check if we saw a Content-Type field.
540 * If not, then assign a default value for
541 * it, and the Init function.
545 * If we are inside a multipart/digest message,
546 * so default type is message/rfc822
549 if (get_ctinfo ("message/rfc822", ct) == NOTOK)
551 ct->c_type = CT_MESSAGE;
552 ct->c_ctinitfnx = InitMessage;
555 * Else default type is text/plain
557 if (get_ctinfo ("text/plain", ct) == NOTOK)
559 ct->c_type = CT_TEXT;
560 ct->c_ctinitfnx = InitText;
564 /* Use default Transfer-Encoding, if necessary */
566 ct->c_encoding = CE_7BIT;
579 * small routine to add header field to list
583 add_header (CT ct, char *name, char *value)
587 /* allocate header field structure */
588 if (!(hp = malloc (sizeof(*hp))))
589 adios (NULL, "out of memory");
591 /* link data into header structure */
596 /* link header structure into the list */
597 if (ct->c_first_hf == NULL) {
598 ct->c_first_hf = hp; /* this is the first */
601 ct->c_last_hf->next = hp; /* add it to the end */
610 * Parse Content-Type line and fill in the
611 * information of the CTinfo structure.
615 get_ctinfo (char *cp, CT ct)
618 char *dp, **ap, **ep;
623 i = strlen (invo_name) + 2;
625 /* store copy of Content-Type line */
626 cp = ct->c_ctline = add (cp, NULL);
628 while (isspace (*cp)) /* trim leading spaces */
631 /* change newlines to spaces */
632 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
635 /* trim trailing spaces */
636 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
642 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
644 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
647 for (dp = cp; istoken (*dp); dp++)
650 ci->ci_type = add (cp, NULL); /* store content type */
654 advise (NULL, "invalid %s: field in message %s (empty type)",
655 TYPE_FIELD, ct->c_file);
659 /* down case the content type string */
660 for (dp = ci->ci_type; *dp; dp++)
661 if (isalpha(*dp) && isupper (*dp))
664 while (isspace (*cp))
667 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
671 ci->ci_subtype = add ("", NULL);
676 while (isspace (*cp))
679 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
682 for (dp = cp; istoken (*dp); dp++)
685 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
688 if (!*ci->ci_subtype) {
690 "invalid %s: field in message %s (empty subtype for \"%s\")",
691 TYPE_FIELD, ct->c_file, ci->ci_type);
695 /* down case the content subtype string */
696 for (dp = ci->ci_subtype; *dp; dp++)
697 if (isalpha(*dp) && isupper (*dp))
701 while (isspace (*cp))
704 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
708 * Parse attribute/value pairs given with Content-Type
710 ep = (ap = ci->ci_attrs) + NPARMS;
716 "too many parameters in message %s's %s: field (%d max)",
717 ct->c_file, TYPE_FIELD, NPARMS);
722 while (isspace (*cp))
725 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
730 "extraneous trailing ';' in message %s's %s: parameter list",
731 ct->c_file, TYPE_FIELD);
735 /* down case the attribute name */
736 for (dp = cp; istoken (*dp); dp++)
737 if (isalpha(*dp) && isupper (*dp))
740 for (up = dp; isspace (*dp);)
742 if (dp == cp || *dp != '=') {
744 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
745 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
749 vp = (*ap = add (cp, NULL)) + (up - cp);
751 for (dp++; isspace (*dp);)
754 /* now add the attribute value */
755 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
758 for (cp = ++dp, dp = vp;;) {
763 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
764 ct->c_file, TYPE_FIELD, i, i, "", *ap);
769 if ((c = *cp++) == '\0')
784 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
790 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
791 ct->c_file, TYPE_FIELD, i, i, "", *ap);
796 while (isspace (*cp))
799 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
804 * Check if anything is left over
807 advise (NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)",
808 ct->c_file, TYPE_FIELD, i, i, "", cp);
816 get_comment (CT ct, char **ap, int istype)
820 char c, buffer[BUFSIZ], *dp;
832 advise (NULL, "invalid comment in message %s's %s: field",
833 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
838 if ((c = *cp++) == '\0')
861 if ((dp = ci->ci_comment)) {
862 ci->ci_comment = concat (dp, " ", buffer, NULL);
865 ci->ci_comment = add (buffer, NULL);
869 while (isspace (*cp))
880 * Handles content types audio, image, and video.
881 * There's not much to do right here.
887 return OK; /* not much to do here */
900 char **ap, **ep, *cp;
903 CI ci = &ct->c_ctinfo;
905 /* check for missing subtype */
906 if (!*ci->ci_subtype)
907 ci->ci_subtype = add ("plain", ci->ci_subtype);
910 for (kv = SubText; kv->kv_key; kv++)
911 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
913 ct->c_subtype = kv->kv_value;
915 /* allocate text structure */
916 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
917 adios (NULL, "out of memory");
918 ct->c_ctparams = (void *) t;
920 /* scan for charset parameter */
921 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
922 if (!strcasecmp (*ap, "charset"))
928 chset = "US-ASCII"; /* default for text */
930 /* match character set, or set to unknown */
931 for (kv = Charset; kv->kv_key; kv++)
932 if (!strcasecmp (chset, kv->kv_key))
934 t->tx_charset = kv->kv_value;
937 * If we can not handle character set natively,
938 * then check profile for string to modify the
939 * terminal or display method.
941 if (!check_charset (chset, strlen (chset))) {
942 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
943 if ((cp = context_find (buffer)))
944 ct->c_termproc = getcpy (cp);
956 InitMultiPart (CT ct)
960 char *cp, *dp, **ap, **ep;
961 char *bp, buffer[BUFSIZ];
964 struct part *part, **next;
965 CI ci = &ct->c_ctinfo;
970 * The encoding for multipart messages must be either
971 * 7bit, 8bit, or binary (per RFC2045).
973 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
974 && ct->c_encoding != CE_BINARY) {
976 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
977 ci->ci_type, ci->ci_subtype, ct->c_file);
982 for (kv = SubMultiPart; kv->kv_key; kv++)
983 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
985 ct->c_subtype = kv->kv_value;
988 * Check for "boundary" parameter, which is
989 * required for multipart messages.
992 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
993 if (!strcasecmp (*ap, "boundary")) {
999 /* complain if boundary parameter is missing */
1002 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1003 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1007 /* allocate primary structure for multipart info */
1008 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1009 adios (NULL, "out of memory");
1010 ct->c_ctparams = (void *) m;
1012 /* check if boundary parameter contains only whitespace characters */
1013 for (cp = bp; isspace (*cp); cp++)
1016 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1017 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1021 /* remove trailing whitespace from boundary parameter */
1022 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1027 /* record boundary separators */
1028 m->mp_start = concat (bp, "\n", NULL);
1029 m->mp_stop = concat (bp, "--\n", NULL);
1031 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1032 advise (ct->c_file, "unable to open for reading");
1036 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1038 next = &m->mp_parts;
1042 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1046 pos += strlen (buffer);
1047 if (buffer[0] != '-' || buffer[1] != '-')
1050 if (strcmp (buffer + 2, m->mp_start))
1053 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1054 adios (NULL, "out of memory");
1056 next = &part->mp_next;
1058 if (!(p = get_content (fp, ct->c_file,
1059 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1067 fseek (fp, pos, SEEK_SET);
1070 if (strcmp (buffer + 2, m->mp_start) == 0) {
1074 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1075 if (p->c_end < p->c_begin)
1076 p->c_begin = p->c_end;
1081 if (strcmp (buffer + 2, m->mp_stop) == 0)
1087 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1088 if (!inout && part) {
1090 p->c_end = ct->c_end;
1092 if (p->c_begin >= p->c_end) {
1093 for (next = &m->mp_parts; *next != part;
1094 next = &((*next)->mp_next))
1098 free ((char *) part);
1103 /* reverse the order of the parts for multipart/alternative */
1104 if (ct->c_subtype == MULTI_ALTERNATE)
1108 * label all subparts with part number, and
1109 * then initialize the content of the subpart.
1114 char partnam[BUFSIZ];
1117 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1118 pp = partnam + strlen (partnam);
1123 for (part = m->mp_parts, partnum = 1; part;
1124 part = part->mp_next, partnum++) {
1127 sprintf (pp, "%d", partnum);
1128 p->c_partno = add (partnam, NULL);
1130 /* initialize the content of the subparts */
1131 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1146 * reverse the order of the parts of a multipart
1150 reverse_parts (CT ct)
1153 struct multipart *m;
1154 struct part **base, **bmp, **next, *part;
1156 m = (struct multipart *) ct->c_ctparams;
1158 /* if only one part, just return */
1159 if (!m->mp_parts || !m->mp_parts->mp_next)
1162 /* count number of parts */
1164 for (part = m->mp_parts; part; part = part->mp_next)
1167 /* allocate array of pointers to the parts */
1168 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1169 adios (NULL, "out of memory");
1172 /* point at all the parts */
1173 for (part = m->mp_parts; part; part = part->mp_next)
1177 /* reverse the order of the parts */
1178 next = &m->mp_parts;
1179 for (bmp--; bmp >= base; bmp--) {
1182 next = &part->mp_next;
1186 /* free array of pointers */
1187 free ((char *) base);
1199 CI ci = &ct->c_ctinfo;
1201 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1203 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1204 ci->ci_type, ci->ci_subtype, ct->c_file);
1208 /* check for missing subtype */
1209 if (!*ci->ci_subtype)
1210 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1213 for (kv = SubMessage; kv->kv_key; kv++)
1214 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1216 ct->c_subtype = kv->kv_value;
1218 switch (ct->c_subtype) {
1219 case MESSAGE_RFC822:
1222 case MESSAGE_PARTIAL:
1227 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1228 adios (NULL, "out of memory");
1229 ct->c_ctparams = (void *) p;
1231 /* scan for parameters "id", "number", and "total" */
1232 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1233 if (!strcasecmp (*ap, "id")) {
1234 p->pm_partid = add (*ep, NULL);
1237 if (!strcasecmp (*ap, "number")) {
1238 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1239 || p->pm_partno < 1) {
1242 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1243 *ap, ci->ci_type, ci->ci_subtype,
1244 ct->c_file, TYPE_FIELD);
1249 if (!strcasecmp (*ap, "total")) {
1250 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1259 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1261 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1262 ci->ci_type, ci->ci_subtype,
1263 ct->c_file, TYPE_FIELD);
1269 case MESSAGE_EXTERNAL:
1276 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1277 adios (NULL, "out of memory");
1278 ct->c_ctparams = (void *) e;
1281 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1282 advise (ct->c_file, "unable to open for reading");
1286 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1288 if (!(p = get_content (fp, ct->c_file, 0))) {
1297 if ((exresult = params_external (ct, 0)) != NOTOK
1298 && p->c_ceopenfnx == openMail) {
1302 if ((size = ct->c_end - p->c_begin) <= 0) {
1304 content_error (NULL, ct,
1305 "empty body for access-type=mail-server");
1309 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1310 adios (NULL, "out of memory");
1311 fseek (p->c_fp, p->c_begin, SEEK_SET);
1313 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1315 adios ("failed", "fread");
1318 adios (NULL, "unexpected EOF from fread");
1321 bp += cc, size -= cc;
1328 p->c_end = p->c_begin;
1333 if (exresult == NOTOK)
1335 if (e->eb_flags == NOTOK)
1338 switch (p->c_type) {
1343 if (p->c_subtype != MESSAGE_RFC822)
1347 e->eb_partno = ct->c_partno;
1349 (*p->c_ctinitfnx) (p);
1364 params_external (CT ct, int composing)
1367 struct exbody *e = (struct exbody *) ct->c_ctparams;
1368 CI ci = &ct->c_ctinfo;
1370 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1371 if (!strcasecmp (*ap, "access-type")) {
1372 struct str2init *s2i;
1373 CT p = e->eb_content;
1375 for (s2i = str2methods; s2i->si_key; s2i++)
1376 if (!strcasecmp (*ep, s2i->si_key))
1380 e->eb_flags = NOTOK;
1381 p->c_encoding = CE_EXTERNAL;
1384 e->eb_access = s2i->si_key;
1385 e->eb_flags = s2i->si_val;
1386 p->c_encoding = CE_EXTERNAL;
1388 /* Call the Init function for this external type */
1389 if ((*s2i->si_init)(p) == NOTOK)
1393 if (!strcasecmp (*ap, "name")) {
1397 if (!strcasecmp (*ap, "permission")) {
1398 e->eb_permission = *ep;
1401 if (!strcasecmp (*ap, "site")) {
1405 if (!strcasecmp (*ap, "directory")) {
1409 if (!strcasecmp (*ap, "mode")) {
1413 if (!strcasecmp (*ap, "size")) {
1414 sscanf (*ep, "%lu", &e->eb_size);
1417 if (!strcasecmp (*ap, "server")) {
1421 if (!strcasecmp (*ap, "subject")) {
1422 e->eb_subject = *ep;
1425 if (composing && !strcasecmp (*ap, "body")) {
1426 e->eb_body = getcpy (*ep);
1431 if (!e->eb_access) {
1433 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1434 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1447 InitApplication (CT ct)
1450 CI ci = &ct->c_ctinfo;
1453 for (kv = SubApplication; kv->kv_key; kv++)
1454 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1456 ct->c_subtype = kv->kv_value;
1463 * TRANSFER ENCODINGS
1467 init_encoding (CT ct, OpenCEFunc openfnx)
1471 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1472 adios (NULL, "out of memory");
1475 ct->c_ceopenfnx = openfnx;
1476 ct->c_ceclosefnx = close_encoding;
1477 ct->c_cesizefnx = size_encoding;
1484 close_encoding (CT ct)
1488 if (!(ce = ct->c_cefile))
1498 static unsigned long
1499 size_encoding (CT ct)
1507 if (!(ce = ct->c_cefile))
1508 return (ct->c_end - ct->c_begin);
1510 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1511 return (long) st.st_size;
1514 if (stat (ce->ce_file, &st) != NOTOK)
1515 return (long) st.st_size;
1520 if (ct->c_encoding == CE_EXTERNAL)
1521 return (ct->c_end - ct->c_begin);
1524 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1525 return (ct->c_end - ct->c_begin);
1527 if (fstat (fd, &st) != NOTOK)
1528 size = (long) st.st_size;
1532 (*ct->c_ceclosefnx) (ct);
1541 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1547 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1548 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1549 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1550 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1551 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1552 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1553 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1554 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1555 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1556 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1557 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1564 return init_encoding (ct, openBase64);
1569 openBase64 (CT ct, char **file)
1571 int bitno, cc, digested;
1574 unsigned char value, *b, *b1, *b2, *b3;
1575 char *cp, *ep, buffer[BUFSIZ];
1576 /* sbeck -- handle prefixes */
1581 b = (unsigned char *) &bits;
1582 b1 = &b[endian > 0 ? 1 : 2];
1583 b2 = &b[endian > 0 ? 2 : 1];
1584 b3 = &b[endian > 0 ? 3 : 0];
1588 fseek (ce->ce_fp, 0L, SEEK_SET);
1593 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1594 content_error (ce->ce_file, ct, "unable to fopen for reading");
1600 if (*file == NULL) {
1601 ce->ce_file = add (m_scratch ("", tmp), NULL);
1604 ce->ce_file = add (*file, NULL);
1608 /* sbeck@cise.ufl.edu -- handle suffixes */
1610 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1611 invo_name, ci->ci_type, ci->ci_subtype);
1612 cp = context_find (buffer);
1613 if (cp == NULL || *cp == '\0') {
1614 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1616 cp = context_find (buffer);
1618 if (cp != NULL && *cp != '\0')
1619 ce->ce_file = add (cp, ce->ce_file);
1621 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1622 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1626 if ((len = ct->c_end - ct->c_begin) < 0)
1627 adios (NULL, "internal error(1)");
1629 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1630 content_error (ct->c_file, ct, "unable to open for reading");
1634 if ((digested = ct->c_digested))
1635 MD5Init (&mdContext);
1641 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1643 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1645 content_error (ct->c_file, ct, "error reading from");
1649 content_error (NULL, ct, "premature eof");
1657 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1662 if (skip || (*cp & 0x80)
1663 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1665 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1667 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1670 content_error (NULL, ct,
1671 "invalid BASE64 encoding -- continuing");
1675 bits |= value << bitno;
1677 if ((bitno -= 6) < 0) {
1678 putc ((char) *b1, ce->ce_fp);
1680 MD5Update (&mdContext, b1, 1);
1682 putc ((char) *b2, ce->ce_fp);
1684 MD5Update (&mdContext, b2, 1);
1686 putc ((char) *b3, ce->ce_fp);
1688 MD5Update (&mdContext, b3, 1);
1692 if (ferror (ce->ce_fp)) {
1693 content_error (ce->ce_file, ct,
1694 "error writing to");
1697 bitno = 18, bits = 0L, skip = 0;
1703 goto self_delimiting;
1712 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1714 content_error (NULL, ct, "invalid BASE64 encoding");
1719 fseek (ct->c_fp, 0L, SEEK_SET);
1721 if (fflush (ce->ce_fp)) {
1722 content_error (ce->ce_file, ct, "error writing to");
1727 unsigned char digest[16];
1729 MD5Final (digest, &mdContext);
1730 if (memcmp((char *) digest, (char *) ct->c_digest,
1731 sizeof(digest) / sizeof(digest[0])))
1732 content_error (NULL, ct,
1733 "content integrity suspect (digest mismatch) -- continuing");
1736 fprintf (stderr, "content integrity confirmed\n");
1739 fseek (ce->ce_fp, 0L, SEEK_SET);
1742 *file = ce->ce_file;
1743 return fileno (ce->ce_fp);
1746 free_encoding (ct, 0);
1755 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1762 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1763 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1764 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1765 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1767 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1768 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1769 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1770 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1771 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1778 return init_encoding (ct, openQuoted);
1783 openQuoted (CT ct, char **file)
1785 int cc, digested, len, quoted;
1787 char buffer[BUFSIZ];
1790 /* sbeck -- handle prefixes */
1796 fseek (ce->ce_fp, 0L, SEEK_SET);
1801 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1802 content_error (ce->ce_file, ct, "unable to fopen for reading");
1808 if (*file == NULL) {
1809 ce->ce_file = add (m_scratch ("", tmp), NULL);
1812 ce->ce_file = add (*file, NULL);
1816 /* sbeck@cise.ufl.edu -- handle suffixes */
1818 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1819 invo_name, ci->ci_type, ci->ci_subtype);
1820 cp = context_find (buffer);
1821 if (cp == NULL || *cp == '\0') {
1822 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1824 cp = context_find (buffer);
1826 if (cp != NULL && *cp != '\0')
1827 ce->ce_file = add (cp, ce->ce_file);
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 ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1835 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1839 if ((len = ct->c_end - ct->c_begin) < 0)
1840 adios (NULL, "internal error(2)");
1842 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1843 content_error (ct->c_file, ct, "unable to open for reading");
1847 if ((digested = ct->c_digested))
1848 MD5Init (&mdContext);
1855 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1859 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1860 content_error (NULL, ct, "premature eof");
1864 if ((cc = strlen (buffer)) > len)
1868 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1873 for (; cp < ep; cp++) {
1876 if (!isxdigit (*cp)) {
1878 dp = "expecting hexidecimal-digit";
1879 goto invalid_encoding;
1882 mask |= hex2nib[*cp & 0x7f];
1883 putc (mask, ce->ce_fp);
1885 MD5Update (&mdContext, &mask, 1);
1889 putc (*cp, ce->ce_fp);
1891 MD5Update (&mdContext, (unsigned char *) ":", 1);
1895 if (!isxdigit (*cp))
1897 mask = hex2nib[*cp & 0x7f];
1903 if (ferror (ce->ce_fp)) {
1904 content_error (ce->ce_file, ct, "error writing to");
1913 if (*cp < '!' || *cp > '~') {
1915 dp = "expecting character in range [!..~]";
1918 i = strlen (invo_name) + 2;
1919 content_error (NULL, ct,
1920 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1928 putc (*cp, ce->ce_fp);
1931 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1933 MD5Update (&mdContext, (unsigned char *) cp, 1);
1935 if (ferror (ce->ce_fp)) {
1936 content_error (ce->ce_file, ct, "error writing to");
1942 if (*++cp != '\n') {
1951 content_error (NULL, ct,
1952 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1956 fseek (ct->c_fp, 0L, SEEK_SET);
1958 if (fflush (ce->ce_fp)) {
1959 content_error (ce->ce_file, ct, "error writing to");
1964 unsigned char digest[16];
1966 MD5Final (digest, &mdContext);
1967 if (memcmp((char *) digest, (char *) ct->c_digest,
1968 sizeof(digest) / sizeof(digest[0])))
1969 content_error (NULL, ct,
1970 "content integrity suspect (digest mismatch) -- continuing");
1973 fprintf (stderr, "content integrity confirmed\n");
1976 fseek (ce->ce_fp, 0L, SEEK_SET);
1979 *file = ce->ce_file;
1980 return fileno (ce->ce_fp);
1983 free_encoding (ct, 0);
1995 if (init_encoding (ct, open7Bit) == NOTOK)
1998 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2004 open7Bit (CT ct, char **file)
2007 char buffer[BUFSIZ];
2008 /* sbeck -- handle prefixes */
2015 fseek (ce->ce_fp, 0L, SEEK_SET);
2020 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2021 content_error (ce->ce_file, ct, "unable to fopen for reading");
2027 if (*file == NULL) {
2028 ce->ce_file = add (m_scratch ("", tmp), NULL);
2031 ce->ce_file = add (*file, NULL);
2035 /* sbeck@cise.ufl.edu -- handle suffixes */
2037 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2038 invo_name, ci->ci_type, ci->ci_subtype);
2039 cp = context_find (buffer);
2040 if (cp == NULL || *cp == '\0') {
2041 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2043 cp = context_find (buffer);
2045 if (cp != NULL && *cp != '\0')
2046 ce->ce_file = add (cp, ce->ce_file);
2048 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2049 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2053 if (ct->c_type == CT_MULTIPART) {
2055 CI ci = &ct->c_ctinfo;
2058 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2059 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2060 + 1 + strlen (ci->ci_subtype);
2061 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2062 putc (';', ce->ce_fp);
2065 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2067 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2068 fputs ("\n\t", ce->ce_fp);
2071 putc (' ', ce->ce_fp);
2074 fprintf (ce->ce_fp, "%s", buffer);
2078 if (ci->ci_comment) {
2079 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2080 fputs ("\n\t", ce->ce_fp);
2084 putc (' ', ce->ce_fp);
2087 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2090 fprintf (ce->ce_fp, "\n");
2092 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2094 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2095 fprintf (ce->ce_fp, "\n");
2098 if ((len = ct->c_end - ct->c_begin) < 0)
2099 adios (NULL, "internal error(3)");
2101 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2102 content_error (ct->c_file, ct, "unable to open for reading");
2106 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2108 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2110 content_error (ct->c_file, ct, "error reading from");
2114 content_error (NULL, ct, "premature eof");
2122 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2123 if (ferror (ce->ce_fp)) {
2124 content_error (ce->ce_file, ct, "error writing to");
2129 fseek (ct->c_fp, 0L, SEEK_SET);
2131 if (fflush (ce->ce_fp)) {
2132 content_error (ce->ce_file, ct, "error writing to");
2136 fseek (ce->ce_fp, 0L, SEEK_SET);
2139 *file = ce->ce_file;
2140 return fileno (ce->ce_fp);
2143 free_encoding (ct, 0);
2153 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2155 char cachefile[BUFSIZ];
2158 fseek (ce->ce_fp, 0L, SEEK_SET);
2163 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2164 content_error (ce->ce_file, ct, "unable to fopen for reading");
2170 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2171 cachefile, sizeof(cachefile)) != NOTOK) {
2172 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2173 ce->ce_file = getcpy (cachefile);
2177 admonish (cachefile, "unable to fopen for reading");
2184 *file = ce->ce_file;
2185 *fd = fileno (ce->ce_fp);
2196 return init_encoding (ct, openFile);
2201 openFile (CT ct, char **file)
2204 char cachefile[BUFSIZ];
2205 struct exbody *e = ct->c_ctexbody;
2206 CE ce = ct->c_cefile;
2208 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2220 content_error (NULL, ct, "missing name parameter");
2224 ce->ce_file = getcpy (e->eb_name);
2227 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2228 content_error (ce->ce_file, ct, "unable to fopen for reading");
2232 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2233 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2234 cachefile, sizeof(cachefile)) != NOTOK) {
2238 mask = umask (cachetype ? ~m_gmprot () : 0222);
2239 if ((fp = fopen (cachefile, "w"))) {
2241 char buffer[BUFSIZ];
2242 FILE *gp = ce->ce_fp;
2244 fseek (gp, 0L, SEEK_SET);
2246 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2248 fwrite (buffer, sizeof(*buffer), cc, fp);
2252 admonish (ce->ce_file, "error reading");
2257 admonish (cachefile, "error writing");
2265 fseek (ce->ce_fp, 0L, SEEK_SET);
2266 *file = ce->ce_file;
2267 return fileno (ce->ce_fp);
2277 return init_encoding (ct, openFTP);
2282 openFTP (CT ct, char **file)
2284 int cachetype, caching, fd;
2286 char *bp, *ftp, *user, *pass;
2287 char buffer[BUFSIZ], cachefile[BUFSIZ];
2290 static char *username = NULL;
2291 static char *password = NULL;
2296 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2304 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2315 if (!e->eb_name || !e->eb_site) {
2316 content_error (NULL, ct, "missing %s parameter",
2317 e->eb_name ? "site": "name");
2324 pidcheck (pidwait (xpid, NOTOK));
2328 /* Get the buffer ready to go */
2330 buflen = sizeof(buffer);
2333 * Construct the query message for user
2335 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2341 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2347 snprintf (bp, buflen, "\n using %sFTP from site %s",
2348 e->eb_flags ? "anonymous " : "", e->eb_site);
2353 if (e->eb_size > 0) {
2354 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2359 snprintf (bp, buflen, "? ");
2362 * Now, check the answer
2364 if (!getanswer (buffer))
2369 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2372 ruserpass (e->eb_site, &username, &password);
2377 ce->ce_unlink = (*file == NULL);
2379 cachefile[0] = '\0';
2380 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2381 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2382 cachefile, sizeof(cachefile)) != NOTOK) {
2383 if (*file == NULL) {
2390 ce->ce_file = add (*file, NULL);
2392 ce->ce_file = add (cachefile, NULL);
2394 ce->ce_file = add (m_scratch ("", tmp), NULL);
2396 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2397 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2405 int child_id, i, vecp;
2409 vec[vecp++] = r1bindex (ftp, '/');
2410 vec[vecp++] = e->eb_site;
2413 vec[vecp++] = e->eb_dir;
2414 vec[vecp++] = e->eb_name;
2415 vec[vecp++] = ce->ce_file,
2416 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2417 ? "ascii" : "binary";
2422 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2426 adios ("fork", "unable to");
2430 close (fileno (ce->ce_fp));
2432 fprintf (stderr, "unable to exec ");
2438 if (pidXwait (child_id, NULL)) {
2442 username = password = NULL;
2451 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2453 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2460 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2465 mask = umask (cachetype ? ~m_gmprot () : 0222);
2466 if ((fp = fopen (cachefile, "w"))) {
2468 FILE *gp = ce->ce_fp;
2470 fseek (gp, 0L, SEEK_SET);
2472 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2474 fwrite (buffer, sizeof(*buffer), cc, fp);
2478 admonish (ce->ce_file, "error reading");
2483 admonish (cachefile, "error writing");
2492 fseek (ce->ce_fp, 0L, SEEK_SET);
2493 *file = ce->ce_file;
2494 return fileno (ce->ce_fp);
2505 return init_encoding (ct, openMail);
2510 openMail (CT ct, char **file)
2512 int child_id, fd, i, vecp;
2514 char *bp, buffer[BUFSIZ], *vec[7];
2515 struct exbody *e = ct->c_ctexbody;
2516 CE ce = ct->c_cefile;
2518 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2529 if (!e->eb_server) {
2530 content_error (NULL, ct, "missing server parameter");
2537 pidcheck (pidwait (xpid, NOTOK));
2541 /* Get buffer ready to go */
2543 buflen = sizeof(buffer);
2545 /* Now, construct query message */
2546 snprintf (bp, buflen, "Retrieve content");
2552 snprintf (bp, buflen, " %s", e->eb_partno);
2558 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2560 e->eb_subject ? e->eb_subject : e->eb_body);
2562 /* Now, check answer */
2563 if (!getanswer (buffer))
2567 vec[vecp++] = r1bindex (mailproc, '/');
2568 vec[vecp++] = e->eb_server;
2569 vec[vecp++] = "-subject";
2570 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2571 vec[vecp++] = "-body";
2572 vec[vecp++] = e->eb_body;
2575 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2579 advise ("fork", "unable to");
2583 execvp (mailproc, vec);
2584 fprintf (stderr, "unable to exec ");
2590 if (pidXwait (child_id, NULL) == OK)
2591 advise (NULL, "request sent");
2595 if (*file == NULL) {
2596 ce->ce_file = add (m_scratch ("", tmp), NULL);
2599 ce->ce_file = add (*file, NULL);
2603 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2604 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2609 free (ct->c_showproc);
2610 ct->c_showproc = add ("true", NULL);
2612 fseek (ce->ce_fp, 0L, SEEK_SET);
2613 *file = ce->ce_file;
2614 return fileno (ce->ce_fp);
2619 readDigest (CT ct, char *cp)
2624 unsigned char *dp, value, *ep;
2625 unsigned char *b, *b1, *b2, *b3;
2627 b = (unsigned char *) &bits,
2628 b1 = &b[endian > 0 ? 1 : 2],
2629 b2 = &b[endian > 0 ? 2 : 1],
2630 b3 = &b[endian > 0 ? 3 : 0];
2635 for (ep = (dp = ct->c_digest)
2636 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2641 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2643 fprintf (stderr, "invalid BASE64 encoding\n");
2647 bits |= value << bitno;
2649 if ((bitno -= 6) < 0) {
2650 if (dp + (3 - skip) > ep)
2651 goto invalid_digest;
2666 goto self_delimiting;
2671 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2681 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2689 fprintf (stderr, "MD5 digest=");
2690 for (dp = ct->c_digest; dp < ep; dp++)
2691 fprintf (stderr, "%02x", *dp & 0xff);
2692 fprintf (stderr, "\n");