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, NULL },
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.
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");