3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern int endian; /* mhmisc.c */
27 extern pid_t xpid; /* mhshowsbr.c */
30 extern int rcachesw; /* mhcachesbr.c */
31 extern int wcachesw; /* mhcachesbr.c */
33 int checksw = 0; /* check Content-MD5 field */
36 * Directory to place temp files. This must
37 * be set before these routines are called.
42 * Structures for TEXT messages
44 struct k2v SubText[] = {
45 { "plain", TEXT_PLAIN },
46 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
47 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
48 { NULL, TEXT_UNKNOWN } /* this one must be last! */
51 struct k2v Charset[] = {
52 { "us-ascii", CHARSET_USASCII },
53 { "iso-8859-1", CHARSET_LATIN },
54 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
58 * Structures for MULTIPART messages
60 struct k2v SubMultiPart[] = {
61 { "mixed", MULTI_MIXED },
62 { "alternative", MULTI_ALTERNATE },
63 { "digest", MULTI_DIGEST },
64 { "parallel", MULTI_PARALLEL },
65 { NULL, MULTI_UNKNOWN } /* this one must be last! */
69 * Structures for MESSAGE messages
71 struct k2v SubMessage[] = {
72 { "rfc822", MESSAGE_RFC822 },
73 { "partial", MESSAGE_PARTIAL },
74 { "external-body", MESSAGE_EXTERNAL },
75 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
79 * Structure for APPLICATION messages
81 struct k2v SubApplication[] = {
82 { "octet-stream", APPLICATION_OCTETS },
83 { "postscript", APPLICATION_POSTSCRIPT },
84 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
89 int find_cache (CT, int, int *, char *, char *, int);
92 int part_ok (CT, int);
93 int type_ok (CT, int);
94 void content_error (char *, CT, char *, ...);
97 void free_content (CT);
98 void free_encoding (CT, int);
103 static CT get_content (FILE *, char *, int);
104 static int get_comment (CT, unsigned char **, int);
106 static int InitGeneric (CT);
107 static int InitText (CT);
108 static int InitMultiPart (CT);
109 static void reverse_parts (CT);
110 static int InitMessage (CT);
111 static int InitApplication (CT);
112 static int init_encoding (CT, OpenCEFunc);
113 static unsigned long size_encoding (CT);
114 static int InitBase64 (CT);
115 static int openBase64 (CT, char **);
116 static int InitQuoted (CT);
117 static int openQuoted (CT, char **);
118 static int Init7Bit (CT);
119 static int openExternal (CT, CT, CE, char **, int *);
120 static int InitFile (CT);
121 static int openFile (CT, char **);
122 static int InitFTP (CT);
123 static int openFTP (CT, char **);
124 static int InitMail (CT);
125 static int openMail (CT, char **);
126 static int readDigest (CT, char *);
128 struct str2init str2cts[] = {
129 { "application", CT_APPLICATION, InitApplication },
130 { "audio", CT_AUDIO, InitGeneric },
131 { "image", CT_IMAGE, InitGeneric },
132 { "message", CT_MESSAGE, InitMessage },
133 { "multipart", CT_MULTIPART, InitMultiPart },
134 { "text", CT_TEXT, InitText },
135 { "video", CT_VIDEO, InitGeneric },
136 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
137 { NULL, CT_UNKNOWN, NULL },
140 struct str2init str2ces[] = {
141 { "base64", CE_BASE64, InitBase64 },
142 { "quoted-printable", CE_QUOTED, InitQuoted },
143 { "8bit", CE_8BIT, Init7Bit },
144 { "7bit", CE_7BIT, Init7Bit },
145 { "binary", CE_BINARY, Init7Bit },
146 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
147 { NULL, CE_UNKNOWN, NULL },
151 * NOTE WELL: si_key MUST NOT have value of NOTOK
153 * si_key is 1 if access method is anonymous.
155 struct str2init str2methods[] = {
156 { "afs", 1, InitFile },
157 { "anon-ftp", 1, InitFTP },
158 { "ftp", 0, InitFTP },
159 { "local-file", 0, InitFile },
160 { "mail-server", 0, InitMail },
166 pidcheck (int status)
168 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
179 * Main entry point for parsing a MIME message or file.
180 * It returns the Content structure for the top level
181 * entity in the file.
185 parse_mime (char *file)
193 * Check if file is actually standard input
195 if ((is_stdin = !(strcmp (file, "-")))) {
196 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
198 advise("mhparse", "unable to create temporary file");
201 file = add (tfile, NULL);
204 while (fgets (buffer, sizeof(buffer), stdin))
208 if (ferror (stdin)) {
210 advise ("stdin", "error reading");
215 advise (file, "error writing");
218 fseek (fp, 0L, SEEK_SET);
219 } else if ((fp = fopen (file, "r")) == NULL) {
220 advise (file, "unable to read");
224 if (!(ct = get_content (fp, file, 1))) {
227 advise (NULL, "unable to decode %s", file);
232 ct->c_unlink = 1; /* temp file to remove */
236 if (ct->c_end == 0L) {
237 fseek (fp, 0L, SEEK_END);
238 ct->c_end = ftell (fp);
241 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
253 * Main routine for reading/parsing the headers
254 * of a message content.
256 * toplevel = 1 # we are at the top level of the message
257 * toplevel = 0 # we are inside message type or multipart type
258 * # other than multipart/digest
259 * toplevel = -1 # we are inside multipart/digest
260 * NB: on failure we will fclose(in)!
264 get_content (FILE *in, char *file, int toplevel)
267 char buf[BUFSIZ], name[NAMESZ];
272 /* allocate the content structure */
273 if (!(ct = (CT) calloc (1, sizeof(*ct))))
274 adios (NULL, "out of memory");
277 ct->c_file = add (file, NULL);
278 ct->c_begin = ftell (ct->c_fp) + 1;
281 * Parse the header fields for this
282 * content into a linked list.
284 for (compnum = 1, state = FLD;;) {
285 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
291 /* get copies of the buffers */
292 np = add (name, NULL);
293 vp = add (buf, NULL);
295 /* if necessary, get rest of field */
296 while (state == FLDPLUS) {
297 state = m_getfld (state, name, buf, sizeof(buf), in);
298 vp = add (buf, vp); /* add to previous value */
301 /* Now add the header data to the list */
302 add_header (ct, np, vp);
304 /* continue, if this isn't the last header field */
305 if (state != FLDEOF) {
306 ct->c_begin = ftell (in) + 1;
313 ct->c_begin = ftell (in) - strlen (buf);
317 ct->c_begin = ftell (in);
322 adios (NULL, "message format error in component #%d", compnum);
325 adios (NULL, "getfld() returned %d", state);
328 /* break out of the loop */
333 * Read the content headers. We will parse the
334 * MIME related header fields into their various
335 * structures and set internal flags related to
336 * content type/subtype, etc.
339 hp = ct->c_first_hf; /* start at first header field */
341 /* Get MIME-Version field */
342 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
345 unsigned char *cp, *dp;
348 advise (NULL, "message %s has multiple %s: fields",
349 ct->c_file, VRSN_FIELD);
352 ct->c_vrsn = add (hp->value, NULL);
354 /* Now, cleanup this field */
357 while (isspace (*cp))
359 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
361 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
366 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
368 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
371 for (dp = cp; istoken (*dp); dp++)
375 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
378 admonish (NULL, "message %s has unknown value for %s: field (%s)",
379 ct->c_file, VRSN_FIELD, cp);
382 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
383 /* Get Content-Type field */
384 struct str2init *s2i;
385 CI ci = &ct->c_ctinfo;
387 /* Check if we've already seen a Content-Type header */
389 advise (NULL, "message %s has multiple %s: fields",
390 ct->c_file, TYPE_FIELD);
394 /* Parse the Content-Type field */
395 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
399 * Set the Init function and the internal
400 * flag for this content type.
402 for (s2i = str2cts; s2i->si_key; s2i++)
403 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
405 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
407 ct->c_type = s2i->si_val;
408 ct->c_ctinitfnx = s2i->si_init;
410 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
411 /* Get Content-Transfer-Encoding field */
413 unsigned char *cp, *dp;
414 struct str2init *s2i;
417 * Check if we've already seen the
418 * Content-Transfer-Encoding field
421 advise (NULL, "message %s has multiple %s: fields",
422 ct->c_file, ENCODING_FIELD);
426 /* get copy of this field */
427 ct->c_celine = cp = add (hp->value, NULL);
429 while (isspace (*cp))
431 for (dp = cp; istoken (*dp); dp++)
437 * Find the internal flag and Init function
438 * for this transfer encoding.
440 for (s2i = str2ces; s2i->si_key; s2i++)
441 if (!mh_strcasecmp (cp, s2i->si_key))
443 if (!s2i->si_key && !uprf (cp, "X-"))
446 ct->c_encoding = s2i->si_val;
448 /* Call the Init function for this encoding */
449 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
452 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
453 /* Get Content-MD5 field */
454 unsigned char *cp, *dp;
460 if (ct->c_digested) {
461 advise (NULL, "message %s has multiple %s: fields",
462 ct->c_file, MD5_FIELD);
466 ep = cp = add (hp->value, NULL); /* get a copy */
468 while (isspace (*cp))
470 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
472 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
477 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
479 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
484 for (dp = cp; *dp && !isspace (*dp); dp++)
492 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
493 /* Get Content-ID field */
494 ct->c_id = add (hp->value, ct->c_id);
496 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
497 /* Get Content-Description field */
498 ct->c_descr = add (hp->value, ct->c_descr);
500 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
501 /* Get Content-Disposition field */
502 ct->c_dispo = add (hp->value, ct->c_dispo);
506 hp = hp->next; /* next header field */
510 * Check if we saw a Content-Type field.
511 * If not, then assign a default value for
512 * it, and the Init function.
516 * If we are inside a multipart/digest message,
517 * so default type is message/rfc822
520 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
522 ct->c_type = CT_MESSAGE;
523 ct->c_ctinitfnx = InitMessage;
526 * Else default type is text/plain
528 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
530 ct->c_type = CT_TEXT;
531 ct->c_ctinitfnx = InitText;
535 /* Use default Transfer-Encoding, if necessary */
537 ct->c_encoding = CE_7BIT;
550 * small routine to add header field to list
554 add_header (CT ct, char *name, char *value)
558 /* allocate header field structure */
559 hp = mh_xmalloc (sizeof(*hp));
561 /* link data into header structure */
566 /* link header structure into the list */
567 if (ct->c_first_hf == NULL) {
568 ct->c_first_hf = hp; /* this is the first */
571 ct->c_last_hf->next = hp; /* add it to the end */
579 /* Make sure that buf contains at least one appearance of name,
580 followed by =. If not, insert both name and value, just after
581 first semicolon, if any. Note that name should not contain a
582 trailing =. And quotes will be added around the value. Typical
583 usage: make sure that a Content-Disposition header contains
584 filename="foo". If it doesn't and value does, use value from
587 incl_name_value (unsigned char *buf, char *name, char *value) {
590 /* Assume that name is non-null. */
592 char *name_plus_equal = concat (name, "=", NULL);
594 if (! strstr (buf, name_plus_equal)) {
597 char *prefix, *suffix;
599 /* Trim trailing space, esp. newline. */
600 for (cp = &buf[strlen (buf) - 1];
601 cp >= buf && isspace (*cp);
606 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
608 /* Insert at first semicolon, if any. If none, append to
610 prefix = add (buf, NULL);
611 if ((cp = strchr (prefix, ';'))) {
612 suffix = concat (cp, NULL);
614 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
618 newbuf = concat (buf, insertion, "\n", NULL);
626 free (name_plus_equal);
632 /* Extract just name_suffix="foo", if any, from value. If there isn't
633 one, return the entire value. Note that, for example, a name_suffix
634 of name will match filename="foo", and return foo. */
636 extract_name_value (char *name_suffix, char *value) {
637 char *extracted_name_value = value;
638 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
639 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
642 free (name_suffix_plus_quote);
643 if (name_suffix_equals) {
644 char *name_suffix_begin;
647 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
648 name_suffix_begin = ++cp;
649 /* Find second \". */
650 for (; *cp != '"'; ++cp) /* empty */;
652 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
653 memcpy (extracted_name_value,
655 cp - name_suffix_begin);
656 extracted_name_value[cp - name_suffix_begin] = '\0';
659 return extracted_name_value;
663 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
664 * directives. Fills in the information of the CTinfo structure.
667 get_ctinfo (unsigned char *cp, CT ct, int magic)
676 i = strlen (invo_name) + 2;
678 /* store copy of Content-Type line */
679 cp = ct->c_ctline = add (cp, NULL);
681 while (isspace (*cp)) /* trim leading spaces */
684 /* change newlines to spaces */
685 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
688 /* trim trailing spaces */
689 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
695 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
697 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
700 for (dp = cp; istoken (*dp); dp++)
703 ci->ci_type = add (cp, NULL); /* store content type */
707 advise (NULL, "invalid %s: field in message %s (empty type)",
708 TYPE_FIELD, ct->c_file);
712 /* down case the content type string */
713 for (dp = ci->ci_type; *dp; dp++)
714 if (isalpha(*dp) && isupper (*dp))
717 while (isspace (*cp))
720 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
725 ci->ci_subtype = add ("", NULL);
730 while (isspace (*cp))
733 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
736 for (dp = cp; istoken (*dp); dp++)
739 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
742 if (!*ci->ci_subtype) {
744 "invalid %s: field in message %s (empty subtype for \"%s\")",
745 TYPE_FIELD, ct->c_file, ci->ci_type);
749 /* down case the content subtype string */
750 for (dp = ci->ci_subtype; *dp; dp++)
751 if (isalpha(*dp) && isupper (*dp))
755 while (isspace (*cp))
758 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
762 * Parse attribute/value pairs given with Content-Type
764 ep = (ap = ci->ci_attrs) + NPARMS;
771 "too many parameters in message %s's %s: field (%d max)",
772 ct->c_file, TYPE_FIELD, NPARMS);
777 while (isspace (*cp))
780 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
785 "extraneous trailing ';' in message %s's %s: parameter list",
786 ct->c_file, TYPE_FIELD);
790 /* down case the attribute name */
791 for (dp = cp; istoken (*dp); dp++)
792 if (isalpha(*dp) && isupper (*dp))
795 for (up = dp; isspace (*dp);)
797 if (dp == cp || *dp != '=') {
799 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
800 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
804 vp = (*ap = add (cp, NULL)) + (up - cp);
806 for (dp++; isspace (*dp);)
809 /* now add the attribute value */
810 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
813 for (cp = ++dp, dp = vp;;) {
818 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
819 ct->c_file, TYPE_FIELD, i, i, "", *ap);
824 if ((c = *cp++) == '\0')
839 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
845 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
846 ct->c_file, TYPE_FIELD, i, i, "", *ap);
851 while (isspace (*cp))
854 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
859 * Get any <Content-Id> given in buffer
861 if (magic && *cp == '<') {
866 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
867 advise (NULL, "invalid ID in message %s", ct->c_file);
873 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
879 while (isspace (*cp))
884 * Get any [Content-Description] given in buffer.
886 if (magic && *cp == '[') {
888 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
892 advise (NULL, "invalid description in message %s", ct->c_file);
900 ct->c_descr = concat (ct->c_descr, "\n", NULL);
906 while (isspace (*cp))
911 * Get any {Content-Disposition} given in buffer.
913 if (magic && *cp == '{') {
915 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
919 advise (NULL, "invalid disposition in message %s", ct->c_file);
927 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
933 while (isspace (*cp))
938 * Check if anything is left over
942 ci->ci_magic = add (cp, NULL);
944 /* If there is a Content-Disposition header and it doesn't
945 have a *filename=, extract it from the magic contents.
946 The r1bindex call skips any leading directory
950 incl_name_value (ct->c_dispo,
952 r1bindex (extract_name_value ("name",
959 "extraneous information in message %s's %s: field\n%*.*s(%s)",
960 ct->c_file, TYPE_FIELD, i, i, "", cp);
968 get_comment (CT ct, unsigned char **ap, int istype)
973 char c, buffer[BUFSIZ], *dp;
985 advise (NULL, "invalid comment in message %s's %s: field",
986 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
991 if ((c = *cp++) == '\0')
1014 if ((dp = ci->ci_comment)) {
1015 ci->ci_comment = concat (dp, " ", buffer, NULL);
1018 ci->ci_comment = add (buffer, NULL);
1022 while (isspace (*cp))
1033 * Handles content types audio, image, and video.
1034 * There's not much to do right here.
1042 return OK; /* not much to do here */
1053 char buffer[BUFSIZ];
1055 char **ap, **ep, *cp;
1058 CI ci = &ct->c_ctinfo;
1060 /* check for missing subtype */
1061 if (!*ci->ci_subtype)
1062 ci->ci_subtype = add ("plain", ci->ci_subtype);
1065 for (kv = SubText; kv->kv_key; kv++)
1066 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1068 ct->c_subtype = kv->kv_value;
1070 /* allocate text character set structure */
1071 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1072 adios (NULL, "out of memory");
1073 ct->c_ctparams = (void *) t;
1075 /* scan for charset parameter */
1076 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1077 if (!mh_strcasecmp (*ap, "charset"))
1080 /* check if content specified a character set */
1082 /* match character set or set to CHARSET_UNKNOWN */
1083 for (kv = Charset; kv->kv_key; kv++) {
1084 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1089 t->tx_charset = kv->kv_value;
1091 t->tx_charset = CHARSET_UNSPECIFIED;
1095 * If we can not handle character set natively,
1096 * then check profile for string to modify the
1097 * terminal or display method.
1099 * termproc is for mhshow, though mhlist -debug prints it, too.
1101 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1102 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1103 if ((cp = context_find (buffer)))
1104 ct->c_termproc = getcpy (cp);
1116 InitMultiPart (CT ct)
1120 unsigned char *cp, *dp;
1122 char *bp, buffer[BUFSIZ];
1123 struct multipart *m;
1125 struct part *part, **next;
1126 CI ci = &ct->c_ctinfo;
1131 * The encoding for multipart messages must be either
1132 * 7bit, 8bit, or binary (per RFC2045).
1134 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1135 && ct->c_encoding != CE_BINARY) {
1137 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1138 ci->ci_type, ci->ci_subtype, ct->c_file);
1143 for (kv = SubMultiPart; kv->kv_key; kv++)
1144 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1146 ct->c_subtype = kv->kv_value;
1149 * Check for "boundary" parameter, which is
1150 * required for multipart messages.
1153 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1154 if (!mh_strcasecmp (*ap, "boundary")) {
1160 /* complain if boundary parameter is missing */
1163 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1164 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1168 /* allocate primary structure for multipart info */
1169 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1170 adios (NULL, "out of memory");
1171 ct->c_ctparams = (void *) m;
1173 /* check if boundary parameter contains only whitespace characters */
1174 for (cp = bp; isspace (*cp); cp++)
1177 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1178 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1182 /* remove trailing whitespace from boundary parameter */
1183 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1188 /* record boundary separators */
1189 m->mp_start = concat (bp, "\n", NULL);
1190 m->mp_stop = concat (bp, "--\n", NULL);
1192 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1193 advise (ct->c_file, "unable to open for reading");
1197 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1199 next = &m->mp_parts;
1203 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1207 pos += strlen (buffer);
1208 if (buffer[0] != '-' || buffer[1] != '-')
1211 if (strcmp (buffer + 2, m->mp_start))
1214 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1215 adios (NULL, "out of memory");
1217 next = &part->mp_next;
1219 if (!(p = get_content (fp, ct->c_file,
1220 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1227 fseek (fp, pos, SEEK_SET);
1230 if (strcmp (buffer + 2, m->mp_start) == 0) {
1234 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1235 if (p->c_end < p->c_begin)
1236 p->c_begin = p->c_end;
1241 if (strcmp (buffer + 2, m->mp_stop) == 0)
1247 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1248 if (!inout && part) {
1250 p->c_end = ct->c_end;
1252 if (p->c_begin >= p->c_end) {
1253 for (next = &m->mp_parts; *next != part;
1254 next = &((*next)->mp_next))
1258 free ((char *) part);
1263 /* reverse the order of the parts for multipart/alternative */
1264 if (ct->c_subtype == MULTI_ALTERNATE)
1268 * label all subparts with part number, and
1269 * then initialize the content of the subpart.
1274 char partnam[BUFSIZ];
1277 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1278 pp = partnam + strlen (partnam);
1283 for (part = m->mp_parts, partnum = 1; part;
1284 part = part->mp_next, partnum++) {
1287 sprintf (pp, "%d", partnum);
1288 p->c_partno = add (partnam, NULL);
1290 /* initialize the content of the subparts */
1291 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1306 * reverse the order of the parts of a multipart
1310 reverse_parts (CT ct)
1313 struct multipart *m;
1314 struct part **base, **bmp, **next, *part;
1316 m = (struct multipart *) ct->c_ctparams;
1318 /* if only one part, just return */
1319 if (!m->mp_parts || !m->mp_parts->mp_next)
1322 /* count number of parts */
1324 for (part = m->mp_parts; part; part = part->mp_next)
1327 /* allocate array of pointers to the parts */
1328 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1329 adios (NULL, "out of memory");
1332 /* point at all the parts */
1333 for (part = m->mp_parts; part; part = part->mp_next)
1337 /* reverse the order of the parts */
1338 next = &m->mp_parts;
1339 for (bmp--; bmp >= base; bmp--) {
1342 next = &part->mp_next;
1346 /* free array of pointers */
1347 free ((char *) base);
1359 CI ci = &ct->c_ctinfo;
1361 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1363 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1364 ci->ci_type, ci->ci_subtype, ct->c_file);
1368 /* check for missing subtype */
1369 if (!*ci->ci_subtype)
1370 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1373 for (kv = SubMessage; kv->kv_key; kv++)
1374 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1376 ct->c_subtype = kv->kv_value;
1378 switch (ct->c_subtype) {
1379 case MESSAGE_RFC822:
1382 case MESSAGE_PARTIAL:
1387 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1388 adios (NULL, "out of memory");
1389 ct->c_ctparams = (void *) p;
1391 /* scan for parameters "id", "number", and "total" */
1392 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1393 if (!mh_strcasecmp (*ap, "id")) {
1394 p->pm_partid = add (*ep, NULL);
1397 if (!mh_strcasecmp (*ap, "number")) {
1398 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1399 || p->pm_partno < 1) {
1402 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1403 *ap, ci->ci_type, ci->ci_subtype,
1404 ct->c_file, TYPE_FIELD);
1409 if (!mh_strcasecmp (*ap, "total")) {
1410 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1419 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1421 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1422 ci->ci_type, ci->ci_subtype,
1423 ct->c_file, TYPE_FIELD);
1429 case MESSAGE_EXTERNAL:
1436 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1437 adios (NULL, "out of memory");
1438 ct->c_ctparams = (void *) e;
1441 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1442 advise (ct->c_file, "unable to open for reading");
1446 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1448 if (!(p = get_content (fp, ct->c_file, 0))) {
1456 if ((exresult = params_external (ct, 0)) != NOTOK
1457 && p->c_ceopenfnx == openMail) {
1461 if ((size = ct->c_end - p->c_begin) <= 0) {
1463 content_error (NULL, ct,
1464 "empty body for access-type=mail-server");
1468 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1469 fseek (p->c_fp, p->c_begin, SEEK_SET);
1471 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1473 adios ("failed", "fread");
1476 adios (NULL, "unexpected EOF from fread");
1479 bp += cc, size -= cc;
1486 p->c_end = p->c_begin;
1491 if (exresult == NOTOK)
1493 if (e->eb_flags == NOTOK)
1496 switch (p->c_type) {
1501 if (p->c_subtype != MESSAGE_RFC822)
1505 e->eb_partno = ct->c_partno;
1507 (*p->c_ctinitfnx) (p);
1522 params_external (CT ct, int composing)
1525 struct exbody *e = (struct exbody *) ct->c_ctparams;
1526 CI ci = &ct->c_ctinfo;
1528 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1529 if (!mh_strcasecmp (*ap, "access-type")) {
1530 struct str2init *s2i;
1531 CT p = e->eb_content;
1533 for (s2i = str2methods; s2i->si_key; s2i++)
1534 if (!mh_strcasecmp (*ep, s2i->si_key))
1538 e->eb_flags = NOTOK;
1539 p->c_encoding = CE_EXTERNAL;
1542 e->eb_access = s2i->si_key;
1543 e->eb_flags = s2i->si_val;
1544 p->c_encoding = CE_EXTERNAL;
1546 /* Call the Init function for this external type */
1547 if ((*s2i->si_init)(p) == NOTOK)
1551 if (!mh_strcasecmp (*ap, "name")) {
1555 if (!mh_strcasecmp (*ap, "permission")) {
1556 e->eb_permission = *ep;
1559 if (!mh_strcasecmp (*ap, "site")) {
1563 if (!mh_strcasecmp (*ap, "directory")) {
1567 if (!mh_strcasecmp (*ap, "mode")) {
1571 if (!mh_strcasecmp (*ap, "size")) {
1572 sscanf (*ep, "%lu", &e->eb_size);
1575 if (!mh_strcasecmp (*ap, "server")) {
1579 if (!mh_strcasecmp (*ap, "subject")) {
1580 e->eb_subject = *ep;
1583 if (composing && !mh_strcasecmp (*ap, "body")) {
1584 e->eb_body = getcpy (*ep);
1589 if (!e->eb_access) {
1591 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1592 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1605 InitApplication (CT ct)
1608 CI ci = &ct->c_ctinfo;
1611 for (kv = SubApplication; kv->kv_key; kv++)
1612 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1614 ct->c_subtype = kv->kv_value;
1621 * TRANSFER ENCODINGS
1625 init_encoding (CT ct, OpenCEFunc openfnx)
1629 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1630 adios (NULL, "out of memory");
1633 ct->c_ceopenfnx = openfnx;
1634 ct->c_ceclosefnx = close_encoding;
1635 ct->c_cesizefnx = size_encoding;
1642 close_encoding (CT ct)
1646 if (!(ce = ct->c_cefile))
1656 static unsigned long
1657 size_encoding (CT ct)
1665 if (!(ce = ct->c_cefile))
1666 return (ct->c_end - ct->c_begin);
1668 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1669 return (long) st.st_size;
1672 if (stat (ce->ce_file, &st) != NOTOK)
1673 return (long) st.st_size;
1678 if (ct->c_encoding == CE_EXTERNAL)
1679 return (ct->c_end - ct->c_begin);
1682 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1683 return (ct->c_end - ct->c_begin);
1685 if (fstat (fd, &st) != NOTOK)
1686 size = (long) st.st_size;
1690 (*ct->c_ceclosefnx) (ct);
1699 static unsigned char b642nib[0x80] = {
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1706 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1707 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1709 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1710 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1711 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1713 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1714 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1715 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1722 return init_encoding (ct, openBase64);
1727 openBase64 (CT ct, char **file)
1729 int bitno, cc, digested;
1730 int fd, len, skip, own_ct_fp = 0;
1732 unsigned char value, *b, *b1, *b2, *b3;
1733 unsigned char *cp, *ep;
1734 char buffer[BUFSIZ];
1735 /* sbeck -- handle suffixes */
1740 b = (unsigned char *) &bits;
1741 b1 = &b[endian > 0 ? 1 : 2];
1742 b2 = &b[endian > 0 ? 2 : 1];
1743 b3 = &b[endian > 0 ? 3 : 0];
1747 fseek (ce->ce_fp, 0L, SEEK_SET);
1752 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1753 content_error (ce->ce_file, ct, "unable to fopen for reading");
1759 if (*file == NULL) {
1760 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1763 ce->ce_file = add (*file, NULL);
1767 /* sbeck@cise.ufl.edu -- handle suffixes */
1769 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1770 invo_name, ci->ci_type, ci->ci_subtype);
1771 cp = context_find (buffer);
1772 if (cp == NULL || *cp == '\0') {
1773 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1775 cp = context_find (buffer);
1777 if (cp != NULL && *cp != '\0') {
1778 if (ce->ce_unlink) {
1779 // Temporary file already exists, so we rename to
1780 // version with extension.
1781 char *file_org = strdup(ce->ce_file);
1782 ce->ce_file = add (cp, ce->ce_file);
1783 if (rename(file_org, ce->ce_file)) {
1784 adios (ce->ce_file, "unable to rename %s to ", file_org);
1789 ce->ce_file = add (cp, ce->ce_file);
1793 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1794 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1798 if ((len = ct->c_end - ct->c_begin) < 0)
1799 adios (NULL, "internal error(1)");
1802 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1803 content_error (ct->c_file, ct, "unable to open for reading");
1809 if ((digested = ct->c_digested))
1810 MD5Init (&mdContext);
1816 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1818 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1820 content_error (ct->c_file, ct, "error reading from");
1824 content_error (NULL, ct, "premature eof");
1832 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1837 if (skip || (*cp & 0x80)
1838 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1840 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1842 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1845 content_error (NULL, ct,
1846 "invalid BASE64 encoding -- continuing");
1850 bits |= value << bitno;
1852 if ((bitno -= 6) < 0) {
1853 putc ((char) *b1, ce->ce_fp);
1855 MD5Update (&mdContext, b1, 1);
1857 putc ((char) *b2, ce->ce_fp);
1859 MD5Update (&mdContext, b2, 1);
1861 putc ((char) *b3, ce->ce_fp);
1863 MD5Update (&mdContext, b3, 1);
1867 if (ferror (ce->ce_fp)) {
1868 content_error (ce->ce_file, ct,
1869 "error writing to");
1872 bitno = 18, bits = 0L, skip = 0;
1878 goto self_delimiting;
1887 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1889 content_error (NULL, ct, "invalid BASE64 encoding");
1894 fseek (ct->c_fp, 0L, SEEK_SET);
1896 if (fflush (ce->ce_fp)) {
1897 content_error (ce->ce_file, ct, "error writing to");
1902 unsigned char digest[16];
1904 MD5Final (digest, &mdContext);
1905 if (memcmp((char *) digest, (char *) ct->c_digest,
1906 sizeof(digest) / sizeof(digest[0])))
1907 content_error (NULL, ct,
1908 "content integrity suspect (digest mismatch) -- continuing");
1911 fprintf (stderr, "content integrity confirmed\n");
1914 fseek (ce->ce_fp, 0L, SEEK_SET);
1917 *file = ce->ce_file;
1922 return fileno (ce->ce_fp);
1929 free_encoding (ct, 0);
1938 static char hex2nib[0x80] = {
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1946 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1951 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1952 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1961 return init_encoding (ct, openQuoted);
1966 openQuoted (CT ct, char **file)
1968 int cc, digested, len, quoted, own_ct_fp = 0;
1969 unsigned char *cp, *ep;
1970 char buffer[BUFSIZ];
1973 /* sbeck -- handle suffixes */
1979 fseek (ce->ce_fp, 0L, SEEK_SET);
1984 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1985 content_error (ce->ce_file, ct, "unable to fopen for reading");
1991 if (*file == NULL) {
1992 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1995 ce->ce_file = add (*file, NULL);
1999 /* sbeck@cise.ufl.edu -- handle suffixes */
2001 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2002 invo_name, ci->ci_type, ci->ci_subtype);
2003 cp = context_find (buffer);
2004 if (cp == NULL || *cp == '\0') {
2005 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2007 cp = context_find (buffer);
2009 if (cp != NULL && *cp != '\0') {
2010 if (ce->ce_unlink) {
2011 // Temporary file already exists, so we rename to
2012 // version with extension.
2013 char *file_org = strdup(ce->ce_file);
2014 ce->ce_file = add (cp, ce->ce_file);
2015 if (rename(file_org, ce->ce_file)) {
2016 adios (ce->ce_file, "unable to rename %s to ", file_org);
2021 ce->ce_file = add (cp, ce->ce_file);
2025 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2026 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2030 if ((len = ct->c_end - ct->c_begin) < 0)
2031 adios (NULL, "internal error(2)");
2034 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2035 content_error (ct->c_file, ct, "unable to open for reading");
2041 if ((digested = ct->c_digested))
2042 MD5Init (&mdContext);
2049 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2051 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2052 content_error (NULL, ct, "premature eof");
2056 if ((cc = strlen (buffer)) > len)
2060 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2065 for (; cp < ep; cp++) {
2067 /* in an escape sequence */
2069 /* at byte 1 of an escape sequence */
2070 mask = hex2nib[*cp & 0x7f];
2071 /* next is byte 2 */
2074 /* at byte 2 of an escape sequence */
2076 mask |= hex2nib[*cp & 0x7f];
2077 putc (mask, ce->ce_fp);
2079 MD5Update (&mdContext, &mask, 1);
2080 if (ferror (ce->ce_fp)) {
2081 content_error (ce->ce_file, ct, "error writing to");
2084 /* finished escape sequence; next may be literal or a new
2085 * escape sequence */
2088 /* on to next byte */
2092 /* not in an escape sequence */
2094 /* starting an escape sequence, or invalid '='? */
2095 if (cp + 1 < ep && cp[1] == '\n') {
2096 /* "=\n" soft line break, eat the \n */
2100 if (cp + 1 >= ep || cp + 2 >= ep) {
2101 /* We don't have 2 bytes left, so this is an invalid
2102 * escape sequence; just show the raw bytes (below). */
2103 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2104 /* Next 2 bytes are hex digits, making this a valid escape
2105 * sequence; let's decode it (above). */
2109 /* One or both of the next 2 is out of range, making this
2110 * an invalid escape sequence; just show the raw bytes
2115 /* Just show the raw byte. */
2116 putc (*cp, ce->ce_fp);
2119 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2121 MD5Update (&mdContext, (unsigned char *) cp, 1);
2124 if (ferror (ce->ce_fp)) {
2125 content_error (ce->ce_file, ct, "error writing to");
2131 content_error (NULL, ct,
2132 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2136 fseek (ct->c_fp, 0L, SEEK_SET);
2138 if (fflush (ce->ce_fp)) {
2139 content_error (ce->ce_file, ct, "error writing to");
2144 unsigned char digest[16];
2146 MD5Final (digest, &mdContext);
2147 if (memcmp((char *) digest, (char *) ct->c_digest,
2148 sizeof(digest) / sizeof(digest[0])))
2149 content_error (NULL, ct,
2150 "content integrity suspect (digest mismatch) -- continuing");
2153 fprintf (stderr, "content integrity confirmed\n");
2156 fseek (ce->ce_fp, 0L, SEEK_SET);
2159 *file = ce->ce_file;
2164 return fileno (ce->ce_fp);
2167 free_encoding (ct, 0);
2183 if (init_encoding (ct, open7Bit) == NOTOK)
2186 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2192 open7Bit (CT ct, char **file)
2194 int cc, fd, len, own_ct_fp = 0;
2195 char buffer[BUFSIZ];
2196 /* sbeck -- handle suffixes */
2203 fseek (ce->ce_fp, 0L, SEEK_SET);
2208 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2209 content_error (ce->ce_file, ct, "unable to fopen for reading");
2215 if (*file == NULL) {
2216 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2219 ce->ce_file = add (*file, NULL);
2223 /* sbeck@cise.ufl.edu -- handle suffixes */
2225 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2226 invo_name, ci->ci_type, ci->ci_subtype);
2227 cp = context_find (buffer);
2228 if (cp == NULL || *cp == '\0') {
2229 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2231 cp = context_find (buffer);
2233 if (cp != NULL && *cp != '\0') {
2234 if (ce->ce_unlink) {
2235 // Temporary file already exists, so we rename to
2236 // version with extension.
2237 char *file_org = strdup(ce->ce_file);
2238 ce->ce_file = add (cp, ce->ce_file);
2239 if (rename(file_org, ce->ce_file)) {
2240 adios (ce->ce_file, "unable to rename %s to ", file_org);
2245 ce->ce_file = add (cp, ce->ce_file);
2249 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2250 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2254 if (ct->c_type == CT_MULTIPART) {
2256 CI ci = &ct->c_ctinfo;
2259 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2260 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2261 + 1 + strlen (ci->ci_subtype);
2262 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2263 putc (';', ce->ce_fp);
2266 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2268 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2269 fputs ("\n\t", ce->ce_fp);
2272 putc (' ', ce->ce_fp);
2275 fprintf (ce->ce_fp, "%s", buffer);
2279 if (ci->ci_comment) {
2280 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2281 fputs ("\n\t", ce->ce_fp);
2285 putc (' ', ce->ce_fp);
2288 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2291 fprintf (ce->ce_fp, "\n");
2293 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2295 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2297 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2298 fprintf (ce->ce_fp, "\n");
2301 if ((len = ct->c_end - ct->c_begin) < 0)
2302 adios (NULL, "internal error(3)");
2305 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2306 content_error (ct->c_file, ct, "unable to open for reading");
2312 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2314 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2316 content_error (ct->c_file, ct, "error reading from");
2320 content_error (NULL, ct, "premature eof");
2328 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2329 if (ferror (ce->ce_fp)) {
2330 content_error (ce->ce_file, ct, "error writing to");
2335 fseek (ct->c_fp, 0L, SEEK_SET);
2337 if (fflush (ce->ce_fp)) {
2338 content_error (ce->ce_file, ct, "error writing to");
2342 fseek (ce->ce_fp, 0L, SEEK_SET);
2345 *file = ce->ce_file;
2350 return fileno (ce->ce_fp);
2353 free_encoding (ct, 0);
2367 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2369 char cachefile[BUFSIZ];
2372 fseek (ce->ce_fp, 0L, SEEK_SET);
2377 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2378 content_error (ce->ce_file, ct, "unable to fopen for reading");
2384 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2385 cachefile, sizeof(cachefile)) != NOTOK) {
2386 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2387 ce->ce_file = getcpy (cachefile);
2391 admonish (cachefile, "unable to fopen for reading");
2398 *file = ce->ce_file;
2399 *fd = fileno (ce->ce_fp);
2410 return init_encoding (ct, openFile);
2415 openFile (CT ct, char **file)
2418 char cachefile[BUFSIZ];
2419 struct exbody *e = ct->c_ctexbody;
2420 CE ce = ct->c_cefile;
2422 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2434 content_error (NULL, ct, "missing name parameter");
2438 ce->ce_file = getcpy (e->eb_name);
2441 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2442 content_error (ce->ce_file, ct, "unable to fopen for reading");
2446 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2447 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2448 cachefile, sizeof(cachefile)) != NOTOK) {
2452 mask = umask (cachetype ? ~m_gmprot () : 0222);
2453 if ((fp = fopen (cachefile, "w"))) {
2455 char buffer[BUFSIZ];
2456 FILE *gp = ce->ce_fp;
2458 fseek (gp, 0L, SEEK_SET);
2460 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2462 fwrite (buffer, sizeof(*buffer), cc, fp);
2466 admonish (ce->ce_file, "error reading");
2471 admonish (cachefile, "error writing");
2479 fseek (ce->ce_fp, 0L, SEEK_SET);
2480 *file = ce->ce_file;
2481 return fileno (ce->ce_fp);
2491 return init_encoding (ct, openFTP);
2496 openFTP (CT ct, char **file)
2498 int cachetype, caching, fd;
2500 char *bp, *ftp, *user, *pass;
2501 char buffer[BUFSIZ], cachefile[BUFSIZ];
2504 static char *username = NULL;
2505 static char *password = NULL;
2510 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2516 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2527 if (!e->eb_name || !e->eb_site) {
2528 content_error (NULL, ct, "missing %s parameter",
2529 e->eb_name ? "site": "name");
2536 pidcheck (pidwait (xpid, NOTOK));
2540 /* Get the buffer ready to go */
2542 buflen = sizeof(buffer);
2545 * Construct the query message for user
2547 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2553 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2559 snprintf (bp, buflen, "\n using %sFTP from site %s",
2560 e->eb_flags ? "anonymous " : "", e->eb_site);
2565 if (e->eb_size > 0) {
2566 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2571 snprintf (bp, buflen, "? ");
2574 * Now, check the answer
2576 if (!getanswer (buffer))
2581 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2585 ruserpass (e->eb_site, &username, &password);
2590 ce->ce_unlink = (*file == NULL);
2592 cachefile[0] = '\0';
2593 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2594 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2595 cachefile, sizeof(cachefile)) != NOTOK) {
2596 if (*file == NULL) {
2603 ce->ce_file = add (*file, NULL);
2605 ce->ce_file = add (cachefile, NULL);
2607 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2609 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2610 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2615 int child_id, i, vecp;
2619 vec[vecp++] = r1bindex (ftp, '/');
2620 vec[vecp++] = e->eb_site;
2623 vec[vecp++] = e->eb_dir;
2624 vec[vecp++] = e->eb_name;
2625 vec[vecp++] = ce->ce_file,
2626 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2627 ? "ascii" : "binary";
2632 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2636 adios ("fork", "unable to");
2640 close (fileno (ce->ce_fp));
2642 fprintf (stderr, "unable to exec ");
2648 if (pidXwait (child_id, NULL)) {
2649 username = password = NULL;
2659 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2664 mask = umask (cachetype ? ~m_gmprot () : 0222);
2665 if ((fp = fopen (cachefile, "w"))) {
2667 FILE *gp = ce->ce_fp;
2669 fseek (gp, 0L, SEEK_SET);
2671 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2673 fwrite (buffer, sizeof(*buffer), cc, fp);
2677 admonish (ce->ce_file, "error reading");
2682 admonish (cachefile, "error writing");
2691 fseek (ce->ce_fp, 0L, SEEK_SET);
2692 *file = ce->ce_file;
2693 return fileno (ce->ce_fp);
2704 return init_encoding (ct, openMail);
2709 openMail (CT ct, char **file)
2711 int child_id, fd, i, vecp;
2713 char *bp, buffer[BUFSIZ], *vec[7];
2714 struct exbody *e = ct->c_ctexbody;
2715 CE ce = ct->c_cefile;
2717 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2728 if (!e->eb_server) {
2729 content_error (NULL, ct, "missing server parameter");
2736 pidcheck (pidwait (xpid, NOTOK));
2740 /* Get buffer ready to go */
2742 buflen = sizeof(buffer);
2744 /* Now, construct query message */
2745 snprintf (bp, buflen, "Retrieve content");
2751 snprintf (bp, buflen, " %s", e->eb_partno);
2757 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2759 e->eb_subject ? e->eb_subject : e->eb_body);
2761 /* Now, check answer */
2762 if (!getanswer (buffer))
2766 vec[vecp++] = r1bindex (mailproc, '/');
2767 vec[vecp++] = e->eb_server;
2768 vec[vecp++] = "-subject";
2769 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2770 vec[vecp++] = "-body";
2771 vec[vecp++] = e->eb_body;
2774 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2778 advise ("fork", "unable to");
2782 execvp (mailproc, vec);
2783 fprintf (stderr, "unable to exec ");
2789 if (pidXwait (child_id, NULL) == OK)
2790 advise (NULL, "request sent");
2794 if (*file == NULL) {
2795 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2798 ce->ce_file = add (*file, NULL);
2802 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2803 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2807 /* showproc is for mhshow and mhstore, though mhlist -debug
2808 * prints it, too. */
2810 free (ct->c_showproc);
2811 ct->c_showproc = add ("true", NULL);
2813 fseek (ce->ce_fp, 0L, SEEK_SET);
2814 *file = ce->ce_file;
2815 return fileno (ce->ce_fp);
2820 readDigest (CT ct, char *cp)
2825 unsigned char *dp, value, *ep;
2826 unsigned char *b, *b1, *b2, *b3;
2828 b = (unsigned char *) &bits,
2829 b1 = &b[endian > 0 ? 1 : 2],
2830 b2 = &b[endian > 0 ? 2 : 1],
2831 b3 = &b[endian > 0 ? 3 : 0];
2836 for (ep = (dp = ct->c_digest)
2837 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2842 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2844 fprintf (stderr, "invalid BASE64 encoding\n");
2848 bits |= value << bitno;
2850 if ((bitno -= 6) < 0) {
2851 if (dp + (3 - skip) > ep)
2852 goto invalid_digest;
2867 goto self_delimiting;
2872 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2882 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2890 fprintf (stderr, "MD5 digest=");
2891 for (dp = ct->c_digest; dp < ep; dp++)
2892 fprintf (stderr, "%02x", *dp & 0xff);
2893 fprintf (stderr, "\n");