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>
17 #include <h/mhparse.h>
22 extern int endian; /* mhmisc.c */
24 extern pid_t xpid; /* mhshowsbr.c */
27 extern int rcachesw; /* mhcachesbr.c */
28 extern int wcachesw; /* mhcachesbr.c */
31 ** Directory to place temp files. This must
32 ** be set before these routines are called.
37 ** Structures for TEXT messages
39 struct k2v SubText[] = {
40 { "plain", TEXT_PLAIN },
41 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
42 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
43 { NULL, TEXT_UNKNOWN } /* this one must be last! */
46 struct k2v Charset[] = {
47 { "us-ascii", CHARSET_USASCII },
48 { "iso-8859-1", CHARSET_LATIN },
49 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
53 ** Structures for MULTIPART messages
55 struct k2v SubMultiPart[] = {
56 { "mixed", MULTI_MIXED },
57 { "alternative", MULTI_ALTERNATE },
58 { "digest", MULTI_DIGEST },
59 { "parallel", MULTI_PARALLEL },
60 { NULL, MULTI_UNKNOWN } /* this one must be last! */
64 ** Structures for MESSAGE messages
66 struct k2v SubMessage[] = {
67 { "rfc822", MESSAGE_RFC822 },
68 { "partial", MESSAGE_PARTIAL },
69 { "external-body", MESSAGE_EXTERNAL },
70 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
74 ** Structure for APPLICATION messages
76 struct k2v SubApplication[] = {
77 { "octet-stream", APPLICATION_OCTETS },
78 { "postscript", APPLICATION_POSTSCRIPT },
79 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
84 int find_cache(CT, int, int *, char *, char *, int);
89 int make_intermediates(char *);
90 void content_error(char *, CT, char *, ...);
93 void free_content(CT);
94 void free_encoding(CT, int);
99 static CT get_content(FILE *, char *, int);
100 static int get_comment(CT, unsigned char **, int);
102 static int InitGeneric(CT);
103 static int InitText(CT);
104 static int InitMultiPart(CT);
105 static void reverse_parts(CT);
106 static int InitMessage(CT);
107 static int InitApplication(CT);
108 static int init_encoding(CT, OpenCEFunc);
109 static unsigned long size_encoding(CT);
110 static int InitBase64(CT);
111 static int openBase64(CT, char **);
112 static int InitQuoted(CT);
113 static int openQuoted(CT, char **);
114 static int Init7Bit(CT);
115 static int openExternal(CT, CT, CE, char **, int *);
116 static int InitFile(CT);
117 static int openFile(CT, char **);
118 static int InitFTP(CT);
119 static int openFTP(CT, char **);
120 static int InitMail(CT);
121 static int openMail(CT, char **);
123 struct str2init str2cts[] = {
124 { "application", CT_APPLICATION, InitApplication },
125 { "audio", CT_AUDIO, InitGeneric },
126 { "image", CT_IMAGE, InitGeneric },
127 { "message", CT_MESSAGE, InitMessage },
128 { "multipart", CT_MULTIPART, InitMultiPart },
129 { "text", CT_TEXT, InitText },
130 { "video", CT_VIDEO, InitGeneric },
131 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
132 { NULL, CT_UNKNOWN, NULL },
135 struct str2init str2ces[] = {
136 { "base64", CE_BASE64, InitBase64 },
137 { "quoted-printable", CE_QUOTED, InitQuoted },
138 { "8bit", CE_8BIT, Init7Bit },
139 { "7bit", CE_7BIT, Init7Bit },
140 { "binary", CE_BINARY, Init7Bit },
141 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
142 { NULL, CE_UNKNOWN, NULL },
146 ** NOTE WELL: si_key MUST NOT have value of NOTOK
148 ** si_key is 1 if access method is anonymous.
150 struct str2init str2methods[] = {
151 { "afs", 1, InitFile },
152 { "anon-ftp", 1, InitFTP },
153 { "ftp", 0, InitFTP },
154 { "local-file", 0, InitFile },
155 { "mail-server", 0, InitMail },
163 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
174 ** Main entry point for parsing a MIME message or file.
175 ** It returns the Content structure for the top level
176 ** entity in the file.
179 parse_mime(char *file)
187 ** Check if file is actually standard input
189 if ((is_stdin = (strcmp(file, "-")==0))) {
190 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
192 advise("mhparse", "unable to create temporary file");
195 file = getcpy(tfile);
198 while (fgets(buffer, sizeof(buffer), stdin))
204 advise("stdin", "error reading");
209 advise(file, "error writing");
212 fseek(fp, 0L, SEEK_SET);
213 } else if ((fp = fopen(file, "r")) == NULL) {
214 advise(file, "unable to read");
218 if (!(ct = get_content(fp, file, 1))) {
221 advise(NULL, "unable to decode %s", file);
226 ct->c_unlink = 1; /* temp file to remove */
230 if (ct->c_end == 0L) {
231 fseek(fp, 0L, SEEK_END);
232 ct->c_end = ftell(fp);
235 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
247 ** Main routine for reading/parsing the headers
248 ** of a message content.
250 ** toplevel = 1 # we are at the top level of the message
251 ** toplevel = 0 # we are inside message type or multipart type
252 ** # other than multipart/digest
253 ** toplevel = -1 # we are inside multipart/digest
254 ** NB: on failure we will fclose(in)!
258 get_content(FILE *in, char *file, int toplevel)
261 char buf[BUFSIZ], name[NAMESZ];
266 /* allocate the content structure */
267 if (!(ct = (CT) calloc(1, sizeof(*ct))))
268 adios(NULL, "out of memory");
271 ct->c_file = getcpy(file);
272 ct->c_begin = ftell(ct->c_fp) + 1;
275 ** Parse the header fields for this
276 ** content into a linked list.
278 for (compnum = 1, state = FLD;;) {
279 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
285 /* get copies of the buffers */
289 /* if necessary, get rest of field */
290 while (state == FLDPLUS) {
291 state = m_getfld(state, name, buf,
293 vp = add(buf, vp); /* add to previous value */
296 /* Now add the header data to the list */
297 add_header(ct, np, vp);
299 /* continue, if this isn't the last header field */
300 if (state != FLDEOF) {
301 ct->c_begin = ftell(in) + 1;
308 ct->c_begin = ftell(in) - strlen(buf);
312 ct->c_begin = ftell(in);
317 adios(NULL, "message format error in component #%d",
321 adios(NULL, "getfld() returned %d", state);
324 /* break out of the loop */
329 ** Read the content headers. We will parse the
330 ** MIME related header fields into their various
331 ** structures and set internal flags related to
332 ** content type/subtype, etc.
335 hp = ct->c_first_hf; /* start at first header field */
337 /* Get MIME-Version field */
338 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
341 unsigned char *cp, *dp;
344 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
347 ct->c_vrsn = getcpy(hp->value);
349 /* Now, cleanup this field */
354 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
356 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
361 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
363 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
366 for (dp = cp; istoken(*dp); dp++)
370 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
373 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
376 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
377 /* Get Content-Type field */
378 struct str2init *s2i;
379 CI ci = &ct->c_ctinfo;
381 /* Check if we've already seen a Content-Type header */
383 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
387 /* Parse the Content-Type field */
388 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
392 ** Set the Init function and the internal
393 ** flag for this content type.
395 for (s2i = str2cts; s2i->si_key; s2i++)
396 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
398 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
400 ct->c_type = s2i->si_val;
401 ct->c_ctinitfnx = s2i->si_init;
403 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
404 /* Get Content-Transfer-Encoding field */
406 unsigned char *cp, *dp;
407 struct str2init *s2i;
410 ** Check if we've already seen the
411 ** Content-Transfer-Encoding field
414 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
418 /* get copy of this field */
419 ct->c_celine = cp = getcpy(hp->value);
423 for (dp = cp; istoken(*dp); dp++)
429 ** Find the internal flag and Init function
430 ** for this transfer encoding.
432 for (s2i = str2ces; s2i->si_key; s2i++)
433 if (!mh_strcasecmp(cp, s2i->si_key))
435 if (!s2i->si_key && !uprf(cp, "X-"))
438 ct->c_encoding = s2i->si_val;
440 /* Call the Init function for this encoding */
441 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
444 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
445 /* Get Content-ID field */
446 ct->c_id = add(hp->value, ct->c_id);
448 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
449 /* Get Content-Description field */
450 ct->c_descr = add(hp->value, ct->c_descr);
452 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
453 /* Get Content-Disposition field */
454 ct->c_dispo = add(hp->value, ct->c_dispo);
458 hp = hp->next; /* next header field */
462 ** Check if we saw a Content-Type field.
463 ** If not, then assign a default value for
464 ** it, and the Init function.
468 ** If we are inside a multipart/digest message,
469 ** so default type is message/rfc822
472 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
474 ct->c_type = CT_MESSAGE;
475 ct->c_ctinitfnx = InitMessage;
478 ** Else default type is text/plain
480 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
482 ct->c_type = CT_TEXT;
483 ct->c_ctinitfnx = InitText;
487 /* Use default Transfer-Encoding, if necessary */
489 ct->c_encoding = CE_7BIT;
502 ** small routine to add header field to list
506 add_header(CT ct, char *name, char *value)
510 /* allocate header field structure */
511 hp = mh_xmalloc(sizeof(*hp));
513 /* link data into header structure */
518 /* link header structure into the list */
519 if (ct->c_first_hf == NULL) {
520 ct->c_first_hf = hp; /* this is the first */
523 ct->c_last_hf->next = hp; /* add it to the end */
532 ** Make sure that buf contains at least one appearance of name,
533 ** followed by =. If not, insert both name and value, just after
534 ** first semicolon, if any. Note that name should not contain a
535 ** trailing =. And quotes will be added around the value. Typical
536 ** usage: make sure that a Content-Disposition header contains
537 ** filename="foo". If it doesn't and value does, use value from
541 incl_name_value(unsigned char *buf, char *name, char *value) {
544 /* Assume that name is non-null. */
546 char *name_plus_equal = concat(name, "=", NULL);
548 if (!strstr(buf, name_plus_equal)) {
551 char *prefix, *suffix;
553 /* Trim trailing space, esp. newline. */
554 for (cp = &buf[strlen(buf) - 1];
555 cp >= buf && isspace(*cp); --cp) {
559 insertion = concat("; ", name, "=", "\"", value, "\"",
563 ** Insert at first semicolon, if any.
564 ** If none, append to end.
566 prefix = getcpy(buf);
567 if ((cp = strchr(prefix, ';'))) {
568 suffix = concat(cp, NULL);
570 newbuf = concat(prefix, insertion, suffix,
575 newbuf = concat(buf, insertion, "\n", NULL);
583 free(name_plus_equal);
590 ** Extract just name_suffix="foo", if any, from value. If there isn't
591 ** one, return the entire value. Note that, for example, a name_suffix
592 ** of name will match filename="foo", and return foo.
595 extract_name_value(char *name_suffix, char *value) {
596 char *extracted_name_value = value;
597 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
598 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
601 free(name_suffix_plus_quote);
602 if (name_suffix_equals) {
603 char *name_suffix_begin;
606 for (cp = name_suffix_equals; *cp != '"'; ++cp)
608 name_suffix_begin = ++cp;
609 /* Find second \". */
610 for (; *cp != '"'; ++cp)
613 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
614 memcpy(extracted_name_value, name_suffix_begin,
615 cp - name_suffix_begin);
616 extracted_name_value[cp - name_suffix_begin] = '\0';
619 return extracted_name_value;
623 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
624 ** directives. Fills in the information of the CTinfo structure.
627 get_ctinfo(unsigned char *cp, CT ct, int magic)
636 i = strlen(invo_name) + 2;
638 /* store copy of Content-Type line */
639 cp = ct->c_ctline = getcpy(cp);
641 while (isspace(*cp)) /* trim leading spaces */
644 /* change newlines to spaces */
645 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
648 /* trim trailing spaces */
649 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
655 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
657 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
660 for (dp = cp; istoken(*dp); dp++)
663 ci->ci_type = getcpy(cp); /* store content type */
667 advise(NULL, "invalid %s: field in message %s (empty type)",
668 TYPE_FIELD, ct->c_file);
672 /* down case the content type string */
673 for (dp = ci->ci_type; *dp; dp++)
674 if (isalpha(*dp) && isupper(*dp))
680 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
685 ci->ci_subtype = getcpy("");
693 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
696 for (dp = cp; istoken(*dp); dp++)
699 ci->ci_subtype = getcpy(cp); /* store the content subtype */
702 if (!*ci->ci_subtype) {
703 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
707 /* down case the content subtype string */
708 for (dp = ci->ci_subtype; *dp; dp++)
709 if (isalpha(*dp) && isupper(*dp))
716 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
720 ** Parse attribute/value pairs given with Content-Type
722 ep = (ap = ci->ci_attrs) + NPARMS;
728 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
736 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
740 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
744 /* down case the attribute name */
745 for (dp = cp; istoken(*dp); dp++)
746 if (isalpha(*dp) && isupper(*dp))
749 for (up = dp; isspace(*dp);)
751 if (dp == cp || *dp != '=') {
752 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);
756 vp = (*ap = getcpy(cp)) + (up - cp);
758 for (dp++; isspace(*dp);)
761 /* now add the attribute value */
762 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
765 for (cp = ++dp, dp = vp;;) {
769 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
774 if ((c = *cp++) == '\0')
789 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
794 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
802 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
807 ** Get any <Content-Id> given in buffer
809 if (magic && *cp == '<') {
814 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
815 advise(NULL, "invalid ID in message %s", ct->c_file);
821 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
832 ** Get any [Content-Description] given in buffer.
834 if (magic && *cp == '[') {
836 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
840 advise(NULL, "invalid description in message %s",
849 ct->c_descr = concat(ct->c_descr, "\n", NULL);
860 ** Get any {Content-Disposition} given in buffer.
862 if (magic && *cp == '{') {
864 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
868 advise(NULL, "invalid disposition in message %s",
877 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
888 ** Check if anything is left over
892 ci->ci_magic = getcpy(cp);
895 ** If there is a Content-Disposition header and
896 ** it doesn't have a *filename=, extract it from
897 ** the magic contents. The mhbasename call skips
898 ** any leading directory components.
901 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
903 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
911 get_comment(CT ct, unsigned char **ap, int istype)
916 char c, buffer[BUFSIZ], *dp;
928 advise(NULL, "invalid comment in message %s's %s: field",
929 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
934 if ((c = *cp++) == '\0')
957 if ((dp = ci->ci_comment)) {
958 ci->ci_comment = concat(dp, " ", buffer, NULL);
961 ci->ci_comment = getcpy(buffer);
976 ** Handles content types audio, image, and video.
977 ** There's not much to do right here.
983 return OK; /* not much to do here */
996 char **ap, **ep, *cp;
999 CI ci = &ct->c_ctinfo;
1001 /* check for missing subtype */
1002 if (!*ci->ci_subtype)
1003 ci->ci_subtype = add("plain", ci->ci_subtype);
1006 for (kv = SubText; kv->kv_key; kv++)
1007 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1009 ct->c_subtype = kv->kv_value;
1011 /* allocate text character set structure */
1012 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1013 adios(NULL, "out of memory");
1014 ct->c_ctparams = (void *) t;
1016 /* scan for charset parameter */
1017 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1018 if (!mh_strcasecmp(*ap, "charset"))
1021 /* check if content specified a character set */
1023 /* match character set or set to CHARSET_UNKNOWN */
1024 for (kv = Charset; kv->kv_key; kv++) {
1025 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1030 t->tx_charset = kv->kv_value;
1032 t->tx_charset = CHARSET_UNSPECIFIED;
1036 ** If we can not handle character set natively,
1037 ** then check profile for string to modify the
1038 ** terminal or display method.
1040 ** termproc is for mhshow, though mhlist -debug prints it, too.
1042 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1043 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1045 if ((cp = context_find(buffer)))
1046 ct->c_termproc = getcpy(cp);
1058 InitMultiPart(CT ct)
1062 unsigned char *cp, *dp;
1064 char *bp, buffer[BUFSIZ];
1065 struct multipart *m;
1067 struct part *part, **next;
1068 CI ci = &ct->c_ctinfo;
1073 ** The encoding for multipart messages must be either
1074 ** 7bit, 8bit, or binary (per RFC2045).
1076 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1077 && ct->c_encoding != CE_BINARY) {
1078 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);
1083 for (kv = SubMultiPart; kv->kv_key; kv++)
1084 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1086 ct->c_subtype = kv->kv_value;
1089 ** Check for "boundary" parameter, which is
1090 ** required for multipart messages.
1093 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1094 if (!mh_strcasecmp(*ap, "boundary")) {
1100 /* complain if boundary parameter is missing */
1102 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);
1106 /* allocate primary structure for multipart info */
1107 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1108 adios(NULL, "out of memory");
1109 ct->c_ctparams = (void *) m;
1111 /* check if boundary parameter contains only whitespace characters */
1112 for (cp = bp; isspace(*cp); cp++)
1115 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);
1119 /* remove trailing whitespace from boundary parameter */
1120 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1125 /* record boundary separators */
1126 m->mp_start = concat(bp, "\n", NULL);
1127 m->mp_stop = concat(bp, "--\n", NULL);
1129 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1130 advise(ct->c_file, "unable to open for reading");
1134 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1136 next = &m->mp_parts;
1140 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1144 pos += strlen(buffer);
1145 if (buffer[0] != '-' || buffer[1] != '-')
1148 if (strcmp(buffer + 2, m->mp_start)!=0)
1151 if ((part = (struct part *) calloc(1, sizeof(*part)))
1153 adios(NULL, "out of memory");
1155 next = &part->mp_next;
1157 if (!(p = get_content(fp, ct->c_file,
1158 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1165 fseek(fp, pos, SEEK_SET);
1168 if (strcmp(buffer + 2, m->mp_start) == 0) {
1172 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1173 if (p->c_end < p->c_begin)
1174 p->c_begin = p->c_end;
1179 if (strcmp(buffer + 2, m->mp_stop) == 0)
1185 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1186 if (!inout && part) {
1188 p->c_end = ct->c_end;
1190 if (p->c_begin >= p->c_end) {
1191 for (next = &m->mp_parts; *next != part;
1192 next = &((*next)->mp_next))
1196 free((char *) part);
1201 /* reverse the order of the parts for multipart/alternative */
1202 if (ct->c_subtype == MULTI_ALTERNATE)
1206 ** label all subparts with part number, and
1207 ** then initialize the content of the subpart.
1212 char partnam[BUFSIZ];
1215 snprintf(partnam, sizeof(partnam), "%s.",
1217 pp = partnam + strlen(partnam);
1222 for (part = m->mp_parts, partnum = 1; part;
1223 part = part->mp_next, partnum++) {
1226 sprintf(pp, "%d", partnum);
1227 p->c_partno = getcpy(partnam);
1229 /* initialize the content of the subparts */
1230 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1245 ** reverse the order of the parts of a multipart
1249 reverse_parts(CT ct)
1252 struct multipart *m;
1253 struct part **base, **bmp, **next, *part;
1255 m = (struct multipart *) ct->c_ctparams;
1257 /* if only one part, just return */
1258 if (!m->mp_parts || !m->mp_parts->mp_next)
1261 /* count number of parts */
1263 for (part = m->mp_parts; part; part = part->mp_next)
1266 /* allocate array of pointers to the parts */
1267 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1268 adios(NULL, "out of memory");
1271 /* point at all the parts */
1272 for (part = m->mp_parts; part; part = part->mp_next)
1276 /* reverse the order of the parts */
1277 next = &m->mp_parts;
1278 for (bmp--; bmp >= base; bmp--) {
1281 next = &part->mp_next;
1285 /* free array of pointers */
1286 free((char *) base);
1298 CI ci = &ct->c_ctinfo;
1300 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1301 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1305 /* check for missing subtype */
1306 if (!*ci->ci_subtype)
1307 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1310 for (kv = SubMessage; kv->kv_key; kv++)
1311 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1313 ct->c_subtype = kv->kv_value;
1315 switch (ct->c_subtype) {
1316 case MESSAGE_RFC822:
1319 case MESSAGE_PARTIAL:
1324 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1325 adios(NULL, "out of memory");
1326 ct->c_ctparams = (void *) p;
1329 ** scan for parameters "id", "number",
1332 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1333 if (!mh_strcasecmp(*ap, "id")) {
1334 p->pm_partid = getcpy(*ep);
1337 if (!mh_strcasecmp(*ap, "number")) {
1338 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1340 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);
1345 if (!mh_strcasecmp(*ap, "total")) {
1346 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1353 if (!p->pm_partid || !p->pm_partno
1354 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1355 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);
1361 case MESSAGE_EXTERNAL:
1368 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1369 adios(NULL, "out of memory");
1370 ct->c_ctparams = (void *) e;
1372 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1373 advise(ct->c_file, "unable to open for reading");
1377 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1379 if (!(p = get_content(fp, ct->c_file, 0))) {
1387 if ((exresult = params_external(ct, 0)) != NOTOK &&
1388 p->c_ceopenfnx == openMail) {
1392 if ((size = ct->c_end - p->c_begin) <= 0) {
1394 content_error(NULL, ct, "empty body for access-type=mail-server");
1398 e->eb_body = bp = mh_xmalloc((unsigned) size);
1399 fseek(p->c_fp, p->c_begin, SEEK_SET);
1401 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1403 adios("failed", "fread");
1405 adios(NULL, "unexpected EOF from fread");
1407 bp += cc, size -= cc;
1414 p->c_end = p->c_begin;
1419 if (exresult == NOTOK)
1421 if (e->eb_flags == NOTOK)
1424 switch (p->c_type) {
1429 if (p->c_subtype != MESSAGE_RFC822)
1433 e->eb_partno = ct->c_partno;
1435 (*p->c_ctinitfnx) (p);
1450 params_external(CT ct, int composing)
1453 struct exbody *e = (struct exbody *) ct->c_ctparams;
1454 CI ci = &ct->c_ctinfo;
1456 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1457 if (!mh_strcasecmp(*ap, "access-type")) {
1458 struct str2init *s2i;
1459 CT p = e->eb_content;
1461 for (s2i = str2methods; s2i->si_key; s2i++)
1462 if (!mh_strcasecmp(*ep, s2i->si_key))
1466 e->eb_flags = NOTOK;
1467 p->c_encoding = CE_EXTERNAL;
1470 e->eb_access = s2i->si_key;
1471 e->eb_flags = s2i->si_val;
1472 p->c_encoding = CE_EXTERNAL;
1474 /* Call the Init function for this external type */
1475 if ((*s2i->si_init)(p) == NOTOK)
1479 if (!mh_strcasecmp(*ap, "name")) {
1483 if (!mh_strcasecmp(*ap, "permission")) {
1484 e->eb_permission = *ep;
1487 if (!mh_strcasecmp(*ap, "site")) {
1491 if (!mh_strcasecmp(*ap, "directory")) {
1495 if (!mh_strcasecmp(*ap, "mode")) {
1499 if (!mh_strcasecmp(*ap, "size")) {
1500 sscanf(*ep, "%lu", &e->eb_size);
1503 if (!mh_strcasecmp(*ap, "server")) {
1507 if (!mh_strcasecmp(*ap, "subject")) {
1508 e->eb_subject = *ep;
1511 if (composing && !mh_strcasecmp(*ap, "body")) {
1512 e->eb_body = getcpy(*ep);
1517 if (!e->eb_access) {
1518 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);
1531 InitApplication(CT ct)
1534 CI ci = &ct->c_ctinfo;
1537 for (kv = SubApplication; kv->kv_key; kv++)
1538 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1540 ct->c_subtype = kv->kv_value;
1547 ** TRANSFER ENCODINGS
1551 init_encoding(CT ct, OpenCEFunc openfnx)
1555 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1556 adios(NULL, "out of memory");
1559 ct->c_ceopenfnx = openfnx;
1560 ct->c_ceclosefnx = close_encoding;
1561 ct->c_cesizefnx = size_encoding;
1568 close_encoding(CT ct)
1572 if (!(ce = ct->c_cefile))
1582 static unsigned long
1583 size_encoding(CT ct)
1591 if (!(ce = ct->c_cefile))
1592 return (ct->c_end - ct->c_begin);
1594 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1595 return (long) st.st_size;
1598 if (stat(ce->ce_file, &st) != NOTOK)
1599 return (long) st.st_size;
1604 if (ct->c_encoding == CE_EXTERNAL)
1605 return (ct->c_end - ct->c_begin);
1608 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1609 return (ct->c_end - ct->c_begin);
1611 if (fstat(fd, &st) != NOTOK)
1612 size = (long) st.st_size;
1616 (*ct->c_ceclosefnx) (ct);
1625 static unsigned char b642nib[0x80] = {
1626 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1627 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1628 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1629 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1630 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1631 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1632 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1633 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1634 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1635 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1636 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1637 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1638 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1639 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1640 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1641 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1648 return init_encoding(ct, openBase64);
1653 openBase64(CT ct, char **file)
1656 int fd, len, skip, own_ct_fp = 0;
1658 unsigned char value, *b, *b1, *b2, *b3;
1659 unsigned char *cp, *ep;
1660 char buffer[BUFSIZ];
1661 /* sbeck -- handle suffixes */
1665 b = (unsigned char *) &bits;
1666 b1 = &b[endian > 0 ? 1 : 2];
1667 b2 = &b[endian > 0 ? 2 : 1];
1668 b3 = &b[endian > 0 ? 3 : 0];
1672 fseek(ce->ce_fp, 0L, SEEK_SET);
1677 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1678 content_error(ce->ce_file, ct,
1679 "unable to fopen for reading");
1685 if (*file == NULL) {
1686 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1689 ce->ce_file = getcpy(*file);
1693 /* sbeck@cise.ufl.edu -- handle suffixes */
1695 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1696 invo_name, ci->ci_type, ci->ci_subtype);
1697 cp = context_find(buffer);
1698 if (cp == NULL || *cp == '\0') {
1699 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1701 cp = context_find(buffer);
1703 if (cp != NULL && *cp != '\0') {
1704 if (ce->ce_unlink) {
1706 ** Temporary file already exists, so we rename to
1707 ** version with extension.
1709 char *file_org = strdup(ce->ce_file);
1710 ce->ce_file = add(cp, ce->ce_file);
1711 if (rename(file_org, ce->ce_file)) {
1712 adios(ce->ce_file, "unable to rename %s to ",
1718 ce->ce_file = add(cp, ce->ce_file);
1722 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1723 content_error(ce->ce_file, ct,
1724 "unable to fopen for reading/writing");
1728 if ((len = ct->c_end - ct->c_begin) < 0)
1729 adios(NULL, "internal error(1)");
1732 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1733 content_error(ct->c_file, ct,
1734 "unable to open for reading");
1744 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1746 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1748 content_error(ct->c_file, ct, "error reading from");
1752 content_error(NULL, ct, "premature eof");
1760 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1765 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1767 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1769 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1773 bits |= value << bitno;
1775 if ((bitno -= 6) < 0) {
1776 putc((char) *b1, ce->ce_fp);
1778 putc((char) *b2, ce->ce_fp);
1780 putc((char) *b3, ce->ce_fp);
1784 if (ferror(ce->ce_fp)) {
1785 content_error(ce->ce_file, ct,
1786 "error writing to");
1789 bitno = 18, bits = 0L, skip = 0;
1795 goto self_delimiting;
1804 fprintf(stderr, "premature ending (bitno %d)\n",
1807 content_error(NULL, ct, "invalid BASE64 encoding");
1812 fseek(ct->c_fp, 0L, SEEK_SET);
1814 if (fflush(ce->ce_fp)) {
1815 content_error(ce->ce_file, ct, "error writing to");
1819 fseek(ce->ce_fp, 0L, SEEK_SET);
1822 *file = ce->ce_file;
1827 return fileno(ce->ce_fp);
1830 free_encoding(ct, 0);
1843 static char hex2nib[0x80] = {
1844 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1845 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1846 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1847 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1848 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1849 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1850 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1851 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
1853 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1854 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1855 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1856 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1857 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1858 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1859 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1866 return init_encoding(ct, openQuoted);
1871 openQuoted(CT ct, char **file)
1873 int cc, len, quoted, own_ct_fp = 0;
1874 unsigned char *cp, *ep;
1875 char buffer[BUFSIZ];
1878 /* sbeck -- handle suffixes */
1883 fseek(ce->ce_fp, 0L, SEEK_SET);
1888 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1889 content_error(ce->ce_file, ct,
1890 "unable to fopen for reading");
1896 if (*file == NULL) {
1897 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1900 ce->ce_file = getcpy(*file);
1904 /* sbeck@cise.ufl.edu -- handle suffixes */
1906 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1907 invo_name, ci->ci_type, ci->ci_subtype);
1908 cp = context_find(buffer);
1909 if (cp == NULL || *cp == '\0') {
1910 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1912 cp = context_find(buffer);
1914 if (cp != NULL && *cp != '\0') {
1915 if (ce->ce_unlink) {
1916 // Temporary file already exists, so we rename to
1917 // version with extension.
1918 char *file_org = strdup(ce->ce_file);
1919 ce->ce_file = add(cp, ce->ce_file);
1920 if (rename(file_org, ce->ce_file)) {
1921 adios(ce->ce_file, "unable to rename %s to ",
1927 ce->ce_file = add(cp, ce->ce_file);
1931 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1932 content_error(ce->ce_file, ct,
1933 "unable to fopen for reading/writing");
1937 if ((len = ct->c_end - ct->c_begin) < 0)
1938 adios(NULL, "internal error(2)");
1941 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1942 content_error(ct->c_file, ct,
1943 "unable to open for reading");
1954 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
1956 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1957 content_error(NULL, ct, "premature eof");
1961 if ((cc = strlen(buffer)) > len)
1965 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1970 for (; cp < ep; cp++) {
1972 /* in an escape sequence */
1974 /* at byte 1 of an escape sequence */
1975 mask = hex2nib[*cp & 0x7f];
1976 /* next is byte 2 */
1979 /* at byte 2 of an escape sequence */
1981 mask |= hex2nib[*cp & 0x7f];
1982 putc(mask, ce->ce_fp);
1983 if (ferror(ce->ce_fp)) {
1984 content_error(ce->ce_file, ct, "error writing to");
1988 ** finished escape sequence; next may
1989 ** be literal or a new escape sequence
1993 /* on to next byte */
1997 /* not in an escape sequence */
2000 ** starting an escape sequence,
2003 if (cp + 1 < ep && cp[1] == '\n') {
2004 /* "=\n" soft line break, eat the \n */
2008 if (cp + 1 >= ep || cp + 2 >= ep) {
2010 ** We don't have 2 bytes left,
2011 ** so this is an invalid escape
2012 ** sequence; just show the raw bytes
2015 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2017 ** Next 2 bytes are hex digits,
2018 ** making this a valid escape
2019 ** sequence; let's decode it (above).
2025 ** One or both of the next 2 is
2026 ** out of range, making this an
2027 ** invalid escape sequence; just
2028 ** show the raw bytes (below).
2033 /* Just show the raw byte. */
2034 putc(*cp, ce->ce_fp);
2035 if (ferror(ce->ce_fp)) {
2036 content_error(ce->ce_file, ct,
2037 "error writing to");
2043 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2047 fseek(ct->c_fp, 0L, SEEK_SET);
2049 if (fflush(ce->ce_fp)) {
2050 content_error(ce->ce_file, ct, "error writing to");
2054 fseek(ce->ce_fp, 0L, SEEK_SET);
2057 *file = ce->ce_file;
2062 return fileno(ce->ce_fp);
2065 free_encoding(ct, 0);
2081 if (init_encoding(ct, open7Bit) == NOTOK)
2084 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2090 open7Bit(CT ct, char **file)
2092 int cc, fd, len, own_ct_fp = 0;
2093 char buffer[BUFSIZ];
2094 /* sbeck -- handle suffixes */
2101 fseek(ce->ce_fp, 0L, SEEK_SET);
2106 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2107 content_error(ce->ce_file, ct,
2108 "unable to fopen for reading");
2114 if (*file == NULL) {
2115 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2118 ce->ce_file = getcpy(*file);
2122 /* sbeck@cise.ufl.edu -- handle suffixes */
2124 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2125 invo_name, ci->ci_type, ci->ci_subtype);
2126 cp = context_find(buffer);
2127 if (cp == NULL || *cp == '\0') {
2128 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2130 cp = context_find(buffer);
2132 if (cp != NULL && *cp != '\0') {
2133 if (ce->ce_unlink) {
2135 ** Temporary file already exists, so we rename to
2136 ** version with extension.
2138 char *file_org = strdup(ce->ce_file);
2139 ce->ce_file = add(cp, ce->ce_file);
2140 if (rename(file_org, ce->ce_file)) {
2141 adios(ce->ce_file, "unable to rename %s to ",
2147 ce->ce_file = add(cp, ce->ce_file);
2151 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2152 content_error(ce->ce_file, ct,
2153 "unable to fopen for reading/writing");
2157 if (ct->c_type == CT_MULTIPART) {
2159 CI ci = &ct->c_ctinfo;
2162 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2164 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2165 strlen(ci->ci_subtype);
2166 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2167 putc(';', ce->ce_fp);
2170 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2173 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2174 fputs("\n\t", ce->ce_fp);
2177 putc(' ', ce->ce_fp);
2180 fprintf(ce->ce_fp, "%s", buffer);
2184 if (ci->ci_comment) {
2185 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2187 fputs("\n\t", ce->ce_fp);
2190 putc(' ', ce->ce_fp);
2193 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2196 fprintf(ce->ce_fp, "\n");
2198 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2200 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2202 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2203 fprintf(ce->ce_fp, "\n");
2206 if ((len = ct->c_end - ct->c_begin) < 0)
2207 adios(NULL, "internal error(3)");
2210 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2211 content_error(ct->c_file, ct,
2212 "unable to open for reading");
2218 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2220 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2222 content_error(ct->c_file, ct, "error reading from");
2226 content_error(NULL, ct, "premature eof");
2234 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2235 if (ferror(ce->ce_fp)) {
2236 content_error(ce->ce_file, ct,
2237 "error writing to");
2242 fseek(ct->c_fp, 0L, SEEK_SET);
2244 if (fflush(ce->ce_fp)) {
2245 content_error(ce->ce_file, ct, "error writing to");
2249 fseek(ce->ce_fp, 0L, SEEK_SET);
2252 *file = ce->ce_file;
2257 return fileno(ce->ce_fp);
2260 free_encoding(ct, 0);
2274 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2276 char cachefile[BUFSIZ];
2279 fseek(ce->ce_fp, 0L, SEEK_SET);
2284 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2285 content_error(ce->ce_file, ct,
2286 "unable to fopen for reading");
2292 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2293 cachefile, sizeof(cachefile)) != NOTOK) {
2294 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2295 ce->ce_file = getcpy(cachefile);
2299 admonish(cachefile, "unable to fopen for reading");
2306 *file = ce->ce_file;
2307 *fd = fileno(ce->ce_fp);
2318 return init_encoding(ct, openFile);
2323 openFile(CT ct, char **file)
2326 char cachefile[BUFSIZ];
2327 struct exbody *e = ct->c_ctexbody;
2328 CE ce = ct->c_cefile;
2330 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2340 content_error(NULL, ct, "missing name parameter");
2344 ce->ce_file = getcpy(e->eb_name);
2347 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2348 content_error(ce->ce_file, ct, "unable to fopen for reading");
2352 if ((!e->eb_permission ||
2353 mh_strcasecmp(e->eb_permission, "read-write")) &&
2354 find_cache(NULL, wcachesw, &cachetype,
2355 e->eb_content->c_id, cachefile, sizeof(cachefile))
2360 mask = umask(cachetype ? ~m_gmprot() : 0222);
2361 if ((fp = fopen(cachefile, "w"))) {
2363 char buffer[BUFSIZ];
2364 FILE *gp = ce->ce_fp;
2366 fseek(gp, 0L, SEEK_SET);
2368 while ((cc = fread(buffer, sizeof(*buffer),
2369 sizeof(buffer), gp)) > 0)
2370 fwrite(buffer, sizeof(*buffer), cc, fp);
2374 admonish(ce->ce_file, "error reading");
2376 } else if (ferror(fp)) {
2377 admonish(cachefile, "error writing");
2385 fseek(ce->ce_fp, 0L, SEEK_SET);
2386 *file = ce->ce_file;
2387 return fileno(ce->ce_fp);
2397 return init_encoding(ct, openFTP);
2402 openFTP(CT ct, char **file)
2404 int cachetype, caching, fd;
2406 char *bp, *ftp, *user, *pass;
2407 char buffer[BUFSIZ], cachefile[BUFSIZ];
2410 static char *username = NULL;
2411 static char *password = NULL;
2418 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2424 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2433 if (!e->eb_name || !e->eb_site) {
2434 content_error(NULL, ct, "missing %s parameter",
2435 e->eb_name ? "site": "name");
2442 pidcheck(pidwait(xpid, NOTOK));
2446 /* Get the buffer ready to go */
2448 buflen = sizeof(buffer);
2451 ** Construct the query message for user
2453 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2459 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2465 snprintf(bp, buflen, "\n using %sFTP from site %s",
2466 e->eb_flags ? "anonymous " : "", e->eb_site);
2471 if (e->eb_size > 0) {
2472 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2477 snprintf(bp, buflen, "? ");
2480 ** Now, check the answer
2482 if (!getanswer(buffer))
2487 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2491 ruserpass(e->eb_site, &username, &password);
2496 ce->ce_unlink = (*file == NULL);
2498 cachefile[0] = '\0';
2499 if ((!e->eb_permission ||
2500 mh_strcasecmp(e->eb_permission, "read-write")) &&
2501 find_cache(NULL, wcachesw, &cachetype,
2502 e->eb_content->c_id, cachefile, sizeof(cachefile))
2504 if (*file == NULL) {
2511 ce->ce_file = getcpy(*file);
2513 ce->ce_file = getcpy(cachefile);
2515 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2517 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2518 content_error (ce->ce_file, ct,
2519 "unable to fopen for reading/writing");
2524 vec[vecp++] = mhbasename(ftp);
2525 vec[vecp++] = e->eb_site;
2528 vec[vecp++] = e->eb_dir;
2529 vec[vecp++] = e->eb_name;
2530 vec[vecp++] = ce->ce_file,
2531 vec[vecp++] = e->eb_mode &&
2532 !mh_strcasecmp(e->eb_mode, "ascii") ?
2538 switch (child_id = fork()) {
2540 adios("fork", "unable to");
2544 close(fileno(ce->ce_fp));
2546 fprintf(stderr, "unable to exec ");
2552 if (pidXwait(child_id, NULL)) {
2553 username = password = NULL;
2562 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2567 mask = umask(cachetype ? ~m_gmprot() : 0222);
2568 if ((fp = fopen(cachefile, "w"))) {
2570 FILE *gp = ce->ce_fp;
2572 fseek(gp, 0L, SEEK_SET);
2574 while ((cc= fread(buffer, sizeof(*buffer),
2575 sizeof(buffer), gp)) > 0)
2576 fwrite(buffer, sizeof(*buffer), cc, fp);
2580 admonish(ce->ce_file, "error reading");
2582 } else if (ferror(fp)) {
2583 admonish(cachefile, "error writing");
2592 fseek(ce->ce_fp, 0L, SEEK_SET);
2593 *file = ce->ce_file;
2594 return fileno(ce->ce_fp);
2605 return init_encoding(ct, openMail);
2610 openMail(CT ct, char **file)
2612 int child_id, fd, vecp;
2614 char *bp, buffer[BUFSIZ], *vec[7];
2615 struct exbody *e = ct->c_ctexbody;
2616 CE ce = ct->c_cefile;
2618 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2627 if (!e->eb_server) {
2628 content_error(NULL, ct, "missing server parameter");
2635 pidcheck(pidwait(xpid, NOTOK));
2639 /* Get buffer ready to go */
2641 buflen = sizeof(buffer);
2643 /* Now, construct query message */
2644 snprintf(bp, buflen, "Retrieve content");
2650 snprintf(bp, buflen, " %s", e->eb_partno);
2656 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2657 e->eb_subject ? e->eb_subject : e->eb_body);
2659 /* Now, check answer */
2660 if (!getanswer(buffer))
2664 vec[vecp++] = "mhmail";
2665 vec[vecp++] = e->eb_server;
2666 vec[vecp++] = "-subject";
2667 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2668 vec[vecp++] = "-body";
2669 vec[vecp++] = e->eb_body;
2672 switch (child_id = fork()) {
2674 advise("fork", "unable to");
2679 fprintf(stderr, "unable to exec ");
2685 if (pidXwait(child_id, NULL) == OK)
2686 advise(NULL, "request sent");
2690 if (*file == NULL) {
2691 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2694 ce->ce_file = getcpy(*file);
2698 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2699 content_error(ce->ce_file, ct,
2700 "unable to fopen for reading/writing");
2705 ** showproc is for mhshow and mhstore, though mhlist -debug
2709 free(ct->c_showproc);
2710 ct->c_showproc = getcpy("true");
2712 fseek(ce->ce_fp, 0L, SEEK_SET);
2713 *file = ce->ce_file;
2714 return fileno(ce->ce_fp);