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>
19 #include <h/mhparse.h>
22 #ifdef HAVE_SYS_WAIT_H
23 # include <sys/wait.h>
29 extern int endian; /* mhmisc.c */
31 extern pid_t xpid; /* mhshowsbr.c */
34 extern int rcachesw; /* mhcachesbr.c */
35 extern int wcachesw; /* mhcachesbr.c */
37 int checksw = 0; /* check Content-MD5 field */
40 ** Directory to place temp files. This must
41 ** be set before these routines are called.
46 ** Structures for TEXT messages
48 struct k2v SubText[] = {
49 { "plain", TEXT_PLAIN },
50 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
51 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
52 { NULL, TEXT_UNKNOWN } /* this one must be last! */
55 struct k2v Charset[] = {
56 { "us-ascii", CHARSET_USASCII },
57 { "iso-8859-1", CHARSET_LATIN },
58 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
62 ** Structures for MULTIPART messages
64 struct k2v SubMultiPart[] = {
65 { "mixed", MULTI_MIXED },
66 { "alternative", MULTI_ALTERNATE },
67 { "digest", MULTI_DIGEST },
68 { "parallel", MULTI_PARALLEL },
69 { NULL, MULTI_UNKNOWN } /* this one must be last! */
73 ** Structures for MESSAGE messages
75 struct k2v SubMessage[] = {
76 { "rfc822", MESSAGE_RFC822 },
77 { "partial", MESSAGE_PARTIAL },
78 { "external-body", MESSAGE_EXTERNAL },
79 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
83 ** Structure for APPLICATION messages
85 struct k2v SubApplication[] = {
86 { "octet-stream", APPLICATION_OCTETS },
87 { "postscript", APPLICATION_POSTSCRIPT },
88 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
93 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
96 int find_cache(CT, int, int *, char *, char *, int);
100 int type_ok(CT, int);
101 int make_intermediates(char *);
102 void content_error(char *, CT, char *, ...);
105 void free_content(CT);
106 void free_encoding(CT, int);
111 static CT get_content(FILE *, char *, int);
112 static int get_comment(CT, unsigned char **, int);
114 static int InitGeneric(CT);
115 static int InitText(CT);
116 static int InitMultiPart(CT);
117 static void reverse_parts(CT);
118 static int InitMessage(CT);
119 static int InitApplication(CT);
120 static int init_encoding(CT, OpenCEFunc);
121 static unsigned long size_encoding(CT);
122 static int InitBase64(CT);
123 static int openBase64(CT, char **);
124 static int InitQuoted(CT);
125 static int openQuoted(CT, char **);
126 static int Init7Bit(CT);
127 static int openExternal(CT, CT, CE, char **, int *);
128 static int InitFile(CT);
129 static int openFile(CT, char **);
130 static int InitFTP(CT);
131 static int openFTP(CT, char **);
132 static int InitMail(CT);
133 static int openMail(CT, char **);
134 static int readDigest(CT, char *);
136 struct str2init str2cts[] = {
137 { "application", CT_APPLICATION, InitApplication },
138 { "audio", CT_AUDIO, InitGeneric },
139 { "image", CT_IMAGE, InitGeneric },
140 { "message", CT_MESSAGE, InitMessage },
141 { "multipart", CT_MULTIPART, InitMultiPart },
142 { "text", CT_TEXT, InitText },
143 { "video", CT_VIDEO, InitGeneric },
144 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
145 { NULL, CT_UNKNOWN, NULL },
148 struct str2init str2ces[] = {
149 { "base64", CE_BASE64, InitBase64 },
150 { "quoted-printable", CE_QUOTED, InitQuoted },
151 { "8bit", CE_8BIT, Init7Bit },
152 { "7bit", CE_7BIT, Init7Bit },
153 { "binary", CE_BINARY, Init7Bit },
154 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
155 { NULL, CE_UNKNOWN, NULL },
159 ** NOTE WELL: si_key MUST NOT have value of NOTOK
161 ** si_key is 1 if access method is anonymous.
163 struct str2init str2methods[] = {
164 { "afs", 1, InitFile },
165 { "anon-ftp", 1, InitFTP },
166 { "ftp", 0, InitFTP },
167 { "local-file", 0, InitFile },
168 { "mail-server", 0, InitMail },
176 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
187 ** Main entry point for parsing a MIME message or file.
188 ** It returns the Content structure for the top level
189 ** entity in the file.
192 parse_mime(char *file)
200 ** Check if file is actually standard input
202 if ((is_stdin = (strcmp(file, "-")==0))) {
203 char *tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
205 advise("mhparse", "unable to create temporary file");
208 file = getcpy(tfile);
211 while (fgets(buffer, sizeof(buffer), stdin))
217 advise("stdin", "error reading");
222 advise(file, "error writing");
225 fseek(fp, 0L, SEEK_SET);
226 } else if ((fp = fopen(file, "r")) == NULL) {
227 advise(file, "unable to read");
231 if (!(ct = get_content(fp, file, 1))) {
234 advise(NULL, "unable to decode %s", file);
239 ct->c_unlink = 1; /* temp file to remove */
243 if (ct->c_end == 0L) {
244 fseek(fp, 0L, SEEK_END);
245 ct->c_end = ftell(fp);
248 if (ct->c_ctinitfnx && (*ct->c_ctinitfnx) (ct) == NOTOK) {
260 ** Main routine for reading/parsing the headers
261 ** of a message content.
263 ** toplevel = 1 # we are at the top level of the message
264 ** toplevel = 0 # we are inside message type or multipart type
265 ** # other than multipart/digest
266 ** toplevel = -1 # we are inside multipart/digest
267 ** NB: on failure we will fclose(in)!
271 get_content(FILE *in, char *file, int toplevel)
274 char buf[BUFSIZ], name[NAMESZ];
279 /* allocate the content structure */
280 if (!(ct = (CT) calloc(1, sizeof(*ct))))
281 adios(NULL, "out of memory");
284 ct->c_file = getcpy(file);
285 ct->c_begin = ftell(ct->c_fp) + 1;
288 ** Parse the header fields for this
289 ** content into a linked list.
291 for (compnum = 1, state = FLD;;) {
292 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
298 /* get copies of the buffers */
302 /* if necessary, get rest of field */
303 while (state == FLDPLUS) {
304 state = m_getfld(state, name, buf,
306 vp = add(buf, vp); /* add to previous value */
309 /* Now add the header data to the list */
310 add_header(ct, np, vp);
312 /* continue, if this isn't the last header field */
313 if (state != FLDEOF) {
314 ct->c_begin = ftell(in) + 1;
321 ct->c_begin = ftell(in) - strlen(buf);
325 ct->c_begin = ftell(in);
330 adios(NULL, "message format error in component #%d",
334 adios(NULL, "getfld() returned %d", state);
337 /* break out of the loop */
342 ** Read the content headers. We will parse the
343 ** MIME related header fields into their various
344 ** structures and set internal flags related to
345 ** content type/subtype, etc.
348 hp = ct->c_first_hf; /* start at first header field */
350 /* Get MIME-Version field */
351 if (!mh_strcasecmp(hp->name, VRSN_FIELD)) {
354 unsigned char *cp, *dp;
357 advise(NULL, "message %s has multiple %s: fields", ct->c_file, VRSN_FIELD);
360 ct->c_vrsn = getcpy(hp->value);
362 /* Now, cleanup this field */
367 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
369 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
374 fprintf(stderr, "%s: %s\n", VRSN_FIELD, cp);
376 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK)
379 for (dp = cp; istoken(*dp); dp++)
383 ucmp = !mh_strcasecmp(cp, VRSN_VALUE);
386 admonish(NULL, "message %s has unknown value for %s: field (%s)", ct->c_file, VRSN_FIELD, cp);
389 } else if (!mh_strcasecmp(hp->name, TYPE_FIELD)) {
390 /* Get Content-Type field */
391 struct str2init *s2i;
392 CI ci = &ct->c_ctinfo;
394 /* Check if we've already seen a Content-Type header */
396 advise(NULL, "message %s has multiple %s: fields", ct->c_file, TYPE_FIELD);
400 /* Parse the Content-Type field */
401 if (get_ctinfo(hp->value, ct, 0) == NOTOK)
405 ** Set the Init function and the internal
406 ** flag for this content type.
408 for (s2i = str2cts; s2i->si_key; s2i++)
409 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
411 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
413 ct->c_type = s2i->si_val;
414 ct->c_ctinitfnx = s2i->si_init;
416 } else if (!mh_strcasecmp(hp->name, ENCODING_FIELD)) {
417 /* Get Content-Transfer-Encoding field */
419 unsigned char *cp, *dp;
420 struct str2init *s2i;
423 ** Check if we've already seen the
424 ** Content-Transfer-Encoding field
427 advise(NULL, "message %s has multiple %s: fields", ct->c_file, ENCODING_FIELD);
431 /* get copy of this field */
432 ct->c_celine = cp = getcpy(hp->value);
436 for (dp = cp; istoken(*dp); dp++)
442 ** Find the internal flag and Init function
443 ** for this transfer encoding.
445 for (s2i = str2ces; s2i->si_key; s2i++)
446 if (!mh_strcasecmp(cp, s2i->si_key))
448 if (!s2i->si_key && !uprf(cp, "X-"))
451 ct->c_encoding = s2i->si_val;
453 /* Call the Init function for this encoding */
454 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
457 } else if (!mh_strcasecmp(hp->name, MD5_FIELD)) {
458 /* Get Content-MD5 field */
459 unsigned char *cp, *dp;
465 if (ct->c_digested) {
466 advise(NULL, "message %s has multiple %s: fields", ct->c_file, MD5_FIELD);
470 ep = cp = getcpy(hp->value);
474 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
476 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
481 fprintf(stderr, "%s: %s\n", MD5_FIELD, cp);
483 if (*cp == '(' && get_comment(ct, &cp, 0) == NOTOK) {
488 for (dp = cp; *dp && !isspace(*dp); dp++)
496 } else if (!mh_strcasecmp(hp->name, ID_FIELD)) {
497 /* Get Content-ID field */
498 ct->c_id = add(hp->value, ct->c_id);
500 } else if (!mh_strcasecmp(hp->name, DESCR_FIELD)) {
501 /* Get Content-Description field */
502 ct->c_descr = add(hp->value, ct->c_descr);
504 } else if (!mh_strcasecmp(hp->name, DISPO_FIELD)) {
505 /* Get Content-Disposition field */
506 ct->c_dispo = add(hp->value, ct->c_dispo);
510 hp = hp->next; /* next header field */
514 ** Check if we saw a Content-Type field.
515 ** If not, then assign a default value for
516 ** it, and the Init function.
520 ** If we are inside a multipart/digest message,
521 ** so default type is message/rfc822
524 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
526 ct->c_type = CT_MESSAGE;
527 ct->c_ctinitfnx = InitMessage;
530 ** Else default type is text/plain
532 if (get_ctinfo("text/plain", ct, 0) == NOTOK)
534 ct->c_type = CT_TEXT;
535 ct->c_ctinitfnx = InitText;
539 /* Use default Transfer-Encoding, if necessary */
541 ct->c_encoding = CE_7BIT;
554 ** small routine to add header field to list
558 add_header(CT ct, char *name, char *value)
562 /* allocate header field structure */
563 hp = mh_xmalloc(sizeof(*hp));
565 /* link data into header structure */
570 /* link header structure into the list */
571 if (ct->c_first_hf == NULL) {
572 ct->c_first_hf = hp; /* this is the first */
575 ct->c_last_hf->next = hp; /* add it to the end */
584 ** Make sure that buf contains at least one appearance of name,
585 ** followed by =. If not, insert both name and value, just after
586 ** first semicolon, if any. Note that name should not contain a
587 ** trailing =. And quotes will be added around the value. Typical
588 ** usage: make sure that a Content-Disposition header contains
589 ** filename="foo". If it doesn't and value does, use value from
593 incl_name_value(unsigned char *buf, char *name, char *value) {
596 /* Assume that name is non-null. */
598 char *name_plus_equal = concat(name, "=", NULL);
600 if (!strstr(buf, name_plus_equal)) {
603 char *prefix, *suffix;
605 /* Trim trailing space, esp. newline. */
606 for (cp = &buf[strlen(buf) - 1];
607 cp >= buf && isspace(*cp); --cp) {
611 insertion = concat("; ", name, "=", "\"", value, "\"",
615 ** Insert at first semicolon, if any.
616 ** If none, append to end.
618 prefix = getcpy(buf);
619 if ((cp = strchr(prefix, ';'))) {
620 suffix = concat(cp, NULL);
622 newbuf = concat(prefix, insertion, suffix,
627 newbuf = concat(buf, insertion, "\n", NULL);
635 free(name_plus_equal);
642 ** Extract just name_suffix="foo", if any, from value. If there isn't
643 ** one, return the entire value. Note that, for example, a name_suffix
644 ** of name will match filename="foo", and return foo.
647 extract_name_value(char *name_suffix, char *value) {
648 char *extracted_name_value = value;
649 char *name_suffix_plus_quote = concat(name_suffix, "=\"", NULL);
650 char *name_suffix_equals = strstr(value, name_suffix_plus_quote);
653 free(name_suffix_plus_quote);
654 if (name_suffix_equals) {
655 char *name_suffix_begin;
658 for (cp = name_suffix_equals; *cp != '"'; ++cp)
660 name_suffix_begin = ++cp;
661 /* Find second \". */
662 for (; *cp != '"'; ++cp)
665 extracted_name_value = mh_xmalloc(cp - name_suffix_begin + 1);
666 memcpy(extracted_name_value, name_suffix_begin,
667 cp - name_suffix_begin);
668 extracted_name_value[cp - name_suffix_begin] = '\0';
671 return extracted_name_value;
675 ** Parse Content-Type line and (if `magic' is non-zero) mhbuild composition
676 ** directives. Fills in the information of the CTinfo structure.
679 get_ctinfo(unsigned char *cp, CT ct, int magic)
688 i = strlen(invo_name) + 2;
690 /* store copy of Content-Type line */
691 cp = ct->c_ctline = getcpy(cp);
693 while (isspace(*cp)) /* trim leading spaces */
696 /* change newlines to spaces */
697 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
700 /* trim trailing spaces */
701 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
707 fprintf(stderr, "%s: %s\n", TYPE_FIELD, cp);
709 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
712 for (dp = cp; istoken(*dp); dp++)
715 ci->ci_type = getcpy(cp); /* store content type */
719 advise(NULL, "invalid %s: field in message %s (empty type)",
720 TYPE_FIELD, ct->c_file);
724 /* down case the content type string */
725 for (dp = ci->ci_type; *dp; dp++)
726 if (isalpha(*dp) && isupper(*dp))
732 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
737 ci->ci_subtype = getcpy("");
745 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
748 for (dp = cp; istoken(*dp); dp++)
751 ci->ci_subtype = getcpy(cp); /* store the content subtype */
754 if (!*ci->ci_subtype) {
755 advise(NULL, "invalid %s: field in message %s (empty subtype for \"%s\")", TYPE_FIELD, ct->c_file, ci->ci_type);
759 /* down case the content subtype string */
760 for (dp = ci->ci_subtype; *dp; dp++)
761 if (isalpha(*dp) && isupper(*dp))
768 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
772 ** Parse attribute/value pairs given with Content-Type
774 ep = (ap = ci->ci_attrs) + NPARMS;
780 advise(NULL, "too many parameters in message %s's %s: field (%d max)", ct->c_file, TYPE_FIELD, NPARMS);
788 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
792 advise (NULL, "extraneous trailing ';' in message %s's %s: parameter list", ct->c_file, TYPE_FIELD);
796 /* down case the attribute name */
797 for (dp = cp; istoken(*dp); dp++)
798 if (isalpha(*dp) && isupper(*dp))
801 for (up = dp; isspace(*dp);)
803 if (dp == cp || *dp != '=') {
804 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);
808 vp = (*ap = getcpy(cp)) + (up - cp);
810 for (dp++; isspace(*dp);)
813 /* now add the attribute value */
814 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
817 for (cp = ++dp, dp = vp;;) {
821 advise(NULL, "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
826 if ((c = *cp++) == '\0')
841 for (cp = dp, dp = vp; istoken(*cp); cp++, dp++)
846 advise(NULL, "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)", ct->c_file, TYPE_FIELD, i, i, "", *ap);
854 if (*cp == '(' && get_comment(ct, &cp, 1) == NOTOK)
859 ** Get any <Content-Id> given in buffer
861 if (magic && *cp == '<') {
866 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
867 advise(NULL, "invalid ID in message %s", ct->c_file);
873 ct->c_id = concat("<", ct->c_id, ">\n", NULL);
884 ** Get any [Content-Description] given in buffer.
886 if (magic && *cp == '[') {
888 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
892 advise(NULL, "invalid description in message %s",
901 ct->c_descr = concat(ct->c_descr, "\n", NULL);
912 ** Get any {Content-Disposition} given in buffer.
914 if (magic && *cp == '{') {
916 for (dp = cp + strlen(cp) - 1; dp >= cp; dp--)
920 advise(NULL, "invalid disposition in message %s",
929 ct->c_dispo = concat(ct->c_dispo, "\n", NULL);
940 ** Check if anything is left over
944 ci->ci_magic = getcpy(cp);
947 ** If there is a Content-Disposition header and
948 ** it doesn't have a *filename=, extract it from
949 ** the magic contents. The mhbasename call skips
950 ** any leading directory components.
953 ct->c_dispo = incl_name_value(ct->c_dispo, "filename", mhbasename(extract_name_value("name", ci->ci_magic)));
955 advise(NULL, "extraneous information in message %s's %s: field\n%*.*s(%s)", ct->c_file, TYPE_FIELD, i, i, "", cp);
963 get_comment(CT ct, unsigned char **ap, int istype)
968 char c, buffer[BUFSIZ], *dp;
980 advise(NULL, "invalid comment in message %s's %s: field",
981 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
986 if ((c = *cp++) == '\0')
1009 if ((dp = ci->ci_comment)) {
1010 ci->ci_comment = concat(dp, " ", buffer, NULL);
1013 ci->ci_comment = getcpy(buffer);
1017 while (isspace(*cp))
1028 ** Handles content types audio, image, and video.
1029 ** There's not much to do right here.
1035 return OK; /* not much to do here */
1046 char buffer[BUFSIZ];
1048 char **ap, **ep, *cp;
1051 CI ci = &ct->c_ctinfo;
1053 /* check for missing subtype */
1054 if (!*ci->ci_subtype)
1055 ci->ci_subtype = add("plain", ci->ci_subtype);
1058 for (kv = SubText; kv->kv_key; kv++)
1059 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1061 ct->c_subtype = kv->kv_value;
1063 /* allocate text character set structure */
1064 if ((t = (struct text *) calloc(1, sizeof(*t))) == NULL)
1065 adios(NULL, "out of memory");
1066 ct->c_ctparams = (void *) t;
1068 /* scan for charset parameter */
1069 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1070 if (!mh_strcasecmp(*ap, "charset"))
1073 /* check if content specified a character set */
1075 /* match character set or set to CHARSET_UNKNOWN */
1076 for (kv = Charset; kv->kv_key; kv++) {
1077 if (!mh_strcasecmp(*ep, kv->kv_key)) {
1082 t->tx_charset = kv->kv_value;
1084 t->tx_charset = CHARSET_UNSPECIFIED;
1088 ** If we can not handle character set natively,
1089 ** then check profile for string to modify the
1090 ** terminal or display method.
1092 ** termproc is for mhshow, though mhlist -debug prints it, too.
1094 if (chset != NULL && !check_charset(chset, strlen(chset))) {
1095 snprintf(buffer, sizeof(buffer), "%s-charset-%s",
1097 if ((cp = context_find(buffer)))
1098 ct->c_termproc = getcpy(cp);
1110 InitMultiPart(CT ct)
1114 unsigned char *cp, *dp;
1116 char *bp, buffer[BUFSIZ];
1117 struct multipart *m;
1119 struct part *part, **next;
1120 CI ci = &ct->c_ctinfo;
1125 ** The encoding for multipart messages must be either
1126 ** 7bit, 8bit, or binary (per RFC2045).
1128 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1129 && ct->c_encoding != CE_BINARY) {
1130 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);
1135 for (kv = SubMultiPart; kv->kv_key; kv++)
1136 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1138 ct->c_subtype = kv->kv_value;
1141 ** Check for "boundary" parameter, which is
1142 ** required for multipart messages.
1145 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1146 if (!mh_strcasecmp(*ap, "boundary")) {
1152 /* complain if boundary parameter is missing */
1154 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);
1158 /* allocate primary structure for multipart info */
1159 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1160 adios(NULL, "out of memory");
1161 ct->c_ctparams = (void *) m;
1163 /* check if boundary parameter contains only whitespace characters */
1164 for (cp = bp; isspace(*cp); cp++)
1167 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);
1171 /* remove trailing whitespace from boundary parameter */
1172 for (cp = bp, dp = cp + strlen(cp) - 1; dp > cp; dp--)
1177 /* record boundary separators */
1178 m->mp_start = concat(bp, "\n", NULL);
1179 m->mp_stop = concat(bp, "--\n", NULL);
1181 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1182 advise(ct->c_file, "unable to open for reading");
1186 fseek(fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1188 next = &m->mp_parts;
1192 while (fgets(buffer, sizeof(buffer) - 1, fp)) {
1196 pos += strlen(buffer);
1197 if (buffer[0] != '-' || buffer[1] != '-')
1200 if (strcmp(buffer + 2, m->mp_start)!=0)
1203 if ((part = (struct part *) calloc(1, sizeof(*part)))
1205 adios(NULL, "out of memory");
1207 next = &part->mp_next;
1209 if (!(p = get_content(fp, ct->c_file,
1210 ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1217 fseek(fp, pos, SEEK_SET);
1220 if (strcmp(buffer + 2, m->mp_start) == 0) {
1224 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1225 if (p->c_end < p->c_begin)
1226 p->c_begin = p->c_end;
1231 if (strcmp(buffer + 2, m->mp_stop) == 0)
1237 advise(NULL, "bogus multipart content in message %s", ct->c_file);
1238 if (!inout && part) {
1240 p->c_end = ct->c_end;
1242 if (p->c_begin >= p->c_end) {
1243 for (next = &m->mp_parts; *next != part;
1244 next = &((*next)->mp_next))
1248 free((char *) part);
1253 /* reverse the order of the parts for multipart/alternative */
1254 if (ct->c_subtype == MULTI_ALTERNATE)
1258 ** label all subparts with part number, and
1259 ** then initialize the content of the subpart.
1264 char partnam[BUFSIZ];
1267 snprintf(partnam, sizeof(partnam), "%s.",
1269 pp = partnam + strlen(partnam);
1274 for (part = m->mp_parts, partnum = 1; part;
1275 part = part->mp_next, partnum++) {
1278 sprintf(pp, "%d", partnum);
1279 p->c_partno = getcpy(partnam);
1281 /* initialize the content of the subparts */
1282 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1297 ** reverse the order of the parts of a multipart
1301 reverse_parts(CT ct)
1304 struct multipart *m;
1305 struct part **base, **bmp, **next, *part;
1307 m = (struct multipart *) ct->c_ctparams;
1309 /* if only one part, just return */
1310 if (!m->mp_parts || !m->mp_parts->mp_next)
1313 /* count number of parts */
1315 for (part = m->mp_parts; part; part = part->mp_next)
1318 /* allocate array of pointers to the parts */
1319 if (!(base = (struct part **) calloc((size_t) (i + 1), sizeof(*base))))
1320 adios(NULL, "out of memory");
1323 /* point at all the parts */
1324 for (part = m->mp_parts; part; part = part->mp_next)
1328 /* reverse the order of the parts */
1329 next = &m->mp_parts;
1330 for (bmp--; bmp >= base; bmp--) {
1333 next = &part->mp_next;
1337 /* free array of pointers */
1338 free((char *) base);
1350 CI ci = &ct->c_ctinfo;
1352 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1353 admonish(NULL, "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit", ci->ci_type, ci->ci_subtype, ct->c_file);
1357 /* check for missing subtype */
1358 if (!*ci->ci_subtype)
1359 ci->ci_subtype = add("rfc822", ci->ci_subtype);
1362 for (kv = SubMessage; kv->kv_key; kv++)
1363 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1365 ct->c_subtype = kv->kv_value;
1367 switch (ct->c_subtype) {
1368 case MESSAGE_RFC822:
1371 case MESSAGE_PARTIAL:
1376 if ((p = (struct partial *) calloc(1, sizeof(*p))) == NULL)
1377 adios(NULL, "out of memory");
1378 ct->c_ctparams = (void *) p;
1381 ** scan for parameters "id", "number",
1384 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1385 if (!mh_strcasecmp(*ap, "id")) {
1386 p->pm_partid = getcpy(*ep);
1389 if (!mh_strcasecmp(*ap, "number")) {
1390 if (sscanf(*ep, "%d", &p->pm_partno) != 1 || p->pm_partno < 1) {
1392 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);
1397 if (!mh_strcasecmp(*ap, "total")) {
1398 if (sscanf(*ep, "%d", &p->pm_maxno) != 1 ||
1405 if (!p->pm_partid || !p->pm_partno
1406 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1407 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);
1413 case MESSAGE_EXTERNAL:
1420 if ((e = (struct exbody *) calloc(1, sizeof(*e))) == NULL)
1421 adios(NULL, "out of memory");
1422 ct->c_ctparams = (void *) e;
1424 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1425 advise(ct->c_file, "unable to open for reading");
1429 fseek(fp = ct->c_fp, ct->c_begin, SEEK_SET);
1431 if (!(p = get_content(fp, ct->c_file, 0))) {
1439 if ((exresult = params_external(ct, 0)) != NOTOK &&
1440 p->c_ceopenfnx == openMail) {
1444 if ((size = ct->c_end - p->c_begin) <= 0) {
1446 content_error(NULL, ct, "empty body for access-type=mail-server");
1450 e->eb_body = bp = mh_xmalloc((unsigned) size);
1451 fseek(p->c_fp, p->c_begin, SEEK_SET);
1453 switch (cc = fread(bp, sizeof(*bp), size, p->c_fp)) {
1455 adios("failed", "fread");
1457 adios(NULL, "unexpected EOF from fread");
1459 bp += cc, size -= cc;
1466 p->c_end = p->c_begin;
1471 if (exresult == NOTOK)
1473 if (e->eb_flags == NOTOK)
1476 switch (p->c_type) {
1481 if (p->c_subtype != MESSAGE_RFC822)
1485 e->eb_partno = ct->c_partno;
1487 (*p->c_ctinitfnx) (p);
1502 params_external(CT ct, int composing)
1505 struct exbody *e = (struct exbody *) ct->c_ctparams;
1506 CI ci = &ct->c_ctinfo;
1508 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1509 if (!mh_strcasecmp(*ap, "access-type")) {
1510 struct str2init *s2i;
1511 CT p = e->eb_content;
1513 for (s2i = str2methods; s2i->si_key; s2i++)
1514 if (!mh_strcasecmp(*ep, s2i->si_key))
1518 e->eb_flags = NOTOK;
1519 p->c_encoding = CE_EXTERNAL;
1522 e->eb_access = s2i->si_key;
1523 e->eb_flags = s2i->si_val;
1524 p->c_encoding = CE_EXTERNAL;
1526 /* Call the Init function for this external type */
1527 if ((*s2i->si_init)(p) == NOTOK)
1531 if (!mh_strcasecmp(*ap, "name")) {
1535 if (!mh_strcasecmp(*ap, "permission")) {
1536 e->eb_permission = *ep;
1539 if (!mh_strcasecmp(*ap, "site")) {
1543 if (!mh_strcasecmp(*ap, "directory")) {
1547 if (!mh_strcasecmp(*ap, "mode")) {
1551 if (!mh_strcasecmp(*ap, "size")) {
1552 sscanf(*ep, "%lu", &e->eb_size);
1555 if (!mh_strcasecmp(*ap, "server")) {
1559 if (!mh_strcasecmp(*ap, "subject")) {
1560 e->eb_subject = *ep;
1563 if (composing && !mh_strcasecmp(*ap, "body")) {
1564 e->eb_body = getcpy(*ep);
1569 if (!e->eb_access) {
1570 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);
1583 InitApplication(CT ct)
1586 CI ci = &ct->c_ctinfo;
1589 for (kv = SubApplication; kv->kv_key; kv++)
1590 if (!mh_strcasecmp(ci->ci_subtype, kv->kv_key))
1592 ct->c_subtype = kv->kv_value;
1599 ** TRANSFER ENCODINGS
1603 init_encoding(CT ct, OpenCEFunc openfnx)
1607 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
1608 adios(NULL, "out of memory");
1611 ct->c_ceopenfnx = openfnx;
1612 ct->c_ceclosefnx = close_encoding;
1613 ct->c_cesizefnx = size_encoding;
1620 close_encoding(CT ct)
1624 if (!(ce = ct->c_cefile))
1634 static unsigned long
1635 size_encoding(CT ct)
1643 if (!(ce = ct->c_cefile))
1644 return (ct->c_end - ct->c_begin);
1646 if (ce->ce_fp && fstat(fileno(ce->ce_fp), &st) != NOTOK)
1647 return (long) st.st_size;
1650 if (stat(ce->ce_file, &st) != NOTOK)
1651 return (long) st.st_size;
1656 if (ct->c_encoding == CE_EXTERNAL)
1657 return (ct->c_end - ct->c_begin);
1660 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1661 return (ct->c_end - ct->c_begin);
1663 if (fstat(fd, &st) != NOTOK)
1664 size = (long) st.st_size;
1668 (*ct->c_ceclosefnx) (ct);
1677 static unsigned char b642nib[0x80] = {
1678 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1679 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1680 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1681 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1682 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1683 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1684 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1685 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1686 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1687 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1688 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1689 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1690 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1691 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1692 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1693 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1700 return init_encoding(ct, openBase64);
1705 openBase64(CT ct, char **file)
1707 int bitno, cc, digested;
1710 unsigned char value, *b, *b1, *b2, *b3;
1711 unsigned char *cp, *ep;
1712 char buffer[BUFSIZ];
1713 /* sbeck -- handle suffixes */
1718 b = (unsigned char *) &bits;
1719 b1 = &b[endian > 0 ? 1 : 2];
1720 b2 = &b[endian > 0 ? 2 : 1];
1721 b3 = &b[endian > 0 ? 3 : 0];
1725 fseek(ce->ce_fp, 0L, SEEK_SET);
1730 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1731 content_error(ce->ce_file, ct,
1732 "unable to fopen for reading");
1738 if (*file == NULL) {
1739 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1742 ce->ce_file = getcpy(*file);
1746 /* sbeck@cise.ufl.edu -- handle suffixes */
1748 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1749 invo_name, ci->ci_type, ci->ci_subtype);
1750 cp = context_find(buffer);
1751 if (cp == NULL || *cp == '\0') {
1752 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1754 cp = context_find(buffer);
1756 if (cp != NULL && *cp != '\0') {
1757 if (ce->ce_unlink) {
1759 ** Temporary file already exists, so we rename to
1760 ** version with extension.
1762 char *file_org = strdup(ce->ce_file);
1763 ce->ce_file = add(cp, ce->ce_file);
1764 if (rename(file_org, ce->ce_file)) {
1765 adios(ce->ce_file, "unable to rename %s to ",
1771 ce->ce_file = add(cp, ce->ce_file);
1775 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1776 content_error(ce->ce_file, ct,
1777 "unable to fopen for reading/writing");
1781 if ((len = ct->c_end - ct->c_begin) < 0)
1782 adios(NULL, "internal error(1)");
1784 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
1785 content_error(ct->c_file, ct, "unable to open for reading");
1789 if ((digested = ct->c_digested))
1790 MD5Init(&mdContext);
1796 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1798 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
1800 content_error(ct->c_file, ct, "error reading from");
1804 content_error(NULL, ct, "premature eof");
1812 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1817 if (skip || (*cp & 0x80) || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1819 fprintf(stderr, "*cp=0x%x pos=%ld skip=%d\n", *cp, (long) (lseek(fd, (off_t) 0, SEEK_CUR) - (ep - cp)), skip);
1821 content_error(NULL, ct, "invalid BASE64 encoding -- continuing");
1825 bits |= value << bitno;
1827 if ((bitno -= 6) < 0) {
1828 putc((char) *b1, ce->ce_fp);
1830 MD5Update(&mdContext, b1, 1);
1832 putc((char) *b2, ce->ce_fp);
1834 MD5Update(&mdContext, b2, 1);
1836 putc((char) *b3, ce->ce_fp);
1838 MD5Update(&mdContext, b3, 1);
1842 if (ferror(ce->ce_fp)) {
1843 content_error(ce->ce_file, ct,
1844 "error writing to");
1847 bitno = 18, bits = 0L, skip = 0;
1853 goto self_delimiting;
1862 fprintf(stderr, "premature ending (bitno %d)\n",
1865 content_error(NULL, ct, "invalid BASE64 encoding");
1870 fseek(ct->c_fp, 0L, SEEK_SET);
1872 if (fflush(ce->ce_fp)) {
1873 content_error(ce->ce_file, ct, "error writing to");
1878 unsigned char digest[16];
1880 MD5Final(digest, &mdContext);
1881 if (memcmp((char *) digest, (char *) ct->c_digest,
1882 sizeof(digest) / sizeof(digest[0])))
1883 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
1885 fprintf(stderr, "content integrity confirmed\n");
1888 fseek(ce->ce_fp, 0L, SEEK_SET);
1891 *file = ce->ce_file;
1892 return fileno(ce->ce_fp);
1895 free_encoding(ct, 0);
1904 static char hex2nib[0x80] = {
1905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1911 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
1912 0x08, 0x09, 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,
1917 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
1918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1927 return init_encoding(ct, openQuoted);
1932 openQuoted(CT ct, char **file)
1934 int cc, digested, len, quoted;
1935 unsigned char *cp, *ep;
1936 char buffer[BUFSIZ];
1939 /* sbeck -- handle suffixes */
1945 fseek(ce->ce_fp, 0L, SEEK_SET);
1950 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
1951 content_error(ce->ce_file, ct,
1952 "unable to fopen for reading");
1958 if (*file == NULL) {
1959 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
1962 ce->ce_file = getcpy(*file);
1966 /* sbeck@cise.ufl.edu -- handle suffixes */
1968 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
1969 invo_name, ci->ci_type, ci->ci_subtype);
1970 cp = context_find(buffer);
1971 if (cp == NULL || *cp == '\0') {
1972 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
1974 cp = context_find(buffer);
1976 if (cp != NULL && *cp != '\0') {
1977 if (ce->ce_unlink) {
1978 // Temporary file already exists, so we rename to
1979 // version with extension.
1980 char *file_org = strdup(ce->ce_file);
1981 ce->ce_file = add(cp, ce->ce_file);
1982 if (rename(file_org, ce->ce_file)) {
1983 adios(ce->ce_file, "unable to rename %s to ",
1989 ce->ce_file = add(cp, ce->ce_file);
1993 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
1994 content_error(ce->ce_file, ct,
1995 "unable to fopen for reading/writing");
1999 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2000 content_error(ce->ce_file, ct,
2001 "unable to fopen for reading/writing");
2005 if ((len = ct->c_end - ct->c_begin) < 0)
2006 adios(NULL, "internal error(2)");
2008 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2009 content_error(ct->c_file, ct, "unable to open for reading");
2013 if ((digested = ct->c_digested))
2014 MD5Init(&mdContext);
2021 fseek(ct->c_fp, ct->c_begin, SEEK_SET);
2023 if (fgets(buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2024 content_error(NULL, ct, "premature eof");
2028 if ((cc = strlen(buffer)) > len)
2032 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2037 for (; cp < ep; cp++) {
2039 /* in an escape sequence */
2041 /* at byte 1 of an escape sequence */
2042 mask = hex2nib[*cp & 0x7f];
2043 /* next is byte 2 */
2046 /* at byte 2 of an escape sequence */
2048 mask |= hex2nib[*cp & 0x7f];
2049 putc(mask, ce->ce_fp);
2051 MD5Update(&mdContext, &mask, 1);
2052 if (ferror(ce->ce_fp)) {
2053 content_error(ce->ce_file, ct, "error writing to");
2057 ** finished escape sequence; next may
2058 ** be literal or a new escape sequence
2062 /* on to next byte */
2066 /* not in an escape sequence */
2069 ** starting an escape sequence,
2072 if (cp + 1 < ep && cp[1] == '\n') {
2073 /* "=\n" soft line break, eat the \n */
2077 if (cp + 1 >= ep || cp + 2 >= ep) {
2079 ** We don't have 2 bytes left,
2080 ** so this is an invalid escape
2081 ** sequence; just show the raw bytes
2084 } else if (isxdigit(cp[1]) && isxdigit(cp[2])) {
2086 ** Next 2 bytes are hex digits,
2087 ** making this a valid escape
2088 ** sequence; let's decode it (above).
2094 ** One or both of the next 2 is
2095 ** out of range, making this an
2096 ** invalid escape sequence; just
2097 ** show the raw bytes (below).
2102 /* Just show the raw byte. */
2103 putc(*cp, ce->ce_fp);
2106 MD5Update(&mdContext, (unsigned char *) "\r\n",2);
2108 MD5Update(&mdContext, (unsigned char *) cp, 1);
2111 if (ferror(ce->ce_fp)) {
2112 content_error(ce->ce_file, ct,
2113 "error writing to");
2119 content_error(NULL, ct, "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2123 fseek(ct->c_fp, 0L, SEEK_SET);
2125 if (fflush(ce->ce_fp)) {
2126 content_error(ce->ce_file, ct, "error writing to");
2131 unsigned char digest[16];
2133 MD5Final(digest, &mdContext);
2134 if (memcmp((char *) digest, (char *) ct->c_digest,
2135 sizeof(digest) / sizeof(digest[0])))
2136 content_error(NULL, ct, "content integrity suspect (digest mismatch) -- continuing");
2138 fprintf(stderr, "content integrity confirmed\n");
2141 fseek(ce->ce_fp, 0L, SEEK_SET);
2144 *file = ce->ce_file;
2145 return fileno(ce->ce_fp);
2148 free_encoding(ct, 0);
2160 if (init_encoding(ct, open7Bit) == NOTOK)
2163 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2169 open7Bit(CT ct, char **file)
2172 char buffer[BUFSIZ];
2173 /* sbeck -- handle suffixes */
2180 fseek(ce->ce_fp, 0L, SEEK_SET);
2185 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2186 content_error(ce->ce_file, ct,
2187 "unable to fopen for reading");
2193 if (*file == NULL) {
2194 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2197 ce->ce_file = getcpy(*file);
2201 /* sbeck@cise.ufl.edu -- handle suffixes */
2203 snprintf(buffer, sizeof(buffer), "%s-suffix-%s/%s",
2204 invo_name, ci->ci_type, ci->ci_subtype);
2205 cp = context_find(buffer);
2206 if (cp == NULL || *cp == '\0') {
2207 snprintf(buffer, sizeof(buffer), "%s-suffix-%s", invo_name,
2209 cp = context_find(buffer);
2211 if (cp != NULL && *cp != '\0') {
2212 if (ce->ce_unlink) {
2214 ** Temporary file already exists, so we rename to
2215 ** version with extension.
2217 char *file_org = strdup(ce->ce_file);
2218 ce->ce_file = add(cp, ce->ce_file);
2219 if (rename(file_org, ce->ce_file)) {
2220 adios(ce->ce_file, "unable to rename %s to ",
2226 ce->ce_file = add(cp, ce->ce_file);
2230 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2231 content_error(ce->ce_file, ct,
2232 "unable to fopen for reading/writing");
2236 if (ct->c_type == CT_MULTIPART) {
2238 CI ci = &ct->c_ctinfo;
2241 fprintf(ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type,
2243 len += strlen(TYPE_FIELD) + 2 + strlen(ci->ci_type) + 1 +
2244 strlen(ci->ci_subtype);
2245 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2246 putc(';', ce->ce_fp);
2249 snprintf(buffer, sizeof(buffer), "%s=\"%s\"",
2252 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
2253 fputs("\n\t", ce->ce_fp);
2256 putc(' ', ce->ce_fp);
2259 fprintf(ce->ce_fp, "%s", buffer);
2263 if (ci->ci_comment) {
2264 if (len + 1 + (cc = 2 + strlen(ci->ci_comment))
2266 fputs("\n\t", ce->ce_fp);
2269 putc(' ', ce->ce_fp);
2272 fprintf(ce->ce_fp, "(%s)", ci->ci_comment);
2275 fprintf(ce->ce_fp, "\n");
2277 fprintf(ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2279 fprintf(ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2281 fprintf(ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2282 fprintf(ce->ce_fp, "\n");
2285 if ((len = ct->c_end - ct->c_begin) < 0)
2286 adios(NULL, "internal error(3)");
2288 if (!ct->c_fp && (ct->c_fp = fopen(ct->c_file, "r")) == NULL) {
2289 content_error(ct->c_file, ct, "unable to open for reading");
2293 lseek(fd = fileno(ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2295 switch (cc = read(fd, buffer, sizeof(buffer) - 1)) {
2297 content_error(ct->c_file, ct, "error reading from");
2301 content_error(NULL, ct, "premature eof");
2309 fwrite(buffer, sizeof(*buffer), cc, ce->ce_fp);
2310 if (ferror(ce->ce_fp)) {
2311 content_error(ce->ce_file, ct,
2312 "error writing to");
2317 fseek(ct->c_fp, 0L, SEEK_SET);
2319 if (fflush(ce->ce_fp)) {
2320 content_error(ce->ce_file, ct, "error writing to");
2324 fseek(ce->ce_fp, 0L, SEEK_SET);
2327 *file = ce->ce_file;
2328 return fileno(ce->ce_fp);
2331 free_encoding(ct, 0);
2341 openExternal(CT ct, CT cb, CE ce, char **file, int *fd)
2343 char cachefile[BUFSIZ];
2346 fseek(ce->ce_fp, 0L, SEEK_SET);
2351 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2352 content_error(ce->ce_file, ct,
2353 "unable to fopen for reading");
2359 if (find_cache(ct, rcachesw, (int *) 0, cb->c_id,
2360 cachefile, sizeof(cachefile)) != NOTOK) {
2361 if ((ce->ce_fp = fopen(cachefile, "r"))) {
2362 ce->ce_file = getcpy(cachefile);
2366 admonish(cachefile, "unable to fopen for reading");
2373 *file = ce->ce_file;
2374 *fd = fileno(ce->ce_fp);
2385 return init_encoding(ct, openFile);
2390 openFile(CT ct, char **file)
2393 char cachefile[BUFSIZ];
2394 struct exbody *e = ct->c_ctexbody;
2395 CE ce = ct->c_cefile;
2397 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2407 content_error(NULL, ct, "missing name parameter");
2411 ce->ce_file = getcpy(e->eb_name);
2414 if ((ce->ce_fp = fopen(ce->ce_file, "r")) == NULL) {
2415 content_error(ce->ce_file, ct, "unable to fopen for reading");
2419 if ((!e->eb_permission ||
2420 mh_strcasecmp(e->eb_permission, "read-write")) &&
2421 find_cache(NULL, wcachesw, &cachetype,
2422 e->eb_content->c_id, cachefile, sizeof(cachefile))
2427 mask = umask(cachetype ? ~m_gmprot() : 0222);
2428 if ((fp = fopen(cachefile, "w"))) {
2430 char buffer[BUFSIZ];
2431 FILE *gp = ce->ce_fp;
2433 fseek(gp, 0L, SEEK_SET);
2435 while ((cc = fread(buffer, sizeof(*buffer),
2436 sizeof(buffer), gp)) > 0)
2437 fwrite(buffer, sizeof(*buffer), cc, fp);
2441 admonish(ce->ce_file, "error reading");
2443 } else if (ferror(fp)) {
2444 admonish(cachefile, "error writing");
2452 fseek(ce->ce_fp, 0L, SEEK_SET);
2453 *file = ce->ce_file;
2454 return fileno(ce->ce_fp);
2464 return init_encoding(ct, openFTP);
2469 openFTP(CT ct, char **file)
2471 int cachetype, caching, fd;
2473 char *bp, *ftp, *user, *pass;
2474 char buffer[BUFSIZ], cachefile[BUFSIZ];
2477 static char *username = NULL;
2478 static char *password = NULL;
2483 if ((ftp = context_find(nmhaccessftp)) && !*ftp)
2491 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2500 if (!e->eb_name || !e->eb_site) {
2501 content_error(NULL, ct, "missing %s parameter",
2502 e->eb_name ? "site": "name");
2509 pidcheck(pidwait(xpid, NOTOK));
2513 /* Get the buffer ready to go */
2515 buflen = sizeof(buffer);
2518 ** Construct the query message for user
2520 snprintf(bp, buflen, "Retrieve %s", e->eb_name);
2526 snprintf(bp, buflen, " (content %s)", e->eb_partno);
2532 snprintf(bp, buflen, "\n using %sFTP from site %s",
2533 e->eb_flags ? "anonymous " : "", e->eb_site);
2538 if (e->eb_size > 0) {
2539 snprintf(bp, buflen, " (%lu octets)", e->eb_size);
2544 snprintf(bp, buflen, "? ");
2547 ** Now, check the answer
2549 if (!getanswer(buffer))
2554 snprintf(buffer, sizeof(buffer), "%s@%s", getusername(),
2558 ruserpass(e->eb_site, &username, &password);
2563 ce->ce_unlink = (*file == NULL);
2565 cachefile[0] = '\0';
2566 if ((!e->eb_permission ||
2567 mh_strcasecmp(e->eb_permission, "read-write")) &&
2568 find_cache(NULL, wcachesw, &cachetype,
2569 e->eb_content->c_id, cachefile, sizeof(cachefile))
2571 if (*file == NULL) {
2578 ce->ce_file = getcpy(*file);
2580 ce->ce_file = getcpy(cachefile);
2582 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2584 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2585 content_error (ce->ce_file, ct,
2586 "unable to fopen for reading/writing");
2594 int child_id, i, vecp;
2598 vec[vecp++] = mhbasename(ftp);
2599 vec[vecp++] = e->eb_site;
2602 vec[vecp++] = e->eb_dir;
2603 vec[vecp++] = e->eb_name;
2604 vec[vecp++] = ce->ce_file,
2605 vec[vecp++] = e->eb_mode &&
2606 !mh_strcasecmp(e->eb_mode, "ascii") ?
2612 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2616 adios("fork", "unable to");
2620 close(fileno(ce->ce_fp));
2622 fprintf(stderr, "unable to exec ");
2628 if (pidXwait(child_id, NULL)) {
2632 username = password = NULL;
2640 else if (ftp_get(e->eb_site, user, pass, e->eb_dir, e->eb_name,
2641 ce->ce_file, e->eb_mode && !mh_strcasecmp(e->eb_mode,
2642 "ascii"), 0) == NOTOK)
2648 chmod(cachefile, cachetype ? m_gmprot() : 0444);
2653 mask = umask(cachetype ? ~m_gmprot() : 0222);
2654 if ((fp = fopen(cachefile, "w"))) {
2656 FILE *gp = ce->ce_fp;
2658 fseek(gp, 0L, SEEK_SET);
2660 while ((cc= fread(buffer, sizeof(*buffer),
2661 sizeof(buffer), gp)) > 0)
2662 fwrite(buffer, sizeof(*buffer), cc, fp);
2666 admonish(ce->ce_file, "error reading");
2668 } else if (ferror(fp)) {
2669 admonish(cachefile, "error writing");
2678 fseek(ce->ce_fp, 0L, SEEK_SET);
2679 *file = ce->ce_file;
2680 return fileno(ce->ce_fp);
2691 return init_encoding(ct, openMail);
2696 openMail(CT ct, char **file)
2698 int child_id, fd, i, vecp;
2700 char *bp, buffer[BUFSIZ], *vec[7];
2701 struct exbody *e = ct->c_ctexbody;
2702 CE ce = ct->c_cefile;
2704 switch (openExternal(e->eb_parent, e->eb_content, ce, file, &fd)) {
2713 if (!e->eb_server) {
2714 content_error(NULL, ct, "missing server parameter");
2721 pidcheck(pidwait(xpid, NOTOK));
2725 /* Get buffer ready to go */
2727 buflen = sizeof(buffer);
2729 /* Now, construct query message */
2730 snprintf(bp, buflen, "Retrieve content");
2736 snprintf(bp, buflen, " %s", e->eb_partno);
2742 snprintf(bp, buflen, " by asking %s\n\n%s\n? ", e->eb_server,
2743 e->eb_subject ? e->eb_subject : e->eb_body);
2745 /* Now, check answer */
2746 if (!getanswer(buffer))
2750 vec[vecp++] = mhbasename(mailproc);
2751 vec[vecp++] = e->eb_server;
2752 vec[vecp++] = "-subject";
2753 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2754 vec[vecp++] = "-body";
2755 vec[vecp++] = e->eb_body;
2758 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
2762 advise("fork", "unable to");
2766 execvp(mailproc, vec);
2767 fprintf(stderr, "unable to exec ");
2773 if (pidXwait(child_id, NULL) == OK)
2774 advise(NULL, "request sent");
2778 if (*file == NULL) {
2779 ce->ce_file = getcpy(m_mktemp(tmp, NULL, NULL));
2782 ce->ce_file = getcpy(*file);
2786 if ((ce->ce_fp = fopen(ce->ce_file, "w+")) == NULL) {
2787 content_error(ce->ce_file, ct,
2788 "unable to fopen for reading/writing");
2793 ** showproc is for mhshow and mhstore, though mhlist -debug
2797 free(ct->c_showproc);
2798 ct->c_showproc = getcpy("true");
2800 fseek(ce->ce_fp, 0L, SEEK_SET);
2801 *file = ce->ce_file;
2802 return fileno(ce->ce_fp);
2807 readDigest(CT ct, char *cp)
2812 unsigned char *dp, value, *ep;
2813 unsigned char *b, *b1, *b2, *b3;
2815 b = (unsigned char *) &bits,
2816 b1 = &b[endian > 0 ? 1 : 2],
2817 b2 = &b[endian > 0 ? 2 : 1],
2818 b3 = &b[endian > 0 ? 3 : 0];
2823 for (ep = (dp = ct->c_digest)
2824 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
2827 if (skip || (*cp & 0x80) ||
2828 (value = b642nib[*cp & 0x7f])
2831 fprintf(stderr, "invalid BASE64 encoding\n");
2835 bits |= value << bitno;
2837 if ((bitno -= 6) < 0) {
2838 if (dp + (3 - skip) > ep)
2839 goto invalid_digest;
2854 goto self_delimiting;
2859 fprintf(stderr, "premature ending (bitno %d)\n",
2870 fprintf(stderr, "invalid MD5 digest (got %d octets)\n",
2878 fprintf(stderr, "MD5 digest=");
2879 for (dp = ct->c_digest; dp < ep; dp++)
2880 fprintf(stderr, "%02x", *dp & 0xff);
2881 fprintf(stderr, "\n");