3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
11 * This code was originally part of mhn.c. I split it into
12 * a separate program (mhbuild.c) and then later split it
13 * again (mhbuildsbr.c). But the code still has some of
14 * the mhn.c code in it. This program needs additional
15 * streamlining and removal of unneeded code.
20 #include <h/signals.h>
27 #include <h/mhparse.h>
30 #ifdef TIME_WITH_SYS_TIME
31 # include <sys/time.h>
34 # ifdef TM_IN_SYS_TIME
35 # include <sys/time.h>
41 #ifdef HAVE_SYS_WAIT_H
42 # include <sys/wait.h>
52 extern int contentidsw;
54 extern int endian; /* mhmisc.c */
57 extern int rcachesw; /* mhcachesbr.c */
58 extern int wcachesw; /* mhcachesbr.c */
61 * Directory to place tmp files. This must
62 * be set before these routines are called.
68 static char prefix[] = "----- =_aaaaaaaaaa";
72 int make_intermediates (char *);
73 void content_error (char *, CT, char *, ...);
76 int find_cache (CT, int, int *, char *, char *, int);
79 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
82 void free_content (CT);
83 void free_ctinfo (CT);
84 void free_encoding (CT, int);
89 CT build_mime (char *);
94 static int init_decoded_content (CT);
95 static char *fgetstr (char *, int, FILE *);
96 static int user_content (FILE *, char *, char *, CT *);
97 static void set_id (CT, int);
98 static int compose_content (CT);
99 static int scan_content (CT);
100 static int build_headers (CT);
101 static char *calculate_digest (CT, int);
105 * Main routine for translating composition file
106 * into valid MIME message. It translates the draft
107 * into a content structure (actually a tree of content
108 * structures). This message then can be manipulated
109 * in various ways, including being output via
114 build_mime (char *infile)
117 char buf[BUFSIZ], name[NAMESZ];
124 umask (~m_gmprot ());
126 /* open the composition draft */
127 if ((in = fopen (infile, "r")) == NULL)
128 adios (infile, "unable to open for reading");
131 * Allocate space for primary (outside) content
133 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
134 adios (NULL, "out of memory");
137 * Allocate structure for handling decoded content
138 * for this part. We don't really need this, but
139 * allocate it to remain consistent.
141 init_decoded_content (ct);
144 * Parse some of the header fields in the composition
145 * draft into the linked list of header fields for
146 * the new MIME message.
148 for (compnum = 1, state = FLD;;) {
149 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
155 /* abort if draft has Mime-Version header field */
156 if (!mh_strcasecmp (name, VRSN_FIELD))
157 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
159 /* abort if draft has Content-Transfer-Encoding header field */
160 if (!mh_strcasecmp (name, ENCODING_FIELD))
161 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
163 /* ignore any Content-Type fields in the header */
164 if (!mh_strcasecmp (name, TYPE_FIELD)) {
165 while (state == FLDPLUS)
166 state = m_getfld (state, name, buf, sizeof(buf), in);
170 /* get copies of the buffers */
171 np = add (name, NULL);
172 vp = add (buf, NULL);
174 /* if necessary, get rest of field */
175 while (state == FLDPLUS) {
176 state = m_getfld (state, name, buf, sizeof(buf), in);
177 vp = add (buf, vp); /* add to previous value */
180 /* Now add the header data to the list */
181 add_header (ct, np, vp);
184 /* if this wasn't the last header field, then continue */
190 adios (NULL, "draft has empty body -- no directives!");
195 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
200 adios (NULL, "message format error in component #%d", compnum);
203 adios (NULL, "getfld() returned %d", state);
209 * Now add the MIME-Version header field
210 * to the list of header fields.
212 np = add (VRSN_FIELD, NULL);
213 vp = concat (" ", VRSN_VALUE, "\n", NULL);
214 add_header (ct, np, vp);
217 * We initally assume we will find multiple contents in the
218 * draft. So create a multipart/mixed content to hold everything.
219 * We can remove this later, if it is not needed.
221 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
223 ct->c_type = CT_MULTIPART;
224 ct->c_subtype = MULTI_MIXED;
225 ct->c_file = add (infile, NULL);
227 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
228 adios (NULL, "out of memory");
229 ct->c_ctparams = (void *) m;
233 * read and parse the composition file
234 * and the directives it contains.
236 while (fgetstr (buf, sizeof(buf) - 1, in)) {
240 if (user_content (in, infile, buf, &p) == DONE) {
241 admonish (NULL, "ignoring spurious #end");
247 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
248 adios (NULL, "out of memory");
255 * close the composition draft since
256 * it's not needed any longer.
260 /* check if any contents were found */
262 adios (NULL, "no content directives found");
265 * If only one content was found, then remove and
266 * free the outer multipart content.
268 if (!m->mp_parts->mp_next) {
271 p = m->mp_parts->mp_part;
272 m->mp_parts->mp_part = NULL;
274 /* move header fields */
275 p->c_first_hf = ct->c_first_hf;
276 p->c_last_hf = ct->c_last_hf;
277 ct->c_first_hf = NULL;
278 ct->c_last_hf = NULL;
287 * Fill out, or expand directives. Parse and execute
288 * commands specified by profile composition strings.
290 compose_content (ct);
292 if ((cp = strchr(prefix, 'a')) == NULL)
293 adios (NULL, "internal error(4)");
296 * Scan the contents. Choose a transfer encoding, and
297 * check if prefix for multipart boundary clashes with
298 * any of the contents.
300 while (scan_content (ct) == NOTOK) {
305 adios (NULL, "giving up trying to find a unique delimiter string");
311 /* Build the rest of the header field structures */
319 * Set up structures for placing unencoded
320 * content when building parts.
324 init_decoded_content (CT ct)
328 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
329 adios (NULL, "out of memory");
332 ct->c_ceopenfnx = open7Bit; /* since unencoded */
333 ct->c_ceclosefnx = close_encoding;
334 ct->c_cesizefnx = NULL; /* since unencoded */
341 fgetstr (char *s, int n, FILE *stream)
345 for (ep = (cp = s) + n; cp < ep; ) {
348 if (!fgets (cp, n, stream))
349 return (cp != s ? s : NULL);
350 if (cp == s && *cp != '#')
353 cp += (i = strlen (cp)) - 1;
354 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
365 * Parse the composition draft for text and directives.
366 * Do initial setup of Content structure.
370 user_content (FILE *in, char *file, char *buf, CT *ctp)
379 struct str2init *s2i;
384 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
389 /* allocate basic Content structure */
390 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
391 adios (NULL, "out of memory");
394 /* allocate basic structure for handling decoded content */
395 init_decoded_content (ct);
402 * Handle inline text. Check if line
403 * is one of the following forms:
405 * 1) doesn't begin with '#' (implicit directive)
406 * 2) begins with "##" (implicit directive)
407 * 3) begins with "#<"
409 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
413 char content[BUFSIZ];
417 cp = m_mktemp2(NULL, invo_name, NULL, &out);
418 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
420 /* use a temp file to collect the plain text lines */
421 ce->ce_file = add (cp, NULL);
424 if (buf[0] == '#' && buf[1] == '<') {
425 strncpy (content, buf + 2, sizeof(content));
432 /* the directive is implicit */
433 strncpy (content, "text/plain", sizeof(content));
435 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
439 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
440 && buffer[i = strlen (DESCR_FIELD)] == ':') {
444 ct->c_descr = add (buffer + i + 1, ct->c_descr);
445 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
446 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
454 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
462 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
463 && buffer[i = strlen (DISPO_FIELD)] == ':') {
467 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
468 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
469 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
477 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
485 if (headers != 1 || buffer[0] != '\n')
491 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
493 if (buffer[0] == '#') {
496 if (buffer[1] != '#')
498 for (cp = (bp = buffer) + 1; *cp; cp++)
505 ct->c_end = ftell (out);
508 /* parse content type */
509 if (get_ctinfo (content, ct, inlineD) == NOTOK)
512 for (s2i = str2cts; s2i->si_key; s2i++)
513 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
515 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
519 * check type specified (possibly implicitly)
521 switch (ct->c_type = s2i->si_val) {
523 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
524 ct->c_encoding = CE_7BIT;
529 adios (NULL, "it doesn't make sense to define an in-line %s content",
530 ct->c_type == CT_MESSAGE ? "message" : "multipart");
535 if ((ct->c_ctinitfnx = s2i->si_init))
536 (*ct->c_ctinitfnx) (ct);
541 fseek (in, pos, SEEK_SET);
546 * If we've reached this point, the next line
547 * must be some type of explicit directive.
550 /* check if directive is external-type */
551 extrnal = (buf[1] == '@');
553 /* parse directive */
554 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
557 /* check directive against the list of MIME types */
558 for (s2i = str2cts; s2i->si_key; s2i++)
559 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
563 * Check if the directive specified a valid type.
564 * This will happen if it was one of the following forms:
571 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
573 switch (ct->c_type = s2i->si_val) {
575 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
576 ci->ci_type, ci->ci_subtype);
580 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
581 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
582 ci->ci_type, ci->ci_subtype);
583 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
584 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
585 ci->ci_type, ci->ci_subtype);
588 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
589 ci->ci_type, ci->ci_subtype);
593 if ((ct->c_ctinitfnx = s2i->si_init))
594 (*ct->c_ctinitfnx) (ct);
599 * #@type/subtype (external types directive)
606 adios (NULL, "need external information for \"#@%s/%s\"",
607 ci->ci_type, ci->ci_subtype);
610 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
615 * Since we are using the current Content structure to
616 * hold information about the type of the external
617 * reference, we need to create another Content structure
618 * for the message/external-body to wrap it in.
620 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
621 adios (NULL, "out of memory");
624 if (get_ctinfo (buffer, ct, 0) == NOTOK)
626 ct->c_type = CT_MESSAGE;
627 ct->c_subtype = MESSAGE_EXTERNAL;
629 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
630 adios (NULL, "out of memory");
631 ct->c_ctparams = (void *) e;
637 if (params_external (ct, 1) == NOTOK)
643 /* Handle [file] argument */
645 /* check if specifies command to execute */
646 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
647 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
650 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
655 /* record filename of decoded contents */
656 ce->ce_file = ci->ci_magic;
657 if (access (ce->ce_file, R_OK) == NOTOK)
658 adios ("reading", "unable to access %s for", ce->ce_file);
659 if (listsw && stat (ce->ce_file, &st) != NOTOK)
660 ct->c_end = (long) st.st_size;
667 * No [file] argument, so check profile for
668 * method to compose content.
670 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
671 invo_name, ci->ci_type, ci->ci_subtype);
672 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
673 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
674 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
675 content_error (NULL, ct, "don't know how to compose content");
679 ci->ci_magic = add (cp, NULL);
684 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
688 * #forw [+folder] [msgs]
690 if (!mh_strcasecmp (ci->ci_type, "forw")) {
692 char *folder, *arguments[MAXARGS];
696 ap = brkstring (ci->ci_magic, " ", "\n");
697 copyip (ap, arguments, MAXARGS);
699 arguments[0] = "cur";
704 /* search the arguments for a folder name */
705 for (ap = arguments; *ap; ap++) {
707 if (*cp == '+' || *cp == '@') {
709 adios (NULL, "only one folder per #forw directive");
711 folder = pluspath (cp);
715 /* else, use the current folder */
717 folder = add (getfolder (1), NULL);
719 if (!(mp = folder_read (folder)))
720 adios (NULL, "unable to read folder %s", folder);
721 for (ap = arguments; *ap; ap++) {
723 if (*cp != '+' && *cp != '@')
724 if (!m_convert (mp, cp))
731 * If there is more than one message to include, make this
732 * a content of type "multipart/digest" and insert each message
733 * as a subpart. If there is only one message, then make this
734 * a content of type "message/rfc822".
736 if (mp->numsel > 1) {
737 /* we are forwarding multiple messages */
738 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
740 ct->c_type = CT_MULTIPART;
741 ct->c_subtype = MULTI_DIGEST;
743 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
744 adios (NULL, "out of memory");
745 ct->c_ctparams = (void *) m;
748 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
749 if (is_selected(mp, msgnum)) {
754 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
755 adios (NULL, "out of memory");
756 init_decoded_content (p);
758 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
760 p->c_type = CT_MESSAGE;
761 p->c_subtype = MESSAGE_RFC822;
763 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
764 pe->ce_file = add (buffer, NULL);
765 if (listsw && stat (pe->ce_file, &st) != NOTOK)
766 p->c_end = (long) st.st_size;
768 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
769 adios (NULL, "out of memory");
776 /* we are forwarding one message */
777 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
779 ct->c_type = CT_MESSAGE;
780 ct->c_subtype = MESSAGE_RFC822;
783 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
784 ce->ce_file = add (buffer, NULL);
785 if (listsw && stat (ce->ce_file, &st) != NOTOK)
786 ct->c_end = (long) st.st_size;
789 folder_free (mp); /* free folder/message structure */
796 if (!mh_strcasecmp (ci->ci_type, "end")) {
803 * #begin [ alternative | parallel ]
805 if (!mh_strcasecmp (ci->ci_type, "begin")) {
808 cp = SubMultiPart[vrsn - 1].kv_key;
809 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
810 vrsn = MULTI_ALTERNATE;
811 cp = SubMultiPart[vrsn - 1].kv_key;
812 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
813 vrsn = MULTI_PARALLEL;
814 cp = SubMultiPart[vrsn - 1].kv_key;
815 } else if (uprf (ci->ci_magic, "digest")) {
818 vrsn = MULTI_UNKNOWN;
823 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
824 if (get_ctinfo (buffer, ct, 0) == NOTOK)
826 ct->c_type = CT_MULTIPART;
827 ct->c_subtype = vrsn;
829 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
830 adios (NULL, "out of memory");
831 ct->c_ctparams = (void *) m;
834 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
838 if (user_content (in, file, buffer, &p) == DONE) {
840 adios (NULL, "empty \"#begin ... #end\" sequence");
846 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
847 adios (NULL, "out of memory");
852 admonish (NULL, "premature end-of-file, missing #end");
859 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
860 return NOTOK; /* NOT REACHED */
865 set_id (CT ct, int top)
869 static time_t clock = 0;
874 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
875 (int) getpid(), (long) clock, LocalName());
877 msgfmt = getcpy(msgid);
879 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
880 ct->c_id = getcpy (msgid);
884 static char ebcdicsafe[0x100] = {
885 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
886 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
888 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
889 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
890 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
891 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
892 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
893 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
894 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
895 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
896 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
897 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
898 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
899 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
900 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
910 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
911 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
912 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
913 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
914 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
921 * Fill out, or expand the various contents in the composition
922 * draft. Read-in any necessary files. Parse and execute any
923 * commands specified by profile composition strings.
927 compose_content (CT ct)
929 CE ce = ct->c_cefile;
931 switch (ct->c_type) {
936 char partnam[BUFSIZ];
937 struct multipart *m = (struct multipart *) ct->c_ctparams;
941 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
942 pp = partnam + strlen (partnam);
947 /* first, we call compose_content on all the subparts */
948 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
949 CT p = part->mp_part;
951 sprintf (pp, "%d", partnum);
952 p->c_partno = add (partnam, NULL);
953 if (compose_content (p) == NOTOK)
958 * If the -rfc934mode switch is given, then check all
959 * the subparts of a multipart/digest. If they are all
960 * message/rfc822, then mark this content and all
961 * subparts with the rfc934 compatibility mode flag.
963 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
966 for (part = m->mp_parts; part; part = part->mp_next) {
967 CT p = part->mp_part;
969 if (p->c_subtype != MESSAGE_RFC822) {
974 ct->c_rfc934 = is934;
975 for (part = m->mp_parts; part; part = part->mp_next) {
976 CT p = part->mp_part;
978 if ((p->c_rfc934 = is934))
984 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
988 for (part = m->mp_parts; part; part = part->mp_next)
989 ct->c_end += part->mp_part->c_end + partnum;
995 /* Nothing to do for type message */
999 * Discrete types (text/application/audio/image/video)
1004 int i, xstdout, len, buflen;
1005 char *bp, **ap, *cp;
1006 char *vec[4], buffer[BUFSIZ];
1008 CI ci = &ct->c_ctinfo;
1011 if (!(cp = ci->ci_magic))
1012 adios (NULL, "internal error(5)");
1014 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1015 if (tfile == NULL) {
1016 adios("mhbuildsbr", "unable to create temporary file");
1018 ce->ce_file = add (tfile, NULL);
1023 /* Get buffer ready to go */
1026 buflen = sizeof(buffer);
1029 * Parse composition string into buffer
1031 for ( ; *cp; cp++) {
1036 /* insert parameters from directive */
1040 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1041 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1051 /* %f, and stdout is not-redirected */
1057 * insert temporary filename where
1058 * content should be written
1060 snprintf (bp, buflen, "%s", ce->ce_file);
1064 /* insert content subtype */
1065 strncpy (bp, ci->ci_subtype, buflen);
1069 /* insert character % */
1090 printf ("composing content %s/%s from command\n\t%s\n",
1091 ci->ci_type, ci->ci_subtype, buffer);
1093 fflush (stdout); /* not sure if need for -noverbose */
1100 if ((out = fopen (ce->ce_file, "w")) == NULL)
1101 adios (ce->ce_file, "unable to open for writing");
1103 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1107 adios ("fork", "unable to fork");
1112 dup2 (fileno (out), 1);
1113 close (fileno (out));
1114 execvp ("/bin/sh", vec);
1115 fprintf (stderr, "unable to exec ");
1122 if (pidXwait(child_id, NULL))
1128 /* Check size of file */
1129 if (listsw && ct->c_end == 0L) {
1132 if (stat (ce->ce_file, &st) != NOTOK)
1133 ct->c_end = (long) st.st_size;
1145 * 1) choose a transfer encoding.
1146 * 2) check for clashes with multipart boundary string.
1147 * 3) for text content, figure out which character set is being used.
1149 * If there is a clash with one of the contents and the multipart boundary,
1150 * this function will exit with NOTOK. This will cause the scanning process
1151 * to be repeated with a different multipart boundary. It is possible
1152 * (although highly unlikely) that this scan will be repeated multiple times.
1156 scan_content (CT ct)
1159 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1160 int checklinelen = 0, linelen = 0; /* check for long lines */
1161 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1162 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1163 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1164 unsigned char *cp = NULL, buffer[BUFSIZ];
1165 struct text *t = NULL;
1167 CE ce = ct->c_cefile;
1170 * handle multipart by scanning all subparts
1171 * and then checking their encoding.
1173 if (ct->c_type == CT_MULTIPART) {
1174 struct multipart *m = (struct multipart *) ct->c_ctparams;
1177 /* initially mark the domain of enclosing multipart as 7bit */
1178 ct->c_encoding = CE_7BIT;
1180 for (part = m->mp_parts; part; part = part->mp_next) {
1181 CT p = part->mp_part;
1183 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1186 /* if necessary, enlarge encoding for enclosing multipart */
1187 if (p->c_encoding == CE_BINARY)
1188 ct->c_encoding = CE_BINARY;
1189 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1190 ct->c_encoding = CE_8BIT;
1197 * Decide what to check while scanning this content.
1199 switch (ct->c_type) {
1203 if (ct->c_subtype == TEXT_PLAIN) {
1208 checkebcdic = ebcdicsw;
1214 case CT_APPLICATION:
1216 checkebcdic = ebcdicsw;
1228 /* don't check anything for message/external */
1229 if (ct->c_subtype == MESSAGE_EXTERNAL)
1239 * Don't check anything for these types,
1240 * since we are forcing use of base64.
1251 * Scan the unencoded content
1253 if (check8bit || checklinelen || checklinespace || checkboundary) {
1254 if ((in = fopen (ce->ce_file, "r")) == NULL)
1255 adios (ce->ce_file, "unable to open for reading");
1256 len = strlen (prefix);
1258 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1260 * Check for 8bit data.
1263 for (cp = buffer; *cp; cp++) {
1264 if (!isascii (*cp)) {
1266 check8bit = 0; /* no need to keep checking */
1269 * Check if character is ebcdic-safe. We only check
1270 * this if also checking for 8bit data.
1272 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1274 checkebcdic = 0; /* no need to keep checking */
1280 * Check line length.
1282 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1284 checklinelen = 0; /* no need to keep checking */
1288 * Check if line ends with a space.
1290 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1292 checklinespace = 0; /* no need to keep checking */
1296 * Check if content contains a line that clashes
1297 * with our standard boundary for multipart messages.
1299 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1300 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1304 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1306 checkboundary = 0; /* no need to keep checking */
1314 * Decide which transfer encoding to use.
1316 switch (ct->c_type) {
1319 * If the text content didn't specify a character
1320 * set, we need to figure out which one was used.
1322 t = (struct text *) ct->c_ctparams;
1323 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1324 CI ci = &ct->c_ctinfo;
1327 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1331 t->tx_charset = CHARSET_UNKNOWN;
1332 *ap = concat ("charset=", write_charset_8bit(), NULL);
1334 t->tx_charset = CHARSET_USASCII;
1335 *ap = add ("charset=us-ascii", NULL);
1338 cp = strchr(*ap++, '=');
1344 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1345 ct->c_encoding = CE_QUOTED;
1347 ct->c_encoding = CE_7BIT;
1350 case CT_APPLICATION:
1351 /* For application type, use base64, except when postscript */
1352 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1353 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1354 ? CE_QUOTED : CE_BASE64;
1356 ct->c_encoding = CE_7BIT;
1360 ct->c_encoding = CE_7BIT;
1366 /* For audio, image, and video contents, just use base64 */
1367 ct->c_encoding = CE_BASE64;
1371 return (boundaryclash ? NOTOK : OK);
1376 * Scan the content structures, and build header
1377 * fields that will need to be output into the
1382 build_headers (CT ct)
1384 int cc, mailbody, len;
1386 char *np, *vp, buffer[BUFSIZ];
1387 CI ci = &ct->c_ctinfo;
1390 * If message is type multipart, then add the multipart
1391 * boundary to the list of attribute/value pairs.
1393 if (ct->c_type == CT_MULTIPART) {
1395 static int level = 0; /* store nesting level */
1399 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1400 cp = strchr(*ap++ = add (buffer, NULL), '=');
1407 * Skip the output of Content-Type, parameters, content
1408 * description and disposition, and Content-ID if the
1409 * content is of type "message" and the rfc934 compatibility
1410 * flag is set (which means we are inside multipart/digest
1411 * and the switch -rfc934mode was given).
1413 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1417 * output the content type and subtype
1419 np = add (TYPE_FIELD, NULL);
1420 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1422 /* keep track of length of line */
1423 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1424 + strlen (ci->ci_subtype) + 3;
1426 mailbody = ct->c_type == CT_MESSAGE
1427 && ct->c_subtype == MESSAGE_EXTERNAL
1428 && ((struct exbody *) ct->c_ctparams)->eb_body;
1431 * Append the attribute/value pairs to
1432 * the end of the Content-Type line.
1434 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1435 if (mailbody && !mh_strcasecmp (*ap, "body"))
1441 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1442 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1443 vp = add ("\n\t", vp);
1449 vp = add (buffer, vp);
1454 * Append any RFC-822 comment to the end of
1455 * the Content-Type line.
1457 if (ci->ci_comment) {
1458 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1459 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1460 vp = add ("\n\t", vp);
1466 vp = add (buffer, vp);
1469 vp = add ("\n", vp);
1470 add_header (ct, np, vp);
1473 * output the Content-ID, unless disabled by -nocontentid
1475 if (contentidsw && ct->c_id) {
1476 np = add (ID_FIELD, NULL);
1477 vp = concat (" ", ct->c_id, NULL);
1478 add_header (ct, np, vp);
1482 * output the Content-Description
1485 np = add (DESCR_FIELD, NULL);
1486 vp = concat (" ", ct->c_descr, NULL);
1487 add_header (ct, np, vp);
1491 * output the Content-Disposition
1494 np = add (DISPO_FIELD, NULL);
1495 vp = concat (" ", ct->c_dispo, NULL);
1496 add_header (ct, np, vp);
1501 * If this is the internal content structure for a
1502 * "message/external", then we are done with the
1503 * headers (since it has no body).
1509 * output the Content-MD5
1512 np = add (MD5_FIELD, NULL);
1513 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1514 add_header (ct, np, vp);
1518 * output the Content-Transfer-Encoding
1520 switch (ct->c_encoding) {
1522 /* Nothing to output */
1524 np = add (ENCODING_FIELD, NULL);
1525 vp = concat (" ", "7bit", "\n", NULL);
1526 add_header (ct, np, vp);
1531 if (ct->c_type == CT_MESSAGE)
1532 adios (NULL, "internal error, invalid encoding");
1534 np = add (ENCODING_FIELD, NULL);
1535 vp = concat (" ", "8bit", "\n", NULL);
1536 add_header (ct, np, vp);
1540 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1541 adios (NULL, "internal error, invalid encoding");
1543 np = add (ENCODING_FIELD, NULL);
1544 vp = concat (" ", "quoted-printable", "\n", NULL);
1545 add_header (ct, np, vp);
1549 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1550 adios (NULL, "internal error, invalid encoding");
1552 np = add (ENCODING_FIELD, NULL);
1553 vp = concat (" ", "base64", "\n", NULL);
1554 add_header (ct, np, vp);
1558 if (ct->c_type == CT_MESSAGE)
1559 adios (NULL, "internal error, invalid encoding");
1561 np = add (ENCODING_FIELD, NULL);
1562 vp = concat (" ", "binary", "\n", NULL);
1563 add_header (ct, np, vp);
1567 adios (NULL, "unknown transfer encoding in content");
1572 * Additional content specific header processing
1574 switch (ct->c_type) {
1577 struct multipart *m;
1580 m = (struct multipart *) ct->c_ctparams;
1581 for (part = m->mp_parts; part; part = part->mp_next) {
1591 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1594 e = (struct exbody *) ct->c_ctparams;
1595 build_headers (e->eb_content);
1608 static char nib2b64[0x40+1] =
1609 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1612 calculate_digest (CT ct, int asciiP)
1615 char buffer[BUFSIZ], *vp, *op;
1617 unsigned char digest[16];
1618 unsigned char outbuf[25];
1621 CE ce = ct->c_cefile;
1624 if ((in = fopen (ce->ce_file, "r")) == NULL)
1625 adios (ce->ce_file, "unable to open for reading");
1627 /* Initialize md5 context */
1628 MD5Init (&mdContext);
1630 /* calculate md5 message digest */
1632 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1635 cp = buffer + strlen (buffer) - 1;
1636 if ((c = *cp) == '\n')
1639 MD5Update (&mdContext, (unsigned char *) buffer,
1640 (unsigned int) strlen (buffer));
1643 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1646 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1647 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1650 /* md5 finalization. Write digest and zero md5 context */
1651 MD5Final (digest, &mdContext);
1656 /* print debugging info */
1660 fprintf (stderr, "MD5 digest=");
1661 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1663 fprintf (stderr, "%02x", *dp & 0xff);
1664 fprintf (stderr, "\n");
1667 /* encode the digest using base64 */
1668 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1669 cc > 0; cc -= 3, op += 4) {
1673 bits = (*dp++ & 0xff) << 16;
1675 bits |= (*dp++ & 0xff) << 8;
1677 bits |= *dp++ & 0xff;
1680 for (bp = op + 4; bp > op; bits >>= 6)
1681 *--bp = nib2b64[bits & 0x3f];
1689 /* null terminate string */
1692 /* now make copy and return string */
1693 vp = concat (" ", outbuf, "\n", NULL);