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>
18 #include <h/mhparse.h>
23 extern int endian; /* mhmisc.c */
25 extern pid_t xpid; /* mhshowsbr.c */
28 extern int rcachesw; /* mhcachesbr.c */
29 extern int wcachesw; /* mhcachesbr.c */
31 int checksw = 0; /* check Content-MD5 field */
34 ** Directory to place temp files. This must
35 ** be set before these routines are called.
40 ** Structures for TEXT messages
42 struct k2v SubText[] = {
43 { "plain", TEXT_PLAIN },
44 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
45 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
46 { NULL, TEXT_UNKNOWN } /* this one must be last! */
49 struct k2v Charset[] = {
50 { "us-ascii", CHARSET_USASCII },
51 { "iso-8859-1", CHARSET_LATIN },
52 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
56 ** Structures for MULTIPART messages
58 struct k2v SubMultiPart[] = {
59 { "mixed", MULTI_MIXED },
60 { "alternative", MULTI_ALTERNATE },
61 { "digest", MULTI_DIGEST },
62 { "parallel", MULTI_PARALLEL },
63 { NULL, MULTI_UNKNOWN } /* this one must be last! */
67 ** Structures for MESSAGE messages
69 struct k2v SubMessage[] = {
70 { "rfc822", MESSAGE_RFC822 },
71 { "partial", MESSAGE_PARTIAL },
72 { "external-body", MESSAGE_EXTERNAL },
73 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
77 ** Structure for APPLICATION messages
79 struct k2v SubApplication[] = {
80 { "octet-stream", APPLICATION_OCTETS },
81 { "postscript", APPLICATION_POSTSCRIPT },
82 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
87 int find_cache(CT, int, int *, char *, char *, int);
92 int make_intermediates(char *);
93 void content_error(char *, CT, char *, ...);
96 void free_content(CT);
97 void free_encoding(CT, int);
102 static CT get_content(FILE *, char *, int);
103 static int get_comment(CT, unsigned char **, int);
105 static int InitGeneric(CT);
106 static int InitText(CT);
107 static int InitMultiPart(CT);
108 static void reverse_parts(CT);
109 static int InitMessage(CT);
110 static int InitApplication(CT);
111 static int init_encoding(CT, OpenCEFunc);
112 static unsigned long size_encoding(CT);
113 static int InitBase64(CT);
114 static int openBase64(CT, char **);
115 static int InitQuoted(CT);
116 static int openQuoted(CT, char **);
117 static int Init7Bit(CT);
118 static int openExternal(CT, CT, CE, char **, int *);
119 static int InitFile(CT);
120 static int openFile(CT, char **);
121 static int InitFTP(CT);
122 static int openFTP(CT, char **);
123 static int InitMail(CT);
124 static int openMail(CT, char **);
125 static int readDigest(CT, char *);
127 struct str2init str2cts[] = {
128 { "application", CT_APPLICATION, InitApplication },
129 { "audio", CT_AUDIO, InitGeneric },
130 { "image", CT_IMAGE, InitGeneric },
131 { "message", CT_MESSAGE, InitMessage },
132 { "multipart", CT_MULTIPART, InitMultiPart },
133 { "text", CT_TEXT, InitText },
134 { "video", CT_VIDEO, InitGeneric },
135 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
136 { NULL, CT_UNKNOWN, NULL },
139 struct str2init str2ces[] = {
140 { "base64", CE_BASE64, InitBase64 },
141 { "quoted-printable", CE_QUOTED, InitQuoted },
142 { "8bit", CE_8BIT, Init7Bit },
143 { "7bit", CE_7BIT, Init7Bit },
144 { "binary", CE_BINARY, Init7Bit },
145 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
146 { NULL, CE_UNKNOWN, NULL },
150 ** NOTE WELL: si_key MUST NOT have value of NOTOK
152 ** si_key is 1 if access method is anonymous.
154 struct str2init str2methods[] = {
155 { "afs", 1, InitFile },
156 { "anon-ftp", 1, InitFTP },
157 { "ftp", 0, InitFTP },
158 { "local-file", 0, InitFile },
159 { "mail-server", 0, InitMail },
167 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
178 ** Main entry point for parsing a MIME message or file.
179 ** It returns the Content structure for the top level
180 ** entity in the file.
183 parse_mime(char *file)
191 ** Check if file is actually standard input
193 if ((is_stdin = (strcmp(file, "-")==0))) {
194 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
196 advise("mhparse", "unable to create temporary file");
199 file = getcpy(tfile);
202 while (fgets(buffer, sizeof(buffer), stdin))
208 advise("stdin", "error reading");
213 advise(file, "error writing");
216 fseek(fp, 0L, SEEK_SET);
217 } else if ((fp = fopen(file, "r")) == NULL) {
218 advise(file, "unable to read");
222 if (!(ct = get_content(fp, file, 1))) {
225 advise(NULL, "unable to decode %s", file);
230 ct->c_unlink = 1; /* temp file to remove */
234 if (ct->c_end == 0L) {
235 fseek(fp, 0L, SEEK_END);
236 ct->c_end = ftell(fp);
239 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
251 ** Main routine for reading/parsing the headers
252 ** of a message content.
254 ** toplevel = 1 # we are at the top level of the message
255 ** toplevel = 0 # we are inside message type or multipart type
256 ** # other than multipart/digest
257 ** toplevel = -1 # we are inside multipart/digest
258 ** NB: on failure we will fclose(in)!
262 get_content(FILE *in, char *file, int toplevel)
265 char buf[BUFSIZ], name[NAMESZ];
270 /* allocate the content structure */
271 if (!(ct = (CT) calloc(1, sizeof(*ct))))
272 adios(NULL, "out of memory");
275 ct->c_file = getcpy(file);
276 ct->c_begin = ftell(ct->c_fp) + 1;
279 ** Parse the header fields for this
280 ** content into a linked list.
282 for (compnum = 1, state = FLD;;) {
283 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
289 /* get copies of the buffers */
293 /* if necessary, get rest of field */
294 while (state == FLDPLUS) {
295 state = m_getfld(state, name, buf,
297 vp = add(buf, vp); /* add to previous value */
300 /* Now add the header data to the list */
301 add_header(ct, np, vp);
303 /* continue, if this isn't the last header field */
304 if (state != FLDEOF) {
305 ct->c_begin = ftell(in) + 1;
312 ct->c_begin = ftell(in) - strlen(buf);
316 ct->c_begin = ftell(in);
321 adios(NULL, "message format error in component #%d",
325 adios(NULL, "getfld() returned %d", state);
328 /* break out of the loop */
333 ** Read the content headers. We will parse the
334 ** MIME related header fields into their various
335 ** structures and set internal flags related to
336 ** content type/subtype, etc.
339 hp = ct->c_first_hf; /* start at first header field */
341 /* Get MIME-Version field */
342 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
345 unsigned char *cp, *dp;
348 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
351 ct->c_vrsn = getcpy(hp->value);
353 /* Now, cleanup this field */
358 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
360 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
365 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
367 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
370 for (dp = cp; istoken(*dp); dp++)
374 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
377 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
380 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
381 /* Get Content-Type field */
382 struct str2init *s2i;
383 CI ci = &ct->c_ctinfo;
385 /* Check if we've already seen a Content-Type header */
387 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
391 /* Parse the Content-Type field */
392 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
396 ** Set the Init function and the internal
397 ** flag for this content type.
399 for (s2i = str2cts; s2i->si_key; s2i++)
400 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
402 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
404 ct->c_type = s2i->si_val;
405 ct->c_ctinitfnx = s2i->si_init;
407 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
408 /* Get Content-Transfer-Encoding field */
410 unsigned char *cp, *dp;
411 struct str2init *s2i;
414 ** Check if we've already seen the
415 ** Content-Transfer-Encoding field
418 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
422 /* get copy of this field */
423 ct->c_celine = cp = getcpy(hp->value);
427 for (dp = cp; istoken(*dp); dp++)
433 ** Find the internal flag and Init function
434 ** for this transfer encoding.
436 for (s2i = str2ces; s2i->si_key; s2i++)
437 if (!mh_strcasecmp(cp, s2i->si_key))
439 if (!s2i->si_key && !uprf(cp, "X-"))
442 ct->c_encoding = s2i->si_val;
444 /* Call the Init function for this encoding */
445 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
448 } else if (!mh_strcasecmp(hp->name, MD5_FIELD)) {
449 /* Get Content-MD5 field */
450 unsigned char *cp, *dp;
456 if (ct->c_digested) {
457 advise(NULL, "message %s has multiple %s: fields", ct->c_file, MD5_FIELD);
461 ep = cp = getcpy(hp->value);
465 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
467 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
472 fprintf(stderr, "%s: %s\n", MD5_FIELD, cp);
474 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK) {
479 for (dp = cp; *dp && !isspace(*dp); dp++)
487 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
488 /* Get Content-ID field */
489 ct->c_id = add(hp->value, ct->c_id);
491 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
492 /* Get Content-Description field */
493 ct->c_descr = add(hp->value, ct->c_descr);
495 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
496 /* Get Content-Disposition field */
497 ct->c_dispo = add(hp->value, ct->c_dispo);
501 hp = hp->next; /* next header field */
505 ** Check if we saw a Content-Type field.
506 ** If not, then assign a default value for
507 ** it, and the Init function.
511 ** If we are inside a multipart/digest message,
512 ** so default type is message/rfc822
515 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
517 ct->c_type = CT_MESSAGE;
518 ct->c_ctinitfnx = InitMessage;
521 ** Else default type is text/plain
523 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
525 ct->c_type = CT_TEXT;
526 ct->c_ctinitfnx = InitText;
530 /* Use default Transfer-Encoding, if necessary */
532 ct->c_encoding = CE_7BIT;
545 ** small routine to add header field to list
549 add_header(CT ct, char *name, char *value)
553 /* allocate header field structure */
554 hp = mh_xmalloc(sizeof(*hp));
556 /* link data into header structure */
561 /* link header structure into the list */
562 if (ct->c_first_hf == NULL) {
563 ct->c_first_hf = hp; /* this is the first */
566 ct->c_last_hf->next = hp; /* add it to the end */
575 ** Make sure that buf contains at least one appearance of name,
576 ** followed by =. If not, insert both name and value, just after
577 ** first semicolon, if any. Note that name should not contain a
578 ** trailing =. And quotes will be added around the value. Typical
579 ** usage: make sure that a Content-Disposition header contains
580 ** filename="foo". If it doesn't and value does, use value from
584 incl_name_value(unsigned char *buf, char *name, char *value) {
587 /* Assume that name is non-null. */
589 char *name_plus_equal = concat(name, "=", NULL);
591 if (!strstr(buf, name_plus_equal)) {
594 char *prefix, *suffix;
596 /* Trim trailing space, esp. newline. */
597 for (cp = &buf[strlen(buf) - 1];
598 cp >= buf && isspace(*cp); --cp) {
602 insertion = concat("; ", name, "=", "\"", value, "\"",
606 ** Insert at first semicolon, if any.
607 ** If none, append to end.
609 prefix = getcpy(buf);
610 if ((cp = strchr(prefix, ';'))) {
611 suffix = concat(cp, NULL);
613 newbuf = concat(prefix, insertion, suffix,
618 newbuf = concat(buf, insertion, "\n", NULL);
626 free(name_plus_equal);
633 ** Extract just name_suffix="foo", if any, from value. If there isn't
634 ** one, return the entire value. Note that, for example, a name_suffix
635 ** of name will match filename="foo", and return foo.
638 extract_name_value(char *name_suffix, char *value) {
639 char *extracted_name_value = value;
640 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
641 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
644 free(name_suffix_plus_quote);
645 if (name_suffix_equals) {
646 char *name_suffix_begin;
649 for (cp = name_suffix_equals; *cp != '"'; ++cp)
651 name_suffix_begin = ++cp;
652 /* Find second \". */
653 for (; *cp != '"'; ++cp)
656 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
657 memcpy(extracted_name_value, name_suffix_begin,
658 cp - name_suffix_begin);
659 extracted_name_value[cp - name_suffix_begin] = '\0';
662 return extracted_name_value;
666 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
667 ** directives. Fills in the information of the CTinfo structure.
670 get_ctinfo(unsigned char *cp, CT ct, int magic)
679 i = strlen(invo_name) + 2;
681 /* store copy of Content-Type line */
682 cp = ct->c_ctline = getcpy(cp);
684 while (isspace(*cp)) /* trim leading spaces */
687 /* change newlines to spaces */
688 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
691 /* trim trailing spaces */
692 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
698 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
700 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
703 for (dp = cp; istoken(*dp); dp++)
706 ci->ci_type = getcpy(cp); /* store content type */
710 advise(NULL, "invalid %s: field in message %s (empty type)",
711 TYPE_FIELD, ct->c_file);
715 /* down case the content type string */
716 for (dp = ci->ci_type; *dp; dp++)
717 if (isalpha(*dp) && isupper(*dp))
723 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
728 ci->ci_subtype = getcpy("");
736 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
739 for (dp = cp; istoken(*dp); dp++)
742 ci->ci_subtype = getcpy(cp); /* store the content subtype */
745 if (!*ci->ci_subtype) {
746 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
750 /* down case the content subtype string */
751 for (dp = ci->ci_subtype; *dp; dp++)
752 if (isalpha(*dp) && isupper(*dp))
759 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
763 ** Parse attribute/value pairs given with Content-Type
765 ep = (ap = ci->ci_attrs) + NPARMS;
771 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
779 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
783 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
787 /* down case the attribute name */
788 for (dp = cp; istoken(*dp); dp++)
789 if (isalpha(*dp) && isupper(*dp))
792 for (up = dp; isspace(*dp);)
794 if (dp == cp || *dp != '=') {
795 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);
799 vp = (*ap = getcpy(cp)) + (up - cp);
801 for (dp++; isspace(*dp);)
804 /* now add the attribute value */
805 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
808 for (cp = ++dp, dp = vp;;) {
812 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
817 if ((c = *cp++) == '\0')
832 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
837 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
845 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
850 ** Get any <Content-Id> given in buffer
852 if (magic && *cp == '<') {
857 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
858 advise(NULL, "invalid ID in message %s", ct->c_file);
864 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
875 ** Get any [Content-Description] given in buffer.
877 if (magic && *cp == '[') {
879 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
883 advise(NULL, "invalid description in message %s",
892 ct->c_descr = concat(ct->c_descr, "\n", NULL);
903 ** Get any {Content-Disposition} given in buffer.
905 if (magic && *cp == '{') {
907 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
911 advise(NULL, "invalid disposition in message %s",
920 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
931 ** Check if anything is left over
935 ci->ci_magic = getcpy(cp);
938 ** If there is a Content-Disposition header and
939 ** it doesn't have a *filename=, extract it from
940 ** the magic contents. The mhbasename call skips
941 ** any leading directory components.
944 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
946 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
954 get_comment(CT ct, unsigned char **ap, int istype)
959 char c, buffer[BUFSIZ], *dp;
971 advise(NULL, "invalid comment in message %s's %s: field",
972 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
977 if ((c = *cp++) == '\0')
1000 if ((dp = ci->ci_comment)) {
1001 ci->ci_comment = concat(dp, " ", buffer, NULL);
1004 ci->ci_comment = getcpy(buffer);
1008 while (isspace(*cp))
1019 ** Handles content types audio, image, and video.
1020 ** There's not much to do right here.
1026 return OK; /* not much to do here */
1037 char buffer[BUFSIZ];
1039 char **ap, **ep, *cp;
1042 CI ci = &ct->c_ctinfo;
1044 /* check for missing subtype */
1045 if (!*ci->ci_subtype)
1046 ci->ci_subtype = add("plain", ci->ci_subtype);
1049 for (kv = SubText; kv->kv_key; kv++)
1050 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1052 ct->c_subtype = kv->kv_value;
1054 /* allocate text character set structure */
1055 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1056 adios(NULL, "out of memory");
1057 ct->c_ctparams = (void *) t;
1059 /* scan for charset parameter */
1060 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1061 if (!mh_strcasecmp(*ap, "charset"))
1064 /* check if content specified a character set */
1066 /* match character set or set to CHARSET_UNKNOWN */
1067 for (kv = Charset; kv->kv_key; kv++) {
1068 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1073 t->tx_charset = kv->kv_value;
1075 t->tx_charset = CHARSET_UNSPECIFIED;
1079 ** If we can not handle character set natively,
1080 ** then check profile for string to modify the
1081 ** terminal or display method.
1083 ** termproc is for mhshow, though mhlist -debug prints it, too.
1085 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1086 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1088 if ((cp = context_find(buffer)))
1089 ct->c_termproc = getcpy(cp);
1101 InitMultiPart(CT ct)
1105 unsigned char *cp, *dp;
1107 char *bp, buffer[BUFSIZ];
1108 struct multipart *m;
1110 struct part *part, **next;
1111 CI ci = &ct->c_ctinfo;
1116 ** The encoding for multipart messages must be either
1117 ** 7bit, 8bit, or binary (per RFC2045).
1119 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1120 && ct->c_encoding != CE_BINARY) {
1121 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);
1126 for (kv = SubMultiPart; kv->kv_key; kv++)
1127 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1129 ct->c_subtype = kv->kv_value;
1132 ** Check for "boundary" parameter, which is
1133 ** required for multipart messages.
1136 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1137 if (!mh_strcasecmp(*ap, "boundary")) {
1143 /* complain if boundary parameter is missing */
1145 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);
1149 /* allocate primary structure for multipart info */
1150 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1151 adios(NULL, "out of memory");
1152 ct->c_ctparams = (void *) m;
1154 /* check if boundary parameter contains only whitespace characters */
1155 for (cp = bp; isspace(*cp); cp++)
1158 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);
1162 /* remove trailing whitespace from boundary parameter */
1163 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1168 /* record boundary separators */
1169 m->mp_start = concat(bp, "\n", NULL);
1170 m->mp_stop = concat(bp, "--\n", NULL);
1172 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1173 advise(ct->c_file, "unable to open for reading");
1177 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1179 next = &m->mp_parts;
1183 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1187 pos += strlen(buffer);
1188 if (buffer[0] != '-' || buffer[1] != '-')
1191 if (strcmp(buffer + 2, m->mp_start)!=0)
1194 if ((part = (struct part *) calloc(1, sizeof(*part)))
1196 adios(NULL, "out of memory");
1198 next = &part->mp_next;
1200 if (!(p = get_content(fp, ct->c_file,
1201 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1208 fseek(fp, pos, SEEK_SET);
1211 if (strcmp(buffer + 2, m->mp_start) == 0) {
1215 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1216 if (p->c_end < p->c_begin)
1217 p->c_begin = p->c_end;
1222 if (strcmp(buffer + 2, m->mp_stop) == 0)
1228 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1229 if (!inout && part) {
1231 p->c_end = ct->c_end;
1233 if (p->c_begin >= p->c_end) {
1234 for (next = &m->mp_parts; *next != part;
1235 next = &((*next)->mp_next))
1239 free((char *) part);
1244 /* reverse the order of the parts for multipart/alternative */
1245 if (ct->c_subtype == MULTI_ALTERNATE)
1249 ** label all subparts with part number, and
1250 ** then initialize the content of the subpart.
1255 char partnam[BUFSIZ];
1258 snprintf(partnam, sizeof(partnam), "%s.",
1260 pp = partnam + strlen(partnam);
1265 for (part = m->mp_parts, partnum = 1; part;
1266 part = part->mp_next, partnum++) {
1269 sprintf(pp, "%d", partnum);
1270 p->c_partno = getcpy(partnam);
1272 /* initialize the content of the subparts */
1273 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1288 ** reverse the order of the parts of a multipart
1292 reverse_parts(CT ct)
1295 struct multipart *m;
1296 struct part **base, **bmp, **next, *part;
1298 m = (struct multipart *) ct->c_ctparams;
1300 /* if only one part, just return */
1301 if (!m->mp_parts || !m->mp_parts->mp_next)
1304 /* count number of parts */
1306 for (part = m->mp_parts; part; part = part->mp_next)
1309 /* allocate array of pointers to the parts */
1310 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1311 adios(NULL, "out of memory");
1314 /* point at all the parts */
1315 for (part = m->mp_parts; part; part = part->mp_next)
1319 /* reverse the order of the parts */
1320 next = &m->mp_parts;
1321 for (bmp--; bmp >= base; bmp--) {
1324 next = &part->mp_next;
1328 /* free array of pointers */
1329 free((char *) base);
1341 CI ci = &ct->c_ctinfo;
1343 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1344 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1348 /* check for missing subtype */
1349 if (!*ci->ci_subtype)
1350 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1353 for (kv = SubMessage; kv->kv_key; kv++)
1354 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1356 ct->c_subtype = kv->kv_value;
1358 switch (ct->c_subtype) {
1359 case MESSAGE_RFC822:
1362 case MESSAGE_PARTIAL:
1367 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1368 adios(NULL, "out of memory");
1369 ct->c_ctparams = (void *) p;
1372 ** scan for parameters "id", "number",
1375 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1376 if (!mh_strcasecmp(*ap, "id")) {
1377 p->pm_partid = getcpy(*ep);
1380 if (!mh_strcasecmp(*ap, "number")) {
1381 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1383 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);
1388 if (!mh_strcasecmp(*ap, "total")) {
1389 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1396 if (!p->pm_partid || !p->pm_partno
1397 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1398 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);
1404 case MESSAGE_EXTERNAL:
1411 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1412 adios(NULL, "out of memory");
1413 ct->c_ctparams = (void *) e;
1415 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1416 advise(ct->c_file, "unable to open for reading");
1420 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1422 if (!(p = get_content(fp, ct->c_file, 0))) {
1430 if ((exresult = params_external(ct, 0)) != NOTOK &&
1431 p->c_ceopenfnx == openMail) {
1435 if ((size = ct->c_end - p->c_begin) <= 0) {
1437 content_error(NULL, ct, "empty body for access-type=mail-server");
1441 e->eb_body = bp = mh_xmalloc((unsigned) size);
1442 fseek(p->c_fp, p->c_begin, SEEK_SET);
1444 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1446 adios("failed", "fread");
1448 adios(NULL, "unexpected EOF from fread");
1450 bp += cc, size -= cc;
1457 p->c_end = p->c_begin;
1462 if (exresult == NOTOK)
1464 if (e->eb_flags == NOTOK)
1467 switch (p->c_type) {
1472 if (p->c_subtype != MESSAGE_RFC822)
1476 e->eb_partno = ct->c_partno;
1478 (*p->c_ctinitfnx) (p);
1493 params_external(CT ct, int composing)
1496 struct exbody *e = (struct exbody *) ct->c_ctparams;
1497 CI ci = &ct->c_ctinfo;
1499 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1500 if (!mh_strcasecmp(*ap, "access-type")) {
1501 struct str2init *s2i;
1502 CT p = e->eb_content;
1504 for (s2i = str2methods; s2i->si_key; s2i++)
1505 if (!mh_strcasecmp(*ep, s2i->si_key))
1509 e->eb_flags = NOTOK;
1510 p->c_encoding = CE_EXTERNAL;
1513 e->eb_access = s2i->si_key;
1514 e->eb_flags = s2i->si_val;
1515 p->c_encoding = CE_EXTERNAL;
1517 /* Call the Init function for this external type */
1518 if ((*s2i->si_init)(p) == NOTOK)
1522 if (!mh_strcasecmp(*ap, "name")) {
1526 if (!mh_strcasecmp(*ap, "permission")) {
1527 e->eb_permission = *ep;
1530 if (!mh_strcasecmp(*ap, "site")) {
1534 if (!mh_strcasecmp(*ap, "directory")) {
1538 if (!mh_strcasecmp(*ap, "mode")) {
1542 if (!mh_strcasecmp(*ap, "size")) {
1543 sscanf(*ep, "%lu", &e->eb_size);
1546 if (!mh_strcasecmp(*ap, "server")) {
1550 if (!mh_strcasecmp(*ap, "subject")) {
1551 e->eb_subject = *ep;
1554 if (composing && !mh_strcasecmp(*ap, "body")) {
1555 e->eb_body = getcpy(*ep);
1560 if (!e->eb_access) {
1561 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);
1574 InitApplication(CT ct)
1577 CI ci = &ct->c_ctinfo;
1580 for (kv = SubApplication; kv->kv_key; kv++)
1581 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1583 ct->c_subtype = kv->kv_value;
1590 ** TRANSFER ENCODINGS
1594 init_encoding(CT ct, OpenCEFunc openfnx)
1598 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1599 adios(NULL, "out of memory");
1602 ct->c_ceopenfnx = openfnx;
1603 ct->c_ceclosefnx = close_encoding;
1604 ct->c_cesizefnx = size_encoding;
1611 close_encoding(CT ct)
1615 if (!(ce = ct->c_cefile))
1625 static unsigned long
1626 size_encoding(CT ct)
1634 if (!(ce = ct->c_cefile))
1635 return (ct->c_end - ct->c_begin);
1637 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1638 return (long) st.st_size;
1641 if (stat(ce->ce_file, &st) != NOTOK)
1642 return (long) st.st_size;
1647 if (ct->c_encoding == CE_EXTERNAL)
1648 return (ct->c_end - ct->c_begin);
1651 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1652 return (ct->c_end - ct->c_begin);
1654 if (fstat(fd, &st) != NOTOK)
1655 size = (long) st.st_size;
1659 (*ct->c_ceclosefnx) (ct);
1668 static unsigned char b642nib[0x80] = {
1669 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1670 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1671 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1672 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1673 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1674 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1675 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1676 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1677 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1678 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1679 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1680 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1681 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1682 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1683 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1684 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1691 return init_encoding(ct, openBase64);
1696 openBase64(CT ct, char **file)
1698 int bitno, cc, digested;
1699 int fd, len, skip, own_ct_fp = 0;
1701 unsigned char value, *b, *b1, *b2, *b3;
1702 unsigned char *cp, *ep;
1703 char buffer[BUFSIZ];
1704 /* sbeck -- handle suffixes */
1709 b = (unsigned char *) &bits;
1710 b1 = &b[endian > 0 ? 1 : 2];
1711 b2 = &b[endian > 0 ? 2 : 1];
1712 b3 = &b[endian > 0 ? 3 : 0];
1716 fseek(ce->ce_fp, 0L, SEEK_SET);
1721 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1722 content_error(ce->ce_file, ct,
1723 "unable to fopen for reading");
1729 if (*file == NULL) {
1730 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1733 ce->ce_file = getcpy(*file);
1737 /* sbeck@cise.ufl.edu -- handle suffixes */
1739 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1740 invo_name, ci->ci_type, ci->ci_subtype);
1741 cp = context_find(buffer);
1742 if (cp == NULL || *cp == '\0') {
1743 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1745 cp = context_find(buffer);
1747 if (cp != NULL && *cp != '\0') {
1748 if (ce->ce_unlink) {
1750 ** Temporary file already exists, so we rename to
1751 ** version with extension.
1753 char *file_org = strdup(ce->ce_file);
1754 ce->ce_file = add(cp, ce->ce_file);
1755 if (rename(file_org, ce->ce_file)) {
1756 adios(ce->ce_file, "unable to rename %s to ",
1762 ce->ce_file = add(cp, ce->ce_file);
1766 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1767 content_error(ce->ce_file, ct,
1768 "unable to fopen for reading/writing");
1772 if ((len = ct->c_end - ct->c_begin) < 0)
1773 adios(NULL, "internal error(1)");
1776 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1777 content_error(ct->c_file, ct,
1778 "unable to open for reading");
1784 if ((digested = ct->c_digested))
1785 MD5Init(&mdContext);
1791 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1793 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1795 content_error(ct->c_file, ct, "error reading from");
1799 content_error(NULL, ct, "premature eof");
1807 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1812 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1814 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1816 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1820 bits |= value << bitno;
1822 if ((bitno -= 6) < 0) {
1823 putc((char) *b1, ce->ce_fp);
1825 MD5Update(&mdContext, b1, 1);
1827 putc((char) *b2, ce->ce_fp);
1829 MD5Update(&mdContext, b2, 1);
1831 putc((char) *b3, ce->ce_fp);
1833 MD5Update(&mdContext, b3, 1);
1837 if (ferror(ce->ce_fp)) {
1838 content_error(ce->ce_file, ct,
1839 "error writing to");
1842 bitno = 18, bits = 0L, skip = 0;
1848 goto self_delimiting;
1857 fprintf(stderr, "premature ending (bitno %d)\n",
1860 content_error(NULL, ct, "invalid BASE64 encoding");
1865 fseek(ct->c_fp, 0L, SEEK_SET);
1867 if (fflush(ce->ce_fp)) {
1868 content_error(ce->ce_file, ct, "error writing to");
1873 unsigned char digest[16];
1875 MD5Final(digest, &mdContext);
1876 if (memcmp((char *) digest, (char *) ct->c_digest,
1877 sizeof(digest) / sizeof(digest[0])))
1878 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1880 fprintf(stderr, "content integrity confirmed\n");
1883 fseek(ce->ce_fp, 0L, SEEK_SET);
1886 *file = ce->ce_file;
1891 return fileno(ce->ce_fp);
1894 free_encoding(ct, 0);
1907 static char hex2nib[0x80] = {
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1915 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1916 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1930 return init_encoding(ct, openQuoted);
1935 openQuoted(CT ct, char **file)
1937 int cc, digested, len, quoted, own_ct_fp = 0;
1938 unsigned char *cp, *ep;
1939 char buffer[BUFSIZ];
1942 /* sbeck -- handle suffixes */
1948 fseek(ce->ce_fp, 0L, SEEK_SET);
1953 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1954 content_error(ce->ce_file, ct,
1955 "unable to fopen for reading");
1961 if (*file == NULL) {
1962 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1965 ce->ce_file = getcpy(*file);
1969 /* sbeck@cise.ufl.edu -- handle suffixes */
1971 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1972 invo_name, ci->ci_type, ci->ci_subtype);
1973 cp = context_find(buffer);
1974 if (cp == NULL || *cp == '\0') {
1975 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1977 cp = context_find(buffer);
1979 if (cp != NULL && *cp != '\0') {
1980 if (ce->ce_unlink) {
1981 // Temporary file already exists, so we rename to
1982 // version with extension.
1983 char *file_org = strdup(ce->ce_file);
1984 ce->ce_file = add(cp, ce->ce_file);
1985 if (rename(file_org, ce->ce_file)) {
1986 adios(ce->ce_file, "unable to rename %s to ",
1992 ce->ce_file = add(cp, ce->ce_file);
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)");
2006 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2007 content_error(ct->c_file, ct,
2008 "unable to open for reading");
2014 if ((digested = ct->c_digested))
2015 MD5Init(&mdContext);
2022 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2024 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2025 content_error(NULL, ct, "premature eof");
2029 if ((cc = strlen(buffer)) > len)
2033 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2038 for (; cp < ep; cp++) {
2040 /* in an escape sequence */
2042 /* at byte 1 of an escape sequence */
2043 mask = hex2nib[*cp & 0x7f];
2044 /* next is byte 2 */
2047 /* at byte 2 of an escape sequence */
2049 mask |= hex2nib[*cp & 0x7f];
2050 putc(mask, ce->ce_fp);
2052 MD5Update(&mdContext, &mask, 1);
2053 if (ferror(ce->ce_fp)) {
2054 content_error(ce->ce_file, ct, "error writing to");
2058 ** finished escape sequence; next may
2059 ** be literal or a new escape sequence
2063 /* on to next byte */
2067 /* not in an escape sequence */
2070 ** starting an escape sequence,
2073 if (cp + 1 < ep && cp[1] == '\n') {
2074 /* "=\n" soft line break, eat the \n */
2078 if (cp + 1 >= ep || cp + 2 >= ep) {
2080 ** We don't have 2 bytes left,
2081 ** so this is an invalid escape
2082 ** sequence; just show the raw bytes
2085 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2087 ** Next 2 bytes are hex digits,
2088 ** making this a valid escape
2089 ** sequence; let's decode it (above).
2095 ** One or both of the next 2 is
2096 ** out of range, making this an
2097 ** invalid escape sequence; just
2098 ** show the raw bytes (below).
2103 /* Just show the raw byte. */
2104 putc(*cp, ce->ce_fp);
2107 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2109 MD5Update(&mdContext, (unsigned char *) cp, 1);
2112 if (ferror(ce->ce_fp)) {
2113 content_error(ce->ce_file, ct,
2114 "error writing to");
2120 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2124 fseek(ct->c_fp, 0L, SEEK_SET);
2126 if (fflush(ce->ce_fp)) {
2127 content_error(ce->ce_file, ct, "error writing to");
2132 unsigned char digest[16];
2134 MD5Final(digest, &mdContext);
2135 if (memcmp((char *) digest, (char *) ct->c_digest,
2136 sizeof(digest) / sizeof(digest[0])))
2137 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2139 fprintf(stderr, "content integrity confirmed\n");
2142 fseek(ce->ce_fp, 0L, SEEK_SET);
2145 *file = ce->ce_file;
2150 return fileno(ce->ce_fp);
2153 free_encoding(ct, 0);
2169 if (init_encoding(ct, open7Bit) == NOTOK)
2172 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2178 open7Bit(CT ct, char **file)
2180 int cc, fd, len, own_ct_fp = 0;
2181 char buffer[BUFSIZ];
2182 /* sbeck -- handle suffixes */
2189 fseek(ce->ce_fp, 0L, SEEK_SET);
2194 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2195 content_error(ce->ce_file, ct,
2196 "unable to fopen for reading");
2202 if (*file == NULL) {
2203 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2206 ce->ce_file = getcpy(*file);
2210 /* sbeck@cise.ufl.edu -- handle suffixes */
2212 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2213 invo_name, ci->ci_type, ci->ci_subtype);
2214 cp = context_find(buffer);
2215 if (cp == NULL || *cp == '\0') {
2216 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2218 cp = context_find(buffer);
2220 if (cp != NULL && *cp != '\0') {
2221 if (ce->ce_unlink) {
2223 ** Temporary file already exists, so we rename to
2224 ** version with extension.
2226 char *file_org = strdup(ce->ce_file);
2227 ce->ce_file = add(cp, ce->ce_file);
2228 if (rename(file_org, ce->ce_file)) {
2229 adios(ce->ce_file, "unable to rename %s to ",
2235 ce->ce_file = add(cp, ce->ce_file);
2239 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2240 content_error(ce->ce_file, ct,
2241 "unable to fopen for reading/writing");
2245 if (ct->c_type == CT_MULTIPART) {
2247 CI ci = &ct->c_ctinfo;
2250 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2252 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2253 strlen(ci->ci_subtype);
2254 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2255 putc(';', ce->ce_fp);
2258 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2261 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2262 fputs("\n\t", ce->ce_fp);
2265 putc(' ', ce->ce_fp);
2268 fprintf(ce->ce_fp, "%s", buffer);
2272 if (ci->ci_comment) {
2273 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2275 fputs("\n\t", ce->ce_fp);
2278 putc(' ', ce->ce_fp);
2281 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2284 fprintf(ce->ce_fp, "\n");
2286 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2288 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2290 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2291 fprintf(ce->ce_fp, "\n");
2294 if ((len = ct->c_end - ct->c_begin) < 0)
2295 adios(NULL, "internal error(3)");
2298 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2299 content_error(ct->c_file, ct,
2300 "unable to open for reading");
2306 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2308 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2310 content_error(ct->c_file, ct, "error reading from");
2314 content_error(NULL, ct, "premature eof");
2322 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2323 if (ferror(ce->ce_fp)) {
2324 content_error(ce->ce_file, ct,
2325 "error writing to");
2330 fseek(ct->c_fp, 0L, SEEK_SET);
2332 if (fflush(ce->ce_fp)) {
2333 content_error(ce->ce_file, ct, "error writing to");
2337 fseek(ce->ce_fp, 0L, SEEK_SET);
2340 *file = ce->ce_file;
2345 return fileno(ce->ce_fp);
2348 free_encoding(ct, 0);
2362 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2364 char cachefile[BUFSIZ];
2367 fseek(ce->ce_fp, 0L, SEEK_SET);
2372 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2373 content_error(ce->ce_file, ct,
2374 "unable to fopen for reading");
2380 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2381 cachefile, sizeof(cachefile)) != NOTOK) {
2382 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2383 ce->ce_file = getcpy(cachefile);
2387 admonish(cachefile, "unable to fopen for reading");
2394 *file = ce->ce_file;
2395 *fd = fileno(ce->ce_fp);
2406 return init_encoding(ct, openFile);
2411 openFile(CT ct, char **file)
2414 char cachefile[BUFSIZ];
2415 struct exbody *e = ct->c_ctexbody;
2416 CE ce = ct->c_cefile;
2418 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2428 content_error(NULL, ct, "missing name parameter");
2432 ce->ce_file = getcpy(e->eb_name);
2435 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2436 content_error(ce->ce_file, ct, "unable to fopen for reading");
2440 if ((!e->eb_permission ||
2441 mh_strcasecmp(e->eb_permission, "read-write")) &&
2442 find_cache(NULL, wcachesw, &cachetype,
2443 e->eb_content->c_id, cachefile, sizeof(cachefile))
2448 mask = umask(cachetype ? ~m_gmprot() : 0222);
2449 if ((fp = fopen(cachefile, "w"))) {
2451 char buffer[BUFSIZ];
2452 FILE *gp = ce->ce_fp;
2454 fseek(gp, 0L, SEEK_SET);
2456 while ((cc = fread(buffer, sizeof(*buffer),
2457 sizeof(buffer), gp)) > 0)
2458 fwrite(buffer, sizeof(*buffer), cc, fp);
2462 admonish(ce->ce_file, "error reading");
2464 } else if (ferror(fp)) {
2465 admonish(cachefile, "error writing");
2473 fseek(ce->ce_fp, 0L, SEEK_SET);
2474 *file = ce->ce_file;
2475 return fileno(ce->ce_fp);
2485 return init_encoding(ct, openFTP);
2490 openFTP(CT ct, char **file)
2492 int cachetype, caching, fd;
2494 char *bp, *ftp, *user, *pass;
2495 char buffer[BUFSIZ], cachefile[BUFSIZ];
2498 static char *username = NULL;
2499 static char *password = NULL;
2506 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2512 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2521 if (!e->eb_name || !e->eb_site) {
2522 content_error(NULL, ct, "missing %s parameter",
2523 e->eb_name ? "site": "name");
2530 pidcheck(pidwait(xpid, NOTOK));
2534 /* Get the buffer ready to go */
2536 buflen = sizeof(buffer);
2539 ** Construct the query message for user
2541 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2547 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2553 snprintf(bp, buflen, "\n using %sFTP from site %s",
2554 e->eb_flags ? "anonymous " : "", e->eb_site);
2559 if (e->eb_size > 0) {
2560 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2565 snprintf(bp, buflen, "? ");
2568 ** Now, check the answer
2570 if (!getanswer(buffer))
2575 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2579 ruserpass(e->eb_site, &username, &password);
2584 ce->ce_unlink = (*file == NULL);
2586 cachefile[0] = '\0';
2587 if ((!e->eb_permission ||
2588 mh_strcasecmp(e->eb_permission, "read-write")) &&
2589 find_cache(NULL, wcachesw, &cachetype,
2590 e->eb_content->c_id, cachefile, sizeof(cachefile))
2592 if (*file == NULL) {
2599 ce->ce_file = getcpy(*file);
2601 ce->ce_file = getcpy(cachefile);
2603 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2605 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2606 content_error (ce->ce_file, ct,
2607 "unable to fopen for reading/writing");
2612 vec[vecp++] = mhbasename(ftp);
2613 vec[vecp++] = e->eb_site;
2616 vec[vecp++] = e->eb_dir;
2617 vec[vecp++] = e->eb_name;
2618 vec[vecp++] = ce->ce_file,
2619 vec[vecp++] = e->eb_mode &&
2620 !mh_strcasecmp(e->eb_mode, "ascii") ?
2626 switch (child_id = fork()) {
2628 adios("fork", "unable to");
2632 close(fileno(ce->ce_fp));
2634 fprintf(stderr, "unable to exec ");
2640 if (pidXwait(child_id, NULL)) {
2641 username = password = NULL;
2650 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2655 mask = umask(cachetype ? ~m_gmprot() : 0222);
2656 if ((fp = fopen(cachefile, "w"))) {
2658 FILE *gp = ce->ce_fp;
2660 fseek(gp, 0L, SEEK_SET);
2662 while ((cc= fread(buffer, sizeof(*buffer),
2663 sizeof(buffer), gp)) > 0)
2664 fwrite(buffer, sizeof(*buffer), cc, fp);
2668 admonish(ce->ce_file, "error reading");
2670 } else if (ferror(fp)) {
2671 admonish(cachefile, "error writing");
2680 fseek(ce->ce_fp, 0L, SEEK_SET);
2681 *file = ce->ce_file;
2682 return fileno(ce->ce_fp);
2693 return init_encoding(ct, openMail);
2698 openMail(CT ct, char **file)
2700 int child_id, fd, vecp;
2702 char *bp, buffer[BUFSIZ], *vec[7];
2703 struct exbody *e = ct->c_ctexbody;
2704 CE ce = ct->c_cefile;
2706 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2715 if (!e->eb_server) {
2716 content_error(NULL, ct, "missing server parameter");
2723 pidcheck(pidwait(xpid, NOTOK));
2727 /* Get buffer ready to go */
2729 buflen = sizeof(buffer);
2731 /* Now, construct query message */
2732 snprintf(bp, buflen, "Retrieve content");
2738 snprintf(bp, buflen, " %s", e->eb_partno);
2744 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2745 e->eb_subject ? e->eb_subject : e->eb_body);
2747 /* Now, check answer */
2748 if (!getanswer(buffer))
2752 vec[vecp++] = "mhmail";
2753 vec[vecp++] = e->eb_server;
2754 vec[vecp++] = "-subject";
2755 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2756 vec[vecp++] = "-body";
2757 vec[vecp++] = e->eb_body;
2760 switch (child_id = fork()) {
2762 advise("fork", "unable to");
2767 fprintf(stderr, "unable to exec ");
2773 if (pidXwait(child_id, NULL) == OK)
2774 advise(NULL, "request sent");
2778 if (*file == NULL) {
2779 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2782 ce->ce_file = getcpy(*file);
2786 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2787 content_error(ce->ce_file, ct,
2788 "unable to fopen for reading/writing");
2793 ** showproc is for mhshow and mhstore, though mhlist -debug
2797 free(ct->c_showproc);
2798 ct->c_showproc = getcpy("true");
2800 fseek(ce->ce_fp, 0L, SEEK_SET);
2801 *file = ce->ce_file;
2802 return fileno(ce->ce_fp);
2807 readDigest(CT ct, char *cp)
2812 unsigned char *dp, value, *ep;
2813 unsigned char *b, *b1, *b2, *b3;
2815 b = (unsigned char *) &bits,
2816 b1 = &b[endian > 0 ? 1 : 2],
2817 b2 = &b[endian > 0 ? 2 : 1],
2818 b3 = &b[endian > 0 ? 3 : 0];
2823 for (ep = (dp = ct->c_digest)
2824 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2827 if (skip || (*cp & 0x80) ||
2828 (value = b642nib[*cp & 0x7f])
2831 fprintf(stderr, "invalid BASE64 encoding\n");
2835 bits |= value << bitno;
2837 if ((bitno -= 6) < 0) {
2838 if (dp + (3 - skip) > ep)
2839 goto invalid_digest;
2854 goto self_delimiting;
2859 fprintf(stderr, "premature ending (bitno %d)\n",
2870 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2878 fprintf(stderr, "MD5 digest=");
2879 for (dp = ct->c_digest; dp < ep; dp++)
2880 fprintf(stderr, "%02x", *dp & 0xff);
2881 fprintf(stderr, "\n");