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)
253 * Main routine for translating composition file
254 * into valid MIME message. It translates the draft
255 * into a content structure (actually a tree of content
256 * structures). This message then can be manipulated
257 * in various ways, including being output via
262 build_mime (char *infile)
265 char buf[BUFSIZ], name[NAMESZ];
272 umask (~m_gmprot ());
274 /* open the composition draft */
275 if ((in = fopen (infile, "r")) == NULL)
276 adios (infile, "unable to open for reading");
279 * Allocate space for primary (outside) content
281 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
282 adios (NULL, "out of memory");
285 * Allocate structure for handling decoded content
286 * for this part. We don't really need this, but
287 * allocate it to remain consistent.
289 init_decoded_content (ct);
292 * Parse some of the header fields in the composition
293 * draft into the linked list of header fields for
294 * the new MIME message.
296 for (compnum = 1, state = FLD;;) {
297 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
303 /* abort if draft has Mime-Version header field */
304 if (!mh_strcasecmp (name, VRSN_FIELD))
305 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
307 /* abort if draft has Content-Transfer-Encoding header field */
308 if (!mh_strcasecmp (name, ENCODING_FIELD))
309 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
311 /* ignore any Content-Type fields in the header */
312 if (!mh_strcasecmp (name, TYPE_FIELD)) {
313 while (state == FLDPLUS)
314 state = m_getfld (state, name, buf, sizeof(buf), in);
318 /* get copies of the buffers */
319 np = add (name, NULL);
320 vp = add (buf, NULL);
322 /* if necessary, get rest of field */
323 while (state == FLDPLUS) {
324 state = m_getfld (state, name, buf, sizeof(buf), in);
325 vp = add (buf, vp); /* add to previous value */
328 /* Now add the header data to the list */
329 add_header (ct, np, vp);
332 /* if this wasn't the last header field, then continue */
338 adios (NULL, "draft has empty body -- no directives!");
343 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
348 adios (NULL, "message format error in component #%d", compnum);
351 adios (NULL, "getfld() returned %d", state);
357 * Now add the MIME-Version header field
358 * to the list of header fields.
360 np = add (VRSN_FIELD, NULL);
361 vp = concat (" ", VRSN_VALUE, "\n", NULL);
362 add_header (ct, np, vp);
365 * We initally assume we will find multiple contents in the
366 * draft. So create a multipart/mixed content to hold everything.
367 * We can remove this later, if it is not needed.
369 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
371 ct->c_type = CT_MULTIPART;
372 ct->c_subtype = MULTI_MIXED;
373 ct->c_file = add (infile, NULL);
375 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
376 adios (NULL, "out of memory");
377 ct->c_ctparams = (void *) m;
381 * read and parse the composition file
382 * and the directives it contains.
384 while (fgetstr (buf, sizeof(buf) - 1, in)) {
388 if (user_content (in, infile, buf, &p) == DONE) {
389 admonish (NULL, "ignoring spurious #end");
395 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
396 adios (NULL, "out of memory");
403 * close the composition draft since
404 * it's not needed any longer.
408 /* check if any contents were found */
410 adios (NULL, "no content directives found");
413 * If only one content was found, then remove and
414 * free the outer multipart content.
416 if (!m->mp_parts->mp_next) {
419 p = m->mp_parts->mp_part;
420 m->mp_parts->mp_part = NULL;
422 /* move header fields */
423 p->c_first_hf = ct->c_first_hf;
424 p->c_last_hf = ct->c_last_hf;
425 ct->c_first_hf = NULL;
426 ct->c_last_hf = NULL;
435 * Fill out, or expand directives. Parse and execute
436 * commands specified by profile composition strings.
438 compose_content (ct);
440 if ((cp = strchr(prefix, 'a')) == NULL)
441 adios (NULL, "internal error(4)");
444 * Scan the contents. Choose a transfer encoding, and
445 * check if prefix for multipart boundary clashes with
446 * any of the contents.
448 while (scan_content (ct) == NOTOK) {
453 adios (NULL, "giving up trying to find a unique delimiter string");
459 /* Build the rest of the header field structures */
467 * Main routine for reading/parsing the headers
468 * of a message content.
470 * toplevel = 1 # we are at the top level of the message
471 * toplevel = 0 # we are inside message type or multipart type
472 * # other than multipart/digest
473 * toplevel = -1 # we are inside multipart/digest
477 get_content (FILE *in, char *file, int toplevel)
480 char buf[BUFSIZ], name[NAMESZ];
483 if (!(ct = (CT) calloc (1, sizeof(*ct))))
484 adios (NULL, "out of memory");
487 ct->c_file = add (file, NULL);
488 ct->c_begin = ftell (ct->c_fp) + 1;
491 * Read the content headers
493 for (compnum = 1, state = FLD;;) {
494 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
500 /* Get MIME-Version field */
501 if (!mh_strcasecmp (name, VRSN_FIELD)) {
504 unsigned char *cp, *dp;
506 cp = add (buf, NULL);
507 while (state == FLDPLUS) {
508 state = m_getfld (state, name, buf, sizeof(buf), in);
513 advise (NULL, "message %s has multiple %s: fields (%s)",
514 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
521 while (isspace (*cp))
523 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
525 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
530 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
532 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
535 for (dp = cp; istoken (*dp); dp++)
538 ucmp = !mh_strcasecmp (cp, VRSN_VALUE);
542 "message %s has unknown value for %s: field (%s)",
543 ct->c_file, VRSN_FIELD, cp);
547 /* Get Content-Type field */
548 if (!mh_strcasecmp (name, TYPE_FIELD)) {
550 struct str2init *s2i;
551 CI ci = &ct->c_ctinfo;
553 cp = add (buf, NULL);
554 while (state == FLDPLUS) {
555 state = m_getfld (state, name, buf, sizeof(buf), in);
559 /* Check if we've already seen a Content-Type header */
561 char *dp = trimcpy (cp);
563 advise (NULL, "message %s has multiple %s: fields (%s)",
564 ct->c_file, TYPE_FIELD, dp);
570 /* Parse the Content-Type field */
571 if (get_ctinfo (cp, ct, 0) == NOTOK)
575 * Set the Init function and the internal
576 * flag for this content type.
578 for (s2i = str2cts; s2i->si_key; s2i++)
579 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
581 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
583 ct->c_type = s2i->si_val;
584 ct->c_ctinitfnx = s2i->si_init;
588 /* Get Content-Transfer-Encoding field */
589 if (!mh_strcasecmp (name, ENCODING_FIELD)) {
590 unsigned char *cp, *dp;
592 struct str2init *s2i;
594 cp = add (buf, NULL);
595 while (state == FLDPLUS) {
596 state = m_getfld (state, name, buf, sizeof(buf), in);
601 * Check if we've already seen the
602 * Content-Transfer-Encoding field
605 advise (NULL, "message %s has multiple %s: fields (%s)",
606 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
612 ct->c_celine = cp; /* Save copy of this field */
613 while (isspace (*cp))
615 for (dp = cp; istoken (*dp); dp++)
621 * Find the internal flag and Init function
622 * for this transfer encoding.
624 for (s2i = str2ces; s2i->si_key; s2i++)
625 if (!mh_strcasecmp (cp, s2i->si_key))
627 if (!s2i->si_key && !uprf (cp, "X-"))
630 ct->c_encoding = s2i->si_val;
632 /* Call the Init function for this encoding */
633 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
638 /* Get Content-ID field */
639 if (!mh_strcasecmp (name, ID_FIELD)) {
640 ct->c_id = add (buf, ct->c_id);
641 while (state == FLDPLUS) {
642 state = m_getfld (state, name, buf, sizeof(buf), in);
643 ct->c_id = add (buf, ct->c_id);
648 /* Get Content-Description field */
649 if (!mh_strcasecmp (name, DESCR_FIELD)) {
650 ct->c_descr = add (buf, ct->c_descr);
651 while (state == FLDPLUS) {
652 state = m_getfld (state, name, buf, sizeof(buf), in);
653 ct->c_descr = add (buf, ct->c_descr);
658 /* Get Content-Disposition field */
659 if (!mh_strcasecmp (name, DISPO_FIELD)) {
660 ct->c_dispo = add (buf, ct->c_dispo);
661 while (state == FLDPLUS) {
662 state = m_getfld (state, name, buf, sizeof(buf), in);
663 ct->c_dispo = add (buf, ct->c_dispo);
668 /* Get Content-MD5 field */
669 if (!mh_strcasecmp (name, MD5_FIELD)) {
670 unsigned char *cp, *dp;
673 cp = add (buf, NULL);
674 while (state == FLDPLUS) {
675 state = m_getfld (state, name, buf, sizeof(buf), in);
684 if (ct->c_digested) {
685 advise (NULL, "message %s has multiple %s: fields (%s)",
686 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
693 while (isspace (*cp))
695 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
697 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
702 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
704 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
709 for (dp = cp; *dp && !isspace (*dp); dp++)
720 if (uprf (name, XXX_FIELD_PRF))
721 advise (NULL, "unknown field (%s) in message %s",
726 while (state == FLDPLUS)
727 state = m_getfld (state, name, buf, sizeof(buf), in);
730 if (state != FLDEOF) {
731 ct->c_begin = ftell (in) + 1;
738 ct->c_begin = ftell (in) - strlen (buf);
742 ct->c_begin = ftell (in);
747 adios (NULL, "message format error in component #%d", compnum);
750 adios (NULL, "getfld() returned %d", state);
756 * Check if we saw a Content-Type field.
757 * If not, then assign a default value for
758 * it, and the Init function.
762 * If we are inside a multipart/digest message,
763 * so default type is message/rfc822
766 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
768 ct->c_type = CT_MESSAGE;
769 ct->c_ctinitfnx = InitMessage;
772 * Else default type is text/plain
774 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
776 ct->c_type = CT_TEXT;
777 ct->c_ctinitfnx = InitText;
781 /* Use default Transfer-Encoding, if necessary */
783 ct->c_encoding = CE_7BIT;
796 * small routine to add header field to list
800 add_header (CT ct, char *name, char *value)
804 /* allocate header field structure */
805 hp = mh_xmalloc (sizeof(*hp));
807 /* link data into header structure */
812 /* link header structure into the list */
813 if (ct->c_first_hf == NULL) {
814 ct->c_first_hf = hp; /* this is the first */
817 ct->c_last_hf->next = hp; /* add it to the end */
826 * Used to parse both:
827 * 1) Content-Type line
828 * 2) composition directives
830 * and fills in the information of the CTinfo structure.
834 get_ctinfo (unsigned char *cp, CT ct, int magic)
843 i = strlen (invo_name) + 2;
845 /* store copy of Content-Type line */
846 cp = ct->c_ctline = add (cp, NULL);
848 while (isspace (*cp)) /* trim leading spaces */
851 /* change newlines to spaces */
852 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
855 /* trim trailing spaces */
856 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
862 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
864 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
867 for (dp = cp; istoken (*dp); dp++)
870 ci->ci_type = add (cp, NULL); /* store content type */
874 advise (NULL, "invalid %s: field in message %s (empty type)",
875 TYPE_FIELD, ct->c_file);
879 /* down case the content type string */
880 for (dp = ci->ci_type; *dp; dp++)
881 if (isalpha(*dp) && isupper (*dp))
884 while (isspace (*cp))
887 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
892 ci->ci_subtype = add ("", NULL);
897 while (isspace (*cp))
900 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
903 for (dp = cp; istoken (*dp); dp++)
906 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
909 if (!*ci->ci_subtype) {
911 "invalid %s: field in message %s (empty subtype for \"%s\")",
912 TYPE_FIELD, ct->c_file, ci->ci_type);
916 /* down case the content subtype string */
917 for (dp = ci->ci_subtype; *dp; dp++)
918 if (isalpha(*dp) && isupper (*dp))
922 while (isspace (*cp))
925 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
929 * Parse attribute/value pairs given with Content-Type
931 ep = (ap = ci->ci_attrs) + NPARMS;
938 "too many parameters in message %s's %s: field (%d max)",
939 ct->c_file, TYPE_FIELD, NPARMS);
944 while (isspace (*cp))
947 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
952 "extraneous trailing ';' in message %s's %s: parameter list",
953 ct->c_file, TYPE_FIELD);
957 /* down case the attribute name */
958 for (dp = cp; istoken (*dp); dp++)
959 if (isalpha(*dp) && isupper (*dp))
962 for (up = dp; isspace (*dp); )
964 if (dp == cp || *dp != '=') {
966 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
967 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
971 vp = (*ap = add (cp, NULL)) + (up - cp);
973 for (dp++; isspace (*dp); )
976 /* now add the attribute value */
977 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
980 for (cp = ++dp, dp = vp;;) {
985 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
986 ct->c_file, TYPE_FIELD, i, i, "", *ap);
991 if ((c = *cp++) == '\0')
1006 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
1012 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
1013 ct->c_file, TYPE_FIELD, i, i, "", *ap);
1018 while (isspace (*cp))
1021 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1026 * Get any <Content-Id> given in buffer
1028 if (magic && *cp == '<') {
1033 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1034 advise (NULL, "invalid ID in message %s", ct->c_file);
1040 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1046 while (isspace (*cp))
1051 * Get any [Content-Description] given in buffer.
1053 if (magic && *cp == '[') {
1055 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1059 advise (NULL, "invalid description in message %s", ct->c_file);
1067 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1073 while (isspace (*cp))
1078 * Get any {Content-Disposition} given in buffer.
1080 if (magic && *cp == '{') {
1082 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1086 advise (NULL, "invalid disposition in message %s", ct->c_file);
1094 ct->c_dispo = concat (ct->c_dispo, "\n", NULL);
1100 while (isspace (*cp))
1105 * Check if anything is left over
1109 ci->ci_magic = add (cp, NULL);
1111 /* If there is a Content-Disposition header and it doesn't
1112 have a *filename=, extract it from the magic contents.
1113 The r1bindex call skips any leading directory
1117 incl_name_value (ct->c_dispo,
1119 r1bindex (extract_name_value ("name",
1126 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1127 ct->c_file, TYPE_FIELD, i, i, "", cp);
1135 get_comment (CT ct, unsigned char **ap, int istype)
1140 char c, buffer[BUFSIZ], *dp;
1149 switch (c = *cp++) {
1152 advise (NULL, "invalid comment in message %s's %s: field",
1153 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1158 if ((c = *cp++) == '\0')
1181 if ((dp = ci->ci_comment)) {
1182 ci->ci_comment = concat (dp, " ", buffer, NULL);
1185 ci->ci_comment = add (buffer, NULL);
1189 while (isspace (*cp))
1200 * Handles content types audio, image, and video.
1201 * There's not much to do right here.
1207 return OK; /* not much to do here */
1221 CI ci = &ct->c_ctinfo;
1223 /* check for missing subtype */
1224 if (!*ci->ci_subtype)
1225 ci->ci_subtype = add ("plain", ci->ci_subtype);
1228 for (kv = SubText; kv->kv_key; kv++)
1229 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1231 ct->c_subtype = kv->kv_value;
1233 /* allocate text character set structure */
1234 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1235 adios (NULL, "out of memory");
1236 ct->c_ctparams = (void *) t;
1238 /* initially mark character set as unspecified */
1239 t->tx_charset = CHARSET_UNSPECIFIED;
1241 /* scan for charset parameter */
1242 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1243 if (!mh_strcasecmp (*ap, "charset"))
1246 /* check if content specified a character set */
1248 /* match character set or set to CHARSET_UNKNOWN */
1249 for (kv = Charset; kv->kv_key; kv++)
1250 if (!mh_strcasecmp (*ep, kv->kv_key))
1252 t->tx_charset = kv->kv_value;
1264 InitMultiPart (CT ct)
1268 unsigned char *cp, *dp;
1270 char *bp, buffer[BUFSIZ];
1271 struct multipart *m;
1273 struct part *part, **next;
1274 CI ci = &ct->c_ctinfo;
1279 * The encoding for multipart messages must be either
1280 * 7bit, 8bit, or binary (per RFC2045).
1282 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1283 && ct->c_encoding != CE_BINARY) {
1285 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1286 ci->ci_type, ci->ci_subtype, ct->c_file);
1291 for (kv = SubMultiPart; kv->kv_key; kv++)
1292 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1294 ct->c_subtype = kv->kv_value;
1297 * Check for "boundary" parameter, which is
1298 * required for multipart messages.
1300 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1301 if (!mh_strcasecmp (*ap, "boundary")) {
1307 /* complain if boundary parameter is missing */
1310 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1311 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1315 /* allocate primary structure for multipart info */
1316 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1317 adios (NULL, "out of memory");
1318 ct->c_ctparams = (void *) m;
1320 /* check if boundary parameter contains only whitespace characters */
1321 for (cp = bp; isspace (*cp); cp++)
1324 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1325 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1329 /* remove trailing whitespace from boundary parameter */
1330 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1335 /* record boundary separators */
1336 m->mp_start = concat (bp, "\n", NULL);
1337 m->mp_stop = concat (bp, "--\n", NULL);
1339 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1340 advise (ct->c_file, "unable to open for reading");
1344 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1346 next = &m->mp_parts;
1350 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1354 pos += strlen (buffer);
1355 if (buffer[0] != '-' || buffer[1] != '-')
1358 if (strcmp (buffer + 2, m->mp_start))
1361 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1362 adios (NULL, "out of memory");
1364 next = &part->mp_next;
1366 if (!(p = get_content (fp, ct->c_file,
1367 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1375 fseek (fp, pos, SEEK_SET);
1378 if (strcmp (buffer + 2, m->mp_start) == 0) {
1382 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1383 if (p->c_end < p->c_begin)
1384 p->c_begin = p->c_end;
1389 if (strcmp (buffer + 2, m->mp_stop) == 0)
1395 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1396 if (!inout && part) {
1398 p->c_end = ct->c_end;
1400 if (p->c_begin >= p->c_end) {
1401 for (next = &m->mp_parts; *next != part;
1402 next = &((*next)->mp_next))
1406 free ((char *) part);
1411 /* reverse the order of the parts for multipart/alternative */
1412 if (ct->c_subtype == MULTI_ALTERNATE)
1416 * label all subparts with part number, and
1417 * then initialize the content of the subpart.
1422 char partnam[BUFSIZ];
1425 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1426 pp = partnam + strlen (partnam);
1431 for (part = m->mp_parts, partnum = 1; part;
1432 part = part->mp_next, partnum++) {
1435 sprintf (pp, "%d", partnum);
1436 p->c_partno = add (partnam, NULL);
1438 /* initialize the content of the subparts */
1439 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1454 * reverse the order of the parts of a multipart
1458 reverse_parts (CT ct)
1461 struct multipart *m;
1462 struct part **base, **bmp, **next, *part;
1464 m = (struct multipart *) ct->c_ctparams;
1466 /* if only one part, just return */
1467 if (!m->mp_parts || !m->mp_parts->mp_next)
1470 /* count number of parts */
1472 for (part = m->mp_parts; part; part = part->mp_next)
1475 /* allocate array of pointers to the parts */
1476 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1477 adios (NULL, "out of memory");
1480 /* point at all the parts */
1481 for (part = m->mp_parts; part; part = part->mp_next)
1485 /* reverse the order of the parts */
1486 next = &m->mp_parts;
1487 for (bmp--; bmp >= base; bmp--) {
1490 next = &part->mp_next;
1494 /* free array of pointers */
1495 free ((char *) base);
1507 CI ci = &ct->c_ctinfo;
1509 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1511 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1512 ci->ci_type, ci->ci_subtype, ct->c_file);
1516 /* check for missing subtype */
1517 if (!*ci->ci_subtype)
1518 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1521 for (kv = SubMessage; kv->kv_key; kv++)
1522 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1524 ct->c_subtype = kv->kv_value;
1526 switch (ct->c_subtype) {
1527 case MESSAGE_RFC822:
1530 case MESSAGE_PARTIAL:
1535 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1536 adios (NULL, "out of memory");
1537 ct->c_ctparams = (void *) p;
1539 /* scan for parameters "id", "number", and "total" */
1540 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1541 if (!mh_strcasecmp (*ap, "id")) {
1542 p->pm_partid = add (*ep, NULL);
1545 if (!mh_strcasecmp (*ap, "number")) {
1546 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1547 || p->pm_partno < 1) {
1550 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1551 *ap, ci->ci_type, ci->ci_subtype,
1552 ct->c_file, TYPE_FIELD);
1557 if (!mh_strcasecmp (*ap, "total")) {
1558 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1567 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1569 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1570 ci->ci_type, ci->ci_subtype,
1571 ct->c_file, TYPE_FIELD);
1577 case MESSAGE_EXTERNAL:
1584 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1585 adios (NULL, "out of memory");
1586 ct->c_ctparams = (void *) e;
1589 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1590 advise (ct->c_file, "unable to open for reading");
1594 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1596 if (!(p = get_content (fp, ct->c_file, 0))) {
1605 if ((exresult = params_external (ct, 0)) != NOTOK
1606 && p->c_ceopenfnx == openMail) {
1610 if ((size = ct->c_end - p->c_begin) <= 0) {
1612 content_error (NULL, ct,
1613 "empty body for access-type=mail-server");
1617 e->eb_body = bp = mh_xmalloc ((unsigned) size);
1618 fseek (p->c_fp, p->c_begin, SEEK_SET);
1620 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1622 adios ("failed", "fread");
1625 adios (NULL, "unexpected EOF from fread");
1628 bp += cc, size -= cc;
1635 p->c_end = p->c_begin;
1640 if (exresult == NOTOK)
1642 if (e->eb_flags == NOTOK)
1645 switch (p->c_type) {
1650 if (p->c_subtype != MESSAGE_RFC822)
1654 e->eb_partno = ct->c_partno;
1656 (*p->c_ctinitfnx) (p);
1671 params_external (CT ct, int composing)
1674 struct exbody *e = (struct exbody *) ct->c_ctparams;
1675 CI ci = &ct->c_ctinfo;
1677 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1678 if (!mh_strcasecmp (*ap, "access-type")) {
1679 struct str2init *s2i;
1680 CT p = e->eb_content;
1682 for (s2i = str2methods; s2i->si_key; s2i++)
1683 if (!mh_strcasecmp (*ep, s2i->si_key))
1688 e->eb_flags = NOTOK;
1689 p->c_encoding = CE_EXTERNAL;
1692 e->eb_access = s2i->si_key;
1693 e->eb_flags = s2i->si_val;
1694 p->c_encoding = CE_EXTERNAL;
1696 /* Call the Init function for this external type */
1697 if ((*s2i->si_init)(p) == NOTOK)
1701 if (!mh_strcasecmp (*ap, "name")) {
1705 if (!mh_strcasecmp (*ap, "permission")) {
1706 e->eb_permission = *ep;
1709 if (!mh_strcasecmp (*ap, "site")) {
1713 if (!mh_strcasecmp (*ap, "directory")) {
1717 if (!mh_strcasecmp (*ap, "mode")) {
1721 if (!mh_strcasecmp (*ap, "size")) {
1722 sscanf (*ep, "%lu", &e->eb_size);
1725 if (!mh_strcasecmp (*ap, "server")) {
1729 if (!mh_strcasecmp (*ap, "subject")) {
1730 e->eb_subject = *ep;
1733 if (composing && !mh_strcasecmp (*ap, "body")) {
1734 e->eb_body = getcpy (*ep);
1739 if (!e->eb_access) {
1741 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1742 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1755 InitApplication (CT ct)
1758 CI ci = &ct->c_ctinfo;
1761 for (kv = SubApplication; kv->kv_key; kv++)
1762 if (!mh_strcasecmp (ci->ci_subtype, kv->kv_key))
1764 ct->c_subtype = kv->kv_value;
1771 * Set up structures for placing unencoded
1772 * content when building parts.
1776 init_decoded_content (CT ct)
1780 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1781 adios (NULL, "out of memory");
1784 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1785 ct->c_ceclosefnx = close_encoding;
1786 ct->c_cesizefnx = NULL; /* since unencoded */
1793 * TRANSFER ENCODINGS
1797 init_encoding (CT ct, OpenCEFunc openfnx)
1801 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1802 adios (NULL, "out of memory");
1805 ct->c_ceopenfnx = openfnx;
1806 ct->c_ceclosefnx = close_encoding;
1807 ct->c_cesizefnx = size_encoding;
1814 close_encoding (CT ct)
1818 if (!(ce = ct->c_cefile))
1828 static unsigned long
1829 size_encoding (CT ct)
1837 if (!(ce = ct->c_cefile))
1838 return (ct->c_end - ct->c_begin);
1840 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1841 return (long) st.st_size;
1844 if (stat (ce->ce_file, &st) != NOTOK)
1845 return (long) st.st_size;
1850 if (ct->c_encoding == CE_EXTERNAL)
1851 return (ct->c_end - ct->c_begin);
1854 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1855 return (ct->c_end - ct->c_begin);
1857 if (fstat (fd, &st) != NOTOK)
1858 size = (long) st.st_size;
1862 (*ct->c_ceclosefnx) (ct);
1871 static unsigned char b642nib[0x80] = {
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, 0xff, 0xff, 0xff, 0xff, 0xff,
1877 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1878 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1879 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1880 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1881 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1882 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1883 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1884 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1885 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1886 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1887 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1894 return init_encoding (ct, openBase64);
1899 openBase64 (CT ct, char **file)
1901 int bitno, cc, digested;
1904 unsigned char value, *b, *b1, *b2, *b3;
1905 unsigned char *cp, *ep;
1906 char buffer[BUFSIZ];
1910 b = (unsigned char *) &bits;
1911 b1 = &b[endian > 0 ? 1 : 2];
1912 b2 = &b[endian > 0 ? 2 : 1];
1913 b3 = &b[endian > 0 ? 3 : 0];
1917 fseek (ce->ce_fp, 0L, SEEK_SET);
1922 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1923 content_error (ce->ce_file, ct, "unable to fopen for reading");
1929 if (*file == NULL) {
1930 ce->ce_file = add (m_scratch ("", tmp), NULL);
1933 ce->ce_file = add (*file, NULL);
1937 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1938 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1942 if ((len = ct->c_end - ct->c_begin) < 0)
1943 adios (NULL, "internal error(1)");
1945 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1946 content_error (ct->c_file, ct, "unable to open for reading");
1950 if ((digested = ct->c_digested))
1951 MD5Init (&mdContext);
1957 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1959 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1961 content_error (ct->c_file, ct, "error reading from");
1965 content_error (NULL, ct, "premature eof");
1973 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1978 if (skip || (*cp & 0x80)
1979 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1981 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1983 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1986 content_error (NULL, ct,
1987 "invalid BASE64 encoding -- continuing");
1991 bits |= value << bitno;
1993 if ((bitno -= 6) < 0) {
1994 putc ((char) *b1, ce->ce_fp);
1996 MD5Update (&mdContext, b1, 1);
1998 putc ((char) *b2, ce->ce_fp);
2000 MD5Update (&mdContext, b2, 1);
2002 putc ((char) *b3, ce->ce_fp);
2004 MD5Update (&mdContext, b3, 1);
2008 if (ferror (ce->ce_fp)) {
2009 content_error (ce->ce_file, ct,
2010 "error writing to");
2013 bitno = 18, bits = 0L, skip = 0;
2019 goto self_delimiting;
2028 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
2030 content_error (NULL, ct, "invalid BASE64 encoding");
2035 fseek (ct->c_fp, 0L, SEEK_SET);
2037 if (fflush (ce->ce_fp)) {
2038 content_error (ce->ce_file, ct, "error writing to");
2043 unsigned char digest[16];
2045 MD5Final (digest, &mdContext);
2046 if (memcmp((char *) digest, (char *) ct->c_digest,
2047 sizeof(digest) / sizeof(digest[0])))
2048 content_error (NULL, ct,
2049 "content integrity suspect (digest mismatch) -- continuing");
2052 fprintf (stderr, "content integrity confirmed\n");
2055 fseek (ce->ce_fp, 0L, SEEK_SET);
2058 *file = ce->ce_file;
2059 return fileno (ce->ce_fp);
2062 free_encoding (ct, 0);
2071 static char hex2nib[0x80] = {
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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2078 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2079 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2080 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2081 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2082 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2083 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2084 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2085 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2086 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2087 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2094 return init_encoding (ct, openQuoted);
2099 openQuoted (CT ct, char **file)
2101 int cc, digested, len, quoted;
2102 unsigned char *cp, *ep;
2103 char buffer[BUFSIZ];
2110 fseek (ce->ce_fp, 0L, SEEK_SET);
2115 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2116 content_error (ce->ce_file, ct, "unable to fopen for reading");
2122 if (*file == NULL) {
2123 ce->ce_file = add (m_scratch ("", tmp), NULL);
2126 ce->ce_file = add (*file, NULL);
2130 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2131 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2135 if ((len = ct->c_end - ct->c_begin) < 0)
2136 adios (NULL, "internal error(2)");
2138 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2139 content_error (ct->c_file, ct, "unable to open for reading");
2143 if ((digested = ct->c_digested))
2144 MD5Init (&mdContext);
2151 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2155 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2156 content_error (NULL, ct, "premature eof");
2160 if ((cc = strlen (buffer)) > len)
2164 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2169 for (; cp < ep; cp++) {
2172 if (!isxdigit (*cp)) {
2174 dp = "expecting hexidecimal-digit";
2175 goto invalid_encoding;
2178 mask |= hex2nib[*cp & 0x7f];
2179 putc (mask, ce->ce_fp);
2181 MD5Update (&mdContext, &mask, 1);
2185 putc (*cp, ce->ce_fp);
2187 MD5Update (&mdContext, (unsigned char *) ":", 1);
2191 if (!isxdigit (*cp))
2193 mask = hex2nib[*cp & 0x7f];
2199 if (ferror (ce->ce_fp)) {
2200 content_error (ce->ce_file, ct, "error writing to");
2209 if (*cp < '!' || *cp > '~') {
2211 dp = "expecting character in range [!..~]";
2214 i = strlen (invo_name) + 2;
2215 content_error (NULL, ct,
2216 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2224 putc (*cp, ce->ce_fp);
2227 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2229 MD5Update (&mdContext, (unsigned char *) cp, 1);
2231 if (ferror (ce->ce_fp)) {
2232 content_error (ce->ce_file, ct, "error writing to");
2238 if (*++cp != '\n') {
2247 content_error (NULL, ct,
2248 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2252 fseek (ct->c_fp, 0L, SEEK_SET);
2254 if (fflush (ce->ce_fp)) {
2255 content_error (ce->ce_file, ct, "error writing to");
2260 unsigned char digest[16];
2262 MD5Final (digest, &mdContext);
2263 if (memcmp((char *) digest, (char *) ct->c_digest,
2264 sizeof(digest) / sizeof(digest[0])))
2265 content_error (NULL, ct,
2266 "content integrity suspect (digest mismatch) -- continuing");
2269 fprintf (stderr, "content integrity confirmed\n");
2272 fseek (ce->ce_fp, 0L, SEEK_SET);
2275 *file = ce->ce_file;
2276 return fileno (ce->ce_fp);
2279 free_encoding (ct, 0);
2291 if (init_encoding (ct, open7Bit) == NOTOK)
2294 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2300 open7Bit (CT ct, char **file)
2303 char buffer[BUFSIZ];
2308 fseek (ce->ce_fp, 0L, SEEK_SET);
2313 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2314 content_error (ce->ce_file, ct, "unable to fopen for reading");
2320 if (*file == NULL) {
2321 ce->ce_file = add (m_scratch ("", tmp), NULL);
2324 ce->ce_file = add (*file, NULL);
2328 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2329 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2333 if (ct->c_type == CT_MULTIPART) {
2335 CI ci = &ct->c_ctinfo;
2338 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2339 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2340 + 1 + strlen (ci->ci_subtype);
2341 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2342 putc (';', ce->ce_fp);
2345 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2347 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2348 fputs ("\n\t", ce->ce_fp);
2351 putc (' ', ce->ce_fp);
2354 fprintf (ce->ce_fp, "%s", buffer);
2358 if (ci->ci_comment) {
2359 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2360 fputs ("\n\t", ce->ce_fp);
2364 putc (' ', ce->ce_fp);
2367 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2370 fprintf (ce->ce_fp, "\n");
2372 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2374 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2376 fprintf (ce->ce_fp, "%s:%s", DISPO_FIELD, ct->c_dispo);
2377 fprintf (ce->ce_fp, "\n");
2380 if ((len = ct->c_end - ct->c_begin) < 0)
2381 adios (NULL, "internal error(3)");
2383 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2384 content_error (ct->c_file, ct, "unable to open for reading");
2388 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2390 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2392 content_error (ct->c_file, ct, "error reading from");
2396 content_error (NULL, ct, "premature eof");
2404 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2405 if (ferror (ce->ce_fp)) {
2406 content_error (ce->ce_file, ct, "error writing to");
2411 fseek (ct->c_fp, 0L, SEEK_SET);
2413 if (fflush (ce->ce_fp)) {
2414 content_error (ce->ce_file, ct, "error writing to");
2418 fseek (ce->ce_fp, 0L, SEEK_SET);
2421 *file = ce->ce_file;
2422 return fileno (ce->ce_fp);
2425 free_encoding (ct, 0);
2435 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2437 char cachefile[BUFSIZ];
2440 fseek (ce->ce_fp, 0L, SEEK_SET);
2445 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2446 content_error (ce->ce_file, ct, "unable to fopen for reading");
2452 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2453 cachefile, sizeof(cachefile)) != NOTOK) {
2454 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2455 ce->ce_file = getcpy (cachefile);
2459 admonish (cachefile, "unable to fopen for reading");
2466 *file = ce->ce_file;
2467 *fd = fileno (ce->ce_fp);
2478 return init_encoding (ct, openFile);
2483 openFile (CT ct, char **file)
2486 char cachefile[BUFSIZ];
2487 struct exbody *e = ct->c_ctexbody;
2488 CE ce = ct->c_cefile;
2490 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2502 content_error (NULL, ct, "missing name parameter");
2506 ce->ce_file = getcpy (e->eb_name);
2509 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2510 content_error (ce->ce_file, ct, "unable to fopen for reading");
2514 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2515 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2516 cachefile, sizeof(cachefile)) != NOTOK) {
2520 mask = umask (cachetype ? ~m_gmprot () : 0222);
2521 if ((fp = fopen (cachefile, "w"))) {
2523 char buffer[BUFSIZ];
2524 FILE *gp = ce->ce_fp;
2526 fseek (gp, 0L, SEEK_SET);
2528 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2530 fwrite (buffer, sizeof(*buffer), cc, fp);
2534 admonish (ce->ce_file, "error reading");
2539 admonish (cachefile, "error writing");
2547 fseek (ce->ce_fp, 0L, SEEK_SET);
2548 *file = ce->ce_file;
2549 return fileno (ce->ce_fp);
2559 return init_encoding (ct, openFTP);
2564 openFTP (CT ct, char **file)
2566 int cachetype, caching, fd;
2568 char *bp, *ftp, *user, *pass;
2569 char buffer[BUFSIZ], cachefile[BUFSIZ];
2572 static char *username = NULL;
2573 static char *password = NULL;
2578 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2586 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2597 if (!e->eb_name || !e->eb_site) {
2598 content_error (NULL, ct, "missing %s parameter",
2599 e->eb_name ? "site": "name");
2606 pidcheck (pidwait (xpid, NOTOK));
2610 /* Get the buffer ready to go */
2612 buflen = sizeof(buffer);
2615 * Construct the query message for user
2617 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2623 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2629 snprintf (bp, buflen, "\n using %sFTP from site %s",
2630 e->eb_flags ? "anonymous " : "", e->eb_site);
2635 if (e->eb_size > 0) {
2636 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2641 snprintf (bp, buflen, "? ");
2644 * Now, check the answer
2646 if (!getanswer (buffer))
2651 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2654 ruserpass (e->eb_site, &username, &password);
2659 ce->ce_unlink = (*file == NULL);
2661 cachefile[0] = '\0';
2662 if ((!e->eb_permission || mh_strcasecmp (e->eb_permission, "read-write"))
2663 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2664 cachefile, sizeof(cachefile)) != NOTOK) {
2665 if (*file == NULL) {
2672 ce->ce_file = add (*file, NULL);
2674 ce->ce_file = add (cachefile, NULL);
2676 ce->ce_file = add (m_scratch ("", tmp), NULL);
2678 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2679 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2687 int child_id, i, vecp;
2691 vec[vecp++] = r1bindex (ftp, '/');
2692 vec[vecp++] = e->eb_site;
2695 vec[vecp++] = e->eb_dir;
2696 vec[vecp++] = e->eb_name;
2697 vec[vecp++] = ce->ce_file,
2698 vec[vecp++] = e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii")
2699 ? "ascii" : "binary";
2704 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2708 adios ("fork", "unable to");
2712 close (fileno (ce->ce_fp));
2714 fprintf (stderr, "unable to exec ");
2720 if (pidXwait (child_id, NULL)) {
2724 username = password = NULL;
2733 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2735 e->eb_mode && !mh_strcasecmp (e->eb_mode, "ascii"), 0)
2742 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2747 mask = umask (cachetype ? ~m_gmprot () : 0222);
2748 if ((fp = fopen (cachefile, "w"))) {
2750 FILE *gp = ce->ce_fp;
2752 fseek (gp, 0L, SEEK_SET);
2754 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2756 fwrite (buffer, sizeof(*buffer), cc, fp);
2760 admonish (ce->ce_file, "error reading");
2765 admonish (cachefile, "error writing");
2774 fseek (ce->ce_fp, 0L, SEEK_SET);
2775 *file = ce->ce_file;
2776 return fileno (ce->ce_fp);
2787 return init_encoding (ct, openMail);
2792 openMail (CT ct, char **file)
2794 int child_id, fd, i, vecp;
2796 char *bp, buffer[BUFSIZ], *vec[7];
2797 struct exbody *e = ct->c_ctexbody;
2798 CE ce = ct->c_cefile;
2800 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2811 if (!e->eb_server) {
2812 content_error (NULL, ct, "missing server parameter");
2819 pidcheck (pidwait (xpid, NOTOK));
2823 /* Get buffer ready to go */
2825 buflen = sizeof(buffer);
2827 /* Now construct query message */
2828 snprintf (bp, buflen, "Retrieve content");
2834 snprintf (bp, buflen, " %s", e->eb_partno);
2840 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2842 e->eb_subject ? e->eb_subject : e->eb_body);
2844 /* Now, check answer */
2845 if (!getanswer (buffer))
2849 vec[vecp++] = r1bindex (mailproc, '/');
2850 vec[vecp++] = e->eb_server;
2851 vec[vecp++] = "-subject";
2852 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2853 vec[vecp++] = "-body";
2854 vec[vecp++] = e->eb_body;
2857 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2861 advise ("fork", "unable to");
2865 execvp (mailproc, vec);
2866 fprintf (stderr, "unable to exec ");
2872 if (pidXwait (child_id, NULL) == OK)
2873 advise (NULL, "request sent");
2877 if (*file == NULL) {
2878 ce->ce_file = add (m_scratch ("", tmp), NULL);
2881 ce->ce_file = add (*file, NULL);
2885 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2886 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2890 fseek (ce->ce_fp, 0L, SEEK_SET);
2891 *file = ce->ce_file;
2892 return fileno (ce->ce_fp);
2897 fgetstr (char *s, int n, FILE *stream)
2901 for (ep = (cp = s) + n; cp < ep; ) {
2904 if (!fgets (cp, n, stream))
2905 return (cp != s ? s : NULL);
2906 if (cp == s && *cp != '#')
2909 cp += (i = strlen (cp)) - 1;
2910 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2921 * Parse the composition draft for text and directives.
2922 * Do initial setup of Content structure.
2926 user_content (FILE *in, char *file, char *buf, CT *ctp)
2931 char buffer[BUFSIZ];
2932 struct multipart *m;
2935 struct str2init *s2i;
2940 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2945 /* allocate basic Content structure */
2946 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2947 adios (NULL, "out of memory");
2950 /* allocate basic structure for handling decoded content */
2951 init_decoded_content (ct);
2958 * Handle inline text. Check if line
2959 * is one of the following forms:
2961 * 1) doesn't begin with '#' (implicit directive)
2962 * 2) begins with "##" (implicit directive)
2963 * 3) begins with "#<"
2965 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2969 char content[BUFSIZ];
2972 /* use a temp file to collect the plain text lines */
2973 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2976 if ((out = fopen (ce->ce_file, "w")) == NULL)
2977 adios (ce->ce_file, "unable to open for writing");
2979 if (buf[0] == '#' && buf[1] == '<') {
2980 strncpy (content, buf + 2, sizeof(content));
2987 /* the directive is implicit */
2988 strncpy (content, "text/plain", sizeof(content));
2990 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2994 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2995 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2999 ct->c_descr = add (buffer + i + 1, ct->c_descr);
3000 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
3001 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
3002 switch (buffer[0]) {
3009 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
3017 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
3018 && buffer[i = strlen (DISPO_FIELD)] == ':') {
3022 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
3023 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
3024 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
3025 switch (buffer[0]) {
3032 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
3040 if (headers != 1 || buffer[0] != '\n')
3041 fputs (buffer, out);
3046 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
3048 if (buffer[0] == '#') {
3051 if (buffer[1] != '#')
3053 for (cp = (bp = buffer) + 1; *cp; cp++)
3060 ct->c_end = ftell (out);
3063 /* parse content type */
3064 if (get_ctinfo (content, ct, inlineD) == NOTOK)
3067 for (s2i = str2cts; s2i->si_key; s2i++)
3068 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
3070 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
3074 * check type specified (possibly implicitly)
3076 switch (ct->c_type = s2i->si_val) {
3078 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
3079 ct->c_encoding = CE_7BIT;
3084 adios (NULL, "it doesn't make sense to define an in-line %s content",
3085 ct->c_type == CT_MESSAGE ? "message" : "multipart");
3090 if ((ct->c_ctinitfnx = s2i->si_init))
3091 (*ct->c_ctinitfnx) (ct);
3096 fseek (in, pos, SEEK_SET);
3101 * If we've reached this point, the next line
3102 * must be some type of explicit directive.
3105 /* check if directive is external-type */
3106 extrnal = (buf[1] == '@');
3108 /* parse directive */
3109 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3112 /* check directive against the list of MIME types */
3113 for (s2i = str2cts; s2i->si_key; s2i++)
3114 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
3118 * Check if the directive specified a valid type.
3119 * This will happen if it was one of the following forms:
3121 * #type/subtype (or)
3125 if (!ci->ci_subtype)
3126 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3128 switch (ct->c_type = s2i->si_val) {
3130 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3131 ci->ci_type, ci->ci_subtype);
3135 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
3136 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3137 ci->ci_type, ci->ci_subtype);
3138 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
3139 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3140 ci->ci_type, ci->ci_subtype);
3143 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3144 ci->ci_type, ci->ci_subtype);
3148 if ((ct->c_ctinitfnx = s2i->si_init))
3149 (*ct->c_ctinitfnx) (ct);
3154 * #@type/subtype (external types directive)
3161 adios (NULL, "need external information for \"#@%s/%s\"",
3162 ci->ci_type, ci->ci_subtype);
3165 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3166 free (ci->ci_magic);
3167 ci->ci_magic = NULL;
3170 * Since we are using the current Content structure to
3171 * hold information about the type of the external
3172 * reference, we need to create another Content structure
3173 * for the message/external-body to wrap it in.
3175 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3176 adios (NULL, "out of memory");
3179 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3181 ct->c_type = CT_MESSAGE;
3182 ct->c_subtype = MESSAGE_EXTERNAL;
3184 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3185 adios (NULL, "out of memory");
3186 ct->c_ctparams = (void *) e;
3192 if (params_external (ct, 1) == NOTOK)
3198 /* Handle [file] argument */
3200 /* check if specifies command to execute */
3201 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3202 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3205 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3206 cp = add (cp, NULL);
3207 free (ci->ci_magic);
3210 /* record filename of decoded contents */
3211 ce->ce_file = ci->ci_magic;
3212 if (access (ce->ce_file, R_OK) == NOTOK)
3213 adios ("reading", "unable to access %s for", ce->ce_file);
3214 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3215 ct->c_end = (long) st.st_size;
3216 ci->ci_magic = NULL;
3222 * No [file] argument, so check profile for
3223 * method to compose content.
3225 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3226 invo_name, ci->ci_type, ci->ci_subtype);
3227 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3228 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3229 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3230 content_error (NULL, ct, "don't know how to compose content");
3234 ci->ci_magic = add (cp, NULL);
3239 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3243 * #forw [+folder] [msgs]
3245 if (!mh_strcasecmp (ci->ci_type, "forw")) {
3247 char *folder, *arguments[MAXARGS];
3251 ap = brkstring (ci->ci_magic, " ", "\n");
3252 copyip (ap, arguments, MAXARGS);
3254 arguments[0] = "cur";
3255 arguments[1] = NULL;
3259 /* search the arguments for a folder name */
3260 for (ap = arguments; *ap; ap++) {
3262 if (*cp == '+' || *cp == '@') {
3264 adios (NULL, "only one folder per #forw directive");
3266 folder = pluspath (cp);
3270 /* else, use the current folder */
3272 folder = add (getfolder (1), NULL);
3274 if (!(mp = folder_read (folder)))
3275 adios (NULL, "unable to read folder %s", folder);
3276 for (ap = arguments; *ap; ap++) {
3278 if (*cp != '+' && *cp != '@')
3279 if (!m_convert (mp, cp))
3286 * If there is more than one message to include, make this
3287 * a content of type "multipart/digest" and insert each message
3288 * as a subpart. If there is only one message, then make this
3289 * a content of type "message/rfc822".
3291 if (mp->numsel > 1) {
3292 /* we are forwarding multiple messages */
3293 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3295 ct->c_type = CT_MULTIPART;
3296 ct->c_subtype = MULTI_DIGEST;
3298 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3299 adios (NULL, "out of memory");
3300 ct->c_ctparams = (void *) m;
3303 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3304 if (is_selected(mp, msgnum)) {
3309 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3310 adios (NULL, "out of memory");
3311 init_decoded_content (p);
3313 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3315 p->c_type = CT_MESSAGE;
3316 p->c_subtype = MESSAGE_RFC822;
3318 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3319 pe->ce_file = add (buffer, NULL);
3320 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3321 p->c_end = (long) st.st_size;
3323 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3324 adios (NULL, "out of memory");
3326 pp = &part->mp_next;
3331 /* we are forwarding one message */
3332 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3334 ct->c_type = CT_MESSAGE;
3335 ct->c_subtype = MESSAGE_RFC822;
3337 msgnum = mp->lowsel;
3338 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3339 ce->ce_file = add (buffer, NULL);
3340 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3341 ct->c_end = (long) st.st_size;
3344 folder_free (mp); /* free folder/message structure */
3351 if (!mh_strcasecmp (ci->ci_type, "end")) {
3358 * #begin [ alternative | parallel ]
3360 if (!mh_strcasecmp (ci->ci_type, "begin")) {
3361 if (!ci->ci_magic) {
3363 cp = SubMultiPart[vrsn - 1].kv_key;
3364 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
3365 vrsn = MULTI_ALTERNATE;
3366 cp = SubMultiPart[vrsn - 1].kv_key;
3367 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
3368 vrsn = MULTI_PARALLEL;
3369 cp = SubMultiPart[vrsn - 1].kv_key;
3370 } else if (uprf (ci->ci_magic, "digest")) {
3373 vrsn = MULTI_UNKNOWN;
3378 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3379 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3381 ct->c_type = CT_MULTIPART;
3382 ct->c_subtype = vrsn;
3384 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3385 adios (NULL, "out of memory");
3386 ct->c_ctparams = (void *) m;
3389 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3393 if (user_content (in, file, buffer, &p) == DONE) {
3395 adios (NULL, "empty \"#begin ... #end\" sequence");
3401 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3402 adios (NULL, "out of memory");
3404 pp = &part->mp_next;
3407 admonish (NULL, "premature end-of-file, missing #end");
3414 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3415 return NOTOK; /* NOT REACHED */
3420 set_id (CT ct, int top)
3424 static time_t clock = 0;
3425 static char *msgfmt;
3429 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3430 (int) getpid(), (long) clock, LocalName());
3432 msgfmt = getcpy(msgid);
3434 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3435 ct->c_id = getcpy (msgid);
3439 static char ebcdicsafe[0x100] = {
3440 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3441 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3442 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3443 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3444 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3445 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3446 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3447 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3448 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3449 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3450 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3451 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3452 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3453 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3454 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3455 0x01, 0x01, 0x01, 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,
3471 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3476 * Fill out, or expand the various contents in the composition
3477 * draft. Read-in any necessary files. Parse and execute any
3478 * commands specified by profile composition strings.
3482 compose_content (CT ct)
3484 CE ce = ct->c_cefile;
3486 switch (ct->c_type) {
3491 char partnam[BUFSIZ];
3492 struct multipart *m = (struct multipart *) ct->c_ctparams;
3496 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3497 pp = partnam + strlen (partnam);
3502 /* first, we call compose_content on all the subparts */
3503 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3504 CT p = part->mp_part;
3506 sprintf (pp, "%d", partnum);
3507 p->c_partno = add (partnam, NULL);
3508 if (compose_content (p) == NOTOK)
3513 * If the -rfc934mode switch is given, then check all
3514 * the subparts of a multipart/digest. If they are all
3515 * message/rfc822, then mark this content and all
3516 * subparts with the rfc934 compatibility mode flag.
3518 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3521 for (part = m->mp_parts; part; part = part->mp_next) {
3522 CT p = part->mp_part;
3524 if (p->c_subtype != MESSAGE_RFC822) {
3529 ct->c_rfc934 = is934;
3530 for (part = m->mp_parts; part; part = part->mp_next) {
3531 CT p = part->mp_part;
3533 if ((p->c_rfc934 = is934))
3539 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3543 for (part = m->mp_parts; part; part = part->mp_next)
3544 ct->c_end += part->mp_part->c_end + partnum;
3550 /* Nothing to do for type message */
3554 * Discrete types (text/application/audio/image/video)
3559 int i, xstdout, len, buflen;
3560 char *bp, **ap, *cp;
3561 char *vec[4], buffer[BUFSIZ];
3563 CI ci = &ct->c_ctinfo;
3565 if (!(cp = ci->ci_magic))
3566 adios (NULL, "internal error(5)");
3568 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3573 /* Get buffer ready to go */
3576 buflen = sizeof(buffer);
3579 * Parse composition string into buffer
3581 for ( ; *cp; cp++) {
3586 /* insert parameters from directive */
3590 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3591 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3601 /* %f, and stdout is not-redirected */
3607 * insert temporary filename where
3608 * content should be written
3610 snprintf (bp, buflen, "%s", ce->ce_file);
3614 /* insert content subtype */
3615 strncpy (bp, ci->ci_subtype, buflen);
3619 /* insert character % */
3640 printf ("composing content %s/%s from command\n\t%s\n",
3641 ci->ci_type, ci->ci_subtype, buffer);
3643 fflush (stdout); /* not sure if need for -noverbose */
3650 if ((out = fopen (ce->ce_file, "w")) == NULL)
3651 adios (ce->ce_file, "unable to open for writing");
3653 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3657 adios ("fork", "unable to fork");
3662 dup2 (fileno (out), 1);
3663 close (fileno (out));
3664 execvp ("/bin/sh", vec);
3665 fprintf (stderr, "unable to exec ");
3672 if (pidXwait(child_id, NULL))
3678 /* Check size of file */
3679 if (listsw && ct->c_end == 0L) {
3682 if (stat (ce->ce_file, &st) != NOTOK)
3683 ct->c_end = (long) st.st_size;
3695 * 1) choose a transfer encoding.
3696 * 2) check for clashes with multipart boundary string.
3697 * 3) for text content, figure out which character set is being used.
3699 * If there is a clash with one of the contents and the multipart boundary,
3700 * this function will exit with NOTOK. This will cause the scanning process
3701 * to be repeated with a different multipart boundary. It is possible
3702 * (although highly unlikely) that this scan will be repeated multiple times.
3706 scan_content (CT ct)
3709 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3710 int checklinelen, linelen = 0; /* check for long lines */
3711 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3712 int checklinespace, linespace = 0; /* check if any line ends with space */
3713 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3714 unsigned char *cp, buffer[BUFSIZ];
3717 CE ce = ct->c_cefile;
3720 * handle multipart by scanning all subparts
3721 * and then checking their encoding.
3723 if (ct->c_type == CT_MULTIPART) {
3724 struct multipart *m = (struct multipart *) ct->c_ctparams;
3727 /* initially mark the domain of enclosing multipart as 7bit */
3728 ct->c_encoding = CE_7BIT;
3730 for (part = m->mp_parts; part; part = part->mp_next) {
3731 CT p = part->mp_part;
3733 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3736 /* if necessary, enlarge encoding for enclosing multipart */
3737 if (p->c_encoding == CE_BINARY)
3738 ct->c_encoding = CE_BINARY;
3739 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3740 ct->c_encoding = CE_8BIT;
3747 * Decide what to check while scanning this content.
3749 switch (ct->c_type) {
3753 if (ct->c_subtype == TEXT_PLAIN) {
3758 checkebcdic = ebcdicsw;
3764 case CT_APPLICATION:
3766 checkebcdic = ebcdicsw;
3778 /* don't check anything for message/external */
3779 if (ct->c_subtype == MESSAGE_EXTERNAL)
3789 * Don't check anything for these types,
3790 * since we are forcing use of base64.
3801 * Scan the unencoded content
3803 if (check8bit || checklinelen || checklinespace || checkboundary) {
3804 if ((in = fopen (ce->ce_file, "r")) == NULL)
3805 adios (ce->ce_file, "unable to open for reading");
3806 len = strlen (prefix);
3808 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3810 * Check for 8bit data.
3813 for (cp = buffer; *cp; cp++) {
3814 if (!isascii (*cp)) {
3816 check8bit = 0; /* no need to keep checking */
3819 * Check if character is ebcdic-safe. We only check
3820 * this if also checking for 8bit data.
3822 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3824 checkebcdic = 0; /* no need to keep checking */
3830 * Check line length.
3832 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3834 checklinelen = 0; /* no need to keep checking */
3838 * Check if line ends with a space.
3840 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3842 checklinespace = 0; /* no need to keep checking */
3846 * Check if content contains a line that clashes
3847 * with our standard boundary for multipart messages.
3849 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3850 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3854 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3856 checkboundary = 0; /* no need to keep checking */
3864 * Decide which transfer encoding to use.
3866 switch (ct->c_type) {
3869 * If the text content didn't specify a character
3870 * set, we need to figure out which one was used.
3872 t = (struct text *) ct->c_ctparams;
3873 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3874 CI ci = &ct->c_ctinfo;
3877 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3881 t->tx_charset = CHARSET_UNKNOWN;
3882 *ap = concat ("charset=", write_charset_8bit(), NULL);
3884 t->tx_charset = CHARSET_USASCII;
3885 *ap = add ("charset=us-ascii", NULL);
3888 cp = strchr(*ap++, '=');
3894 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3895 ct->c_encoding = CE_QUOTED;
3897 ct->c_encoding = CE_7BIT;
3900 case CT_APPLICATION:
3901 /* For application type, use base64, except when postscript */
3902 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3903 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3904 ? CE_QUOTED : CE_BASE64;
3906 ct->c_encoding = CE_7BIT;
3910 ct->c_encoding = CE_7BIT;
3916 /* For audio, image, and video contents, just use base64 */
3917 ct->c_encoding = CE_BASE64;
3921 return (boundaryclash ? NOTOK : OK);
3926 * Scan the content structures, and build header
3927 * fields that will need to be output into the
3932 build_headers (CT ct)
3934 int cc, mailbody, len;
3936 char *np, *vp, buffer[BUFSIZ];
3937 CI ci = &ct->c_ctinfo;
3940 * If message is type multipart, then add the multipart
3941 * boundary to the list of attribute/value pairs.
3943 if (ct->c_type == CT_MULTIPART) {
3945 static int level = 0; /* store nesting level */
3949 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3950 cp = strchr(*ap++ = add (buffer, NULL), '=');
3957 * Skip the output of Content-Type, parameters, content
3958 * description and disposition, and Content-ID if the
3959 * content is of type "message" and the rfc934 compatibility
3960 * flag is set (which means we are inside multipart/digest
3961 * and the switch -rfc934mode was given).
3963 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3967 * output the content type and subtype
3969 np = add (TYPE_FIELD, NULL);
3970 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3972 /* keep track of length of line */
3973 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3974 + strlen (ci->ci_subtype) + 3;
3976 mailbody = ct->c_type == CT_MESSAGE
3977 && ct->c_subtype == MESSAGE_EXTERNAL
3978 && ((struct exbody *) ct->c_ctparams)->eb_body;
3981 * Append the attribute/value pairs to
3982 * the end of the Content-Type line.
3984 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3985 if (mailbody && !mh_strcasecmp (*ap, "body"))
3991 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3992 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3993 vp = add ("\n\t", vp);
3999 vp = add (buffer, vp);
4004 * Append any RFC-822 comment to the end of
4005 * the Content-Type line.
4007 if (ci->ci_comment) {
4008 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
4009 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
4010 vp = add ("\n\t", vp);
4016 vp = add (buffer, vp);
4019 vp = add ("\n", vp);
4020 add_header (ct, np, vp);
4023 * output the Content-ID, unless disabled by -nocontentid
4025 if (contentidsw && ct->c_id) {
4026 np = add (ID_FIELD, NULL);
4027 vp = concat (" ", ct->c_id, NULL);
4028 add_header (ct, np, vp);
4032 * output the Content-Description
4035 np = add (DESCR_FIELD, NULL);
4036 vp = concat (" ", ct->c_descr, NULL);
4037 add_header (ct, np, vp);
4041 * output the Content-Disposition
4044 np = add (DISPO_FIELD, NULL);
4045 vp = concat (" ", ct->c_dispo, NULL);
4046 add_header (ct, np, vp);
4051 * If this is the internal content structure for a
4052 * "message/external", then we are done with the
4053 * headers (since it has no body).
4059 * output the Content-MD5
4062 np = add (MD5_FIELD, NULL);
4063 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
4064 add_header (ct, np, vp);
4068 * output the Content-Transfer-Encoding
4070 switch (ct->c_encoding) {
4072 /* Nothing to output */
4074 np = add (ENCODING_FIELD, NULL);
4075 vp = concat (" ", "7bit", "\n", NULL);
4076 add_header (ct, np, vp);
4081 if (ct->c_type == CT_MESSAGE)
4082 adios (NULL, "internal error, invalid encoding");
4084 np = add (ENCODING_FIELD, NULL);
4085 vp = concat (" ", "8bit", "\n", NULL);
4086 add_header (ct, np, vp);
4090 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4091 adios (NULL, "internal error, invalid encoding");
4093 np = add (ENCODING_FIELD, NULL);
4094 vp = concat (" ", "quoted-printable", "\n", NULL);
4095 add_header (ct, np, vp);
4099 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4100 adios (NULL, "internal error, invalid encoding");
4102 np = add (ENCODING_FIELD, NULL);
4103 vp = concat (" ", "base64", "\n", NULL);
4104 add_header (ct, np, vp);
4108 if (ct->c_type == CT_MESSAGE)
4109 adios (NULL, "internal error, invalid encoding");
4111 np = add (ENCODING_FIELD, NULL);
4112 vp = concat (" ", "binary", "\n", NULL);
4113 add_header (ct, np, vp);
4117 adios (NULL, "unknown transfer encoding in content");
4122 * Additional content specific header processing
4124 switch (ct->c_type) {
4127 struct multipart *m;
4130 m = (struct multipart *) ct->c_ctparams;
4131 for (part = m->mp_parts; part; part = part->mp_next) {
4141 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4144 e = (struct exbody *) ct->c_ctparams;
4145 build_headers (e->eb_content);
4158 static char nib2b64[0x40+1] =
4159 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4162 calculate_digest (CT ct, int asciiP)
4165 char buffer[BUFSIZ], *vp, *op;
4167 unsigned char digest[16];
4168 unsigned char outbuf[25];
4171 CE ce = ct->c_cefile;
4174 if ((in = fopen (ce->ce_file, "r")) == NULL)
4175 adios (ce->ce_file, "unable to open for reading");
4177 /* Initialize md5 context */
4178 MD5Init (&mdContext);
4180 /* calculate md5 message digest */
4182 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4185 cp = buffer + strlen (buffer) - 1;
4186 if ((c = *cp) == '\n')
4189 MD5Update (&mdContext, (unsigned char *) buffer,
4190 (unsigned int) strlen (buffer));
4193 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4196 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4197 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4200 /* md5 finalization. Write digest and zero md5 context */
4201 MD5Final (digest, &mdContext);
4206 /* print debugging info */
4210 fprintf (stderr, "MD5 digest=");
4211 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4213 fprintf (stderr, "%02x", *dp & 0xff);
4214 fprintf (stderr, "\n");
4217 /* encode the digest using base64 */
4218 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4219 cc > 0; cc -= 3, op += 4) {
4223 bits = (*dp++ & 0xff) << 16;
4225 bits |= (*dp++ & 0xff) << 8;
4227 bits |= *dp++ & 0xff;
4230 for (bp = op + 4; bp > op; bits >>= 6)
4231 *--bp = nib2b64[bits & 0x3f];
4239 /* null terminate string */
4242 /* now make copy and return string */
4243 vp = concat (" ", outbuf, "\n", NULL);
4249 readDigest (CT ct, char *cp)
4254 unsigned char *dp, value, *ep;
4255 unsigned char *b, *b1, *b2, *b3;
4257 b = (unsigned char *) &bits,
4258 b1 = &b[endian > 0 ? 1 : 2],
4259 b2 = &b[endian > 0 ? 2 : 1],
4260 b3 = &b[endian > 0 ? 3 : 0];
4265 for (ep = (dp = ct->c_digest)
4266 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4271 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4273 fprintf (stderr, "invalid BASE64 encoding\n");
4277 bits |= value << bitno;
4279 if ((bitno -= 6) < 0) {
4280 if (dp + (3 - skip) > ep)
4281 goto invalid_digest;
4296 goto self_delimiting;
4301 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4311 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4319 fprintf (stderr, "MD5 digest=");
4320 for (dp = ct->c_digest; dp < ep; dp++)
4321 fprintf (stderr, "%02x", *dp & 0xff);
4322 fprintf (stderr, "\n");
4329 /* Make sure that buf contains at least one appearance of name,
4330 followed by =. If not, insert both name and value, just after
4331 first semicolon, if any. Note that name should not contain a
4332 trailing =. And quotes will be added around the value. Typical
4333 usage: make sure that a Content-Disposition header contains
4334 filename="foo". If it doesn't and value does, use value from
4337 incl_name_value (unsigned char *buf, char *name, char *value) {
4340 /* Assume that name is non-null. */
4342 char *name_plus_equal = concat (name, "=", NULL);
4344 if (! strstr (buf, name_plus_equal)) {
4347 char *prefix, *suffix;
4349 /* Trim trailing space, esp. newline. */
4350 for (cp = &buf[strlen (buf) - 1];
4351 cp >= buf && isspace (*cp);
4356 insertion = concat ("; ", name, "=", "\"", value, "\"", NULL);
4358 /* Insert at first semicolon, if any. If none, append to
4360 prefix = add (buf, NULL);
4361 if ((cp = strchr (prefix, ';'))) {
4362 suffix = concat (cp, NULL);
4364 newbuf = concat (prefix, insertion, suffix, "\n", NULL);
4367 /* Append to end. */
4368 newbuf = concat (buf, insertion, "\n", NULL);
4376 free (name_plus_equal);
4383 /* Extract just name_suffix="foo", if any, from value. If there isn't
4384 one, return the entire value. Note that, for example, a name_suffix
4385 of name will match filename="foo", and return foo. */
4387 extract_name_value (char *name_suffix, char *value) {
4388 char *extracted_name_value = value;
4389 char *name_suffix_plus_quote = concat (name_suffix, "=\"", NULL);
4390 char *name_suffix_equals = strstr (value, name_suffix_plus_quote);
4393 free (name_suffix_plus_quote);
4394 if (name_suffix_equals) {
4395 char *name_suffix_begin;
4397 /* Find first \". */
4398 for (cp = name_suffix_equals; *cp != '"'; ++cp) /* empty */;
4399 name_suffix_begin = ++cp;
4400 /* Find second \". */
4401 for (; *cp != '"'; ++cp) /* empty */;
4403 extracted_name_value = mh_xmalloc (cp - name_suffix_begin + 1);
4404 memcpy (extracted_name_value,
4406 cp - name_suffix_begin);
4407 extracted_name_value[cp - name_suffix_begin] = '\0';
4410 return extracted_name_value;