2 ** mhparse.c -- routines to parse the contents of MIME messages
4 ** This code is Copyright (c) 2002, by the authors of nmh. See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
11 #include <h/signals.h>
19 #include <h/mhparse.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
29 extern int endian; /* mhmisc.c */
31 extern pid_t xpid; /* mhshowsbr.c */
34 extern int rcachesw; /* mhcachesbr.c */
35 extern int wcachesw; /* mhcachesbr.c */
37 int checksw = 0; /* check Content-MD5 field */
40 ** Directory to place temp files. This must
41 ** be set before these routines are called.
46 ** Structures for TEXT messages
48 struct k2v SubText[] = {
49 { "plain", TEXT_PLAIN },
50 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
51 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
52 { NULL, TEXT_UNKNOWN } /* this one must be last! */
55 struct k2v Charset[] = {
56 { "us-ascii", CHARSET_USASCII },
57 { "iso-8859-1", CHARSET_LATIN },
58 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
62 ** Structures for MULTIPART messages
64 struct k2v SubMultiPart[] = {
65 { "mixed", MULTI_MIXED },
66 { "alternative", MULTI_ALTERNATE },
67 { "digest", MULTI_DIGEST },
68 { "parallel", MULTI_PARALLEL },
69 { NULL, MULTI_UNKNOWN } /* this one must be last! */
73 ** Structures for MESSAGE messages
75 struct k2v SubMessage[] = {
76 { "rfc822", MESSAGE_RFC822 },
77 { "partial", MESSAGE_PARTIAL },
78 { "external-body", MESSAGE_EXTERNAL },
79 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
83 ** Structure for APPLICATION messages
85 struct k2v SubApplication[] = {
86 { "octet-stream", APPLICATION_OCTETS },
87 { "postscript", APPLICATION_POSTSCRIPT },
88 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
93 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
96 int find_cache(CT, int, int *, char *, char *, int);
100 int type_ok(CT, int);
101 int make_intermediates(char *);
102 void content_error(char *, CT, char *, ...);
105 void free_content(CT);
106 void free_encoding(CT, int);
111 static CT get_content(FILE *, char *, int);
112 static int get_comment(CT, unsigned char **, int);
114 static int InitGeneric(CT);
115 static int InitText(CT);
116 static int InitMultiPart(CT);
117 static void reverse_parts(CT);
118 static int InitMessage(CT);
119 static int InitApplication(CT);
120 static int init_encoding(CT, OpenCEFunc);
121 static unsigned long size_encoding(CT);
122 static int InitBase64(CT);
123 static int openBase64(CT, char **);
124 static int InitQuoted(CT);
125 static int openQuoted(CT, char **);
126 static int Init7Bit(CT);
127 static int openExternal(CT, CT, CE, char **, int *);
128 static int InitFile(CT);
129 static int openFile(CT, char **);
130 static int InitFTP(CT);
131 static int openFTP(CT, char **);
132 static int InitMail(CT);
133 static int openMail(CT, char **);
134 static int readDigest(CT, char *);
136 struct str2init str2cts[] = {
137 { "application", CT_APPLICATION, InitApplication },
138 { "audio", CT_AUDIO, InitGeneric },
139 { "image", CT_IMAGE, InitGeneric },
140 { "message", CT_MESSAGE, InitMessage },
141 { "multipart", CT_MULTIPART, InitMultiPart },
142 { "text", CT_TEXT, InitText },
143 { "video", CT_VIDEO, InitGeneric },
144 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
145 { NULL, CT_UNKNOWN, NULL },
148 struct str2init str2ces[] = {
149 { "base64", CE_BASE64, InitBase64 },
150 { "quoted-printable", CE_QUOTED, InitQuoted },
151 { "8bit", CE_8BIT, Init7Bit },
152 { "7bit", CE_7BIT, Init7Bit },
153 { "binary", CE_BINARY, Init7Bit },
154 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
155 { NULL, CE_UNKNOWN, NULL },
159 ** NOTE WELL: si_key MUST NOT have value of NOTOK
161 ** si_key is 1 if access method is anonymous.
163 struct str2init str2methods[] = {
164 { "afs", 1, InitFile },
165 { "anon-ftp", 1, InitFTP },
166 { "ftp", 0, InitFTP },
167 { "local-file", 0, InitFile },
168 { "mail-server", 0, InitMail },
176 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
187 ** Main entry point for parsing a MIME message or file.
188 ** It returns the Content structure for the top level
189 ** entity in the file.
192 parse_mime(char *file)
200 ** Check if file is actually standard input
202 if ((is_stdin = !(strcmp(file, "-")))) {
203 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
205 advise("mhparse", "unable to create temporary file");
208 file = getcpy(tfile);
211 while (fgets(buffer, sizeof(buffer), stdin))
217 advise("stdin", "error reading");
222 advise(file, "error writing");
225 fseek(fp, 0L, SEEK_SET);
226 } else if ((fp = fopen(file, "r")) == NULL) {
227 advise(file, "unable to read");
231 if (!(ct = get_content(fp, file, 1))) {
234 advise(NULL, "unable to decode %s", file);
239 ct->c_unlink = 1; /* temp file to remove */
243 if (ct->c_end == 0L) {
244 fseek(fp, 0L, SEEK_END);
245 ct->c_end = ftell(fp);
248 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
260 ** Main routine for reading/parsing the headers
261 ** of a message content.
263 ** toplevel = 1 # we are at the top level of the message
264 ** toplevel = 0 # we are inside message type or multipart type
265 ** # other than multipart/digest
266 ** toplevel = -1 # we are inside multipart/digest
267 ** NB: on failure we will fclose(in)!
271 get_content(FILE *in, char *file, int toplevel)
274 char buf[BUFSIZ], name[NAMESZ];
279 /* allocate the content structure */
280 if (!(ct = (CT) calloc(1, sizeof(*ct))))
281 adios(NULL, "out of memory");
284 ct->c_file = getcpy(file);
285 ct->c_begin = ftell(ct->c_fp) + 1;
288 ** Parse the header fields for this
289 ** content into a linked list.
291 for (compnum = 1, state = FLD;;) {
292 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
298 /* get copies of the buffers */
302 /* if necessary, get rest of field */
303 while (state == FLDPLUS) {
304 state = m_getfld(state, name, buf,
306 vp = add(buf, vp); /* add to previous value */
309 /* Now add the header data to the list */
310 add_header(ct, np, vp);
312 /* continue, if this isn't the last header field */
313 if (state != FLDEOF) {
314 ct->c_begin = ftell(in) + 1;
321 ct->c_begin = ftell(in) - strlen(buf);
325 ct->c_begin = ftell(in);
330 adios(NULL, "message format error in component #%d",
334 adios(NULL, "getfld() returned %d", state);
337 /* break out of the loop */
342 ** Read the content headers. We will parse the
343 ** MIME related header fields into their various
344 ** structures and set internal flags related to
345 ** content type/subtype, etc.
348 hp = ct->c_first_hf; /* start at first header field */
350 /* Get MIME-Version field */
351 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
354 unsigned char *cp, *dp;
357 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
360 ct->c_vrsn = getcpy(hp->value);
362 /* Now, cleanup this field */
367 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
369 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
374 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
376 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
379 for (dp = cp; istoken(*dp); dp++)
383 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
386 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
389 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
390 /* Get Content-Type field */
391 struct str2init *s2i;
392 CI ci = &ct->c_ctinfo;
394 /* Check if we've already seen a Content-Type header */
396 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
400 /* Parse the Content-Type field */
401 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
405 ** Set the Init function and the internal
406 ** flag for this content type.
408 for (s2i = str2cts; s2i->si_key; s2i++)
409 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
411 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
413 ct->c_type = s2i->si_val;
414 ct->c_ctinitfnx = s2i->si_init;
416 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
417 /* Get Content-Transfer-Encoding field */
419 unsigned char *cp, *dp;
420 struct str2init *s2i;
423 ** Check if we've already seen the
424 ** Content-Transfer-Encoding field
427 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
431 /* get copy of this field */
432 ct->c_celine = cp = getcpy(hp->value);
436 for (dp = cp; istoken(*dp); dp++)
442 ** Find the internal flag and Init function
443 ** for this transfer encoding.
445 for (s2i = str2ces; s2i->si_key; s2i++)
446 if (!mh_strcasecmp(cp, s2i->si_key))
448 if (!s2i->si_key && !uprf(cp, "X-"))
451 ct->c_encoding = s2i->si_val;
453 /* Call the Init function for this encoding */
454 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
457 } else if (!mh_strcasecmp(hp->name, MD5_FIELD)) {
458 /* Get Content-MD5 field */
459 unsigned char *cp, *dp;
465 if (ct->c_digested) {
466 advise(NULL, "message %s has multiple %s: fields", ct->c_file, MD5_FIELD);
470 ep = cp = getcpy(hp->value);
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 */
584 ** Make sure that buf contains at least one appearance of name,
585 ** followed by =. If not, insert both name and value, just after
586 ** first semicolon, if any. Note that name should not contain a
587 ** trailing =. And quotes will be added around the value. Typical
588 ** usage: make sure that a Content-Disposition header contains
589 ** filename="foo". If it doesn't and value does, use value from
593 incl_name_value(unsigned char *buf, char *name, char *value) {
596 /* Assume that name is non-null. */
598 char *name_plus_equal = concat(name, "=", NULL);
600 if (!strstr(buf, name_plus_equal)) {
603 char *prefix, *suffix;
605 /* Trim trailing space, esp. newline. */
606 for (cp = &buf[strlen(buf) - 1];
607 cp >= buf && isspace(*cp); --cp) {
611 insertion = concat("; ", name, "=", "\"", value, "\"",
615 ** Insert at first semicolon, if any.
616 ** If none, append to end.
618 prefix = getcpy(buf);
619 if ((cp = strchr(prefix, ';'))) {
620 suffix = concat(cp, NULL);
622 newbuf = concat(prefix, insertion, suffix,
627 newbuf = concat(buf, insertion, "\n", NULL);
635 free(name_plus_equal);
642 ** Extract just name_suffix="foo", if any, from value. If there isn't
643 ** one, return the entire value. Note that, for example, a name_suffix
644 ** of name will match filename="foo", and return foo.
647 extract_name_value(char *name_suffix, char *value) {
648 char *extracted_name_value = value;
649 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
650 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
653 free(name_suffix_plus_quote);
654 if (name_suffix_equals) {
655 char *name_suffix_begin;
658 for (cp = name_suffix_equals; *cp != '"'; ++cp)
660 name_suffix_begin = ++cp;
661 /* Find second \". */
662 for (; *cp != '"'; ++cp)
665 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
666 memcpy(extracted_name_value, name_suffix_begin,
667 cp - name_suffix_begin);
668 extracted_name_value[cp - name_suffix_begin] = '\0';
671 return extracted_name_value;
675 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
676 ** directives. Fills in the information of the CTinfo structure.
679 get_ctinfo(unsigned char *cp, CT ct, int magic)
688 i = strlen(invo_name) + 2;
690 /* store copy of Content-Type line */
691 cp = ct->c_ctline = getcpy(cp);
693 while (isspace(*cp)) /* trim leading spaces */
696 /* change newlines to spaces */
697 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
700 /* trim trailing spaces */
701 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
707 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
709 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
712 for (dp = cp; istoken(*dp); dp++)
715 ci->ci_type = getcpy(cp); /* store content type */
719 advise(NULL, "invalid %s: field in message %s (empty type)",
720 TYPE_FIELD, ct->c_file);
724 /* down case the content type string */
725 for (dp = ci->ci_type; *dp; dp++)
726 if (isalpha(*dp) && isupper(*dp))
732 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
737 ci->ci_subtype = getcpy("");
745 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
748 for (dp = cp; istoken(*dp); dp++)
751 ci->ci_subtype = getcpy(cp); /* store the content subtype */
754 if (!*ci->ci_subtype) {
755 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
759 /* down case the content subtype string */
760 for (dp = ci->ci_subtype; *dp; dp++)
761 if (isalpha(*dp) && isupper(*dp))
768 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
772 ** Parse attribute/value pairs given with Content-Type
774 ep = (ap = ci->ci_attrs) + NPARMS;
780 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
788 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
792 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
796 /* down case the attribute name */
797 for (dp = cp; istoken(*dp); dp++)
798 if (isalpha(*dp) && isupper(*dp))
801 for (up = dp; isspace(*dp);)
803 if (dp == cp || *dp != '=') {
804 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)", ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
808 vp = (*ap = getcpy(cp)) + (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;;) {
821 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
826 if ((c = *cp++) == '\0')
841 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
846 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
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);
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",
901 ct->c_descr = concat(ct->c_descr, "\n", NULL);
912 ** Get any {Content-Disposition} given in buffer.
914 if (magic && *cp == '{') {
916 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
920 advise(NULL, "invalid disposition in message %s",
929 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
940 ** Check if anything is left over
944 ci->ci_magic = getcpy(cp);
947 ** If there is a Content-Disposition header and
948 ** it doesn't have a *filename=, extract it from
949 ** the magic contents. The mhbasename call skips
950 ** any leading directory components.
953 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
955 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
963 get_comment(CT ct, unsigned char **ap, int istype)
968 char c, buffer[BUFSIZ], *dp;
980 advise(NULL, "invalid comment in message %s's %s: field",
981 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
986 if ((c = *cp++) == '\0')
1009 if ((dp = ci->ci_comment)) {
1010 ci->ci_comment = concat(dp, " ", buffer, NULL);
1013 ci->ci_comment = getcpy(buffer);
1017 while (isspace(*cp))
1028 ** Handles content types audio, image, and video.
1029 ** There's not much to do right here.
1035 return OK; /* not much to do here */
1046 char buffer[BUFSIZ];
1048 char **ap, **ep, *cp;
1051 CI ci = &ct->c_ctinfo;
1053 /* check for missing subtype */
1054 if (!*ci->ci_subtype)
1055 ci->ci_subtype = add("plain", ci->ci_subtype);
1058 for (kv = SubText; kv->kv_key; kv++)
1059 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1061 ct->c_subtype = kv->kv_value;
1063 /* allocate text character set structure */
1064 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1065 adios(NULL, "out of memory");
1066 ct->c_ctparams = (void *) t;
1068 /* scan for charset parameter */
1069 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1070 if (!mh_strcasecmp(*ap, "charset"))
1073 /* check if content specified a character set */
1075 /* match character set or set to CHARSET_UNKNOWN */
1076 for (kv = Charset; kv->kv_key; kv++) {
1077 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1082 t->tx_charset = kv->kv_value;
1084 t->tx_charset = CHARSET_UNSPECIFIED;
1088 ** If we can not handle character set natively,
1089 ** then check profile for string to modify the
1090 ** terminal or display method.
1092 ** termproc is for mhshow, though mhlist -debug prints it, too.
1094 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1095 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1097 if ((cp = context_find(buffer)))
1098 ct->c_termproc = getcpy(cp);
1110 InitMultiPart(CT ct)
1114 unsigned char *cp, *dp;
1116 char *bp, buffer[BUFSIZ];
1117 struct multipart *m;
1119 struct part *part, **next;
1120 CI ci = &ct->c_ctinfo;
1125 ** The encoding for multipart messages must be either
1126 ** 7bit, 8bit, or binary (per RFC2045).
1128 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1129 && ct->c_encoding != CE_BINARY) {
1130 admonish(NULL, "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary", ci->ci_type, ci->ci_subtype, ct->c_file);
1135 for (kv = SubMultiPart; kv->kv_key; kv++)
1136 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1138 ct->c_subtype = kv->kv_value;
1141 ** Check for "boundary" parameter, which is
1142 ** required for multipart messages.
1145 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1146 if (!mh_strcasecmp(*ap, "boundary")) {
1152 /* complain if boundary parameter is missing */
1154 advise (NULL, "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field", ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1158 /* allocate primary structure for multipart info */
1159 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1160 adios(NULL, "out of memory");
1161 ct->c_ctparams = (void *) m;
1163 /* check if boundary parameter contains only whitespace characters */
1164 for (cp = bp; isspace(*cp); cp++)
1167 advise(NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field", ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1171 /* remove trailing whitespace from boundary parameter */
1172 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1177 /* record boundary separators */
1178 m->mp_start = concat(bp, "\n", NULL);
1179 m->mp_stop = concat(bp, "--\n", NULL);
1181 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1182 advise(ct->c_file, "unable to open for reading");
1186 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1188 next = &m->mp_parts;
1192 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1196 pos += strlen(buffer);
1197 if (buffer[0] != '-' || buffer[1] != '-')
1200 if (strcmp(buffer + 2, m->mp_start))
1203 if ((part = (struct part *) calloc(1, sizeof(*part)))
1205 adios(NULL, "out of memory");
1207 next = &part->mp_next;
1209 if (!(p = get_content(fp, ct->c_file,
1210 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1217 fseek(fp, pos, SEEK_SET);
1220 if (strcmp(buffer + 2, m->mp_start) == 0) {
1224 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1225 if (p->c_end < p->c_begin)
1226 p->c_begin = p->c_end;
1231 if (strcmp(buffer + 2, m->mp_stop) == 0)
1237 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1238 if (!inout && part) {
1240 p->c_end = ct->c_end;
1242 if (p->c_begin >= p->c_end) {
1243 for (next = &m->mp_parts; *next != part;
1244 next = &((*next)->mp_next))
1248 free((char *) part);
1253 /* reverse the order of the parts for multipart/alternative */
1254 if (ct->c_subtype == MULTI_ALTERNATE)
1258 ** label all subparts with part number, and
1259 ** then initialize the content of the subpart.
1264 char partnam[BUFSIZ];
1267 snprintf(partnam, sizeof(partnam), "%s.",
1269 pp = partnam + strlen(partnam);
1274 for (part = m->mp_parts, partnum = 1; part;
1275 part = part->mp_next, partnum++) {
1278 sprintf(pp, "%d", partnum);
1279 p->c_partno = getcpy(partnam);
1281 /* initialize the content of the subparts */
1282 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1297 ** reverse the order of the parts of a multipart
1301 reverse_parts(CT ct)
1304 struct multipart *m;
1305 struct part **base, **bmp, **next, *part;
1307 m = (struct multipart *) ct->c_ctparams;
1309 /* if only one part, just return */
1310 if (!m->mp_parts || !m->mp_parts->mp_next)
1313 /* count number of parts */
1315 for (part = m->mp_parts; part; part = part->mp_next)
1318 /* allocate array of pointers to the parts */
1319 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1320 adios(NULL, "out of memory");
1323 /* point at all the parts */
1324 for (part = m->mp_parts; part; part = part->mp_next)
1328 /* reverse the order of the parts */
1329 next = &m->mp_parts;
1330 for (bmp--; bmp >= base; bmp--) {
1333 next = &part->mp_next;
1337 /* free array of pointers */
1338 free((char *) base);
1350 CI ci = &ct->c_ctinfo;
1352 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1353 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1357 /* check for missing subtype */
1358 if (!*ci->ci_subtype)
1359 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1362 for (kv = SubMessage; kv->kv_key; kv++)
1363 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1365 ct->c_subtype = kv->kv_value;
1367 switch (ct->c_subtype) {
1368 case MESSAGE_RFC822:
1371 case MESSAGE_PARTIAL:
1376 if ((p = (struct partial *)
1377 calloc(1, sizeof(*p))) == NULL)
1378 adios(NULL, "out of memory");
1379 ct->c_ctparams = (void *) p;
1382 ** scan for parameters "id", "number",
1385 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1386 if (!mh_strcasecmp(*ap, "id")) {
1387 p->pm_partid = getcpy(*ep);
1390 if (!mh_strcasecmp(*ap, "number")) {
1391 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1393 advise(NULL, "invalid %s parameter for \"%s/%s\" type in message %s's %s field", *ap, ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1398 if (!mh_strcasecmp(*ap, "total")) {
1399 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 || p->pm_maxno < 1)
1405 if (!p->pm_partid || !p->pm_partno
1406 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1407 advise(NULL, "invalid parameters for \"%s/%s\" type in message %s's %s field", ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1413 case MESSAGE_EXTERNAL:
1420 if ((e = (struct exbody *)
1421 calloc(1, sizeof(*e))) == NULL)
1422 adios(NULL, "out of memory");
1423 ct->c_ctparams = (void *) e;
1425 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1426 advise(ct->c_file, "unable to open for reading");
1430 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1432 if (!(p = get_content(fp, ct->c_file, 0))) {
1440 if ((exresult = params_external(ct, 0))
1442 p->c_ceopenfnx == openMail) {
1446 if ((size = ct->c_end - p->c_begin) <= 0) {
1448 content_error(NULL, ct, "empty body for access-type=mail-server");
1452 e->eb_body = bp = mh_xmalloc((unsigned) size);
1453 fseek(p->c_fp, p->c_begin, SEEK_SET);
1455 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1457 adios("failed", "fread");
1460 adios(NULL, "unexpected EOF from fread");
1463 bp += cc, size -= cc;
1470 p->c_end = p->c_begin;
1475 if (exresult == NOTOK)
1477 if (e->eb_flags == NOTOK)
1480 switch (p->c_type) {
1485 if (p->c_subtype != MESSAGE_RFC822)
1489 e->eb_partno = ct->c_partno;
1491 (*p->c_ctinitfnx) (p);
1506 params_external(CT ct, int composing)
1509 struct exbody *e = (struct exbody *) ct->c_ctparams;
1510 CI ci = &ct->c_ctinfo;
1512 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1513 if (!mh_strcasecmp(*ap, "access-type")) {
1514 struct str2init *s2i;
1515 CT p = e->eb_content;
1517 for (s2i = str2methods; s2i->si_key; s2i++)
1518 if (!mh_strcasecmp(*ep, s2i->si_key))
1522 e->eb_flags = NOTOK;
1523 p->c_encoding = CE_EXTERNAL;
1526 e->eb_access = s2i->si_key;
1527 e->eb_flags = s2i->si_val;
1528 p->c_encoding = CE_EXTERNAL;
1530 /* Call the Init function for this external type */
1531 if ((*s2i->si_init)(p) == NOTOK)
1535 if (!mh_strcasecmp(*ap, "name")) {
1539 if (!mh_strcasecmp(*ap, "permission")) {
1540 e->eb_permission = *ep;
1543 if (!mh_strcasecmp(*ap, "site")) {
1547 if (!mh_strcasecmp(*ap, "directory")) {
1551 if (!mh_strcasecmp(*ap, "mode")) {
1555 if (!mh_strcasecmp(*ap, "size")) {
1556 sscanf(*ep, "%lu", &e->eb_size);
1559 if (!mh_strcasecmp(*ap, "server")) {
1563 if (!mh_strcasecmp(*ap, "subject")) {
1564 e->eb_subject = *ep;
1567 if (composing && !mh_strcasecmp(*ap, "body")) {
1568 e->eb_body = getcpy(*ep);
1573 if (!e->eb_access) {
1574 advise(NULL, "invalid parameters for \"%s/%s\" type in message %s's %s field", ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1587 InitApplication(CT ct)
1590 CI ci = &ct->c_ctinfo;
1593 for (kv = SubApplication; kv->kv_key; kv++)
1594 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1596 ct->c_subtype = kv->kv_value;
1603 ** TRANSFER ENCODINGS
1607 init_encoding(CT ct, OpenCEFunc openfnx)
1611 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1612 adios(NULL, "out of memory");
1615 ct->c_ceopenfnx = openfnx;
1616 ct->c_ceclosefnx = close_encoding;
1617 ct->c_cesizefnx = size_encoding;
1624 close_encoding(CT ct)
1628 if (!(ce = ct->c_cefile))
1638 static unsigned long
1639 size_encoding(CT ct)
1647 if (!(ce = ct->c_cefile))
1648 return (ct->c_end - ct->c_begin);
1650 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1651 return (long) st.st_size;
1654 if (stat(ce->ce_file, &st) != NOTOK)
1655 return (long) st.st_size;
1660 if (ct->c_encoding == CE_EXTERNAL)
1661 return (ct->c_end - ct->c_begin);
1664 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1665 return (ct->c_end - ct->c_begin);
1667 if (fstat(fd, &st) != NOTOK)
1668 size = (long) st.st_size;
1672 (*ct->c_ceclosefnx) (ct);
1681 static unsigned char b642nib[0x80] = {
1682 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1683 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1684 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1685 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1686 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1687 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1688 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1689 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1690 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1691 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1692 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1693 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1694 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1695 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1696 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1697 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1704 return init_encoding(ct, openBase64);
1709 openBase64(CT ct, char **file)
1711 int bitno, cc, digested;
1714 unsigned char value, *b, *b1, *b2, *b3;
1715 unsigned char *cp, *ep;
1716 char buffer[BUFSIZ];
1717 /* sbeck -- handle suffixes */
1722 b = (unsigned char *) &bits;
1723 b1 = &b[endian > 0 ? 1 : 2];
1724 b2 = &b[endian > 0 ? 2 : 1];
1725 b3 = &b[endian > 0 ? 3 : 0];
1729 fseek(ce->ce_fp, 0L, SEEK_SET);
1734 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1735 content_error(ce->ce_file, ct,
1736 "unable to fopen for reading");
1742 if (*file == NULL) {
1743 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1746 ce->ce_file = getcpy(*file);
1750 /* sbeck@cise.ufl.edu -- handle suffixes */
1752 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1753 invo_name, ci->ci_type, ci->ci_subtype);
1754 cp = context_find(buffer);
1755 if (cp == NULL || *cp == '\0') {
1756 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1758 cp = context_find(buffer);
1760 if (cp != NULL && *cp != '\0') {
1761 if (ce->ce_unlink) {
1763 ** Temporary file already exists, so we rename to
1764 ** version with extension.
1766 char *file_org = strdup(ce->ce_file);
1767 ce->ce_file = add(cp, ce->ce_file);
1768 if (rename(file_org, ce->ce_file)) {
1769 adios(ce->ce_file, "unable to rename %s to ",
1775 ce->ce_file = add(cp, ce->ce_file);
1779 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1780 content_error(ce->ce_file, ct,
1781 "unable to fopen for reading/writing");
1785 if ((len = ct->c_end - ct->c_begin) < 0)
1786 adios(NULL, "internal error(1)");
1788 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1789 content_error(ct->c_file, ct, "unable to open for reading");
1793 if ((digested = ct->c_digested))
1794 MD5Init(&mdContext);
1800 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1802 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1804 content_error(ct->c_file, ct, "error reading from");
1808 content_error(NULL, ct, "premature eof");
1816 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1821 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1823 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1825 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1829 bits |= value << bitno;
1831 if ((bitno -= 6) < 0) {
1832 putc((char) *b1, ce->ce_fp);
1834 MD5Update(&mdContext, b1, 1);
1836 putc((char) *b2, ce->ce_fp);
1838 MD5Update(&mdContext, b2, 1);
1840 putc((char) *b3, ce->ce_fp);
1842 MD5Update(&mdContext, b3, 1);
1846 if (ferror(ce->ce_fp)) {
1847 content_error(ce->ce_file, ct,
1848 "error writing to");
1851 bitno = 18, bits = 0L, skip = 0;
1857 goto self_delimiting;
1866 fprintf(stderr, "premature ending (bitno %d)\n",
1869 content_error(NULL, ct, "invalid BASE64 encoding");
1874 fseek(ct->c_fp, 0L, SEEK_SET);
1876 if (fflush(ce->ce_fp)) {
1877 content_error(ce->ce_file, ct, "error writing to");
1882 unsigned char digest[16];
1884 MD5Final(digest, &mdContext);
1885 if (memcmp((char *) digest, (char *) ct->c_digest,
1886 sizeof(digest) / sizeof(digest[0])))
1887 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1889 fprintf(stderr, "content integrity confirmed\n");
1892 fseek(ce->ce_fp, 0L, SEEK_SET);
1895 *file = ce->ce_file;
1896 return fileno(ce->ce_fp);
1899 free_encoding(ct, 0);
1908 static char hex2nib[0x80] = {
1909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1911 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1912 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1913 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1915 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1916 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1917 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1921 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1931 return init_encoding(ct, openQuoted);
1936 openQuoted(CT ct, char **file)
1938 int cc, digested, len, quoted;
1939 unsigned char *cp, *ep;
1940 char buffer[BUFSIZ];
1943 /* sbeck -- handle suffixes */
1949 fseek(ce->ce_fp, 0L, SEEK_SET);
1954 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1955 content_error(ce->ce_file, ct,
1956 "unable to fopen for reading");
1962 if (*file == NULL) {
1963 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1966 ce->ce_file = getcpy(*file);
1970 /* sbeck@cise.ufl.edu -- handle suffixes */
1972 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1973 invo_name, ci->ci_type, ci->ci_subtype);
1974 cp = context_find(buffer);
1975 if (cp == NULL || *cp == '\0') {
1976 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1978 cp = context_find(buffer);
1980 if (cp != NULL && *cp != '\0') {
1981 if (ce->ce_unlink) {
1982 // Temporary file already exists, so we rename to
1983 // version with extension.
1984 char *file_org = strdup(ce->ce_file);
1985 ce->ce_file = add(cp, ce->ce_file);
1986 if (rename(file_org, ce->ce_file)) {
1987 adios(ce->ce_file, "unable to rename %s to ",
1993 ce->ce_file = add(cp, ce->ce_file);
1997 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1998 content_error(ce->ce_file, ct,
1999 "unable to fopen for reading/writing");
2003 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2004 content_error(ce->ce_file, ct,
2005 "unable to fopen for reading/writing");
2009 if ((len = ct->c_end - ct->c_begin) < 0)
2010 adios(NULL, "internal error(2)");
2012 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2013 content_error(ct->c_file, ct, "unable to open for reading");
2017 if ((digested = ct->c_digested))
2018 MD5Init(&mdContext);
2025 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2027 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2028 content_error(NULL, ct, "premature eof");
2032 if ((cc = strlen(buffer)) > len)
2036 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2041 for (; cp < ep; cp++) {
2043 /* in an escape sequence */
2045 /* at byte 1 of an escape sequence */
2046 mask = hex2nib[*cp & 0x7f];
2047 /* next is byte 2 */
2050 /* at byte 2 of an escape sequence */
2052 mask |= hex2nib[*cp & 0x7f];
2053 putc(mask, ce->ce_fp);
2055 MD5Update(&mdContext, &mask, 1);
2056 if (ferror(ce->ce_fp)) {
2057 content_error(ce->ce_file, ct, "error writing to");
2061 ** finished escape sequence; next may
2062 ** be literal or a new escape sequence
2066 /* on to next byte */
2070 /* not in an escape sequence */
2073 ** starting an escape sequence,
2076 if (cp + 1 < ep && cp[1] == '\n') {
2077 /* "=\n" soft line break, eat the \n */
2081 if (cp + 1 >= ep || cp + 2 >= ep) {
2083 ** We don't have 2 bytes left,
2084 ** so this is an invalid escape
2085 ** sequence; just show the raw bytes
2088 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2090 ** Next 2 bytes are hex digits,
2091 ** making this a valid escape
2092 ** sequence; let's decode it (above).
2098 ** One or both of the next 2 is
2099 ** out of range, making this an
2100 ** invalid escape sequence; just
2101 ** show the raw bytes (below).
2106 /* Just show the raw byte. */
2107 putc(*cp, ce->ce_fp);
2110 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2112 MD5Update(&mdContext, (unsigned char *) cp, 1);
2115 if (ferror(ce->ce_fp)) {
2116 content_error(ce->ce_file, ct,
2117 "error writing to");
2123 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2127 fseek(ct->c_fp, 0L, SEEK_SET);
2129 if (fflush(ce->ce_fp)) {
2130 content_error(ce->ce_file, ct, "error writing to");
2135 unsigned char digest[16];
2137 MD5Final(digest, &mdContext);
2138 if (memcmp((char *) digest, (char *) ct->c_digest,
2139 sizeof(digest) / sizeof(digest[0])))
2140 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2142 fprintf(stderr, "content integrity confirmed\n");
2145 fseek(ce->ce_fp, 0L, SEEK_SET);
2148 *file = ce->ce_file;
2149 return fileno(ce->ce_fp);
2152 free_encoding(ct, 0);
2164 if (init_encoding(ct, open7Bit) == NOTOK)
2167 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2173 open7Bit(CT ct, char **file)
2176 char buffer[BUFSIZ];
2177 /* sbeck -- handle suffixes */
2184 fseek(ce->ce_fp, 0L, SEEK_SET);
2189 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2190 content_error(ce->ce_file, ct,
2191 "unable to fopen for reading");
2197 if (*file == NULL) {
2198 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2201 ce->ce_file = getcpy(*file);
2205 /* sbeck@cise.ufl.edu -- handle suffixes */
2207 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2208 invo_name, ci->ci_type, ci->ci_subtype);
2209 cp = context_find(buffer);
2210 if (cp == NULL || *cp == '\0') {
2211 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2213 cp = context_find(buffer);
2215 if (cp != NULL && *cp != '\0') {
2216 if (ce->ce_unlink) {
2218 ** Temporary file already exists, so we rename to
2219 ** version with extension.
2221 char *file_org = strdup(ce->ce_file);
2222 ce->ce_file = add(cp, ce->ce_file);
2223 if (rename(file_org, ce->ce_file)) {
2224 adios(ce->ce_file, "unable to rename %s to ",
2230 ce->ce_file = add(cp, ce->ce_file);
2234 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2235 content_error(ce->ce_file, ct,
2236 "unable to fopen for reading/writing");
2240 if (ct->c_type == CT_MULTIPART) {
2242 CI ci = &ct->c_ctinfo;
2245 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2247 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2248 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\"",
2256 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2257 fputs("\n\t", ce->ce_fp);
2260 putc(' ', ce->ce_fp);
2263 fprintf(ce->ce_fp, "%s", buffer);
2267 if (ci->ci_comment) {
2268 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2270 fputs("\n\t", ce->ce_fp);
2273 putc(' ', ce->ce_fp);
2276 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2279 fprintf(ce->ce_fp, "\n");
2281 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2283 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2285 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2286 fprintf(ce->ce_fp, "\n");
2289 if ((len = ct->c_end - ct->c_begin) < 0)
2290 adios(NULL, "internal error(3)");
2292 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2293 content_error(ct->c_file, ct, "unable to open for reading");
2297 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2299 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2301 content_error(ct->c_file, ct, "error reading from");
2305 content_error(NULL, ct, "premature eof");
2313 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2314 if (ferror(ce->ce_fp)) {
2315 content_error(ce->ce_file, ct,
2316 "error writing to");
2321 fseek(ct->c_fp, 0L, SEEK_SET);
2323 if (fflush(ce->ce_fp)) {
2324 content_error(ce->ce_file, ct, "error writing to");
2328 fseek(ce->ce_fp, 0L, SEEK_SET);
2331 *file = ce->ce_file;
2332 return fileno(ce->ce_fp);
2335 free_encoding(ct, 0);
2345 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2347 char cachefile[BUFSIZ];
2350 fseek(ce->ce_fp, 0L, SEEK_SET);
2355 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2356 content_error(ce->ce_file, ct,
2357 "unable to fopen for reading");
2363 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2364 cachefile, sizeof(cachefile)) != NOTOK) {
2365 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2366 ce->ce_file = getcpy(cachefile);
2370 admonish(cachefile, "unable to fopen for reading");
2377 *file = ce->ce_file;
2378 *fd = fileno(ce->ce_fp);
2389 return init_encoding(ct, openFile);
2394 openFile(CT ct, char **file)
2397 char cachefile[BUFSIZ];
2398 struct exbody *e = ct->c_ctexbody;
2399 CE ce = ct->c_cefile;
2401 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2413 content_error(NULL, ct, "missing name parameter");
2417 ce->ce_file = getcpy(e->eb_name);
2420 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2421 content_error(ce->ce_file, ct, "unable to fopen for reading");
2425 if ((!e->eb_permission ||
2426 mh_strcasecmp(e->eb_permission, "read-write")) &&
2427 find_cache(NULL, wcachesw, &cachetype,
2428 e->eb_content->c_id, cachefile, sizeof(cachefile))
2433 mask = umask(cachetype ? ~m_gmprot() : 0222);
2434 if ((fp = fopen(cachefile, "w"))) {
2436 char buffer[BUFSIZ];
2437 FILE *gp = ce->ce_fp;
2439 fseek(gp, 0L, SEEK_SET);
2441 while ((cc = fread(buffer, sizeof(*buffer),
2442 sizeof(buffer), gp)) > 0)
2443 fwrite(buffer, sizeof(*buffer), cc, fp);
2447 admonish(ce->ce_file, "error reading");
2449 } else if (ferror(fp)) {
2450 admonish(cachefile, "error writing");
2458 fseek(ce->ce_fp, 0L, SEEK_SET);
2459 *file = ce->ce_file;
2460 return fileno(ce->ce_fp);
2470 return init_encoding(ct, openFTP);
2475 openFTP(CT ct, char **file)
2477 int cachetype, caching, fd;
2479 char *bp, *ftp, *user, *pass;
2480 char buffer[BUFSIZ], cachefile[BUFSIZ];
2483 static char *username = NULL;
2484 static char *password = NULL;
2489 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2497 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2508 if (!e->eb_name || !e->eb_site) {
2509 content_error(NULL, ct, "missing %s parameter",
2510 e->eb_name ? "site": "name");
2517 pidcheck(pidwait(xpid, NOTOK));
2521 /* Get the buffer ready to go */
2523 buflen = sizeof(buffer);
2526 ** Construct the query message for user
2528 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2534 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2540 snprintf(bp, buflen, "\n using %sFTP from site %s",
2541 e->eb_flags ? "anonymous " : "", e->eb_site);
2546 if (e->eb_size > 0) {
2547 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2552 snprintf(bp, buflen, "? ");
2555 ** Now, check the answer
2557 if (!getanswer(buffer))
2562 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2566 ruserpass(e->eb_site, &username, &password);
2571 ce->ce_unlink = (*file == NULL);
2573 cachefile[0] = '\0';
2574 if ((!e->eb_permission ||
2575 mh_strcasecmp(e->eb_permission, "read-write")) &&
2576 find_cache(NULL, wcachesw, &cachetype,
2577 e->eb_content->c_id, cachefile, sizeof(cachefile))
2579 if (*file == NULL) {
2586 ce->ce_file = getcpy(*file);
2588 ce->ce_file = getcpy(cachefile);
2590 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2592 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2593 content_error (ce->ce_file, ct,
2594 "unable to fopen for reading/writing");
2602 int child_id, i, vecp;
2606 vec[vecp++] = mhbasename(ftp);
2607 vec[vecp++] = e->eb_site;
2610 vec[vecp++] = e->eb_dir;
2611 vec[vecp++] = e->eb_name;
2612 vec[vecp++] = ce->ce_file,
2613 vec[vecp++] = e->eb_mode &&
2614 !mh_strcasecmp(e->eb_mode, "ascii") ?
2620 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2624 adios("fork", "unable to");
2628 close(fileno(ce->ce_fp));
2630 fprintf(stderr, "unable to exec ");
2636 if (pidXwait(child_id, NULL)) {
2640 username = password = NULL;
2648 else if (ftp_get(e->eb_site, user, pass, e->eb_dir, e->eb_name,
2649 ce->ce_file, e->eb_mode && !mh_strcasecmp(e->eb_mode,
2650 "ascii"), 0) == NOTOK)
2656 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2661 mask = umask(cachetype ? ~m_gmprot() : 0222);
2662 if ((fp = fopen(cachefile, "w"))) {
2664 FILE *gp = ce->ce_fp;
2666 fseek(gp, 0L, SEEK_SET);
2668 while ((cc= fread(buffer, sizeof(*buffer),
2669 sizeof(buffer), gp)) > 0)
2670 fwrite(buffer, sizeof(*buffer), cc, fp);
2674 admonish(ce->ce_file, "error reading");
2676 } else if (ferror(fp)) {
2677 admonish(cachefile, "error writing");
2686 fseek(ce->ce_fp, 0L, SEEK_SET);
2687 *file = ce->ce_file;
2688 return fileno(ce->ce_fp);
2699 return init_encoding(ct, openMail);
2704 openMail(CT ct, char **file)
2706 int child_id, fd, i, vecp;
2708 char *bp, buffer[BUFSIZ], *vec[7];
2709 struct exbody *e = ct->c_ctexbody;
2710 CE ce = ct->c_cefile;
2712 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2723 if (!e->eb_server) {
2724 content_error(NULL, ct, "missing server parameter");
2731 pidcheck(pidwait(xpid, NOTOK));
2735 /* Get buffer ready to go */
2737 buflen = sizeof(buffer);
2739 /* Now, construct query message */
2740 snprintf(bp, buflen, "Retrieve content");
2746 snprintf(bp, buflen, " %s", e->eb_partno);
2752 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2753 e->eb_subject ? e->eb_subject : e->eb_body);
2755 /* Now, check answer */
2756 if (!getanswer(buffer))
2760 vec[vecp++] = mhbasename(mailproc);
2761 vec[vecp++] = e->eb_server;
2762 vec[vecp++] = "-subject";
2763 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2764 vec[vecp++] = "-body";
2765 vec[vecp++] = e->eb_body;
2768 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
2772 advise("fork", "unable to");
2776 execvp(mailproc, vec);
2777 fprintf(stderr, "unable to exec ");
2783 if (pidXwait(child_id, NULL) == OK)
2784 advise(NULL, "request sent");
2788 if (*file == NULL) {
2789 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2792 ce->ce_file = getcpy(*file);
2796 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2797 content_error(ce->ce_file, ct,
2798 "unable to fopen for reading/writing");
2803 ** showproc is for mhshow and mhstore, though mhlist -debug
2807 free(ct->c_showproc);
2808 ct->c_showproc = getcpy("true");
2810 fseek(ce->ce_fp, 0L, SEEK_SET);
2811 *file = ce->ce_file;
2812 return fileno(ce->ce_fp);
2817 readDigest(CT ct, char *cp)
2822 unsigned char *dp, value, *ep;
2823 unsigned char *b, *b1, *b2, *b3;
2825 b = (unsigned char *) &bits,
2826 b1 = &b[endian > 0 ? 1 : 2],
2827 b2 = &b[endian > 0 ? 2 : 1],
2828 b3 = &b[endian > 0 ? 3 : 0];
2833 for (ep = (dp = ct->c_digest)
2834 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2837 if (skip || (*cp & 0x80) ||
2838 (value = b642nib[*cp & 0x7f])
2841 fprintf(stderr, "invalid BASE64 encoding\n");
2845 bits |= value << bitno;
2847 if ((bitno -= 6) < 0) {
2848 if (dp + (3 - skip) > ep)
2849 goto invalid_digest;
2864 goto self_delimiting;
2869 fprintf(stderr, "premature ending (bitno %d)\n",
2880 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2888 fprintf(stderr, "MD5 digest=");
2889 for (dp = ct->c_digest; dp < ep; dp++)
2890 fprintf(stderr, "%02x", *dp & 0xff);
2891 fprintf(stderr, "\n");