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>
21 #ifdef HAVE_SYS_WAIT_H
22 # include <sys/wait.h>
28 extern int endian; /* mhmisc.c */
30 extern pid_t xpid; /* mhshowsbr.c */
33 extern int rcachesw; /* mhcachesbr.c */
34 extern int wcachesw; /* mhcachesbr.c */
36 int checksw = 0; /* check Content-MD5 field */
39 ** Directory to place temp files. This must
40 ** be set before these routines are called.
45 ** Structures for TEXT messages
47 struct k2v SubText[] = {
48 { "plain", TEXT_PLAIN },
49 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
50 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
51 { NULL, TEXT_UNKNOWN } /* this one must be last! */
54 struct k2v Charset[] = {
55 { "us-ascii", CHARSET_USASCII },
56 { "iso-8859-1", CHARSET_LATIN },
57 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
61 ** Structures for MULTIPART messages
63 struct k2v SubMultiPart[] = {
64 { "mixed", MULTI_MIXED },
65 { "alternative", MULTI_ALTERNATE },
66 { "digest", MULTI_DIGEST },
67 { "parallel", MULTI_PARALLEL },
68 { NULL, MULTI_UNKNOWN } /* this one must be last! */
72 ** Structures for MESSAGE messages
74 struct k2v SubMessage[] = {
75 { "rfc822", MESSAGE_RFC822 },
76 { "partial", MESSAGE_PARTIAL },
77 { "external-body", MESSAGE_EXTERNAL },
78 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
82 ** Structure for APPLICATION messages
84 struct k2v SubApplication[] = {
85 { "octet-stream", APPLICATION_OCTETS },
86 { "postscript", APPLICATION_POSTSCRIPT },
87 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
92 int find_cache(CT, int, int *, char *, char *, int);
97 int make_intermediates(char *);
98 void content_error(char *, CT, char *, ...);
101 void free_content(CT);
102 void free_encoding(CT, int);
107 static CT get_content(FILE *, char *, int);
108 static int get_comment(CT, unsigned char **, int);
110 static int InitGeneric(CT);
111 static int InitText(CT);
112 static int InitMultiPart(CT);
113 static void reverse_parts(CT);
114 static int InitMessage(CT);
115 static int InitApplication(CT);
116 static int init_encoding(CT, OpenCEFunc);
117 static unsigned long size_encoding(CT);
118 static int InitBase64(CT);
119 static int openBase64(CT, char **);
120 static int InitQuoted(CT);
121 static int openQuoted(CT, char **);
122 static int Init7Bit(CT);
123 static int openExternal(CT, CT, CE, char **, int *);
124 static int InitFile(CT);
125 static int openFile(CT, char **);
126 static int InitFTP(CT);
127 static int openFTP(CT, char **);
128 static int InitMail(CT);
129 static int openMail(CT, char **);
130 static int readDigest(CT, char *);
132 struct str2init str2cts[] = {
133 { "application", CT_APPLICATION, InitApplication },
134 { "audio", CT_AUDIO, InitGeneric },
135 { "image", CT_IMAGE, InitGeneric },
136 { "message", CT_MESSAGE, InitMessage },
137 { "multipart", CT_MULTIPART, InitMultiPart },
138 { "text", CT_TEXT, InitText },
139 { "video", CT_VIDEO, InitGeneric },
140 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
141 { NULL, CT_UNKNOWN, NULL },
144 struct str2init str2ces[] = {
145 { "base64", CE_BASE64, InitBase64 },
146 { "quoted-printable", CE_QUOTED, InitQuoted },
147 { "8bit", CE_8BIT, Init7Bit },
148 { "7bit", CE_7BIT, Init7Bit },
149 { "binary", CE_BINARY, Init7Bit },
150 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
151 { NULL, CE_UNKNOWN, NULL },
155 ** NOTE WELL: si_key MUST NOT have value of NOTOK
157 ** si_key is 1 if access method is anonymous.
159 struct str2init str2methods[] = {
160 { "afs", 1, InitFile },
161 { "anon-ftp", 1, InitFTP },
162 { "ftp", 0, InitFTP },
163 { "local-file", 0, InitFile },
164 { "mail-server", 0, InitMail },
172 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
183 ** Main entry point for parsing a MIME message or file.
184 ** It returns the Content structure for the top level
185 ** entity in the file.
188 parse_mime(char *file)
196 ** Check if file is actually standard input
198 if ((is_stdin = (strcmp(file, "-")==0))) {
199 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
201 advise("mhparse", "unable to create temporary file");
204 file = getcpy(tfile);
207 while (fgets(buffer, sizeof(buffer), stdin))
213 advise("stdin", "error reading");
218 advise(file, "error writing");
221 fseek(fp, 0L, SEEK_SET);
222 } else if ((fp = fopen(file, "r")) == NULL) {
223 advise(file, "unable to read");
227 if (!(ct = get_content(fp, file, 1))) {
230 advise(NULL, "unable to decode %s", file);
235 ct->c_unlink = 1; /* temp file to remove */
239 if (ct->c_end == 0L) {
240 fseek(fp, 0L, SEEK_END);
241 ct->c_end = ftell(fp);
244 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
256 ** Main routine for reading/parsing the headers
257 ** of a message content.
259 ** toplevel = 1 # we are at the top level of the message
260 ** toplevel = 0 # we are inside message type or multipart type
261 ** # other than multipart/digest
262 ** toplevel = -1 # we are inside multipart/digest
263 ** NB: on failure we will fclose(in)!
267 get_content(FILE *in, char *file, int toplevel)
270 char buf[BUFSIZ], name[NAMESZ];
275 /* allocate the content structure */
276 if (!(ct = (CT) calloc(1, sizeof(*ct))))
277 adios(NULL, "out of memory");
280 ct->c_file = getcpy(file);
281 ct->c_begin = ftell(ct->c_fp) + 1;
284 ** Parse the header fields for this
285 ** content into a linked list.
287 for (compnum = 1, state = FLD;;) {
288 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
294 /* get copies of the buffers */
298 /* if necessary, get rest of field */
299 while (state == FLDPLUS) {
300 state = m_getfld(state, name, buf,
302 vp = add(buf, vp); /* add to previous value */
305 /* Now add the header data to the list */
306 add_header(ct, np, vp);
308 /* continue, if this isn't the last header field */
309 if (state != FLDEOF) {
310 ct->c_begin = ftell(in) + 1;
317 ct->c_begin = ftell(in) - strlen(buf);
321 ct->c_begin = ftell(in);
326 adios(NULL, "message format error in component #%d",
330 adios(NULL, "getfld() returned %d", state);
333 /* break out of the loop */
338 ** Read the content headers. We will parse the
339 ** MIME related header fields into their various
340 ** structures and set internal flags related to
341 ** content type/subtype, etc.
344 hp = ct->c_first_hf; /* start at first header field */
346 /* Get MIME-Version field */
347 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
350 unsigned char *cp, *dp;
353 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
356 ct->c_vrsn = getcpy(hp->value);
358 /* Now, cleanup this field */
363 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
365 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
370 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
372 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
375 for (dp = cp; istoken(*dp); dp++)
379 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
382 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
385 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
386 /* Get Content-Type field */
387 struct str2init *s2i;
388 CI ci = &ct->c_ctinfo;
390 /* Check if we've already seen a Content-Type header */
392 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
396 /* Parse the Content-Type field */
397 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
401 ** Set the Init function and the internal
402 ** flag for this content type.
404 for (s2i = str2cts; s2i->si_key; s2i++)
405 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
407 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
409 ct->c_type = s2i->si_val;
410 ct->c_ctinitfnx = s2i->si_init;
412 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
413 /* Get Content-Transfer-Encoding field */
415 unsigned char *cp, *dp;
416 struct str2init *s2i;
419 ** Check if we've already seen the
420 ** Content-Transfer-Encoding field
423 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
427 /* get copy of this field */
428 ct->c_celine = cp = getcpy(hp->value);
432 for (dp = cp; istoken(*dp); dp++)
438 ** Find the internal flag and Init function
439 ** for this transfer encoding.
441 for (s2i = str2ces; s2i->si_key; s2i++)
442 if (!mh_strcasecmp(cp, s2i->si_key))
444 if (!s2i->si_key && !uprf(cp, "X-"))
447 ct->c_encoding = s2i->si_val;
449 /* Call the Init function for this encoding */
450 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
453 } else if (!mh_strcasecmp(hp->name, MD5_FIELD)) {
454 /* Get Content-MD5 field */
455 unsigned char *cp, *dp;
461 if (ct->c_digested) {
462 advise(NULL, "message %s has multiple %s: fields", ct->c_file, MD5_FIELD);
466 ep = cp = getcpy(hp->value);
470 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
472 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
477 fprintf(stderr, "%s: %s\n", MD5_FIELD, cp);
479 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK) {
484 for (dp = cp; *dp && !isspace(*dp); dp++)
492 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
493 /* Get Content-ID field */
494 ct->c_id = add(hp->value, ct->c_id);
496 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
497 /* Get Content-Description field */
498 ct->c_descr = add(hp->value, ct->c_descr);
500 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
501 /* Get Content-Disposition field */
502 ct->c_dispo = add(hp->value, ct->c_dispo);
506 hp = hp->next; /* next header field */
510 ** Check if we saw a Content-Type field.
511 ** If not, then assign a default value for
512 ** it, and the Init function.
516 ** If we are inside a multipart/digest message,
517 ** so default type is message/rfc822
520 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
522 ct->c_type = CT_MESSAGE;
523 ct->c_ctinitfnx = InitMessage;
526 ** Else default type is text/plain
528 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
530 ct->c_type = CT_TEXT;
531 ct->c_ctinitfnx = InitText;
535 /* Use default Transfer-Encoding, if necessary */
537 ct->c_encoding = CE_7BIT;
550 ** small routine to add header field to list
554 add_header(CT ct, char *name, char *value)
558 /* allocate header field structure */
559 hp = mh_xmalloc(sizeof(*hp));
561 /* link data into header structure */
566 /* link header structure into the list */
567 if (ct->c_first_hf == NULL) {
568 ct->c_first_hf = hp; /* this is the first */
571 ct->c_last_hf->next = hp; /* add it to the end */
580 ** Make sure that buf contains at least one appearance of name,
581 ** followed by =. If not, insert both name and value, just after
582 ** first semicolon, if any. Note that name should not contain a
583 ** trailing =. And quotes will be added around the value. Typical
584 ** usage: make sure that a Content-Disposition header contains
585 ** filename="foo". If it doesn't and value does, use value from
589 incl_name_value(unsigned char *buf, char *name, char *value) {
592 /* Assume that name is non-null. */
594 char *name_plus_equal = concat(name, "=", NULL);
596 if (!strstr(buf, name_plus_equal)) {
599 char *prefix, *suffix;
601 /* Trim trailing space, esp. newline. */
602 for (cp = &buf[strlen(buf) - 1];
603 cp >= buf && isspace(*cp); --cp) {
607 insertion = concat("; ", name, "=", "\"", value, "\"",
611 ** Insert at first semicolon, if any.
612 ** If none, append to end.
614 prefix = getcpy(buf);
615 if ((cp = strchr(prefix, ';'))) {
616 suffix = concat(cp, NULL);
618 newbuf = concat(prefix, insertion, suffix,
623 newbuf = concat(buf, insertion, "\n", NULL);
631 free(name_plus_equal);
638 ** Extract just name_suffix="foo", if any, from value. If there isn't
639 ** one, return the entire value. Note that, for example, a name_suffix
640 ** of name will match filename="foo", and return foo.
643 extract_name_value(char *name_suffix, char *value) {
644 char *extracted_name_value = value;
645 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
646 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
649 free(name_suffix_plus_quote);
650 if (name_suffix_equals) {
651 char *name_suffix_begin;
654 for (cp = name_suffix_equals; *cp != '"'; ++cp)
656 name_suffix_begin = ++cp;
657 /* Find second \". */
658 for (; *cp != '"'; ++cp)
661 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
662 memcpy(extracted_name_value, name_suffix_begin,
663 cp - name_suffix_begin);
664 extracted_name_value[cp - name_suffix_begin] = '\0';
667 return extracted_name_value;
671 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
672 ** directives. Fills in the information of the CTinfo structure.
675 get_ctinfo(unsigned char *cp, CT ct, int magic)
684 i = strlen(invo_name) + 2;
686 /* store copy of Content-Type line */
687 cp = ct->c_ctline = getcpy(cp);
689 while (isspace(*cp)) /* trim leading spaces */
692 /* change newlines to spaces */
693 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
696 /* trim trailing spaces */
697 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
703 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
705 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
708 for (dp = cp; istoken(*dp); dp++)
711 ci->ci_type = getcpy(cp); /* store content type */
715 advise(NULL, "invalid %s: field in message %s (empty type)",
716 TYPE_FIELD, ct->c_file);
720 /* down case the content type string */
721 for (dp = ci->ci_type; *dp; dp++)
722 if (isalpha(*dp) && isupper(*dp))
728 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
733 ci->ci_subtype = getcpy("");
741 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
744 for (dp = cp; istoken(*dp); dp++)
747 ci->ci_subtype = getcpy(cp); /* store the content subtype */
750 if (!*ci->ci_subtype) {
751 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
755 /* down case the content subtype string */
756 for (dp = ci->ci_subtype; *dp; dp++)
757 if (isalpha(*dp) && isupper(*dp))
764 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
768 ** Parse attribute/value pairs given with Content-Type
770 ep = (ap = ci->ci_attrs) + NPARMS;
776 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
784 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
788 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
792 /* down case the attribute name */
793 for (dp = cp; istoken(*dp); dp++)
794 if (isalpha(*dp) && isupper(*dp))
797 for (up = dp; isspace(*dp);)
799 if (dp == cp || *dp != '=') {
800 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);
804 vp = (*ap = getcpy(cp)) + (up - cp);
806 for (dp++; isspace(*dp);)
809 /* now add the attribute value */
810 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
813 for (cp = ++dp, dp = vp;;) {
817 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
822 if ((c = *cp++) == '\0')
837 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
842 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
850 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
855 ** Get any <Content-Id> given in buffer
857 if (magic && *cp == '<') {
862 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
863 advise(NULL, "invalid ID in message %s", ct->c_file);
869 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
880 ** Get any [Content-Description] given in buffer.
882 if (magic && *cp == '[') {
884 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
888 advise(NULL, "invalid description in message %s",
897 ct->c_descr = concat(ct->c_descr, "\n", NULL);
908 ** Get any {Content-Disposition} given in buffer.
910 if (magic && *cp == '{') {
912 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
916 advise(NULL, "invalid disposition in message %s",
925 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
936 ** Check if anything is left over
940 ci->ci_magic = getcpy(cp);
943 ** If there is a Content-Disposition header and
944 ** it doesn't have a *filename=, extract it from
945 ** the magic contents. The mhbasename call skips
946 ** any leading directory components.
949 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
951 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
959 get_comment(CT ct, unsigned char **ap, int istype)
964 char c, buffer[BUFSIZ], *dp;
976 advise(NULL, "invalid comment in message %s's %s: field",
977 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
982 if ((c = *cp++) == '\0')
1005 if ((dp = ci->ci_comment)) {
1006 ci->ci_comment = concat(dp, " ", buffer, NULL);
1009 ci->ci_comment = getcpy(buffer);
1013 while (isspace(*cp))
1024 ** Handles content types audio, image, and video.
1025 ** There's not much to do right here.
1031 return OK; /* not much to do here */
1042 char buffer[BUFSIZ];
1044 char **ap, **ep, *cp;
1047 CI ci = &ct->c_ctinfo;
1049 /* check for missing subtype */
1050 if (!*ci->ci_subtype)
1051 ci->ci_subtype = add("plain", ci->ci_subtype);
1054 for (kv = SubText; kv->kv_key; kv++)
1055 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1057 ct->c_subtype = kv->kv_value;
1059 /* allocate text character set structure */
1060 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1061 adios(NULL, "out of memory");
1062 ct->c_ctparams = (void *) t;
1064 /* scan for charset parameter */
1065 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1066 if (!mh_strcasecmp(*ap, "charset"))
1069 /* check if content specified a character set */
1071 /* match character set or set to CHARSET_UNKNOWN */
1072 for (kv = Charset; kv->kv_key; kv++) {
1073 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1078 t->tx_charset = kv->kv_value;
1080 t->tx_charset = CHARSET_UNSPECIFIED;
1084 ** If we can not handle character set natively,
1085 ** then check profile for string to modify the
1086 ** terminal or display method.
1088 ** termproc is for mhshow, though mhlist -debug prints it, too.
1090 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1091 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1093 if ((cp = context_find(buffer)))
1094 ct->c_termproc = getcpy(cp);
1106 InitMultiPart(CT ct)
1110 unsigned char *cp, *dp;
1112 char *bp, buffer[BUFSIZ];
1113 struct multipart *m;
1115 struct part *part, **next;
1116 CI ci = &ct->c_ctinfo;
1121 ** The encoding for multipart messages must be either
1122 ** 7bit, 8bit, or binary (per RFC2045).
1124 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1125 && ct->c_encoding != CE_BINARY) {
1126 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);
1131 for (kv = SubMultiPart; kv->kv_key; kv++)
1132 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1134 ct->c_subtype = kv->kv_value;
1137 ** Check for "boundary" parameter, which is
1138 ** required for multipart messages.
1141 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1142 if (!mh_strcasecmp(*ap, "boundary")) {
1148 /* complain if boundary parameter is missing */
1150 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);
1154 /* allocate primary structure for multipart info */
1155 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1156 adios(NULL, "out of memory");
1157 ct->c_ctparams = (void *) m;
1159 /* check if boundary parameter contains only whitespace characters */
1160 for (cp = bp; isspace(*cp); cp++)
1163 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);
1167 /* remove trailing whitespace from boundary parameter */
1168 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1173 /* record boundary separators */
1174 m->mp_start = concat(bp, "\n", NULL);
1175 m->mp_stop = concat(bp, "--\n", NULL);
1177 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1178 advise(ct->c_file, "unable to open for reading");
1182 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1184 next = &m->mp_parts;
1188 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1192 pos += strlen(buffer);
1193 if (buffer[0] != '-' || buffer[1] != '-')
1196 if (strcmp(buffer + 2, m->mp_start)!=0)
1199 if ((part = (struct part *) calloc(1, sizeof(*part)))
1201 adios(NULL, "out of memory");
1203 next = &part->mp_next;
1205 if (!(p = get_content(fp, ct->c_file,
1206 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1213 fseek(fp, pos, SEEK_SET);
1216 if (strcmp(buffer + 2, m->mp_start) == 0) {
1220 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1221 if (p->c_end < p->c_begin)
1222 p->c_begin = p->c_end;
1227 if (strcmp(buffer + 2, m->mp_stop) == 0)
1233 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1234 if (!inout && part) {
1236 p->c_end = ct->c_end;
1238 if (p->c_begin >= p->c_end) {
1239 for (next = &m->mp_parts; *next != part;
1240 next = &((*next)->mp_next))
1244 free((char *) part);
1249 /* reverse the order of the parts for multipart/alternative */
1250 if (ct->c_subtype == MULTI_ALTERNATE)
1254 ** label all subparts with part number, and
1255 ** then initialize the content of the subpart.
1260 char partnam[BUFSIZ];
1263 snprintf(partnam, sizeof(partnam), "%s.",
1265 pp = partnam + strlen(partnam);
1270 for (part = m->mp_parts, partnum = 1; part;
1271 part = part->mp_next, partnum++) {
1274 sprintf(pp, "%d", partnum);
1275 p->c_partno = getcpy(partnam);
1277 /* initialize the content of the subparts */
1278 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1293 ** reverse the order of the parts of a multipart
1297 reverse_parts(CT ct)
1300 struct multipart *m;
1301 struct part **base, **bmp, **next, *part;
1303 m = (struct multipart *) ct->c_ctparams;
1305 /* if only one part, just return */
1306 if (!m->mp_parts || !m->mp_parts->mp_next)
1309 /* count number of parts */
1311 for (part = m->mp_parts; part; part = part->mp_next)
1314 /* allocate array of pointers to the parts */
1315 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1316 adios(NULL, "out of memory");
1319 /* point at all the parts */
1320 for (part = m->mp_parts; part; part = part->mp_next)
1324 /* reverse the order of the parts */
1325 next = &m->mp_parts;
1326 for (bmp--; bmp >= base; bmp--) {
1329 next = &part->mp_next;
1333 /* free array of pointers */
1334 free((char *) base);
1346 CI ci = &ct->c_ctinfo;
1348 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1349 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1353 /* check for missing subtype */
1354 if (!*ci->ci_subtype)
1355 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1358 for (kv = SubMessage; kv->kv_key; kv++)
1359 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1361 ct->c_subtype = kv->kv_value;
1363 switch (ct->c_subtype) {
1364 case MESSAGE_RFC822:
1367 case MESSAGE_PARTIAL:
1372 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1373 adios(NULL, "out of memory");
1374 ct->c_ctparams = (void *) p;
1377 ** scan for parameters "id", "number",
1380 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1381 if (!mh_strcasecmp(*ap, "id")) {
1382 p->pm_partid = getcpy(*ep);
1385 if (!mh_strcasecmp(*ap, "number")) {
1386 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1388 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);
1393 if (!mh_strcasecmp(*ap, "total")) {
1394 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1401 if (!p->pm_partid || !p->pm_partno
1402 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1403 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);
1409 case MESSAGE_EXTERNAL:
1416 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1417 adios(NULL, "out of memory");
1418 ct->c_ctparams = (void *) e;
1420 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1421 advise(ct->c_file, "unable to open for reading");
1425 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1427 if (!(p = get_content(fp, ct->c_file, 0))) {
1435 if ((exresult = params_external(ct, 0)) != NOTOK &&
1436 p->c_ceopenfnx == openMail) {
1440 if ((size = ct->c_end - p->c_begin) <= 0) {
1442 content_error(NULL, ct, "empty body for access-type=mail-server");
1446 e->eb_body = bp = mh_xmalloc((unsigned) size);
1447 fseek(p->c_fp, p->c_begin, SEEK_SET);
1449 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1451 adios("failed", "fread");
1453 adios(NULL, "unexpected EOF from fread");
1455 bp += cc, size -= cc;
1462 p->c_end = p->c_begin;
1467 if (exresult == NOTOK)
1469 if (e->eb_flags == NOTOK)
1472 switch (p->c_type) {
1477 if (p->c_subtype != MESSAGE_RFC822)
1481 e->eb_partno = ct->c_partno;
1483 (*p->c_ctinitfnx) (p);
1498 params_external(CT ct, int composing)
1501 struct exbody *e = (struct exbody *) ct->c_ctparams;
1502 CI ci = &ct->c_ctinfo;
1504 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1505 if (!mh_strcasecmp(*ap, "access-type")) {
1506 struct str2init *s2i;
1507 CT p = e->eb_content;
1509 for (s2i = str2methods; s2i->si_key; s2i++)
1510 if (!mh_strcasecmp(*ep, s2i->si_key))
1514 e->eb_flags = NOTOK;
1515 p->c_encoding = CE_EXTERNAL;
1518 e->eb_access = s2i->si_key;
1519 e->eb_flags = s2i->si_val;
1520 p->c_encoding = CE_EXTERNAL;
1522 /* Call the Init function for this external type */
1523 if ((*s2i->si_init)(p) == NOTOK)
1527 if (!mh_strcasecmp(*ap, "name")) {
1531 if (!mh_strcasecmp(*ap, "permission")) {
1532 e->eb_permission = *ep;
1535 if (!mh_strcasecmp(*ap, "site")) {
1539 if (!mh_strcasecmp(*ap, "directory")) {
1543 if (!mh_strcasecmp(*ap, "mode")) {
1547 if (!mh_strcasecmp(*ap, "size")) {
1548 sscanf(*ep, "%lu", &e->eb_size);
1551 if (!mh_strcasecmp(*ap, "server")) {
1555 if (!mh_strcasecmp(*ap, "subject")) {
1556 e->eb_subject = *ep;
1559 if (composing && !mh_strcasecmp(*ap, "body")) {
1560 e->eb_body = getcpy(*ep);
1565 if (!e->eb_access) {
1566 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);
1579 InitApplication(CT ct)
1582 CI ci = &ct->c_ctinfo;
1585 for (kv = SubApplication; kv->kv_key; kv++)
1586 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1588 ct->c_subtype = kv->kv_value;
1595 ** TRANSFER ENCODINGS
1599 init_encoding(CT ct, OpenCEFunc openfnx)
1603 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1604 adios(NULL, "out of memory");
1607 ct->c_ceopenfnx = openfnx;
1608 ct->c_ceclosefnx = close_encoding;
1609 ct->c_cesizefnx = size_encoding;
1616 close_encoding(CT ct)
1620 if (!(ce = ct->c_cefile))
1630 static unsigned long
1631 size_encoding(CT ct)
1639 if (!(ce = ct->c_cefile))
1640 return (ct->c_end - ct->c_begin);
1642 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1643 return (long) st.st_size;
1646 if (stat(ce->ce_file, &st) != NOTOK)
1647 return (long) st.st_size;
1652 if (ct->c_encoding == CE_EXTERNAL)
1653 return (ct->c_end - ct->c_begin);
1656 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1657 return (ct->c_end - ct->c_begin);
1659 if (fstat(fd, &st) != NOTOK)
1660 size = (long) st.st_size;
1664 (*ct->c_ceclosefnx) (ct);
1673 static unsigned char b642nib[0x80] = {
1674 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1675 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1676 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1677 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1678 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1679 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1680 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1681 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1682 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1683 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1684 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1685 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1686 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1687 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1688 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1689 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1696 return init_encoding(ct, openBase64);
1701 openBase64(CT ct, char **file)
1703 int bitno, cc, digested;
1706 unsigned char value, *b, *b1, *b2, *b3;
1707 unsigned char *cp, *ep;
1708 char buffer[BUFSIZ];
1709 /* sbeck -- handle suffixes */
1714 b = (unsigned char *) &bits;
1715 b1 = &b[endian > 0 ? 1 : 2];
1716 b2 = &b[endian > 0 ? 2 : 1];
1717 b3 = &b[endian > 0 ? 3 : 0];
1721 fseek(ce->ce_fp, 0L, SEEK_SET);
1726 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1727 content_error(ce->ce_file, ct,
1728 "unable to fopen for reading");
1734 if (*file == NULL) {
1735 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1738 ce->ce_file = getcpy(*file);
1742 /* sbeck@cise.ufl.edu -- handle suffixes */
1744 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1745 invo_name, ci->ci_type, ci->ci_subtype);
1746 cp = context_find(buffer);
1747 if (cp == NULL || *cp == '\0') {
1748 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1750 cp = context_find(buffer);
1752 if (cp != NULL && *cp != '\0') {
1753 if (ce->ce_unlink) {
1755 ** Temporary file already exists, so we rename to
1756 ** version with extension.
1758 char *file_org = strdup(ce->ce_file);
1759 ce->ce_file = add(cp, ce->ce_file);
1760 if (rename(file_org, ce->ce_file)) {
1761 adios(ce->ce_file, "unable to rename %s to ",
1767 ce->ce_file = add(cp, ce->ce_file);
1771 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1772 content_error(ce->ce_file, ct,
1773 "unable to fopen for reading/writing");
1777 if ((len = ct->c_end - ct->c_begin) < 0)
1778 adios(NULL, "internal error(1)");
1780 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1781 content_error(ct->c_file, ct, "unable to open for reading");
1785 if ((digested = ct->c_digested))
1786 MD5Init(&mdContext);
1792 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1794 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1796 content_error(ct->c_file, ct, "error reading from");
1800 content_error(NULL, ct, "premature eof");
1808 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1813 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1815 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1817 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1821 bits |= value << bitno;
1823 if ((bitno -= 6) < 0) {
1824 putc((char) *b1, ce->ce_fp);
1826 MD5Update(&mdContext, b1, 1);
1828 putc((char) *b2, ce->ce_fp);
1830 MD5Update(&mdContext, b2, 1);
1832 putc((char) *b3, ce->ce_fp);
1834 MD5Update(&mdContext, b3, 1);
1838 if (ferror(ce->ce_fp)) {
1839 content_error(ce->ce_file, ct,
1840 "error writing to");
1843 bitno = 18, bits = 0L, skip = 0;
1849 goto self_delimiting;
1858 fprintf(stderr, "premature ending (bitno %d)\n",
1861 content_error(NULL, ct, "invalid BASE64 encoding");
1866 fseek(ct->c_fp, 0L, SEEK_SET);
1868 if (fflush(ce->ce_fp)) {
1869 content_error(ce->ce_file, ct, "error writing to");
1874 unsigned char digest[16];
1876 MD5Final(digest, &mdContext);
1877 if (memcmp((char *) digest, (char *) ct->c_digest,
1878 sizeof(digest) / sizeof(digest[0])))
1879 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1881 fprintf(stderr, "content integrity confirmed\n");
1884 fseek(ce->ce_fp, 0L, SEEK_SET);
1887 *file = ce->ce_file;
1888 return fileno(ce->ce_fp);
1891 free_encoding(ct, 0);
1900 static char hex2nib[0x80] = {
1901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1908 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1909 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1923 return init_encoding(ct, openQuoted);
1928 openQuoted(CT ct, char **file)
1930 int cc, digested, len, quoted;
1931 unsigned char *cp, *ep;
1932 char buffer[BUFSIZ];
1935 /* sbeck -- handle suffixes */
1941 fseek(ce->ce_fp, 0L, SEEK_SET);
1946 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1947 content_error(ce->ce_file, ct,
1948 "unable to fopen for reading");
1954 if (*file == NULL) {
1955 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1958 ce->ce_file = getcpy(*file);
1962 /* sbeck@cise.ufl.edu -- handle suffixes */
1964 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1965 invo_name, ci->ci_type, ci->ci_subtype);
1966 cp = context_find(buffer);
1967 if (cp == NULL || *cp == '\0') {
1968 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1970 cp = context_find(buffer);
1972 if (cp != NULL && *cp != '\0') {
1973 if (ce->ce_unlink) {
1974 // Temporary file already exists, so we rename to
1975 // version with extension.
1976 char *file_org = strdup(ce->ce_file);
1977 ce->ce_file = add(cp, ce->ce_file);
1978 if (rename(file_org, ce->ce_file)) {
1979 adios(ce->ce_file, "unable to rename %s to ",
1985 ce->ce_file = add(cp, ce->ce_file);
1989 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1990 content_error(ce->ce_file, ct,
1991 "unable to fopen for reading/writing");
1995 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1996 content_error(ce->ce_file, ct,
1997 "unable to fopen for reading/writing");
2001 if ((len = ct->c_end - ct->c_begin) < 0)
2002 adios(NULL, "internal error(2)");
2004 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2005 content_error(ct->c_file, ct, "unable to open for reading");
2009 if ((digested = ct->c_digested))
2010 MD5Init(&mdContext);
2017 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2019 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2020 content_error(NULL, ct, "premature eof");
2024 if ((cc = strlen(buffer)) > len)
2028 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2033 for (; cp < ep; cp++) {
2035 /* in an escape sequence */
2037 /* at byte 1 of an escape sequence */
2038 mask = hex2nib[*cp & 0x7f];
2039 /* next is byte 2 */
2042 /* at byte 2 of an escape sequence */
2044 mask |= hex2nib[*cp & 0x7f];
2045 putc(mask, ce->ce_fp);
2047 MD5Update(&mdContext, &mask, 1);
2048 if (ferror(ce->ce_fp)) {
2049 content_error(ce->ce_file, ct, "error writing to");
2053 ** finished escape sequence; next may
2054 ** be literal or a new escape sequence
2058 /* on to next byte */
2062 /* not in an escape sequence */
2065 ** starting an escape sequence,
2068 if (cp + 1 < ep && cp[1] == '\n') {
2069 /* "=\n" soft line break, eat the \n */
2073 if (cp + 1 >= ep || cp + 2 >= ep) {
2075 ** We don't have 2 bytes left,
2076 ** so this is an invalid escape
2077 ** sequence; just show the raw bytes
2080 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2082 ** Next 2 bytes are hex digits,
2083 ** making this a valid escape
2084 ** sequence; let's decode it (above).
2090 ** One or both of the next 2 is
2091 ** out of range, making this an
2092 ** invalid escape sequence; just
2093 ** show the raw bytes (below).
2098 /* Just show the raw byte. */
2099 putc(*cp, ce->ce_fp);
2102 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2104 MD5Update(&mdContext, (unsigned char *) cp, 1);
2107 if (ferror(ce->ce_fp)) {
2108 content_error(ce->ce_file, ct,
2109 "error writing to");
2115 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2119 fseek(ct->c_fp, 0L, SEEK_SET);
2121 if (fflush(ce->ce_fp)) {
2122 content_error(ce->ce_file, ct, "error writing to");
2127 unsigned char digest[16];
2129 MD5Final(digest, &mdContext);
2130 if (memcmp((char *) digest, (char *) ct->c_digest,
2131 sizeof(digest) / sizeof(digest[0])))
2132 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2134 fprintf(stderr, "content integrity confirmed\n");
2137 fseek(ce->ce_fp, 0L, SEEK_SET);
2140 *file = ce->ce_file;
2141 return fileno(ce->ce_fp);
2144 free_encoding(ct, 0);
2156 if (init_encoding(ct, open7Bit) == NOTOK)
2159 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2165 open7Bit(CT ct, char **file)
2168 char buffer[BUFSIZ];
2169 /* sbeck -- handle suffixes */
2176 fseek(ce->ce_fp, 0L, SEEK_SET);
2181 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2182 content_error(ce->ce_file, ct,
2183 "unable to fopen for reading");
2189 if (*file == NULL) {
2190 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2193 ce->ce_file = getcpy(*file);
2197 /* sbeck@cise.ufl.edu -- handle suffixes */
2199 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2200 invo_name, ci->ci_type, ci->ci_subtype);
2201 cp = context_find(buffer);
2202 if (cp == NULL || *cp == '\0') {
2203 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2205 cp = context_find(buffer);
2207 if (cp != NULL && *cp != '\0') {
2208 if (ce->ce_unlink) {
2210 ** Temporary file already exists, so we rename to
2211 ** version with extension.
2213 char *file_org = strdup(ce->ce_file);
2214 ce->ce_file = add(cp, ce->ce_file);
2215 if (rename(file_org, ce->ce_file)) {
2216 adios(ce->ce_file, "unable to rename %s to ",
2222 ce->ce_file = add(cp, ce->ce_file);
2226 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2227 content_error(ce->ce_file, ct,
2228 "unable to fopen for reading/writing");
2232 if (ct->c_type == CT_MULTIPART) {
2234 CI ci = &ct->c_ctinfo;
2237 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2239 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2240 strlen(ci->ci_subtype);
2241 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2242 putc(';', ce->ce_fp);
2245 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2248 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2249 fputs("\n\t", ce->ce_fp);
2252 putc(' ', ce->ce_fp);
2255 fprintf(ce->ce_fp, "%s", buffer);
2259 if (ci->ci_comment) {
2260 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2262 fputs("\n\t", ce->ce_fp);
2265 putc(' ', ce->ce_fp);
2268 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2271 fprintf(ce->ce_fp, "\n");
2273 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2275 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2277 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2278 fprintf(ce->ce_fp, "\n");
2281 if ((len = ct->c_end - ct->c_begin) < 0)
2282 adios(NULL, "internal error(3)");
2284 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2285 content_error(ct->c_file, ct, "unable to open for reading");
2289 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2291 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2293 content_error(ct->c_file, ct, "error reading from");
2297 content_error(NULL, ct, "premature eof");
2305 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2306 if (ferror(ce->ce_fp)) {
2307 content_error(ce->ce_file, ct,
2308 "error writing to");
2313 fseek(ct->c_fp, 0L, SEEK_SET);
2315 if (fflush(ce->ce_fp)) {
2316 content_error(ce->ce_file, ct, "error writing to");
2320 fseek(ce->ce_fp, 0L, SEEK_SET);
2323 *file = ce->ce_file;
2324 return fileno(ce->ce_fp);
2327 free_encoding(ct, 0);
2337 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2339 char cachefile[BUFSIZ];
2342 fseek(ce->ce_fp, 0L, SEEK_SET);
2347 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2348 content_error(ce->ce_file, ct,
2349 "unable to fopen for reading");
2355 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2356 cachefile, sizeof(cachefile)) != NOTOK) {
2357 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2358 ce->ce_file = getcpy(cachefile);
2362 admonish(cachefile, "unable to fopen for reading");
2369 *file = ce->ce_file;
2370 *fd = fileno(ce->ce_fp);
2381 return init_encoding(ct, openFile);
2386 openFile(CT ct, char **file)
2389 char cachefile[BUFSIZ];
2390 struct exbody *e = ct->c_ctexbody;
2391 CE ce = ct->c_cefile;
2393 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2403 content_error(NULL, ct, "missing name parameter");
2407 ce->ce_file = getcpy(e->eb_name);
2410 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2411 content_error(ce->ce_file, ct, "unable to fopen for reading");
2415 if ((!e->eb_permission ||
2416 mh_strcasecmp(e->eb_permission, "read-write")) &&
2417 find_cache(NULL, wcachesw, &cachetype,
2418 e->eb_content->c_id, cachefile, sizeof(cachefile))
2423 mask = umask(cachetype ? ~m_gmprot() : 0222);
2424 if ((fp = fopen(cachefile, "w"))) {
2426 char buffer[BUFSIZ];
2427 FILE *gp = ce->ce_fp;
2429 fseek(gp, 0L, SEEK_SET);
2431 while ((cc = fread(buffer, sizeof(*buffer),
2432 sizeof(buffer), gp)) > 0)
2433 fwrite(buffer, sizeof(*buffer), cc, fp);
2437 admonish(ce->ce_file, "error reading");
2439 } else if (ferror(fp)) {
2440 admonish(cachefile, "error writing");
2448 fseek(ce->ce_fp, 0L, SEEK_SET);
2449 *file = ce->ce_file;
2450 return fileno(ce->ce_fp);
2460 return init_encoding(ct, openFTP);
2465 openFTP(CT ct, char **file)
2467 int cachetype, caching, fd;
2469 char *bp, *ftp, *user, *pass;
2470 char buffer[BUFSIZ], cachefile[BUFSIZ];
2473 static char *username = NULL;
2474 static char *password = NULL;
2481 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2487 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2496 if (!e->eb_name || !e->eb_site) {
2497 content_error(NULL, ct, "missing %s parameter",
2498 e->eb_name ? "site": "name");
2505 pidcheck(pidwait(xpid, NOTOK));
2509 /* Get the buffer ready to go */
2511 buflen = sizeof(buffer);
2514 ** Construct the query message for user
2516 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2522 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2528 snprintf(bp, buflen, "\n using %sFTP from site %s",
2529 e->eb_flags ? "anonymous " : "", e->eb_site);
2534 if (e->eb_size > 0) {
2535 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2540 snprintf(bp, buflen, "? ");
2543 ** Now, check the answer
2545 if (!getanswer(buffer))
2550 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2554 ruserpass(e->eb_site, &username, &password);
2559 ce->ce_unlink = (*file == NULL);
2561 cachefile[0] = '\0';
2562 if ((!e->eb_permission ||
2563 mh_strcasecmp(e->eb_permission, "read-write")) &&
2564 find_cache(NULL, wcachesw, &cachetype,
2565 e->eb_content->c_id, cachefile, sizeof(cachefile))
2567 if (*file == NULL) {
2574 ce->ce_file = getcpy(*file);
2576 ce->ce_file = getcpy(cachefile);
2578 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2580 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2581 content_error (ce->ce_file, ct,
2582 "unable to fopen for reading/writing");
2587 vec[vecp++] = mhbasename(ftp);
2588 vec[vecp++] = e->eb_site;
2591 vec[vecp++] = e->eb_dir;
2592 vec[vecp++] = e->eb_name;
2593 vec[vecp++] = ce->ce_file,
2594 vec[vecp++] = e->eb_mode &&
2595 !mh_strcasecmp(e->eb_mode, "ascii") ?
2601 switch (child_id = fork()) {
2603 adios("fork", "unable to");
2607 close(fileno(ce->ce_fp));
2609 fprintf(stderr, "unable to exec ");
2615 if (pidXwait(child_id, NULL)) {
2616 username = password = NULL;
2625 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2630 mask = umask(cachetype ? ~m_gmprot() : 0222);
2631 if ((fp = fopen(cachefile, "w"))) {
2633 FILE *gp = ce->ce_fp;
2635 fseek(gp, 0L, SEEK_SET);
2637 while ((cc= fread(buffer, sizeof(*buffer),
2638 sizeof(buffer), gp)) > 0)
2639 fwrite(buffer, sizeof(*buffer), cc, fp);
2643 admonish(ce->ce_file, "error reading");
2645 } else if (ferror(fp)) {
2646 admonish(cachefile, "error writing");
2655 fseek(ce->ce_fp, 0L, SEEK_SET);
2656 *file = ce->ce_file;
2657 return fileno(ce->ce_fp);
2668 return init_encoding(ct, openMail);
2673 openMail(CT ct, char **file)
2675 int child_id, fd, vecp;
2677 char *bp, buffer[BUFSIZ], *vec[7];
2678 struct exbody *e = ct->c_ctexbody;
2679 CE ce = ct->c_cefile;
2681 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2690 if (!e->eb_server) {
2691 content_error(NULL, ct, "missing server parameter");
2698 pidcheck(pidwait(xpid, NOTOK));
2702 /* Get buffer ready to go */
2704 buflen = sizeof(buffer);
2706 /* Now, construct query message */
2707 snprintf(bp, buflen, "Retrieve content");
2713 snprintf(bp, buflen, " %s", e->eb_partno);
2719 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2720 e->eb_subject ? e->eb_subject : e->eb_body);
2722 /* Now, check answer */
2723 if (!getanswer(buffer))
2727 vec[vecp++] = "mhmail";
2728 vec[vecp++] = e->eb_server;
2729 vec[vecp++] = "-subject";
2730 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2731 vec[vecp++] = "-body";
2732 vec[vecp++] = e->eb_body;
2735 switch (child_id = fork()) {
2737 advise("fork", "unable to");
2742 fprintf(stderr, "unable to exec ");
2748 if (pidXwait(child_id, NULL) == OK)
2749 advise(NULL, "request sent");
2753 if (*file == NULL) {
2754 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2757 ce->ce_file = getcpy(*file);
2761 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2762 content_error(ce->ce_file, ct,
2763 "unable to fopen for reading/writing");
2768 ** showproc is for mhshow and mhstore, though mhlist -debug
2772 free(ct->c_showproc);
2773 ct->c_showproc = getcpy("true");
2775 fseek(ce->ce_fp, 0L, SEEK_SET);
2776 *file = ce->ce_file;
2777 return fileno(ce->ce_fp);
2782 readDigest(CT ct, char *cp)
2787 unsigned char *dp, value, *ep;
2788 unsigned char *b, *b1, *b2, *b3;
2790 b = (unsigned char *) &bits,
2791 b1 = &b[endian > 0 ? 1 : 2],
2792 b2 = &b[endian > 0 ? 2 : 1],
2793 b3 = &b[endian > 0 ? 3 : 0];
2798 for (ep = (dp = ct->c_digest)
2799 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2802 if (skip || (*cp & 0x80) ||
2803 (value = b642nib[*cp & 0x7f])
2806 fprintf(stderr, "invalid BASE64 encoding\n");
2810 bits |= value << bitno;
2812 if ((bitno -= 6) < 0) {
2813 if (dp + (3 - skip) > ep)
2814 goto invalid_digest;
2829 goto self_delimiting;
2834 fprintf(stderr, "premature ending (bitno %d)\n",
2845 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2853 fprintf(stderr, "MD5 digest=");
2854 for (dp = ct->c_digest; dp < ep; dp++)
2855 fprintf(stderr, "%02x", *dp & 0xff);
2856 fprintf(stderr, "\n");