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))) {
262 advise (NULL, "unable to decode %s", file);
267 ct->c_unlink = 1; /* temp file to remove */
271 if (ct->c_end == 0L) {
272 fseek (fp, 0L, SEEK_END);
273 ct->c_end = ftell (fp);
276 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
288 * Main routine for reading/parsing the headers
289 * of a message content.
291 * toplevel = 1 # we are at the top level of the message
292 * toplevel = 0 # we are inside message type or multipart type
293 * # other than multipart/digest
294 * toplevel = -1 # we are inside multipart/digest
295 * NB: on failure we will fclose(in)!
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))) {
1065 fseek (fp, pos, SEEK_SET);
1068 if (strcmp (buffer + 2, m->mp_start) == 0) {
1072 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1073 if (p->c_end < p->c_begin)
1074 p->c_begin = p->c_end;
1079 if (strcmp (buffer + 2, m->mp_stop) == 0)
1085 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1086 if (!inout && part) {
1088 p->c_end = ct->c_end;
1090 if (p->c_begin >= p->c_end) {
1091 for (next = &m->mp_parts; *next != part;
1092 next = &((*next)->mp_next))
1096 free ((char *) part);
1101 /* reverse the order of the parts for multipart/alternative */
1102 if (ct->c_subtype == MULTI_ALTERNATE)
1106 * label all subparts with part number, and
1107 * then initialize the content of the subpart.
1112 char partnam[BUFSIZ];
1115 snprintf (partnam, sizeof(partnum), "%s.", ct->c_partno);
1116 pp = partnam + strlen (partnam);
1121 for (part = m->mp_parts, partnum = 1; part;
1122 part = part->mp_next, partnum++) {
1125 sprintf (pp, "%d", partnum);
1126 p->c_partno = add (partnam, NULL);
1128 /* initialize the content of the subparts */
1129 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1144 * reverse the order of the parts of a multipart
1148 reverse_parts (CT ct)
1151 struct multipart *m;
1152 struct part **base, **bmp, **next, *part;
1154 m = (struct multipart *) ct->c_ctparams;
1156 /* if only one part, just return */
1157 if (!m->mp_parts || !m->mp_parts->mp_next)
1160 /* count number of parts */
1162 for (part = m->mp_parts; part; part = part->mp_next)
1165 /* allocate array of pointers to the parts */
1166 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1167 adios (NULL, "out of memory");
1170 /* point at all the parts */
1171 for (part = m->mp_parts; part; part = part->mp_next)
1175 /* reverse the order of the parts */
1176 next = &m->mp_parts;
1177 for (bmp--; bmp >= base; bmp--) {
1180 next = &part->mp_next;
1184 /* free array of pointers */
1185 free ((char *) base);
1197 CI ci = &ct->c_ctinfo;
1199 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1201 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1202 ci->ci_type, ci->ci_subtype, ct->c_file);
1206 /* check for missing subtype */
1207 if (!*ci->ci_subtype)
1208 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1211 for (kv = SubMessage; kv->kv_key; kv++)
1212 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1214 ct->c_subtype = kv->kv_value;
1216 switch (ct->c_subtype) {
1217 case MESSAGE_RFC822:
1220 case MESSAGE_PARTIAL:
1225 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1226 adios (NULL, "out of memory");
1227 ct->c_ctparams = (void *) p;
1229 /* scan for parameters "id", "number", and "total" */
1230 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1231 if (!strcasecmp (*ap, "id")) {
1232 p->pm_partid = add (*ep, NULL);
1235 if (!strcasecmp (*ap, "number")) {
1236 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1237 || p->pm_partno < 1) {
1240 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1241 *ap, ci->ci_type, ci->ci_subtype,
1242 ct->c_file, TYPE_FIELD);
1247 if (!strcasecmp (*ap, "total")) {
1248 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1257 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1259 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1260 ci->ci_type, ci->ci_subtype,
1261 ct->c_file, TYPE_FIELD);
1267 case MESSAGE_EXTERNAL:
1274 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1275 adios (NULL, "out of memory");
1276 ct->c_ctparams = (void *) e;
1279 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1280 advise (ct->c_file, "unable to open for reading");
1284 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1286 if (!(p = get_content (fp, ct->c_file, 0))) {
1294 if ((exresult = params_external (ct, 0)) != NOTOK
1295 && p->c_ceopenfnx == openMail) {
1299 if ((size = ct->c_end - p->c_begin) <= 0) {
1301 content_error (NULL, ct,
1302 "empty body for access-type=mail-server");
1306 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1307 adios (NULL, "out of memory");
1308 fseek (p->c_fp, p->c_begin, SEEK_SET);
1310 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1312 adios ("failed", "fread");
1315 adios (NULL, "unexpected EOF from fread");
1318 bp += cc, size -= cc;
1325 p->c_end = p->c_begin;
1330 if (exresult == NOTOK)
1332 if (e->eb_flags == NOTOK)
1335 switch (p->c_type) {
1340 if (p->c_subtype != MESSAGE_RFC822)
1344 e->eb_partno = ct->c_partno;
1346 (*p->c_ctinitfnx) (p);
1361 params_external (CT ct, int composing)
1364 struct exbody *e = (struct exbody *) ct->c_ctparams;
1365 CI ci = &ct->c_ctinfo;
1367 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1368 if (!strcasecmp (*ap, "access-type")) {
1369 struct str2init *s2i;
1370 CT p = e->eb_content;
1372 for (s2i = str2methods; s2i->si_key; s2i++)
1373 if (!strcasecmp (*ep, s2i->si_key))
1377 e->eb_flags = NOTOK;
1378 p->c_encoding = CE_EXTERNAL;
1381 e->eb_access = s2i->si_key;
1382 e->eb_flags = s2i->si_val;
1383 p->c_encoding = CE_EXTERNAL;
1385 /* Call the Init function for this external type */
1386 if ((*s2i->si_init)(p) == NOTOK)
1390 if (!strcasecmp (*ap, "name")) {
1394 if (!strcasecmp (*ap, "permission")) {
1395 e->eb_permission = *ep;
1398 if (!strcasecmp (*ap, "site")) {
1402 if (!strcasecmp (*ap, "directory")) {
1406 if (!strcasecmp (*ap, "mode")) {
1410 if (!strcasecmp (*ap, "size")) {
1411 sscanf (*ep, "%lu", &e->eb_size);
1414 if (!strcasecmp (*ap, "server")) {
1418 if (!strcasecmp (*ap, "subject")) {
1419 e->eb_subject = *ep;
1422 if (composing && !strcasecmp (*ap, "body")) {
1423 e->eb_body = getcpy (*ep);
1428 if (!e->eb_access) {
1430 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1431 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1444 InitApplication (CT ct)
1447 CI ci = &ct->c_ctinfo;
1450 for (kv = SubApplication; kv->kv_key; kv++)
1451 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1453 ct->c_subtype = kv->kv_value;
1460 * TRANSFER ENCODINGS
1464 init_encoding (CT ct, OpenCEFunc openfnx)
1468 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1469 adios (NULL, "out of memory");
1472 ct->c_ceopenfnx = openfnx;
1473 ct->c_ceclosefnx = close_encoding;
1474 ct->c_cesizefnx = size_encoding;
1481 close_encoding (CT ct)
1485 if (!(ce = ct->c_cefile))
1495 static unsigned long
1496 size_encoding (CT ct)
1504 if (!(ce = ct->c_cefile))
1505 return (ct->c_end - ct->c_begin);
1507 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1508 return (long) st.st_size;
1511 if (stat (ce->ce_file, &st) != NOTOK)
1512 return (long) st.st_size;
1517 if (ct->c_encoding == CE_EXTERNAL)
1518 return (ct->c_end - ct->c_begin);
1521 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1522 return (ct->c_end - ct->c_begin);
1524 if (fstat (fd, &st) != NOTOK)
1525 size = (long) st.st_size;
1529 (*ct->c_ceclosefnx) (ct);
1538 static unsigned char b642nib[0x80] = {
1539 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1540 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1541 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1542 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1543 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1544 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1545 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1546 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1547 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1548 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1549 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1550 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1551 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1552 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1553 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1554 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1561 return init_encoding (ct, openBase64);
1566 openBase64 (CT ct, char **file)
1568 int bitno, cc, digested;
1571 unsigned char value, *b, *b1, *b2, *b3;
1572 char *cp, *ep, buffer[BUFSIZ];
1573 /* sbeck -- handle prefixes */
1578 b = (unsigned char *) &bits;
1579 b1 = &b[endian > 0 ? 1 : 2];
1580 b2 = &b[endian > 0 ? 2 : 1];
1581 b3 = &b[endian > 0 ? 3 : 0];
1585 fseek (ce->ce_fp, 0L, SEEK_SET);
1590 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1591 content_error (ce->ce_file, ct, "unable to fopen for reading");
1597 if (*file == NULL) {
1598 ce->ce_file = add (m_scratch ("", tmp), NULL);
1601 ce->ce_file = add (*file, NULL);
1605 /* sbeck@cise.ufl.edu -- handle suffixes */
1607 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1608 invo_name, ci->ci_type, ci->ci_subtype);
1609 cp = context_find (buffer);
1610 if (cp == NULL || *cp == '\0') {
1611 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1613 cp = context_find (buffer);
1615 if (cp != NULL && *cp != '\0')
1616 ce->ce_file = add (cp, ce->ce_file);
1618 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1619 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1623 if ((len = ct->c_end - ct->c_begin) < 0)
1624 adios (NULL, "internal error(1)");
1626 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1627 content_error (ct->c_file, ct, "unable to open for reading");
1631 if ((digested = ct->c_digested))
1632 MD5Init (&mdContext);
1638 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1640 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1642 content_error (ct->c_file, ct, "error reading from");
1646 content_error (NULL, ct, "premature eof");
1654 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1659 if (skip || (*cp & 0x80)
1660 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1662 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1664 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1667 content_error (NULL, ct,
1668 "invalid BASE64 encoding -- continuing");
1672 bits |= value << bitno;
1674 if ((bitno -= 6) < 0) {
1675 putc ((char) *b1, ce->ce_fp);
1677 MD5Update (&mdContext, b1, 1);
1679 putc ((char) *b2, ce->ce_fp);
1681 MD5Update (&mdContext, b2, 1);
1683 putc ((char) *b3, ce->ce_fp);
1685 MD5Update (&mdContext, b3, 1);
1689 if (ferror (ce->ce_fp)) {
1690 content_error (ce->ce_file, ct,
1691 "error writing to");
1694 bitno = 18, bits = 0L, skip = 0;
1700 goto self_delimiting;
1709 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1711 content_error (NULL, ct, "invalid BASE64 encoding");
1716 fseek (ct->c_fp, 0L, SEEK_SET);
1718 if (fflush (ce->ce_fp)) {
1719 content_error (ce->ce_file, ct, "error writing to");
1724 unsigned char digest[16];
1726 MD5Final (digest, &mdContext);
1727 if (memcmp((char *) digest, (char *) ct->c_digest,
1728 sizeof(digest) / sizeof(digest[0])))
1729 content_error (NULL, ct,
1730 "content integrity suspect (digest mismatch) -- continuing");
1733 fprintf (stderr, "content integrity confirmed\n");
1736 fseek (ce->ce_fp, 0L, SEEK_SET);
1739 *file = ce->ce_file;
1740 return fileno (ce->ce_fp);
1743 free_encoding (ct, 0);
1752 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1758 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1759 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1760 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1761 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1762 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1763 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1764 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1765 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1766 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1767 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1768 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1775 return init_encoding (ct, openQuoted);
1780 openQuoted (CT ct, char **file)
1782 int cc, digested, len, quoted;
1784 char buffer[BUFSIZ];
1787 /* sbeck -- handle prefixes */
1793 fseek (ce->ce_fp, 0L, SEEK_SET);
1798 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1799 content_error (ce->ce_file, ct, "unable to fopen for reading");
1805 if (*file == NULL) {
1806 ce->ce_file = add (m_scratch ("", tmp), NULL);
1809 ce->ce_file = add (*file, NULL);
1813 /* sbeck@cise.ufl.edu -- handle suffixes */
1815 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1816 invo_name, ci->ci_type, ci->ci_subtype);
1817 cp = context_find (buffer);
1818 if (cp == NULL || *cp == '\0') {
1819 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1821 cp = context_find (buffer);
1823 if (cp != NULL && *cp != '\0')
1824 ce->ce_file = add (cp, ce->ce_file);
1826 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1827 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1831 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1832 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1836 if ((len = ct->c_end - ct->c_begin) < 0)
1837 adios (NULL, "internal error(2)");
1839 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1840 content_error (ct->c_file, ct, "unable to open for reading");
1844 if ((digested = ct->c_digested))
1845 MD5Init (&mdContext);
1852 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
1856 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1857 content_error (NULL, ct, "premature eof");
1861 if ((cc = strlen (buffer)) > len)
1865 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1870 for (; cp < ep; cp++) {
1873 if (!isxdigit (*cp)) {
1875 dp = "expecting hexidecimal-digit";
1876 goto invalid_encoding;
1879 mask |= hex2nib[*cp & 0x7f];
1880 putc (mask, ce->ce_fp);
1882 MD5Update (&mdContext, &mask, 1);
1886 putc (*cp, ce->ce_fp);
1888 MD5Update (&mdContext, (unsigned char *) ":", 1);
1892 if (!isxdigit (*cp))
1894 mask = hex2nib[*cp & 0x7f];
1900 if (ferror (ce->ce_fp)) {
1901 content_error (ce->ce_file, ct, "error writing to");
1910 if (*cp < '!' || *cp > '~') {
1912 dp = "expecting character in range [!..~]";
1915 i = strlen (invo_name) + 2;
1916 content_error (NULL, ct,
1917 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
1925 putc (*cp, ce->ce_fp);
1928 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
1930 MD5Update (&mdContext, (unsigned char *) cp, 1);
1932 if (ferror (ce->ce_fp)) {
1933 content_error (ce->ce_file, ct, "error writing to");
1939 if (*++cp != '\n') {
1948 content_error (NULL, ct,
1949 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
1953 fseek (ct->c_fp, 0L, SEEK_SET);
1955 if (fflush (ce->ce_fp)) {
1956 content_error (ce->ce_file, ct, "error writing to");
1961 unsigned char digest[16];
1963 MD5Final (digest, &mdContext);
1964 if (memcmp((char *) digest, (char *) ct->c_digest,
1965 sizeof(digest) / sizeof(digest[0])))
1966 content_error (NULL, ct,
1967 "content integrity suspect (digest mismatch) -- continuing");
1970 fprintf (stderr, "content integrity confirmed\n");
1973 fseek (ce->ce_fp, 0L, SEEK_SET);
1976 *file = ce->ce_file;
1977 return fileno (ce->ce_fp);
1980 free_encoding (ct, 0);
1992 if (init_encoding (ct, open7Bit) == NOTOK)
1995 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2001 open7Bit (CT ct, char **file)
2004 char buffer[BUFSIZ];
2005 /* sbeck -- handle prefixes */
2012 fseek (ce->ce_fp, 0L, SEEK_SET);
2017 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2018 content_error (ce->ce_file, ct, "unable to fopen for reading");
2024 if (*file == NULL) {
2025 ce->ce_file = add (m_scratch ("", tmp), NULL);
2028 ce->ce_file = add (*file, NULL);
2032 /* sbeck@cise.ufl.edu -- handle suffixes */
2034 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2035 invo_name, ci->ci_type, ci->ci_subtype);
2036 cp = context_find (buffer);
2037 if (cp == NULL || *cp == '\0') {
2038 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2040 cp = context_find (buffer);
2042 if (cp != NULL && *cp != '\0')
2043 ce->ce_file = add (cp, ce->ce_file);
2045 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2046 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2050 if (ct->c_type == CT_MULTIPART) {
2052 CI ci = &ct->c_ctinfo;
2055 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2056 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2057 + 1 + strlen (ci->ci_subtype);
2058 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2059 putc (';', ce->ce_fp);
2062 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2064 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2065 fputs ("\n\t", ce->ce_fp);
2068 putc (' ', ce->ce_fp);
2071 fprintf (ce->ce_fp, "%s", buffer);
2075 if (ci->ci_comment) {
2076 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2077 fputs ("\n\t", ce->ce_fp);
2081 putc (' ', ce->ce_fp);
2084 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2087 fprintf (ce->ce_fp, "\n");
2089 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2091 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2092 fprintf (ce->ce_fp, "\n");
2095 if ((len = ct->c_end - ct->c_begin) < 0)
2096 adios (NULL, "internal error(3)");
2098 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2099 content_error (ct->c_file, ct, "unable to open for reading");
2103 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2105 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2107 content_error (ct->c_file, ct, "error reading from");
2111 content_error (NULL, ct, "premature eof");
2119 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2120 if (ferror (ce->ce_fp)) {
2121 content_error (ce->ce_file, ct, "error writing to");
2126 fseek (ct->c_fp, 0L, SEEK_SET);
2128 if (fflush (ce->ce_fp)) {
2129 content_error (ce->ce_file, ct, "error writing to");
2133 fseek (ce->ce_fp, 0L, SEEK_SET);
2136 *file = ce->ce_file;
2137 return fileno (ce->ce_fp);
2140 free_encoding (ct, 0);
2150 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2152 char cachefile[BUFSIZ];
2155 fseek (ce->ce_fp, 0L, SEEK_SET);
2160 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2161 content_error (ce->ce_file, ct, "unable to fopen for reading");
2167 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2168 cachefile, sizeof(cachefile)) != NOTOK) {
2169 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2170 ce->ce_file = getcpy (cachefile);
2174 admonish (cachefile, "unable to fopen for reading");
2181 *file = ce->ce_file;
2182 *fd = fileno (ce->ce_fp);
2193 return init_encoding (ct, openFile);
2198 openFile (CT ct, char **file)
2201 char cachefile[BUFSIZ];
2202 struct exbody *e = ct->c_ctexbody;
2203 CE ce = ct->c_cefile;
2205 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2217 content_error (NULL, ct, "missing name parameter");
2221 ce->ce_file = getcpy (e->eb_name);
2224 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2225 content_error (ce->ce_file, ct, "unable to fopen for reading");
2229 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2230 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2231 cachefile, sizeof(cachefile)) != NOTOK) {
2235 mask = umask (cachetype ? ~m_gmprot () : 0222);
2236 if ((fp = fopen (cachefile, "w"))) {
2238 char buffer[BUFSIZ];
2239 FILE *gp = ce->ce_fp;
2241 fseek (gp, 0L, SEEK_SET);
2243 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2245 fwrite (buffer, sizeof(*buffer), cc, fp);
2249 admonish (ce->ce_file, "error reading");
2254 admonish (cachefile, "error writing");
2262 fseek (ce->ce_fp, 0L, SEEK_SET);
2263 *file = ce->ce_file;
2264 return fileno (ce->ce_fp);
2274 return init_encoding (ct, openFTP);
2279 openFTP (CT ct, char **file)
2281 int cachetype, caching, fd;
2283 char *bp, *ftp, *user, *pass;
2284 char buffer[BUFSIZ], cachefile[BUFSIZ];
2287 static char *username = NULL;
2288 static char *password = NULL;
2293 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2301 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2312 if (!e->eb_name || !e->eb_site) {
2313 content_error (NULL, ct, "missing %s parameter",
2314 e->eb_name ? "site": "name");
2321 pidcheck (pidwait (xpid, NOTOK));
2325 /* Get the buffer ready to go */
2327 buflen = sizeof(buffer);
2330 * Construct the query message for user
2332 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2338 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2344 snprintf (bp, buflen, "\n using %sFTP from site %s",
2345 e->eb_flags ? "anonymous " : "", e->eb_site);
2350 if (e->eb_size > 0) {
2351 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2356 snprintf (bp, buflen, "? ");
2359 * Now, check the answer
2361 if (!getanswer (buffer))
2366 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2369 ruserpass (e->eb_site, &username, &password);
2374 ce->ce_unlink = (*file == NULL);
2376 cachefile[0] = '\0';
2377 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2378 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2379 cachefile, sizeof(cachefile)) != NOTOK) {
2380 if (*file == NULL) {
2387 ce->ce_file = add (*file, NULL);
2389 ce->ce_file = add (cachefile, NULL);
2391 ce->ce_file = add (m_scratch ("", tmp), NULL);
2393 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2394 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2402 int child_id, i, vecp;
2406 vec[vecp++] = r1bindex (ftp, '/');
2407 vec[vecp++] = e->eb_site;
2410 vec[vecp++] = e->eb_dir;
2411 vec[vecp++] = e->eb_name;
2412 vec[vecp++] = ce->ce_file,
2413 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2414 ? "ascii" : "binary";
2419 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2423 adios ("fork", "unable to");
2427 close (fileno (ce->ce_fp));
2429 fprintf (stderr, "unable to exec ");
2435 if (pidXwait (child_id, NULL)) {
2439 username = password = NULL;
2448 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2450 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2457 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2462 mask = umask (cachetype ? ~m_gmprot () : 0222);
2463 if ((fp = fopen (cachefile, "w"))) {
2465 FILE *gp = ce->ce_fp;
2467 fseek (gp, 0L, SEEK_SET);
2469 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2471 fwrite (buffer, sizeof(*buffer), cc, fp);
2475 admonish (ce->ce_file, "error reading");
2480 admonish (cachefile, "error writing");
2489 fseek (ce->ce_fp, 0L, SEEK_SET);
2490 *file = ce->ce_file;
2491 return fileno (ce->ce_fp);
2502 return init_encoding (ct, openMail);
2507 openMail (CT ct, char **file)
2509 int child_id, fd, i, vecp;
2511 char *bp, buffer[BUFSIZ], *vec[7];
2512 struct exbody *e = ct->c_ctexbody;
2513 CE ce = ct->c_cefile;
2515 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2526 if (!e->eb_server) {
2527 content_error (NULL, ct, "missing server parameter");
2534 pidcheck (pidwait (xpid, NOTOK));
2538 /* Get buffer ready to go */
2540 buflen = sizeof(buffer);
2542 /* Now, construct query message */
2543 snprintf (bp, buflen, "Retrieve content");
2549 snprintf (bp, buflen, " %s", e->eb_partno);
2555 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2557 e->eb_subject ? e->eb_subject : e->eb_body);
2559 /* Now, check answer */
2560 if (!getanswer (buffer))
2564 vec[vecp++] = r1bindex (mailproc, '/');
2565 vec[vecp++] = e->eb_server;
2566 vec[vecp++] = "-subject";
2567 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2568 vec[vecp++] = "-body";
2569 vec[vecp++] = e->eb_body;
2572 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2576 advise ("fork", "unable to");
2580 execvp (mailproc, vec);
2581 fprintf (stderr, "unable to exec ");
2587 if (pidXwait (child_id, NULL) == OK)
2588 advise (NULL, "request sent");
2592 if (*file == NULL) {
2593 ce->ce_file = add (m_scratch ("", tmp), NULL);
2596 ce->ce_file = add (*file, NULL);
2600 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2601 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2606 free (ct->c_showproc);
2607 ct->c_showproc = add ("true", NULL);
2609 fseek (ce->ce_fp, 0L, SEEK_SET);
2610 *file = ce->ce_file;
2611 return fileno (ce->ce_fp);
2616 readDigest (CT ct, char *cp)
2621 unsigned char *dp, value, *ep;
2622 unsigned char *b, *b1, *b2, *b3;
2624 b = (unsigned char *) &bits,
2625 b1 = &b[endian > 0 ? 1 : 2],
2626 b2 = &b[endian > 0 ? 2 : 1],
2627 b3 = &b[endian > 0 ? 3 : 0];
2632 for (ep = (dp = ct->c_digest)
2633 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2638 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2640 fprintf (stderr, "invalid BASE64 encoding\n");
2644 bits |= value << bitno;
2646 if ((bitno -= 6) < 0) {
2647 if (dp + (3 - skip) > ep)
2648 goto invalid_digest;
2663 goto self_delimiting;
2668 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2678 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2686 fprintf (stderr, "MD5 digest=");
2687 for (dp = ct->c_digest; dp < ep; dp++)
2688 fprintf (stderr, "%02x", *dp & 0xff);
2689 fprintf (stderr, "\n");