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;
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)");
1775 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1776 content_error(ct->c_file, ct, "unable to open for reading");
1780 if ((digested = ct->c_digested))
1781 MD5Init(&mdContext);
1787 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1789 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1791 content_error(ct->c_file, ct, "error reading from");
1795 content_error(NULL, ct, "premature eof");
1803 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1808 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1810 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1812 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1816 bits |= value << bitno;
1818 if ((bitno -= 6) < 0) {
1819 putc((char) *b1, ce->ce_fp);
1821 MD5Update(&mdContext, b1, 1);
1823 putc((char) *b2, ce->ce_fp);
1825 MD5Update(&mdContext, b2, 1);
1827 putc((char) *b3, ce->ce_fp);
1829 MD5Update(&mdContext, b3, 1);
1833 if (ferror(ce->ce_fp)) {
1834 content_error(ce->ce_file, ct,
1835 "error writing to");
1838 bitno = 18, bits = 0L, skip = 0;
1844 goto self_delimiting;
1853 fprintf(stderr, "premature ending (bitno %d)\n",
1856 content_error(NULL, ct, "invalid BASE64 encoding");
1861 fseek(ct->c_fp, 0L, SEEK_SET);
1863 if (fflush(ce->ce_fp)) {
1864 content_error(ce->ce_file, ct, "error writing to");
1869 unsigned char digest[16];
1871 MD5Final(digest, &mdContext);
1872 if (memcmp((char *) digest, (char *) ct->c_digest,
1873 sizeof(digest) / sizeof(digest[0])))
1874 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1876 fprintf(stderr, "content integrity confirmed\n");
1879 fseek(ce->ce_fp, 0L, SEEK_SET);
1882 *file = ce->ce_file;
1883 return fileno(ce->ce_fp);
1886 free_encoding(ct, 0);
1895 static char hex2nib[0x80] = {
1896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1902 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1903 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 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
1918 return init_encoding(ct, openQuoted);
1923 openQuoted(CT ct, char **file)
1925 int cc, digested, len, quoted;
1926 unsigned char *cp, *ep;
1927 char buffer[BUFSIZ];
1930 /* sbeck -- handle suffixes */
1936 fseek(ce->ce_fp, 0L, SEEK_SET);
1941 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1942 content_error(ce->ce_file, ct,
1943 "unable to fopen for reading");
1949 if (*file == NULL) {
1950 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1953 ce->ce_file = getcpy(*file);
1957 /* sbeck@cise.ufl.edu -- handle suffixes */
1959 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1960 invo_name, ci->ci_type, ci->ci_subtype);
1961 cp = context_find(buffer);
1962 if (cp == NULL || *cp == '\0') {
1963 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1965 cp = context_find(buffer);
1967 if (cp != NULL && *cp != '\0') {
1968 if (ce->ce_unlink) {
1969 // Temporary file already exists, so we rename to
1970 // version with extension.
1971 char *file_org = strdup(ce->ce_file);
1972 ce->ce_file = add(cp, ce->ce_file);
1973 if (rename(file_org, ce->ce_file)) {
1974 adios(ce->ce_file, "unable to rename %s to ",
1980 ce->ce_file = add(cp, ce->ce_file);
1984 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1985 content_error(ce->ce_file, ct,
1986 "unable to fopen for reading/writing");
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 ((len = ct->c_end - ct->c_begin) < 0)
1997 adios(NULL, "internal error(2)");
1999 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2000 content_error(ct->c_file, ct, "unable to open for reading");
2004 if ((digested = ct->c_digested))
2005 MD5Init(&mdContext);
2012 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2014 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2015 content_error(NULL, ct, "premature eof");
2019 if ((cc = strlen(buffer)) > len)
2023 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2028 for (; cp < ep; cp++) {
2030 /* in an escape sequence */
2032 /* at byte 1 of an escape sequence */
2033 mask = hex2nib[*cp & 0x7f];
2034 /* next is byte 2 */
2037 /* at byte 2 of an escape sequence */
2039 mask |= hex2nib[*cp & 0x7f];
2040 putc(mask, ce->ce_fp);
2042 MD5Update(&mdContext, &mask, 1);
2043 if (ferror(ce->ce_fp)) {
2044 content_error(ce->ce_file, ct, "error writing to");
2048 ** finished escape sequence; next may
2049 ** be literal or a new escape sequence
2053 /* on to next byte */
2057 /* not in an escape sequence */
2060 ** starting an escape sequence,
2063 if (cp + 1 < ep && cp[1] == '\n') {
2064 /* "=\n" soft line break, eat the \n */
2068 if (cp + 1 >= ep || cp + 2 >= ep) {
2070 ** We don't have 2 bytes left,
2071 ** so this is an invalid escape
2072 ** sequence; just show the raw bytes
2075 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2077 ** Next 2 bytes are hex digits,
2078 ** making this a valid escape
2079 ** sequence; let's decode it (above).
2085 ** One or both of the next 2 is
2086 ** out of range, making this an
2087 ** invalid escape sequence; just
2088 ** show the raw bytes (below).
2093 /* Just show the raw byte. */
2094 putc(*cp, ce->ce_fp);
2097 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2099 MD5Update(&mdContext, (unsigned char *) cp, 1);
2102 if (ferror(ce->ce_fp)) {
2103 content_error(ce->ce_file, ct,
2104 "error writing to");
2110 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2114 fseek(ct->c_fp, 0L, SEEK_SET);
2116 if (fflush(ce->ce_fp)) {
2117 content_error(ce->ce_file, ct, "error writing to");
2122 unsigned char digest[16];
2124 MD5Final(digest, &mdContext);
2125 if (memcmp((char *) digest, (char *) ct->c_digest,
2126 sizeof(digest) / sizeof(digest[0])))
2127 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2129 fprintf(stderr, "content integrity confirmed\n");
2132 fseek(ce->ce_fp, 0L, SEEK_SET);
2135 *file = ce->ce_file;
2136 return fileno(ce->ce_fp);
2139 free_encoding(ct, 0);
2151 if (init_encoding(ct, open7Bit) == NOTOK)
2154 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2160 open7Bit(CT ct, char **file)
2163 char buffer[BUFSIZ];
2164 /* sbeck -- handle suffixes */
2171 fseek(ce->ce_fp, 0L, SEEK_SET);
2176 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2177 content_error(ce->ce_file, ct,
2178 "unable to fopen for reading");
2184 if (*file == NULL) {
2185 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2188 ce->ce_file = getcpy(*file);
2192 /* sbeck@cise.ufl.edu -- handle suffixes */
2194 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2195 invo_name, ci->ci_type, ci->ci_subtype);
2196 cp = context_find(buffer);
2197 if (cp == NULL || *cp == '\0') {
2198 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2200 cp = context_find(buffer);
2202 if (cp != NULL && *cp != '\0') {
2203 if (ce->ce_unlink) {
2205 ** Temporary file already exists, so we rename to
2206 ** version with extension.
2208 char *file_org = strdup(ce->ce_file);
2209 ce->ce_file = add(cp, ce->ce_file);
2210 if (rename(file_org, ce->ce_file)) {
2211 adios(ce->ce_file, "unable to rename %s to ",
2217 ce->ce_file = add(cp, ce->ce_file);
2221 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2222 content_error(ce->ce_file, ct,
2223 "unable to fopen for reading/writing");
2227 if (ct->c_type == CT_MULTIPART) {
2229 CI ci = &ct->c_ctinfo;
2232 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2234 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2235 strlen(ci->ci_subtype);
2236 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2237 putc(';', ce->ce_fp);
2240 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2243 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2244 fputs("\n\t", ce->ce_fp);
2247 putc(' ', ce->ce_fp);
2250 fprintf(ce->ce_fp, "%s", buffer);
2254 if (ci->ci_comment) {
2255 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2257 fputs("\n\t", ce->ce_fp);
2260 putc(' ', ce->ce_fp);
2263 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2266 fprintf(ce->ce_fp, "\n");
2268 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2270 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2272 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2273 fprintf(ce->ce_fp, "\n");
2276 if ((len = ct->c_end - ct->c_begin) < 0)
2277 adios(NULL, "internal error(3)");
2279 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2280 content_error(ct->c_file, ct, "unable to open for reading");
2284 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2286 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2288 content_error(ct->c_file, ct, "error reading from");
2292 content_error(NULL, ct, "premature eof");
2300 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2301 if (ferror(ce->ce_fp)) {
2302 content_error(ce->ce_file, ct,
2303 "error writing to");
2308 fseek(ct->c_fp, 0L, SEEK_SET);
2310 if (fflush(ce->ce_fp)) {
2311 content_error(ce->ce_file, ct, "error writing to");
2315 fseek(ce->ce_fp, 0L, SEEK_SET);
2318 *file = ce->ce_file;
2319 return fileno(ce->ce_fp);
2322 free_encoding(ct, 0);
2332 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2334 char cachefile[BUFSIZ];
2337 fseek(ce->ce_fp, 0L, SEEK_SET);
2342 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2343 content_error(ce->ce_file, ct,
2344 "unable to fopen for reading");
2350 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2351 cachefile, sizeof(cachefile)) != NOTOK) {
2352 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2353 ce->ce_file = getcpy(cachefile);
2357 admonish(cachefile, "unable to fopen for reading");
2364 *file = ce->ce_file;
2365 *fd = fileno(ce->ce_fp);
2376 return init_encoding(ct, openFile);
2381 openFile(CT ct, char **file)
2384 char cachefile[BUFSIZ];
2385 struct exbody *e = ct->c_ctexbody;
2386 CE ce = ct->c_cefile;
2388 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2398 content_error(NULL, ct, "missing name parameter");
2402 ce->ce_file = getcpy(e->eb_name);
2405 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2406 content_error(ce->ce_file, ct, "unable to fopen for reading");
2410 if ((!e->eb_permission ||
2411 mh_strcasecmp(e->eb_permission, "read-write")) &&
2412 find_cache(NULL, wcachesw, &cachetype,
2413 e->eb_content->c_id, cachefile, sizeof(cachefile))
2418 mask = umask(cachetype ? ~m_gmprot() : 0222);
2419 if ((fp = fopen(cachefile, "w"))) {
2421 char buffer[BUFSIZ];
2422 FILE *gp = ce->ce_fp;
2424 fseek(gp, 0L, SEEK_SET);
2426 while ((cc = fread(buffer, sizeof(*buffer),
2427 sizeof(buffer), gp)) > 0)
2428 fwrite(buffer, sizeof(*buffer), cc, fp);
2432 admonish(ce->ce_file, "error reading");
2434 } else if (ferror(fp)) {
2435 admonish(cachefile, "error writing");
2443 fseek(ce->ce_fp, 0L, SEEK_SET);
2444 *file = ce->ce_file;
2445 return fileno(ce->ce_fp);
2455 return init_encoding(ct, openFTP);
2460 openFTP(CT ct, char **file)
2462 int cachetype, caching, fd;
2464 char *bp, *ftp, *user, *pass;
2465 char buffer[BUFSIZ], cachefile[BUFSIZ];
2468 static char *username = NULL;
2469 static char *password = NULL;
2476 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2482 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2491 if (!e->eb_name || !e->eb_site) {
2492 content_error(NULL, ct, "missing %s parameter",
2493 e->eb_name ? "site": "name");
2500 pidcheck(pidwait(xpid, NOTOK));
2504 /* Get the buffer ready to go */
2506 buflen = sizeof(buffer);
2509 ** Construct the query message for user
2511 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2517 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2523 snprintf(bp, buflen, "\n using %sFTP from site %s",
2524 e->eb_flags ? "anonymous " : "", e->eb_site);
2529 if (e->eb_size > 0) {
2530 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2535 snprintf(bp, buflen, "? ");
2538 ** Now, check the answer
2540 if (!getanswer(buffer))
2545 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2549 ruserpass(e->eb_site, &username, &password);
2554 ce->ce_unlink = (*file == NULL);
2556 cachefile[0] = '\0';
2557 if ((!e->eb_permission ||
2558 mh_strcasecmp(e->eb_permission, "read-write")) &&
2559 find_cache(NULL, wcachesw, &cachetype,
2560 e->eb_content->c_id, cachefile, sizeof(cachefile))
2562 if (*file == NULL) {
2569 ce->ce_file = getcpy(*file);
2571 ce->ce_file = getcpy(cachefile);
2573 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2575 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2576 content_error (ce->ce_file, ct,
2577 "unable to fopen for reading/writing");
2582 vec[vecp++] = mhbasename(ftp);
2583 vec[vecp++] = e->eb_site;
2586 vec[vecp++] = e->eb_dir;
2587 vec[vecp++] = e->eb_name;
2588 vec[vecp++] = ce->ce_file,
2589 vec[vecp++] = e->eb_mode &&
2590 !mh_strcasecmp(e->eb_mode, "ascii") ?
2596 switch (child_id = fork()) {
2598 adios("fork", "unable to");
2602 close(fileno(ce->ce_fp));
2604 fprintf(stderr, "unable to exec ");
2610 if (pidXwait(child_id, NULL)) {
2611 username = password = NULL;
2620 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2625 mask = umask(cachetype ? ~m_gmprot() : 0222);
2626 if ((fp = fopen(cachefile, "w"))) {
2628 FILE *gp = ce->ce_fp;
2630 fseek(gp, 0L, SEEK_SET);
2632 while ((cc= fread(buffer, sizeof(*buffer),
2633 sizeof(buffer), gp)) > 0)
2634 fwrite(buffer, sizeof(*buffer), cc, fp);
2638 admonish(ce->ce_file, "error reading");
2640 } else if (ferror(fp)) {
2641 admonish(cachefile, "error writing");
2650 fseek(ce->ce_fp, 0L, SEEK_SET);
2651 *file = ce->ce_file;
2652 return fileno(ce->ce_fp);
2663 return init_encoding(ct, openMail);
2668 openMail(CT ct, char **file)
2670 int child_id, fd, vecp;
2672 char *bp, buffer[BUFSIZ], *vec[7];
2673 struct exbody *e = ct->c_ctexbody;
2674 CE ce = ct->c_cefile;
2676 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2685 if (!e->eb_server) {
2686 content_error(NULL, ct, "missing server parameter");
2693 pidcheck(pidwait(xpid, NOTOK));
2697 /* Get buffer ready to go */
2699 buflen = sizeof(buffer);
2701 /* Now, construct query message */
2702 snprintf(bp, buflen, "Retrieve content");
2708 snprintf(bp, buflen, " %s", e->eb_partno);
2714 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2715 e->eb_subject ? e->eb_subject : e->eb_body);
2717 /* Now, check answer */
2718 if (!getanswer(buffer))
2722 vec[vecp++] = "mhmail";
2723 vec[vecp++] = e->eb_server;
2724 vec[vecp++] = "-subject";
2725 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2726 vec[vecp++] = "-body";
2727 vec[vecp++] = e->eb_body;
2730 switch (child_id = fork()) {
2732 advise("fork", "unable to");
2737 fprintf(stderr, "unable to exec ");
2743 if (pidXwait(child_id, NULL) == OK)
2744 advise(NULL, "request sent");
2748 if (*file == NULL) {
2749 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2752 ce->ce_file = getcpy(*file);
2756 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2757 content_error(ce->ce_file, ct,
2758 "unable to fopen for reading/writing");
2763 ** showproc is for mhshow and mhstore, though mhlist -debug
2767 free(ct->c_showproc);
2768 ct->c_showproc = getcpy("true");
2770 fseek(ce->ce_fp, 0L, SEEK_SET);
2771 *file = ce->ce_file;
2772 return fileno(ce->ce_fp);
2777 readDigest(CT ct, char *cp)
2782 unsigned char *dp, value, *ep;
2783 unsigned char *b, *b1, *b2, *b3;
2785 b = (unsigned char *) &bits,
2786 b1 = &b[endian > 0 ? 1 : 2],
2787 b2 = &b[endian > 0 ? 2 : 1],
2788 b3 = &b[endian > 0 ? 3 : 0];
2793 for (ep = (dp = ct->c_digest)
2794 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2797 if (skip || (*cp & 0x80) ||
2798 (value = b642nib[*cp & 0x7f])
2801 fprintf(stderr, "invalid BASE64 encoding\n");
2805 bits |= value << bitno;
2807 if ((bitno -= 6) < 0) {
2808 if (dp + (3 - skip) > ep)
2809 goto invalid_digest;
2824 goto self_delimiting;
2829 fprintf(stderr, "premature ending (bitno %d)\n",
2840 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2848 fprintf(stderr, "MD5 digest=");
2849 for (dp = ct->c_digest; dp < ep; dp++)
2850 fprintf(stderr, "%02x", *dp & 0xff);
2851 fprintf(stderr, "\n");