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 ** Directory to place temp files. This must
28 ** be set before these routines are called.
33 ** Structures for TEXT messages
35 struct k2v SubText[] = {
36 { "plain", TEXT_PLAIN },
37 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
38 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
39 { NULL, TEXT_UNKNOWN } /* this one must be last! */
42 struct k2v Charset[] = {
43 { "us-ascii", CHARSET_USASCII },
44 { "iso-8859-1", CHARSET_LATIN },
45 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
49 ** Structures for MULTIPART messages
51 struct k2v SubMultiPart[] = {
52 { "mixed", MULTI_MIXED },
53 { "alternative", MULTI_ALTERNATE },
54 { "digest", MULTI_DIGEST },
55 { "parallel", MULTI_PARALLEL },
56 { NULL, MULTI_UNKNOWN } /* this one must be last! */
60 ** Structures for MESSAGE messages
62 struct k2v SubMessage[] = {
63 { "rfc822", MESSAGE_RFC822 },
64 { "partial", MESSAGE_PARTIAL },
65 { "external-body", MESSAGE_EXTERNAL },
66 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
70 ** Structure for APPLICATION messages
72 struct k2v SubApplication[] = {
73 { "octet-stream", APPLICATION_OCTETS },
74 { "postscript", APPLICATION_POSTSCRIPT },
75 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
82 int make_intermediates(char *);
83 void content_error(char *, CT, char *, ...);
86 void free_content(CT);
87 void free_encoding(CT, int);
92 static CT get_content(FILE *, char *, int);
93 static int get_comment(CT, unsigned char **, int);
95 static int InitGeneric(CT);
96 static int InitText(CT);
97 static int InitMultiPart(CT);
98 static void reverse_parts(CT);
99 static int InitMessage(CT);
100 static int InitApplication(CT);
101 static int init_encoding(CT, OpenCEFunc);
102 static unsigned long size_encoding(CT);
103 static int InitBase64(CT);
104 static int openBase64(CT, char **);
105 static int InitQuoted(CT);
106 static int openQuoted(CT, char **);
107 static int Init7Bit(CT);
108 static int openExternal(CT, CT, CE, char **, int *);
109 static int InitFile(CT);
110 static int openFile(CT, char **);
111 static int InitFTP(CT);
112 static int openFTP(CT, char **);
113 static int InitMail(CT);
114 static int openMail(CT, char **);
116 struct str2init str2cts[] = {
117 { "application", CT_APPLICATION, InitApplication },
118 { "audio", CT_AUDIO, InitGeneric },
119 { "image", CT_IMAGE, InitGeneric },
120 { "message", CT_MESSAGE, InitMessage },
121 { "multipart", CT_MULTIPART, InitMultiPart },
122 { "text", CT_TEXT, InitText },
123 { "video", CT_VIDEO, InitGeneric },
124 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
125 { NULL, CT_UNKNOWN, NULL },
128 struct str2init str2ces[] = {
129 { "base64", CE_BASE64, InitBase64 },
130 { "quoted-printable", CE_QUOTED, InitQuoted },
131 { "8bit", CE_8BIT, Init7Bit },
132 { "7bit", CE_7BIT, Init7Bit },
133 { "binary", CE_BINARY, Init7Bit },
134 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
135 { NULL, CE_UNKNOWN, NULL },
139 ** NOTE WELL: si_key MUST NOT have value of NOTOK
141 ** si_key is 1 if access method is anonymous.
143 struct str2init str2methods[] = {
144 { "afs", 1, InitFile },
145 { "anon-ftp", 1, InitFTP },
146 { "ftp", 0, InitFTP },
147 { "local-file", 0, InitFile },
148 { "mail-server", 0, InitMail },
156 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
167 ** Main entry point for parsing a MIME message or file.
168 ** It returns the Content structure for the top level
169 ** entity in the file.
172 parse_mime(char *file)
180 ** Check if file is actually standard input
182 if ((is_stdin = (strcmp(file, "-")==0))) {
183 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
185 advise("mhparse", "unable to create temporary file");
188 file = getcpy(tfile);
191 while (fgets(buffer, sizeof(buffer), stdin))
197 advise("stdin", "error reading");
202 advise(file, "error writing");
205 fseek(fp, 0L, SEEK_SET);
206 } else if ((fp = fopen(file, "r")) == NULL) {
207 advise(file, "unable to read");
211 if (!(ct = get_content(fp, file, 1))) {
214 advise(NULL, "unable to decode %s", file);
219 ct->c_unlink = 1; /* temp file to remove */
223 if (ct->c_end == 0L) {
224 fseek(fp, 0L, SEEK_END);
225 ct->c_end = ftell(fp);
228 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
240 ** Main routine for reading/parsing the headers
241 ** of a message content.
243 ** toplevel = 1 # we are at the top level of the message
244 ** toplevel = 0 # we are inside message type or multipart type
245 ** # other than multipart/digest
246 ** toplevel = -1 # we are inside multipart/digest
247 ** NB: on failure we will fclose(in)!
251 get_content(FILE *in, char *file, int toplevel)
254 char buf[BUFSIZ], name[NAMESZ];
259 /* allocate the content structure */
260 if (!(ct = (CT) calloc(1, sizeof(*ct))))
261 adios(NULL, "out of memory");
264 ct->c_file = getcpy(file);
265 ct->c_begin = ftell(ct->c_fp) + 1;
268 ** Parse the header fields for this
269 ** content into a linked list.
271 for (compnum = 1, state = FLD;;) {
272 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
278 /* get copies of the buffers */
282 /* if necessary, get rest of field */
283 while (state == FLDPLUS) {
284 state = m_getfld(state, name, buf,
286 vp = add(buf, vp); /* add to previous value */
289 /* Now add the header data to the list */
290 add_header(ct, np, vp);
292 /* continue, if this isn't the last header field */
293 if (state != FLDEOF) {
294 ct->c_begin = ftell(in) + 1;
301 ct->c_begin = ftell(in) - strlen(buf);
305 ct->c_begin = ftell(in);
310 adios(NULL, "message format error in component #%d",
314 adios(NULL, "getfld() returned %d", state);
317 /* break out of the loop */
322 ** Read the content headers. We will parse the
323 ** MIME related header fields into their various
324 ** structures and set internal flags related to
325 ** content type/subtype, etc.
328 hp = ct->c_first_hf; /* start at first header field */
330 /* Get MIME-Version field */
331 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
334 unsigned char *cp, *dp;
337 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
340 ct->c_vrsn = getcpy(hp->value);
342 /* Now, cleanup this field */
347 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
349 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
354 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
356 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
359 for (dp = cp; istoken(*dp); dp++)
363 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
366 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
369 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
370 /* Get Content-Type field */
371 struct str2init *s2i;
372 CI ci = &ct->c_ctinfo;
374 /* Check if we've already seen a Content-Type header */
376 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
380 /* Parse the Content-Type field */
381 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
385 ** Set the Init function and the internal
386 ** flag for this content type.
388 for (s2i = str2cts; s2i->si_key; s2i++)
389 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
391 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
393 ct->c_type = s2i->si_val;
394 ct->c_ctinitfnx = s2i->si_init;
396 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
397 /* Get Content-Transfer-Encoding field */
399 unsigned char *cp, *dp;
400 struct str2init *s2i;
403 ** Check if we've already seen the
404 ** Content-Transfer-Encoding field
407 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
411 /* get copy of this field */
412 ct->c_celine = cp = getcpy(hp->value);
416 for (dp = cp; istoken(*dp); dp++)
422 ** Find the internal flag and Init function
423 ** for this transfer encoding.
425 for (s2i = str2ces; s2i->si_key; s2i++)
426 if (!mh_strcasecmp(cp, s2i->si_key))
428 if (!s2i->si_key && !uprf(cp, "X-"))
431 ct->c_encoding = s2i->si_val;
433 /* Call the Init function for this encoding */
434 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
437 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
438 /* Get Content-ID field */
439 ct->c_id = add(hp->value, ct->c_id);
441 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
442 /* Get Content-Description field */
443 ct->c_descr = add(hp->value, ct->c_descr);
445 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
446 /* Get Content-Disposition field */
447 ct->c_dispo = add(hp->value, ct->c_dispo);
451 hp = hp->next; /* next header field */
455 ** Check if we saw a Content-Type field.
456 ** If not, then assign a default value for
457 ** it, and the Init function.
461 ** If we are inside a multipart/digest message,
462 ** so default type is message/rfc822
465 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
467 ct->c_type = CT_MESSAGE;
468 ct->c_ctinitfnx = InitMessage;
471 ** Else default type is text/plain
473 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
475 ct->c_type = CT_TEXT;
476 ct->c_ctinitfnx = InitText;
480 /* Use default Transfer-Encoding, if necessary */
482 ct->c_encoding = CE_7BIT;
495 ** small routine to add header field to list
499 add_header(CT ct, char *name, char *value)
503 /* allocate header field structure */
504 hp = mh_xmalloc(sizeof(*hp));
506 /* link data into header structure */
511 /* link header structure into the list */
512 if (ct->c_first_hf == NULL) {
513 ct->c_first_hf = hp; /* this is the first */
516 ct->c_last_hf->next = hp; /* add it to the end */
525 ** Make sure that buf contains at least one appearance of name,
526 ** followed by =. If not, insert both name and value, just after
527 ** first semicolon, if any. Note that name should not contain a
528 ** trailing =. And quotes will be added around the value. Typical
529 ** usage: make sure that a Content-Disposition header contains
530 ** filename="foo". If it doesn't and value does, use value from
534 incl_name_value(unsigned char *buf, char *name, char *value) {
537 /* Assume that name is non-null. */
539 char *name_plus_equal = concat(name, "=", NULL);
541 if (!strstr(buf, name_plus_equal)) {
544 char *prefix, *suffix;
546 /* Trim trailing space, esp. newline. */
547 for (cp = &buf[strlen(buf) - 1];
548 cp >= buf && isspace(*cp); --cp) {
552 insertion = concat("; ", name, "=", "\"", value, "\"",
556 ** Insert at first semicolon, if any.
557 ** If none, append to end.
559 prefix = getcpy(buf);
560 if ((cp = strchr(prefix, ';'))) {
561 suffix = concat(cp, NULL);
563 newbuf = concat(prefix, insertion, suffix,
568 newbuf = concat(buf, insertion, "\n", NULL);
576 free(name_plus_equal);
583 ** Extract just name_suffix="foo", if any, from value. If there isn't
584 ** one, return the entire value. Note that, for example, a name_suffix
585 ** of name will match filename="foo", and return foo.
588 extract_name_value(char *name_suffix, char *value) {
589 char *extracted_name_value = value;
590 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
591 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
594 free(name_suffix_plus_quote);
595 if (name_suffix_equals) {
596 char *name_suffix_begin;
599 for (cp = name_suffix_equals; *cp != '"'; ++cp)
601 name_suffix_begin = ++cp;
602 /* Find second \". */
603 for (; *cp != '"'; ++cp)
606 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
607 memcpy(extracted_name_value, name_suffix_begin,
608 cp - name_suffix_begin);
609 extracted_name_value[cp - name_suffix_begin] = '\0';
612 return extracted_name_value;
616 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
617 ** directives. Fills in the information of the CTinfo structure.
620 get_ctinfo(unsigned char *cp, CT ct, int magic)
629 i = strlen(invo_name) + 2;
631 /* store copy of Content-Type line */
632 cp = ct->c_ctline = getcpy(cp);
634 while (isspace(*cp)) /* trim leading spaces */
637 /* change newlines to spaces */
638 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
641 /* trim trailing spaces */
642 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
648 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
650 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
653 for (dp = cp; istoken(*dp); dp++)
656 ci->ci_type = getcpy(cp); /* store content type */
660 advise(NULL, "invalid %s: field in message %s (empty type)",
661 TYPE_FIELD, ct->c_file);
665 /* down case the content type string */
666 for (dp = ci->ci_type; *dp; dp++)
667 if (isalpha(*dp) && isupper(*dp))
673 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
678 ci->ci_subtype = getcpy("");
686 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
689 for (dp = cp; istoken(*dp); dp++)
692 ci->ci_subtype = getcpy(cp); /* store the content subtype */
695 if (!*ci->ci_subtype) {
696 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
700 /* down case the content subtype string */
701 for (dp = ci->ci_subtype; *dp; dp++)
702 if (isalpha(*dp) && isupper(*dp))
709 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
713 ** Parse attribute/value pairs given with Content-Type
715 ep = (ap = ci->ci_attrs) + NPARMS;
721 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
729 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
733 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
737 /* down case the attribute name */
738 for (dp = cp; istoken(*dp); dp++)
739 if (isalpha(*dp) && isupper(*dp))
742 for (up = dp; isspace(*dp);)
744 if (dp == cp || *dp != '=') {
745 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);
749 vp = (*ap = getcpy(cp)) + (up - cp);
751 for (dp++; isspace(*dp);)
754 /* now add the attribute value */
755 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
758 for (cp = ++dp, dp = vp;;) {
762 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
767 if ((c = *cp++) == '\0')
782 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
787 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
795 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
800 ** Get any <Content-Id> given in buffer
802 if (magic && *cp == '<') {
807 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
808 advise(NULL, "invalid ID in message %s", ct->c_file);
814 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
825 ** Get any [Content-Description] given in buffer.
827 if (magic && *cp == '[') {
829 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
833 advise(NULL, "invalid description in message %s",
842 ct->c_descr = concat(ct->c_descr, "\n", NULL);
853 ** Get any {Content-Disposition} given in buffer.
855 if (magic && *cp == '{') {
857 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
861 advise(NULL, "invalid disposition in message %s",
870 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
881 ** Check if anything is left over
885 ci->ci_magic = getcpy(cp);
888 ** If there is a Content-Disposition header and
889 ** it doesn't have a *filename=, extract it from
890 ** the magic contents. The mhbasename call skips
891 ** any leading directory components.
894 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
896 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
904 get_comment(CT ct, unsigned char **ap, int istype)
909 char c, buffer[BUFSIZ], *dp;
921 advise(NULL, "invalid comment in message %s's %s: field",
922 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
927 if ((c = *cp++) == '\0')
950 if ((dp = ci->ci_comment)) {
951 ci->ci_comment = concat(dp, " ", buffer, NULL);
954 ci->ci_comment = getcpy(buffer);
969 ** Handles content types audio, image, and video.
970 ** There's not much to do right here.
976 return OK; /* not much to do here */
989 char **ap, **ep, *cp;
992 CI ci = &ct->c_ctinfo;
994 /* check for missing subtype */
995 if (!*ci->ci_subtype)
996 ci->ci_subtype = add("plain", ci->ci_subtype);
999 for (kv = SubText; kv->kv_key; kv++)
1000 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1002 ct->c_subtype = kv->kv_value;
1004 /* allocate text character set structure */
1005 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1006 adios(NULL, "out of memory");
1007 ct->c_ctparams = (void *) t;
1009 /* scan for charset parameter */
1010 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1011 if (!mh_strcasecmp(*ap, "charset"))
1014 /* check if content specified a character set */
1016 /* match character set or set to CHARSET_UNKNOWN */
1017 for (kv = Charset; kv->kv_key; kv++) {
1018 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1023 t->tx_charset = kv->kv_value;
1025 t->tx_charset = CHARSET_UNSPECIFIED;
1029 ** If we can not handle character set natively,
1030 ** then check profile for string to modify the
1031 ** terminal or display method.
1033 ** termproc is for mhshow, though mhlist -debug prints it, too.
1035 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1036 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1038 if ((cp = context_find(buffer)))
1039 ct->c_termproc = getcpy(cp);
1051 InitMultiPart(CT ct)
1055 unsigned char *cp, *dp;
1057 char *bp, buffer[BUFSIZ];
1058 struct multipart *m;
1060 struct part *part, **next;
1061 CI ci = &ct->c_ctinfo;
1066 ** The encoding for multipart messages must be either
1067 ** 7bit, 8bit, or binary (per RFC2045).
1069 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1070 && ct->c_encoding != CE_BINARY) {
1071 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);
1076 for (kv = SubMultiPart; kv->kv_key; kv++)
1077 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1079 ct->c_subtype = kv->kv_value;
1082 ** Check for "boundary" parameter, which is
1083 ** required for multipart messages.
1086 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1087 if (!mh_strcasecmp(*ap, "boundary")) {
1093 /* complain if boundary parameter is missing */
1095 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);
1099 /* allocate primary structure for multipart info */
1100 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1101 adios(NULL, "out of memory");
1102 ct->c_ctparams = (void *) m;
1104 /* check if boundary parameter contains only whitespace characters */
1105 for (cp = bp; isspace(*cp); cp++)
1108 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);
1112 /* remove trailing whitespace from boundary parameter */
1113 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1118 /* record boundary separators */
1119 m->mp_start = concat(bp, "\n", NULL);
1120 m->mp_stop = concat(bp, "--\n", NULL);
1122 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1123 advise(ct->c_file, "unable to open for reading");
1127 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1129 next = &m->mp_parts;
1133 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1137 pos += strlen(buffer);
1138 if (buffer[0] != '-' || buffer[1] != '-')
1141 if (strcmp(buffer + 2, m->mp_start)!=0)
1144 if ((part = (struct part *) calloc(1, sizeof(*part)))
1146 adios(NULL, "out of memory");
1148 next = &part->mp_next;
1150 if (!(p = get_content(fp, ct->c_file,
1151 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1158 fseek(fp, pos, SEEK_SET);
1161 if (strcmp(buffer + 2, m->mp_start) == 0) {
1165 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1166 if (p->c_end < p->c_begin)
1167 p->c_begin = p->c_end;
1172 if (strcmp(buffer + 2, m->mp_stop) == 0)
1178 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1179 if (!inout && part) {
1181 p->c_end = ct->c_end;
1183 if (p->c_begin >= p->c_end) {
1184 for (next = &m->mp_parts; *next != part;
1185 next = &((*next)->mp_next))
1189 free((char *) part);
1194 /* reverse the order of the parts for multipart/alternative */
1195 if (ct->c_subtype == MULTI_ALTERNATE)
1199 ** label all subparts with part number, and
1200 ** then initialize the content of the subpart.
1205 char partnam[BUFSIZ];
1208 snprintf(partnam, sizeof(partnam), "%s.",
1210 pp = partnam + strlen(partnam);
1215 for (part = m->mp_parts, partnum = 1; part;
1216 part = part->mp_next, partnum++) {
1219 sprintf(pp, "%d", partnum);
1220 p->c_partno = getcpy(partnam);
1222 /* initialize the content of the subparts */
1223 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1238 ** reverse the order of the parts of a multipart
1242 reverse_parts(CT ct)
1245 struct multipart *m;
1246 struct part **base, **bmp, **next, *part;
1248 m = (struct multipart *) ct->c_ctparams;
1250 /* if only one part, just return */
1251 if (!m->mp_parts || !m->mp_parts->mp_next)
1254 /* count number of parts */
1256 for (part = m->mp_parts; part; part = part->mp_next)
1259 /* allocate array of pointers to the parts */
1260 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1261 adios(NULL, "out of memory");
1264 /* point at all the parts */
1265 for (part = m->mp_parts; part; part = part->mp_next)
1269 /* reverse the order of the parts */
1270 next = &m->mp_parts;
1271 for (bmp--; bmp >= base; bmp--) {
1274 next = &part->mp_next;
1278 /* free array of pointers */
1279 free((char *) base);
1291 CI ci = &ct->c_ctinfo;
1293 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1294 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1298 /* check for missing subtype */
1299 if (!*ci->ci_subtype)
1300 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1303 for (kv = SubMessage; kv->kv_key; kv++)
1304 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1306 ct->c_subtype = kv->kv_value;
1308 switch (ct->c_subtype) {
1309 case MESSAGE_RFC822:
1312 case MESSAGE_PARTIAL:
1317 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1318 adios(NULL, "out of memory");
1319 ct->c_ctparams = (void *) p;
1322 ** scan for parameters "id", "number",
1325 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1326 if (!mh_strcasecmp(*ap, "id")) {
1327 p->pm_partid = getcpy(*ep);
1330 if (!mh_strcasecmp(*ap, "number")) {
1331 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1333 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);
1338 if (!mh_strcasecmp(*ap, "total")) {
1339 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1346 if (!p->pm_partid || !p->pm_partno
1347 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1348 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);
1354 case MESSAGE_EXTERNAL:
1361 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1362 adios(NULL, "out of memory");
1363 ct->c_ctparams = (void *) e;
1365 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1366 advise(ct->c_file, "unable to open for reading");
1370 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1372 if (!(p = get_content(fp, ct->c_file, 0))) {
1380 if ((exresult = params_external(ct, 0)) != NOTOK &&
1381 p->c_ceopenfnx == openMail) {
1385 if ((size = ct->c_end - p->c_begin) <= 0) {
1387 content_error(NULL, ct, "empty body for access-type=mail-server");
1391 e->eb_body = bp = mh_xmalloc((unsigned) size);
1392 fseek(p->c_fp, p->c_begin, SEEK_SET);
1394 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1396 adios("failed", "fread");
1398 adios(NULL, "unexpected EOF from fread");
1400 bp += cc, size -= cc;
1407 p->c_end = p->c_begin;
1412 if (exresult == NOTOK)
1414 if (e->eb_flags == NOTOK)
1417 switch (p->c_type) {
1422 if (p->c_subtype != MESSAGE_RFC822)
1426 e->eb_partno = ct->c_partno;
1428 (*p->c_ctinitfnx) (p);
1443 params_external(CT ct, int composing)
1446 struct exbody *e = (struct exbody *) ct->c_ctparams;
1447 CI ci = &ct->c_ctinfo;
1449 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1450 if (!mh_strcasecmp(*ap, "access-type")) {
1451 struct str2init *s2i;
1452 CT p = e->eb_content;
1454 for (s2i = str2methods; s2i->si_key; s2i++)
1455 if (!mh_strcasecmp(*ep, s2i->si_key))
1459 e->eb_flags = NOTOK;
1460 p->c_encoding = CE_EXTERNAL;
1463 e->eb_access = s2i->si_key;
1464 e->eb_flags = s2i->si_val;
1465 p->c_encoding = CE_EXTERNAL;
1467 /* Call the Init function for this external type */
1468 if ((*s2i->si_init)(p) == NOTOK)
1472 if (!mh_strcasecmp(*ap, "name")) {
1476 if (!mh_strcasecmp(*ap, "permission")) {
1477 e->eb_permission = *ep;
1480 if (!mh_strcasecmp(*ap, "site")) {
1484 if (!mh_strcasecmp(*ap, "directory")) {
1488 if (!mh_strcasecmp(*ap, "mode")) {
1492 if (!mh_strcasecmp(*ap, "size")) {
1493 sscanf(*ep, "%lu", &e->eb_size);
1496 if (!mh_strcasecmp(*ap, "server")) {
1500 if (!mh_strcasecmp(*ap, "subject")) {
1501 e->eb_subject = *ep;
1504 if (composing && !mh_strcasecmp(*ap, "body")) {
1505 e->eb_body = getcpy(*ep);
1510 if (!e->eb_access) {
1511 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);
1524 InitApplication(CT ct)
1527 CI ci = &ct->c_ctinfo;
1530 for (kv = SubApplication; kv->kv_key; kv++)
1531 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1533 ct->c_subtype = kv->kv_value;
1540 ** TRANSFER ENCODINGS
1544 init_encoding(CT ct, OpenCEFunc openfnx)
1548 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1549 adios(NULL, "out of memory");
1552 ct->c_ceopenfnx = openfnx;
1553 ct->c_ceclosefnx = close_encoding;
1554 ct->c_cesizefnx = size_encoding;
1561 close_encoding(CT ct)
1565 if (!(ce = ct->c_cefile))
1575 static unsigned long
1576 size_encoding(CT ct)
1584 if (!(ce = ct->c_cefile))
1585 return (ct->c_end - ct->c_begin);
1587 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1588 return (long) st.st_size;
1591 if (stat(ce->ce_file, &st) != NOTOK)
1592 return (long) st.st_size;
1597 if (ct->c_encoding == CE_EXTERNAL)
1598 return (ct->c_end - ct->c_begin);
1601 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1602 return (ct->c_end - ct->c_begin);
1604 if (fstat(fd, &st) != NOTOK)
1605 size = (long) st.st_size;
1609 (*ct->c_ceclosefnx) (ct);
1618 static unsigned char b642nib[0x80] = {
1619 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1620 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1621 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1622 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1623 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1624 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1625 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1626 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1627 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1628 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1629 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1630 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1631 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1632 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1633 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1634 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1641 return init_encoding(ct, openBase64);
1646 openBase64(CT ct, char **file)
1649 int fd, len, skip, own_ct_fp = 0;
1651 unsigned char value, *b, *b1, *b2, *b3;
1652 unsigned char *cp, *ep;
1653 char buffer[BUFSIZ];
1654 /* sbeck -- handle suffixes */
1658 b = (unsigned char *) &bits;
1659 b1 = &b[endian > 0 ? 1 : 2];
1660 b2 = &b[endian > 0 ? 2 : 1];
1661 b3 = &b[endian > 0 ? 3 : 0];
1665 fseek(ce->ce_fp, 0L, SEEK_SET);
1670 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1671 content_error(ce->ce_file, ct,
1672 "unable to fopen for reading");
1678 if (*file == NULL) {
1679 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1682 ce->ce_file = getcpy(*file);
1686 /* sbeck@cise.ufl.edu -- handle suffixes */
1688 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1689 invo_name, ci->ci_type, ci->ci_subtype);
1690 cp = context_find(buffer);
1691 if (cp == NULL || *cp == '\0') {
1692 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1694 cp = context_find(buffer);
1696 if (cp != NULL && *cp != '\0') {
1697 if (ce->ce_unlink) {
1699 ** Temporary file already exists, so we rename to
1700 ** version with extension.
1702 char *file_org = strdup(ce->ce_file);
1703 ce->ce_file = add(cp, ce->ce_file);
1704 if (rename(file_org, ce->ce_file)) {
1705 adios(ce->ce_file, "unable to rename %s to ",
1711 ce->ce_file = add(cp, ce->ce_file);
1715 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1716 content_error(ce->ce_file, ct,
1717 "unable to fopen for reading/writing");
1721 if ((len = ct->c_end - ct->c_begin) < 0)
1722 adios(NULL, "internal error(1)");
1725 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1726 content_error(ct->c_file, ct,
1727 "unable to open for reading");
1737 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1739 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1741 content_error(ct->c_file, ct, "error reading from");
1745 content_error(NULL, ct, "premature eof");
1753 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1758 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1760 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1762 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1766 bits |= value << bitno;
1768 if ((bitno -= 6) < 0) {
1769 putc((char) *b1, ce->ce_fp);
1771 putc((char) *b2, ce->ce_fp);
1773 putc((char) *b3, ce->ce_fp);
1777 if (ferror(ce->ce_fp)) {
1778 content_error(ce->ce_file, ct,
1779 "error writing to");
1782 bitno = 18, bits = 0L, skip = 0;
1788 goto self_delimiting;
1797 fprintf(stderr, "premature ending (bitno %d)\n",
1800 content_error(NULL, ct, "invalid BASE64 encoding");
1805 fseek(ct->c_fp, 0L, SEEK_SET);
1807 if (fflush(ce->ce_fp)) {
1808 content_error(ce->ce_file, ct, "error writing to");
1812 fseek(ce->ce_fp, 0L, SEEK_SET);
1815 *file = ce->ce_file;
1820 return fileno(ce->ce_fp);
1823 free_encoding(ct, 0);
1836 static char hex2nib[0x80] = {
1837 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1838 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1839 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1840 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1841 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1842 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1843 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1844 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1845 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 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, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1850 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1851 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1852 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1859 return init_encoding(ct, openQuoted);
1864 openQuoted(CT ct, char **file)
1866 int cc, len, quoted, own_ct_fp = 0;
1867 unsigned char *cp, *ep;
1868 char buffer[BUFSIZ];
1869 unsigned char mask = 0;
1871 /* sbeck -- handle suffixes */
1876 fseek(ce->ce_fp, 0L, SEEK_SET);
1881 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1882 content_error(ce->ce_file, ct,
1883 "unable to fopen for reading");
1889 if (*file == NULL) {
1890 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1893 ce->ce_file = getcpy(*file);
1897 /* sbeck@cise.ufl.edu -- handle suffixes */
1899 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1900 invo_name, ci->ci_type, ci->ci_subtype);
1901 cp = context_find(buffer);
1902 if (cp == NULL || *cp == '\0') {
1903 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1905 cp = context_find(buffer);
1907 if (cp != NULL && *cp != '\0') {
1908 if (ce->ce_unlink) {
1909 // Temporary file already exists, so we rename to
1910 // version with extension.
1911 char *file_org = strdup(ce->ce_file);
1912 ce->ce_file = add(cp, ce->ce_file);
1913 if (rename(file_org, ce->ce_file)) {
1914 adios(ce->ce_file, "unable to rename %s to ",
1920 ce->ce_file = add(cp, ce->ce_file);
1924 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1925 content_error(ce->ce_file, ct,
1926 "unable to fopen for reading/writing");
1930 if ((len = ct->c_end - ct->c_begin) < 0)
1931 adios(NULL, "internal error(2)");
1934 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1935 content_error(ct->c_file, ct,
1936 "unable to open for reading");
1944 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
1946 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
1947 content_error(NULL, ct, "premature eof");
1951 if ((cc = strlen(buffer)) > len)
1955 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
1960 for (; cp < ep; cp++) {
1962 /* in an escape sequence */
1964 /* at byte 1 of an escape sequence */
1965 mask = hex2nib[*cp & 0x7f];
1966 /* next is byte 2 */
1969 /* at byte 2 of an escape sequence */
1971 mask |= hex2nib[*cp & 0x7f];
1972 putc(mask, ce->ce_fp);
1973 if (ferror(ce->ce_fp)) {
1974 content_error(ce->ce_file, ct, "error writing to");
1978 ** finished escape sequence; next may
1979 ** be literal or a new escape sequence
1983 /* on to next byte */
1987 /* not in an escape sequence */
1990 ** starting an escape sequence,
1993 if (cp + 1 < ep && cp[1] == '\n') {
1994 /* "=\n" soft line break, eat the \n */
1998 if (cp + 1 >= ep || cp + 2 >= ep) {
2000 ** We don't have 2 bytes left,
2001 ** so this is an invalid escape
2002 ** sequence; just show the raw bytes
2005 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2007 ** Next 2 bytes are hex digits,
2008 ** making this a valid escape
2009 ** sequence; let's decode it (above).
2015 ** One or both of the next 2 is
2016 ** out of range, making this an
2017 ** invalid escape sequence; just
2018 ** show the raw bytes (below).
2023 /* Just show the raw byte. */
2024 putc(*cp, ce->ce_fp);
2025 if (ferror(ce->ce_fp)) {
2026 content_error(ce->ce_file, ct,
2027 "error writing to");
2033 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2037 fseek(ct->c_fp, 0L, SEEK_SET);
2039 if (fflush(ce->ce_fp)) {
2040 content_error(ce->ce_file, ct, "error writing to");
2044 fseek(ce->ce_fp, 0L, SEEK_SET);
2047 *file = ce->ce_file;
2052 return fileno(ce->ce_fp);
2055 free_encoding(ct, 0);
2071 if (init_encoding(ct, open7Bit) == NOTOK)
2074 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2080 open7Bit(CT ct, char **file)
2082 int cc, fd, len, own_ct_fp = 0;
2083 char buffer[BUFSIZ];
2084 /* sbeck -- handle suffixes */
2091 fseek(ce->ce_fp, 0L, SEEK_SET);
2096 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2097 content_error(ce->ce_file, ct,
2098 "unable to fopen for reading");
2104 if (*file == NULL) {
2105 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2108 ce->ce_file = getcpy(*file);
2112 /* sbeck@cise.ufl.edu -- handle suffixes */
2114 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2115 invo_name, ci->ci_type, ci->ci_subtype);
2116 cp = context_find(buffer);
2117 if (cp == NULL || *cp == '\0') {
2118 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2120 cp = context_find(buffer);
2122 if (cp != NULL && *cp != '\0') {
2123 if (ce->ce_unlink) {
2125 ** Temporary file already exists, so we rename to
2126 ** version with extension.
2128 char *file_org = strdup(ce->ce_file);
2129 ce->ce_file = add(cp, ce->ce_file);
2130 if (rename(file_org, ce->ce_file)) {
2131 adios(ce->ce_file, "unable to rename %s to ",
2137 ce->ce_file = add(cp, ce->ce_file);
2141 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2142 content_error(ce->ce_file, ct,
2143 "unable to fopen for reading/writing");
2147 if (ct->c_type == CT_MULTIPART) {
2149 CI ci = &ct->c_ctinfo;
2152 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2154 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2155 strlen(ci->ci_subtype);
2156 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2157 putc(';', ce->ce_fp);
2160 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2163 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2164 fputs("\n\t", ce->ce_fp);
2167 putc(' ', ce->ce_fp);
2170 fprintf(ce->ce_fp, "%s", buffer);
2174 if (ci->ci_comment) {
2175 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2177 fputs("\n\t", ce->ce_fp);
2180 putc(' ', ce->ce_fp);
2183 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2186 fprintf(ce->ce_fp, "\n");
2188 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2190 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2192 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2193 fprintf(ce->ce_fp, "\n");
2196 if ((len = ct->c_end - ct->c_begin) < 0)
2197 adios(NULL, "internal error(3)");
2200 if ((ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2201 content_error(ct->c_file, ct,
2202 "unable to open for reading");
2208 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2210 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2212 content_error(ct->c_file, ct, "error reading from");
2216 content_error(NULL, ct, "premature eof");
2224 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2225 if (ferror(ce->ce_fp)) {
2226 content_error(ce->ce_file, ct,
2227 "error writing to");
2232 fseek(ct->c_fp, 0L, SEEK_SET);
2234 if (fflush(ce->ce_fp)) {
2235 content_error(ce->ce_file, ct, "error writing to");
2239 fseek(ce->ce_fp, 0L, SEEK_SET);
2242 *file = ce->ce_file;
2247 return fileno(ce->ce_fp);
2250 free_encoding(ct, 0);
2264 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2267 fseek(ce->ce_fp, 0L, SEEK_SET);
2272 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2273 content_error(ce->ce_file, ct,
2274 "unable to fopen for reading");
2283 *file = ce->ce_file;
2284 *fd = fileno(ce->ce_fp);
2295 return init_encoding(ct, openFile);
2300 openFile(CT ct, char **file)
2303 struct exbody *e = ct->c_ctexbody;
2304 CE ce = ct->c_cefile;
2306 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2316 content_error(NULL, ct, "missing name parameter");
2320 ce->ce_file = getcpy(e->eb_name);
2323 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2324 content_error(ce->ce_file, ct, "unable to fopen for reading");
2328 fseek(ce->ce_fp, 0L, SEEK_SET);
2329 *file = ce->ce_file;
2330 return fileno(ce->ce_fp);
2340 return init_encoding(ct, openFTP);
2345 openFTP(CT ct, char **file)
2349 char *bp, *ftp, *user, *pass;
2350 char buffer[BUFSIZ];
2353 static char *username = NULL;
2354 static char *password = NULL;
2361 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2367 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2376 if (!e->eb_name || !e->eb_site) {
2377 content_error(NULL, ct, "missing %s parameter",
2378 e->eb_name ? "site": "name");
2385 pidcheck(pidwait(xpid, NOTOK));
2389 /* Get the buffer ready to go */
2391 buflen = sizeof(buffer);
2394 ** Construct the query message for user
2396 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2402 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2408 snprintf(bp, buflen, "\n using %sFTP from site %s",
2409 e->eb_flags ? "anonymous " : "", e->eb_site);
2414 if (e->eb_size > 0) {
2415 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2420 snprintf(bp, buflen, "? ");
2423 ** Now, check the answer
2425 if (!getanswer(buffer))
2430 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2434 ruserpass(e->eb_site, &username, &password);
2439 ce->ce_unlink = (*file == NULL);
2442 ce->ce_file = getcpy(*file);
2444 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2446 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2447 content_error (ce->ce_file, ct,
2448 "unable to fopen for reading/writing");
2453 vec[vecp++] = mhbasename(ftp);
2454 vec[vecp++] = e->eb_site;
2457 vec[vecp++] = e->eb_dir;
2458 vec[vecp++] = e->eb_name;
2459 vec[vecp++] = ce->ce_file,
2460 vec[vecp++] = e->eb_mode &&
2461 !mh_strcasecmp(e->eb_mode, "ascii") ?
2467 switch (child_id = fork()) {
2469 adios("fork", "unable to");
2473 close(fileno(ce->ce_fp));
2475 fprintf(stderr, "unable to exec ");
2481 if (pidXwait(child_id, NULL)) {
2482 username = password = NULL;
2489 fseek(ce->ce_fp, 0L, SEEK_SET);
2490 *file = ce->ce_file;
2491 return fileno(ce->ce_fp);
2502 return init_encoding(ct, openMail);
2507 openMail(CT ct, char **file)
2509 int child_id, fd, vecp;
2511 char *bp, buffer[BUFSIZ], *vec[7];
2512 struct exbody *e = ct->c_ctexbody;
2513 CE ce = ct->c_cefile;
2515 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2524 if (!e->eb_server) {
2525 content_error(NULL, ct, "missing server parameter");
2532 pidcheck(pidwait(xpid, NOTOK));
2536 /* Get buffer ready to go */
2538 buflen = sizeof(buffer);
2540 /* Now, construct query message */
2541 snprintf(bp, buflen, "Retrieve content");
2547 snprintf(bp, buflen, " %s", e->eb_partno);
2553 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2554 e->eb_subject ? e->eb_subject : e->eb_body);
2556 /* Now, check answer */
2557 if (!getanswer(buffer))
2561 vec[vecp++] = "mhmail";
2562 vec[vecp++] = e->eb_server;
2563 vec[vecp++] = "-subject";
2564 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2565 vec[vecp++] = "-body";
2566 vec[vecp++] = e->eb_body;
2569 switch (child_id = fork()) {
2571 advise("fork", "unable to");
2576 fprintf(stderr, "unable to exec ");
2582 if (pidXwait(child_id, NULL) == OK)
2583 advise(NULL, "request sent");
2587 if (*file == NULL) {
2588 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2591 ce->ce_file = getcpy(*file);
2595 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2596 content_error(ce->ce_file, ct,
2597 "unable to fopen for reading/writing");
2602 ** showproc is for mhshow and mhstore, though mhlist -debug
2606 free(ct->c_showproc);
2607 ct->c_showproc = getcpy("true");
2609 fseek(ce->ce_fp, 0L, SEEK_SET);
2610 *file = ce->ce_file;
2611 return fileno(ce->ce_fp);