3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
13 * This code was originally part of mhn.c. I split it into
14 * a separate program (mhbuild.c) and then later split it
15 * again (mhbuildsbr.c). But the code still has some of
16 * the mhn.c code in it. This program needs additional
17 * streamlining and removal of unneeded code.
22 #include <h/signals.h>
29 #include <h/mhparse.h>
32 #ifdef TIME_WITH_SYS_TIME
33 # include <sys/time.h>
36 # ifdef TM_IN_SYS_TIME
37 # include <sys/time.h>
43 #ifdef HAVE_SYS_WAIT_H
44 # include <sys/wait.h>
54 extern int contentidsw;
56 extern int endian; /* mhmisc.c */
59 extern int rcachesw; /* mhcachesbr.c */
60 extern int wcachesw; /* mhcachesbr.c */
62 int checksw = 0; /* Add Content-MD5 field */
65 * Directory to place tmp files. This must
66 * be set before these routines are called.
72 static char prefix[] = "----- =_aaaaaaaaaa";
75 * Structure for mapping types to their internal flags
83 * Structures for TEXT messages
85 static struct k2v SubText[] = {
86 { "plain", TEXT_PLAIN },
87 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
88 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
89 { NULL, TEXT_UNKNOWN } /* this one must be last! */
92 static struct k2v Charset[] = {
93 { "us-ascii", CHARSET_USASCII },
94 { "iso-8859-1", CHARSET_LATIN },
95 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
99 * Structures for MULTIPART messages
101 static struct k2v SubMultiPart[] = {
102 { "mixed", MULTI_MIXED },
103 { "alternative", MULTI_ALTERNATE },
104 { "digest", MULTI_DIGEST },
105 { "parallel", MULTI_PARALLEL },
106 { NULL, MULTI_UNKNOWN } /* this one must be last! */
110 * Structures for MESSAGE messages
112 static struct k2v SubMessage[] = {
113 { "rfc822", MESSAGE_RFC822 },
114 { "partial", MESSAGE_PARTIAL },
115 { "external-body", MESSAGE_EXTERNAL },
116 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
120 * Structure for APPLICATION messages
122 static struct k2v SubApplication[] = {
123 { "octet-stream", APPLICATION_OCTETS },
124 { "postscript", APPLICATION_POSTSCRIPT },
125 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
130 int make_intermediates (char *);
131 void content_error (char *, CT, char *, ...);
134 int find_cache (CT, int, int *, char *, char *, int);
137 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
140 void free_content (CT);
141 void free_ctinfo (CT);
142 void free_encoding (CT, int);
147 CT build_mime (char *);
153 static CT get_content (FILE *, char *, int);
154 static int add_header (CT, char *, char *);
155 static int get_ctinfo (unsigned char *, CT, int);
156 static int get_comment (CT, unsigned char **, int);
157 static int InitGeneric (CT);
158 static int InitText (CT);
159 static int InitMultiPart (CT);
160 static void reverse_parts (CT);
161 static int InitMessage (CT);
162 static int params_external (CT, int);
163 static int InitApplication (CT);
164 static int init_decoded_content (CT);
165 static int init_encoding (CT, OpenCEFunc);
166 static void close_encoding (CT);
167 static unsigned long size_encoding (CT);
168 static int InitBase64 (CT);
169 static int openBase64 (CT, char **);
170 static int InitQuoted (CT);
171 static int openQuoted (CT, char **);
172 static int Init7Bit (CT);
173 static int open7Bit (CT, char **);
174 static int openExternal (CT, CT, CE, char **, int *);
175 static int InitFile (CT);
176 static int openFile (CT, char **);
177 static int InitFTP (CT);
178 static int openFTP (CT, char **);
179 static int InitMail (CT);
180 static int openMail (CT, char **);
181 static char *fgetstr (char *, int, FILE *);
182 static int user_content (FILE *, char *, char *, CT *);
183 static void set_id (CT, int);
184 static int compose_content (CT);
185 static int scan_content (CT);
186 static int build_headers (CT);
187 static char *calculate_digest (CT, int);
188 static int readDigest (CT, char *);
189 static char *incl_name_value (unsigned char *, char *, char *);
190 static char *extract_name_value (char *, char *);
193 * Structures for mapping (content) types to
194 * the functions to handle them.
202 static struct str2init str2cts[] = {
203 { "application", CT_APPLICATION, InitApplication },
204 { "audio", CT_AUDIO, InitGeneric },
205 { "image", CT_IMAGE, InitGeneric },
206 { "message", CT_MESSAGE, InitMessage },
207 { "multipart", CT_MULTIPART, InitMultiPart },
208 { "text", CT_TEXT, InitText },
209 { "video", CT_VIDEO, InitGeneric },
210 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
211 { NULL, CT_UNKNOWN, NULL },
214 static struct str2init str2ces[] = {
215 { "base64", CE_BASE64, InitBase64 },
216 { "quoted-printable", CE_QUOTED, InitQuoted },
217 { "8bit", CE_8BIT, Init7Bit },
218 { "7bit", CE_7BIT, Init7Bit },
219 { "binary", CE_BINARY, NULL },
220 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
221 { NULL, CE_UNKNOWN, NULL },
225 * NOTE WELL: si_key MUST NOT have value of NOTOK
227 * si_key is 1 if access method is anonymous.
229 static struct str2init str2methods[] = {
230 { "afs", 1, InitFile },
231 { "anon-ftp", 1, InitFTP },
232 { "ftp", 0, InitFTP },
233 { "local-file", 0, InitFile },
234 { "mail-server", 0, InitMail },
240 pidcheck (int status)
242 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
252 * Main routine for translating composition file
253 * into valid MIME message. It translates the draft
254 * into a content structure (actually a tree of content
255 * structures). This message then can be manipulated
256 * in various ways, including being output via
261 build_mime (char *infile)
264 char buf[BUFSIZ], name[NAMESZ];
271 umask (~m_gmprot ());
273 /* open the composition draft */
274 if ((in = fopen (infile, "r")) == NULL)
275 adios (infile, "unable to open for reading");
278 * Allocate space for primary (outside) content
280 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
281 adios (NULL, "out of memory");
284 * Allocate structure for handling decoded content
285 * for this part. We don't really need this, but
286 * allocate it to remain consistent.
288 init_decoded_content (ct);
291 * Parse some of the header fields in the composition
292 * draft into the linked list of header fields for
293 * the new MIME message.
295 for (compnum = 1, state = FLD;;) {
296 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
302 /* abort if draft has Mime-Version header field */
303 if (!mh_strcasecmp (name, VRSN_FIELD))
304 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
306 /* abort if draft has Content-Transfer-Encoding header field */
307 if (!mh_strcasecmp (name, ENCODING_FIELD))
308 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
310 /* ignore any Content-Type fields in the header */
311 if (!mh_strcasecmp (name, TYPE_FIELD)) {
312 while (state == FLDPLUS)
313 state = m_getfld (state, name, buf, sizeof(buf), in);
317 /* get copies of the buffers */
318 np = add (name, NULL);
319 vp = add (buf, NULL);
321 /* if necessary, get rest of field */
322 while (state == FLDPLUS) {
323 state = m_getfld (state, name, buf, sizeof(buf), in);
324 vp = add (buf, vp); /* add to previous value */
327 /* Now add the header data to the list */
328 add_header (ct, np, vp);
331 /* if this wasn't the last header field, then continue */
337 adios (NULL, "draft has empty body -- no directives!");
342 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
347 adios (NULL, "message format error in component #%d", compnum);
350 adios (NULL, "getfld() returned %d", state);
356 * Now add the MIME-Version header field
357 * to the list of header fields.
359 np = add (VRSN_FIELD, NULL);
360 vp = concat (" ", VRSN_VALUE, "\n", NULL);
361 add_header (ct, np, vp);
364 * We initally assume we will find multiple contents in the
365 * draft. So create a multipart/mixed content to hold everything.
366 * We can remove this later, if it is not needed.
368 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
370 ct->c_type = CT_MULTIPART;
371 ct->c_subtype = MULTI_MIXED;
372 ct->c_file = add (infile, NULL);
374 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
375 adios (NULL, "out of memory");
376 ct->c_ctparams = (void *) m;
380 * read and parse the composition file
381 * and the directives it contains.
383 while (fgetstr (buf, sizeof(buf) - 1, in)) {
387 if (user_content (in, infile, buf, &p) == DONE) {
388 admonish (NULL, "ignoring spurious #end");
394 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
395 adios (NULL, "out of memory");
402 * close the composition draft since
403 * it's not needed any longer.
407 /* check if any contents were found */
409 adios (NULL, "no content directives found");
412 * If only one content was found, then remove and
413 * free the outer multipart content.
415 if (!m->mp_parts->mp_next) {
418 p = m->mp_parts->mp_part;
419 m->mp_parts->mp_part = NULL;
421 /* move header fields */
422 p->c_first_hf = ct->c_first_hf;
423 p->c_last_hf = ct->c_last_hf;
424 ct->c_first_hf = NULL;
425 ct->c_last_hf = NULL;
434 * Fill out, or expand directives. Parse and execute
435 * commands specified by profile composition strings.
437 compose_content (ct);
439 if ((cp = strchr(prefix, 'a')) == NULL)
440 adios (NULL, "internal error(4)");
443 * Scan the contents. Choose a transfer encoding, and
444 * check if prefix for multipart boundary clashes with
445 * any of the contents.
447 while (scan_content (ct) == NOTOK) {
452 adios (NULL, "giving up trying to find a unique delimiter string");
458 /* Build the rest of the header field structures */
466 * Main routine for reading/parsing the headers
467 * of a message content.
469 * toplevel = 1 # we are at the top level of the message
470 * toplevel = 0 # we are inside message type or multipart type
471 * # other than multipart/digest
472 * toplevel = -1 # we are inside multipart/digest
476 get_content (FILE *in, char *file, int toplevel)
479 char buf[BUFSIZ], name[NAMESZ];
482 if (!(ct = (CT) calloc (1, sizeof(*ct))))
483 adios (NULL, "out of memory");
486 ct->c_file = add (file, NULL);
487 ct->c_begin = ftell (ct->c_fp) + 1;
490 * Read the content headers
492 for (compnum = 1, state = FLD;;) {
493 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
499 /* Get MIME-Version field */
500 if (!mh_strcasecmp (name, VRSN_FIELD)) {
503 unsigned char *cp, *dp;
505 cp = add (buf, NULL);
506 while (state == FLDPLUS) {
507 state = m_getfld (state, name, buf, sizeof(buf), in);
512 advise (NULL, "message %s has multiple %s: fields (%s)",
513 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
520 while (isspace (*cp))
522 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
524 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
529 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
531 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
534 for (dp = cp; istoken (*dp); dp++)
537 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
541 "message %s has unknown value for %s: field (%s)",
542 ct->c_file, VRSN_FIELD, cp);
546 /* Get Content-Type field */
547 if (!mh_strcasecmp (name, TYPE_FIELD)) {
549 struct str2init *s2i;
550 CI ci = &ct->c_ctinfo;
552 cp = add (buf, NULL);
553 while (state == FLDPLUS) {
554 state = m_getfld (state, name, buf, sizeof(buf), in);
558 /* Check if we've already seen a Content-Type header */
560 char *dp = trimcpy (cp);
562 advise (NULL, "message %s has multiple %s: fields (%s)",
563 ct->c_file, TYPE_FIELD, dp);
569 /* Parse the Content-Type field */
570 if (get_ctinfo (cp, ct, 0) == NOTOK)
574 * Set the Init function and the internal
575 * flag for this content type.
577 for (s2i = str2cts; s2i->si_key; s2i++)
578 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
580 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
582 ct->c_type = s2i->si_val;
583 ct->c_ctinitfnx = s2i->si_init;
587 /* Get Content-Transfer-Encoding field */
588 if (!mh_strcasecmp (name, ENCODING_FIELD)) {
589 unsigned char *cp, *dp;
591 struct str2init *s2i;
593 cp = add (buf, NULL);
594 while (state == FLDPLUS) {
595 state = m_getfld (state, name, buf, sizeof(buf), in);
600 * Check if we've already seen the
601 * Content-Transfer-Encoding field
604 advise (NULL, "message %s has multiple %s: fields (%s)",
605 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
611 ct->c_celine = cp; /* Save copy of this field */
612 while (isspace (*cp))
614 for (dp = cp; istoken (*dp); dp++)
620 * Find the internal flag and Init function
621 * for this transfer encoding.
623 for (s2i = str2ces; s2i->si_key; s2i++)
624 if (!mh_strcasecmp (cp, s2i->si_key))
626 if (!s2i->si_key && !uprf (cp, "X-"))
629 ct->c_encoding = s2i->si_val;
631 /* Call the Init function for this encoding */
632 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
637 /* Get Content-ID field */
638 if (!mh_strcasecmp (name, ID_FIELD)) {
639 ct->c_id = add (buf, ct->c_id);
640 while (state == FLDPLUS) {
641 state = m_getfld (state, name, buf, sizeof(buf), in);
642 ct->c_id = add (buf, ct->c_id);
647 /* Get Content-Description field */
648 if (!mh_strcasecmp (name, DESCR_FIELD)) {
649 ct->c_descr = add (buf, ct->c_descr);
650 while (state == FLDPLUS) {
651 state = m_getfld (state, name, buf, sizeof(buf), in);
652 ct->c_descr = add (buf, ct->c_descr);
657 /* Get Content-Disposition field */
658 if (!mh_strcasecmp (name, DISPO_FIELD)) {
659 ct->c_dispo = add (buf, ct->c_dispo);
660 while (state == FLDPLUS) {
661 state = m_getfld (state, name, buf, sizeof(buf), in);
662 ct->c_dispo = add (buf, ct->c_dispo);
667 /* Get Content-MD5 field */
668 if (!mh_strcasecmp (name, MD5_FIELD)) {
669 unsigned char *cp, *dp;
672 cp = add (buf, NULL);
673 while (state == FLDPLUS) {
674 state = m_getfld (state, name, buf, sizeof(buf), in);
683 if (ct->c_digested) {
684 advise (NULL, "message %s has multiple %s: fields (%s)",
685 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
692 while (isspace (*cp))
694 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
696 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
701 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
703 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
708 for (dp = cp; *dp && !isspace (*dp); dp++)
719 if (uprf (name, XXX_FIELD_PRF))
720 advise (NULL, "unknown field (%s) in message %s",
725 while (state == FLDPLUS)
726 state = m_getfld (state, name, buf, sizeof(buf), in);
729 if (state != FLDEOF) {
730 ct->c_begin = ftell (in) + 1;
737 ct->c_begin = ftell (in) - strlen (buf);
741 ct->c_begin = ftell (in);
746 adios (NULL, "message format error in component #%d", compnum);
749 adios (NULL, "getfld() returned %d", state);
755 * Check if we saw a Content-Type field.
756 * If not, then assign a default value for
757 * it, and the Init function.
761 * If we are inside a multipart/digest message,
762 * so default type is message/rfc822
765 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
767 ct->c_type = CT_MESSAGE;
768 ct->c_ctinitfnx = InitMessage;
771 * Else default type is text/plain
773 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
775 ct->c_type = CT_TEXT;
776 ct->c_ctinitfnx = InitText;
780 /* Use default Transfer-Encoding, if necessary */
782 ct->c_encoding = CE_7BIT;
795 * small routine to add header field to list
799 add_header (CT ct, char *name, char *value)
803 /* allocate header field structure */
804 hp = mh_xmalloc (sizeof(*hp));
806 /* link data into header structure */
811 /* link header structure into the list */
812 if (ct->c_first_hf == NULL) {
813 ct->c_first_hf = hp; /* this is the first */
816 ct->c_last_hf->next = hp; /* add it to the end */
825 * Used to parse both:
826 * 1) Content-Type line
827 * 2) composition directives
829 * and fills in the information of the CTinfo structure.
833 get_ctinfo (unsigned char *cp, CT ct, int magic)
842 i = strlen (invo_name) + 2;
844 /* store copy of Content-Type line */
845 cp = ct->c_ctline = add (cp, NULL);
847 while (isspace (*cp)) /* trim leading spaces */
850 /* change newlines to spaces */
851 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
854 /* trim trailing spaces */
855 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
861 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
863 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
866 for (dp = cp; istoken (*dp); dp++)
869 ci->ci_type = add (cp, NULL); /* store content type */
873 advise (NULL, "invalid %s: field in message %s (empty type)",
874 TYPE_FIELD, ct->c_file);
878 /* down case the content type string */
879 for (dp = ci->ci_type; *dp; dp++)
880 if (isalpha(*dp) && isupper (*dp))
883 while (isspace (*cp))
886 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
891 ci->ci_subtype = add ("", NULL);
896 while (isspace (*cp))
899 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
902 for (dp = cp; istoken (*dp); dp++)
905 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
908 if (!*ci->ci_subtype) {
910 "invalid %s: field in message %s (empty subtype for \"%s\")",
911 TYPE_FIELD, ct->c_file, ci->ci_type);
915 /* down case the content subtype string */
916 for (dp = ci->ci_subtype; *dp; dp++)
917 if (isalpha(*dp) && isupper (*dp))
921 while (isspace (*cp))
924 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
928 * Parse attribute/value pairs given with Content-Type
930 ep = (ap = ci->ci_attrs) + NPARMS;
937 "too many parameters in message %s's %s: field (%d max)",
938 ct->c_file, TYPE_FIELD, NPARMS);
943 while (isspace (*cp))
946 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
951 "extraneous trailing ';' in message %s's %s: parameter list",
952 ct->c_file, TYPE_FIELD);
956 /* down case the attribute name */
957 for (dp = cp; istoken (*dp); dp++)
958 if (isalpha(*dp) && isupper (*dp))
961 for (up = dp; isspace (*dp); )
963 if (dp == cp || *dp != '=') {
965 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
966 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
970 vp = (*ap = add (cp, NULL)) + (up - cp);
972 for (dp++; isspace (*dp); )
975 /* now add the attribute value */
976 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
979 for (cp = ++dp, dp = vp;;) {
984 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
985 ct->c_file, TYPE_FIELD, i, i, "", *ap);
990 if ((c = *cp++) == '\0')
1005 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
1011 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
1012 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1017 while (isspace (*cp))
1020 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1025 * Get any <Content-Id> given in buffer
1027 if (magic && *cp == '<') {
1032 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1033 advise (NULL, "invalid ID in message %s", ct->c_file);
1039 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1045 while (isspace (*cp))
1050 * Get any [Content-Description] given in buffer.
1052 if (magic && *cp == '[') {
1054 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1058 advise (NULL, "invalid description in message %s", ct->c_file);
1066 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1072 while (isspace (*cp))
1077 * Get any {Content-Disposition} given in buffer.
1079 if (magic && *cp == '{') {
1081 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1085 advise (NULL, "invalid disposition in message %s", ct->c_file);
1093 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
1099 while (isspace (*cp))
1104 * Check if anything is left over
1108 ci->ci_magic = add (cp, NULL);
1110 /* If there is a Content-Disposition header and it doesn't
1111 have a *filename=, extract it from the magic contents.
1112 The r1bindex call skips any leading directory
1116 incl_name_value (ct->c_dispo,
1118 r1bindex (extract_name_value ("name",
1125 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1126 ct->c_file, TYPE_FIELD, i, i, "", cp);
1134 get_comment (CT ct, unsigned char **ap, int istype)
1139 char c, buffer[BUFSIZ], *dp;
1148 switch (c = *cp++) {
1151 advise (NULL, "invalid comment in message %s's %s: field",
1152 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1157 if ((c = *cp++) == '\0')
1180 if ((dp = ci->ci_comment)) {
1181 ci->ci_comment = concat (dp, " ", buffer, NULL);
1184 ci->ci_comment = add (buffer, NULL);
1188 while (isspace (*cp))
1199 * Handles content types audio, image, and video.
1200 * There's not much to do right here.
1206 return OK; /* not much to do here */
1220 CI ci = &ct->c_ctinfo;
1222 /* check for missing subtype */
1223 if (!*ci->ci_subtype)
1224 ci->ci_subtype = add ("plain", ci->ci_subtype);
1227 for (kv = SubText; kv->kv_key; kv++)
1228 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1230 ct->c_subtype = kv->kv_value;
1232 /* allocate text character set structure */
1233 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1234 adios (NULL, "out of memory");
1235 ct->c_ctparams = (void *) t;
1237 /* initially mark character set as unspecified */
1238 t->tx_charset = CHARSET_UNSPECIFIED;
1240 /* scan for charset parameter */
1241 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1242 if (!mh_strcasecmp (*ap, "charset"))
1245 /* check if content specified a character set */
1247 /* match character set or set to CHARSET_UNKNOWN */
1248 for (kv = Charset; kv->kv_key; kv++)
1249 if (!mh_strcasecmp (*ep, kv->kv_key))
1251 t->tx_charset = kv->kv_value;
1263 InitMultiPart (CT ct)
1267 unsigned char *cp, *dp;
1269 char *bp, buffer[BUFSIZ];
1270 struct multipart *m;
1272 struct part *part, **next;
1273 CI ci = &ct->c_ctinfo;
1278 * The encoding for multipart messages must be either
1279 * 7bit, 8bit, or binary (per RFC2045).
1281 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1282 && ct->c_encoding != CE_BINARY) {
1284 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1285 ci->ci_type, ci->ci_subtype, ct->c_file);
1290 for (kv = SubMultiPart; kv->kv_key; kv++)
1291 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1293 ct->c_subtype = kv->kv_value;
1296 * Check for "boundary" parameter, which is
1297 * required for multipart messages.
1299 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1300 if (!mh_strcasecmp (*ap, "boundary")) {
1306 /* complain if boundary parameter is missing */
1309 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1310 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1314 /* allocate primary structure for multipart info */
1315 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1316 adios (NULL, "out of memory");
1317 ct->c_ctparams = (void *) m;
1319 /* check if boundary parameter contains only whitespace characters */
1320 for (cp = bp; isspace (*cp); cp++)
1323 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1324 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1328 /* remove trailing whitespace from boundary parameter */
1329 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1334 /* record boundary separators */
1335 m->mp_start = concat (bp, "\n", NULL);
1336 m->mp_stop = concat (bp, "--\n", NULL);
1338 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1339 advise (ct->c_file, "unable to open for reading");
1343 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1345 next = &m->mp_parts;
1349 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1353 pos += strlen (buffer);
1354 if (buffer[0] != '-' || buffer[1] != '-')
1357 if (strcmp (buffer + 2, m->mp_start))
1360 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1361 adios (NULL, "out of memory");
1363 next = &part->mp_next;
1365 if (!(p = get_content (fp, ct->c_file,
1366 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1374 fseek (fp, pos, SEEK_SET);
1377 if (strcmp (buffer + 2, m->mp_start) == 0) {
1381 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1382 if (p->c_end < p->c_begin)
1383 p->c_begin = p->c_end;
1388 if (strcmp (buffer + 2, m->mp_stop) == 0)
1394 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1395 if (!inout && part) {
1397 p->c_end = ct->c_end;
1399 if (p->c_begin >= p->c_end) {
1400 for (next = &m->mp_parts; *next != part;
1401 next = &((*next)->mp_next))
1405 free ((char *) part);
1410 /* reverse the order of the parts for multipart/alternative */
1411 if (ct->c_subtype == MULTI_ALTERNATE)
1415 * label all subparts with part number, and
1416 * then initialize the content of the subpart.
1421 char partnam[BUFSIZ];
1424 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1425 pp = partnam + strlen (partnam);
1430 for (part = m->mp_parts, partnum = 1; part;
1431 part = part->mp_next, partnum++) {
1434 sprintf (pp, "%d", partnum);
1435 p->c_partno = add (partnam, NULL);
1437 /* initialize the content of the subparts */
1438 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1453 * reverse the order of the parts of a multipart
1457 reverse_parts (CT ct)
1460 struct multipart *m;
1461 struct part **base, **bmp, **next, *part;
1463 m = (struct multipart *) ct->c_ctparams;
1465 /* if only one part, just return */
1466 if (!m->mp_parts || !m->mp_parts->mp_next)
1469 /* count number of parts */
1471 for (part = m->mp_parts; part; part = part->mp_next)
1474 /* allocate array of pointers to the parts */
1475 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1476 adios (NULL, "out of memory");
1479 /* point at all the parts */
1480 for (part = m->mp_parts; part; part = part->mp_next)
1484 /* reverse the order of the parts */
1485 next = &m->mp_parts;
1486 for (bmp--; bmp >= base; bmp--) {
1489 next = &part->mp_next;
1493 /* free array of pointers */
1494 free ((char *) base);
1506 CI ci = &ct->c_ctinfo;
1508 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1510 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1511 ci->ci_type, ci->ci_subtype, ct->c_file);
1515 /* check for missing subtype */
1516 if (!*ci->ci_subtype)
1517 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1520 for (kv = SubMessage; kv->kv_key; kv++)
1521 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1523 ct->c_subtype = kv->kv_value;
1525 switch (ct->c_subtype) {
1526 case MESSAGE_RFC822:
1529 case MESSAGE_PARTIAL:
1534 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1535 adios (NULL, "out of memory");
1536 ct->c_ctparams = (void *) p;
1538 /* scan for parameters "id", "number", and "total" */
1539 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1540 if (!mh_strcasecmp (*ap, "id")) {
1541 p->pm_partid = add (*ep, NULL);
1544 if (!mh_strcasecmp (*ap, "number")) {
1545 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1546 || p->pm_partno < 1) {
1549 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1550 *ap, ci->ci_type, ci->ci_subtype,
1551 ct->c_file, TYPE_FIELD);
1556 if (!mh_strcasecmp (*ap, "total")) {
1557 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1566 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1568 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1569 ci->ci_type, ci->ci_subtype,
1570 ct->c_file, TYPE_FIELD);
1576 case MESSAGE_EXTERNAL:
1583 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1584 adios (NULL, "out of memory");
1585 ct->c_ctparams = (void *) e;
1588 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1589 advise (ct->c_file, "unable to open for reading");
1593 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1595 if (!(p = get_content (fp, ct->c_file, 0))) {
1604 if ((exresult = params_external (ct, 0)) != NOTOK
1605 && p->c_ceopenfnx == openMail) {
1609 if ((size = ct->c_end - p->c_begin) <= 0) {
1611 content_error (NULL, ct,
1612 "empty body for access-type=mail-server");
1616 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1617 fseek (p->c_fp, p->c_begin, SEEK_SET);
1619 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1621 adios ("failed", "fread");
1624 adios (NULL, "unexpected EOF from fread");
1627 bp += cc, size -= cc;
1634 p->c_end = p->c_begin;
1639 if (exresult == NOTOK)
1641 if (e->eb_flags == NOTOK)
1644 switch (p->c_type) {
1649 if (p->c_subtype != MESSAGE_RFC822)
1653 e->eb_partno = ct->c_partno;
1655 (*p->c_ctinitfnx) (p);
1670 params_external (CT ct, int composing)
1673 struct exbody *e = (struct exbody *) ct->c_ctparams;
1674 CI ci = &ct->c_ctinfo;
1676 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1677 if (!mh_strcasecmp (*ap, "access-type")) {
1678 struct str2init *s2i;
1679 CT p = e->eb_content;
1681 for (s2i = str2methods; s2i->si_key; s2i++)
1682 if (!mh_strcasecmp (*ep, s2i->si_key))
1687 e->eb_flags = NOTOK;
1688 p->c_encoding = CE_EXTERNAL;
1691 e->eb_access = s2i->si_key;
1692 e->eb_flags = s2i->si_val;
1693 p->c_encoding = CE_EXTERNAL;
1695 /* Call the Init function for this external type */
1696 if ((*s2i->si_init)(p) == NOTOK)
1700 if (!mh_strcasecmp (*ap, "name")) {
1704 if (!mh_strcasecmp (*ap, "permission")) {
1705 e->eb_permission = *ep;
1708 if (!mh_strcasecmp (*ap, "site")) {
1712 if (!mh_strcasecmp (*ap, "directory")) {
1716 if (!mh_strcasecmp (*ap, "mode")) {
1720 if (!mh_strcasecmp (*ap, "size")) {
1721 sscanf (*ep, "%lu", &e->eb_size);
1724 if (!mh_strcasecmp (*ap, "server")) {
1728 if (!mh_strcasecmp (*ap, "subject")) {
1729 e->eb_subject = *ep;
1732 if (composing && !mh_strcasecmp (*ap, "body")) {
1733 e->eb_body = getcpy (*ep);
1738 if (!e->eb_access) {
1740 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1741 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1754 InitApplication (CT ct)
1757 CI ci = &ct->c_ctinfo;
1760 for (kv = SubApplication; kv->kv_key; kv++)
1761 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1763 ct->c_subtype = kv->kv_value;
1770 * Set up structures for placing unencoded
1771 * content when building parts.
1775 init_decoded_content (CT ct)
1779 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1780 adios (NULL, "out of memory");
1783 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1784 ct->c_ceclosefnx = close_encoding;
1785 ct->c_cesizefnx = NULL; /* since unencoded */
1792 * TRANSFER ENCODINGS
1796 init_encoding (CT ct, OpenCEFunc openfnx)
1800 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1801 adios (NULL, "out of memory");
1804 ct->c_ceopenfnx = openfnx;
1805 ct->c_ceclosefnx = close_encoding;
1806 ct->c_cesizefnx = size_encoding;
1813 close_encoding (CT ct)
1817 if (!(ce = ct->c_cefile))
1827 static unsigned long
1828 size_encoding (CT ct)
1836 if (!(ce = ct->c_cefile))
1837 return (ct->c_end - ct->c_begin);
1839 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1840 return (long) st.st_size;
1843 if (stat (ce->ce_file, &st) != NOTOK)
1844 return (long) st.st_size;
1849 if (ct->c_encoding == CE_EXTERNAL)
1850 return (ct->c_end - ct->c_begin);
1853 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1854 return (ct->c_end - ct->c_begin);
1856 if (fstat (fd, &st) != NOTOK)
1857 size = (long) st.st_size;
1861 (*ct->c_ceclosefnx) (ct);
1870 static unsigned char b642nib[0x80] = {
1871 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1872 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1873 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1874 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1875 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1876 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1877 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1878 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1879 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1880 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1881 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1882 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1883 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1884 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1885 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1886 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1893 return init_encoding (ct, openBase64);
1898 openBase64 (CT ct, char **file)
1900 int bitno, cc, digested;
1903 unsigned char value, *b, *b1, *b2, *b3;
1904 unsigned char *cp, *ep;
1905 char buffer[BUFSIZ];
1909 b = (unsigned char *) &bits;
1910 b1 = &b[endian > 0 ? 1 : 2];
1911 b2 = &b[endian > 0 ? 2 : 1];
1912 b3 = &b[endian > 0 ? 3 : 0];
1916 fseek (ce->ce_fp, 0L, SEEK_SET);
1921 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1922 content_error (ce->ce_file, ct, "unable to fopen for reading");
1928 if (*file == NULL) {
1929 ce->ce_file = add (m_scratch ("", tmp), NULL);
1932 ce->ce_file = add (*file, NULL);
1936 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1937 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1941 if ((len = ct->c_end - ct->c_begin) < 0)
1942 adios (NULL, "internal error(1)");
1944 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1945 content_error (ct->c_file, ct, "unable to open for reading");
1949 if ((digested = ct->c_digested))
1950 MD5Init (&mdContext);
1956 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1958 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1960 content_error (ct->c_file, ct, "error reading from");
1964 content_error (NULL, ct, "premature eof");
1972 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1977 if (skip || (*cp & 0x80)
1978 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1980 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1982 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1985 content_error (NULL, ct,
1986 "invalid BASE64 encoding -- continuing");
1990 bits |= value << bitno;
1992 if ((bitno -= 6) < 0) {
1993 putc ((char) *b1, ce->ce_fp);
1995 MD5Update (&mdContext, b1, 1);
1997 putc ((char) *b2, ce->ce_fp);
1999 MD5Update (&mdContext, b2, 1);
2001 putc ((char) *b3, ce->ce_fp);
2003 MD5Update (&mdContext, b3, 1);
2007 if (ferror (ce->ce_fp)) {
2008 content_error (ce->ce_file, ct,
2009 "error writing to");
2012 bitno = 18, bits = 0L, skip = 0;
2018 goto self_delimiting;
2027 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2029 content_error (NULL, ct, "invalid BASE64 encoding");
2034 fseek (ct->c_fp, 0L, SEEK_SET);
2036 if (fflush (ce->ce_fp)) {
2037 content_error (ce->ce_file, ct, "error writing to");
2042 unsigned char digest[16];
2044 MD5Final (digest, &mdContext);
2045 if (memcmp((char *) digest, (char *) ct->c_digest,
2046 sizeof(digest) / sizeof(digest[0])))
2047 content_error (NULL, ct,
2048 "content integrity suspect (digest mismatch) -- continuing");
2051 fprintf (stderr, "content integrity confirmed\n");
2054 fseek (ce->ce_fp, 0L, SEEK_SET);
2057 *file = ce->ce_file;
2058 return fileno (ce->ce_fp);
2061 free_encoding (ct, 0);
2070 static char hex2nib[0x80] = {
2071 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2072 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2073 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2074 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2075 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2076 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2077 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2078 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2079 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2080 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2081 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2082 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2083 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2084 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2085 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2086 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2093 return init_encoding (ct, openQuoted);
2098 openQuoted (CT ct, char **file)
2100 int cc, digested, len, quoted;
2101 unsigned char *cp, *ep;
2102 char buffer[BUFSIZ];
2109 fseek (ce->ce_fp, 0L, SEEK_SET);
2114 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2115 content_error (ce->ce_file, ct, "unable to fopen for reading");
2121 if (*file == NULL) {
2122 ce->ce_file = add (m_scratch ("", tmp), NULL);
2125 ce->ce_file = add (*file, NULL);
2129 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2130 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2134 if ((len = ct->c_end - ct->c_begin) < 0)
2135 adios (NULL, "internal error(2)");
2137 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2138 content_error (ct->c_file, ct, "unable to open for reading");
2142 if ((digested = ct->c_digested))
2143 MD5Init (&mdContext);
2150 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2154 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2155 content_error (NULL, ct, "premature eof");
2159 if ((cc = strlen (buffer)) > len)
2163 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2168 for (; cp < ep; cp++) {
2171 if (!isxdigit (*cp)) {
2173 dp = "expecting hexidecimal-digit";
2174 goto invalid_encoding;
2177 mask |= hex2nib[*cp & 0x7f];
2178 putc (mask, ce->ce_fp);
2180 MD5Update (&mdContext, &mask, 1);
2184 putc (*cp, ce->ce_fp);
2186 MD5Update (&mdContext, (unsigned char *) ":", 1);
2190 if (!isxdigit (*cp))
2192 mask = hex2nib[*cp & 0x7f];
2198 if (ferror (ce->ce_fp)) {
2199 content_error (ce->ce_file, ct, "error writing to");
2208 if (*cp < '!' || *cp > '~') {
2210 dp = "expecting character in range [!..~]";
2213 i = strlen (invo_name) + 2;
2214 content_error (NULL, ct,
2215 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2223 putc (*cp, ce->ce_fp);
2226 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2228 MD5Update (&mdContext, (unsigned char *) cp, 1);
2230 if (ferror (ce->ce_fp)) {
2231 content_error (ce->ce_file, ct, "error writing to");
2237 if (*++cp != '\n') {
2246 content_error (NULL, ct,
2247 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2251 fseek (ct->c_fp, 0L, SEEK_SET);
2253 if (fflush (ce->ce_fp)) {
2254 content_error (ce->ce_file, ct, "error writing to");
2259 unsigned char digest[16];
2261 MD5Final (digest, &mdContext);
2262 if (memcmp((char *) digest, (char *) ct->c_digest,
2263 sizeof(digest) / sizeof(digest[0])))
2264 content_error (NULL, ct,
2265 "content integrity suspect (digest mismatch) -- continuing");
2268 fprintf (stderr, "content integrity confirmed\n");
2271 fseek (ce->ce_fp, 0L, SEEK_SET);
2274 *file = ce->ce_file;
2275 return fileno (ce->ce_fp);
2278 free_encoding (ct, 0);
2290 if (init_encoding (ct, open7Bit) == NOTOK)
2293 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2299 open7Bit (CT ct, char **file)
2302 char buffer[BUFSIZ];
2307 fseek (ce->ce_fp, 0L, SEEK_SET);
2312 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2313 content_error (ce->ce_file, ct, "unable to fopen for reading");
2319 if (*file == NULL) {
2320 ce->ce_file = add (m_scratch ("", tmp), NULL);
2323 ce->ce_file = add (*file, NULL);
2327 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2328 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2332 if (ct->c_type == CT_MULTIPART) {
2334 CI ci = &ct->c_ctinfo;
2337 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2338 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2339 + 1 + strlen (ci->ci_subtype);
2340 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2341 putc (';', ce->ce_fp);
2344 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2346 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2347 fputs ("\n\t", ce->ce_fp);
2350 putc (' ', ce->ce_fp);
2353 fprintf (ce->ce_fp, "%s", buffer);
2357 if (ci->ci_comment) {
2358 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2359 fputs ("\n\t", ce->ce_fp);
2363 putc (' ', ce->ce_fp);
2366 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2369 fprintf (ce->ce_fp, "\n");
2371 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2373 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2375 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2376 fprintf (ce->ce_fp, "\n");
2379 if ((len = ct->c_end - ct->c_begin) < 0)
2380 adios (NULL, "internal error(3)");
2382 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2383 content_error (ct->c_file, ct, "unable to open for reading");
2387 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2389 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2391 content_error (ct->c_file, ct, "error reading from");
2395 content_error (NULL, ct, "premature eof");
2403 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2404 if (ferror (ce->ce_fp)) {
2405 content_error (ce->ce_file, ct, "error writing to");
2410 fseek (ct->c_fp, 0L, SEEK_SET);
2412 if (fflush (ce->ce_fp)) {
2413 content_error (ce->ce_file, ct, "error writing to");
2417 fseek (ce->ce_fp, 0L, SEEK_SET);
2420 *file = ce->ce_file;
2421 return fileno (ce->ce_fp);
2424 free_encoding (ct, 0);
2434 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2436 char cachefile[BUFSIZ];
2439 fseek (ce->ce_fp, 0L, SEEK_SET);
2444 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2445 content_error (ce->ce_file, ct, "unable to fopen for reading");
2451 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2452 cachefile, sizeof(cachefile)) != NOTOK) {
2453 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2454 ce->ce_file = getcpy (cachefile);
2458 admonish (cachefile, "unable to fopen for reading");
2465 *file = ce->ce_file;
2466 *fd = fileno (ce->ce_fp);
2477 return init_encoding (ct, openFile);
2482 openFile (CT ct, char **file)
2485 char cachefile[BUFSIZ];
2486 struct exbody *e = ct->c_ctexbody;
2487 CE ce = ct->c_cefile;
2489 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2501 content_error (NULL, ct, "missing name parameter");
2505 ce->ce_file = getcpy (e->eb_name);
2508 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2509 content_error (ce->ce_file, ct, "unable to fopen for reading");
2513 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2514 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2515 cachefile, sizeof(cachefile)) != NOTOK) {
2519 mask = umask (cachetype ? ~m_gmprot () : 0222);
2520 if ((fp = fopen (cachefile, "w"))) {
2522 char buffer[BUFSIZ];
2523 FILE *gp = ce->ce_fp;
2525 fseek (gp, 0L, SEEK_SET);
2527 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2529 fwrite (buffer, sizeof(*buffer), cc, fp);
2533 admonish (ce->ce_file, "error reading");
2538 admonish (cachefile, "error writing");
2546 fseek (ce->ce_fp, 0L, SEEK_SET);
2547 *file = ce->ce_file;
2548 return fileno (ce->ce_fp);
2558 return init_encoding (ct, openFTP);
2563 openFTP (CT ct, char **file)
2565 int cachetype, caching, fd;
2567 char *bp, *ftp, *user, *pass;
2568 char buffer[BUFSIZ], cachefile[BUFSIZ];
2571 static char *username = NULL;
2572 static char *password = NULL;
2577 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2585 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2596 if (!e->eb_name || !e->eb_site) {
2597 content_error (NULL, ct, "missing %s parameter",
2598 e->eb_name ? "site": "name");
2605 pidcheck (pidwait (xpid, NOTOK));
2609 /* Get the buffer ready to go */
2611 buflen = sizeof(buffer);
2614 * Construct the query message for user
2616 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2622 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2628 snprintf (bp, buflen, "\n using %sFTP from site %s",
2629 e->eb_flags ? "anonymous " : "", e->eb_site);
2634 if (e->eb_size > 0) {
2635 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2640 snprintf (bp, buflen, "? ");
2643 * Now, check the answer
2645 if (!getanswer (buffer))
2650 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2653 ruserpass (e->eb_site, &username, &password);
2658 ce->ce_unlink = (*file == NULL);
2660 cachefile[0] = '\0';
2661 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2662 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2663 cachefile, sizeof(cachefile)) != NOTOK) {
2664 if (*file == NULL) {
2671 ce->ce_file = add (*file, NULL);
2673 ce->ce_file = add (cachefile, NULL);
2675 ce->ce_file = add (m_scratch ("", tmp), NULL);
2677 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2678 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2686 int child_id, i, vecp;
2690 vec[vecp++] = r1bindex (ftp, '/');
2691 vec[vecp++] = e->eb_site;
2694 vec[vecp++] = e->eb_dir;
2695 vec[vecp++] = e->eb_name;
2696 vec[vecp++] = ce->ce_file,
2697 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2698 ? "ascii" : "binary";
2703 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2707 adios ("fork", "unable to");
2711 close (fileno (ce->ce_fp));
2713 fprintf (stderr, "unable to exec ");
2719 if (pidXwait (child_id, NULL)) {
2723 username = password = NULL;
2732 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2734 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2741 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2746 mask = umask (cachetype ? ~m_gmprot () : 0222);
2747 if ((fp = fopen (cachefile, "w"))) {
2749 FILE *gp = ce->ce_fp;
2751 fseek (gp, 0L, SEEK_SET);
2753 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2755 fwrite (buffer, sizeof(*buffer), cc, fp);
2759 admonish (ce->ce_file, "error reading");
2764 admonish (cachefile, "error writing");
2773 fseek (ce->ce_fp, 0L, SEEK_SET);
2774 *file = ce->ce_file;
2775 return fileno (ce->ce_fp);
2786 return init_encoding (ct, openMail);
2791 openMail (CT ct, char **file)
2793 int child_id, fd, i, vecp;
2795 char *bp, buffer[BUFSIZ], *vec[7];
2796 struct exbody *e = ct->c_ctexbody;
2797 CE ce = ct->c_cefile;
2799 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2810 if (!e->eb_server) {
2811 content_error (NULL, ct, "missing server parameter");
2818 pidcheck (pidwait (xpid, NOTOK));
2822 /* Get buffer ready to go */
2824 buflen = sizeof(buffer);
2826 /* Now construct query message */
2827 snprintf (bp, buflen, "Retrieve content");
2833 snprintf (bp, buflen, " %s", e->eb_partno);
2839 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2841 e->eb_subject ? e->eb_subject : e->eb_body);
2843 /* Now, check answer */
2844 if (!getanswer (buffer))
2848 vec[vecp++] = r1bindex (mailproc, '/');
2849 vec[vecp++] = e->eb_server;
2850 vec[vecp++] = "-subject";
2851 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2852 vec[vecp++] = "-body";
2853 vec[vecp++] = e->eb_body;
2856 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2860 advise ("fork", "unable to");
2864 execvp (mailproc, vec);
2865 fprintf (stderr, "unable to exec ");
2871 if (pidXwait (child_id, NULL) == OK)
2872 advise (NULL, "request sent");
2876 if (*file == NULL) {
2877 ce->ce_file = add (m_scratch ("", tmp), NULL);
2880 ce->ce_file = add (*file, NULL);
2884 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2885 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2889 fseek (ce->ce_fp, 0L, SEEK_SET);
2890 *file = ce->ce_file;
2891 return fileno (ce->ce_fp);
2896 fgetstr (char *s, int n, FILE *stream)
2900 for (ep = (cp = s) + n; cp < ep; ) {
2903 if (!fgets (cp, n, stream))
2904 return (cp != s ? s : NULL);
2905 if (cp == s && *cp != '#')
2908 cp += (i = strlen (cp)) - 1;
2909 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2920 * Parse the composition draft for text and directives.
2921 * Do initial setup of Content structure.
2925 user_content (FILE *in, char *file, char *buf, CT *ctp)
2930 char buffer[BUFSIZ];
2931 struct multipart *m;
2934 struct str2init *s2i;
2939 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2944 /* allocate basic Content structure */
2945 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2946 adios (NULL, "out of memory");
2949 /* allocate basic structure for handling decoded content */
2950 init_decoded_content (ct);
2957 * Handle inline text. Check if line
2958 * is one of the following forms:
2960 * 1) doesn't begin with '#' (implicit directive)
2961 * 2) begins with "##" (implicit directive)
2962 * 3) begins with "#<"
2964 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2968 char content[BUFSIZ];
2971 /* use a temp file to collect the plain text lines */
2972 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2975 if ((out = fopen (ce->ce_file, "w")) == NULL)
2976 adios (ce->ce_file, "unable to open for writing");
2978 if (buf[0] == '#' && buf[1] == '<') {
2979 strncpy (content, buf + 2, sizeof(content));
2986 /* the directive is implicit */
2987 strncpy (content, "text/plain", sizeof(content));
2989 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2993 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2994 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2998 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2999 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
3000 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
3001 switch (buffer[0]) {
3008 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
3016 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
3017 && buffer[i = strlen (DISPO_FIELD)] == ':') {
3021 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
3022 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
3023 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
3024 switch (buffer[0]) {
3031 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
3039 if (headers != 1 || buffer[0] != '\n')
3040 fputs (buffer, out);
3045 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
3047 if (buffer[0] == '#') {
3050 if (buffer[1] != '#')
3052 for (cp = (bp = buffer) + 1; *cp; cp++)
3059 ct->c_end = ftell (out);
3062 /* parse content type */
3063 if (get_ctinfo (content, ct, inlineD) == NOTOK)
3066 for (s2i = str2cts; s2i->si_key; s2i++)
3067 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
3069 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
3073 * check type specified (possibly implicitly)
3075 switch (ct->c_type = s2i->si_val) {
3077 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
3078 ct->c_encoding = CE_7BIT;
3083 adios (NULL, "it doesn't make sense to define an in-line %s content",
3084 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3089 if ((ct->c_ctinitfnx = s2i->si_init))
3090 (*ct->c_ctinitfnx) (ct);
3095 fseek (in, pos, SEEK_SET);
3100 * If we've reached this point, the next line
3101 * must be some type of explicit directive.
3104 /* check if directive is external-type */
3105 extrnal = (buf[1] == '@');
3107 /* parse directive */
3108 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3111 /* check directive against the list of MIME types */
3112 for (s2i = str2cts; s2i->si_key; s2i++)
3113 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
3117 * Check if the directive specified a valid type.
3118 * This will happen if it was one of the following forms:
3120 * #type/subtype (or)
3124 if (!ci->ci_subtype)
3125 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3127 switch (ct->c_type = s2i->si_val) {
3129 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3130 ci->ci_type, ci->ci_subtype);
3134 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
3135 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3136 ci->ci_type, ci->ci_subtype);
3137 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
3138 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3139 ci->ci_type, ci->ci_subtype);
3142 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3143 ci->ci_type, ci->ci_subtype);
3147 if ((ct->c_ctinitfnx = s2i->si_init))
3148 (*ct->c_ctinitfnx) (ct);
3153 * #@type/subtype (external types directive)
3160 adios (NULL, "need external information for \"#@%s/%s\"",
3161 ci->ci_type, ci->ci_subtype);
3164 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3165 free (ci->ci_magic);
3166 ci->ci_magic = NULL;
3169 * Since we are using the current Content structure to
3170 * hold information about the type of the external
3171 * reference, we need to create another Content structure
3172 * for the message/external-body to wrap it in.
3174 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3175 adios (NULL, "out of memory");
3178 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3180 ct->c_type = CT_MESSAGE;
3181 ct->c_subtype = MESSAGE_EXTERNAL;
3183 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3184 adios (NULL, "out of memory");
3185 ct->c_ctparams = (void *) e;
3191 if (params_external (ct, 1) == NOTOK)
3197 /* Handle [file] argument */
3199 /* check if specifies command to execute */
3200 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3201 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3204 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3205 cp = add (cp, NULL);
3206 free (ci->ci_magic);
3209 /* record filename of decoded contents */
3210 ce->ce_file = ci->ci_magic;
3211 if (access (ce->ce_file, R_OK) == NOTOK)
3212 adios ("reading", "unable to access %s for", ce->ce_file);
3213 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3214 ct->c_end = (long) st.st_size;
3215 ci->ci_magic = NULL;
3221 * No [file] argument, so check profile for
3222 * method to compose content.
3224 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3225 invo_name, ci->ci_type, ci->ci_subtype);
3226 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3227 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3228 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3229 content_error (NULL, ct, "don't know how to compose content");
3233 ci->ci_magic = add (cp, NULL);
3238 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3242 * #forw [+folder] [msgs]
3244 if (!mh_strcasecmp (ci->ci_type, "forw")) {
3246 char *folder, *arguments[MAXARGS];
3250 ap = brkstring (ci->ci_magic, " ", "\n");
3251 copyip (ap, arguments, MAXARGS);
3253 arguments[0] = "cur";
3254 arguments[1] = NULL;
3258 /* search the arguments for a folder name */
3259 for (ap = arguments; *ap; ap++) {
3261 if (*cp == '+' || *cp == '@') {
3263 adios (NULL, "only one folder per #forw directive");
3265 folder = pluspath (cp);
3269 /* else, use the current folder */
3271 folder = add (getfolder (1), NULL);
3273 if (!(mp = folder_read (folder)))
3274 adios (NULL, "unable to read folder %s", folder);
3275 for (ap = arguments; *ap; ap++) {
3277 if (*cp != '+' && *cp != '@')
3278 if (!m_convert (mp, cp))
3285 * If there is more than one message to include, make this
3286 * a content of type "multipart/digest" and insert each message
3287 * as a subpart. If there is only one message, then make this
3288 * a content of type "message/rfc822".
3290 if (mp->numsel > 1) {
3291 /* we are forwarding multiple messages */
3292 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3294 ct->c_type = CT_MULTIPART;
3295 ct->c_subtype = MULTI_DIGEST;
3297 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3298 adios (NULL, "out of memory");
3299 ct->c_ctparams = (void *) m;
3302 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3303 if (is_selected(mp, msgnum)) {
3308 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3309 adios (NULL, "out of memory");
3310 init_decoded_content (p);
3312 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3314 p->c_type = CT_MESSAGE;
3315 p->c_subtype = MESSAGE_RFC822;
3317 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3318 pe->ce_file = add (buffer, NULL);
3319 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3320 p->c_end = (long) st.st_size;
3322 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3323 adios (NULL, "out of memory");
3325 pp = &part->mp_next;
3330 /* we are forwarding one message */
3331 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3333 ct->c_type = CT_MESSAGE;
3334 ct->c_subtype = MESSAGE_RFC822;
3336 msgnum = mp->lowsel;
3337 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3338 ce->ce_file = add (buffer, NULL);
3339 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3340 ct->c_end = (long) st.st_size;
3343 folder_free (mp); /* free folder/message structure */
3350 if (!mh_strcasecmp (ci->ci_type, "end")) {
3357 * #begin [ alternative | parallel ]
3359 if (!mh_strcasecmp (ci->ci_type, "begin")) {
3360 if (!ci->ci_magic) {
3362 cp = SubMultiPart[vrsn - 1].kv_key;
3363 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
3364 vrsn = MULTI_ALTERNATE;
3365 cp = SubMultiPart[vrsn - 1].kv_key;
3366 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
3367 vrsn = MULTI_PARALLEL;
3368 cp = SubMultiPart[vrsn - 1].kv_key;
3369 } else if (uprf (ci->ci_magic, "digest")) {
3372 vrsn = MULTI_UNKNOWN;
3377 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3378 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3380 ct->c_type = CT_MULTIPART;
3381 ct->c_subtype = vrsn;
3383 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3384 adios (NULL, "out of memory");
3385 ct->c_ctparams = (void *) m;
3388 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3392 if (user_content (in, file, buffer, &p) == DONE) {
3394 adios (NULL, "empty \"#begin ... #end\" sequence");
3400 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3401 adios (NULL, "out of memory");
3403 pp = &part->mp_next;
3406 admonish (NULL, "premature end-of-file, missing #end");
3413 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3414 return NOTOK; /* NOT REACHED */
3419 set_id (CT ct, int top)
3423 static time_t clock = 0;
3424 static char *msgfmt;
3428 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3429 (int) getpid(), (long) clock, LocalName());
3431 msgfmt = getcpy(msgid);
3433 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3434 ct->c_id = getcpy (msgid);
3438 static char ebcdicsafe[0x100] = {
3439 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3440 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3441 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3443 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3444 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3445 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3446 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3447 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3448 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3449 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3450 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3451 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3452 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3453 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3454 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3455 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3456 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3457 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3458 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3460 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3461 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3462 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3469 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3475 * Fill out, or expand the various contents in the composition
3476 * draft. Read-in any necessary files. Parse and execute any
3477 * commands specified by profile composition strings.
3481 compose_content (CT ct)
3483 CE ce = ct->c_cefile;
3485 switch (ct->c_type) {
3490 char partnam[BUFSIZ];
3491 struct multipart *m = (struct multipart *) ct->c_ctparams;
3495 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3496 pp = partnam + strlen (partnam);
3501 /* first, we call compose_content on all the subparts */
3502 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3503 CT p = part->mp_part;
3505 sprintf (pp, "%d", partnum);
3506 p->c_partno = add (partnam, NULL);
3507 if (compose_content (p) == NOTOK)
3512 * If the -rfc934mode switch is given, then check all
3513 * the subparts of a multipart/digest. If they are all
3514 * message/rfc822, then mark this content and all
3515 * subparts with the rfc934 compatibility mode flag.
3517 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3520 for (part = m->mp_parts; part; part = part->mp_next) {
3521 CT p = part->mp_part;
3523 if (p->c_subtype != MESSAGE_RFC822) {
3528 ct->c_rfc934 = is934;
3529 for (part = m->mp_parts; part; part = part->mp_next) {
3530 CT p = part->mp_part;
3532 if ((p->c_rfc934 = is934))
3538 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3542 for (part = m->mp_parts; part; part = part->mp_next)
3543 ct->c_end += part->mp_part->c_end + partnum;
3549 /* Nothing to do for type message */
3553 * Discrete types (text/application/audio/image/video)
3558 int i, xstdout, len, buflen;
3559 char *bp, **ap, *cp;
3560 char *vec[4], buffer[BUFSIZ];
3562 CI ci = &ct->c_ctinfo;
3564 if (!(cp = ci->ci_magic))
3565 adios (NULL, "internal error(5)");
3567 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3572 /* Get buffer ready to go */
3575 buflen = sizeof(buffer);
3578 * Parse composition string into buffer
3580 for ( ; *cp; cp++) {
3585 /* insert parameters from directive */
3589 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3590 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3600 /* %f, and stdout is not-redirected */
3606 * insert temporary filename where
3607 * content should be written
3609 snprintf (bp, buflen, "%s", ce->ce_file);
3613 /* insert content subtype */
3614 strncpy (bp, ci->ci_subtype, buflen);
3618 /* insert character % */
3639 printf ("composing content %s/%s from command\n\t%s\n",
3640 ci->ci_type, ci->ci_subtype, buffer);
3642 fflush (stdout); /* not sure if need for -noverbose */
3649 if ((out = fopen (ce->ce_file, "w")) == NULL)
3650 adios (ce->ce_file, "unable to open for writing");
3652 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3656 adios ("fork", "unable to fork");
3661 dup2 (fileno (out), 1);
3662 close (fileno (out));
3663 execvp ("/bin/sh", vec);
3664 fprintf (stderr, "unable to exec ");
3671 if (pidXwait(child_id, NULL))
3677 /* Check size of file */
3678 if (listsw && ct->c_end == 0L) {
3681 if (stat (ce->ce_file, &st) != NOTOK)
3682 ct->c_end = (long) st.st_size;
3694 * 1) choose a transfer encoding.
3695 * 2) check for clashes with multipart boundary string.
3696 * 3) for text content, figure out which character set is being used.
3698 * If there is a clash with one of the contents and the multipart boundary,
3699 * this function will exit with NOTOK. This will cause the scanning process
3700 * to be repeated with a different multipart boundary. It is possible
3701 * (although highly unlikely) that this scan will be repeated multiple times.
3705 scan_content (CT ct)
3708 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3709 int checklinelen, linelen = 0; /* check for long lines */
3710 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3711 int checklinespace, linespace = 0; /* check if any line ends with space */
3712 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3713 unsigned char *cp, buffer[BUFSIZ];
3716 CE ce = ct->c_cefile;
3719 * handle multipart by scanning all subparts
3720 * and then checking their encoding.
3722 if (ct->c_type == CT_MULTIPART) {
3723 struct multipart *m = (struct multipart *) ct->c_ctparams;
3726 /* initially mark the domain of enclosing multipart as 7bit */
3727 ct->c_encoding = CE_7BIT;
3729 for (part = m->mp_parts; part; part = part->mp_next) {
3730 CT p = part->mp_part;
3732 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3735 /* if necessary, enlarge encoding for enclosing multipart */
3736 if (p->c_encoding == CE_BINARY)
3737 ct->c_encoding = CE_BINARY;
3738 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3739 ct->c_encoding = CE_8BIT;
3746 * Decide what to check while scanning this content.
3748 switch (ct->c_type) {
3752 if (ct->c_subtype == TEXT_PLAIN) {
3757 checkebcdic = ebcdicsw;
3763 case CT_APPLICATION:
3765 checkebcdic = ebcdicsw;
3777 /* don't check anything for message/external */
3778 if (ct->c_subtype == MESSAGE_EXTERNAL)
3788 * Don't check anything for these types,
3789 * since we are forcing use of base64.
3800 * Scan the unencoded content
3802 if (check8bit || checklinelen || checklinespace || checkboundary) {
3803 if ((in = fopen (ce->ce_file, "r")) == NULL)
3804 adios (ce->ce_file, "unable to open for reading");
3805 len = strlen (prefix);
3807 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3809 * Check for 8bit data.
3812 for (cp = buffer; *cp; cp++) {
3813 if (!isascii (*cp)) {
3815 check8bit = 0; /* no need to keep checking */
3818 * Check if character is ebcdic-safe. We only check
3819 * this if also checking for 8bit data.
3821 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3823 checkebcdic = 0; /* no need to keep checking */
3829 * Check line length.
3831 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3833 checklinelen = 0; /* no need to keep checking */
3837 * Check if line ends with a space.
3839 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3841 checklinespace = 0; /* no need to keep checking */
3845 * Check if content contains a line that clashes
3846 * with our standard boundary for multipart messages.
3848 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3849 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3853 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3855 checkboundary = 0; /* no need to keep checking */
3863 * Decide which transfer encoding to use.
3865 switch (ct->c_type) {
3868 * If the text content didn't specify a character
3869 * set, we need to figure out which one was used.
3871 t = (struct text *) ct->c_ctparams;
3872 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3873 CI ci = &ct->c_ctinfo;
3876 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3880 t->tx_charset = CHARSET_UNKNOWN;
3881 *ap = concat ("charset=", write_charset_8bit(), NULL);
3883 t->tx_charset = CHARSET_USASCII;
3884 *ap = add ("charset=us-ascii", NULL);
3887 cp = strchr(*ap++, '=');
3893 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3894 ct->c_encoding = CE_QUOTED;
3896 ct->c_encoding = CE_7BIT;
3899 case CT_APPLICATION:
3900 /* For application type, use base64, except when postscript */
3901 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3902 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3903 ? CE_QUOTED : CE_BASE64;
3905 ct->c_encoding = CE_7BIT;
3909 ct->c_encoding = CE_7BIT;
3915 /* For audio, image, and video contents, just use base64 */
3916 ct->c_encoding = CE_BASE64;
3920 return (boundaryclash ? NOTOK : OK);
3925 * Scan the content structures, and build header
3926 * fields that will need to be output into the
3931 build_headers (CT ct)
3933 int cc, mailbody, len;
3935 char *np, *vp, buffer[BUFSIZ];
3936 CI ci = &ct->c_ctinfo;
3939 * If message is type multipart, then add the multipart
3940 * boundary to the list of attribute/value pairs.
3942 if (ct->c_type == CT_MULTIPART) {
3944 static int level = 0; /* store nesting level */
3948 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3949 cp = strchr(*ap++ = add (buffer, NULL), '=');
3956 * Skip the output of Content-Type, parameters, content
3957 * description and disposition, and Content-ID if the
3958 * content is of type "message" and the rfc934 compatibility
3959 * flag is set (which means we are inside multipart/digest
3960 * and the switch -rfc934mode was given).
3962 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3966 * output the content type and subtype
3968 np = add (TYPE_FIELD, NULL);
3969 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3971 /* keep track of length of line */
3972 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3973 + strlen (ci->ci_subtype) + 3;
3975 mailbody = ct->c_type == CT_MESSAGE
3976 && ct->c_subtype == MESSAGE_EXTERNAL
3977 && ((struct exbody *) ct->c_ctparams)->eb_body;
3980 * Append the attribute/value pairs to
3981 * the end of the Content-Type line.
3983 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3984 if (mailbody && !mh_strcasecmp (*ap, "body"))
3990 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3991 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3992 vp = add ("\n\t", vp);
3998 vp = add (buffer, vp);
4003 * Append any RFC-822 comment to the end of
4004 * the Content-Type line.
4006 if (ci->ci_comment) {
4007 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
4008 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
4009 vp = add ("\n\t", vp);
4015 vp = add (buffer, vp);
4018 vp = add ("\n", vp);
4019 add_header (ct, np, vp);
4022 * output the Content-ID, unless disabled by -nocontentid
4024 if (contentidsw && ct->c_id) {
4025 np = add (ID_FIELD, NULL);
4026 vp = concat (" ", ct->c_id, NULL);
4027 add_header (ct, np, vp);
4031 * output the Content-Description
4034 np = add (DESCR_FIELD, NULL);
4035 vp = concat (" ", ct->c_descr, NULL);
4036 add_header (ct, np, vp);
4040 * output the Content-Disposition
4043 np = add (DISPO_FIELD, NULL);
4044 vp = concat (" ", ct->c_dispo, NULL);
4045 add_header (ct, np, vp);
4050 * If this is the internal content structure for a
4051 * "message/external", then we are done with the
4052 * headers (since it has no body).
4058 * output the Content-MD5
4061 np = add (MD5_FIELD, NULL);
4062 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
4063 add_header (ct, np, vp);
4067 * output the Content-Transfer-Encoding
4069 switch (ct->c_encoding) {
4071 /* Nothing to output */
4073 np = add (ENCODING_FIELD, NULL);
4074 vp = concat (" ", "7bit", "\n", NULL);
4075 add_header (ct, np, vp);
4080 if (ct->c_type == CT_MESSAGE)
4081 adios (NULL, "internal error, invalid encoding");
4083 np = add (ENCODING_FIELD, NULL);
4084 vp = concat (" ", "8bit", "\n", NULL);
4085 add_header (ct, np, vp);
4089 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4090 adios (NULL, "internal error, invalid encoding");
4092 np = add (ENCODING_FIELD, NULL);
4093 vp = concat (" ", "quoted-printable", "\n", NULL);
4094 add_header (ct, np, vp);
4098 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4099 adios (NULL, "internal error, invalid encoding");
4101 np = add (ENCODING_FIELD, NULL);
4102 vp = concat (" ", "base64", "\n", NULL);
4103 add_header (ct, np, vp);
4107 if (ct->c_type == CT_MESSAGE)
4108 adios (NULL, "internal error, invalid encoding");
4110 np = add (ENCODING_FIELD, NULL);
4111 vp = concat (" ", "binary", "\n", NULL);
4112 add_header (ct, np, vp);
4116 adios (NULL, "unknown transfer encoding in content");
4121 * Additional content specific header processing
4123 switch (ct->c_type) {
4126 struct multipart *m;
4129 m = (struct multipart *) ct->c_ctparams;
4130 for (part = m->mp_parts; part; part = part->mp_next) {
4140 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4143 e = (struct exbody *) ct->c_ctparams;
4144 build_headers (e->eb_content);
4157 static char nib2b64[0x40+1] =
4158 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4161 calculate_digest (CT ct, int asciiP)
4164 char buffer[BUFSIZ], *vp, *op;
4166 unsigned char digest[16];
4167 unsigned char outbuf[25];
4170 CE ce = ct->c_cefile;
4173 if ((in = fopen (ce->ce_file, "r")) == NULL)
4174 adios (ce->ce_file, "unable to open for reading");
4176 /* Initialize md5 context */
4177 MD5Init (&mdContext);
4179 /* calculate md5 message digest */
4181 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4184 cp = buffer + strlen (buffer) - 1;
4185 if ((c = *cp) == '\n')
4188 MD5Update (&mdContext, (unsigned char *) buffer,
4189 (unsigned int) strlen (buffer));
4192 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4195 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4196 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4199 /* md5 finalization. Write digest and zero md5 context */
4200 MD5Final (digest, &mdContext);
4205 /* print debugging info */
4209 fprintf (stderr, "MD5 digest=");
4210 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4212 fprintf (stderr, "%02x", *dp & 0xff);
4213 fprintf (stderr, "\n");
4216 /* encode the digest using base64 */
4217 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4218 cc > 0; cc -= 3, op += 4) {
4222 bits = (*dp++ & 0xff) << 16;
4224 bits |= (*dp++ & 0xff) << 8;
4226 bits |= *dp++ & 0xff;
4229 for (bp = op + 4; bp > op; bits >>= 6)
4230 *--bp = nib2b64[bits & 0x3f];
4238 /* null terminate string */
4241 /* now make copy and return string */
4242 vp = concat (" ", outbuf, "\n", NULL);
4248 readDigest (CT ct, char *cp)
4253 unsigned char *dp, value, *ep;
4254 unsigned char *b, *b1, *b2, *b3;
4256 b = (unsigned char *) &bits,
4257 b1 = &b[endian > 0 ? 1 : 2],
4258 b2 = &b[endian > 0 ? 2 : 1],
4259 b3 = &b[endian > 0 ? 3 : 0];
4264 for (ep = (dp = ct->c_digest)
4265 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4270 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4272 fprintf (stderr, "invalid BASE64 encoding\n");
4276 bits |= value << bitno;
4278 if ((bitno -= 6) < 0) {
4279 if (dp + (3 - skip) > ep)
4280 goto invalid_digest;
4295 goto self_delimiting;
4300 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4310 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4318 fprintf (stderr, "MD5 digest=");
4319 for (dp = ct->c_digest; dp < ep; dp++)
4320 fprintf (stderr, "%02x", *dp & 0xff);
4321 fprintf (stderr, "\n");
4328 /* Make sure that buf contains at least one appearance of name,
4329 followed by =. If not, insert both name and value, just after
4330 first semicolon, if any. Note that name should not contain a
4331 trailing =. And quotes will be added around the value. Typical
4332 usage: make sure that a Content-Disposition header contains
4333 filename="foo". If it doesn't and value does, use value from
4336 incl_name_value (unsigned char *buf, char *name, char *value) {
4339 /* Assume that name is non-null. */
4341 char *name_plus_equal = concat (name, "=", NULL);
4343 if (! strstr (buf, name_plus_equal)) {
4346 char *prefix, *suffix;
4348 /* Trim trailing space, esp. newline. */
4349 for (cp = &buf[strlen (buf) - 1];
4350 cp >= buf && isspace (*cp);
4355 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
4357 /* Insert at first semicolon, if any. If none, append to
4359 prefix = add (buf, NULL);
4360 if ((cp = strchr (prefix, ';'))) {
4361 suffix = concat (cp, NULL);
4363 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
4366 /* Append to end. */
4367 newbuf = concat (buf, insertion, "\n", NULL);
4375 free (name_plus_equal);
4382 /* Extract just name_suffix="foo", if any, from value. If there isn't
4383 one, return the entire value. Note that, for example, a name_suffix
4384 of name will match filename="foo", and return foo. */
4386 extract_name_value (char *name_suffix, char *value) {
4387 char *extracted_name_value = value;
4388 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
4389 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
4392 free (name_suffix_plus_quote);
4393 if (name_suffix_equals) {
4394 char *name_suffix_begin;
4396 /* Find first \". */
4397 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
4398 name_suffix_begin = ++cp;
4399 /* Find second \". */
4400 for (; *cp != '"'; ++cp) /* empty */;
4402 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
4403 memcpy (extracted_name_value,
4405 cp - name_suffix_begin);
4406 extracted_name_value[cp - name_suffix_begin] = '\0';
4409 return extracted_name_value;