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;
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)");
1801 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1802 content_error (ct->c_file, ct, "unable to open for reading");
1806 if ((digested = ct->c_digested))
1807 MD5Init (&mdContext);
1813 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1815 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1817 content_error (ct->c_file, ct, "error reading from");
1821 content_error (NULL, ct, "premature eof");
1829 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1834 if (skip || (*cp & 0x80)
1835 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1837 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1839 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1842 content_error (NULL, ct,
1843 "invalid BASE64 encoding -- continuing");
1847 bits |= value << bitno;
1849 if ((bitno -= 6) < 0) {
1850 putc ((char) *b1, ce->ce_fp);
1852 MD5Update (&mdContext, b1, 1);
1854 putc ((char) *b2, ce->ce_fp);
1856 MD5Update (&mdContext, b2, 1);
1858 putc ((char) *b3, ce->ce_fp);
1860 MD5Update (&mdContext, b3, 1);
1864 if (ferror (ce->ce_fp)) {
1865 content_error (ce->ce_file, ct,
1866 "error writing to");
1869 bitno = 18, bits = 0L, skip = 0;
1875 goto self_delimiting;
1884 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1886 content_error (NULL, ct, "invalid BASE64 encoding");
1891 fseek (ct->c_fp, 0L, SEEK_SET);
1893 if (fflush (ce->ce_fp)) {
1894 content_error (ce->ce_file, ct, "error writing to");
1899 unsigned char digest[16];
1901 MD5Final (digest, &mdContext);
1902 if (memcmp((char *) digest, (char *) ct->c_digest,
1903 sizeof(digest) / sizeof(digest[0])))
1904 content_error (NULL, ct,
1905 "content integrity suspect (digest mismatch) -- continuing");
1908 fprintf (stderr, "content integrity confirmed\n");
1911 fseek (ce->ce_fp, 0L, SEEK_SET);
1914 *file = ce->ce_file;
1915 return fileno (ce->ce_fp);
1918 free_encoding (ct, 0);
1927 static char hex2nib[0x80] = {
1928 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1929 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1930 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1935 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 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
1950 return init_encoding (ct, openQuoted);
1955 openQuoted (CT ct, char **file)
1957 int cc, digested, len, quoted;
1958 unsigned char *cp, *ep;
1959 char buffer[BUFSIZ];
1962 /* sbeck -- handle suffixes */
1968 fseek (ce->ce_fp, 0L, SEEK_SET);
1973 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1974 content_error (ce->ce_file, ct, "unable to fopen for reading");
1980 if (*file == NULL) {
1981 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1984 ce->ce_file = add (*file, NULL);
1988 /* sbeck@cise.ufl.edu -- handle suffixes */
1990 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1991 invo_name, ci->ci_type, ci->ci_subtype);
1992 cp = context_find (buffer);
1993 if (cp == NULL || *cp == '\0') {
1994 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1996 cp = context_find (buffer);
1998 if (cp != NULL && *cp != '\0') {
1999 if (ce->ce_unlink) {
2000 // Temporary file already exists, so we rename to
2001 // version with extension.
2002 char *file_org = strdup(ce->ce_file);
2003 ce->ce_file = add (cp, ce->ce_file);
2004 if (rename(file_org, ce->ce_file)) {
2005 adios (ce->ce_file, "unable to rename %s to ", file_org);
2010 ce->ce_file = add (cp, ce->ce_file);
2014 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2015 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2019 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2020 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2024 if ((len = ct->c_end - ct->c_begin) < 0)
2025 adios (NULL, "internal error(2)");
2027 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2028 content_error (ct->c_file, ct, "unable to open for reading");
2032 if ((digested = ct->c_digested))
2033 MD5Init (&mdContext);
2040 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2042 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2043 content_error (NULL, ct, "premature eof");
2047 if ((cc = strlen (buffer)) > len)
2051 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2056 for (; cp < ep; cp++) {
2058 /* in an escape sequence */
2060 /* at byte 1 of an escape sequence */
2061 mask = hex2nib[*cp & 0x7f];
2062 /* next is byte 2 */
2065 /* at byte 2 of an escape sequence */
2067 mask |= hex2nib[*cp & 0x7f];
2068 putc (mask, ce->ce_fp);
2070 MD5Update (&mdContext, &mask, 1);
2071 if (ferror (ce->ce_fp)) {
2072 content_error (ce->ce_file, ct, "error writing to");
2075 /* finished escape sequence; next may be literal or a new
2076 * escape sequence */
2079 /* on to next byte */
2083 /* not in an escape sequence */
2085 /* starting an escape sequence, or invalid '='? */
2086 if (cp + 1 < ep && cp[1] == '\n') {
2087 /* "=\n" soft line break, eat the \n */
2091 if (cp + 1 >= ep || cp + 2 >= ep) {
2092 /* We don't have 2 bytes left, so this is an invalid
2093 * escape sequence; just show the raw bytes (below). */
2094 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2095 /* Next 2 bytes are hex digits, making this a valid escape
2096 * sequence; let's decode it (above). */
2100 /* One or both of the next 2 is out of range, making this
2101 * an invalid escape sequence; just show the raw bytes
2106 /* Just show the raw byte. */
2107 putc (*cp, ce->ce_fp);
2110 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2112 MD5Update (&mdContext, (unsigned char *) cp, 1);
2115 if (ferror (ce->ce_fp)) {
2116 content_error (ce->ce_file, ct, "error writing to");
2122 content_error (NULL, ct,
2123 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2127 fseek (ct->c_fp, 0L, SEEK_SET);
2129 if (fflush (ce->ce_fp)) {
2130 content_error (ce->ce_file, ct, "error writing to");
2135 unsigned char digest[16];
2137 MD5Final (digest, &mdContext);
2138 if (memcmp((char *) digest, (char *) ct->c_digest,
2139 sizeof(digest) / sizeof(digest[0])))
2140 content_error (NULL, ct,
2141 "content integrity suspect (digest mismatch) -- continuing");
2144 fprintf (stderr, "content integrity confirmed\n");
2147 fseek (ce->ce_fp, 0L, SEEK_SET);
2150 *file = ce->ce_file;
2151 return fileno (ce->ce_fp);
2154 free_encoding (ct, 0);
2166 if (init_encoding (ct, open7Bit) == NOTOK)
2169 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2175 open7Bit (CT ct, char **file)
2178 char buffer[BUFSIZ];
2179 /* sbeck -- handle suffixes */
2186 fseek (ce->ce_fp, 0L, SEEK_SET);
2191 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2192 content_error (ce->ce_file, ct, "unable to fopen for reading");
2198 if (*file == NULL) {
2199 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2202 ce->ce_file = add (*file, NULL);
2206 /* sbeck@cise.ufl.edu -- handle suffixes */
2208 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2209 invo_name, ci->ci_type, ci->ci_subtype);
2210 cp = context_find (buffer);
2211 if (cp == NULL || *cp == '\0') {
2212 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2214 cp = context_find (buffer);
2216 if (cp != NULL && *cp != '\0') {
2217 if (ce->ce_unlink) {
2218 // Temporary file already exists, so we rename to
2219 // version with extension.
2220 char *file_org = strdup(ce->ce_file);
2221 ce->ce_file = add (cp, ce->ce_file);
2222 if (rename(file_org, ce->ce_file)) {
2223 adios (ce->ce_file, "unable to rename %s to ", file_org);
2228 ce->ce_file = add (cp, ce->ce_file);
2232 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2233 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2237 if (ct->c_type == CT_MULTIPART) {
2239 CI ci = &ct->c_ctinfo;
2242 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2243 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2244 + 1 + strlen (ci->ci_subtype);
2245 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2246 putc (';', ce->ce_fp);
2249 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2251 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2252 fputs ("\n\t", ce->ce_fp);
2255 putc (' ', ce->ce_fp);
2258 fprintf (ce->ce_fp, "%s", buffer);
2262 if (ci->ci_comment) {
2263 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2264 fputs ("\n\t", ce->ce_fp);
2268 putc (' ', ce->ce_fp);
2271 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2274 fprintf (ce->ce_fp, "\n");
2276 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2278 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2280 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2281 fprintf (ce->ce_fp, "\n");
2284 if ((len = ct->c_end - ct->c_begin) < 0)
2285 adios (NULL, "internal error(3)");
2287 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2288 content_error (ct->c_file, ct, "unable to open for reading");
2292 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2294 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2296 content_error (ct->c_file, ct, "error reading from");
2300 content_error (NULL, ct, "premature eof");
2308 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2309 if (ferror (ce->ce_fp)) {
2310 content_error (ce->ce_file, ct, "error writing to");
2315 fseek (ct->c_fp, 0L, SEEK_SET);
2317 if (fflush (ce->ce_fp)) {
2318 content_error (ce->ce_file, ct, "error writing to");
2322 fseek (ce->ce_fp, 0L, SEEK_SET);
2325 *file = ce->ce_file;
2326 return fileno (ce->ce_fp);
2329 free_encoding (ct, 0);
2339 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2341 char cachefile[BUFSIZ];
2344 fseek (ce->ce_fp, 0L, SEEK_SET);
2349 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2350 content_error (ce->ce_file, ct, "unable to fopen for reading");
2356 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2357 cachefile, sizeof(cachefile)) != NOTOK) {
2358 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2359 ce->ce_file = getcpy (cachefile);
2363 admonish (cachefile, "unable to fopen for reading");
2370 *file = ce->ce_file;
2371 *fd = fileno (ce->ce_fp);
2382 return init_encoding (ct, openFile);
2387 openFile (CT ct, char **file)
2390 char cachefile[BUFSIZ];
2391 struct exbody *e = ct->c_ctexbody;
2392 CE ce = ct->c_cefile;
2394 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2406 content_error (NULL, ct, "missing name parameter");
2410 ce->ce_file = getcpy (e->eb_name);
2413 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2414 content_error (ce->ce_file, ct, "unable to fopen for reading");
2418 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2419 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2420 cachefile, sizeof(cachefile)) != NOTOK) {
2424 mask = umask (cachetype ? ~m_gmprot () : 0222);
2425 if ((fp = fopen (cachefile, "w"))) {
2427 char buffer[BUFSIZ];
2428 FILE *gp = ce->ce_fp;
2430 fseek (gp, 0L, SEEK_SET);
2432 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2434 fwrite (buffer, sizeof(*buffer), cc, fp);
2438 admonish (ce->ce_file, "error reading");
2443 admonish (cachefile, "error writing");
2451 fseek (ce->ce_fp, 0L, SEEK_SET);
2452 *file = ce->ce_file;
2453 return fileno (ce->ce_fp);
2463 return init_encoding (ct, openFTP);
2468 openFTP (CT ct, char **file)
2470 int cachetype, caching, fd;
2472 char *bp, *ftp, *user, *pass;
2473 char buffer[BUFSIZ], cachefile[BUFSIZ];
2476 static char *username = NULL;
2477 static char *password = NULL;
2482 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2488 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2499 if (!e->eb_name || !e->eb_site) {
2500 content_error (NULL, ct, "missing %s parameter",
2501 e->eb_name ? "site": "name");
2508 pidcheck (pidwait (xpid, NOTOK));
2512 /* Get the buffer ready to go */
2514 buflen = sizeof(buffer);
2517 * Construct the query message for user
2519 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2525 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2531 snprintf (bp, buflen, "\n using %sFTP from site %s",
2532 e->eb_flags ? "anonymous " : "", e->eb_site);
2537 if (e->eb_size > 0) {
2538 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2543 snprintf (bp, buflen, "? ");
2546 * Now, check the answer
2548 if (!getanswer (buffer))
2553 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2557 ruserpass (e->eb_site, &username, &password);
2562 ce->ce_unlink = (*file == NULL);
2564 cachefile[0] = '\0';
2565 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2566 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2567 cachefile, sizeof(cachefile)) != NOTOK) {
2568 if (*file == NULL) {
2575 ce->ce_file = add (*file, NULL);
2577 ce->ce_file = add (cachefile, NULL);
2579 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2581 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2582 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2587 int child_id, i, vecp;
2591 vec[vecp++] = r1bindex (ftp, '/');
2592 vec[vecp++] = e->eb_site;
2595 vec[vecp++] = e->eb_dir;
2596 vec[vecp++] = e->eb_name;
2597 vec[vecp++] = ce->ce_file,
2598 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2599 ? "ascii" : "binary";
2604 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2608 adios ("fork", "unable to");
2612 close (fileno (ce->ce_fp));
2614 fprintf (stderr, "unable to exec ");
2620 if (pidXwait (child_id, NULL)) {
2621 username = password = NULL;
2631 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2636 mask = umask (cachetype ? ~m_gmprot () : 0222);
2637 if ((fp = fopen (cachefile, "w"))) {
2639 FILE *gp = ce->ce_fp;
2641 fseek (gp, 0L, SEEK_SET);
2643 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2645 fwrite (buffer, sizeof(*buffer), cc, fp);
2649 admonish (ce->ce_file, "error reading");
2654 admonish (cachefile, "error writing");
2663 fseek (ce->ce_fp, 0L, SEEK_SET);
2664 *file = ce->ce_file;
2665 return fileno (ce->ce_fp);
2676 return init_encoding (ct, openMail);
2681 openMail (CT ct, char **file)
2683 int child_id, fd, i, vecp;
2685 char *bp, buffer[BUFSIZ], *vec[7];
2686 struct exbody *e = ct->c_ctexbody;
2687 CE ce = ct->c_cefile;
2689 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2700 if (!e->eb_server) {
2701 content_error (NULL, ct, "missing server parameter");
2708 pidcheck (pidwait (xpid, NOTOK));
2712 /* Get buffer ready to go */
2714 buflen = sizeof(buffer);
2716 /* Now, construct query message */
2717 snprintf (bp, buflen, "Retrieve content");
2723 snprintf (bp, buflen, " %s", e->eb_partno);
2729 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2731 e->eb_subject ? e->eb_subject : e->eb_body);
2733 /* Now, check answer */
2734 if (!getanswer (buffer))
2738 vec[vecp++] = r1bindex (mailproc, '/');
2739 vec[vecp++] = e->eb_server;
2740 vec[vecp++] = "-subject";
2741 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2742 vec[vecp++] = "-body";
2743 vec[vecp++] = e->eb_body;
2746 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2750 advise ("fork", "unable to");
2754 execvp (mailproc, vec);
2755 fprintf (stderr, "unable to exec ");
2761 if (pidXwait (child_id, NULL) == OK)
2762 advise (NULL, "request sent");
2766 if (*file == NULL) {
2767 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2770 ce->ce_file = add (*file, NULL);
2774 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2775 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2779 /* showproc is for mhshow and mhstore, though mhlist -debug
2780 * prints it, too. */
2782 free (ct->c_showproc);
2783 ct->c_showproc = add ("true", NULL);
2785 fseek (ce->ce_fp, 0L, SEEK_SET);
2786 *file = ce->ce_file;
2787 return fileno (ce->ce_fp);
2792 readDigest (CT ct, char *cp)
2797 unsigned char *dp, value, *ep;
2798 unsigned char *b, *b1, *b2, *b3;
2800 b = (unsigned char *) &bits,
2801 b1 = &b[endian > 0 ? 1 : 2],
2802 b2 = &b[endian > 0 ? 2 : 1],
2803 b3 = &b[endian > 0 ? 3 : 0];
2808 for (ep = (dp = ct->c_digest)
2809 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2814 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2816 fprintf (stderr, "invalid BASE64 encoding\n");
2820 bits |= value << bitno;
2822 if ((bitno -= 6) < 0) {
2823 if (dp + (3 - skip) > ep)
2824 goto invalid_digest;
2839 goto self_delimiting;
2844 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2854 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2862 fprintf (stderr, "MD5 digest=");
2863 for (dp = ct->c_digest; dp < ep; dp++)
2864 fprintf (stderr, "%02x", *dp & 0xff);
2865 fprintf (stderr, "\n");