2 * mhparse.c -- routines to parse the contents of MIME messages
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
11 #include <h/signals.h>
19 #include <h/mhparse.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
29 extern int endian; /* mhmisc.c */
31 extern pid_t xpid; /* mhshowsbr.c */
34 extern int rcachesw; /* mhcachesbr.c */
35 extern int wcachesw; /* mhcachesbr.c */
37 int checksw = 0; /* check Content-MD5 field */
40 * Directory to place temp files. This must
41 * be set before these routines are called.
46 * Structures for TEXT messages
48 struct k2v SubText[] = {
49 { "plain", TEXT_PLAIN },
50 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
51 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
52 { NULL, TEXT_UNKNOWN } /* this one must be last! */
55 struct k2v Charset[] = {
56 { "us-ascii", CHARSET_USASCII },
57 { "iso-8859-1", CHARSET_LATIN },
58 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
62 * Structures for MULTIPART messages
64 struct k2v SubMultiPart[] = {
65 { "mixed", MULTI_MIXED },
66 { "alternative", MULTI_ALTERNATE },
67 { "digest", MULTI_DIGEST },
68 { "parallel", MULTI_PARALLEL },
69 { NULL, MULTI_UNKNOWN } /* this one must be last! */
73 * Structures for MESSAGE messages
75 struct k2v SubMessage[] = {
76 { "rfc822", MESSAGE_RFC822 },
77 { "partial", MESSAGE_PARTIAL },
78 { "external-body", MESSAGE_EXTERNAL },
79 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
83 * Structure for APPLICATION messages
85 struct k2v SubApplication[] = {
86 { "octet-stream", APPLICATION_OCTETS },
87 { "postscript", APPLICATION_POSTSCRIPT },
88 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
93 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
96 int find_cache (CT, int, int *, char *, char *, int);
99 int part_ok (CT, int);
100 int type_ok (CT, int);
101 int make_intermediates (char *);
102 void content_error (char *, CT, char *, ...);
105 void free_content (CT);
106 void free_encoding (CT, int);
111 static CT get_content (FILE *, char *, int);
112 static int get_comment (CT, unsigned char **, int);
114 static int InitGeneric (CT);
115 static int InitText (CT);
116 static int InitMultiPart (CT);
117 static void reverse_parts (CT);
118 static int InitMessage (CT);
119 static int InitApplication (CT);
120 static int init_encoding (CT, OpenCEFunc);
121 static unsigned long size_encoding (CT);
122 static int InitBase64 (CT);
123 static int openBase64 (CT, char **);
124 static int InitQuoted (CT);
125 static int openQuoted (CT, char **);
126 static int Init7Bit (CT);
127 static int openExternal (CT, CT, CE, char **, int *);
128 static int InitFile (CT);
129 static int openFile (CT, char **);
130 static int InitFTP (CT);
131 static int openFTP (CT, char **);
132 static int InitMail (CT);
133 static int openMail (CT, char **);
134 static int readDigest (CT, char *);
136 struct str2init str2cts[] = {
137 { "application", CT_APPLICATION, InitApplication },
138 { "audio", CT_AUDIO, InitGeneric },
139 { "image", CT_IMAGE, InitGeneric },
140 { "message", CT_MESSAGE, InitMessage },
141 { "multipart", CT_MULTIPART, InitMultiPart },
142 { "text", CT_TEXT, InitText },
143 { "video", CT_VIDEO, InitGeneric },
144 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
145 { NULL, CT_UNKNOWN, NULL },
148 struct str2init str2ces[] = {
149 { "base64", CE_BASE64, InitBase64 },
150 { "quoted-printable", CE_QUOTED, InitQuoted },
151 { "8bit", CE_8BIT, Init7Bit },
152 { "7bit", CE_7BIT, Init7Bit },
153 { "binary", CE_BINARY, Init7Bit },
154 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
155 { NULL, CE_UNKNOWN, NULL },
159 * NOTE WELL: si_key MUST NOT have value of NOTOK
161 * si_key is 1 if access method is anonymous.
163 struct str2init str2methods[] = {
164 { "afs", 1, InitFile },
165 { "anon-ftp", 1, InitFTP },
166 { "ftp", 0, InitFTP },
167 { "local-file", 0, InitFile },
168 { "mail-server", 0, InitMail },
174 pidcheck (int status)
176 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
187 * Main entry point for parsing a MIME message or file.
188 * It returns the Content structure for the top level
189 * entity in the file.
192 parse_mime (char *file)
200 * Check if file is actually standard input
202 if ((is_stdin = !(strcmp (file, "-")))) {
203 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
205 advise("mhparse", "unable to create temporary file");
208 file = add (tfile, NULL);
211 while (fgets (buffer, sizeof(buffer), stdin))
215 if (ferror (stdin)) {
217 advise ("stdin", "error reading");
222 advise (file, "error writing");
225 fseek (fp, 0L, SEEK_SET);
226 } else if ((fp = fopen (file, "r")) == NULL) {
227 advise (file, "unable to read");
231 if (!(ct = get_content (fp, file, 1))) {
234 advise (NULL, "unable to decode %s", file);
239 ct->c_unlink = 1; /* temp file to remove */
243 if (ct->c_end == 0L) {
244 fseek (fp, 0L, SEEK_END);
245 ct->c_end = ftell (fp);
248 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
260 * Main routine for reading/parsing the headers
261 * of a message content.
263 * toplevel = 1 # we are at the top level of the message
264 * toplevel = 0 # we are inside message type or multipart type
265 * # other than multipart/digest
266 * toplevel = -1 # we are inside multipart/digest
267 * NB: on failure we will fclose(in)!
271 get_content (FILE *in, char *file, int toplevel)
274 char buf[BUFSIZ], name[NAMESZ];
279 /* allocate the content structure */
280 if (!(ct = (CT) calloc (1, sizeof(*ct))))
281 adios (NULL, "out of memory");
284 ct->c_file = add (file, NULL);
285 ct->c_begin = ftell (ct->c_fp) + 1;
288 * Parse the header fields for this
289 * content into a linked list.
291 for (compnum = 1, state = FLD;;) {
292 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
298 /* get copies of the buffers */
299 np = add (name, NULL);
300 vp = add (buf, NULL);
302 /* if necessary, get rest of field */
303 while (state == FLDPLUS) {
304 state = m_getfld (state, name, buf, sizeof(buf), in);
305 vp = add (buf, vp); /* add to previous value */
308 /* Now add the header data to the list */
309 add_header (ct, np, vp);
311 /* continue, if this isn't the last header field */
312 if (state != FLDEOF) {
313 ct->c_begin = ftell (in) + 1;
320 ct->c_begin = ftell (in) - strlen (buf);
324 ct->c_begin = ftell (in);
329 adios (NULL, "message format error in component #%d", compnum);
332 adios (NULL, "getfld() returned %d", state);
335 /* break out of the loop */
340 * Read the content headers. We will parse the
341 * MIME related header fields into their various
342 * structures and set internal flags related to
343 * content type/subtype, etc.
346 hp = ct->c_first_hf; /* start at first header field */
348 /* Get MIME-Version field */
349 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
352 unsigned char *cp, *dp;
355 advise (NULL, "message %s has multiple %s: fields",
356 ct->c_file, VRSN_FIELD);
359 ct->c_vrsn = add (hp->value, NULL);
361 /* Now, cleanup this field */
364 while (isspace (*cp))
366 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
368 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
373 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
375 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
378 for (dp = cp; istoken (*dp); dp++)
382 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
385 admonish (NULL, "message %s has unknown value for %s: field (%s)",
386 ct->c_file, VRSN_FIELD, cp);
389 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
390 /* Get Content-Type field */
391 struct str2init *s2i;
392 CI ci = &ct->c_ctinfo;
394 /* Check if we've already seen a Content-Type header */
396 advise (NULL, "message %s has multiple %s: fields",
397 ct->c_file, TYPE_FIELD);
401 /* Parse the Content-Type field */
402 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
406 * Set the Init function and the internal
407 * flag for this content type.
409 for (s2i = str2cts; s2i->si_key; s2i++)
410 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
412 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
414 ct->c_type = s2i->si_val;
415 ct->c_ctinitfnx = s2i->si_init;
417 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
418 /* Get Content-Transfer-Encoding field */
420 unsigned char *cp, *dp;
421 struct str2init *s2i;
424 * Check if we've already seen the
425 * Content-Transfer-Encoding field
428 advise (NULL, "message %s has multiple %s: fields",
429 ct->c_file, ENCODING_FIELD);
433 /* get copy of this field */
434 ct->c_celine = cp = add (hp->value, NULL);
436 while (isspace (*cp))
438 for (dp = cp; istoken (*dp); dp++)
444 * Find the internal flag and Init function
445 * for this transfer encoding.
447 for (s2i = str2ces; s2i->si_key; s2i++)
448 if (!mh_strcasecmp (cp, s2i->si_key))
450 if (!s2i->si_key && !uprf (cp, "X-"))
453 ct->c_encoding = s2i->si_val;
455 /* Call the Init function for this encoding */
456 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
459 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
460 /* Get Content-MD5 field */
461 unsigned char *cp, *dp;
467 if (ct->c_digested) {
468 advise (NULL, "message %s has multiple %s: fields",
469 ct->c_file, MD5_FIELD);
473 ep = cp = add (hp->value, NULL); /* get a copy */
475 while (isspace (*cp))
477 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
479 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
484 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
486 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
491 for (dp = cp; *dp && !isspace (*dp); dp++)
499 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
500 /* Get Content-ID field */
501 ct->c_id = add (hp->value, ct->c_id);
503 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
504 /* Get Content-Description field */
505 ct->c_descr = add (hp->value, ct->c_descr);
507 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
508 /* Get Content-Disposition field */
509 ct->c_dispo = add (hp->value, ct->c_dispo);
513 hp = hp->next; /* next header field */
517 * Check if we saw a Content-Type field.
518 * If not, then assign a default value for
519 * it, and the Init function.
523 * If we are inside a multipart/digest message,
524 * so default type is message/rfc822
527 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
529 ct->c_type = CT_MESSAGE;
530 ct->c_ctinitfnx = InitMessage;
533 * Else default type is text/plain
535 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
537 ct->c_type = CT_TEXT;
538 ct->c_ctinitfnx = InitText;
542 /* Use default Transfer-Encoding, if necessary */
544 ct->c_encoding = CE_7BIT;
557 * small routine to add header field to list
561 add_header (CT ct, char *name, char *value)
565 /* allocate header field structure */
566 hp = mh_xmalloc (sizeof(*hp));
568 /* link data into header structure */
573 /* link header structure into the list */
574 if (ct->c_first_hf == NULL) {
575 ct->c_first_hf = hp; /* this is the first */
578 ct->c_last_hf->next = hp; /* add it to the end */
586 /* Make sure that buf contains at least one appearance of name,
587 followed by =. If not, insert both name and value, just after
588 first semicolon, if any. Note that name should not contain a
589 trailing =. And quotes will be added around the value. Typical
590 usage: make sure that a Content-Disposition header contains
591 filename="foo". If it doesn't and value does, use value from
594 incl_name_value (unsigned char *buf, char *name, char *value) {
597 /* Assume that name is non-null. */
599 char *name_plus_equal = concat (name, "=", NULL);
601 if (! strstr (buf, name_plus_equal)) {
604 char *prefix, *suffix;
606 /* Trim trailing space, esp. newline. */
607 for (cp = &buf[strlen (buf) - 1];
608 cp >= buf && isspace (*cp);
613 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
615 /* Insert at first semicolon, if any. If none, append to
617 prefix = add (buf, NULL);
618 if ((cp = strchr (prefix, ';'))) {
619 suffix = concat (cp, NULL);
621 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
625 newbuf = concat (buf, insertion, "\n", NULL);
633 free (name_plus_equal);
639 /* Extract just name_suffix="foo", if any, from value. If there isn't
640 one, return the entire value. Note that, for example, a name_suffix
641 of name will match filename="foo", and return foo. */
643 extract_name_value (char *name_suffix, char *value) {
644 char *extracted_name_value = value;
645 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
646 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
649 free (name_suffix_plus_quote);
650 if (name_suffix_equals) {
651 char *name_suffix_begin;
654 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
655 name_suffix_begin = ++cp;
656 /* Find second \". */
657 for (; *cp != '"'; ++cp) /* empty */;
659 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
660 memcpy (extracted_name_value, name_suffix_begin,
661 cp - name_suffix_begin);
662 extracted_name_value[cp - name_suffix_begin] = '\0';
665 return extracted_name_value;
669 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
670 * directives. Fills in the information of the CTinfo structure.
673 get_ctinfo (unsigned char *cp, CT ct, int magic)
682 i = strlen (invo_name) + 2;
684 /* store copy of Content-Type line */
685 cp = ct->c_ctline = add (cp, NULL);
687 while (isspace (*cp)) /* trim leading spaces */
690 /* change newlines to spaces */
691 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
694 /* trim trailing spaces */
695 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
701 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
703 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
706 for (dp = cp; istoken (*dp); dp++)
709 ci->ci_type = add (cp, NULL); /* store content type */
713 advise (NULL, "invalid %s: field in message %s (empty type)",
714 TYPE_FIELD, ct->c_file);
718 /* down case the content type string */
719 for (dp = ci->ci_type; *dp; dp++)
720 if (isalpha(*dp) && isupper (*dp))
723 while (isspace (*cp))
726 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
731 ci->ci_subtype = add ("", NULL);
736 while (isspace (*cp))
739 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
742 for (dp = cp; istoken (*dp); dp++)
745 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
748 if (!*ci->ci_subtype) {
750 "invalid %s: field in message %s (empty subtype for \"%s\")",
751 TYPE_FIELD, ct->c_file, ci->ci_type);
755 /* down case the content subtype string */
756 for (dp = ci->ci_subtype; *dp; dp++)
757 if (isalpha(*dp) && isupper (*dp))
761 while (isspace (*cp))
764 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
768 * Parse attribute/value pairs given with Content-Type
770 ep = (ap = ci->ci_attrs) + NPARMS;
777 "too many parameters in message %s's %s: field (%d max)",
778 ct->c_file, TYPE_FIELD, NPARMS);
783 while (isspace (*cp))
786 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
791 "extraneous trailing ';' in message %s's %s: parameter list",
792 ct->c_file, TYPE_FIELD);
796 /* down case the attribute name */
797 for (dp = cp; istoken (*dp); dp++)
798 if (isalpha(*dp) && isupper (*dp))
801 for (up = dp; isspace (*dp);)
803 if (dp == cp || *dp != '=') {
805 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
806 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
810 vp = (*ap = add (cp, NULL)) + (up - cp);
812 for (dp++; isspace (*dp);)
815 /* now add the attribute value */
816 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
819 for (cp = ++dp, dp = vp;;) {
824 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
825 ct->c_file, TYPE_FIELD, i, i, "", *ap);
830 if ((c = *cp++) == '\0')
845 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
851 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
852 ct->c_file, TYPE_FIELD, i, i, "", *ap);
857 while (isspace (*cp))
860 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
865 * Get any <Content-Id> given in buffer
867 if (magic && *cp == '<') {
872 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
873 advise (NULL, "invalid ID in message %s", ct->c_file);
879 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
885 while (isspace (*cp))
890 * Get any [Content-Description] given in buffer.
892 if (magic && *cp == '[') {
894 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
898 advise (NULL, "invalid description in message %s", ct->c_file);
906 ct->c_descr = concat (ct->c_descr, "\n", NULL);
912 while (isspace (*cp))
917 * Get any {Content-Disposition} given in buffer.
919 if (magic && *cp == '{') {
921 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
925 advise (NULL, "invalid disposition in message %s", ct->c_file);
933 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
939 while (isspace (*cp))
944 * Check if anything is left over
948 ci->ci_magic = add (cp, NULL);
951 * If there is a Content-Disposition header and
952 * it doesn't have a *filename=, extract it from
953 * the magic contents. The r1bindex call skips
954 * any leading directory components.
957 ct->c_dispo = incl_name_value (ct->c_dispo, "filename", r1bindex (extract_name_value ("name", ci->ci_magic), '/'));
960 "extraneous information in message %s's %s: field\n%*.*s(%s)",
961 ct->c_file, TYPE_FIELD, i, i, "", cp);
969 get_comment (CT ct, unsigned char **ap, int istype)
974 char c, buffer[BUFSIZ], *dp;
986 advise (NULL, "invalid comment in message %s's %s: field",
987 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
992 if ((c = *cp++) == '\0')
1015 if ((dp = ci->ci_comment)) {
1016 ci->ci_comment = concat (dp, " ", buffer, NULL);
1019 ci->ci_comment = add (buffer, NULL);
1023 while (isspace (*cp))
1034 * Handles content types audio, image, and video.
1035 * There's not much to do right here.
1041 return OK; /* not much to do here */
1052 char buffer[BUFSIZ];
1054 char **ap, **ep, *cp;
1057 CI ci = &ct->c_ctinfo;
1059 /* check for missing subtype */
1060 if (!*ci->ci_subtype)
1061 ci->ci_subtype = add ("plain", ci->ci_subtype);
1064 for (kv = SubText; kv->kv_key; kv++)
1065 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1067 ct->c_subtype = kv->kv_value;
1069 /* allocate text character set structure */
1070 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1071 adios (NULL, "out of memory");
1072 ct->c_ctparams = (void *) t;
1074 /* scan for charset parameter */
1075 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1076 if (!mh_strcasecmp (*ap, "charset"))
1079 /* check if content specified a character set */
1081 /* match character set or set to CHARSET_UNKNOWN */
1082 for (kv = Charset; kv->kv_key; kv++) {
1083 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1088 t->tx_charset = kv->kv_value;
1090 t->tx_charset = CHARSET_UNSPECIFIED;
1094 * If we can not handle character set natively,
1095 * then check profile for string to modify the
1096 * terminal or display method.
1098 * termproc is for mhshow, though mhlist -debug prints it, too.
1100 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1101 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1102 if ((cp = context_find (buffer)))
1103 ct->c_termproc = getcpy (cp);
1115 InitMultiPart (CT ct)
1119 unsigned char *cp, *dp;
1121 char *bp, buffer[BUFSIZ];
1122 struct multipart *m;
1124 struct part *part, **next;
1125 CI ci = &ct->c_ctinfo;
1130 * The encoding for multipart messages must be either
1131 * 7bit, 8bit, or binary (per RFC2045).
1133 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1134 && ct->c_encoding != CE_BINARY) {
1136 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1137 ci->ci_type, ci->ci_subtype, ct->c_file);
1142 for (kv = SubMultiPart; kv->kv_key; kv++)
1143 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1145 ct->c_subtype = kv->kv_value;
1148 * Check for "boundary" parameter, which is
1149 * required for multipart messages.
1152 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1153 if (!mh_strcasecmp (*ap, "boundary")) {
1159 /* complain if boundary parameter is missing */
1162 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1163 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1167 /* allocate primary structure for multipart info */
1168 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1169 adios (NULL, "out of memory");
1170 ct->c_ctparams = (void *) m;
1172 /* check if boundary parameter contains only whitespace characters */
1173 for (cp = bp; isspace (*cp); cp++)
1176 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1177 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1181 /* remove trailing whitespace from boundary parameter */
1182 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1187 /* record boundary separators */
1188 m->mp_start = concat (bp, "\n", NULL);
1189 m->mp_stop = concat (bp, "--\n", NULL);
1191 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1192 advise (ct->c_file, "unable to open for reading");
1196 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1198 next = &m->mp_parts;
1202 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1206 pos += strlen (buffer);
1207 if (buffer[0] != '-' || buffer[1] != '-')
1210 if (strcmp (buffer + 2, m->mp_start))
1213 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1214 adios (NULL, "out of memory");
1216 next = &part->mp_next;
1218 if (!(p = get_content (fp, ct->c_file,
1219 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1226 fseek (fp, pos, SEEK_SET);
1229 if (strcmp (buffer + 2, m->mp_start) == 0) {
1233 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1234 if (p->c_end < p->c_begin)
1235 p->c_begin = p->c_end;
1240 if (strcmp (buffer + 2, m->mp_stop) == 0)
1246 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1247 if (!inout && part) {
1249 p->c_end = ct->c_end;
1251 if (p->c_begin >= p->c_end) {
1252 for (next = &m->mp_parts; *next != part;
1253 next = &((*next)->mp_next))
1257 free ((char *) part);
1262 /* reverse the order of the parts for multipart/alternative */
1263 if (ct->c_subtype == MULTI_ALTERNATE)
1267 * label all subparts with part number, and
1268 * then initialize the content of the subpart.
1273 char partnam[BUFSIZ];
1276 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1277 pp = partnam + strlen (partnam);
1282 for (part = m->mp_parts, partnum = 1; part;
1283 part = part->mp_next, partnum++) {
1286 sprintf (pp, "%d", partnum);
1287 p->c_partno = add (partnam, NULL);
1289 /* initialize the content of the subparts */
1290 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1305 * reverse the order of the parts of a multipart
1309 reverse_parts (CT ct)
1312 struct multipart *m;
1313 struct part **base, **bmp, **next, *part;
1315 m = (struct multipart *) ct->c_ctparams;
1317 /* if only one part, just return */
1318 if (!m->mp_parts || !m->mp_parts->mp_next)
1321 /* count number of parts */
1323 for (part = m->mp_parts; part; part = part->mp_next)
1326 /* allocate array of pointers to the parts */
1327 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1328 adios (NULL, "out of memory");
1331 /* point at all the parts */
1332 for (part = m->mp_parts; part; part = part->mp_next)
1336 /* reverse the order of the parts */
1337 next = &m->mp_parts;
1338 for (bmp--; bmp >= base; bmp--) {
1341 next = &part->mp_next;
1345 /* free array of pointers */
1346 free ((char *) base);
1358 CI ci = &ct->c_ctinfo;
1360 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1362 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1363 ci->ci_type, ci->ci_subtype, ct->c_file);
1367 /* check for missing subtype */
1368 if (!*ci->ci_subtype)
1369 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1372 for (kv = SubMessage; kv->kv_key; kv++)
1373 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1375 ct->c_subtype = kv->kv_value;
1377 switch (ct->c_subtype) {
1378 case MESSAGE_RFC822:
1381 case MESSAGE_PARTIAL:
1386 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1387 adios (NULL, "out of memory");
1388 ct->c_ctparams = (void *) p;
1390 /* scan for parameters "id", "number", and "total" */
1391 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1392 if (!mh_strcasecmp (*ap, "id")) {
1393 p->pm_partid = add (*ep, NULL);
1396 if (!mh_strcasecmp (*ap, "number")) {
1397 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1398 || p->pm_partno < 1) {
1401 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1402 *ap, ci->ci_type, ci->ci_subtype,
1403 ct->c_file, TYPE_FIELD);
1408 if (!mh_strcasecmp (*ap, "total")) {
1409 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1416 if (!p->pm_partid || !p->pm_partno
1417 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1419 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1420 ci->ci_type, ci->ci_subtype,
1421 ct->c_file, TYPE_FIELD);
1427 case MESSAGE_EXTERNAL:
1434 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1435 adios (NULL, "out of memory");
1436 ct->c_ctparams = (void *) e;
1438 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1439 advise (ct->c_file, "unable to open for reading");
1443 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1445 if (!(p = get_content (fp, ct->c_file, 0))) {
1453 if ((exresult = params_external (ct, 0)) != NOTOK
1454 && p->c_ceopenfnx == openMail) {
1458 if ((size = ct->c_end - p->c_begin) <= 0) {
1460 content_error (NULL, ct, "empty body for access-type=mail-server");
1464 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1465 fseek (p->c_fp, p->c_begin, SEEK_SET);
1467 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1469 adios ("failed", "fread");
1472 adios (NULL, "unexpected EOF from fread");
1475 bp += cc, size -= cc;
1482 p->c_end = p->c_begin;
1487 if (exresult == NOTOK)
1489 if (e->eb_flags == NOTOK)
1492 switch (p->c_type) {
1497 if (p->c_subtype != MESSAGE_RFC822)
1501 e->eb_partno = ct->c_partno;
1503 (*p->c_ctinitfnx) (p);
1518 params_external (CT ct, int composing)
1521 struct exbody *e = (struct exbody *) ct->c_ctparams;
1522 CI ci = &ct->c_ctinfo;
1524 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1525 if (!mh_strcasecmp (*ap, "access-type")) {
1526 struct str2init *s2i;
1527 CT p = e->eb_content;
1529 for (s2i = str2methods; s2i->si_key; s2i++)
1530 if (!mh_strcasecmp (*ep, s2i->si_key))
1534 e->eb_flags = NOTOK;
1535 p->c_encoding = CE_EXTERNAL;
1538 e->eb_access = s2i->si_key;
1539 e->eb_flags = s2i->si_val;
1540 p->c_encoding = CE_EXTERNAL;
1542 /* Call the Init function for this external type */
1543 if ((*s2i->si_init)(p) == NOTOK)
1547 if (!mh_strcasecmp (*ap, "name")) {
1551 if (!mh_strcasecmp (*ap, "permission")) {
1552 e->eb_permission = *ep;
1555 if (!mh_strcasecmp (*ap, "site")) {
1559 if (!mh_strcasecmp (*ap, "directory")) {
1563 if (!mh_strcasecmp (*ap, "mode")) {
1567 if (!mh_strcasecmp (*ap, "size")) {
1568 sscanf (*ep, "%lu", &e->eb_size);
1571 if (!mh_strcasecmp (*ap, "server")) {
1575 if (!mh_strcasecmp (*ap, "subject")) {
1576 e->eb_subject = *ep;
1579 if (composing && !mh_strcasecmp (*ap, "body")) {
1580 e->eb_body = getcpy (*ep);
1585 if (!e->eb_access) {
1587 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1588 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1601 InitApplication (CT ct)
1604 CI ci = &ct->c_ctinfo;
1607 for (kv = SubApplication; kv->kv_key; kv++)
1608 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1610 ct->c_subtype = kv->kv_value;
1617 * TRANSFER ENCODINGS
1621 init_encoding (CT ct, OpenCEFunc openfnx)
1625 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1626 adios (NULL, "out of memory");
1629 ct->c_ceopenfnx = openfnx;
1630 ct->c_ceclosefnx = close_encoding;
1631 ct->c_cesizefnx = size_encoding;
1638 close_encoding (CT ct)
1642 if (!(ce = ct->c_cefile))
1652 static unsigned long
1653 size_encoding (CT ct)
1661 if (!(ce = ct->c_cefile))
1662 return (ct->c_end - ct->c_begin);
1664 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1665 return (long) st.st_size;
1668 if (stat (ce->ce_file, &st) != NOTOK)
1669 return (long) st.st_size;
1674 if (ct->c_encoding == CE_EXTERNAL)
1675 return (ct->c_end - ct->c_begin);
1678 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1679 return (ct->c_end - ct->c_begin);
1681 if (fstat (fd, &st) != NOTOK)
1682 size = (long) st.st_size;
1686 (*ct->c_ceclosefnx) (ct);
1695 static unsigned char b642nib[0x80] = {
1696 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1697 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1698 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1699 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1702 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1703 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1705 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1706 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1707 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1709 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1710 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1711 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1718 return init_encoding (ct, openBase64);
1723 openBase64 (CT ct, char **file)
1725 int bitno, cc, digested;
1728 unsigned char value, *b, *b1, *b2, *b3;
1729 unsigned char *cp, *ep;
1730 char buffer[BUFSIZ];
1731 /* sbeck -- handle suffixes */
1736 b = (unsigned char *) &bits;
1737 b1 = &b[endian > 0 ? 1 : 2];
1738 b2 = &b[endian > 0 ? 2 : 1];
1739 b3 = &b[endian > 0 ? 3 : 0];
1743 fseek (ce->ce_fp, 0L, SEEK_SET);
1748 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1749 content_error (ce->ce_file, ct, "unable to fopen for reading");
1755 if (*file == NULL) {
1756 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1759 ce->ce_file = add (*file, NULL);
1763 /* sbeck@cise.ufl.edu -- handle suffixes */
1765 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1766 invo_name, ci->ci_type, ci->ci_subtype);
1767 cp = context_find (buffer);
1768 if (cp == NULL || *cp == '\0') {
1769 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1771 cp = context_find (buffer);
1773 if (cp != NULL && *cp != '\0') {
1774 if (ce->ce_unlink) {
1775 // Temporary file already exists, so we rename to
1776 // version with extension.
1777 char *file_org = strdup(ce->ce_file);
1778 ce->ce_file = add (cp, ce->ce_file);
1779 if (rename(file_org, ce->ce_file)) {
1780 adios (ce->ce_file, "unable to rename %s to ", file_org);
1785 ce->ce_file = add (cp, ce->ce_file);
1789 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1790 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1794 if ((len = ct->c_end - ct->c_begin) < 0)
1795 adios (NULL, "internal error(1)");
1797 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1798 content_error (ct->c_file, ct, "unable to open for reading");
1802 if ((digested = ct->c_digested))
1803 MD5Init (&mdContext);
1809 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1811 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1813 content_error (ct->c_file, ct, "error reading from");
1817 content_error (NULL, ct, "premature eof");
1825 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1830 if (skip || (*cp & 0x80)
1831 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1833 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1835 content_error (NULL, ct,
1836 "invalid BASE64 encoding -- continuing");
1840 bits |= value << bitno;
1842 if ((bitno -= 6) < 0) {
1843 putc ((char) *b1, ce->ce_fp);
1845 MD5Update (&mdContext, b1, 1);
1847 putc ((char) *b2, ce->ce_fp);
1849 MD5Update (&mdContext, b2, 1);
1851 putc ((char) *b3, ce->ce_fp);
1853 MD5Update (&mdContext, b3, 1);
1857 if (ferror (ce->ce_fp)) {
1858 content_error (ce->ce_file, ct,
1859 "error writing to");
1862 bitno = 18, bits = 0L, skip = 0;
1868 goto self_delimiting;
1877 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1879 content_error (NULL, ct, "invalid BASE64 encoding");
1884 fseek (ct->c_fp, 0L, SEEK_SET);
1886 if (fflush (ce->ce_fp)) {
1887 content_error (ce->ce_file, ct, "error writing to");
1892 unsigned char digest[16];
1894 MD5Final (digest, &mdContext);
1895 if (memcmp((char *) digest, (char *) ct->c_digest,
1896 sizeof(digest) / sizeof(digest[0])))
1897 content_error (NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1899 fprintf (stderr, "content integrity confirmed\n");
1902 fseek (ce->ce_fp, 0L, SEEK_SET);
1905 *file = ce->ce_file;
1906 return fileno (ce->ce_fp);
1909 free_encoding (ct, 0);
1918 static char hex2nib[0x80] = {
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1925 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1926 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1927 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1941 return init_encoding (ct, openQuoted);
1946 openQuoted (CT ct, char **file)
1948 int cc, digested, len, quoted;
1949 unsigned char *cp, *ep;
1950 char buffer[BUFSIZ];
1953 /* sbeck -- handle suffixes */
1959 fseek (ce->ce_fp, 0L, SEEK_SET);
1964 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1965 content_error (ce->ce_file, ct, "unable to fopen for reading");
1971 if (*file == NULL) {
1972 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1975 ce->ce_file = add (*file, NULL);
1979 /* sbeck@cise.ufl.edu -- handle suffixes */
1981 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1982 invo_name, ci->ci_type, ci->ci_subtype);
1983 cp = context_find (buffer);
1984 if (cp == NULL || *cp == '\0') {
1985 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1987 cp = context_find (buffer);
1989 if (cp != NULL && *cp != '\0') {
1990 if (ce->ce_unlink) {
1991 // Temporary file already exists, so we rename to
1992 // version with extension.
1993 char *file_org = strdup(ce->ce_file);
1994 ce->ce_file = add (cp, ce->ce_file);
1995 if (rename(file_org, ce->ce_file)) {
1996 adios (ce->ce_file, "unable to rename %s to ", file_org);
2001 ce->ce_file = add (cp, ce->ce_file);
2005 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2006 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2010 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2011 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2015 if ((len = ct->c_end - ct->c_begin) < 0)
2016 adios (NULL, "internal error(2)");
2018 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2019 content_error (ct->c_file, ct, "unable to open for reading");
2023 if ((digested = ct->c_digested))
2024 MD5Init (&mdContext);
2031 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2033 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2034 content_error (NULL, ct, "premature eof");
2038 if ((cc = strlen (buffer)) > len)
2042 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2047 for (; cp < ep; cp++) {
2049 /* in an escape sequence */
2051 /* at byte 1 of an escape sequence */
2052 mask = hex2nib[*cp & 0x7f];
2053 /* next is byte 2 */
2056 /* at byte 2 of an escape sequence */
2058 mask |= hex2nib[*cp & 0x7f];
2059 putc (mask, ce->ce_fp);
2061 MD5Update (&mdContext, &mask, 1);
2062 if (ferror (ce->ce_fp)) {
2063 content_error (ce->ce_file, ct, "error writing to");
2066 /* finished escape sequence; next may
2067 * be literal or a new escape
2071 /* on to next byte */
2075 /* not in an escape sequence */
2077 /* starting an escape sequence, or invalid '='? */
2078 if (cp + 1 < ep && cp[1] == '\n') {
2079 /* "=\n" soft line break, eat the \n */
2083 if (cp + 1 >= ep || cp + 2 >= ep) {
2084 /* We don't have 2 bytes left, so this is an invalid
2085 * escape sequence; just show the raw bytes (below). */
2086 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2087 /* Next 2 bytes are hex digits, making this a valid escape
2088 * sequence; let's decode it (above). */
2092 /* One or both of the next 2 is out of range, making this
2093 * an invalid escape sequence; just show the raw bytes
2098 /* Just show the raw byte. */
2099 putc (*cp, ce->ce_fp);
2102 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2104 MD5Update (&mdContext, (unsigned char *) cp, 1);
2107 if (ferror (ce->ce_fp)) {
2108 content_error (ce->ce_file, ct, "error writing to");
2114 content_error (NULL, ct,
2115 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2119 fseek (ct->c_fp, 0L, SEEK_SET);
2121 if (fflush (ce->ce_fp)) {
2122 content_error (ce->ce_file, ct, "error writing to");
2127 unsigned char digest[16];
2129 MD5Final (digest, &mdContext);
2130 if (memcmp((char *) digest, (char *) ct->c_digest,
2131 sizeof(digest) / sizeof(digest[0])))
2132 content_error (NULL, ct,
2133 "content integrity suspect (digest mismatch) -- continuing");
2135 fprintf (stderr, "content integrity confirmed\n");
2138 fseek (ce->ce_fp, 0L, SEEK_SET);
2141 *file = ce->ce_file;
2142 return fileno (ce->ce_fp);
2145 free_encoding (ct, 0);
2157 if (init_encoding (ct, open7Bit) == NOTOK)
2160 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2166 open7Bit (CT ct, char **file)
2169 char buffer[BUFSIZ];
2170 /* sbeck -- handle suffixes */
2177 fseek (ce->ce_fp, 0L, SEEK_SET);
2182 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2183 content_error (ce->ce_file, ct, "unable to fopen for reading");
2189 if (*file == NULL) {
2190 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2193 ce->ce_file = add (*file, NULL);
2197 /* sbeck@cise.ufl.edu -- handle suffixes */
2199 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2200 invo_name, ci->ci_type, ci->ci_subtype);
2201 cp = context_find (buffer);
2202 if (cp == NULL || *cp == '\0') {
2203 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2205 cp = context_find (buffer);
2207 if (cp != NULL && *cp != '\0') {
2208 if (ce->ce_unlink) {
2209 // Temporary file already exists, so we rename to
2210 // version with extension.
2211 char *file_org = strdup(ce->ce_file);
2212 ce->ce_file = add (cp, ce->ce_file);
2213 if (rename(file_org, ce->ce_file)) {
2214 adios (ce->ce_file, "unable to rename %s to ", file_org);
2219 ce->ce_file = add (cp, ce->ce_file);
2223 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2224 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2228 if (ct->c_type == CT_MULTIPART) {
2230 CI ci = &ct->c_ctinfo;
2233 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2234 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2235 + 1 + strlen (ci->ci_subtype);
2236 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2237 putc (';', ce->ce_fp);
2240 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2242 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2243 fputs ("\n\t", ce->ce_fp);
2246 putc (' ', ce->ce_fp);
2249 fprintf (ce->ce_fp, "%s", buffer);
2253 if (ci->ci_comment) {
2254 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2255 fputs ("\n\t", ce->ce_fp);
2259 putc (' ', ce->ce_fp);
2262 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2265 fprintf (ce->ce_fp, "\n");
2267 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2269 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2271 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2272 fprintf (ce->ce_fp, "\n");
2275 if ((len = ct->c_end - ct->c_begin) < 0)
2276 adios (NULL, "internal error(3)");
2278 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2279 content_error (ct->c_file, ct, "unable to open for reading");
2283 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2285 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2287 content_error (ct->c_file, ct, "error reading from");
2291 content_error (NULL, ct, "premature eof");
2299 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2300 if (ferror (ce->ce_fp)) {
2301 content_error (ce->ce_file, ct, "error writing to");
2306 fseek (ct->c_fp, 0L, SEEK_SET);
2308 if (fflush (ce->ce_fp)) {
2309 content_error (ce->ce_file, ct, "error writing to");
2313 fseek (ce->ce_fp, 0L, SEEK_SET);
2316 *file = ce->ce_file;
2317 return fileno (ce->ce_fp);
2320 free_encoding (ct, 0);
2330 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2332 char cachefile[BUFSIZ];
2335 fseek (ce->ce_fp, 0L, SEEK_SET);
2340 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2341 content_error (ce->ce_file, ct, "unable to fopen for reading");
2347 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2348 cachefile, sizeof(cachefile)) != NOTOK) {
2349 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2350 ce->ce_file = getcpy (cachefile);
2354 admonish (cachefile, "unable to fopen for reading");
2361 *file = ce->ce_file;
2362 *fd = fileno (ce->ce_fp);
2373 return init_encoding (ct, openFile);
2378 openFile (CT ct, char **file)
2381 char cachefile[BUFSIZ];
2382 struct exbody *e = ct->c_ctexbody;
2383 CE ce = ct->c_cefile;
2385 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2397 content_error (NULL, ct, "missing name parameter");
2401 ce->ce_file = getcpy (e->eb_name);
2404 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2405 content_error (ce->ce_file, ct, "unable to fopen for reading");
2409 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2410 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2411 cachefile, sizeof(cachefile)) != NOTOK) {
2415 mask = umask (cachetype ? ~m_gmprot () : 0222);
2416 if ((fp = fopen (cachefile, "w"))) {
2418 char buffer[BUFSIZ];
2419 FILE *gp = ce->ce_fp;
2421 fseek (gp, 0L, SEEK_SET);
2423 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0)
2424 fwrite (buffer, sizeof(*buffer), cc, fp);
2428 admonish (ce->ce_file, "error reading");
2430 } else if (ferror (fp)) {
2431 admonish (cachefile, "error writing");
2439 fseek (ce->ce_fp, 0L, SEEK_SET);
2440 *file = ce->ce_file;
2441 return fileno (ce->ce_fp);
2451 return init_encoding (ct, openFTP);
2456 openFTP (CT ct, char **file)
2458 int cachetype, caching, fd;
2460 char *bp, *ftp, *user, *pass;
2461 char buffer[BUFSIZ], cachefile[BUFSIZ];
2464 static char *username = NULL;
2465 static char *password = NULL;
2470 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2478 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2489 if (!e->eb_name || !e->eb_site) {
2490 content_error (NULL, ct, "missing %s parameter",
2491 e->eb_name ? "site": "name");
2498 pidcheck (pidwait (xpid, NOTOK));
2502 /* Get the buffer ready to go */
2504 buflen = sizeof(buffer);
2507 * Construct the query message for user
2509 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2515 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2521 snprintf (bp, buflen, "\n using %sFTP from site %s",
2522 e->eb_flags ? "anonymous " : "", e->eb_site);
2527 if (e->eb_size > 0) {
2528 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2533 snprintf (bp, buflen, "? ");
2536 * Now, check the answer
2538 if (!getanswer (buffer))
2543 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2546 ruserpass (e->eb_site, &username, &password);
2551 ce->ce_unlink = (*file == NULL);
2553 cachefile[0] = '\0';
2554 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2555 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2556 cachefile, sizeof(cachefile)) != NOTOK) {
2557 if (*file == NULL) {
2564 ce->ce_file = add (*file, NULL);
2566 ce->ce_file = add (cachefile, NULL);
2568 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2570 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2571 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2579 int child_id, i, vecp;
2583 vec[vecp++] = r1bindex (ftp, '/');
2584 vec[vecp++] = e->eb_site;
2587 vec[vecp++] = e->eb_dir;
2588 vec[vecp++] = e->eb_name;
2589 vec[vecp++] = ce->ce_file,
2590 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2591 ? "ascii" : "binary";
2596 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2600 adios ("fork", "unable to");
2604 close (fileno (ce->ce_fp));
2606 fprintf (stderr, "unable to exec ");
2612 if (pidXwait (child_id, NULL)) {
2616 username = password = NULL;
2624 else if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2625 ce->ce_file, e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2632 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2637 mask = umask (cachetype ? ~m_gmprot () : 0222);
2638 if ((fp = fopen (cachefile, "w"))) {
2640 FILE *gp = ce->ce_fp;
2642 fseek (gp, 0L, SEEK_SET);
2644 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0)
2645 fwrite (buffer, sizeof(*buffer), cc, fp);
2649 admonish (ce->ce_file, "error reading");
2651 } else if (ferror (fp)) {
2652 admonish (cachefile, "error writing");
2661 fseek (ce->ce_fp, 0L, SEEK_SET);
2662 *file = ce->ce_file;
2663 return fileno (ce->ce_fp);
2674 return init_encoding (ct, openMail);
2679 openMail (CT ct, char **file)
2681 int child_id, fd, i, vecp;
2683 char *bp, buffer[BUFSIZ], *vec[7];
2684 struct exbody *e = ct->c_ctexbody;
2685 CE ce = ct->c_cefile;
2687 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2698 if (!e->eb_server) {
2699 content_error (NULL, ct, "missing server parameter");
2706 pidcheck (pidwait (xpid, NOTOK));
2710 /* Get buffer ready to go */
2712 buflen = sizeof(buffer);
2714 /* Now, construct query message */
2715 snprintf (bp, buflen, "Retrieve content");
2721 snprintf (bp, buflen, " %s", e->eb_partno);
2727 snprintf (bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2728 e->eb_subject ? e->eb_subject : e->eb_body);
2730 /* Now, check answer */
2731 if (!getanswer (buffer))
2735 vec[vecp++] = r1bindex (mailproc, '/');
2736 vec[vecp++] = e->eb_server;
2737 vec[vecp++] = "-subject";
2738 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2739 vec[vecp++] = "-body";
2740 vec[vecp++] = e->eb_body;
2743 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2747 advise ("fork", "unable to");
2751 execvp (mailproc, vec);
2752 fprintf (stderr, "unable to exec ");
2758 if (pidXwait (child_id, NULL) == OK)
2759 advise (NULL, "request sent");
2763 if (*file == NULL) {
2764 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2767 ce->ce_file = add (*file, NULL);
2771 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2772 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2776 /* showproc is for mhshow and mhstore, though mhlist -debug
2777 * prints it, too. */
2779 free (ct->c_showproc);
2780 ct->c_showproc = add ("true", NULL);
2782 fseek (ce->ce_fp, 0L, SEEK_SET);
2783 *file = ce->ce_file;
2784 return fileno (ce->ce_fp);
2789 readDigest (CT ct, char *cp)
2794 unsigned char *dp, value, *ep;
2795 unsigned char *b, *b1, *b2, *b3;
2797 b = (unsigned char *) &bits,
2798 b1 = &b[endian > 0 ? 1 : 2],
2799 b2 = &b[endian > 0 ? 2 : 1],
2800 b3 = &b[endian > 0 ? 3 : 0];
2805 for (ep = (dp = ct->c_digest)
2806 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2809 if (skip || (*cp & 0x80)
2810 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2812 fprintf (stderr, "invalid BASE64 encoding\n");
2816 bits |= value << bitno;
2818 if ((bitno -= 6) < 0) {
2819 if (dp + (3 - skip) > ep)
2820 goto invalid_digest;
2835 goto self_delimiting;
2840 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2850 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2858 fprintf (stderr, "MD5 digest=");
2859 for (dp = ct->c_digest; dp < ep; dp++)
2860 fprintf (stderr, "%02x", *dp & 0xff);
2861 fprintf (stderr, "\n");