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 ftp_get (char *, char *, char *, char *, char *, char *, int, int);
92 int find_cache (CT, int, int *, char *, char *, int);
95 int part_ok (CT, int);
96 int type_ok (CT, int);
97 int make_intermediates (char *);
98 void content_error (char *, CT, char *, ...);
101 void free_content (CT);
102 void free_encoding (CT, int);
107 static CT get_content (FILE *, char *, int);
108 static int get_comment (CT, unsigned char **, int);
110 static int InitGeneric (CT);
111 static int InitText (CT);
112 static int InitMultiPart (CT);
113 static void reverse_parts (CT);
114 static int InitMessage (CT);
115 static int InitApplication (CT);
116 static int init_encoding (CT, OpenCEFunc);
117 static unsigned long size_encoding (CT);
118 static int InitBase64 (CT);
119 static int openBase64 (CT, char **);
120 static int InitQuoted (CT);
121 static int openQuoted (CT, char **);
122 static int Init7Bit (CT);
123 static int openExternal (CT, CT, CE, char **, int *);
124 static int InitFile (CT);
125 static int openFile (CT, char **);
126 static int InitFTP (CT);
127 static int openFTP (CT, char **);
128 static int InitMail (CT);
129 static int openMail (CT, char **);
130 static int readDigest (CT, char *);
132 struct str2init str2cts[] = {
133 { "application", CT_APPLICATION, InitApplication },
134 { "audio", CT_AUDIO, InitGeneric },
135 { "image", CT_IMAGE, InitGeneric },
136 { "message", CT_MESSAGE, InitMessage },
137 { "multipart", CT_MULTIPART, InitMultiPart },
138 { "text", CT_TEXT, InitText },
139 { "video", CT_VIDEO, InitGeneric },
140 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
141 { NULL, CT_UNKNOWN, NULL },
144 struct str2init str2ces[] = {
145 { "base64", CE_BASE64, InitBase64 },
146 { "quoted-printable", CE_QUOTED, InitQuoted },
147 { "8bit", CE_8BIT, Init7Bit },
148 { "7bit", CE_7BIT, Init7Bit },
149 { "binary", CE_BINARY, Init7Bit },
150 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
151 { NULL, CE_UNKNOWN, NULL },
155 * NOTE WELL: si_key MUST NOT have value of NOTOK
157 * si_key is 1 if access method is anonymous.
159 struct str2init str2methods[] = {
160 { "afs", 1, InitFile },
161 { "anon-ftp", 1, InitFTP },
162 { "ftp", 0, InitFTP },
163 { "local-file", 0, InitFile },
164 { "mail-server", 0, InitMail },
170 pidcheck (int status)
172 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
183 * Main entry point for parsing a MIME message or file.
184 * It returns the Content structure for the top level
185 * entity in the file.
189 parse_mime (char *file)
197 * Check if file is actually standard input
199 if ((is_stdin = !(strcmp (file, "-")))) {
200 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
202 advise("mhparse", "unable to create temporary file");
205 file = add (tfile, NULL);
208 while (fgets (buffer, sizeof(buffer), stdin))
212 if (ferror (stdin)) {
214 advise ("stdin", "error reading");
219 advise (file, "error writing");
222 fseek (fp, 0L, SEEK_SET);
223 } else if ((fp = fopen (file, "r")) == NULL) {
224 advise (file, "unable to read");
228 if (!(ct = get_content (fp, file, 1))) {
231 advise (NULL, "unable to decode %s", file);
236 ct->c_unlink = 1; /* temp file to remove */
240 if (ct->c_end == 0L) {
241 fseek (fp, 0L, SEEK_END);
242 ct->c_end = ftell (fp);
245 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
257 * Main routine for reading/parsing the headers
258 * of a message content.
260 * toplevel = 1 # we are at the top level of the message
261 * toplevel = 0 # we are inside message type or multipart type
262 * # other than multipart/digest
263 * toplevel = -1 # we are inside multipart/digest
264 * NB: on failure we will fclose(in)!
268 get_content (FILE *in, char *file, int toplevel)
271 char buf[BUFSIZ], name[NAMESZ];
276 /* allocate the content structure */
277 if (!(ct = (CT) calloc (1, sizeof(*ct))))
278 adios (NULL, "out of memory");
281 ct->c_file = add (file, NULL);
282 ct->c_begin = ftell (ct->c_fp) + 1;
285 * Parse the header fields for this
286 * content into a linked list.
288 for (compnum = 1, state = FLD;;) {
289 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
295 /* get copies of the buffers */
296 np = add (name, NULL);
297 vp = add (buf, NULL);
299 /* if necessary, get rest of field */
300 while (state == FLDPLUS) {
301 state = m_getfld (state, name, buf, sizeof(buf), in);
302 vp = add (buf, vp); /* add to previous value */
305 /* Now add the header data to the list */
306 add_header (ct, np, vp);
308 /* continue, if this isn't the last header field */
309 if (state != FLDEOF) {
310 ct->c_begin = ftell (in) + 1;
317 ct->c_begin = ftell (in) - strlen (buf);
321 ct->c_begin = ftell (in);
326 adios (NULL, "message format error in component #%d", compnum);
329 adios (NULL, "getfld() returned %d", state);
332 /* break out of the loop */
337 * Read the content headers. We will parse the
338 * MIME related header fields into their various
339 * structures and set internal flags related to
340 * content type/subtype, etc.
343 hp = ct->c_first_hf; /* start at first header field */
345 /* Get MIME-Version field */
346 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
349 unsigned char *cp, *dp;
352 advise (NULL, "message %s has multiple %s: fields",
353 ct->c_file, VRSN_FIELD);
356 ct->c_vrsn = add (hp->value, NULL);
358 /* Now, cleanup this field */
361 while (isspace (*cp))
363 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
365 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
370 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
372 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
375 for (dp = cp; istoken (*dp); dp++)
379 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
382 admonish (NULL, "message %s has unknown value for %s: field (%s)",
383 ct->c_file, VRSN_FIELD, cp);
386 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
387 /* Get Content-Type field */
388 struct str2init *s2i;
389 CI ci = &ct->c_ctinfo;
391 /* Check if we've already seen a Content-Type header */
393 advise (NULL, "message %s has multiple %s: fields",
394 ct->c_file, TYPE_FIELD);
398 /* Parse the Content-Type field */
399 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
403 * Set the Init function and the internal
404 * flag for this content type.
406 for (s2i = str2cts; s2i->si_key; s2i++)
407 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
409 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
411 ct->c_type = s2i->si_val;
412 ct->c_ctinitfnx = s2i->si_init;
414 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
415 /* Get Content-Transfer-Encoding field */
417 unsigned char *cp, *dp;
418 struct str2init *s2i;
421 * Check if we've already seen the
422 * Content-Transfer-Encoding field
425 advise (NULL, "message %s has multiple %s: fields",
426 ct->c_file, ENCODING_FIELD);
430 /* get copy of this field */
431 ct->c_celine = cp = add (hp->value, NULL);
433 while (isspace (*cp))
435 for (dp = cp; istoken (*dp); dp++)
441 * Find the internal flag and Init function
442 * for this transfer encoding.
444 for (s2i = str2ces; s2i->si_key; s2i++)
445 if (!mh_strcasecmp (cp, s2i->si_key))
447 if (!s2i->si_key && !uprf (cp, "X-"))
450 ct->c_encoding = s2i->si_val;
452 /* Call the Init function for this encoding */
453 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
456 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
457 /* Get Content-MD5 field */
458 unsigned char *cp, *dp;
464 if (ct->c_digested) {
465 advise (NULL, "message %s has multiple %s: fields",
466 ct->c_file, MD5_FIELD);
470 ep = cp = add (hp->value, NULL); /* get a copy */
472 while (isspace (*cp))
474 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
476 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
481 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
483 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
488 for (dp = cp; *dp && !isspace (*dp); dp++)
496 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
497 /* Get Content-ID field */
498 ct->c_id = add (hp->value, ct->c_id);
500 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
501 /* Get Content-Description field */
502 ct->c_descr = add (hp->value, ct->c_descr);
504 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
505 /* Get Content-Disposition field */
506 ct->c_dispo = add (hp->value, ct->c_dispo);
510 hp = hp->next; /* next header field */
514 * Check if we saw a Content-Type field.
515 * If not, then assign a default value for
516 * it, and the Init function.
520 * If we are inside a multipart/digest message,
521 * so default type is message/rfc822
524 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
526 ct->c_type = CT_MESSAGE;
527 ct->c_ctinitfnx = InitMessage;
530 * Else default type is text/plain
532 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
534 ct->c_type = CT_TEXT;
535 ct->c_ctinitfnx = InitText;
539 /* Use default Transfer-Encoding, if necessary */
541 ct->c_encoding = CE_7BIT;
554 * small routine to add header field to list
558 add_header (CT ct, char *name, char *value)
562 /* allocate header field structure */
563 hp = mh_xmalloc (sizeof(*hp));
565 /* link data into header structure */
570 /* link header structure into the list */
571 if (ct->c_first_hf == NULL) {
572 ct->c_first_hf = hp; /* this is the first */
575 ct->c_last_hf->next = hp; /* add it to the end */
583 /* Make sure that buf contains at least one appearance of name,
584 followed by =. If not, insert both name and value, just after
585 first semicolon, if any. Note that name should not contain a
586 trailing =. And quotes will be added around the value. Typical
587 usage: make sure that a Content-Disposition header contains
588 filename="foo". If it doesn't and value does, use value from
591 incl_name_value (unsigned char *buf, char *name, char *value) {
594 /* Assume that name is non-null. */
596 char *name_plus_equal = concat (name, "=", NULL);
598 if (! strstr (buf, name_plus_equal)) {
601 char *prefix, *suffix;
603 /* Trim trailing space, esp. newline. */
604 for (cp = &buf[strlen (buf) - 1];
605 cp >= buf && isspace (*cp);
610 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
612 /* Insert at first semicolon, if any. If none, append to
614 prefix = add (buf, NULL);
615 if ((cp = strchr (prefix, ';'))) {
616 suffix = concat (cp, NULL);
618 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
622 newbuf = concat (buf, insertion, "\n", NULL);
630 free (name_plus_equal);
636 /* Extract just name_suffix="foo", if any, from value. If there isn't
637 one, return the entire value. Note that, for example, a name_suffix
638 of name will match filename="foo", and return foo. */
640 extract_name_value (char *name_suffix, char *value) {
641 char *extracted_name_value = value;
642 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
643 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
646 free (name_suffix_plus_quote);
647 if (name_suffix_equals) {
648 char *name_suffix_begin;
651 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
652 name_suffix_begin = ++cp;
653 /* Find second \". */
654 for (; *cp != '"'; ++cp) /* empty */;
656 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
657 memcpy (extracted_name_value,
659 cp - name_suffix_begin);
660 extracted_name_value[cp - name_suffix_begin] = '\0';
663 return extracted_name_value;
667 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
668 * directives. Fills in the information of the CTinfo structure.
671 get_ctinfo (unsigned char *cp, CT ct, int magic)
680 i = strlen (invo_name) + 2;
682 /* store copy of Content-Type line */
683 cp = ct->c_ctline = add (cp, NULL);
685 while (isspace (*cp)) /* trim leading spaces */
688 /* change newlines to spaces */
689 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
692 /* trim trailing spaces */
693 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
699 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
701 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
704 for (dp = cp; istoken (*dp); dp++)
707 ci->ci_type = add (cp, NULL); /* store content type */
711 advise (NULL, "invalid %s: field in message %s (empty type)",
712 TYPE_FIELD, ct->c_file);
716 /* down case the content type string */
717 for (dp = ci->ci_type; *dp; dp++)
718 if (isalpha(*dp) && isupper (*dp))
721 while (isspace (*cp))
724 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
729 ci->ci_subtype = add ("", NULL);
734 while (isspace (*cp))
737 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
740 for (dp = cp; istoken (*dp); dp++)
743 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
746 if (!*ci->ci_subtype) {
748 "invalid %s: field in message %s (empty subtype for \"%s\")",
749 TYPE_FIELD, ct->c_file, ci->ci_type);
753 /* down case the content subtype string */
754 for (dp = ci->ci_subtype; *dp; dp++)
755 if (isalpha(*dp) && isupper (*dp))
759 while (isspace (*cp))
762 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
766 * Parse attribute/value pairs given with Content-Type
768 ep = (ap = ci->ci_attrs) + NPARMS;
775 "too many parameters in message %s's %s: field (%d max)",
776 ct->c_file, TYPE_FIELD, NPARMS);
781 while (isspace (*cp))
784 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
789 "extraneous trailing ';' in message %s's %s: parameter list",
790 ct->c_file, TYPE_FIELD);
794 /* down case the attribute name */
795 for (dp = cp; istoken (*dp); dp++)
796 if (isalpha(*dp) && isupper (*dp))
799 for (up = dp; isspace (*dp);)
801 if (dp == cp || *dp != '=') {
803 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
804 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
808 vp = (*ap = add (cp, NULL)) + (up - cp);
810 for (dp++; isspace (*dp);)
813 /* now add the attribute value */
814 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
817 for (cp = ++dp, dp = vp;;) {
822 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
823 ct->c_file, TYPE_FIELD, i, i, "", *ap);
828 if ((c = *cp++) == '\0')
843 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
849 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
850 ct->c_file, TYPE_FIELD, i, i, "", *ap);
855 while (isspace (*cp))
858 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
863 * Get any <Content-Id> given in buffer
865 if (magic && *cp == '<') {
870 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
871 advise (NULL, "invalid ID in message %s", ct->c_file);
877 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
883 while (isspace (*cp))
888 * Get any [Content-Description] given in buffer.
890 if (magic && *cp == '[') {
892 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
896 advise (NULL, "invalid description in message %s", ct->c_file);
904 ct->c_descr = concat (ct->c_descr, "\n", NULL);
910 while (isspace (*cp))
915 * Get any {Content-Disposition} given in buffer.
917 if (magic && *cp == '{') {
919 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
923 advise (NULL, "invalid disposition in message %s", ct->c_file);
931 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
937 while (isspace (*cp))
942 * Check if anything is left over
946 ci->ci_magic = add (cp, NULL);
948 /* If there is a Content-Disposition header and it doesn't
949 have a *filename=, extract it from the magic contents.
950 The r1bindex call skips any leading directory
954 incl_name_value (ct->c_dispo,
956 r1bindex (extract_name_value ("name",
963 "extraneous information in message %s's %s: field\n%*.*s(%s)",
964 ct->c_file, TYPE_FIELD, i, i, "", cp);
972 get_comment (CT ct, unsigned char **ap, int istype)
977 char c, buffer[BUFSIZ], *dp;
989 advise (NULL, "invalid comment in message %s's %s: field",
990 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
995 if ((c = *cp++) == '\0')
1018 if ((dp = ci->ci_comment)) {
1019 ci->ci_comment = concat (dp, " ", buffer, NULL);
1022 ci->ci_comment = add (buffer, NULL);
1026 while (isspace (*cp))
1037 * Handles content types audio, image, and video.
1038 * There's not much to do right here.
1046 return OK; /* not much to do here */
1057 char buffer[BUFSIZ];
1059 char **ap, **ep, *cp;
1062 CI ci = &ct->c_ctinfo;
1064 /* check for missing subtype */
1065 if (!*ci->ci_subtype)
1066 ci->ci_subtype = add ("plain", ci->ci_subtype);
1069 for (kv = SubText; kv->kv_key; kv++)
1070 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1072 ct->c_subtype = kv->kv_value;
1074 /* allocate text character set structure */
1075 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1076 adios (NULL, "out of memory");
1077 ct->c_ctparams = (void *) t;
1079 /* scan for charset parameter */
1080 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1081 if (!mh_strcasecmp (*ap, "charset"))
1084 /* check if content specified a character set */
1086 /* match character set or set to CHARSET_UNKNOWN */
1087 for (kv = Charset; kv->kv_key; kv++) {
1088 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1093 t->tx_charset = kv->kv_value;
1095 t->tx_charset = CHARSET_UNSPECIFIED;
1099 * If we can not handle character set natively,
1100 * then check profile for string to modify the
1101 * terminal or display method.
1103 * termproc is for mhshow, though mhlist -debug prints it, too.
1105 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1106 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1107 if ((cp = context_find (buffer)))
1108 ct->c_termproc = getcpy (cp);
1120 InitMultiPart (CT ct)
1124 unsigned char *cp, *dp;
1126 char *bp, buffer[BUFSIZ];
1127 struct multipart *m;
1129 struct part *part, **next;
1130 CI ci = &ct->c_ctinfo;
1135 * The encoding for multipart messages must be either
1136 * 7bit, 8bit, or binary (per RFC2045).
1138 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1139 && ct->c_encoding != CE_BINARY) {
1141 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1142 ci->ci_type, ci->ci_subtype, ct->c_file);
1147 for (kv = SubMultiPart; kv->kv_key; kv++)
1148 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1150 ct->c_subtype = kv->kv_value;
1153 * Check for "boundary" parameter, which is
1154 * required for multipart messages.
1157 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1158 if (!mh_strcasecmp (*ap, "boundary")) {
1164 /* complain if boundary parameter is missing */
1167 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1168 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1172 /* allocate primary structure for multipart info */
1173 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1174 adios (NULL, "out of memory");
1175 ct->c_ctparams = (void *) m;
1177 /* check if boundary parameter contains only whitespace characters */
1178 for (cp = bp; isspace (*cp); cp++)
1181 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1182 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1186 /* remove trailing whitespace from boundary parameter */
1187 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1192 /* record boundary separators */
1193 m->mp_start = concat (bp, "\n", NULL);
1194 m->mp_stop = concat (bp, "--\n", NULL);
1196 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1197 advise (ct->c_file, "unable to open for reading");
1201 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1203 next = &m->mp_parts;
1207 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1211 pos += strlen (buffer);
1212 if (buffer[0] != '-' || buffer[1] != '-')
1215 if (strcmp (buffer + 2, m->mp_start))
1218 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1219 adios (NULL, "out of memory");
1221 next = &part->mp_next;
1223 if (!(p = get_content (fp, ct->c_file,
1224 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1231 fseek (fp, pos, SEEK_SET);
1234 if (strcmp (buffer + 2, m->mp_start) == 0) {
1238 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1239 if (p->c_end < p->c_begin)
1240 p->c_begin = p->c_end;
1245 if (strcmp (buffer + 2, m->mp_stop) == 0)
1251 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1252 if (!inout && part) {
1254 p->c_end = ct->c_end;
1256 if (p->c_begin >= p->c_end) {
1257 for (next = &m->mp_parts; *next != part;
1258 next = &((*next)->mp_next))
1262 free ((char *) part);
1267 /* reverse the order of the parts for multipart/alternative */
1268 if (ct->c_subtype == MULTI_ALTERNATE)
1272 * label all subparts with part number, and
1273 * then initialize the content of the subpart.
1278 char partnam[BUFSIZ];
1281 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1282 pp = partnam + strlen (partnam);
1287 for (part = m->mp_parts, partnum = 1; part;
1288 part = part->mp_next, partnum++) {
1291 sprintf (pp, "%d", partnum);
1292 p->c_partno = add (partnam, NULL);
1294 /* initialize the content of the subparts */
1295 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1310 * reverse the order of the parts of a multipart
1314 reverse_parts (CT ct)
1317 struct multipart *m;
1318 struct part **base, **bmp, **next, *part;
1320 m = (struct multipart *) ct->c_ctparams;
1322 /* if only one part, just return */
1323 if (!m->mp_parts || !m->mp_parts->mp_next)
1326 /* count number of parts */
1328 for (part = m->mp_parts; part; part = part->mp_next)
1331 /* allocate array of pointers to the parts */
1332 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1333 adios (NULL, "out of memory");
1336 /* point at all the parts */
1337 for (part = m->mp_parts; part; part = part->mp_next)
1341 /* reverse the order of the parts */
1342 next = &m->mp_parts;
1343 for (bmp--; bmp >= base; bmp--) {
1346 next = &part->mp_next;
1350 /* free array of pointers */
1351 free ((char *) base);
1363 CI ci = &ct->c_ctinfo;
1365 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1367 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1368 ci->ci_type, ci->ci_subtype, ct->c_file);
1372 /* check for missing subtype */
1373 if (!*ci->ci_subtype)
1374 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1377 for (kv = SubMessage; kv->kv_key; kv++)
1378 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1380 ct->c_subtype = kv->kv_value;
1382 switch (ct->c_subtype) {
1383 case MESSAGE_RFC822:
1386 case MESSAGE_PARTIAL:
1391 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1392 adios (NULL, "out of memory");
1393 ct->c_ctparams = (void *) p;
1395 /* scan for parameters "id", "number", and "total" */
1396 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1397 if (!mh_strcasecmp (*ap, "id")) {
1398 p->pm_partid = add (*ep, NULL);
1401 if (!mh_strcasecmp (*ap, "number")) {
1402 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1403 || p->pm_partno < 1) {
1406 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1407 *ap, ci->ci_type, ci->ci_subtype,
1408 ct->c_file, TYPE_FIELD);
1413 if (!mh_strcasecmp (*ap, "total")) {
1414 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1423 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1425 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1426 ci->ci_type, ci->ci_subtype,
1427 ct->c_file, TYPE_FIELD);
1433 case MESSAGE_EXTERNAL:
1440 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1441 adios (NULL, "out of memory");
1442 ct->c_ctparams = (void *) e;
1445 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1446 advise (ct->c_file, "unable to open for reading");
1450 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1452 if (!(p = get_content (fp, ct->c_file, 0))) {
1460 if ((exresult = params_external (ct, 0)) != NOTOK
1461 && p->c_ceopenfnx == openMail) {
1465 if ((size = ct->c_end - p->c_begin) <= 0) {
1467 content_error (NULL, ct,
1468 "empty body for access-type=mail-server");
1472 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1473 fseek (p->c_fp, p->c_begin, SEEK_SET);
1475 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1477 adios ("failed", "fread");
1480 adios (NULL, "unexpected EOF from fread");
1483 bp += cc, size -= cc;
1490 p->c_end = p->c_begin;
1495 if (exresult == NOTOK)
1497 if (e->eb_flags == NOTOK)
1500 switch (p->c_type) {
1505 if (p->c_subtype != MESSAGE_RFC822)
1509 e->eb_partno = ct->c_partno;
1511 (*p->c_ctinitfnx) (p);
1526 params_external (CT ct, int composing)
1529 struct exbody *e = (struct exbody *) ct->c_ctparams;
1530 CI ci = &ct->c_ctinfo;
1532 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1533 if (!mh_strcasecmp (*ap, "access-type")) {
1534 struct str2init *s2i;
1535 CT p = e->eb_content;
1537 for (s2i = str2methods; s2i->si_key; s2i++)
1538 if (!mh_strcasecmp (*ep, s2i->si_key))
1542 e->eb_flags = NOTOK;
1543 p->c_encoding = CE_EXTERNAL;
1546 e->eb_access = s2i->si_key;
1547 e->eb_flags = s2i->si_val;
1548 p->c_encoding = CE_EXTERNAL;
1550 /* Call the Init function for this external type */
1551 if ((*s2i->si_init)(p) == NOTOK)
1555 if (!mh_strcasecmp (*ap, "name")) {
1559 if (!mh_strcasecmp (*ap, "permission")) {
1560 e->eb_permission = *ep;
1563 if (!mh_strcasecmp (*ap, "site")) {
1567 if (!mh_strcasecmp (*ap, "directory")) {
1571 if (!mh_strcasecmp (*ap, "mode")) {
1575 if (!mh_strcasecmp (*ap, "size")) {
1576 sscanf (*ep, "%lu", &e->eb_size);
1579 if (!mh_strcasecmp (*ap, "server")) {
1583 if (!mh_strcasecmp (*ap, "subject")) {
1584 e->eb_subject = *ep;
1587 if (composing && !mh_strcasecmp (*ap, "body")) {
1588 e->eb_body = getcpy (*ep);
1593 if (!e->eb_access) {
1595 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1596 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1609 InitApplication (CT ct)
1612 CI ci = &ct->c_ctinfo;
1615 for (kv = SubApplication; kv->kv_key; kv++)
1616 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1618 ct->c_subtype = kv->kv_value;
1625 * TRANSFER ENCODINGS
1629 init_encoding (CT ct, OpenCEFunc openfnx)
1633 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1634 adios (NULL, "out of memory");
1637 ct->c_ceopenfnx = openfnx;
1638 ct->c_ceclosefnx = close_encoding;
1639 ct->c_cesizefnx = size_encoding;
1646 close_encoding (CT ct)
1650 if (!(ce = ct->c_cefile))
1660 static unsigned long
1661 size_encoding (CT ct)
1669 if (!(ce = ct->c_cefile))
1670 return (ct->c_end - ct->c_begin);
1672 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1673 return (long) st.st_size;
1676 if (stat (ce->ce_file, &st) != NOTOK)
1677 return (long) st.st_size;
1682 if (ct->c_encoding == CE_EXTERNAL)
1683 return (ct->c_end - ct->c_begin);
1686 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1687 return (ct->c_end - ct->c_begin);
1689 if (fstat (fd, &st) != NOTOK)
1690 size = (long) st.st_size;
1694 (*ct->c_ceclosefnx) (ct);
1703 static unsigned char b642nib[0x80] = {
1704 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1706 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1707 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1709 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1710 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1711 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1713 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1714 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1715 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1716 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1717 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1718 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1719 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1726 return init_encoding (ct, openBase64);
1731 openBase64 (CT ct, char **file)
1733 int bitno, cc, digested;
1736 unsigned char value, *b, *b1, *b2, *b3;
1737 unsigned char *cp, *ep;
1738 char buffer[BUFSIZ];
1739 /* sbeck -- handle suffixes */
1744 b = (unsigned char *) &bits;
1745 b1 = &b[endian > 0 ? 1 : 2];
1746 b2 = &b[endian > 0 ? 2 : 1];
1747 b3 = &b[endian > 0 ? 3 : 0];
1751 fseek (ce->ce_fp, 0L, SEEK_SET);
1756 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1757 content_error (ce->ce_file, ct, "unable to fopen for reading");
1763 if (*file == NULL) {
1764 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1767 ce->ce_file = add (*file, NULL);
1771 /* sbeck@cise.ufl.edu -- handle suffixes */
1773 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1774 invo_name, ci->ci_type, ci->ci_subtype);
1775 cp = context_find (buffer);
1776 if (cp == NULL || *cp == '\0') {
1777 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1779 cp = context_find (buffer);
1781 if (cp != NULL && *cp != '\0') {
1782 if (ce->ce_unlink) {
1783 // Temporary file already exists, so we rename to
1784 // version with extension.
1785 char *file_org = strdup(ce->ce_file);
1786 ce->ce_file = add (cp, ce->ce_file);
1787 if (rename(file_org, ce->ce_file)) {
1788 adios (ce->ce_file, "unable to rename %s to ", file_org);
1793 ce->ce_file = add (cp, ce->ce_file);
1797 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1798 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1802 if ((len = ct->c_end - ct->c_begin) < 0)
1803 adios (NULL, "internal error(1)");
1805 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1806 content_error (ct->c_file, ct, "unable to open for reading");
1810 if ((digested = ct->c_digested))
1811 MD5Init (&mdContext);
1817 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1819 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1821 content_error (ct->c_file, ct, "error reading from");
1825 content_error (NULL, ct, "premature eof");
1833 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1838 if (skip || (*cp & 0x80)
1839 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1841 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1843 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1846 content_error (NULL, ct,
1847 "invalid BASE64 encoding -- continuing");
1851 bits |= value << bitno;
1853 if ((bitno -= 6) < 0) {
1854 putc ((char) *b1, ce->ce_fp);
1856 MD5Update (&mdContext, b1, 1);
1858 putc ((char) *b2, ce->ce_fp);
1860 MD5Update (&mdContext, b2, 1);
1862 putc ((char) *b3, ce->ce_fp);
1864 MD5Update (&mdContext, b3, 1);
1868 if (ferror (ce->ce_fp)) {
1869 content_error (ce->ce_file, ct,
1870 "error writing to");
1873 bitno = 18, bits = 0L, skip = 0;
1879 goto self_delimiting;
1888 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1890 content_error (NULL, ct, "invalid BASE64 encoding");
1895 fseek (ct->c_fp, 0L, SEEK_SET);
1897 if (fflush (ce->ce_fp)) {
1898 content_error (ce->ce_file, ct, "error writing to");
1903 unsigned char digest[16];
1905 MD5Final (digest, &mdContext);
1906 if (memcmp((char *) digest, (char *) ct->c_digest,
1907 sizeof(digest) / sizeof(digest[0])))
1908 content_error (NULL, ct,
1909 "content integrity suspect (digest mismatch) -- continuing");
1912 fprintf (stderr, "content integrity confirmed\n");
1915 fseek (ce->ce_fp, 0L, SEEK_SET);
1918 *file = ce->ce_file;
1919 return fileno (ce->ce_fp);
1922 free_encoding (ct, 0);
1931 static char hex2nib[0x80] = {
1932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1938 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1939 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1940 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1944 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1954 return init_encoding (ct, openQuoted);
1959 openQuoted (CT ct, char **file)
1961 int cc, digested, len, quoted;
1962 unsigned char *cp, *ep;
1963 char buffer[BUFSIZ];
1966 /* sbeck -- handle suffixes */
1972 fseek (ce->ce_fp, 0L, SEEK_SET);
1977 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1978 content_error (ce->ce_file, ct, "unable to fopen for reading");
1984 if (*file == NULL) {
1985 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1988 ce->ce_file = add (*file, NULL);
1992 /* sbeck@cise.ufl.edu -- handle suffixes */
1994 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1995 invo_name, ci->ci_type, ci->ci_subtype);
1996 cp = context_find (buffer);
1997 if (cp == NULL || *cp == '\0') {
1998 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2000 cp = context_find (buffer);
2002 if (cp != NULL && *cp != '\0') {
2003 if (ce->ce_unlink) {
2004 // Temporary file already exists, so we rename to
2005 // version with extension.
2006 char *file_org = strdup(ce->ce_file);
2007 ce->ce_file = add (cp, ce->ce_file);
2008 if (rename(file_org, ce->ce_file)) {
2009 adios (ce->ce_file, "unable to rename %s to ", file_org);
2014 ce->ce_file = add (cp, ce->ce_file);
2018 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2019 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2023 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2024 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2028 if ((len = ct->c_end - ct->c_begin) < 0)
2029 adios (NULL, "internal error(2)");
2031 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2032 content_error (ct->c_file, ct, "unable to open for reading");
2036 if ((digested = ct->c_digested))
2037 MD5Init (&mdContext);
2044 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2046 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2047 content_error (NULL, ct, "premature eof");
2051 if ((cc = strlen (buffer)) > len)
2055 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2060 for (; cp < ep; cp++) {
2062 /* in an escape sequence */
2064 /* at byte 1 of an escape sequence */
2065 mask = hex2nib[*cp & 0x7f];
2066 /* next is byte 2 */
2069 /* at byte 2 of an escape sequence */
2071 mask |= hex2nib[*cp & 0x7f];
2072 putc (mask, ce->ce_fp);
2074 MD5Update (&mdContext, &mask, 1);
2075 if (ferror (ce->ce_fp)) {
2076 content_error (ce->ce_file, ct, "error writing to");
2079 /* finished escape sequence; next may be literal or a new
2080 * escape sequence */
2083 /* on to next byte */
2087 /* not in an escape sequence */
2089 /* starting an escape sequence, or invalid '='? */
2090 if (cp + 1 < ep && cp[1] == '\n') {
2091 /* "=\n" soft line break, eat the \n */
2095 if (cp + 1 >= ep || cp + 2 >= ep) {
2096 /* We don't have 2 bytes left, so this is an invalid
2097 * escape sequence; just show the raw bytes (below). */
2098 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2099 /* Next 2 bytes are hex digits, making this a valid escape
2100 * sequence; let's decode it (above). */
2104 /* One or both of the next 2 is out of range, making this
2105 * an invalid escape sequence; just show the raw bytes
2110 /* Just show the raw byte. */
2111 putc (*cp, ce->ce_fp);
2114 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2116 MD5Update (&mdContext, (unsigned char *) cp, 1);
2119 if (ferror (ce->ce_fp)) {
2120 content_error (ce->ce_file, ct, "error writing to");
2126 content_error (NULL, ct,
2127 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2131 fseek (ct->c_fp, 0L, SEEK_SET);
2133 if (fflush (ce->ce_fp)) {
2134 content_error (ce->ce_file, ct, "error writing to");
2139 unsigned char digest[16];
2141 MD5Final (digest, &mdContext);
2142 if (memcmp((char *) digest, (char *) ct->c_digest,
2143 sizeof(digest) / sizeof(digest[0])))
2144 content_error (NULL, ct,
2145 "content integrity suspect (digest mismatch) -- continuing");
2148 fprintf (stderr, "content integrity confirmed\n");
2151 fseek (ce->ce_fp, 0L, SEEK_SET);
2154 *file = ce->ce_file;
2155 return fileno (ce->ce_fp);
2158 free_encoding (ct, 0);
2170 if (init_encoding (ct, open7Bit) == NOTOK)
2173 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2179 open7Bit (CT ct, char **file)
2182 char buffer[BUFSIZ];
2183 /* sbeck -- handle suffixes */
2190 fseek (ce->ce_fp, 0L, SEEK_SET);
2195 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2196 content_error (ce->ce_file, ct, "unable to fopen for reading");
2202 if (*file == NULL) {
2203 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2206 ce->ce_file = add (*file, NULL);
2210 /* sbeck@cise.ufl.edu -- handle suffixes */
2212 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2213 invo_name, ci->ci_type, ci->ci_subtype);
2214 cp = context_find (buffer);
2215 if (cp == NULL || *cp == '\0') {
2216 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2218 cp = context_find (buffer);
2220 if (cp != NULL && *cp != '\0') {
2221 if (ce->ce_unlink) {
2222 // Temporary file already exists, so we rename to
2223 // version with extension.
2224 char *file_org = strdup(ce->ce_file);
2225 ce->ce_file = add (cp, ce->ce_file);
2226 if (rename(file_org, ce->ce_file)) {
2227 adios (ce->ce_file, "unable to rename %s to ", file_org);
2232 ce->ce_file = add (cp, ce->ce_file);
2236 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2237 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2241 if (ct->c_type == CT_MULTIPART) {
2243 CI ci = &ct->c_ctinfo;
2246 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2247 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2248 + 1 + strlen (ci->ci_subtype);
2249 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2250 putc (';', ce->ce_fp);
2253 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2255 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2256 fputs ("\n\t", ce->ce_fp);
2259 putc (' ', ce->ce_fp);
2262 fprintf (ce->ce_fp, "%s", buffer);
2266 if (ci->ci_comment) {
2267 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2268 fputs ("\n\t", ce->ce_fp);
2272 putc (' ', ce->ce_fp);
2275 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2278 fprintf (ce->ce_fp, "\n");
2280 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2282 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2284 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2285 fprintf (ce->ce_fp, "\n");
2288 if ((len = ct->c_end - ct->c_begin) < 0)
2289 adios (NULL, "internal error(3)");
2291 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2292 content_error (ct->c_file, ct, "unable to open for reading");
2296 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2298 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2300 content_error (ct->c_file, ct, "error reading from");
2304 content_error (NULL, ct, "premature eof");
2312 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2313 if (ferror (ce->ce_fp)) {
2314 content_error (ce->ce_file, ct, "error writing to");
2319 fseek (ct->c_fp, 0L, SEEK_SET);
2321 if (fflush (ce->ce_fp)) {
2322 content_error (ce->ce_file, ct, "error writing to");
2326 fseek (ce->ce_fp, 0L, SEEK_SET);
2329 *file = ce->ce_file;
2330 return fileno (ce->ce_fp);
2333 free_encoding (ct, 0);
2343 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2345 char cachefile[BUFSIZ];
2348 fseek (ce->ce_fp, 0L, SEEK_SET);
2353 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2354 content_error (ce->ce_file, ct, "unable to fopen for reading");
2360 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2361 cachefile, sizeof(cachefile)) != NOTOK) {
2362 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2363 ce->ce_file = getcpy (cachefile);
2367 admonish (cachefile, "unable to fopen for reading");
2374 *file = ce->ce_file;
2375 *fd = fileno (ce->ce_fp);
2386 return init_encoding (ct, openFile);
2391 openFile (CT ct, char **file)
2394 char cachefile[BUFSIZ];
2395 struct exbody *e = ct->c_ctexbody;
2396 CE ce = ct->c_cefile;
2398 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2410 content_error (NULL, ct, "missing name parameter");
2414 ce->ce_file = getcpy (e->eb_name);
2417 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2418 content_error (ce->ce_file, ct, "unable to fopen for reading");
2422 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2423 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2424 cachefile, sizeof(cachefile)) != NOTOK) {
2428 mask = umask (cachetype ? ~m_gmprot () : 0222);
2429 if ((fp = fopen (cachefile, "w"))) {
2431 char buffer[BUFSIZ];
2432 FILE *gp = ce->ce_fp;
2434 fseek (gp, 0L, SEEK_SET);
2436 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2438 fwrite (buffer, sizeof(*buffer), cc, fp);
2442 admonish (ce->ce_file, "error reading");
2447 admonish (cachefile, "error writing");
2455 fseek (ce->ce_fp, 0L, SEEK_SET);
2456 *file = ce->ce_file;
2457 return fileno (ce->ce_fp);
2467 return init_encoding (ct, openFTP);
2472 openFTP (CT ct, char **file)
2474 int cachetype, caching, fd;
2476 char *bp, *ftp, *user, *pass;
2477 char buffer[BUFSIZ], cachefile[BUFSIZ];
2480 static char *username = NULL;
2481 static char *password = NULL;
2486 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2492 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2503 if (!e->eb_name || !e->eb_site) {
2504 content_error (NULL, ct, "missing %s parameter",
2505 e->eb_name ? "site": "name");
2512 pidcheck (pidwait (xpid, NOTOK));
2516 /* Get the buffer ready to go */
2518 buflen = sizeof(buffer);
2521 * Construct the query message for user
2523 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2529 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2535 snprintf (bp, buflen, "\n using %sFTP from site %s",
2536 e->eb_flags ? "anonymous " : "", e->eb_site);
2541 if (e->eb_size > 0) {
2542 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2547 snprintf (bp, buflen, "? ");
2550 * Now, check the answer
2552 if (!getanswer (buffer))
2557 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2560 ruserpass (e->eb_site, &username, &password);
2565 ce->ce_unlink = (*file == NULL);
2567 cachefile[0] = '\0';
2568 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2569 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2570 cachefile, sizeof(cachefile)) != NOTOK) {
2571 if (*file == NULL) {
2578 ce->ce_file = add (*file, NULL);
2580 ce->ce_file = add (cachefile, NULL);
2582 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2584 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2585 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2590 int child_id, i, vecp;
2594 vec[vecp++] = r1bindex (ftp, '/');
2595 vec[vecp++] = e->eb_site;
2598 vec[vecp++] = e->eb_dir;
2599 vec[vecp++] = e->eb_name;
2600 vec[vecp++] = ce->ce_file,
2601 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2602 ? "ascii" : "binary";
2607 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2611 adios ("fork", "unable to");
2615 close (fileno (ce->ce_fp));
2617 fprintf (stderr, "unable to exec ");
2623 if (pidXwait (child_id, NULL)) {
2624 username = password = NULL;
2634 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2639 mask = umask (cachetype ? ~m_gmprot () : 0222);
2640 if ((fp = fopen (cachefile, "w"))) {
2642 FILE *gp = ce->ce_fp;
2644 fseek (gp, 0L, SEEK_SET);
2646 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2648 fwrite (buffer, sizeof(*buffer), cc, fp);
2652 admonish (ce->ce_file, "error reading");
2657 admonish (cachefile, "error writing");
2666 fseek (ce->ce_fp, 0L, SEEK_SET);
2667 *file = ce->ce_file;
2668 return fileno (ce->ce_fp);
2679 return init_encoding (ct, openMail);
2684 openMail (CT ct, char **file)
2686 int child_id, fd, i, vecp;
2688 char *bp, buffer[BUFSIZ], *vec[7];
2689 struct exbody *e = ct->c_ctexbody;
2690 CE ce = ct->c_cefile;
2692 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2703 if (!e->eb_server) {
2704 content_error (NULL, ct, "missing server parameter");
2711 pidcheck (pidwait (xpid, NOTOK));
2715 /* Get buffer ready to go */
2717 buflen = sizeof(buffer);
2719 /* Now, construct query message */
2720 snprintf (bp, buflen, "Retrieve content");
2726 snprintf (bp, buflen, " %s", e->eb_partno);
2732 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2734 e->eb_subject ? e->eb_subject : e->eb_body);
2736 /* Now, check answer */
2737 if (!getanswer (buffer))
2741 vec[vecp++] = r1bindex (mailproc, '/');
2742 vec[vecp++] = e->eb_server;
2743 vec[vecp++] = "-subject";
2744 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2745 vec[vecp++] = "-body";
2746 vec[vecp++] = e->eb_body;
2749 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2753 advise ("fork", "unable to");
2757 execvp (mailproc, vec);
2758 fprintf (stderr, "unable to exec ");
2764 if (pidXwait (child_id, NULL) == OK)
2765 advise (NULL, "request sent");
2769 if (*file == NULL) {
2770 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2773 ce->ce_file = add (*file, NULL);
2777 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2778 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2782 /* showproc is for mhshow and mhstore, though mhlist -debug
2783 * prints it, too. */
2785 free (ct->c_showproc);
2786 ct->c_showproc = add ("true", NULL);
2788 fseek (ce->ce_fp, 0L, SEEK_SET);
2789 *file = ce->ce_file;
2790 return fileno (ce->ce_fp);
2795 readDigest (CT ct, char *cp)
2800 unsigned char *dp, value, *ep;
2801 unsigned char *b, *b1, *b2, *b3;
2803 b = (unsigned char *) &bits,
2804 b1 = &b[endian > 0 ? 1 : 2],
2805 b2 = &b[endian > 0 ? 2 : 1],
2806 b3 = &b[endian > 0 ? 3 : 0];
2811 for (ep = (dp = ct->c_digest)
2812 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2817 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2819 fprintf (stderr, "invalid BASE64 encoding\n");
2823 bits |= value << bitno;
2825 if ((bitno -= 6) < 0) {
2826 if (dp + (3 - skip) > ep)
2827 goto invalid_digest;
2842 goto self_delimiting;
2847 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2857 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2865 fprintf (stderr, "MD5 digest=");
2866 for (dp = ct->c_digest; dp < ep; dp++)
2867 fprintf (stderr, "%02x", *dp & 0xff);
2868 fprintf (stderr, "\n");