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 */
587 ** Make sure that buf contains at least one appearance of name,
588 ** followed by =. If not, insert both name and value, just after
589 ** first semicolon, if any. Note that name should not contain a
590 ** trailing =. And quotes will be added around the value. Typical
591 ** usage: make sure that a Content-Disposition header contains
592 ** filename="foo". If it doesn't and value does, use value from
596 incl_name_value (unsigned char *buf, char *name, char *value) {
599 /* Assume that name is non-null. */
601 char *name_plus_equal = concat (name, "=", NULL);
603 if (! strstr (buf, name_plus_equal)) {
606 char *prefix, *suffix;
608 /* Trim trailing space, esp. newline. */
609 for (cp = &buf[strlen (buf) - 1];
610 cp >= buf && isspace (*cp);
615 insertion = concat ("; ", name, "=", "\"", value, "\"",
619 ** Insert at first semicolon, if any.
620 ** If none, append to end.
622 prefix = add (buf, NULL);
623 if ((cp = strchr (prefix, ';'))) {
624 suffix = concat (cp, NULL);
626 newbuf = concat (prefix, insertion, suffix,
631 newbuf = concat (buf, insertion, "\n", NULL);
639 free (name_plus_equal);
646 ** Extract just name_suffix="foo", if any, from value. If there isn't
647 ** one, return the entire value. Note that, for example, a name_suffix
648 ** of name will match filename="foo", and return foo.
651 extract_name_value (char *name_suffix, char *value) {
652 char *extracted_name_value = value;
653 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
654 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
657 free (name_suffix_plus_quote);
658 if (name_suffix_equals) {
659 char *name_suffix_begin;
662 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
663 name_suffix_begin = ++cp;
664 /* Find second \". */
665 for (; *cp != '"'; ++cp) /* empty */;
667 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
668 memcpy (extracted_name_value, name_suffix_begin,
669 cp - name_suffix_begin);
670 extracted_name_value[cp - name_suffix_begin] = '\0';
673 return extracted_name_value;
677 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
678 ** directives. Fills in the information of the CTinfo structure.
681 get_ctinfo (unsigned char *cp, CT ct, int magic)
690 i = strlen (invo_name) + 2;
692 /* store copy of Content-Type line */
693 cp = ct->c_ctline = add (cp, NULL);
695 while (isspace (*cp)) /* trim leading spaces */
698 /* change newlines to spaces */
699 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
702 /* trim trailing spaces */
703 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
709 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
711 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
714 for (dp = cp; istoken (*dp); dp++)
717 ci->ci_type = add (cp, NULL); /* store content type */
721 advise (NULL, "invalid %s: field in message %s (empty type)",
722 TYPE_FIELD, ct->c_file);
726 /* down case the content type string */
727 for (dp = ci->ci_type; *dp; dp++)
728 if (isalpha(*dp) && isupper (*dp))
731 while (isspace (*cp))
734 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
739 ci->ci_subtype = add ("", NULL);
744 while (isspace (*cp))
747 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
750 for (dp = cp; istoken (*dp); dp++)
753 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
756 if (!*ci->ci_subtype) {
758 "invalid %s: field in message %s (empty subtype for \"%s\")",
759 TYPE_FIELD, ct->c_file, ci->ci_type);
763 /* down case the content subtype string */
764 for (dp = ci->ci_subtype; *dp; dp++)
765 if (isalpha(*dp) && isupper (*dp))
769 while (isspace (*cp))
772 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
776 ** Parse attribute/value pairs given with Content-Type
778 ep = (ap = ci->ci_attrs) + NPARMS;
785 "too many parameters in message %s's %s: field (%d max)",
786 ct->c_file, TYPE_FIELD, NPARMS);
791 while (isspace (*cp))
794 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
799 "extraneous trailing ';' in message %s's %s: parameter list",
800 ct->c_file, TYPE_FIELD);
804 /* down case the attribute name */
805 for (dp = cp; istoken (*dp); dp++)
806 if (isalpha(*dp) && isupper (*dp))
809 for (up = dp; isspace (*dp);)
811 if (dp == cp || *dp != '=') {
813 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
814 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
818 vp = (*ap = add (cp, NULL)) + (up - cp);
820 for (dp++; isspace (*dp);)
823 /* now add the attribute value */
824 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
827 for (cp = ++dp, dp = vp;;) {
832 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
833 ct->c_file, TYPE_FIELD, i, i, "", *ap);
838 if ((c = *cp++) == '\0')
853 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
859 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
860 ct->c_file, TYPE_FIELD, i, i, "", *ap);
865 while (isspace (*cp))
868 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
873 ** Get any <Content-Id> given in buffer
875 if (magic && *cp == '<') {
880 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
881 advise (NULL, "invalid ID in message %s", ct->c_file);
887 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
893 while (isspace (*cp))
898 ** Get any [Content-Description] given in buffer.
900 if (magic && *cp == '[') {
902 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
906 advise (NULL, "invalid description in message %s", ct->c_file);
914 ct->c_descr = concat (ct->c_descr, "\n", NULL);
920 while (isspace (*cp))
925 ** Get any {Content-Disposition} given in buffer.
927 if (magic && *cp == '{') {
929 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
933 advise (NULL, "invalid disposition in message %s", ct->c_file);
941 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
947 while (isspace (*cp))
952 ** Check if anything is left over
956 ci->ci_magic = add (cp, NULL);
959 ** If there is a Content-Disposition header and
960 ** it doesn't have a *filename=, extract it from
961 ** the magic contents. The r1bindex call skips
962 ** any leading directory components.
965 ct->c_dispo = incl_name_value (ct->c_dispo, "filename", r1bindex (extract_name_value ("name", ci->ci_magic), '/'));
968 "extraneous information in message %s's %s: field\n%*.*s(%s)",
969 ct->c_file, TYPE_FIELD, i, i, "", cp);
977 get_comment (CT ct, unsigned char **ap, int istype)
982 char c, buffer[BUFSIZ], *dp;
994 advise (NULL, "invalid comment in message %s's %s: field",
995 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1000 if ((c = *cp++) == '\0')
1023 if ((dp = ci->ci_comment)) {
1024 ci->ci_comment = concat (dp, " ", buffer, NULL);
1027 ci->ci_comment = add (buffer, NULL);
1031 while (isspace (*cp))
1042 ** Handles content types audio, image, and video.
1043 ** There's not much to do right here.
1049 return OK; /* not much to do here */
1060 char buffer[BUFSIZ];
1062 char **ap, **ep, *cp;
1065 CI ci = &ct->c_ctinfo;
1067 /* check for missing subtype */
1068 if (!*ci->ci_subtype)
1069 ci->ci_subtype = add ("plain", ci->ci_subtype);
1072 for (kv = SubText; kv->kv_key; kv++)
1073 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1075 ct->c_subtype = kv->kv_value;
1077 /* allocate text character set structure */
1078 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1079 adios (NULL, "out of memory");
1080 ct->c_ctparams = (void *) t;
1082 /* scan for charset parameter */
1083 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1084 if (!mh_strcasecmp (*ap, "charset"))
1087 /* check if content specified a character set */
1089 /* match character set or set to CHARSET_UNKNOWN */
1090 for (kv = Charset; kv->kv_key; kv++) {
1091 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1096 t->tx_charset = kv->kv_value;
1098 t->tx_charset = CHARSET_UNSPECIFIED;
1102 ** If we can not handle character set natively,
1103 ** then check profile for string to modify the
1104 ** terminal or display method.
1106 ** termproc is for mhshow, though mhlist -debug prints it, too.
1108 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1109 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1110 if ((cp = context_find (buffer)))
1111 ct->c_termproc = getcpy (cp);
1123 InitMultiPart (CT ct)
1127 unsigned char *cp, *dp;
1129 char *bp, buffer[BUFSIZ];
1130 struct multipart *m;
1132 struct part *part, **next;
1133 CI ci = &ct->c_ctinfo;
1138 ** The encoding for multipart messages must be either
1139 ** 7bit, 8bit, or binary (per RFC2045).
1141 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1142 && ct->c_encoding != CE_BINARY) {
1144 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1145 ci->ci_type, ci->ci_subtype, ct->c_file);
1150 for (kv = SubMultiPart; kv->kv_key; kv++)
1151 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1153 ct->c_subtype = kv->kv_value;
1156 ** Check for "boundary" parameter, which is
1157 ** required for multipart messages.
1160 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1161 if (!mh_strcasecmp (*ap, "boundary")) {
1167 /* complain if boundary parameter is missing */
1170 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1171 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1175 /* allocate primary structure for multipart info */
1176 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1177 adios (NULL, "out of memory");
1178 ct->c_ctparams = (void *) m;
1180 /* check if boundary parameter contains only whitespace characters */
1181 for (cp = bp; isspace (*cp); cp++)
1184 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1185 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1189 /* remove trailing whitespace from boundary parameter */
1190 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1195 /* record boundary separators */
1196 m->mp_start = concat (bp, "\n", NULL);
1197 m->mp_stop = concat (bp, "--\n", NULL);
1199 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1200 advise (ct->c_file, "unable to open for reading");
1204 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1206 next = &m->mp_parts;
1210 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1214 pos += strlen (buffer);
1215 if (buffer[0] != '-' || buffer[1] != '-')
1218 if (strcmp (buffer + 2, m->mp_start))
1221 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1222 adios (NULL, "out of memory");
1224 next = &part->mp_next;
1226 if (!(p = get_content (fp, ct->c_file,
1227 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1234 fseek (fp, pos, SEEK_SET);
1237 if (strcmp (buffer + 2, m->mp_start) == 0) {
1241 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1242 if (p->c_end < p->c_begin)
1243 p->c_begin = p->c_end;
1248 if (strcmp (buffer + 2, m->mp_stop) == 0)
1254 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1255 if (!inout && part) {
1257 p->c_end = ct->c_end;
1259 if (p->c_begin >= p->c_end) {
1260 for (next = &m->mp_parts; *next != part;
1261 next = &((*next)->mp_next))
1265 free ((char *) part);
1270 /* reverse the order of the parts for multipart/alternative */
1271 if (ct->c_subtype == MULTI_ALTERNATE)
1275 ** label all subparts with part number, and
1276 ** then initialize the content of the subpart.
1281 char partnam[BUFSIZ];
1284 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1285 pp = partnam + strlen (partnam);
1290 for (part = m->mp_parts, partnum = 1; part;
1291 part = part->mp_next, partnum++) {
1294 sprintf (pp, "%d", partnum);
1295 p->c_partno = add (partnam, NULL);
1297 /* initialize the content of the subparts */
1298 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1313 ** reverse the order of the parts of a multipart
1317 reverse_parts (CT ct)
1320 struct multipart *m;
1321 struct part **base, **bmp, **next, *part;
1323 m = (struct multipart *) ct->c_ctparams;
1325 /* if only one part, just return */
1326 if (!m->mp_parts || !m->mp_parts->mp_next)
1329 /* count number of parts */
1331 for (part = m->mp_parts; part; part = part->mp_next)
1334 /* allocate array of pointers to the parts */
1335 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1336 adios (NULL, "out of memory");
1339 /* point at all the parts */
1340 for (part = m->mp_parts; part; part = part->mp_next)
1344 /* reverse the order of the parts */
1345 next = &m->mp_parts;
1346 for (bmp--; bmp >= base; bmp--) {
1349 next = &part->mp_next;
1353 /* free array of pointers */
1354 free ((char *) base);
1366 CI ci = &ct->c_ctinfo;
1368 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1370 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1371 ci->ci_type, ci->ci_subtype, ct->c_file);
1375 /* check for missing subtype */
1376 if (!*ci->ci_subtype)
1377 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1380 for (kv = SubMessage; kv->kv_key; kv++)
1381 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1383 ct->c_subtype = kv->kv_value;
1385 switch (ct->c_subtype) {
1386 case MESSAGE_RFC822:
1389 case MESSAGE_PARTIAL:
1394 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1395 adios (NULL, "out of memory");
1396 ct->c_ctparams = (void *) p;
1399 ** scan for parameters "id", "number",
1402 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1403 if (!mh_strcasecmp (*ap, "id")) {
1404 p->pm_partid = add (*ep, NULL);
1407 if (!mh_strcasecmp (*ap, "number")) {
1408 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1409 || p->pm_partno < 1) {
1412 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1413 *ap, ci->ci_type, ci->ci_subtype,
1414 ct->c_file, TYPE_FIELD);
1419 if (!mh_strcasecmp (*ap, "total")) {
1420 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1427 if (!p->pm_partid || !p->pm_partno
1428 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1430 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1431 ci->ci_type, ci->ci_subtype,
1432 ct->c_file, TYPE_FIELD);
1438 case MESSAGE_EXTERNAL:
1445 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1446 adios (NULL, "out of memory");
1447 ct->c_ctparams = (void *) e;
1449 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1450 advise (ct->c_file, "unable to open for reading");
1454 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1456 if (!(p = get_content (fp, ct->c_file, 0))) {
1464 if ((exresult = params_external (ct, 0)) != NOTOK
1465 && p->c_ceopenfnx == openMail) {
1469 if ((size = ct->c_end - p->c_begin) <= 0) {
1471 content_error (NULL, ct, "empty body for access-type=mail-server");
1475 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1476 fseek (p->c_fp, p->c_begin, SEEK_SET);
1478 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1480 adios ("failed", "fread");
1483 adios (NULL, "unexpected EOF from fread");
1486 bp += cc, size -= cc;
1493 p->c_end = p->c_begin;
1498 if (exresult == NOTOK)
1500 if (e->eb_flags == NOTOK)
1503 switch (p->c_type) {
1508 if (p->c_subtype != MESSAGE_RFC822)
1512 e->eb_partno = ct->c_partno;
1514 (*p->c_ctinitfnx) (p);
1529 params_external (CT ct, int composing)
1532 struct exbody *e = (struct exbody *) ct->c_ctparams;
1533 CI ci = &ct->c_ctinfo;
1535 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1536 if (!mh_strcasecmp (*ap, "access-type")) {
1537 struct str2init *s2i;
1538 CT p = e->eb_content;
1540 for (s2i = str2methods; s2i->si_key; s2i++)
1541 if (!mh_strcasecmp (*ep, s2i->si_key))
1545 e->eb_flags = NOTOK;
1546 p->c_encoding = CE_EXTERNAL;
1549 e->eb_access = s2i->si_key;
1550 e->eb_flags = s2i->si_val;
1551 p->c_encoding = CE_EXTERNAL;
1553 /* Call the Init function for this external type */
1554 if ((*s2i->si_init)(p) == NOTOK)
1558 if (!mh_strcasecmp (*ap, "name")) {
1562 if (!mh_strcasecmp (*ap, "permission")) {
1563 e->eb_permission = *ep;
1566 if (!mh_strcasecmp (*ap, "site")) {
1570 if (!mh_strcasecmp (*ap, "directory")) {
1574 if (!mh_strcasecmp (*ap, "mode")) {
1578 if (!mh_strcasecmp (*ap, "size")) {
1579 sscanf (*ep, "%lu", &e->eb_size);
1582 if (!mh_strcasecmp (*ap, "server")) {
1586 if (!mh_strcasecmp (*ap, "subject")) {
1587 e->eb_subject = *ep;
1590 if (composing && !mh_strcasecmp (*ap, "body")) {
1591 e->eb_body = getcpy (*ep);
1596 if (!e->eb_access) {
1598 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1599 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1612 InitApplication (CT ct)
1615 CI ci = &ct->c_ctinfo;
1618 for (kv = SubApplication; kv->kv_key; kv++)
1619 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1621 ct->c_subtype = kv->kv_value;
1628 ** TRANSFER ENCODINGS
1632 init_encoding (CT ct, OpenCEFunc openfnx)
1636 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1637 adios (NULL, "out of memory");
1640 ct->c_ceopenfnx = openfnx;
1641 ct->c_ceclosefnx = close_encoding;
1642 ct->c_cesizefnx = size_encoding;
1649 close_encoding (CT ct)
1653 if (!(ce = ct->c_cefile))
1663 static unsigned long
1664 size_encoding (CT ct)
1672 if (!(ce = ct->c_cefile))
1673 return (ct->c_end - ct->c_begin);
1675 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1676 return (long) st.st_size;
1679 if (stat (ce->ce_file, &st) != NOTOK)
1680 return (long) st.st_size;
1685 if (ct->c_encoding == CE_EXTERNAL)
1686 return (ct->c_end - ct->c_begin);
1689 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1690 return (ct->c_end - ct->c_begin);
1692 if (fstat (fd, &st) != NOTOK)
1693 size = (long) st.st_size;
1697 (*ct->c_ceclosefnx) (ct);
1706 static unsigned char b642nib[0x80] = {
1707 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1711 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1713 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1714 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1715 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1716 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1717 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1718 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1719 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1720 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1721 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1722 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1729 return init_encoding (ct, openBase64);
1734 openBase64 (CT ct, char **file)
1736 int bitno, cc, digested;
1739 unsigned char value, *b, *b1, *b2, *b3;
1740 unsigned char *cp, *ep;
1741 char buffer[BUFSIZ];
1742 /* sbeck -- handle suffixes */
1747 b = (unsigned char *) &bits;
1748 b1 = &b[endian > 0 ? 1 : 2];
1749 b2 = &b[endian > 0 ? 2 : 1];
1750 b3 = &b[endian > 0 ? 3 : 0];
1754 fseek (ce->ce_fp, 0L, SEEK_SET);
1759 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1760 content_error (ce->ce_file, ct, "unable to fopen for reading");
1766 if (*file == NULL) {
1767 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1770 ce->ce_file = add (*file, NULL);
1774 /* sbeck@cise.ufl.edu -- handle suffixes */
1776 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1777 invo_name, ci->ci_type, ci->ci_subtype);
1778 cp = context_find (buffer);
1779 if (cp == NULL || *cp == '\0') {
1780 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1782 cp = context_find (buffer);
1784 if (cp != NULL && *cp != '\0') {
1785 if (ce->ce_unlink) {
1787 ** Temporary file already exists, so we rename to
1788 ** version with extension.
1790 char *file_org = strdup(ce->ce_file);
1791 ce->ce_file = add (cp, ce->ce_file);
1792 if (rename(file_org, ce->ce_file)) {
1793 adios (ce->ce_file, "unable to rename %s to ", file_org);
1798 ce->ce_file = add (cp, ce->ce_file);
1802 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1803 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1807 if ((len = ct->c_end - ct->c_begin) < 0)
1808 adios (NULL, "internal error(1)");
1810 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1811 content_error (ct->c_file, ct, "unable to open for reading");
1815 if ((digested = ct->c_digested))
1816 MD5Init (&mdContext);
1822 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1824 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1826 content_error (ct->c_file, ct, "error reading from");
1830 content_error (NULL, ct, "premature eof");
1838 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1843 if (skip || (*cp & 0x80)
1844 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1846 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1848 content_error (NULL, ct,
1849 "invalid BASE64 encoding -- continuing");
1853 bits |= value << bitno;
1855 if ((bitno -= 6) < 0) {
1856 putc ((char) *b1, ce->ce_fp);
1858 MD5Update (&mdContext, b1, 1);
1860 putc ((char) *b2, ce->ce_fp);
1862 MD5Update (&mdContext, b2, 1);
1864 putc ((char) *b3, ce->ce_fp);
1866 MD5Update (&mdContext, b3, 1);
1870 if (ferror (ce->ce_fp)) {
1871 content_error (ce->ce_file, ct,
1872 "error writing to");
1875 bitno = 18, bits = 0L, skip = 0;
1881 goto self_delimiting;
1890 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1892 content_error (NULL, ct, "invalid BASE64 encoding");
1897 fseek (ct->c_fp, 0L, SEEK_SET);
1899 if (fflush (ce->ce_fp)) {
1900 content_error (ce->ce_file, ct, "error writing to");
1905 unsigned char digest[16];
1907 MD5Final (digest, &mdContext);
1908 if (memcmp((char *) digest, (char *) ct->c_digest,
1909 sizeof(digest) / sizeof(digest[0])))
1910 content_error (NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1912 fprintf (stderr, "content integrity confirmed\n");
1915 fseek (ce->ce_fp, 0L, SEEK_SET);
1918 *file = ce->ce_file;
1919 return fileno (ce->ce_fp);
1922 free_encoding (ct, 0);
1931 static char hex2nib[0x80] = {
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,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1939 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1944 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1954 return init_encoding (ct, openQuoted);
1959 openQuoted (CT ct, char **file)
1961 int cc, digested, len, quoted;
1962 unsigned char *cp, *ep;
1963 char buffer[BUFSIZ];
1966 /* sbeck -- handle suffixes */
1972 fseek (ce->ce_fp, 0L, SEEK_SET);
1977 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1978 content_error (ce->ce_file, ct,
1979 "unable to fopen for reading");
1985 if (*file == NULL) {
1986 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1989 ce->ce_file = add (*file, NULL);
1993 /* sbeck@cise.ufl.edu -- handle suffixes */
1995 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1996 invo_name, ci->ci_type, ci->ci_subtype);
1997 cp = context_find (buffer);
1998 if (cp == NULL || *cp == '\0') {
1999 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2001 cp = context_find (buffer);
2003 if (cp != NULL && *cp != '\0') {
2004 if (ce->ce_unlink) {
2005 // Temporary file already exists, so we rename to
2006 // version with extension.
2007 char *file_org = strdup(ce->ce_file);
2008 ce->ce_file = add (cp, ce->ce_file);
2009 if (rename(file_org, ce->ce_file)) {
2010 adios (ce->ce_file, "unable to rename %s to ",
2016 ce->ce_file = add (cp, ce->ce_file);
2020 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2021 content_error (ce->ce_file, ct,
2022 "unable to fopen for reading/writing");
2026 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2027 content_error (ce->ce_file, ct,
2028 "unable to fopen for reading/writing");
2032 if ((len = ct->c_end - ct->c_begin) < 0)
2033 adios (NULL, "internal error(2)");
2035 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2036 content_error (ct->c_file, ct, "unable to open for reading");
2040 if ((digested = ct->c_digested))
2041 MD5Init (&mdContext);
2048 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2050 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2051 content_error (NULL, ct, "premature eof");
2055 if ((cc = strlen (buffer)) > len)
2059 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2064 for (; cp < ep; cp++) {
2066 /* in an escape sequence */
2068 /* at byte 1 of an escape sequence */
2069 mask = hex2nib[*cp & 0x7f];
2070 /* next is byte 2 */
2073 /* at byte 2 of an escape sequence */
2075 mask |= hex2nib[*cp & 0x7f];
2076 putc (mask, ce->ce_fp);
2078 MD5Update (&mdContext, &mask, 1);
2079 if (ferror (ce->ce_fp)) {
2080 content_error (ce->ce_file, ct, "error writing to");
2084 ** finished escape sequence; next may
2085 ** be literal or a new escape sequence
2089 /* on to next byte */
2093 /* not in an escape sequence */
2096 ** starting an escape sequence,
2099 if (cp + 1 < ep && cp[1] == '\n') {
2100 /* "=\n" soft line break, eat the \n */
2104 if (cp + 1 >= ep || cp + 2 >= ep) {
2106 ** We don't have 2 bytes left,
2107 ** so this is an invalid escape
2108 ** sequence; just show the raw bytes
2111 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2113 ** Next 2 bytes are hex digits,
2114 ** making this a valid escape
2115 ** sequence; let's decode it (above).
2121 ** One or both of the next 2 is
2122 ** out of range, making this an
2123 ** invalid escape sequence; just
2124 ** show the raw bytes (below).
2129 /* Just show the raw byte. */
2130 putc (*cp, ce->ce_fp);
2133 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2135 MD5Update (&mdContext, (unsigned char *) cp, 1);
2138 if (ferror (ce->ce_fp)) {
2139 content_error (ce->ce_file, ct, "error writing to");
2145 content_error (NULL, ct,
2146 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2150 fseek (ct->c_fp, 0L, SEEK_SET);
2152 if (fflush (ce->ce_fp)) {
2153 content_error (ce->ce_file, ct, "error writing to");
2158 unsigned char digest[16];
2160 MD5Final (digest, &mdContext);
2161 if (memcmp((char *) digest, (char *) ct->c_digest,
2162 sizeof(digest) / sizeof(digest[0])))
2163 content_error (NULL, ct,
2164 "content integrity suspect (digest mismatch) -- continuing");
2166 fprintf (stderr, "content integrity confirmed\n");
2169 fseek (ce->ce_fp, 0L, SEEK_SET);
2172 *file = ce->ce_file;
2173 return fileno (ce->ce_fp);
2176 free_encoding (ct, 0);
2188 if (init_encoding (ct, open7Bit) == NOTOK)
2191 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2197 open7Bit (CT ct, char **file)
2200 char buffer[BUFSIZ];
2201 /* sbeck -- handle suffixes */
2208 fseek (ce->ce_fp, 0L, SEEK_SET);
2213 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2214 content_error (ce->ce_file, ct,
2215 "unable to fopen for reading");
2221 if (*file == NULL) {
2222 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2225 ce->ce_file = add (*file, NULL);
2229 /* sbeck@cise.ufl.edu -- handle suffixes */
2231 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2232 invo_name, ci->ci_type, ci->ci_subtype);
2233 cp = context_find (buffer);
2234 if (cp == NULL || *cp == '\0') {
2235 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2237 cp = context_find (buffer);
2239 if (cp != NULL && *cp != '\0') {
2240 if (ce->ce_unlink) {
2242 ** Temporary file already exists, so we rename to
2243 ** version with extension.
2245 char *file_org = strdup(ce->ce_file);
2246 ce->ce_file = add (cp, ce->ce_file);
2247 if (rename(file_org, ce->ce_file)) {
2248 adios (ce->ce_file, "unable to rename %s to ",
2254 ce->ce_file = add (cp, ce->ce_file);
2258 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2259 content_error (ce->ce_file, ct,
2260 "unable to fopen for reading/writing");
2264 if (ct->c_type == CT_MULTIPART) {
2266 CI ci = &ct->c_ctinfo;
2269 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2271 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2272 + 1 + strlen (ci->ci_subtype);
2273 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2274 putc (';', ce->ce_fp);
2277 snprintf (buffer, sizeof(buffer), "%s=\"%s\"",
2280 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2281 fputs ("\n\t", ce->ce_fp);
2284 putc (' ', ce->ce_fp);
2287 fprintf (ce->ce_fp, "%s", buffer);
2291 if (ci->ci_comment) {
2292 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2293 fputs ("\n\t", ce->ce_fp);
2297 putc (' ', ce->ce_fp);
2300 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2303 fprintf (ce->ce_fp, "\n");
2305 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2307 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2309 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2310 fprintf (ce->ce_fp, "\n");
2313 if ((len = ct->c_end - ct->c_begin) < 0)
2314 adios (NULL, "internal error(3)");
2316 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2317 content_error (ct->c_file, ct, "unable to open for reading");
2321 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2323 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2325 content_error (ct->c_file, ct, "error reading from");
2329 content_error (NULL, ct, "premature eof");
2337 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2338 if (ferror (ce->ce_fp)) {
2339 content_error (ce->ce_file, ct, "error writing to");
2344 fseek (ct->c_fp, 0L, SEEK_SET);
2346 if (fflush (ce->ce_fp)) {
2347 content_error (ce->ce_file, ct, "error writing to");
2351 fseek (ce->ce_fp, 0L, SEEK_SET);
2354 *file = ce->ce_file;
2355 return fileno (ce->ce_fp);
2358 free_encoding (ct, 0);
2368 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2370 char cachefile[BUFSIZ];
2373 fseek (ce->ce_fp, 0L, SEEK_SET);
2378 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2379 content_error (ce->ce_file, ct,
2380 "unable to fopen for reading");
2386 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2387 cachefile, sizeof(cachefile)) != NOTOK) {
2388 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2389 ce->ce_file = getcpy (cachefile);
2393 admonish (cachefile, "unable to fopen for reading");
2400 *file = ce->ce_file;
2401 *fd = fileno (ce->ce_fp);
2412 return init_encoding (ct, openFile);
2417 openFile (CT ct, char **file)
2420 char cachefile[BUFSIZ];
2421 struct exbody *e = ct->c_ctexbody;
2422 CE ce = ct->c_cefile;
2424 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2436 content_error (NULL, ct, "missing name parameter");
2440 ce->ce_file = getcpy (e->eb_name);
2443 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2444 content_error (ce->ce_file, ct, "unable to fopen for reading");
2448 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2449 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2450 cachefile, sizeof(cachefile)) != NOTOK) {
2454 mask = umask (cachetype ? ~m_gmprot () : 0222);
2455 if ((fp = fopen (cachefile, "w"))) {
2457 char buffer[BUFSIZ];
2458 FILE *gp = ce->ce_fp;
2460 fseek (gp, 0L, SEEK_SET);
2462 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0)
2463 fwrite (buffer, sizeof(*buffer), cc, fp);
2467 admonish (ce->ce_file, "error reading");
2469 } else if (ferror (fp)) {
2470 admonish (cachefile, "error writing");
2478 fseek (ce->ce_fp, 0L, SEEK_SET);
2479 *file = ce->ce_file;
2480 return fileno (ce->ce_fp);
2490 return init_encoding (ct, openFTP);
2495 openFTP (CT ct, char **file)
2497 int cachetype, caching, fd;
2499 char *bp, *ftp, *user, *pass;
2500 char buffer[BUFSIZ], cachefile[BUFSIZ];
2503 static char *username = NULL;
2504 static char *password = NULL;
2509 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2517 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2528 if (!e->eb_name || !e->eb_site) {
2529 content_error (NULL, ct, "missing %s parameter",
2530 e->eb_name ? "site": "name");
2537 pidcheck (pidwait (xpid, NOTOK));
2541 /* Get the buffer ready to go */
2543 buflen = sizeof(buffer);
2546 ** Construct the query message for user
2548 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2554 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2560 snprintf (bp, buflen, "\n using %sFTP from site %s",
2561 e->eb_flags ? "anonymous " : "", e->eb_site);
2566 if (e->eb_size > 0) {
2567 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2572 snprintf (bp, buflen, "? ");
2575 ** Now, check the answer
2577 if (!getanswer (buffer))
2582 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2586 ruserpass (e->eb_site, &username, &password);
2591 ce->ce_unlink = (*file == NULL);
2593 cachefile[0] = '\0';
2594 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2595 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2596 cachefile, sizeof(cachefile)) != NOTOK) {
2597 if (*file == NULL) {
2604 ce->ce_file = add (*file, NULL);
2606 ce->ce_file = add (cachefile, NULL);
2608 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2610 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2611 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2619 int child_id, i, vecp;
2623 vec[vecp++] = r1bindex (ftp, '/');
2624 vec[vecp++] = e->eb_site;
2627 vec[vecp++] = e->eb_dir;
2628 vec[vecp++] = e->eb_name;
2629 vec[vecp++] = ce->ce_file,
2630 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2631 ? "ascii" : "binary";
2636 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2640 adios ("fork", "unable to");
2644 close (fileno (ce->ce_fp));
2646 fprintf (stderr, "unable to exec ");
2652 if (pidXwait (child_id, NULL)) {
2656 username = password = NULL;
2664 else if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2665 ce->ce_file, e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2672 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2677 mask = umask (cachetype ? ~m_gmprot () : 0222);
2678 if ((fp = fopen (cachefile, "w"))) {
2680 FILE *gp = ce->ce_fp;
2682 fseek (gp, 0L, SEEK_SET);
2684 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp)) > 0)
2685 fwrite (buffer, sizeof(*buffer), cc, fp);
2689 admonish (ce->ce_file, "error reading");
2691 } else if (ferror (fp)) {
2692 admonish (cachefile, "error writing");
2701 fseek (ce->ce_fp, 0L, SEEK_SET);
2702 *file = ce->ce_file;
2703 return fileno (ce->ce_fp);
2714 return init_encoding (ct, openMail);
2719 openMail (CT ct, char **file)
2721 int child_id, fd, i, vecp;
2723 char *bp, buffer[BUFSIZ], *vec[7];
2724 struct exbody *e = ct->c_ctexbody;
2725 CE ce = ct->c_cefile;
2727 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2738 if (!e->eb_server) {
2739 content_error (NULL, ct, "missing server parameter");
2746 pidcheck (pidwait (xpid, NOTOK));
2750 /* Get buffer ready to go */
2752 buflen = sizeof(buffer);
2754 /* Now, construct query message */
2755 snprintf (bp, buflen, "Retrieve content");
2761 snprintf (bp, buflen, " %s", e->eb_partno);
2767 snprintf (bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2768 e->eb_subject ? e->eb_subject : e->eb_body);
2770 /* Now, check answer */
2771 if (!getanswer (buffer))
2775 vec[vecp++] = r1bindex (mailproc, '/');
2776 vec[vecp++] = e->eb_server;
2777 vec[vecp++] = "-subject";
2778 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2779 vec[vecp++] = "-body";
2780 vec[vecp++] = e->eb_body;
2783 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2787 advise ("fork", "unable to");
2791 execvp (mailproc, vec);
2792 fprintf (stderr, "unable to exec ");
2798 if (pidXwait (child_id, NULL) == OK)
2799 advise (NULL, "request sent");
2803 if (*file == NULL) {
2804 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2807 ce->ce_file = add (*file, NULL);
2811 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2812 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2817 ** showproc is for mhshow and mhstore, though mhlist -debug
2821 free (ct->c_showproc);
2822 ct->c_showproc = add ("true", NULL);
2824 fseek (ce->ce_fp, 0L, SEEK_SET);
2825 *file = ce->ce_file;
2826 return fileno (ce->ce_fp);
2831 readDigest (CT ct, char *cp)
2836 unsigned char *dp, value, *ep;
2837 unsigned char *b, *b1, *b2, *b3;
2839 b = (unsigned char *) &bits,
2840 b1 = &b[endian > 0 ? 1 : 2],
2841 b2 = &b[endian > 0 ? 2 : 1],
2842 b3 = &b[endian > 0 ? 3 : 0];
2847 for (ep = (dp = ct->c_digest)
2848 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2851 if (skip || (*cp & 0x80)
2852 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2854 fprintf (stderr, "invalid BASE64 encoding\n");
2858 bits |= value << bitno;
2860 if ((bitno -= 6) < 0) {
2861 if (dp + (3 - skip) > ep)
2862 goto invalid_digest;
2877 goto self_delimiting;
2882 fprintf (stderr, "premature ending (bitno %d)\n",
2893 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2901 fprintf (stderr, "MD5 digest=");
2902 for (dp = ct->c_digest; dp < ep; dp++)
2903 fprintf (stderr, "%02x", *dp & 0xff);
2904 fprintf (stderr, "\n");