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 find_cache(CT, int, int *, char *, char *, int);
98 int make_intermediates(char *);
99 void content_error(char *, CT, char *, ...);
102 void free_content(CT);
103 void free_encoding(CT, int);
108 static CT get_content(FILE *, char *, int);
109 static int get_comment(CT, unsigned char **, int);
111 static int InitGeneric(CT);
112 static int InitText(CT);
113 static int InitMultiPart(CT);
114 static void reverse_parts(CT);
115 static int InitMessage(CT);
116 static int InitApplication(CT);
117 static int init_encoding(CT, OpenCEFunc);
118 static unsigned long size_encoding(CT);
119 static int InitBase64(CT);
120 static int openBase64(CT, char **);
121 static int InitQuoted(CT);
122 static int openQuoted(CT, char **);
123 static int Init7Bit(CT);
124 static int openExternal(CT, CT, CE, char **, int *);
125 static int InitFile(CT);
126 static int openFile(CT, char **);
127 static int InitFTP(CT);
128 static int openFTP(CT, char **);
129 static int InitMail(CT);
130 static int openMail(CT, char **);
131 static int readDigest(CT, char *);
133 struct str2init str2cts[] = {
134 { "application", CT_APPLICATION, InitApplication },
135 { "audio", CT_AUDIO, InitGeneric },
136 { "image", CT_IMAGE, InitGeneric },
137 { "message", CT_MESSAGE, InitMessage },
138 { "multipart", CT_MULTIPART, InitMultiPart },
139 { "text", CT_TEXT, InitText },
140 { "video", CT_VIDEO, InitGeneric },
141 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
142 { NULL, CT_UNKNOWN, NULL },
145 struct str2init str2ces[] = {
146 { "base64", CE_BASE64, InitBase64 },
147 { "quoted-printable", CE_QUOTED, InitQuoted },
148 { "8bit", CE_8BIT, Init7Bit },
149 { "7bit", CE_7BIT, Init7Bit },
150 { "binary", CE_BINARY, Init7Bit },
151 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
152 { NULL, CE_UNKNOWN, NULL },
156 ** NOTE WELL: si_key MUST NOT have value of NOTOK
158 ** si_key is 1 if access method is anonymous.
160 struct str2init str2methods[] = {
161 { "afs", 1, InitFile },
162 { "anon-ftp", 1, InitFTP },
163 { "ftp", 0, InitFTP },
164 { "local-file", 0, InitFile },
165 { "mail-server", 0, InitMail },
173 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
184 ** Main entry point for parsing a MIME message or file.
185 ** It returns the Content structure for the top level
186 ** entity in the file.
189 parse_mime(char *file)
197 ** Check if file is actually standard input
199 if ((is_stdin = (strcmp(file, "-")==0))) {
200 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
202 advise("mhparse", "unable to create temporary file");
205 file = getcpy(tfile);
208 while (fgets(buffer, sizeof(buffer), stdin))
214 advise("stdin", "error reading");
219 advise(file, "error writing");
222 fseek(fp, 0L, SEEK_SET);
223 } else if ((fp = fopen(file, "r")) == NULL) {
224 advise(file, "unable to read");
228 if (!(ct = get_content(fp, file, 1))) {
231 advise(NULL, "unable to decode %s", file);
236 ct->c_unlink = 1; /* temp file to remove */
240 if (ct->c_end == 0L) {
241 fseek(fp, 0L, SEEK_END);
242 ct->c_end = ftell(fp);
245 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
257 ** Main routine for reading/parsing the headers
258 ** of a message content.
260 ** toplevel = 1 # we are at the top level of the message
261 ** toplevel = 0 # we are inside message type or multipart type
262 ** # other than multipart/digest
263 ** toplevel = -1 # we are inside multipart/digest
264 ** NB: on failure we will fclose(in)!
268 get_content(FILE *in, char *file, int toplevel)
271 char buf[BUFSIZ], name[NAMESZ];
276 /* allocate the content structure */
277 if (!(ct = (CT) calloc(1, sizeof(*ct))))
278 adios(NULL, "out of memory");
281 ct->c_file = getcpy(file);
282 ct->c_begin = ftell(ct->c_fp) + 1;
285 ** Parse the header fields for this
286 ** content into a linked list.
288 for (compnum = 1, state = FLD;;) {
289 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
295 /* get copies of the buffers */
299 /* if necessary, get rest of field */
300 while (state == FLDPLUS) {
301 state = m_getfld(state, name, buf,
303 vp = add(buf, vp); /* add to previous value */
306 /* Now add the header data to the list */
307 add_header(ct, np, vp);
309 /* continue, if this isn't the last header field */
310 if (state != FLDEOF) {
311 ct->c_begin = ftell(in) + 1;
318 ct->c_begin = ftell(in) - strlen(buf);
322 ct->c_begin = ftell(in);
327 adios(NULL, "message format error in component #%d",
331 adios(NULL, "getfld() returned %d", state);
334 /* break out of the loop */
339 ** Read the content headers. We will parse the
340 ** MIME related header fields into their various
341 ** structures and set internal flags related to
342 ** content type/subtype, etc.
345 hp = ct->c_first_hf; /* start at first header field */
347 /* Get MIME-Version field */
348 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
351 unsigned char *cp, *dp;
354 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
357 ct->c_vrsn = getcpy(hp->value);
359 /* Now, cleanup this field */
364 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
366 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
371 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
373 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
376 for (dp = cp; istoken(*dp); dp++)
380 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
383 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
386 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
387 /* Get Content-Type field */
388 struct str2init *s2i;
389 CI ci = &ct->c_ctinfo;
391 /* Check if we've already seen a Content-Type header */
393 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
397 /* Parse the Content-Type field */
398 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
402 ** Set the Init function and the internal
403 ** flag for this content type.
405 for (s2i = str2cts; s2i->si_key; s2i++)
406 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
408 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
410 ct->c_type = s2i->si_val;
411 ct->c_ctinitfnx = s2i->si_init;
413 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
414 /* Get Content-Transfer-Encoding field */
416 unsigned char *cp, *dp;
417 struct str2init *s2i;
420 ** Check if we've already seen the
421 ** Content-Transfer-Encoding field
424 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
428 /* get copy of this field */
429 ct->c_celine = cp = getcpy(hp->value);
433 for (dp = cp; istoken(*dp); dp++)
439 ** Find the internal flag and Init function
440 ** for this transfer encoding.
442 for (s2i = str2ces; s2i->si_key; s2i++)
443 if (!mh_strcasecmp(cp, s2i->si_key))
445 if (!s2i->si_key && !uprf(cp, "X-"))
448 ct->c_encoding = s2i->si_val;
450 /* Call the Init function for this encoding */
451 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
454 } else if (!mh_strcasecmp(hp->name, MD5_FIELD)) {
455 /* Get Content-MD5 field */
456 unsigned char *cp, *dp;
462 if (ct->c_digested) {
463 advise(NULL, "message %s has multiple %s: fields", ct->c_file, MD5_FIELD);
467 ep = cp = getcpy(hp->value);
471 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
473 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
478 fprintf(stderr, "%s: %s\n", MD5_FIELD, cp);
480 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK) {
485 for (dp = cp; *dp && !isspace(*dp); dp++)
493 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
494 /* Get Content-ID field */
495 ct->c_id = add(hp->value, ct->c_id);
497 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
498 /* Get Content-Description field */
499 ct->c_descr = add(hp->value, ct->c_descr);
501 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
502 /* Get Content-Disposition field */
503 ct->c_dispo = add(hp->value, ct->c_dispo);
507 hp = hp->next; /* next header field */
511 ** Check if we saw a Content-Type field.
512 ** If not, then assign a default value for
513 ** it, and the Init function.
517 ** If we are inside a multipart/digest message,
518 ** so default type is message/rfc822
521 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
523 ct->c_type = CT_MESSAGE;
524 ct->c_ctinitfnx = InitMessage;
527 ** Else default type is text/plain
529 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
531 ct->c_type = CT_TEXT;
532 ct->c_ctinitfnx = InitText;
536 /* Use default Transfer-Encoding, if necessary */
538 ct->c_encoding = CE_7BIT;
551 ** small routine to add header field to list
555 add_header(CT ct, char *name, char *value)
559 /* allocate header field structure */
560 hp = mh_xmalloc(sizeof(*hp));
562 /* link data into header structure */
567 /* link header structure into the list */
568 if (ct->c_first_hf == NULL) {
569 ct->c_first_hf = hp; /* this is the first */
572 ct->c_last_hf->next = hp; /* add it to the end */
581 ** Make sure that buf contains at least one appearance of name,
582 ** followed by =. If not, insert both name and value, just after
583 ** first semicolon, if any. Note that name should not contain a
584 ** trailing =. And quotes will be added around the value. Typical
585 ** usage: make sure that a Content-Disposition header contains
586 ** filename="foo". If it doesn't and value does, use value from
590 incl_name_value(unsigned char *buf, char *name, char *value) {
593 /* Assume that name is non-null. */
595 char *name_plus_equal = concat(name, "=", NULL);
597 if (!strstr(buf, name_plus_equal)) {
600 char *prefix, *suffix;
602 /* Trim trailing space, esp. newline. */
603 for (cp = &buf[strlen(buf) - 1];
604 cp >= buf && isspace(*cp); --cp) {
608 insertion = concat("; ", name, "=", "\"", value, "\"",
612 ** Insert at first semicolon, if any.
613 ** If none, append to end.
615 prefix = getcpy(buf);
616 if ((cp = strchr(prefix, ';'))) {
617 suffix = concat(cp, NULL);
619 newbuf = concat(prefix, insertion, suffix,
624 newbuf = concat(buf, insertion, "\n", NULL);
632 free(name_plus_equal);
639 ** Extract just name_suffix="foo", if any, from value. If there isn't
640 ** one, return the entire value. Note that, for example, a name_suffix
641 ** of name will match filename="foo", and return foo.
644 extract_name_value(char *name_suffix, char *value) {
645 char *extracted_name_value = value;
646 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
647 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
650 free(name_suffix_plus_quote);
651 if (name_suffix_equals) {
652 char *name_suffix_begin;
655 for (cp = name_suffix_equals; *cp != '"'; ++cp)
657 name_suffix_begin = ++cp;
658 /* Find second \". */
659 for (; *cp != '"'; ++cp)
662 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
663 memcpy(extracted_name_value, name_suffix_begin,
664 cp - name_suffix_begin);
665 extracted_name_value[cp - name_suffix_begin] = '\0';
668 return extracted_name_value;
672 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
673 ** directives. Fills in the information of the CTinfo structure.
676 get_ctinfo(unsigned char *cp, CT ct, int magic)
685 i = strlen(invo_name) + 2;
687 /* store copy of Content-Type line */
688 cp = ct->c_ctline = getcpy(cp);
690 while (isspace(*cp)) /* trim leading spaces */
693 /* change newlines to spaces */
694 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
697 /* trim trailing spaces */
698 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
704 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
706 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
709 for (dp = cp; istoken(*dp); dp++)
712 ci->ci_type = getcpy(cp); /* store content type */
716 advise(NULL, "invalid %s: field in message %s (empty type)",
717 TYPE_FIELD, ct->c_file);
721 /* down case the content type string */
722 for (dp = ci->ci_type; *dp; dp++)
723 if (isalpha(*dp) && isupper(*dp))
729 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
734 ci->ci_subtype = getcpy("");
742 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
745 for (dp = cp; istoken(*dp); dp++)
748 ci->ci_subtype = getcpy(cp); /* store the content subtype */
751 if (!*ci->ci_subtype) {
752 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
756 /* down case the content subtype string */
757 for (dp = ci->ci_subtype; *dp; dp++)
758 if (isalpha(*dp) && isupper(*dp))
765 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
769 ** Parse attribute/value pairs given with Content-Type
771 ep = (ap = ci->ci_attrs) + NPARMS;
777 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
785 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
789 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
793 /* down case the attribute name */
794 for (dp = cp; istoken(*dp); dp++)
795 if (isalpha(*dp) && isupper(*dp))
798 for (up = dp; isspace(*dp);)
800 if (dp == cp || *dp != '=') {
801 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);
805 vp = (*ap = getcpy(cp)) + (up - cp);
807 for (dp++; isspace(*dp);)
810 /* now add the attribute value */
811 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
814 for (cp = ++dp, dp = vp;;) {
818 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
823 if ((c = *cp++) == '\0')
838 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
843 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
851 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
856 ** Get any <Content-Id> given in buffer
858 if (magic && *cp == '<') {
863 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
864 advise(NULL, "invalid ID in message %s", ct->c_file);
870 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
881 ** Get any [Content-Description] given in buffer.
883 if (magic && *cp == '[') {
885 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
889 advise(NULL, "invalid description in message %s",
898 ct->c_descr = concat(ct->c_descr, "\n", NULL);
909 ** Get any {Content-Disposition} given in buffer.
911 if (magic && *cp == '{') {
913 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
917 advise(NULL, "invalid disposition in message %s",
926 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
937 ** Check if anything is left over
941 ci->ci_magic = getcpy(cp);
944 ** If there is a Content-Disposition header and
945 ** it doesn't have a *filename=, extract it from
946 ** the magic contents. The mhbasename call skips
947 ** any leading directory components.
950 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
952 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
960 get_comment(CT ct, unsigned char **ap, int istype)
965 char c, buffer[BUFSIZ], *dp;
977 advise(NULL, "invalid comment in message %s's %s: field",
978 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
983 if ((c = *cp++) == '\0')
1006 if ((dp = ci->ci_comment)) {
1007 ci->ci_comment = concat(dp, " ", buffer, NULL);
1010 ci->ci_comment = getcpy(buffer);
1014 while (isspace(*cp))
1025 ** Handles content types audio, image, and video.
1026 ** There's not much to do right here.
1032 return OK; /* not much to do here */
1043 char buffer[BUFSIZ];
1045 char **ap, **ep, *cp;
1048 CI ci = &ct->c_ctinfo;
1050 /* check for missing subtype */
1051 if (!*ci->ci_subtype)
1052 ci->ci_subtype = add("plain", ci->ci_subtype);
1055 for (kv = SubText; kv->kv_key; kv++)
1056 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1058 ct->c_subtype = kv->kv_value;
1060 /* allocate text character set structure */
1061 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1062 adios(NULL, "out of memory");
1063 ct->c_ctparams = (void *) t;
1065 /* scan for charset parameter */
1066 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1067 if (!mh_strcasecmp(*ap, "charset"))
1070 /* check if content specified a character set */
1072 /* match character set or set to CHARSET_UNKNOWN */
1073 for (kv = Charset; kv->kv_key; kv++) {
1074 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1079 t->tx_charset = kv->kv_value;
1081 t->tx_charset = CHARSET_UNSPECIFIED;
1085 ** If we can not handle character set natively,
1086 ** then check profile for string to modify the
1087 ** terminal or display method.
1089 ** termproc is for mhshow, though mhlist -debug prints it, too.
1091 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1092 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1094 if ((cp = context_find(buffer)))
1095 ct->c_termproc = getcpy(cp);
1107 InitMultiPart(CT ct)
1111 unsigned char *cp, *dp;
1113 char *bp, buffer[BUFSIZ];
1114 struct multipart *m;
1116 struct part *part, **next;
1117 CI ci = &ct->c_ctinfo;
1122 ** The encoding for multipart messages must be either
1123 ** 7bit, 8bit, or binary (per RFC2045).
1125 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1126 && ct->c_encoding != CE_BINARY) {
1127 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);
1132 for (kv = SubMultiPart; kv->kv_key; kv++)
1133 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1135 ct->c_subtype = kv->kv_value;
1138 ** Check for "boundary" parameter, which is
1139 ** required for multipart messages.
1142 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1143 if (!mh_strcasecmp(*ap, "boundary")) {
1149 /* complain if boundary parameter is missing */
1151 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);
1155 /* allocate primary structure for multipart info */
1156 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1157 adios(NULL, "out of memory");
1158 ct->c_ctparams = (void *) m;
1160 /* check if boundary parameter contains only whitespace characters */
1161 for (cp = bp; isspace(*cp); cp++)
1164 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);
1168 /* remove trailing whitespace from boundary parameter */
1169 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1174 /* record boundary separators */
1175 m->mp_start = concat(bp, "\n", NULL);
1176 m->mp_stop = concat(bp, "--\n", NULL);
1178 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1179 advise(ct->c_file, "unable to open for reading");
1183 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1185 next = &m->mp_parts;
1189 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1193 pos += strlen(buffer);
1194 if (buffer[0] != '-' || buffer[1] != '-')
1197 if (strcmp(buffer + 2, m->mp_start)!=0)
1200 if ((part = (struct part *) calloc(1, sizeof(*part)))
1202 adios(NULL, "out of memory");
1204 next = &part->mp_next;
1206 if (!(p = get_content(fp, ct->c_file,
1207 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1214 fseek(fp, pos, SEEK_SET);
1217 if (strcmp(buffer + 2, m->mp_start) == 0) {
1221 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1222 if (p->c_end < p->c_begin)
1223 p->c_begin = p->c_end;
1228 if (strcmp(buffer + 2, m->mp_stop) == 0)
1234 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1235 if (!inout && part) {
1237 p->c_end = ct->c_end;
1239 if (p->c_begin >= p->c_end) {
1240 for (next = &m->mp_parts; *next != part;
1241 next = &((*next)->mp_next))
1245 free((char *) part);
1250 /* reverse the order of the parts for multipart/alternative */
1251 if (ct->c_subtype == MULTI_ALTERNATE)
1255 ** label all subparts with part number, and
1256 ** then initialize the content of the subpart.
1261 char partnam[BUFSIZ];
1264 snprintf(partnam, sizeof(partnam), "%s.",
1266 pp = partnam + strlen(partnam);
1271 for (part = m->mp_parts, partnum = 1; part;
1272 part = part->mp_next, partnum++) {
1275 sprintf(pp, "%d", partnum);
1276 p->c_partno = getcpy(partnam);
1278 /* initialize the content of the subparts */
1279 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1294 ** reverse the order of the parts of a multipart
1298 reverse_parts(CT ct)
1301 struct multipart *m;
1302 struct part **base, **bmp, **next, *part;
1304 m = (struct multipart *) ct->c_ctparams;
1306 /* if only one part, just return */
1307 if (!m->mp_parts || !m->mp_parts->mp_next)
1310 /* count number of parts */
1312 for (part = m->mp_parts; part; part = part->mp_next)
1315 /* allocate array of pointers to the parts */
1316 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1317 adios(NULL, "out of memory");
1320 /* point at all the parts */
1321 for (part = m->mp_parts; part; part = part->mp_next)
1325 /* reverse the order of the parts */
1326 next = &m->mp_parts;
1327 for (bmp--; bmp >= base; bmp--) {
1330 next = &part->mp_next;
1334 /* free array of pointers */
1335 free((char *) base);
1347 CI ci = &ct->c_ctinfo;
1349 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1350 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1354 /* check for missing subtype */
1355 if (!*ci->ci_subtype)
1356 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1359 for (kv = SubMessage; kv->kv_key; kv++)
1360 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1362 ct->c_subtype = kv->kv_value;
1364 switch (ct->c_subtype) {
1365 case MESSAGE_RFC822:
1368 case MESSAGE_PARTIAL:
1373 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1374 adios(NULL, "out of memory");
1375 ct->c_ctparams = (void *) p;
1378 ** scan for parameters "id", "number",
1381 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1382 if (!mh_strcasecmp(*ap, "id")) {
1383 p->pm_partid = getcpy(*ep);
1386 if (!mh_strcasecmp(*ap, "number")) {
1387 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1389 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);
1394 if (!mh_strcasecmp(*ap, "total")) {
1395 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1402 if (!p->pm_partid || !p->pm_partno
1403 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1404 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);
1410 case MESSAGE_EXTERNAL:
1417 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1418 adios(NULL, "out of memory");
1419 ct->c_ctparams = (void *) e;
1421 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1422 advise(ct->c_file, "unable to open for reading");
1426 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1428 if (!(p = get_content(fp, ct->c_file, 0))) {
1436 if ((exresult = params_external(ct, 0)) != NOTOK &&
1437 p->c_ceopenfnx == openMail) {
1441 if ((size = ct->c_end - p->c_begin) <= 0) {
1443 content_error(NULL, ct, "empty body for access-type=mail-server");
1447 e->eb_body = bp = mh_xmalloc((unsigned) size);
1448 fseek(p->c_fp, p->c_begin, SEEK_SET);
1450 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1452 adios("failed", "fread");
1454 adios(NULL, "unexpected EOF from fread");
1456 bp += cc, size -= cc;
1463 p->c_end = p->c_begin;
1468 if (exresult == NOTOK)
1470 if (e->eb_flags == NOTOK)
1473 switch (p->c_type) {
1478 if (p->c_subtype != MESSAGE_RFC822)
1482 e->eb_partno = ct->c_partno;
1484 (*p->c_ctinitfnx) (p);
1499 params_external(CT ct, int composing)
1502 struct exbody *e = (struct exbody *) ct->c_ctparams;
1503 CI ci = &ct->c_ctinfo;
1505 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1506 if (!mh_strcasecmp(*ap, "access-type")) {
1507 struct str2init *s2i;
1508 CT p = e->eb_content;
1510 for (s2i = str2methods; s2i->si_key; s2i++)
1511 if (!mh_strcasecmp(*ep, s2i->si_key))
1515 e->eb_flags = NOTOK;
1516 p->c_encoding = CE_EXTERNAL;
1519 e->eb_access = s2i->si_key;
1520 e->eb_flags = s2i->si_val;
1521 p->c_encoding = CE_EXTERNAL;
1523 /* Call the Init function for this external type */
1524 if ((*s2i->si_init)(p) == NOTOK)
1528 if (!mh_strcasecmp(*ap, "name")) {
1532 if (!mh_strcasecmp(*ap, "permission")) {
1533 e->eb_permission = *ep;
1536 if (!mh_strcasecmp(*ap, "site")) {
1540 if (!mh_strcasecmp(*ap, "directory")) {
1544 if (!mh_strcasecmp(*ap, "mode")) {
1548 if (!mh_strcasecmp(*ap, "size")) {
1549 sscanf(*ep, "%lu", &e->eb_size);
1552 if (!mh_strcasecmp(*ap, "server")) {
1556 if (!mh_strcasecmp(*ap, "subject")) {
1557 e->eb_subject = *ep;
1560 if (composing && !mh_strcasecmp(*ap, "body")) {
1561 e->eb_body = getcpy(*ep);
1566 if (!e->eb_access) {
1567 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);
1580 InitApplication(CT ct)
1583 CI ci = &ct->c_ctinfo;
1586 for (kv = SubApplication; kv->kv_key; kv++)
1587 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1589 ct->c_subtype = kv->kv_value;
1596 ** TRANSFER ENCODINGS
1600 init_encoding(CT ct, OpenCEFunc openfnx)
1604 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1605 adios(NULL, "out of memory");
1608 ct->c_ceopenfnx = openfnx;
1609 ct->c_ceclosefnx = close_encoding;
1610 ct->c_cesizefnx = size_encoding;
1617 close_encoding(CT ct)
1621 if (!(ce = ct->c_cefile))
1631 static unsigned long
1632 size_encoding(CT ct)
1640 if (!(ce = ct->c_cefile))
1641 return (ct->c_end - ct->c_begin);
1643 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1644 return (long) st.st_size;
1647 if (stat(ce->ce_file, &st) != NOTOK)
1648 return (long) st.st_size;
1653 if (ct->c_encoding == CE_EXTERNAL)
1654 return (ct->c_end - ct->c_begin);
1657 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1658 return (ct->c_end - ct->c_begin);
1660 if (fstat(fd, &st) != NOTOK)
1661 size = (long) st.st_size;
1665 (*ct->c_ceclosefnx) (ct);
1674 static unsigned char b642nib[0x80] = {
1675 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1676 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1677 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1678 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1679 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1680 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1681 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1682 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1683 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1684 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1685 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1686 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1687 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1688 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1689 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1690 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1697 return init_encoding(ct, openBase64);
1702 openBase64(CT ct, char **file)
1704 int bitno, cc, digested;
1707 unsigned char value, *b, *b1, *b2, *b3;
1708 unsigned char *cp, *ep;
1709 char buffer[BUFSIZ];
1710 /* sbeck -- handle suffixes */
1715 b = (unsigned char *) &bits;
1716 b1 = &b[endian > 0 ? 1 : 2];
1717 b2 = &b[endian > 0 ? 2 : 1];
1718 b3 = &b[endian > 0 ? 3 : 0];
1722 fseek(ce->ce_fp, 0L, SEEK_SET);
1727 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1728 content_error(ce->ce_file, ct,
1729 "unable to fopen for reading");
1735 if (*file == NULL) {
1736 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1739 ce->ce_file = getcpy(*file);
1743 /* sbeck@cise.ufl.edu -- handle suffixes */
1745 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1746 invo_name, ci->ci_type, ci->ci_subtype);
1747 cp = context_find(buffer);
1748 if (cp == NULL || *cp == '\0') {
1749 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1751 cp = context_find(buffer);
1753 if (cp != NULL && *cp != '\0') {
1754 if (ce->ce_unlink) {
1756 ** Temporary file already exists, so we rename to
1757 ** version with extension.
1759 char *file_org = strdup(ce->ce_file);
1760 ce->ce_file = add(cp, ce->ce_file);
1761 if (rename(file_org, ce->ce_file)) {
1762 adios(ce->ce_file, "unable to rename %s to ",
1768 ce->ce_file = add(cp, ce->ce_file);
1772 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1773 content_error(ce->ce_file, ct,
1774 "unable to fopen for reading/writing");
1778 if ((len = ct->c_end - ct->c_begin) < 0)
1779 adios(NULL, "internal error(1)");
1781 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1782 content_error(ct->c_file, ct, "unable to open for reading");
1786 if ((digested = ct->c_digested))
1787 MD5Init(&mdContext);
1793 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1795 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1797 content_error(ct->c_file, ct, "error reading from");
1801 content_error(NULL, ct, "premature eof");
1809 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1814 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1816 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1818 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1822 bits |= value << bitno;
1824 if ((bitno -= 6) < 0) {
1825 putc((char) *b1, ce->ce_fp);
1827 MD5Update(&mdContext, b1, 1);
1829 putc((char) *b2, ce->ce_fp);
1831 MD5Update(&mdContext, b2, 1);
1833 putc((char) *b3, ce->ce_fp);
1835 MD5Update(&mdContext, b3, 1);
1839 if (ferror(ce->ce_fp)) {
1840 content_error(ce->ce_file, ct,
1841 "error writing to");
1844 bitno = 18, bits = 0L, skip = 0;
1850 goto self_delimiting;
1859 fprintf(stderr, "premature ending (bitno %d)\n",
1862 content_error(NULL, ct, "invalid BASE64 encoding");
1867 fseek(ct->c_fp, 0L, SEEK_SET);
1869 if (fflush(ce->ce_fp)) {
1870 content_error(ce->ce_file, ct, "error writing to");
1875 unsigned char digest[16];
1877 MD5Final(digest, &mdContext);
1878 if (memcmp((char *) digest, (char *) ct->c_digest,
1879 sizeof(digest) / sizeof(digest[0])))
1880 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1882 fprintf(stderr, "content integrity confirmed\n");
1885 fseek(ce->ce_fp, 0L, SEEK_SET);
1888 *file = ce->ce_file;
1889 return fileno(ce->ce_fp);
1892 free_encoding(ct, 0);
1901 static char hex2nib[0x80] = {
1902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1908 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1909 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1924 return init_encoding(ct, openQuoted);
1929 openQuoted(CT ct, char **file)
1931 int cc, digested, len, quoted;
1932 unsigned char *cp, *ep;
1933 char buffer[BUFSIZ];
1936 /* sbeck -- handle suffixes */
1942 fseek(ce->ce_fp, 0L, SEEK_SET);
1947 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1948 content_error(ce->ce_file, ct,
1949 "unable to fopen for reading");
1955 if (*file == NULL) {
1956 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1959 ce->ce_file = getcpy(*file);
1963 /* sbeck@cise.ufl.edu -- handle suffixes */
1965 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1966 invo_name, ci->ci_type, ci->ci_subtype);
1967 cp = context_find(buffer);
1968 if (cp == NULL || *cp == '\0') {
1969 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1971 cp = context_find(buffer);
1973 if (cp != NULL && *cp != '\0') {
1974 if (ce->ce_unlink) {
1975 // Temporary file already exists, so we rename to
1976 // version with extension.
1977 char *file_org = strdup(ce->ce_file);
1978 ce->ce_file = add(cp, ce->ce_file);
1979 if (rename(file_org, ce->ce_file)) {
1980 adios(ce->ce_file, "unable to rename %s to ",
1986 ce->ce_file = add(cp, ce->ce_file);
1990 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1991 content_error(ce->ce_file, ct,
1992 "unable to fopen for reading/writing");
1996 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1997 content_error(ce->ce_file, ct,
1998 "unable to fopen for reading/writing");
2002 if ((len = ct->c_end - ct->c_begin) < 0)
2003 adios(NULL, "internal error(2)");
2005 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2006 content_error(ct->c_file, ct, "unable to open for reading");
2010 if ((digested = ct->c_digested))
2011 MD5Init(&mdContext);
2018 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2020 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2021 content_error(NULL, ct, "premature eof");
2025 if ((cc = strlen(buffer)) > len)
2029 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2034 for (; cp < ep; cp++) {
2036 /* in an escape sequence */
2038 /* at byte 1 of an escape sequence */
2039 mask = hex2nib[*cp & 0x7f];
2040 /* next is byte 2 */
2043 /* at byte 2 of an escape sequence */
2045 mask |= hex2nib[*cp & 0x7f];
2046 putc(mask, ce->ce_fp);
2048 MD5Update(&mdContext, &mask, 1);
2049 if (ferror(ce->ce_fp)) {
2050 content_error(ce->ce_file, ct, "error writing to");
2054 ** finished escape sequence; next may
2055 ** be literal or a new escape sequence
2059 /* on to next byte */
2063 /* not in an escape sequence */
2066 ** starting an escape sequence,
2069 if (cp + 1 < ep && cp[1] == '\n') {
2070 /* "=\n" soft line break, eat the \n */
2074 if (cp + 1 >= ep || cp + 2 >= ep) {
2076 ** We don't have 2 bytes left,
2077 ** so this is an invalid escape
2078 ** sequence; just show the raw bytes
2081 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2083 ** Next 2 bytes are hex digits,
2084 ** making this a valid escape
2085 ** sequence; let's decode it (above).
2091 ** One or both of the next 2 is
2092 ** out of range, making this an
2093 ** invalid escape sequence; just
2094 ** show the raw bytes (below).
2099 /* Just show the raw byte. */
2100 putc(*cp, ce->ce_fp);
2103 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2105 MD5Update(&mdContext, (unsigned char *) cp, 1);
2108 if (ferror(ce->ce_fp)) {
2109 content_error(ce->ce_file, ct,
2110 "error writing to");
2116 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2120 fseek(ct->c_fp, 0L, SEEK_SET);
2122 if (fflush(ce->ce_fp)) {
2123 content_error(ce->ce_file, ct, "error writing to");
2128 unsigned char digest[16];
2130 MD5Final(digest, &mdContext);
2131 if (memcmp((char *) digest, (char *) ct->c_digest,
2132 sizeof(digest) / sizeof(digest[0])))
2133 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2135 fprintf(stderr, "content integrity confirmed\n");
2138 fseek(ce->ce_fp, 0L, SEEK_SET);
2141 *file = ce->ce_file;
2142 return fileno(ce->ce_fp);
2145 free_encoding(ct, 0);
2157 if (init_encoding(ct, open7Bit) == NOTOK)
2160 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2166 open7Bit(CT ct, char **file)
2169 char buffer[BUFSIZ];
2170 /* sbeck -- handle suffixes */
2177 fseek(ce->ce_fp, 0L, SEEK_SET);
2182 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2183 content_error(ce->ce_file, ct,
2184 "unable to fopen for reading");
2190 if (*file == NULL) {
2191 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2194 ce->ce_file = getcpy(*file);
2198 /* sbeck@cise.ufl.edu -- handle suffixes */
2200 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2201 invo_name, ci->ci_type, ci->ci_subtype);
2202 cp = context_find(buffer);
2203 if (cp == NULL || *cp == '\0') {
2204 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2206 cp = context_find(buffer);
2208 if (cp != NULL && *cp != '\0') {
2209 if (ce->ce_unlink) {
2211 ** Temporary file already exists, so we rename to
2212 ** version with extension.
2214 char *file_org = strdup(ce->ce_file);
2215 ce->ce_file = add(cp, ce->ce_file);
2216 if (rename(file_org, ce->ce_file)) {
2217 adios(ce->ce_file, "unable to rename %s to ",
2223 ce->ce_file = add(cp, ce->ce_file);
2227 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2228 content_error(ce->ce_file, ct,
2229 "unable to fopen for reading/writing");
2233 if (ct->c_type == CT_MULTIPART) {
2235 CI ci = &ct->c_ctinfo;
2238 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2240 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2241 strlen(ci->ci_subtype);
2242 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2243 putc(';', ce->ce_fp);
2246 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2249 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2250 fputs("\n\t", ce->ce_fp);
2253 putc(' ', ce->ce_fp);
2256 fprintf(ce->ce_fp, "%s", buffer);
2260 if (ci->ci_comment) {
2261 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2263 fputs("\n\t", ce->ce_fp);
2266 putc(' ', ce->ce_fp);
2269 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2272 fprintf(ce->ce_fp, "\n");
2274 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2276 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2278 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2279 fprintf(ce->ce_fp, "\n");
2282 if ((len = ct->c_end - ct->c_begin) < 0)
2283 adios(NULL, "internal error(3)");
2285 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2286 content_error(ct->c_file, ct, "unable to open for reading");
2290 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2292 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2294 content_error(ct->c_file, ct, "error reading from");
2298 content_error(NULL, ct, "premature eof");
2306 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2307 if (ferror(ce->ce_fp)) {
2308 content_error(ce->ce_file, ct,
2309 "error writing to");
2314 fseek(ct->c_fp, 0L, SEEK_SET);
2316 if (fflush(ce->ce_fp)) {
2317 content_error(ce->ce_file, ct, "error writing to");
2321 fseek(ce->ce_fp, 0L, SEEK_SET);
2324 *file = ce->ce_file;
2325 return fileno(ce->ce_fp);
2328 free_encoding(ct, 0);
2338 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2340 char cachefile[BUFSIZ];
2343 fseek(ce->ce_fp, 0L, SEEK_SET);
2348 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2349 content_error(ce->ce_file, ct,
2350 "unable to fopen for reading");
2356 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2357 cachefile, sizeof(cachefile)) != NOTOK) {
2358 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2359 ce->ce_file = getcpy(cachefile);
2363 admonish(cachefile, "unable to fopen for reading");
2370 *file = ce->ce_file;
2371 *fd = fileno(ce->ce_fp);
2382 return init_encoding(ct, openFile);
2387 openFile(CT ct, char **file)
2390 char cachefile[BUFSIZ];
2391 struct exbody *e = ct->c_ctexbody;
2392 CE ce = ct->c_cefile;
2394 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2404 content_error(NULL, ct, "missing name parameter");
2408 ce->ce_file = getcpy(e->eb_name);
2411 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2412 content_error(ce->ce_file, ct, "unable to fopen for reading");
2416 if ((!e->eb_permission ||
2417 mh_strcasecmp(e->eb_permission, "read-write")) &&
2418 find_cache(NULL, wcachesw, &cachetype,
2419 e->eb_content->c_id, cachefile, sizeof(cachefile))
2424 mask = umask(cachetype ? ~m_gmprot() : 0222);
2425 if ((fp = fopen(cachefile, "w"))) {
2427 char buffer[BUFSIZ];
2428 FILE *gp = ce->ce_fp;
2430 fseek(gp, 0L, SEEK_SET);
2432 while ((cc = fread(buffer, sizeof(*buffer),
2433 sizeof(buffer), gp)) > 0)
2434 fwrite(buffer, sizeof(*buffer), cc, fp);
2438 admonish(ce->ce_file, "error reading");
2440 } else if (ferror(fp)) {
2441 admonish(cachefile, "error writing");
2449 fseek(ce->ce_fp, 0L, SEEK_SET);
2450 *file = ce->ce_file;
2451 return fileno(ce->ce_fp);
2461 return init_encoding(ct, openFTP);
2466 openFTP(CT ct, char **file)
2468 int cachetype, caching, fd;
2470 char *bp, *ftp, *user, *pass;
2471 char buffer[BUFSIZ], cachefile[BUFSIZ];
2474 static char *username = NULL;
2475 static char *password = NULL;
2476 int child_id, i, vecp;
2482 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2488 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2497 if (!e->eb_name || !e->eb_site) {
2498 content_error(NULL, ct, "missing %s parameter",
2499 e->eb_name ? "site": "name");
2506 pidcheck(pidwait(xpid, NOTOK));
2510 /* Get the buffer ready to go */
2512 buflen = sizeof(buffer);
2515 ** Construct the query message for user
2517 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2523 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2529 snprintf(bp, buflen, "\n using %sFTP from site %s",
2530 e->eb_flags ? "anonymous " : "", e->eb_site);
2535 if (e->eb_size > 0) {
2536 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2541 snprintf(bp, buflen, "? ");
2544 ** Now, check the answer
2546 if (!getanswer(buffer))
2551 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2555 ruserpass(e->eb_site, &username, &password);
2560 ce->ce_unlink = (*file == NULL);
2562 cachefile[0] = '\0';
2563 if ((!e->eb_permission ||
2564 mh_strcasecmp(e->eb_permission, "read-write")) &&
2565 find_cache(NULL, wcachesw, &cachetype,
2566 e->eb_content->c_id, cachefile, sizeof(cachefile))
2568 if (*file == NULL) {
2575 ce->ce_file = getcpy(*file);
2577 ce->ce_file = getcpy(cachefile);
2579 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2581 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2582 content_error (ce->ce_file, ct,
2583 "unable to fopen for reading/writing");
2588 vec[vecp++] = mhbasename(ftp);
2589 vec[vecp++] = e->eb_site;
2592 vec[vecp++] = e->eb_dir;
2593 vec[vecp++] = e->eb_name;
2594 vec[vecp++] = ce->ce_file,
2595 vec[vecp++] = e->eb_mode &&
2596 !mh_strcasecmp(e->eb_mode, "ascii") ?
2602 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2606 adios("fork", "unable to");
2610 close(fileno(ce->ce_fp));
2612 fprintf(stderr, "unable to exec ");
2618 if (pidXwait(child_id, NULL)) {
2619 username = password = NULL;
2628 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2633 mask = umask(cachetype ? ~m_gmprot() : 0222);
2634 if ((fp = fopen(cachefile, "w"))) {
2636 FILE *gp = ce->ce_fp;
2638 fseek(gp, 0L, SEEK_SET);
2640 while ((cc= fread(buffer, sizeof(*buffer),
2641 sizeof(buffer), gp)) > 0)
2642 fwrite(buffer, sizeof(*buffer), cc, fp);
2646 admonish(ce->ce_file, "error reading");
2648 } else if (ferror(fp)) {
2649 admonish(cachefile, "error writing");
2658 fseek(ce->ce_fp, 0L, SEEK_SET);
2659 *file = ce->ce_file;
2660 return fileno(ce->ce_fp);
2671 return init_encoding(ct, openMail);
2676 openMail(CT ct, char **file)
2678 int child_id, fd, i, vecp;
2680 char *bp, buffer[BUFSIZ], *vec[7];
2681 struct exbody *e = ct->c_ctexbody;
2682 CE ce = ct->c_cefile;
2684 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2693 if (!e->eb_server) {
2694 content_error(NULL, ct, "missing server parameter");
2701 pidcheck(pidwait(xpid, NOTOK));
2705 /* Get buffer ready to go */
2707 buflen = sizeof(buffer);
2709 /* Now, construct query message */
2710 snprintf(bp, buflen, "Retrieve content");
2716 snprintf(bp, buflen, " %s", e->eb_partno);
2722 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2723 e->eb_subject ? e->eb_subject : e->eb_body);
2725 /* Now, check answer */
2726 if (!getanswer(buffer))
2730 vec[vecp++] = mhbasename(mailproc);
2731 vec[vecp++] = e->eb_server;
2732 vec[vecp++] = "-subject";
2733 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2734 vec[vecp++] = "-body";
2735 vec[vecp++] = e->eb_body;
2738 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2742 advise("fork", "unable to");
2746 execvp(mailproc, vec);
2747 fprintf(stderr, "unable to exec ");
2753 if (pidXwait(child_id, NULL) == OK)
2754 advise(NULL, "request sent");
2758 if (*file == NULL) {
2759 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2762 ce->ce_file = getcpy(*file);
2766 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2767 content_error(ce->ce_file, ct,
2768 "unable to fopen for reading/writing");
2773 ** showproc is for mhshow and mhstore, though mhlist -debug
2777 free(ct->c_showproc);
2778 ct->c_showproc = getcpy("true");
2780 fseek(ce->ce_fp, 0L, SEEK_SET);
2781 *file = ce->ce_file;
2782 return fileno(ce->ce_fp);
2787 readDigest(CT ct, char *cp)
2792 unsigned char *dp, value, *ep;
2793 unsigned char *b, *b1, *b2, *b3;
2795 b = (unsigned char *) &bits,
2796 b1 = &b[endian > 0 ? 1 : 2],
2797 b2 = &b[endian > 0 ? 2 : 1],
2798 b3 = &b[endian > 0 ? 3 : 0];
2803 for (ep = (dp = ct->c_digest)
2804 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2807 if (skip || (*cp & 0x80) ||
2808 (value = b642nib[*cp & 0x7f])
2811 fprintf(stderr, "invalid BASE64 encoding\n");
2815 bits |= value << bitno;
2817 if ((bitno -= 6) < 0) {
2818 if (dp + (3 - skip) > ep)
2819 goto invalid_digest;
2834 goto self_delimiting;
2839 fprintf(stderr, "premature ending (bitno %d)\n",
2850 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2858 fprintf(stderr, "MD5 digest=");
2859 for (dp = ct->c_digest; dp < ep; dp++)
2860 fprintf(stderr, "%02x", *dp & 0xff);
2861 fprintf(stderr, "\n");