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 pid_t xpid; /* mhshowsbr.c */
28 extern int rcachesw; /* mhcachesbr.c */
29 extern int wcachesw; /* mhcachesbr.c */
31 int checksw = 0; /* check Content-MD5 field */
34 * Directory to place temp files. This must
35 * be set before these routines are called.
40 * Structures for TEXT messages
42 struct k2v SubText[] = {
43 { "plain", TEXT_PLAIN },
44 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
45 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
46 { NULL, TEXT_UNKNOWN } /* this one must be last! */
49 struct k2v Charset[] = {
50 { "us-ascii", CHARSET_USASCII },
51 { "iso-8859-1", CHARSET_LATIN },
52 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
56 * Structures for MULTIPART messages
58 struct k2v SubMultiPart[] = {
59 { "mixed", MULTI_MIXED },
60 { "alternative", MULTI_ALTERNATE },
61 { "digest", MULTI_DIGEST },
62 { "parallel", MULTI_PARALLEL },
63 { NULL, MULTI_UNKNOWN } /* this one must be last! */
67 * Structures for MESSAGE messages
69 struct k2v SubMessage[] = {
70 { "rfc822", MESSAGE_RFC822 },
71 { "partial", MESSAGE_PARTIAL },
72 { "external-body", MESSAGE_EXTERNAL },
73 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
77 * Structure for APPLICATION messages
79 struct k2v SubApplication[] = {
80 { "octet-stream", APPLICATION_OCTETS },
81 { "postscript", APPLICATION_POSTSCRIPT },
82 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
87 int find_cache (CT, int, int *, char *, char *, int);
90 int part_ok (CT, int);
91 int type_ok (CT, int);
92 void content_error (char *, CT, char *, ...);
95 void free_content (CT);
96 void free_encoding (CT, int);
101 static CT get_content (FILE *, char *, int);
102 static int get_comment (CT, unsigned char **, int);
104 static int InitGeneric (CT);
105 static int InitText (CT);
106 static int InitMultiPart (CT);
107 static void reverse_parts (CT);
108 static int InitMessage (CT);
109 static int InitApplication (CT);
110 static int init_encoding (CT, OpenCEFunc);
111 static unsigned long size_encoding (CT);
112 static int InitBase64 (CT);
113 static int openBase64 (CT, char **);
114 static int InitQuoted (CT);
115 static int openQuoted (CT, char **);
116 static int Init7Bit (CT);
117 static int openExternal (CT, CT, CE, char **, int *);
118 static int InitFile (CT);
119 static int openFile (CT, char **);
120 static int InitFTP (CT);
121 static int openFTP (CT, char **);
122 static int InitMail (CT);
123 static int openMail (CT, char **);
124 static int readDigest (CT, char *);
126 struct str2init str2cts[] = {
127 { "application", CT_APPLICATION, InitApplication },
128 { "audio", CT_AUDIO, InitGeneric },
129 { "image", CT_IMAGE, InitGeneric },
130 { "message", CT_MESSAGE, InitMessage },
131 { "multipart", CT_MULTIPART, InitMultiPart },
132 { "text", CT_TEXT, InitText },
133 { "video", CT_VIDEO, InitGeneric },
134 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
135 { NULL, CT_UNKNOWN, NULL },
138 struct str2init str2ces[] = {
139 { "base64", CE_BASE64, InitBase64 },
140 { "quoted-printable", CE_QUOTED, InitQuoted },
141 { "8bit", CE_8BIT, Init7Bit },
142 { "7bit", CE_7BIT, Init7Bit },
143 { "binary", CE_BINARY, Init7Bit },
144 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
145 { NULL, CE_UNKNOWN, NULL },
149 * NOTE WELL: si_key MUST NOT have value of NOTOK
151 * si_key is 1 if access method is anonymous.
153 struct str2init str2methods[] = {
154 { "afs", 1, InitFile },
155 { "anon-ftp", 1, InitFTP },
156 { "ftp", 0, InitFTP },
157 { "local-file", 0, InitFile },
158 { "mail-server", 0, InitMail },
164 pidcheck (int status)
166 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
177 * Main entry point for parsing a MIME message or file.
178 * It returns the Content structure for the top level
179 * entity in the file.
183 parse_mime (char *file)
191 * Check if file is actually standard input
193 if ((is_stdin = !(strcmp (file, "-")))) {
194 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
196 advise("mhparse", "unable to create temporary file");
199 file = add (tfile, NULL);
202 while (fgets (buffer, sizeof(buffer), stdin))
206 if (ferror (stdin)) {
208 advise ("stdin", "error reading");
213 advise (file, "error writing");
216 fseek (fp, 0L, SEEK_SET);
217 } else if ((fp = fopen (file, "r")) == NULL) {
218 advise (file, "unable to read");
222 if (!(ct = get_content (fp, file, 1))) {
225 advise (NULL, "unable to decode %s", file);
230 ct->c_unlink = 1; /* temp file to remove */
234 if (ct->c_end == 0L) {
235 fseek (fp, 0L, SEEK_END);
236 ct->c_end = ftell (fp);
239 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
251 * Main routine for reading/parsing the headers
252 * of a message content.
254 * toplevel = 1 # we are at the top level of the message
255 * toplevel = 0 # we are inside message type or multipart type
256 * # other than multipart/digest
257 * toplevel = -1 # we are inside multipart/digest
258 * NB: on failure we will fclose(in)!
262 get_content (FILE *in, char *file, int toplevel)
265 char buf[BUFSIZ], name[NAMESZ];
270 /* allocate the content structure */
271 if (!(ct = (CT) calloc (1, sizeof(*ct))))
272 adios (NULL, "out of memory");
275 ct->c_file = add (file, NULL);
276 ct->c_begin = ftell (ct->c_fp) + 1;
279 * Parse the header fields for this
280 * content into a linked list.
282 for (compnum = 1, state = FLD;;) {
283 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
289 /* get copies of the buffers */
290 np = add (name, NULL);
291 vp = add (buf, NULL);
293 /* if necessary, get rest of field */
294 while (state == FLDPLUS) {
295 state = m_getfld (state, name, buf, sizeof(buf), in);
296 vp = add (buf, vp); /* add to previous value */
299 /* Now add the header data to the list */
300 add_header (ct, np, vp);
302 /* continue, if this isn't the last header field */
303 if (state != FLDEOF) {
304 ct->c_begin = ftell (in) + 1;
311 ct->c_begin = ftell (in) - strlen (buf);
315 ct->c_begin = ftell (in);
320 adios (NULL, "message format error in component #%d", compnum);
323 adios (NULL, "getfld() returned %d", state);
326 /* break out of the loop */
331 * Read the content headers. We will parse the
332 * MIME related header fields into their various
333 * structures and set internal flags related to
334 * content type/subtype, etc.
337 hp = ct->c_first_hf; /* start at first header field */
339 /* Get MIME-Version field */
340 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
343 unsigned char *cp, *dp;
346 advise (NULL, "message %s has multiple %s: fields",
347 ct->c_file, VRSN_FIELD);
350 ct->c_vrsn = add (hp->value, NULL);
352 /* Now, cleanup this field */
355 while (isspace (*cp))
357 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
359 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
364 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
366 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
369 for (dp = cp; istoken (*dp); dp++)
373 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
376 admonish (NULL, "message %s has unknown value for %s: field (%s)",
377 ct->c_file, VRSN_FIELD, cp);
380 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
381 /* Get Content-Type field */
382 struct str2init *s2i;
383 CI ci = &ct->c_ctinfo;
385 /* Check if we've already seen a Content-Type header */
387 advise (NULL, "message %s has multiple %s: fields",
388 ct->c_file, TYPE_FIELD);
392 /* Parse the Content-Type field */
393 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
397 * Set the Init function and the internal
398 * flag for this content type.
400 for (s2i = str2cts; s2i->si_key; s2i++)
401 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
403 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
405 ct->c_type = s2i->si_val;
406 ct->c_ctinitfnx = s2i->si_init;
408 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
409 /* Get Content-Transfer-Encoding field */
411 unsigned char *cp, *dp;
412 struct str2init *s2i;
415 * Check if we've already seen the
416 * Content-Transfer-Encoding field
419 advise (NULL, "message %s has multiple %s: fields",
420 ct->c_file, ENCODING_FIELD);
424 /* get copy of this field */
425 ct->c_celine = cp = add (hp->value, NULL);
427 while (isspace (*cp))
429 for (dp = cp; istoken (*dp); dp++)
435 * Find the internal flag and Init function
436 * for this transfer encoding.
438 for (s2i = str2ces; s2i->si_key; s2i++)
439 if (!mh_strcasecmp (cp, s2i->si_key))
441 if (!s2i->si_key && !uprf (cp, "X-"))
444 ct->c_encoding = s2i->si_val;
446 /* Call the Init function for this encoding */
447 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
450 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
451 /* Get Content-MD5 field */
452 unsigned char *cp, *dp;
458 if (ct->c_digested) {
459 advise (NULL, "message %s has multiple %s: fields",
460 ct->c_file, MD5_FIELD);
464 ep = cp = add (hp->value, NULL); /* get a copy */
466 while (isspace (*cp))
468 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
470 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
475 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
477 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
482 for (dp = cp; *dp && !isspace (*dp); dp++)
490 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
491 /* Get Content-ID field */
492 ct->c_id = add (hp->value, ct->c_id);
494 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
495 /* Get Content-Description field */
496 ct->c_descr = add (hp->value, ct->c_descr);
498 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
499 /* Get Content-Disposition field */
500 ct->c_dispo = add (hp->value, ct->c_dispo);
504 hp = hp->next; /* next header field */
508 * Check if we saw a Content-Type field.
509 * If not, then assign a default value for
510 * it, and the Init function.
514 * If we are inside a multipart/digest message,
515 * so default type is message/rfc822
518 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
520 ct->c_type = CT_MESSAGE;
521 ct->c_ctinitfnx = InitMessage;
524 * Else default type is text/plain
526 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
528 ct->c_type = CT_TEXT;
529 ct->c_ctinitfnx = InitText;
533 /* Use default Transfer-Encoding, if necessary */
535 ct->c_encoding = CE_7BIT;
548 * small routine to add header field to list
552 add_header (CT ct, char *name, char *value)
556 /* allocate header field structure */
557 hp = mh_xmalloc (sizeof(*hp));
559 /* link data into header structure */
564 /* link header structure into the list */
565 if (ct->c_first_hf == NULL) {
566 ct->c_first_hf = hp; /* this is the first */
569 ct->c_last_hf->next = hp; /* add it to the end */
577 /* Make sure that buf contains at least one appearance of name,
578 followed by =. If not, insert both name and value, just after
579 first semicolon, if any. Note that name should not contain a
580 trailing =. And quotes will be added around the value. Typical
581 usage: make sure that a Content-Disposition header contains
582 filename="foo". If it doesn't and value does, use value from
585 incl_name_value (unsigned char *buf, char *name, char *value) {
588 /* Assume that name is non-null. */
590 char *name_plus_equal = concat (name, "=", NULL);
592 if (! strstr (buf, name_plus_equal)) {
595 char *prefix, *suffix;
597 /* Trim trailing space, esp. newline. */
598 for (cp = &buf[strlen (buf) - 1];
599 cp >= buf && isspace (*cp);
604 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
606 /* Insert at first semicolon, if any. If none, append to
608 prefix = add (buf, NULL);
609 if ((cp = strchr (prefix, ';'))) {
610 suffix = concat (cp, NULL);
612 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
616 newbuf = concat (buf, insertion, "\n", NULL);
624 free (name_plus_equal);
630 /* Extract just name_suffix="foo", if any, from value. If there isn't
631 one, return the entire value. Note that, for example, a name_suffix
632 of name will match filename="foo", and return foo. */
634 extract_name_value (char *name_suffix, char *value) {
635 char *extracted_name_value = value;
636 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
637 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
640 free (name_suffix_plus_quote);
641 if (name_suffix_equals) {
642 char *name_suffix_begin;
645 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
646 name_suffix_begin = ++cp;
647 /* Find second \". */
648 for (; *cp != '"'; ++cp) /* empty */;
650 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
651 memcpy (extracted_name_value,
653 cp - name_suffix_begin);
654 extracted_name_value[cp - name_suffix_begin] = '\0';
657 return extracted_name_value;
661 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
662 * directives. Fills in the information of the CTinfo structure.
665 get_ctinfo (unsigned char *cp, CT ct, int magic)
674 i = strlen (invo_name) + 2;
676 /* store copy of Content-Type line */
677 cp = ct->c_ctline = add (cp, NULL);
679 while (isspace (*cp)) /* trim leading spaces */
682 /* change newlines to spaces */
683 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
686 /* trim trailing spaces */
687 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
693 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
695 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
698 for (dp = cp; istoken (*dp); dp++)
701 ci->ci_type = add (cp, NULL); /* store content type */
705 advise (NULL, "invalid %s: field in message %s (empty type)",
706 TYPE_FIELD, ct->c_file);
710 /* down case the content type string */
711 for (dp = ci->ci_type; *dp; dp++)
712 if (isalpha(*dp) && isupper (*dp))
715 while (isspace (*cp))
718 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
723 ci->ci_subtype = add ("", NULL);
728 while (isspace (*cp))
731 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
734 for (dp = cp; istoken (*dp); dp++)
737 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
740 if (!*ci->ci_subtype) {
742 "invalid %s: field in message %s (empty subtype for \"%s\")",
743 TYPE_FIELD, ct->c_file, ci->ci_type);
747 /* down case the content subtype string */
748 for (dp = ci->ci_subtype; *dp; dp++)
749 if (isalpha(*dp) && isupper (*dp))
753 while (isspace (*cp))
756 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
760 * Parse attribute/value pairs given with Content-Type
762 ep = (ap = ci->ci_attrs) + NPARMS;
769 "too many parameters in message %s's %s: field (%d max)",
770 ct->c_file, TYPE_FIELD, NPARMS);
775 while (isspace (*cp))
778 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
783 "extraneous trailing ';' in message %s's %s: parameter list",
784 ct->c_file, TYPE_FIELD);
788 /* down case the attribute name */
789 for (dp = cp; istoken (*dp); dp++)
790 if (isalpha(*dp) && isupper (*dp))
793 for (up = dp; isspace (*dp);)
795 if (dp == cp || *dp != '=') {
797 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
798 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
802 vp = (*ap = add (cp, NULL)) + (up - cp);
804 for (dp++; isspace (*dp);)
807 /* now add the attribute value */
808 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
811 for (cp = ++dp, dp = vp;;) {
816 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
817 ct->c_file, TYPE_FIELD, i, i, "", *ap);
822 if ((c = *cp++) == '\0')
837 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
843 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
844 ct->c_file, TYPE_FIELD, i, i, "", *ap);
849 while (isspace (*cp))
852 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
857 * Get any <Content-Id> given in buffer
859 if (magic && *cp == '<') {
864 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
865 advise (NULL, "invalid ID in message %s", ct->c_file);
871 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
877 while (isspace (*cp))
882 * Get any [Content-Description] given in buffer.
884 if (magic && *cp == '[') {
886 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
890 advise (NULL, "invalid description in message %s", ct->c_file);
898 ct->c_descr = concat (ct->c_descr, "\n", NULL);
904 while (isspace (*cp))
909 * Get any {Content-Disposition} given in buffer.
911 if (magic && *cp == '{') {
913 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
917 advise (NULL, "invalid disposition in message %s", ct->c_file);
925 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
931 while (isspace (*cp))
936 * Check if anything is left over
940 ci->ci_magic = add (cp, NULL);
942 /* If there is a Content-Disposition header and it doesn't
943 have a *filename=, extract it from the magic contents.
944 The r1bindex call skips any leading directory
948 incl_name_value (ct->c_dispo,
950 r1bindex (extract_name_value ("name",
957 "extraneous information in message %s's %s: field\n%*.*s(%s)",
958 ct->c_file, TYPE_FIELD, i, i, "", cp);
966 get_comment (CT ct, unsigned char **ap, int istype)
971 char c, buffer[BUFSIZ], *dp;
983 advise (NULL, "invalid comment in message %s's %s: field",
984 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
989 if ((c = *cp++) == '\0')
1012 if ((dp = ci->ci_comment)) {
1013 ci->ci_comment = concat (dp, " ", buffer, NULL);
1016 ci->ci_comment = add (buffer, NULL);
1020 while (isspace (*cp))
1031 * Handles content types audio, image, and video.
1032 * There's not much to do right here.
1040 return OK; /* not much to do here */
1051 char buffer[BUFSIZ];
1053 char **ap, **ep, *cp;
1056 CI ci = &ct->c_ctinfo;
1058 /* check for missing subtype */
1059 if (!*ci->ci_subtype)
1060 ci->ci_subtype = add ("plain", ci->ci_subtype);
1063 for (kv = SubText; kv->kv_key; kv++)
1064 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1066 ct->c_subtype = kv->kv_value;
1068 /* allocate text character set structure */
1069 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1070 adios (NULL, "out of memory");
1071 ct->c_ctparams = (void *) t;
1073 /* scan for charset parameter */
1074 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1075 if (!mh_strcasecmp (*ap, "charset"))
1078 /* check if content specified a character set */
1080 /* match character set or set to CHARSET_UNKNOWN */
1081 for (kv = Charset; kv->kv_key; kv++) {
1082 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1087 t->tx_charset = kv->kv_value;
1089 t->tx_charset = CHARSET_UNSPECIFIED;
1093 * If we can not handle character set natively,
1094 * then check profile for string to modify the
1095 * terminal or display method.
1097 * termproc is for mhshow, though mhlist -debug prints it, too.
1099 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1100 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1101 if ((cp = context_find (buffer)))
1102 ct->c_termproc = getcpy (cp);
1114 InitMultiPart (CT ct)
1118 unsigned char *cp, *dp;
1120 char *bp, buffer[BUFSIZ];
1121 struct multipart *m;
1123 struct part *part, **next;
1124 CI ci = &ct->c_ctinfo;
1129 * The encoding for multipart messages must be either
1130 * 7bit, 8bit, or binary (per RFC2045).
1132 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1133 && ct->c_encoding != CE_BINARY) {
1135 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1136 ci->ci_type, ci->ci_subtype, ct->c_file);
1141 for (kv = SubMultiPart; kv->kv_key; kv++)
1142 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1144 ct->c_subtype = kv->kv_value;
1147 * Check for "boundary" parameter, which is
1148 * required for multipart messages.
1151 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1152 if (!mh_strcasecmp (*ap, "boundary")) {
1158 /* complain if boundary parameter is missing */
1161 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1162 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1166 /* allocate primary structure for multipart info */
1167 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1168 adios (NULL, "out of memory");
1169 ct->c_ctparams = (void *) m;
1171 /* check if boundary parameter contains only whitespace characters */
1172 for (cp = bp; isspace (*cp); cp++)
1175 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1176 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1180 /* remove trailing whitespace from boundary parameter */
1181 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1186 /* record boundary separators */
1187 m->mp_start = concat (bp, "\n", NULL);
1188 m->mp_stop = concat (bp, "--\n", NULL);
1190 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1191 advise (ct->c_file, "unable to open for reading");
1195 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1197 next = &m->mp_parts;
1201 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1205 pos += strlen (buffer);
1206 if (buffer[0] != '-' || buffer[1] != '-')
1209 if (strcmp (buffer + 2, m->mp_start))
1212 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1213 adios (NULL, "out of memory");
1215 next = &part->mp_next;
1217 if (!(p = get_content (fp, ct->c_file,
1218 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1225 fseek (fp, pos, SEEK_SET);
1228 if (strcmp (buffer + 2, m->mp_start) == 0) {
1232 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1233 if (p->c_end < p->c_begin)
1234 p->c_begin = p->c_end;
1239 if (strcmp (buffer + 2, m->mp_stop) == 0)
1245 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1246 if (!inout && part) {
1248 p->c_end = ct->c_end;
1250 if (p->c_begin >= p->c_end) {
1251 for (next = &m->mp_parts; *next != part;
1252 next = &((*next)->mp_next))
1256 free ((char *) part);
1261 /* reverse the order of the parts for multipart/alternative */
1262 if (ct->c_subtype == MULTI_ALTERNATE)
1266 * label all subparts with part number, and
1267 * then initialize the content of the subpart.
1272 char partnam[BUFSIZ];
1275 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1276 pp = partnam + strlen (partnam);
1281 for (part = m->mp_parts, partnum = 1; part;
1282 part = part->mp_next, partnum++) {
1285 sprintf (pp, "%d", partnum);
1286 p->c_partno = add (partnam, NULL);
1288 /* initialize the content of the subparts */
1289 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1304 * reverse the order of the parts of a multipart
1308 reverse_parts (CT ct)
1311 struct multipart *m;
1312 struct part **base, **bmp, **next, *part;
1314 m = (struct multipart *) ct->c_ctparams;
1316 /* if only one part, just return */
1317 if (!m->mp_parts || !m->mp_parts->mp_next)
1320 /* count number of parts */
1322 for (part = m->mp_parts; part; part = part->mp_next)
1325 /* allocate array of pointers to the parts */
1326 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1327 adios (NULL, "out of memory");
1330 /* point at all the parts */
1331 for (part = m->mp_parts; part; part = part->mp_next)
1335 /* reverse the order of the parts */
1336 next = &m->mp_parts;
1337 for (bmp--; bmp >= base; bmp--) {
1340 next = &part->mp_next;
1344 /* free array of pointers */
1345 free ((char *) base);
1357 CI ci = &ct->c_ctinfo;
1359 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1361 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1362 ci->ci_type, ci->ci_subtype, ct->c_file);
1366 /* check for missing subtype */
1367 if (!*ci->ci_subtype)
1368 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1371 for (kv = SubMessage; kv->kv_key; kv++)
1372 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1374 ct->c_subtype = kv->kv_value;
1376 switch (ct->c_subtype) {
1377 case MESSAGE_RFC822:
1380 case MESSAGE_PARTIAL:
1385 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1386 adios (NULL, "out of memory");
1387 ct->c_ctparams = (void *) p;
1389 /* scan for parameters "id", "number", and "total" */
1390 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1391 if (!mh_strcasecmp (*ap, "id")) {
1392 p->pm_partid = add (*ep, NULL);
1395 if (!mh_strcasecmp (*ap, "number")) {
1396 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1397 || p->pm_partno < 1) {
1400 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1401 *ap, ci->ci_type, ci->ci_subtype,
1402 ct->c_file, TYPE_FIELD);
1407 if (!mh_strcasecmp (*ap, "total")) {
1408 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1417 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1419 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1420 ci->ci_type, ci->ci_subtype,
1421 ct->c_file, TYPE_FIELD);
1427 case MESSAGE_EXTERNAL:
1434 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1435 adios (NULL, "out of memory");
1436 ct->c_ctparams = (void *) e;
1439 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1440 advise (ct->c_file, "unable to open for reading");
1444 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1446 if (!(p = get_content (fp, ct->c_file, 0))) {
1454 if ((exresult = params_external (ct, 0)) != NOTOK
1455 && p->c_ceopenfnx == openMail) {
1459 if ((size = ct->c_end - p->c_begin) <= 0) {
1461 content_error (NULL, ct,
1462 "empty body for access-type=mail-server");
1466 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1467 fseek (p->c_fp, p->c_begin, SEEK_SET);
1469 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1471 adios ("failed", "fread");
1474 adios (NULL, "unexpected EOF from fread");
1477 bp += cc, size -= cc;
1484 p->c_end = p->c_begin;
1489 if (exresult == NOTOK)
1491 if (e->eb_flags == NOTOK)
1494 switch (p->c_type) {
1499 if (p->c_subtype != MESSAGE_RFC822)
1503 e->eb_partno = ct->c_partno;
1505 (*p->c_ctinitfnx) (p);
1520 params_external (CT ct, int composing)
1523 struct exbody *e = (struct exbody *) ct->c_ctparams;
1524 CI ci = &ct->c_ctinfo;
1526 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1527 if (!mh_strcasecmp (*ap, "access-type")) {
1528 struct str2init *s2i;
1529 CT p = e->eb_content;
1531 for (s2i = str2methods; s2i->si_key; s2i++)
1532 if (!mh_strcasecmp (*ep, s2i->si_key))
1536 e->eb_flags = NOTOK;
1537 p->c_encoding = CE_EXTERNAL;
1540 e->eb_access = s2i->si_key;
1541 e->eb_flags = s2i->si_val;
1542 p->c_encoding = CE_EXTERNAL;
1544 /* Call the Init function for this external type */
1545 if ((*s2i->si_init)(p) == NOTOK)
1549 if (!mh_strcasecmp (*ap, "name")) {
1553 if (!mh_strcasecmp (*ap, "permission")) {
1554 e->eb_permission = *ep;
1557 if (!mh_strcasecmp (*ap, "site")) {
1561 if (!mh_strcasecmp (*ap, "directory")) {
1565 if (!mh_strcasecmp (*ap, "mode")) {
1569 if (!mh_strcasecmp (*ap, "size")) {
1570 sscanf (*ep, "%lu", &e->eb_size);
1573 if (!mh_strcasecmp (*ap, "server")) {
1577 if (!mh_strcasecmp (*ap, "subject")) {
1578 e->eb_subject = *ep;
1581 if (composing && !mh_strcasecmp (*ap, "body")) {
1582 e->eb_body = getcpy (*ep);
1587 if (!e->eb_access) {
1589 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1590 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1603 InitApplication (CT ct)
1606 CI ci = &ct->c_ctinfo;
1609 for (kv = SubApplication; kv->kv_key; kv++)
1610 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1612 ct->c_subtype = kv->kv_value;
1619 * TRANSFER ENCODINGS
1623 init_encoding (CT ct, OpenCEFunc openfnx)
1627 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1628 adios (NULL, "out of memory");
1631 ct->c_ceopenfnx = openfnx;
1632 ct->c_ceclosefnx = close_encoding;
1633 ct->c_cesizefnx = size_encoding;
1640 close_encoding (CT ct)
1644 if (!(ce = ct->c_cefile))
1654 static unsigned long
1655 size_encoding (CT ct)
1663 if (!(ce = ct->c_cefile))
1664 return (ct->c_end - ct->c_begin);
1666 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1667 return (long) st.st_size;
1670 if (stat (ce->ce_file, &st) != NOTOK)
1671 return (long) st.st_size;
1676 if (ct->c_encoding == CE_EXTERNAL)
1677 return (ct->c_end - ct->c_begin);
1680 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1681 return (ct->c_end - ct->c_begin);
1683 if (fstat (fd, &st) != NOTOK)
1684 size = (long) st.st_size;
1688 (*ct->c_ceclosefnx) (ct);
1697 static unsigned char b642nib[0x80] = {
1698 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1699 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1704 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1705 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1707 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1708 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1709 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1710 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1711 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1712 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1713 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1720 return init_encoding (ct, openBase64);
1725 openBase64 (CT ct, char **file)
1727 int bitno, cc, digested;
1728 int fd, len, skip, own_ct_fp = 0;
1730 unsigned char value, b;
1731 unsigned char *cp, *ep;
1732 char buffer[BUFSIZ];
1733 /* sbeck -- handle suffixes */
1740 fseek (ce->ce_fp, 0L, SEEK_SET);
1745 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1746 content_error (ce->ce_file, ct, "unable to fopen for reading");
1752 if (*file == NULL) {
1753 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1756 ce->ce_file = add (*file, NULL);
1760 /* sbeck@cise.ufl.edu -- handle suffixes */
1762 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1763 invo_name, ci->ci_type, ci->ci_subtype);
1764 cp = context_find (buffer);
1765 if (cp == NULL || *cp == '\0') {
1766 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1768 cp = context_find (buffer);
1770 if (cp != NULL && *cp != '\0') {
1771 if (ce->ce_unlink) {
1772 /* Temporary file already exists, so we rename to
1773 version with extension. */
1774 char *file_org = strdup(ce->ce_file);
1775 ce->ce_file = add (cp, ce->ce_file);
1776 if (rename(file_org, ce->ce_file)) {
1777 adios (ce->ce_file, "unable to rename %s to ", file_org);
1782 ce->ce_file = add (cp, ce->ce_file);
1786 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1787 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1791 if ((len = ct->c_end - ct->c_begin) < 0)
1792 adios (NULL, "internal error(1)");
1795 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1796 content_error (ct->c_file, ct, "unable to open for reading");
1802 if ((digested = ct->c_digested))
1803 MD5Init (&mdContext);
1809 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1811 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1813 content_error (ct->c_file, ct, "error reading from");
1817 content_error (NULL, ct, "premature eof");
1825 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1830 if (skip || (*cp & 0x80)
1831 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1833 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1835 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1838 content_error (NULL, ct,
1839 "invalid BASE64 encoding -- continuing");
1843 bits |= value << bitno;
1845 if ((bitno -= 6) < 0) {
1846 b = (bits >> 16) & 0xff;
1847 putc ((char) b, ce->ce_fp);
1849 MD5Update (&mdContext, &b, 1);
1851 b = (bits >> 8) & 0xff;
1852 putc ((char) b, ce->ce_fp);
1854 MD5Update (&mdContext, &b, 1);
1857 putc ((char) b, ce->ce_fp);
1859 MD5Update (&mdContext, &b, 1);
1863 if (ferror (ce->ce_fp)) {
1864 content_error (ce->ce_file, ct,
1865 "error writing to");
1868 bitno = 18, bits = 0L, skip = 0;
1874 goto self_delimiting;
1883 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1885 content_error (NULL, ct, "invalid BASE64 encoding");
1890 fseek (ct->c_fp, 0L, SEEK_SET);
1892 if (fflush (ce->ce_fp)) {
1893 content_error (ce->ce_file, ct, "error writing to");
1898 unsigned char digest[16];
1900 MD5Final (digest, &mdContext);
1901 if (memcmp((char *) digest, (char *) ct->c_digest,
1902 sizeof(digest) / sizeof(digest[0])))
1903 content_error (NULL, ct,
1904 "content integrity suspect (digest mismatch) -- continuing");
1907 fprintf (stderr, "content integrity confirmed\n");
1910 fseek (ce->ce_fp, 0L, SEEK_SET);
1913 *file = ce->ce_file;
1918 return fileno (ce->ce_fp);
1925 free_encoding (ct, 0);
1934 static char hex2nib[0x80] = {
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1941 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1942 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 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
1957 return init_encoding (ct, openQuoted);
1962 openQuoted (CT ct, char **file)
1964 int cc, digested, len, quoted, own_ct_fp = 0;
1965 unsigned char *cp, *ep;
1966 char buffer[BUFSIZ];
1969 /* sbeck -- handle suffixes */
1975 fseek (ce->ce_fp, 0L, SEEK_SET);
1980 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1981 content_error (ce->ce_file, ct, "unable to fopen for reading");
1987 if (*file == NULL) {
1988 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1991 ce->ce_file = add (*file, NULL);
1995 /* sbeck@cise.ufl.edu -- handle suffixes */
1997 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1998 invo_name, ci->ci_type, ci->ci_subtype);
1999 cp = context_find (buffer);
2000 if (cp == NULL || *cp == '\0') {
2001 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2003 cp = context_find (buffer);
2005 if (cp != NULL && *cp != '\0') {
2006 if (ce->ce_unlink) {
2007 /* Temporary file already exists, so we rename to
2008 version with extension. */
2009 char *file_org = strdup(ce->ce_file);
2010 ce->ce_file = add (cp, ce->ce_file);
2011 if (rename(file_org, ce->ce_file)) {
2012 adios (ce->ce_file, "unable to rename %s to ", file_org);
2017 ce->ce_file = add (cp, ce->ce_file);
2021 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2022 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2026 if ((len = ct->c_end - ct->c_begin) < 0)
2027 adios (NULL, "internal error(2)");
2030 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2031 content_error (ct->c_file, ct, "unable to open for reading");
2037 if ((digested = ct->c_digested))
2038 MD5Init (&mdContext);
2045 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2047 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2048 content_error (NULL, ct, "premature eof");
2052 if ((cc = strlen (buffer)) > len)
2056 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2061 for (; cp < ep; cp++) {
2063 /* in an escape sequence */
2065 /* at byte 1 of an escape sequence */
2066 mask = hex2nib[*cp & 0x7f];
2067 /* next is byte 2 */
2070 /* at byte 2 of an escape sequence */
2072 mask |= hex2nib[*cp & 0x7f];
2073 putc (mask, ce->ce_fp);
2075 MD5Update (&mdContext, &mask, 1);
2076 if (ferror (ce->ce_fp)) {
2077 content_error (ce->ce_file, ct, "error writing to");
2080 /* finished escape sequence; next may be literal or a new
2081 * escape sequence */
2084 /* on to next byte */
2088 /* not in an escape sequence */
2090 /* starting an escape sequence, or invalid '='? */
2091 if (cp + 1 < ep && cp[1] == '\n') {
2092 /* "=\n" soft line break, eat the \n */
2096 if (cp + 1 >= ep || cp + 2 >= ep) {
2097 /* We don't have 2 bytes left, so this is an invalid
2098 * escape sequence; just show the raw bytes (below). */
2099 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2100 /* Next 2 bytes are hex digits, making this a valid escape
2101 * sequence; let's decode it (above). */
2105 /* One or both of the next 2 is out of range, making this
2106 * an invalid escape sequence; just show the raw bytes
2111 /* Just show the raw byte. */
2112 putc (*cp, ce->ce_fp);
2115 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2117 MD5Update (&mdContext, (unsigned char *) cp, 1);
2120 if (ferror (ce->ce_fp)) {
2121 content_error (ce->ce_file, ct, "error writing to");
2127 content_error (NULL, ct,
2128 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2132 fseek (ct->c_fp, 0L, SEEK_SET);
2134 if (fflush (ce->ce_fp)) {
2135 content_error (ce->ce_file, ct, "error writing to");
2140 unsigned char digest[16];
2142 MD5Final (digest, &mdContext);
2143 if (memcmp((char *) digest, (char *) ct->c_digest,
2144 sizeof(digest) / sizeof(digest[0])))
2145 content_error (NULL, ct,
2146 "content integrity suspect (digest mismatch) -- continuing");
2149 fprintf (stderr, "content integrity confirmed\n");
2152 fseek (ce->ce_fp, 0L, SEEK_SET);
2155 *file = ce->ce_file;
2160 return fileno (ce->ce_fp);
2163 free_encoding (ct, 0);
2179 if (init_encoding (ct, open7Bit) == NOTOK)
2182 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2188 open7Bit (CT ct, char **file)
2190 int cc, fd, len, own_ct_fp = 0;
2191 char buffer[BUFSIZ];
2192 /* sbeck -- handle suffixes */
2199 fseek (ce->ce_fp, 0L, SEEK_SET);
2204 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2205 content_error (ce->ce_file, ct, "unable to fopen for reading");
2211 if (*file == NULL) {
2212 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2215 ce->ce_file = add (*file, NULL);
2219 /* sbeck@cise.ufl.edu -- handle suffixes */
2221 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2222 invo_name, ci->ci_type, ci->ci_subtype);
2223 cp = context_find (buffer);
2224 if (cp == NULL || *cp == '\0') {
2225 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2227 cp = context_find (buffer);
2229 if (cp != NULL && *cp != '\0') {
2230 if (ce->ce_unlink) {
2231 /* Temporary file already exists, so we rename to
2232 version with extension. */
2233 char *file_org = strdup(ce->ce_file);
2234 ce->ce_file = add (cp, ce->ce_file);
2235 if (rename(file_org, ce->ce_file)) {
2236 adios (ce->ce_file, "unable to rename %s to ", file_org);
2241 ce->ce_file = add (cp, ce->ce_file);
2245 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2246 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2250 if (ct->c_type == CT_MULTIPART) {
2252 CI ci = &ct->c_ctinfo;
2255 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2256 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2257 + 1 + strlen (ci->ci_subtype);
2258 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2259 putc (';', ce->ce_fp);
2262 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2264 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2265 fputs ("\n\t", ce->ce_fp);
2268 putc (' ', ce->ce_fp);
2271 fprintf (ce->ce_fp, "%s", buffer);
2275 if (ci->ci_comment) {
2276 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2277 fputs ("\n\t", ce->ce_fp);
2281 putc (' ', ce->ce_fp);
2284 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2287 fprintf (ce->ce_fp, "\n");
2289 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2291 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2293 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2294 fprintf (ce->ce_fp, "\n");
2297 if ((len = ct->c_end - ct->c_begin) < 0)
2298 adios (NULL, "internal error(3)");
2301 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2302 content_error (ct->c_file, ct, "unable to open for reading");
2308 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2310 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2312 content_error (ct->c_file, ct, "error reading from");
2316 content_error (NULL, ct, "premature eof");
2324 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2325 if (ferror (ce->ce_fp)) {
2326 content_error (ce->ce_file, ct, "error writing to");
2331 fseek (ct->c_fp, 0L, SEEK_SET);
2333 if (fflush (ce->ce_fp)) {
2334 content_error (ce->ce_file, ct, "error writing to");
2338 fseek (ce->ce_fp, 0L, SEEK_SET);
2341 *file = ce->ce_file;
2346 return fileno (ce->ce_fp);
2349 free_encoding (ct, 0);
2363 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2365 char cachefile[BUFSIZ];
2368 fseek (ce->ce_fp, 0L, SEEK_SET);
2373 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2374 content_error (ce->ce_file, ct, "unable to fopen for reading");
2380 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2381 cachefile, sizeof(cachefile)) != NOTOK) {
2382 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2383 ce->ce_file = getcpy (cachefile);
2387 admonish (cachefile, "unable to fopen for reading");
2394 *file = ce->ce_file;
2395 *fd = fileno (ce->ce_fp);
2406 return init_encoding (ct, openFile);
2411 openFile (CT ct, char **file)
2414 char cachefile[BUFSIZ];
2415 struct exbody *e = ct->c_ctexbody;
2416 CE ce = ct->c_cefile;
2418 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2430 content_error (NULL, ct, "missing name parameter");
2434 ce->ce_file = getcpy (e->eb_name);
2437 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2438 content_error (ce->ce_file, ct, "unable to fopen for reading");
2442 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2443 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2444 cachefile, sizeof(cachefile)) != NOTOK) {
2448 mask = umask (cachetype ? ~m_gmprot () : 0222);
2449 if ((fp = fopen (cachefile, "w"))) {
2451 char buffer[BUFSIZ];
2452 FILE *gp = ce->ce_fp;
2454 fseek (gp, 0L, SEEK_SET);
2456 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2458 fwrite (buffer, sizeof(*buffer), cc, fp);
2462 admonish (ce->ce_file, "error reading");
2467 admonish (cachefile, "error writing");
2475 fseek (ce->ce_fp, 0L, SEEK_SET);
2476 *file = ce->ce_file;
2477 return fileno (ce->ce_fp);
2487 return init_encoding (ct, openFTP);
2492 openFTP (CT ct, char **file)
2494 int cachetype, caching, fd;
2496 char *bp, *ftp, *user, *pass;
2497 char buffer[BUFSIZ], cachefile[BUFSIZ];
2500 static char *username = NULL;
2501 static char *password = NULL;
2506 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2512 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2523 if (!e->eb_name || !e->eb_site) {
2524 content_error (NULL, ct, "missing %s parameter",
2525 e->eb_name ? "site": "name");
2532 pidcheck (pidwait (xpid, NOTOK));
2536 /* Get the buffer ready to go */
2538 buflen = sizeof(buffer);
2541 * Construct the query message for user
2543 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2549 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2555 snprintf (bp, buflen, "\n using %sFTP from site %s",
2556 e->eb_flags ? "anonymous " : "", e->eb_site);
2561 if (e->eb_size > 0) {
2562 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2567 snprintf (bp, buflen, "? ");
2570 * Now, check the answer
2572 if (!getanswer (buffer))
2577 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2581 ruserpass (e->eb_site, &username, &password);
2586 ce->ce_unlink = (*file == NULL);
2588 cachefile[0] = '\0';
2589 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2590 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2591 cachefile, sizeof(cachefile)) != NOTOK) {
2592 if (*file == NULL) {
2599 ce->ce_file = add (*file, NULL);
2601 ce->ce_file = add (cachefile, NULL);
2603 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2605 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2606 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2611 int child_id, i, vecp;
2615 vec[vecp++] = r1bindex (ftp, '/');
2616 vec[vecp++] = e->eb_site;
2619 vec[vecp++] = e->eb_dir;
2620 vec[vecp++] = e->eb_name;
2621 vec[vecp++] = ce->ce_file,
2622 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2623 ? "ascii" : "binary";
2628 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2632 adios ("fork", "unable to");
2636 close (fileno (ce->ce_fp));
2638 fprintf (stderr, "unable to exec ");
2644 if (pidXwait (child_id, NULL)) {
2645 username = password = NULL;
2655 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2660 mask = umask (cachetype ? ~m_gmprot () : 0222);
2661 if ((fp = fopen (cachefile, "w"))) {
2663 FILE *gp = ce->ce_fp;
2665 fseek (gp, 0L, SEEK_SET);
2667 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2669 fwrite (buffer, sizeof(*buffer), cc, fp);
2673 admonish (ce->ce_file, "error reading");
2678 admonish (cachefile, "error writing");
2687 fseek (ce->ce_fp, 0L, SEEK_SET);
2688 *file = ce->ce_file;
2689 return fileno (ce->ce_fp);
2700 return init_encoding (ct, openMail);
2705 openMail (CT ct, char **file)
2707 int child_id, fd, i, vecp;
2709 char *bp, buffer[BUFSIZ], *vec[7];
2710 struct exbody *e = ct->c_ctexbody;
2711 CE ce = ct->c_cefile;
2713 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2724 if (!e->eb_server) {
2725 content_error (NULL, ct, "missing server parameter");
2732 pidcheck (pidwait (xpid, NOTOK));
2736 /* Get buffer ready to go */
2738 buflen = sizeof(buffer);
2740 /* Now, construct query message */
2741 snprintf (bp, buflen, "Retrieve content");
2747 snprintf (bp, buflen, " %s", e->eb_partno);
2753 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2755 e->eb_subject ? e->eb_subject : e->eb_body);
2757 /* Now, check answer */
2758 if (!getanswer (buffer))
2762 vec[vecp++] = r1bindex (mailproc, '/');
2763 vec[vecp++] = e->eb_server;
2764 vec[vecp++] = "-subject";
2765 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2766 vec[vecp++] = "-body";
2767 vec[vecp++] = e->eb_body;
2770 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2774 advise ("fork", "unable to");
2778 execvp (mailproc, vec);
2779 fprintf (stderr, "unable to exec ");
2785 if (pidXwait (child_id, NULL) == OK)
2786 advise (NULL, "request sent");
2790 if (*file == NULL) {
2791 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2794 ce->ce_file = add (*file, NULL);
2798 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2799 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2803 /* showproc is for mhshow and mhstore, though mhlist -debug
2804 * prints it, too. */
2806 free (ct->c_showproc);
2807 ct->c_showproc = add ("true", NULL);
2809 fseek (ce->ce_fp, 0L, SEEK_SET);
2810 *file = ce->ce_file;
2811 return fileno (ce->ce_fp);
2816 readDigest (CT ct, char *cp)
2821 unsigned char *dp, value, *ep;
2827 for (ep = (dp = ct->c_digest)
2828 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2833 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2835 fprintf (stderr, "invalid BASE64 encoding\n");
2839 bits |= value << bitno;
2841 if ((bitno -= 6) < 0) {
2842 if (dp + (3 - skip) > ep)
2843 goto invalid_digest;
2844 *dp++ = (bits >> 16) & 0xff;
2846 *dp++ = (bits >> 8) & 0xff;
2848 *dp++ = bits & 0xff;
2858 goto self_delimiting;
2863 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2873 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2881 fprintf (stderr, "MD5 digest=");
2882 for (dp = ct->c_digest; dp < ep; dp++)
2883 fprintf (stderr, "%02x", *dp & 0xff);
2884 fprintf (stderr, "\n");