3 * mhparse.c -- routines to parse the contents of MIME messages
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
12 #include <h/signals.h>
19 #include <h/mhparse.h>
25 extern int endian; /* mhmisc.c */
27 extern pid_t xpid; /* mhshowsbr.c */
30 extern int rcachesw; /* mhcachesbr.c */
31 extern int wcachesw; /* mhcachesbr.c */
33 int checksw = 0; /* check Content-MD5 field */
36 * Directory to place temp files. This must
37 * be set before these routines are called.
42 * Structures for TEXT messages
44 struct k2v SubText[] = {
45 { "plain", TEXT_PLAIN },
46 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
47 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
48 { NULL, TEXT_UNKNOWN } /* this one must be last! */
51 struct k2v Charset[] = {
52 { "us-ascii", CHARSET_USASCII },
53 { "iso-8859-1", CHARSET_LATIN },
54 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
58 * Structures for MULTIPART messages
60 struct k2v SubMultiPart[] = {
61 { "mixed", MULTI_MIXED },
62 { "alternative", MULTI_ALTERNATE },
63 { "digest", MULTI_DIGEST },
64 { "parallel", MULTI_PARALLEL },
65 { NULL, MULTI_UNKNOWN } /* this one must be last! */
69 * Structures for MESSAGE messages
71 struct k2v SubMessage[] = {
72 { "rfc822", MESSAGE_RFC822 },
73 { "partial", MESSAGE_PARTIAL },
74 { "external-body", MESSAGE_EXTERNAL },
75 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
79 * Structure for APPLICATION messages
81 struct k2v SubApplication[] = {
82 { "octet-stream", APPLICATION_OCTETS },
83 { "postscript", APPLICATION_POSTSCRIPT },
84 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
89 int find_cache (CT, int, int *, char *, char *, int);
92 int part_ok (CT, int);
93 int type_ok (CT, int);
94 void content_error (char *, CT, char *, ...);
97 void free_content (CT);
98 void free_encoding (CT, int);
103 static CT get_content (FILE *, char *, int);
104 static int get_comment (CT, unsigned char **, int);
106 static int InitGeneric (CT);
107 static int InitText (CT);
108 static int InitMultiPart (CT);
109 static void reverse_parts (CT);
110 static int InitMessage (CT);
111 static int InitApplication (CT);
112 static int init_encoding (CT, OpenCEFunc);
113 static unsigned long size_encoding (CT);
114 static int InitBase64 (CT);
115 static int openBase64 (CT, char **);
116 static int InitQuoted (CT);
117 static int openQuoted (CT, char **);
118 static int Init7Bit (CT);
119 static int openExternal (CT, CT, CE, char **, int *);
120 static int InitFile (CT);
121 static int openFile (CT, char **);
122 static int InitFTP (CT);
123 static int openFTP (CT, char **);
124 static int InitMail (CT);
125 static int openMail (CT, char **);
126 static int readDigest (CT, char *);
128 struct str2init str2cts[] = {
129 { "application", CT_APPLICATION, InitApplication },
130 { "audio", CT_AUDIO, InitGeneric },
131 { "image", CT_IMAGE, InitGeneric },
132 { "message", CT_MESSAGE, InitMessage },
133 { "multipart", CT_MULTIPART, InitMultiPart },
134 { "text", CT_TEXT, InitText },
135 { "video", CT_VIDEO, InitGeneric },
136 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
137 { NULL, CT_UNKNOWN, NULL },
140 struct str2init str2ces[] = {
141 { "base64", CE_BASE64, InitBase64 },
142 { "quoted-printable", CE_QUOTED, InitQuoted },
143 { "8bit", CE_8BIT, Init7Bit },
144 { "7bit", CE_7BIT, Init7Bit },
145 { "binary", CE_BINARY, Init7Bit },
146 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
147 { NULL, CE_UNKNOWN, NULL },
151 * NOTE WELL: si_key MUST NOT have value of NOTOK
153 * si_key is 1 if access method is anonymous.
155 struct str2init str2methods[] = {
156 { "afs", 1, InitFile },
157 { "anon-ftp", 1, InitFTP },
158 { "ftp", 0, InitFTP },
159 { "local-file", 0, InitFile },
160 { "mail-server", 0, InitMail },
166 pidcheck (int status)
168 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
179 * Main entry point for parsing a MIME message or file.
180 * It returns the Content structure for the top level
181 * entity in the file.
185 parse_mime (char *file)
193 * Check if file is actually standard input
195 if ((is_stdin = !(strcmp (file, "-")))) {
196 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
198 advise("mhparse", "unable to create temporary file");
201 file = add (tfile, NULL);
204 while (fgets (buffer, sizeof(buffer), stdin))
208 if (ferror (stdin)) {
210 advise ("stdin", "error reading");
215 advise (file, "error writing");
218 fseek (fp, 0L, SEEK_SET);
219 } else if ((fp = fopen (file, "r")) == NULL) {
220 advise (file, "unable to read");
224 if (!(ct = get_content (fp, file, 1))) {
227 advise (NULL, "unable to decode %s", file);
232 ct->c_unlink = 1; /* temp file to remove */
236 if (ct->c_end == 0L) {
237 fseek (fp, 0L, SEEK_END);
238 ct->c_end = ftell (fp);
241 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
253 * Main routine for reading/parsing the headers
254 * of a message content.
256 * toplevel = 1 # we are at the top level of the message
257 * toplevel = 0 # we are inside message type or multipart type
258 * # other than multipart/digest
259 * toplevel = -1 # we are inside multipart/digest
260 * NB: on failure we will fclose(in)!
264 get_content (FILE *in, char *file, int toplevel)
267 char buf[BUFSIZ], name[NAMESZ];
272 /* allocate the content structure */
273 if (!(ct = (CT) calloc (1, sizeof(*ct))))
274 adios (NULL, "out of memory");
277 ct->c_file = add (file, NULL);
278 ct->c_begin = ftell (ct->c_fp) + 1;
281 * Parse the header fields for this
282 * content into a linked list.
284 for (compnum = 1, state = FLD;;) {
285 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
291 /* get copies of the buffers */
292 np = add (name, NULL);
293 vp = add (buf, NULL);
295 /* if necessary, get rest of field */
296 while (state == FLDPLUS) {
297 state = m_getfld (state, name, buf, sizeof(buf), in);
298 vp = add (buf, vp); /* add to previous value */
301 /* Now add the header data to the list */
302 add_header (ct, np, vp);
304 /* continue, if this isn't the last header field */
305 if (state != FLDEOF) {
306 ct->c_begin = ftell (in) + 1;
313 ct->c_begin = ftell (in) - strlen (buf);
317 ct->c_begin = ftell (in);
322 adios (NULL, "message format error in component #%d", compnum);
325 adios (NULL, "getfld() returned %d", state);
328 /* break out of the loop */
333 * Read the content headers. We will parse the
334 * MIME related header fields into their various
335 * structures and set internal flags related to
336 * content type/subtype, etc.
339 hp = ct->c_first_hf; /* start at first header field */
341 /* Get MIME-Version field */
342 if (!mh_strcasecmp (hp->name, VRSN_FIELD)) {
345 unsigned char *cp, *dp;
348 advise (NULL, "message %s has multiple %s: fields",
349 ct->c_file, VRSN_FIELD);
352 ct->c_vrsn = add (hp->value, NULL);
354 /* Now, cleanup this field */
357 while (isspace (*cp))
359 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
361 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
366 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
368 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
371 for (dp = cp; istoken (*dp); dp++)
375 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
378 admonish (NULL, "message %s has unknown value for %s: field (%s)",
379 ct->c_file, VRSN_FIELD, cp);
382 else if (!mh_strcasecmp (hp->name, TYPE_FIELD)) {
383 /* Get Content-Type field */
384 struct str2init *s2i;
385 CI ci = &ct->c_ctinfo;
387 /* Check if we've already seen a Content-Type header */
389 advise (NULL, "message %s has multiple %s: fields",
390 ct->c_file, TYPE_FIELD);
394 /* Parse the Content-Type field */
395 if (get_ctinfo (hp->value, ct, 0) == NOTOK)
399 * Set the Init function and the internal
400 * flag for this content type.
402 for (s2i = str2cts; s2i->si_key; s2i++)
403 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
405 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
407 ct->c_type = s2i->si_val;
408 ct->c_ctinitfnx = s2i->si_init;
410 else if (!mh_strcasecmp (hp->name, ENCODING_FIELD)) {
411 /* Get Content-Transfer-Encoding field */
413 unsigned char *cp, *dp;
414 struct str2init *s2i;
417 * Check if we've already seen the
418 * Content-Transfer-Encoding field
421 advise (NULL, "message %s has multiple %s: fields",
422 ct->c_file, ENCODING_FIELD);
426 /* get copy of this field */
427 ct->c_celine = cp = add (hp->value, NULL);
429 while (isspace (*cp))
431 for (dp = cp; istoken (*dp); dp++)
437 * Find the internal flag and Init function
438 * for this transfer encoding.
440 for (s2i = str2ces; s2i->si_key; s2i++)
441 if (!mh_strcasecmp (cp, s2i->si_key))
443 if (!s2i->si_key && !uprf (cp, "X-"))
446 ct->c_encoding = s2i->si_val;
448 /* Call the Init function for this encoding */
449 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
452 else if (!mh_strcasecmp (hp->name, MD5_FIELD)) {
453 /* Get Content-MD5 field */
454 unsigned char *cp, *dp;
460 if (ct->c_digested) {
461 advise (NULL, "message %s has multiple %s: fields",
462 ct->c_file, MD5_FIELD);
466 ep = cp = add (hp->value, NULL); /* get a copy */
468 while (isspace (*cp))
470 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
472 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
477 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
479 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
484 for (dp = cp; *dp && !isspace (*dp); dp++)
492 else if (!mh_strcasecmp (hp->name, ID_FIELD)) {
493 /* Get Content-ID field */
494 ct->c_id = add (hp->value, ct->c_id);
496 else if (!mh_strcasecmp (hp->name, DESCR_FIELD)) {
497 /* Get Content-Description field */
498 ct->c_descr = add (hp->value, ct->c_descr);
500 else if (!mh_strcasecmp (hp->name, DISPO_FIELD)) {
501 /* Get Content-Disposition field */
502 ct->c_dispo = add (hp->value, ct->c_dispo);
506 hp = hp->next; /* next header field */
510 * Check if we saw a Content-Type field.
511 * If not, then assign a default value for
512 * it, and the Init function.
516 * If we are inside a multipart/digest message,
517 * so default type is message/rfc822
520 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
522 ct->c_type = CT_MESSAGE;
523 ct->c_ctinitfnx = InitMessage;
526 * Else default type is text/plain
528 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
530 ct->c_type = CT_TEXT;
531 ct->c_ctinitfnx = InitText;
535 /* Use default Transfer-Encoding, if necessary */
537 ct->c_encoding = CE_7BIT;
550 * small routine to add header field to list
554 add_header (CT ct, char *name, char *value)
558 /* allocate header field structure */
559 hp = mh_xmalloc (sizeof(*hp));
561 /* link data into header structure */
566 /* link header structure into the list */
567 if (ct->c_first_hf == NULL) {
568 ct->c_first_hf = hp; /* this is the first */
571 ct->c_last_hf->next = hp; /* add it to the end */
579 /* Make sure that buf contains at least one appearance of name,
580 followed by =. If not, insert both name and value, just after
581 first semicolon, if any. Note that name should not contain a
582 trailing =. And quotes will be added around the value. Typical
583 usage: make sure that a Content-Disposition header contains
584 filename="foo". If it doesn't and value does, use value from
587 incl_name_value (unsigned char *buf, char *name, char *value) {
590 /* Assume that name is non-null. */
592 char *name_plus_equal = concat (name, "=", NULL);
594 if (! strstr (buf, name_plus_equal)) {
597 char *prefix, *suffix;
599 /* Trim trailing space, esp. newline. */
600 for (cp = &buf[strlen (buf) - 1];
601 cp >= buf && isspace (*cp);
606 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
608 /* Insert at first semicolon, if any. If none, append to
610 prefix = add (buf, NULL);
611 if ((cp = strchr (prefix, ';'))) {
612 suffix = concat (cp, NULL);
614 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
618 newbuf = concat (buf, insertion, "\n", NULL);
626 free (name_plus_equal);
632 /* Extract just name_suffix="foo", if any, from value. If there isn't
633 one, return the entire value. Note that, for example, a name_suffix
634 of name will match filename="foo", and return foo. */
636 extract_name_value (char *name_suffix, char *value) {
637 char *extracted_name_value = value;
638 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
639 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
642 free (name_suffix_plus_quote);
643 if (name_suffix_equals) {
644 char *name_suffix_begin;
647 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
648 name_suffix_begin = ++cp;
649 /* Find second \". */
650 for (; *cp != '"'; ++cp) /* empty */;
652 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
653 memcpy (extracted_name_value,
655 cp - name_suffix_begin);
656 extracted_name_value[cp - name_suffix_begin] = '\0';
659 return extracted_name_value;
663 * Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
664 * directives. Fills in the information of the CTinfo structure.
667 get_ctinfo (unsigned char *cp, CT ct, int magic)
676 i = strlen (invo_name) + 2;
678 /* store copy of Content-Type line */
679 cp = ct->c_ctline = add (cp, NULL);
681 while (isspace (*cp)) /* trim leading spaces */
684 /* change newlines to spaces */
685 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
688 /* trim trailing spaces */
689 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
695 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
697 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
700 for (dp = cp; istoken (*dp); dp++)
703 ci->ci_type = add (cp, NULL); /* store content type */
707 advise (NULL, "invalid %s: field in message %s (empty type)",
708 TYPE_FIELD, ct->c_file);
712 /* down case the content type string */
713 for (dp = ci->ci_type; *dp; dp++)
714 if (isalpha(*dp) && isupper (*dp))
717 while (isspace (*cp))
720 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
725 ci->ci_subtype = add ("", NULL);
730 while (isspace (*cp))
733 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
736 for (dp = cp; istoken (*dp); dp++)
739 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
742 if (!*ci->ci_subtype) {
744 "invalid %s: field in message %s (empty subtype for \"%s\")",
745 TYPE_FIELD, ct->c_file, ci->ci_type);
749 /* down case the content subtype string */
750 for (dp = ci->ci_subtype; *dp; dp++)
751 if (isalpha(*dp) && isupper (*dp))
755 while (isspace (*cp))
758 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
762 * Parse attribute/value pairs given with Content-Type
764 ep = (ap = ci->ci_attrs) + NPARMS;
771 "too many parameters in message %s's %s: field (%d max)",
772 ct->c_file, TYPE_FIELD, NPARMS);
777 while (isspace (*cp))
780 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
785 "extraneous trailing ';' in message %s's %s: parameter list",
786 ct->c_file, TYPE_FIELD);
790 /* down case the attribute name */
791 for (dp = cp; istoken (*dp); dp++)
792 if (isalpha(*dp) && isupper (*dp))
795 for (up = dp; isspace (*dp);)
797 if (dp == cp || *dp != '=') {
799 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
800 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
804 vp = (*ap = add (cp, NULL)) + (up - cp);
806 for (dp++; isspace (*dp);)
809 /* now add the attribute value */
810 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
813 for (cp = ++dp, dp = vp;;) {
818 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
819 ct->c_file, TYPE_FIELD, i, i, "", *ap);
824 if ((c = *cp++) == '\0')
839 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
845 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
846 ct->c_file, TYPE_FIELD, i, i, "", *ap);
851 while (isspace (*cp))
854 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
859 * Get any <Content-Id> given in buffer
861 if (magic && *cp == '<') {
866 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
867 advise (NULL, "invalid ID in message %s", ct->c_file);
873 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
879 while (isspace (*cp))
884 * Get any [Content-Description] given in buffer.
886 if (magic && *cp == '[') {
888 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
892 advise (NULL, "invalid description in message %s", ct->c_file);
900 ct->c_descr = concat (ct->c_descr, "\n", NULL);
906 while (isspace (*cp))
911 * Get any {Content-Disposition} given in buffer.
913 if (magic && *cp == '{') {
915 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
919 advise (NULL, "invalid disposition in message %s", ct->c_file);
927 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
933 while (isspace (*cp))
938 * Check if anything is left over
942 ci->ci_magic = add (cp, NULL);
944 /* If there is a Content-Disposition header and it doesn't
945 have a *filename=, extract it from the magic contents.
946 The r1bindex call skips any leading directory
950 incl_name_value (ct->c_dispo,
952 r1bindex (extract_name_value ("name",
959 "extraneous information in message %s's %s: field\n%*.*s(%s)",
960 ct->c_file, TYPE_FIELD, i, i, "", cp);
968 get_comment (CT ct, unsigned char **ap, int istype)
973 char c, buffer[BUFSIZ], *dp;
985 advise (NULL, "invalid comment in message %s's %s: field",
986 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
991 if ((c = *cp++) == '\0')
1014 if ((dp = ci->ci_comment)) {
1015 ci->ci_comment = concat (dp, " ", buffer, NULL);
1018 ci->ci_comment = add (buffer, NULL);
1022 while (isspace (*cp))
1033 * Handles content types audio, image, and video.
1034 * There's not much to do right here.
1042 return OK; /* not much to do here */
1053 char buffer[BUFSIZ];
1055 char **ap, **ep, *cp;
1058 CI ci = &ct->c_ctinfo;
1060 /* check for missing subtype */
1061 if (!*ci->ci_subtype)
1062 ci->ci_subtype = add ("plain", ci->ci_subtype);
1065 for (kv = SubText; kv->kv_key; kv++)
1066 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1068 ct->c_subtype = kv->kv_value;
1070 /* allocate text character set structure */
1071 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1072 adios (NULL, "out of memory");
1073 ct->c_ctparams = (void *) t;
1075 /* scan for charset parameter */
1076 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1077 if (!mh_strcasecmp (*ap, "charset"))
1080 /* check if content specified a character set */
1082 /* match character set or set to CHARSET_UNKNOWN */
1083 for (kv = Charset; kv->kv_key; kv++) {
1084 if (!mh_strcasecmp (*ep, kv->kv_key)) {
1089 t->tx_charset = kv->kv_value;
1091 t->tx_charset = CHARSET_UNSPECIFIED;
1095 * If we can not handle character set natively,
1096 * then check profile for string to modify the
1097 * terminal or display method.
1099 * termproc is for mhshow, though mhlist -debug prints it, too.
1101 if (chset != NULL && !check_charset (chset, strlen (chset))) {
1102 snprintf (buffer, sizeof(buffer), "%s-charset-%s", invo_name, chset);
1103 if ((cp = context_find (buffer)))
1104 ct->c_termproc = getcpy (cp);
1116 InitMultiPart (CT ct)
1120 unsigned char *cp, *dp;
1122 char *bp, buffer[BUFSIZ];
1123 struct multipart *m;
1125 struct part *part, **next;
1126 CI ci = &ct->c_ctinfo;
1131 * The encoding for multipart messages must be either
1132 * 7bit, 8bit, or binary (per RFC2045).
1134 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1135 && ct->c_encoding != CE_BINARY) {
1137 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1138 ci->ci_type, ci->ci_subtype, ct->c_file);
1143 for (kv = SubMultiPart; kv->kv_key; kv++)
1144 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1146 ct->c_subtype = kv->kv_value;
1149 * Check for "boundary" parameter, which is
1150 * required for multipart messages.
1153 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1154 if (!mh_strcasecmp (*ap, "boundary")) {
1160 /* complain if boundary parameter is missing */
1163 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1164 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1168 /* allocate primary structure for multipart info */
1169 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1170 adios (NULL, "out of memory");
1171 ct->c_ctparams = (void *) m;
1173 /* check if boundary parameter contains only whitespace characters */
1174 for (cp = bp; isspace (*cp); cp++)
1177 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1178 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1182 /* remove trailing whitespace from boundary parameter */
1183 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1188 /* record boundary separators */
1189 m->mp_start = concat (bp, "\n", NULL);
1190 m->mp_stop = concat (bp, "--\n", NULL);
1192 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1193 advise (ct->c_file, "unable to open for reading");
1197 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1199 next = &m->mp_parts;
1203 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1207 pos += strlen (buffer);
1208 if (buffer[0] != '-' || buffer[1] != '-')
1211 if (strcmp (buffer + 2, m->mp_start))
1214 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1215 adios (NULL, "out of memory");
1217 next = &part->mp_next;
1219 if (!(p = get_content (fp, ct->c_file,
1220 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1227 fseek (fp, pos, SEEK_SET);
1230 if (strcmp (buffer + 2, m->mp_start) == 0) {
1234 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1235 if (p->c_end < p->c_begin)
1236 p->c_begin = p->c_end;
1241 if (strcmp (buffer + 2, m->mp_stop) == 0)
1247 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1248 if (!inout && part) {
1250 p->c_end = ct->c_end;
1252 if (p->c_begin >= p->c_end) {
1253 for (next = &m->mp_parts; *next != part;
1254 next = &((*next)->mp_next))
1258 free ((char *) part);
1263 /* reverse the order of the parts for multipart/alternative */
1264 if (ct->c_subtype == MULTI_ALTERNATE)
1268 * label all subparts with part number, and
1269 * then initialize the content of the subpart.
1274 char partnam[BUFSIZ];
1277 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1278 pp = partnam + strlen (partnam);
1283 for (part = m->mp_parts, partnum = 1; part;
1284 part = part->mp_next, partnum++) {
1287 sprintf (pp, "%d", partnum);
1288 p->c_partno = add (partnam, NULL);
1290 /* initialize the content of the subparts */
1291 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1306 * reverse the order of the parts of a multipart
1310 reverse_parts (CT ct)
1313 struct multipart *m;
1314 struct part **base, **bmp, **next, *part;
1316 m = (struct multipart *) ct->c_ctparams;
1318 /* if only one part, just return */
1319 if (!m->mp_parts || !m->mp_parts->mp_next)
1322 /* count number of parts */
1324 for (part = m->mp_parts; part; part = part->mp_next)
1327 /* allocate array of pointers to the parts */
1328 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1329 adios (NULL, "out of memory");
1332 /* point at all the parts */
1333 for (part = m->mp_parts; part; part = part->mp_next)
1337 /* reverse the order of the parts */
1338 next = &m->mp_parts;
1339 for (bmp--; bmp >= base; bmp--) {
1342 next = &part->mp_next;
1346 /* free array of pointers */
1347 free ((char *) base);
1359 CI ci = &ct->c_ctinfo;
1361 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1363 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1364 ci->ci_type, ci->ci_subtype, ct->c_file);
1368 /* check for missing subtype */
1369 if (!*ci->ci_subtype)
1370 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1373 for (kv = SubMessage; kv->kv_key; kv++)
1374 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1376 ct->c_subtype = kv->kv_value;
1378 switch (ct->c_subtype) {
1379 case MESSAGE_RFC822:
1382 case MESSAGE_PARTIAL:
1387 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1388 adios (NULL, "out of memory");
1389 ct->c_ctparams = (void *) p;
1391 /* scan for parameters "id", "number", and "total" */
1392 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1393 if (!mh_strcasecmp (*ap, "id")) {
1394 p->pm_partid = add (*ep, NULL);
1397 if (!mh_strcasecmp (*ap, "number")) {
1398 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1399 || p->pm_partno < 1) {
1402 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1403 *ap, ci->ci_type, ci->ci_subtype,
1404 ct->c_file, TYPE_FIELD);
1409 if (!mh_strcasecmp (*ap, "total")) {
1410 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1419 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1421 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1422 ci->ci_type, ci->ci_subtype,
1423 ct->c_file, TYPE_FIELD);
1429 case MESSAGE_EXTERNAL:
1436 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1437 adios (NULL, "out of memory");
1438 ct->c_ctparams = (void *) e;
1441 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1442 advise (ct->c_file, "unable to open for reading");
1446 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1448 if (!(p = get_content (fp, ct->c_file, 0))) {
1456 if ((exresult = params_external (ct, 0)) != NOTOK
1457 && p->c_ceopenfnx == openMail) {
1461 if ((size = ct->c_end - p->c_begin) <= 0) {
1463 content_error (NULL, ct,
1464 "empty body for access-type=mail-server");
1468 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1469 fseek (p->c_fp, p->c_begin, SEEK_SET);
1471 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1473 adios ("failed", "fread");
1476 adios (NULL, "unexpected EOF from fread");
1479 bp += cc, size -= cc;
1486 p->c_end = p->c_begin;
1491 if (exresult == NOTOK)
1493 if (e->eb_flags == NOTOK)
1496 switch (p->c_type) {
1501 if (p->c_subtype != MESSAGE_RFC822)
1505 e->eb_partno = ct->c_partno;
1507 (*p->c_ctinitfnx) (p);
1522 params_external (CT ct, int composing)
1525 struct exbody *e = (struct exbody *) ct->c_ctparams;
1526 CI ci = &ct->c_ctinfo;
1528 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1529 if (!mh_strcasecmp (*ap, "access-type")) {
1530 struct str2init *s2i;
1531 CT p = e->eb_content;
1533 for (s2i = str2methods; s2i->si_key; s2i++)
1534 if (!mh_strcasecmp (*ep, s2i->si_key))
1538 e->eb_flags = NOTOK;
1539 p->c_encoding = CE_EXTERNAL;
1542 e->eb_access = s2i->si_key;
1543 e->eb_flags = s2i->si_val;
1544 p->c_encoding = CE_EXTERNAL;
1546 /* Call the Init function for this external type */
1547 if ((*s2i->si_init)(p) == NOTOK)
1551 if (!mh_strcasecmp (*ap, "name")) {
1555 if (!mh_strcasecmp (*ap, "permission")) {
1556 e->eb_permission = *ep;
1559 if (!mh_strcasecmp (*ap, "site")) {
1563 if (!mh_strcasecmp (*ap, "directory")) {
1567 if (!mh_strcasecmp (*ap, "mode")) {
1571 if (!mh_strcasecmp (*ap, "size")) {
1572 sscanf (*ep, "%lu", &e->eb_size);
1575 if (!mh_strcasecmp (*ap, "server")) {
1579 if (!mh_strcasecmp (*ap, "subject")) {
1580 e->eb_subject = *ep;
1583 if (composing && !mh_strcasecmp (*ap, "body")) {
1584 e->eb_body = getcpy (*ep);
1589 if (!e->eb_access) {
1591 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1592 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1605 InitApplication (CT ct)
1608 CI ci = &ct->c_ctinfo;
1611 for (kv = SubApplication; kv->kv_key; kv++)
1612 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1614 ct->c_subtype = kv->kv_value;
1621 * TRANSFER ENCODINGS
1625 init_encoding (CT ct, OpenCEFunc openfnx)
1629 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1630 adios (NULL, "out of memory");
1633 ct->c_ceopenfnx = openfnx;
1634 ct->c_ceclosefnx = close_encoding;
1635 ct->c_cesizefnx = size_encoding;
1642 close_encoding (CT ct)
1646 if (!(ce = ct->c_cefile))
1656 static unsigned long
1657 size_encoding (CT ct)
1665 if (!(ce = ct->c_cefile))
1666 return (ct->c_end - ct->c_begin);
1668 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1669 return (long) st.st_size;
1672 if (stat (ce->ce_file, &st) != NOTOK)
1673 return (long) st.st_size;
1678 if (ct->c_encoding == CE_EXTERNAL)
1679 return (ct->c_end - ct->c_begin);
1682 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1683 return (ct->c_end - ct->c_begin);
1685 if (fstat (fd, &st) != NOTOK)
1686 size = (long) st.st_size;
1690 (*ct->c_ceclosefnx) (ct);
1699 static unsigned char b642nib[0x80] = {
1700 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1701 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1702 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1703 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1704 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1705 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1706 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1707 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1708 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1709 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1710 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1711 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1712 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1713 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1714 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1715 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1722 return init_encoding (ct, openBase64);
1727 openBase64 (CT ct, char **file)
1729 int bitno, cc, digested;
1730 int fd, len, skip, own_ct_fp = 0;
1732 unsigned char value, *b, *b1, *b2, *b3;
1733 unsigned char *cp, *ep;
1734 char buffer[BUFSIZ];
1735 /* sbeck -- handle suffixes */
1740 /* the decoder works on the least-significant three bytes of the bits integer,
1741 but their position in memory depend on both endian-ness and size of
1742 long int... for little-endian architectures the size is irrelevant, for
1743 big-endian archs it's crucial... ideally we'd adopt posix and use a64l instead
1745 b = (unsigned char *) &bits;
1746 b1 = &b[endian > 0 ? sizeof(bits)==8?5:1 : 2];
1747 b2 = &b[endian > 0 ? sizeof(bits)==8?6:2 : 1];
1748 b3 = &b[endian > 0 ? sizeof(bits)==8?7:3 : 0];
1752 fseek (ce->ce_fp, 0L, SEEK_SET);
1757 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1758 content_error (ce->ce_file, ct, "unable to fopen for reading");
1764 if (*file == NULL) {
1765 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
1768 ce->ce_file = add (*file, NULL);
1772 /* sbeck@cise.ufl.edu -- handle suffixes */
1774 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
1775 invo_name, ci->ci_type, ci->ci_subtype);
1776 cp = context_find (buffer);
1777 if (cp == NULL || *cp == '\0') {
1778 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1780 cp = context_find (buffer);
1782 if (cp != NULL && *cp != '\0') {
1783 if (ce->ce_unlink) {
1784 /* Temporary file already exists, so we rename to
1785 version with extension. */
1786 char *file_org = strdup(ce->ce_file);
1787 ce->ce_file = add (cp, ce->ce_file);
1788 if (rename(file_org, ce->ce_file)) {
1789 adios (ce->ce_file, "unable to rename %s to ", file_org);
1794 ce->ce_file = add (cp, ce->ce_file);
1798 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1799 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1803 if ((len = ct->c_end - ct->c_begin) < 0)
1804 adios (NULL, "internal error(1)");
1807 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1808 content_error (ct->c_file, ct, "unable to open for reading");
1814 if ((digested = ct->c_digested))
1815 MD5Init (&mdContext);
1821 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1823 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1825 content_error (ct->c_file, ct, "error reading from");
1829 content_error (NULL, ct, "premature eof");
1837 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1842 if (skip || (*cp & 0x80)
1843 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1845 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1847 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1850 content_error (NULL, ct,
1851 "invalid BASE64 encoding -- continuing");
1855 bits |= value << bitno;
1857 if ((bitno -= 6) < 0) {
1858 putc ((char) *b1, ce->ce_fp);
1860 MD5Update (&mdContext, b1, 1);
1862 putc ((char) *b2, ce->ce_fp);
1864 MD5Update (&mdContext, b2, 1);
1866 putc ((char) *b3, ce->ce_fp);
1868 MD5Update (&mdContext, b3, 1);
1872 if (ferror (ce->ce_fp)) {
1873 content_error (ce->ce_file, ct,
1874 "error writing to");
1877 bitno = 18, bits = 0L, skip = 0;
1883 goto self_delimiting;
1892 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1894 content_error (NULL, ct, "invalid BASE64 encoding");
1899 fseek (ct->c_fp, 0L, SEEK_SET);
1901 if (fflush (ce->ce_fp)) {
1902 content_error (ce->ce_file, ct, "error writing to");
1907 unsigned char digest[16];
1909 MD5Final (digest, &mdContext);
1910 if (memcmp((char *) digest, (char *) ct->c_digest,
1911 sizeof(digest) / sizeof(digest[0])))
1912 content_error (NULL, ct,
1913 "content integrity suspect (digest mismatch) -- continuing");
1916 fprintf (stderr, "content integrity confirmed\n");
1919 fseek (ce->ce_fp, 0L, SEEK_SET);
1922 *file = ce->ce_file;
1927 return fileno (ce->ce_fp);
1934 free_encoding (ct, 0);
1943 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1950 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1951 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1952 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1953 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1954 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1955 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1956 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1957 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1958 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1966 return init_encoding (ct, openQuoted);
1971 openQuoted (CT ct, char **file)
1973 int cc, digested, len, quoted, own_ct_fp = 0;
1974 unsigned char *cp, *ep;
1975 char buffer[BUFSIZ];
1978 /* sbeck -- handle suffixes */
1984 fseek (ce->ce_fp, 0L, SEEK_SET);
1989 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1990 content_error (ce->ce_file, ct, "unable to fopen for reading");
1996 if (*file == NULL) {
1997 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2000 ce->ce_file = add (*file, NULL);
2004 /* sbeck@cise.ufl.edu -- handle suffixes */
2006 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2007 invo_name, ci->ci_type, ci->ci_subtype);
2008 cp = context_find (buffer);
2009 if (cp == NULL || *cp == '\0') {
2010 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2012 cp = context_find (buffer);
2014 if (cp != NULL && *cp != '\0') {
2015 if (ce->ce_unlink) {
2016 /* Temporary file already exists, so we rename to
2017 version with extension. */
2018 char *file_org = strdup(ce->ce_file);
2019 ce->ce_file = add (cp, ce->ce_file);
2020 if (rename(file_org, ce->ce_file)) {
2021 adios (ce->ce_file, "unable to rename %s to ", file_org);
2026 ce->ce_file = add (cp, ce->ce_file);
2030 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2031 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2035 if ((len = ct->c_end - ct->c_begin) < 0)
2036 adios (NULL, "internal error(2)");
2039 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2040 content_error (ct->c_file, ct, "unable to open for reading");
2046 if ((digested = ct->c_digested))
2047 MD5Init (&mdContext);
2054 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2056 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2057 content_error (NULL, ct, "premature eof");
2061 if ((cc = strlen (buffer)) > len)
2065 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2070 for (; cp < ep; cp++) {
2072 /* in an escape sequence */
2074 /* at byte 1 of an escape sequence */
2075 mask = hex2nib[*cp & 0x7f];
2076 /* next is byte 2 */
2079 /* at byte 2 of an escape sequence */
2081 mask |= hex2nib[*cp & 0x7f];
2082 putc (mask, ce->ce_fp);
2084 MD5Update (&mdContext, &mask, 1);
2085 if (ferror (ce->ce_fp)) {
2086 content_error (ce->ce_file, ct, "error writing to");
2089 /* finished escape sequence; next may be literal or a new
2090 * escape sequence */
2093 /* on to next byte */
2097 /* not in an escape sequence */
2099 /* starting an escape sequence, or invalid '='? */
2100 if (cp + 1 < ep && cp[1] == '\n') {
2101 /* "=\n" soft line break, eat the \n */
2105 if (cp + 1 >= ep || cp + 2 >= ep) {
2106 /* We don't have 2 bytes left, so this is an invalid
2107 * escape sequence; just show the raw bytes (below). */
2108 } else if (isxdigit (cp[1]) && isxdigit (cp[2])) {
2109 /* Next 2 bytes are hex digits, making this a valid escape
2110 * sequence; let's decode it (above). */
2114 /* One or both of the next 2 is out of range, making this
2115 * an invalid escape sequence; just show the raw bytes
2120 /* Just show the raw byte. */
2121 putc (*cp, ce->ce_fp);
2124 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2126 MD5Update (&mdContext, (unsigned char *) cp, 1);
2129 if (ferror (ce->ce_fp)) {
2130 content_error (ce->ce_file, ct, "error writing to");
2136 content_error (NULL, ct,
2137 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2141 fseek (ct->c_fp, 0L, SEEK_SET);
2143 if (fflush (ce->ce_fp)) {
2144 content_error (ce->ce_file, ct, "error writing to");
2149 unsigned char digest[16];
2151 MD5Final (digest, &mdContext);
2152 if (memcmp((char *) digest, (char *) ct->c_digest,
2153 sizeof(digest) / sizeof(digest[0])))
2154 content_error (NULL, ct,
2155 "content integrity suspect (digest mismatch) -- continuing");
2158 fprintf (stderr, "content integrity confirmed\n");
2161 fseek (ce->ce_fp, 0L, SEEK_SET);
2164 *file = ce->ce_file;
2169 return fileno (ce->ce_fp);
2172 free_encoding (ct, 0);
2188 if (init_encoding (ct, open7Bit) == NOTOK)
2191 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2197 open7Bit (CT ct, char **file)
2199 int cc, fd, len, own_ct_fp = 0;
2200 char buffer[BUFSIZ];
2201 /* sbeck -- handle suffixes */
2208 fseek (ce->ce_fp, 0L, SEEK_SET);
2213 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2214 content_error (ce->ce_file, ct, "unable to fopen for reading");
2220 if (*file == NULL) {
2221 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2224 ce->ce_file = add (*file, NULL);
2228 /* sbeck@cise.ufl.edu -- handle suffixes */
2230 snprintf (buffer, sizeof(buffer), "%s-suffix-%s/%s",
2231 invo_name, ci->ci_type, ci->ci_subtype);
2232 cp = context_find (buffer);
2233 if (cp == NULL || *cp == '\0') {
2234 snprintf (buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2236 cp = context_find (buffer);
2238 if (cp != NULL && *cp != '\0') {
2239 if (ce->ce_unlink) {
2240 /* Temporary file already exists, so we rename to
2241 version with extension. */
2242 char *file_org = strdup(ce->ce_file);
2243 ce->ce_file = add (cp, ce->ce_file);
2244 if (rename(file_org, ce->ce_file)) {
2245 adios (ce->ce_file, "unable to rename %s to ", file_org);
2250 ce->ce_file = add (cp, ce->ce_file);
2254 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2255 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2259 if (ct->c_type == CT_MULTIPART) {
2261 CI ci = &ct->c_ctinfo;
2264 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2265 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2266 + 1 + strlen (ci->ci_subtype);
2267 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2268 putc (';', ce->ce_fp);
2271 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2273 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2274 fputs ("\n\t", ce->ce_fp);
2277 putc (' ', ce->ce_fp);
2280 fprintf (ce->ce_fp, "%s", buffer);
2284 if (ci->ci_comment) {
2285 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2286 fputs ("\n\t", ce->ce_fp);
2290 putc (' ', ce->ce_fp);
2293 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2296 fprintf (ce->ce_fp, "\n");
2298 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2300 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2302 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2303 fprintf (ce->ce_fp, "\n");
2306 if ((len = ct->c_end - ct->c_begin) < 0)
2307 adios (NULL, "internal error(3)");
2310 if ((ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2311 content_error (ct->c_file, ct, "unable to open for reading");
2317 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2319 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2321 content_error (ct->c_file, ct, "error reading from");
2325 content_error (NULL, ct, "premature eof");
2333 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2334 if (ferror (ce->ce_fp)) {
2335 content_error (ce->ce_file, ct, "error writing to");
2340 fseek (ct->c_fp, 0L, SEEK_SET);
2342 if (fflush (ce->ce_fp)) {
2343 content_error (ce->ce_file, ct, "error writing to");
2347 fseek (ce->ce_fp, 0L, SEEK_SET);
2350 *file = ce->ce_file;
2355 return fileno (ce->ce_fp);
2358 free_encoding (ct, 0);
2372 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2374 char cachefile[BUFSIZ];
2377 fseek (ce->ce_fp, 0L, SEEK_SET);
2382 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2383 content_error (ce->ce_file, ct, "unable to fopen for reading");
2389 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2390 cachefile, sizeof(cachefile)) != NOTOK) {
2391 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2392 ce->ce_file = getcpy (cachefile);
2396 admonish (cachefile, "unable to fopen for reading");
2403 *file = ce->ce_file;
2404 *fd = fileno (ce->ce_fp);
2415 return init_encoding (ct, openFile);
2420 openFile (CT ct, char **file)
2423 char cachefile[BUFSIZ];
2424 struct exbody *e = ct->c_ctexbody;
2425 CE ce = ct->c_cefile;
2427 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2439 content_error (NULL, ct, "missing name parameter");
2443 ce->ce_file = getcpy (e->eb_name);
2446 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2447 content_error (ce->ce_file, ct, "unable to fopen for reading");
2451 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2452 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2453 cachefile, sizeof(cachefile)) != NOTOK) {
2457 mask = umask (cachetype ? ~m_gmprot () : 0222);
2458 if ((fp = fopen (cachefile, "w"))) {
2460 char buffer[BUFSIZ];
2461 FILE *gp = ce->ce_fp;
2463 fseek (gp, 0L, SEEK_SET);
2465 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2467 fwrite (buffer, sizeof(*buffer), cc, fp);
2471 admonish (ce->ce_file, "error reading");
2476 admonish (cachefile, "error writing");
2484 fseek (ce->ce_fp, 0L, SEEK_SET);
2485 *file = ce->ce_file;
2486 return fileno (ce->ce_fp);
2496 return init_encoding (ct, openFTP);
2501 openFTP (CT ct, char **file)
2503 int cachetype, caching, fd;
2505 char *bp, *ftp, *user, *pass;
2506 char buffer[BUFSIZ], cachefile[BUFSIZ];
2509 static char *username = NULL;
2510 static char *password = NULL;
2515 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2521 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2532 if (!e->eb_name || !e->eb_site) {
2533 content_error (NULL, ct, "missing %s parameter",
2534 e->eb_name ? "site": "name");
2541 pidcheck (pidwait (xpid, NOTOK));
2545 /* Get the buffer ready to go */
2547 buflen = sizeof(buffer);
2550 * Construct the query message for user
2552 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2558 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2564 snprintf (bp, buflen, "\n using %sFTP from site %s",
2565 e->eb_flags ? "anonymous " : "", e->eb_site);
2570 if (e->eb_size > 0) {
2571 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2576 snprintf (bp, buflen, "? ");
2579 * Now, check the answer
2581 if (!getanswer (buffer))
2586 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (),
2590 ruserpass (e->eb_site, &username, &password);
2595 ce->ce_unlink = (*file == NULL);
2597 cachefile[0] = '\0';
2598 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2599 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2600 cachefile, sizeof(cachefile)) != NOTOK) {
2601 if (*file == NULL) {
2608 ce->ce_file = add (*file, NULL);
2610 ce->ce_file = add (cachefile, NULL);
2612 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2614 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2615 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2620 int child_id, i, vecp;
2624 vec[vecp++] = r1bindex (ftp, '/');
2625 vec[vecp++] = e->eb_site;
2628 vec[vecp++] = e->eb_dir;
2629 vec[vecp++] = e->eb_name;
2630 vec[vecp++] = ce->ce_file,
2631 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2632 ? "ascii" : "binary";
2637 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2641 adios ("fork", "unable to");
2645 close (fileno (ce->ce_fp));
2647 fprintf (stderr, "unable to exec ");
2653 if (pidXwait (child_id, NULL)) {
2654 username = password = NULL;
2664 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2669 mask = umask (cachetype ? ~m_gmprot () : 0222);
2670 if ((fp = fopen (cachefile, "w"))) {
2672 FILE *gp = ce->ce_fp;
2674 fseek (gp, 0L, SEEK_SET);
2676 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2678 fwrite (buffer, sizeof(*buffer), cc, fp);
2682 admonish (ce->ce_file, "error reading");
2687 admonish (cachefile, "error writing");
2696 fseek (ce->ce_fp, 0L, SEEK_SET);
2697 *file = ce->ce_file;
2698 return fileno (ce->ce_fp);
2709 return init_encoding (ct, openMail);
2714 openMail (CT ct, char **file)
2716 int child_id, fd, i, vecp;
2718 char *bp, buffer[BUFSIZ], *vec[7];
2719 struct exbody *e = ct->c_ctexbody;
2720 CE ce = ct->c_cefile;
2722 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2733 if (!e->eb_server) {
2734 content_error (NULL, ct, "missing server parameter");
2741 pidcheck (pidwait (xpid, NOTOK));
2745 /* Get buffer ready to go */
2747 buflen = sizeof(buffer);
2749 /* Now, construct query message */
2750 snprintf (bp, buflen, "Retrieve content");
2756 snprintf (bp, buflen, " %s", e->eb_partno);
2762 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2764 e->eb_subject ? e->eb_subject : e->eb_body);
2766 /* Now, check answer */
2767 if (!getanswer (buffer))
2771 vec[vecp++] = r1bindex (mailproc, '/');
2772 vec[vecp++] = e->eb_server;
2773 vec[vecp++] = "-subject";
2774 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2775 vec[vecp++] = "-body";
2776 vec[vecp++] = e->eb_body;
2779 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2783 advise ("fork", "unable to");
2787 execvp (mailproc, vec);
2788 fprintf (stderr, "unable to exec ");
2794 if (pidXwait (child_id, NULL) == OK)
2795 advise (NULL, "request sent");
2799 if (*file == NULL) {
2800 ce->ce_file = add (m_mktemp(tmp, NULL, NULL), NULL);
2803 ce->ce_file = add (*file, NULL);
2807 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2808 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2812 /* showproc is for mhshow and mhstore, though mhlist -debug
2813 * prints it, too. */
2815 free (ct->c_showproc);
2816 ct->c_showproc = add ("true", NULL);
2818 fseek (ce->ce_fp, 0L, SEEK_SET);
2819 *file = ce->ce_file;
2820 return fileno (ce->ce_fp);
2825 readDigest (CT ct, char *cp)
2830 unsigned char *dp, value, *ep;
2831 unsigned char *b, *b1, *b2, *b3;
2833 /* the decoder works on the least-significant three bytes of the bits integer,
2834 but their position in memory depend on both endian-ness and size of
2835 long int... for little-endian architectures the size is irrelevant, for
2836 big-endian archs it's crucial... ideally we'd adopt posix and use a64l instead
2838 b = (unsigned char *) &bits;
2839 b1 = &b[endian > 0 ? sizeof(bits)==8?5:1 : 2];
2840 b2 = &b[endian > 0 ? sizeof(bits)==8?6:2 : 1];
2841 b3 = &b[endian > 0 ? sizeof(bits)==8?7:3 : 0];
2847 for (ep = (dp = ct->c_digest)
2848 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2853 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
2855 fprintf (stderr, "invalid BASE64 encoding\n");
2859 bits |= value << bitno;
2861 if ((bitno -= 6) < 0) {
2862 if (dp + (3 - skip) > ep)
2863 goto invalid_digest;
2878 goto self_delimiting;
2883 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2893 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
2901 fprintf (stderr, "MD5 digest=");
2902 for (dp = ct->c_digest; dp < ep; dp++)
2903 fprintf (stderr, "%02x", *dp & 0xff);
2904 fprintf (stderr, "\n");