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 */
63 * Directory to place tmp files. This must
64 * be set before these routines are called.
70 static char prefix[] = "----- =_aaaaaaaaaa";
74 int make_intermediates (char *);
75 void content_error (char *, CT, char *, ...);
78 int find_cache (CT, int, int *, char *, char *, int);
81 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
84 void free_content (CT);
85 void free_ctinfo (CT);
86 void free_encoding (CT, int);
91 CT build_mime (char *);
96 static int init_decoded_content (CT);
97 static char *fgetstr (char *, int, FILE *);
98 static int user_content (FILE *, char *, char *, CT *);
99 static void set_id (CT, int);
100 static int compose_content (CT);
101 static int scan_content (CT);
102 static int build_headers (CT);
103 static char *calculate_digest (CT, int);
107 * Main routine for translating composition file
108 * into valid MIME message. It translates the draft
109 * into a content structure (actually a tree of content
110 * structures). This message then can be manipulated
111 * in various ways, including being output via
116 build_mime (char *infile)
119 char buf[BUFSIZ], name[NAMESZ];
126 umask (~m_gmprot ());
128 /* open the composition draft */
129 if ((in = fopen (infile, "r")) == NULL)
130 adios (infile, "unable to open for reading");
133 * Allocate space for primary (outside) content
135 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
136 adios (NULL, "out of memory");
139 * Allocate structure for handling decoded content
140 * for this part. We don't really need this, but
141 * allocate it to remain consistent.
143 init_decoded_content (ct);
146 * Parse some of the header fields in the composition
147 * draft into the linked list of header fields for
148 * the new MIME message.
150 for (compnum = 1, state = FLD;;) {
151 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
157 /* abort if draft has Mime-Version header field */
158 if (!mh_strcasecmp (name, VRSN_FIELD))
159 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
161 /* abort if draft has Content-Transfer-Encoding header field */
162 if (!mh_strcasecmp (name, ENCODING_FIELD))
163 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
165 /* ignore any Content-Type fields in the header */
166 if (!mh_strcasecmp (name, TYPE_FIELD)) {
167 while (state == FLDPLUS)
168 state = m_getfld (state, name, buf, sizeof(buf), in);
172 /* get copies of the buffers */
173 np = add (name, NULL);
174 vp = add (buf, NULL);
176 /* if necessary, get rest of field */
177 while (state == FLDPLUS) {
178 state = m_getfld (state, name, buf, sizeof(buf), in);
179 vp = add (buf, vp); /* add to previous value */
182 /* Now add the header data to the list */
183 add_header (ct, np, vp);
186 /* if this wasn't the last header field, then continue */
192 adios (NULL, "draft has empty body -- no directives!");
197 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
202 adios (NULL, "message format error in component #%d", compnum);
205 adios (NULL, "getfld() returned %d", state);
211 * Now add the MIME-Version header field
212 * to the list of header fields.
214 np = add (VRSN_FIELD, NULL);
215 vp = concat (" ", VRSN_VALUE, "\n", NULL);
216 add_header (ct, np, vp);
219 * We initally assume we will find multiple contents in the
220 * draft. So create a multipart/mixed content to hold everything.
221 * We can remove this later, if it is not needed.
223 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
225 ct->c_type = CT_MULTIPART;
226 ct->c_subtype = MULTI_MIXED;
227 ct->c_file = add (infile, NULL);
229 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
230 adios (NULL, "out of memory");
231 ct->c_ctparams = (void *) m;
235 * read and parse the composition file
236 * and the directives it contains.
238 while (fgetstr (buf, sizeof(buf) - 1, in)) {
242 if (user_content (in, infile, buf, &p) == DONE) {
243 admonish (NULL, "ignoring spurious #end");
249 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
250 adios (NULL, "out of memory");
257 * close the composition draft since
258 * it's not needed any longer.
262 /* check if any contents were found */
264 adios (NULL, "no content directives found");
267 * If only one content was found, then remove and
268 * free the outer multipart content.
270 if (!m->mp_parts->mp_next) {
273 p = m->mp_parts->mp_part;
274 m->mp_parts->mp_part = NULL;
276 /* move header fields */
277 p->c_first_hf = ct->c_first_hf;
278 p->c_last_hf = ct->c_last_hf;
279 ct->c_first_hf = NULL;
280 ct->c_last_hf = NULL;
289 * Fill out, or expand directives. Parse and execute
290 * commands specified by profile composition strings.
292 compose_content (ct);
294 if ((cp = strchr(prefix, 'a')) == NULL)
295 adios (NULL, "internal error(4)");
298 * Scan the contents. Choose a transfer encoding, and
299 * check if prefix for multipart boundary clashes with
300 * any of the contents.
302 while (scan_content (ct) == NOTOK) {
307 adios (NULL, "giving up trying to find a unique delimiter string");
313 /* Build the rest of the header field structures */
321 * Set up structures for placing unencoded
322 * content when building parts.
326 init_decoded_content (CT ct)
330 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
331 adios (NULL, "out of memory");
334 ct->c_ceopenfnx = open7Bit; /* since unencoded */
335 ct->c_ceclosefnx = close_encoding;
336 ct->c_cesizefnx = NULL; /* since unencoded */
343 fgetstr (char *s, int n, FILE *stream)
347 for (ep = (cp = s) + n; cp < ep; ) {
350 if (!fgets (cp, n, stream))
351 return (cp != s ? s : NULL);
352 if (cp == s && *cp != '#')
355 cp += (i = strlen (cp)) - 1;
356 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
367 * Parse the composition draft for text and directives.
368 * Do initial setup of Content structure.
372 user_content (FILE *in, char *file, char *buf, CT *ctp)
381 struct str2init *s2i;
386 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
391 /* allocate basic Content structure */
392 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
393 adios (NULL, "out of memory");
396 /* allocate basic structure for handling decoded content */
397 init_decoded_content (ct);
404 * Handle inline text. Check if line
405 * is one of the following forms:
407 * 1) doesn't begin with '#' (implicit directive)
408 * 2) begins with "##" (implicit directive)
409 * 3) begins with "#<"
411 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
415 char content[BUFSIZ];
419 cp = m_mktemp2(NULL, invo_name, NULL, &out);
420 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
422 /* use a temp file to collect the plain text lines */
423 ce->ce_file = add (cp, NULL);
426 if (buf[0] == '#' && buf[1] == '<') {
427 strncpy (content, buf + 2, sizeof(content));
434 /* the directive is implicit */
435 strncpy (content, "text/plain", sizeof(content));
437 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
441 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
442 && buffer[i = strlen (DESCR_FIELD)] == ':') {
446 ct->c_descr = add (buffer + i + 1, ct->c_descr);
447 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
448 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
456 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
464 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
465 && buffer[i = strlen (DISPO_FIELD)] == ':') {
469 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
470 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
471 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
479 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
487 if (headers != 1 || buffer[0] != '\n')
493 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
495 if (buffer[0] == '#') {
498 if (buffer[1] != '#')
500 for (cp = (bp = buffer) + 1; *cp; cp++)
507 ct->c_end = ftell (out);
510 /* parse content type */
511 if (get_ctinfo (content, ct, inlineD) == NOTOK)
514 for (s2i = str2cts; s2i->si_key; s2i++)
515 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
517 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
521 * check type specified (possibly implicitly)
523 switch (ct->c_type = s2i->si_val) {
525 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
526 ct->c_encoding = CE_7BIT;
531 adios (NULL, "it doesn't make sense to define an in-line %s content",
532 ct->c_type == CT_MESSAGE ? "message" : "multipart");
537 if ((ct->c_ctinitfnx = s2i->si_init))
538 (*ct->c_ctinitfnx) (ct);
543 fseek (in, pos, SEEK_SET);
548 * If we've reached this point, the next line
549 * must be some type of explicit directive.
552 /* check if directive is external-type */
553 extrnal = (buf[1] == '@');
555 /* parse directive */
556 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
559 /* check directive against the list of MIME types */
560 for (s2i = str2cts; s2i->si_key; s2i++)
561 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
565 * Check if the directive specified a valid type.
566 * This will happen if it was one of the following forms:
573 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
575 switch (ct->c_type = s2i->si_val) {
577 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
578 ci->ci_type, ci->ci_subtype);
582 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
583 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
584 ci->ci_type, ci->ci_subtype);
585 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
586 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
587 ci->ci_type, ci->ci_subtype);
590 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
591 ci->ci_type, ci->ci_subtype);
595 if ((ct->c_ctinitfnx = s2i->si_init))
596 (*ct->c_ctinitfnx) (ct);
601 * #@type/subtype (external types directive)
608 adios (NULL, "need external information for \"#@%s/%s\"",
609 ci->ci_type, ci->ci_subtype);
612 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
617 * Since we are using the current Content structure to
618 * hold information about the type of the external
619 * reference, we need to create another Content structure
620 * for the message/external-body to wrap it in.
622 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
623 adios (NULL, "out of memory");
626 if (get_ctinfo (buffer, ct, 0) == NOTOK)
628 ct->c_type = CT_MESSAGE;
629 ct->c_subtype = MESSAGE_EXTERNAL;
631 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
632 adios (NULL, "out of memory");
633 ct->c_ctparams = (void *) e;
639 if (params_external (ct, 1) == NOTOK)
645 /* Handle [file] argument */
647 /* check if specifies command to execute */
648 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
649 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
652 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
657 /* record filename of decoded contents */
658 ce->ce_file = ci->ci_magic;
659 if (access (ce->ce_file, R_OK) == NOTOK)
660 adios ("reading", "unable to access %s for", ce->ce_file);
661 if (listsw && stat (ce->ce_file, &st) != NOTOK)
662 ct->c_end = (long) st.st_size;
669 * No [file] argument, so check profile for
670 * method to compose content.
672 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
673 invo_name, ci->ci_type, ci->ci_subtype);
674 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
675 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
676 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
677 content_error (NULL, ct, "don't know how to compose content");
681 ci->ci_magic = add (cp, NULL);
686 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
690 * #forw [+folder] [msgs]
692 if (!mh_strcasecmp (ci->ci_type, "forw")) {
694 char *folder, *arguments[MAXARGS];
698 ap = brkstring (ci->ci_magic, " ", "\n");
699 copyip (ap, arguments, MAXARGS);
701 arguments[0] = "cur";
706 /* search the arguments for a folder name */
707 for (ap = arguments; *ap; ap++) {
709 if (*cp == '+' || *cp == '@') {
711 adios (NULL, "only one folder per #forw directive");
713 folder = pluspath (cp);
717 /* else, use the current folder */
719 folder = add (getfolder (1), NULL);
721 if (!(mp = folder_read (folder)))
722 adios (NULL, "unable to read folder %s", folder);
723 for (ap = arguments; *ap; ap++) {
725 if (*cp != '+' && *cp != '@')
726 if (!m_convert (mp, cp))
733 * If there is more than one message to include, make this
734 * a content of type "multipart/digest" and insert each message
735 * as a subpart. If there is only one message, then make this
736 * a content of type "message/rfc822".
738 if (mp->numsel > 1) {
739 /* we are forwarding multiple messages */
740 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
742 ct->c_type = CT_MULTIPART;
743 ct->c_subtype = MULTI_DIGEST;
745 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
746 adios (NULL, "out of memory");
747 ct->c_ctparams = (void *) m;
750 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
751 if (is_selected(mp, msgnum)) {
756 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
757 adios (NULL, "out of memory");
758 init_decoded_content (p);
760 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
762 p->c_type = CT_MESSAGE;
763 p->c_subtype = MESSAGE_RFC822;
765 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
766 pe->ce_file = add (buffer, NULL);
767 if (listsw && stat (pe->ce_file, &st) != NOTOK)
768 p->c_end = (long) st.st_size;
770 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
771 adios (NULL, "out of memory");
778 /* we are forwarding one message */
779 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
781 ct->c_type = CT_MESSAGE;
782 ct->c_subtype = MESSAGE_RFC822;
785 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
786 ce->ce_file = add (buffer, NULL);
787 if (listsw && stat (ce->ce_file, &st) != NOTOK)
788 ct->c_end = (long) st.st_size;
791 folder_free (mp); /* free folder/message structure */
798 if (!mh_strcasecmp (ci->ci_type, "end")) {
805 * #begin [ alternative | parallel ]
807 if (!mh_strcasecmp (ci->ci_type, "begin")) {
810 cp = SubMultiPart[vrsn - 1].kv_key;
811 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
812 vrsn = MULTI_ALTERNATE;
813 cp = SubMultiPart[vrsn - 1].kv_key;
814 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
815 vrsn = MULTI_PARALLEL;
816 cp = SubMultiPart[vrsn - 1].kv_key;
817 } else if (uprf (ci->ci_magic, "digest")) {
820 vrsn = MULTI_UNKNOWN;
825 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
826 if (get_ctinfo (buffer, ct, 0) == NOTOK)
828 ct->c_type = CT_MULTIPART;
829 ct->c_subtype = vrsn;
831 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
832 adios (NULL, "out of memory");
833 ct->c_ctparams = (void *) m;
836 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
840 if (user_content (in, file, buffer, &p) == DONE) {
842 adios (NULL, "empty \"#begin ... #end\" sequence");
848 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
849 adios (NULL, "out of memory");
854 admonish (NULL, "premature end-of-file, missing #end");
861 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
862 return NOTOK; /* NOT REACHED */
867 set_id (CT ct, int top)
871 static time_t clock = 0;
876 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
877 (int) getpid(), (long) clock, LocalName());
879 msgfmt = getcpy(msgid);
881 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
882 ct->c_id = getcpy (msgid);
886 static char ebcdicsafe[0x100] = {
887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
888 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
891 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
892 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
893 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
894 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
895 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
896 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
897 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
898 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
899 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
900 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
901 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
902 0x01, 0x01, 0x01, 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,
917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
923 * Fill out, or expand the various contents in the composition
924 * draft. Read-in any necessary files. Parse and execute any
925 * commands specified by profile composition strings.
929 compose_content (CT ct)
931 CE ce = ct->c_cefile;
933 switch (ct->c_type) {
938 char partnam[BUFSIZ];
939 struct multipart *m = (struct multipart *) ct->c_ctparams;
943 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
944 pp = partnam + strlen (partnam);
949 /* first, we call compose_content on all the subparts */
950 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
951 CT p = part->mp_part;
953 sprintf (pp, "%d", partnum);
954 p->c_partno = add (partnam, NULL);
955 if (compose_content (p) == NOTOK)
960 * If the -rfc934mode switch is given, then check all
961 * the subparts of a multipart/digest. If they are all
962 * message/rfc822, then mark this content and all
963 * subparts with the rfc934 compatibility mode flag.
965 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
968 for (part = m->mp_parts; part; part = part->mp_next) {
969 CT p = part->mp_part;
971 if (p->c_subtype != MESSAGE_RFC822) {
976 ct->c_rfc934 = is934;
977 for (part = m->mp_parts; part; part = part->mp_next) {
978 CT p = part->mp_part;
980 if ((p->c_rfc934 = is934))
986 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
990 for (part = m->mp_parts; part; part = part->mp_next)
991 ct->c_end += part->mp_part->c_end + partnum;
997 /* Nothing to do for type message */
1001 * Discrete types (text/application/audio/image/video)
1006 int i, xstdout, len, buflen;
1007 char *bp, **ap, *cp;
1008 char *vec[4], buffer[BUFSIZ];
1010 CI ci = &ct->c_ctinfo;
1013 if (!(cp = ci->ci_magic))
1014 adios (NULL, "internal error(5)");
1016 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1017 if (tfile == NULL) {
1018 adios("mhbuildsbr", "unable to create temporary file");
1020 ce->ce_file = add (tfile, NULL);
1025 /* Get buffer ready to go */
1028 buflen = sizeof(buffer);
1031 * Parse composition string into buffer
1033 for ( ; *cp; cp++) {
1038 /* insert parameters from directive */
1042 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1043 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1053 /* %f, and stdout is not-redirected */
1059 * insert temporary filename where
1060 * content should be written
1062 snprintf (bp, buflen, "%s", ce->ce_file);
1066 /* insert content subtype */
1067 strncpy (bp, ci->ci_subtype, buflen);
1071 /* insert character % */
1092 printf ("composing content %s/%s from command\n\t%s\n",
1093 ci->ci_type, ci->ci_subtype, buffer);
1095 fflush (stdout); /* not sure if need for -noverbose */
1102 if ((out = fopen (ce->ce_file, "w")) == NULL)
1103 adios (ce->ce_file, "unable to open for writing");
1105 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1109 adios ("fork", "unable to fork");
1114 dup2 (fileno (out), 1);
1115 close (fileno (out));
1116 execvp ("/bin/sh", vec);
1117 fprintf (stderr, "unable to exec ");
1124 if (pidXwait(child_id, NULL))
1130 /* Check size of file */
1131 if (listsw && ct->c_end == 0L) {
1134 if (stat (ce->ce_file, &st) != NOTOK)
1135 ct->c_end = (long) st.st_size;
1147 * 1) choose a transfer encoding.
1148 * 2) check for clashes with multipart boundary string.
1149 * 3) for text content, figure out which character set is being used.
1151 * If there is a clash with one of the contents and the multipart boundary,
1152 * this function will exit with NOTOK. This will cause the scanning process
1153 * to be repeated with a different multipart boundary. It is possible
1154 * (although highly unlikely) that this scan will be repeated multiple times.
1158 scan_content (CT ct)
1161 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1162 int checklinelen = 0, linelen = 0; /* check for long lines */
1163 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1164 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1165 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1166 unsigned char *cp = NULL, buffer[BUFSIZ];
1167 struct text *t = NULL;
1169 CE ce = ct->c_cefile;
1172 * handle multipart by scanning all subparts
1173 * and then checking their encoding.
1175 if (ct->c_type == CT_MULTIPART) {
1176 struct multipart *m = (struct multipart *) ct->c_ctparams;
1179 /* initially mark the domain of enclosing multipart as 7bit */
1180 ct->c_encoding = CE_7BIT;
1182 for (part = m->mp_parts; part; part = part->mp_next) {
1183 CT p = part->mp_part;
1185 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1188 /* if necessary, enlarge encoding for enclosing multipart */
1189 if (p->c_encoding == CE_BINARY)
1190 ct->c_encoding = CE_BINARY;
1191 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1192 ct->c_encoding = CE_8BIT;
1199 * Decide what to check while scanning this content.
1201 switch (ct->c_type) {
1205 if (ct->c_subtype == TEXT_PLAIN) {
1210 checkebcdic = ebcdicsw;
1216 case CT_APPLICATION:
1218 checkebcdic = ebcdicsw;
1230 /* don't check anything for message/external */
1231 if (ct->c_subtype == MESSAGE_EXTERNAL)
1241 * Don't check anything for these types,
1242 * since we are forcing use of base64.
1253 * Scan the unencoded content
1255 if (check8bit || checklinelen || checklinespace || checkboundary) {
1256 if ((in = fopen (ce->ce_file, "r")) == NULL)
1257 adios (ce->ce_file, "unable to open for reading");
1258 len = strlen (prefix);
1260 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1262 * Check for 8bit data.
1265 for (cp = buffer; *cp; cp++) {
1266 if (!isascii (*cp)) {
1268 check8bit = 0; /* no need to keep checking */
1271 * Check if character is ebcdic-safe. We only check
1272 * this if also checking for 8bit data.
1274 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1276 checkebcdic = 0; /* no need to keep checking */
1282 * Check line length.
1284 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1286 checklinelen = 0; /* no need to keep checking */
1290 * Check if line ends with a space.
1292 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1294 checklinespace = 0; /* no need to keep checking */
1298 * Check if content contains a line that clashes
1299 * with our standard boundary for multipart messages.
1301 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1302 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1306 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1308 checkboundary = 0; /* no need to keep checking */
1316 * Decide which transfer encoding to use.
1318 switch (ct->c_type) {
1321 * If the text content didn't specify a character
1322 * set, we need to figure out which one was used.
1324 t = (struct text *) ct->c_ctparams;
1325 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1326 CI ci = &ct->c_ctinfo;
1329 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1333 t->tx_charset = CHARSET_UNKNOWN;
1334 *ap = concat ("charset=", write_charset_8bit(), NULL);
1336 t->tx_charset = CHARSET_USASCII;
1337 *ap = add ("charset=us-ascii", NULL);
1340 cp = strchr(*ap++, '=');
1346 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1347 ct->c_encoding = CE_QUOTED;
1349 ct->c_encoding = CE_7BIT;
1352 case CT_APPLICATION:
1353 /* For application type, use base64, except when postscript */
1354 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1355 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1356 ? CE_QUOTED : CE_BASE64;
1358 ct->c_encoding = CE_7BIT;
1362 ct->c_encoding = CE_7BIT;
1368 /* For audio, image, and video contents, just use base64 */
1369 ct->c_encoding = CE_BASE64;
1373 return (boundaryclash ? NOTOK : OK);
1378 * Scan the content structures, and build header
1379 * fields that will need to be output into the
1384 build_headers (CT ct)
1386 int cc, mailbody, len;
1388 char *np, *vp, buffer[BUFSIZ];
1389 CI ci = &ct->c_ctinfo;
1392 * If message is type multipart, then add the multipart
1393 * boundary to the list of attribute/value pairs.
1395 if (ct->c_type == CT_MULTIPART) {
1397 static int level = 0; /* store nesting level */
1401 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1402 cp = strchr(*ap++ = add (buffer, NULL), '=');
1409 * Skip the output of Content-Type, parameters, content
1410 * description and disposition, and Content-ID if the
1411 * content is of type "message" and the rfc934 compatibility
1412 * flag is set (which means we are inside multipart/digest
1413 * and the switch -rfc934mode was given).
1415 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1419 * output the content type and subtype
1421 np = add (TYPE_FIELD, NULL);
1422 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1424 /* keep track of length of line */
1425 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1426 + strlen (ci->ci_subtype) + 3;
1428 mailbody = ct->c_type == CT_MESSAGE
1429 && ct->c_subtype == MESSAGE_EXTERNAL
1430 && ((struct exbody *) ct->c_ctparams)->eb_body;
1433 * Append the attribute/value pairs to
1434 * the end of the Content-Type line.
1436 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1437 if (mailbody && !mh_strcasecmp (*ap, "body"))
1443 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1444 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1445 vp = add ("\n\t", vp);
1451 vp = add (buffer, vp);
1456 * Append any RFC-822 comment to the end of
1457 * the Content-Type line.
1459 if (ci->ci_comment) {
1460 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1461 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1462 vp = add ("\n\t", vp);
1468 vp = add (buffer, vp);
1471 vp = add ("\n", vp);
1472 add_header (ct, np, vp);
1475 * output the Content-ID, unless disabled by -nocontentid
1477 if (contentidsw && ct->c_id) {
1478 np = add (ID_FIELD, NULL);
1479 vp = concat (" ", ct->c_id, NULL);
1480 add_header (ct, np, vp);
1484 * output the Content-Description
1487 np = add (DESCR_FIELD, NULL);
1488 vp = concat (" ", ct->c_descr, NULL);
1489 add_header (ct, np, vp);
1493 * output the Content-Disposition
1496 np = add (DISPO_FIELD, NULL);
1497 vp = concat (" ", ct->c_dispo, NULL);
1498 add_header (ct, np, vp);
1503 * If this is the internal content structure for a
1504 * "message/external", then we are done with the
1505 * headers (since it has no body).
1511 * output the Content-MD5
1514 np = add (MD5_FIELD, NULL);
1515 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1516 add_header (ct, np, vp);
1520 * output the Content-Transfer-Encoding
1522 switch (ct->c_encoding) {
1524 /* Nothing to output */
1526 np = add (ENCODING_FIELD, NULL);
1527 vp = concat (" ", "7bit", "\n", NULL);
1528 add_header (ct, np, vp);
1533 if (ct->c_type == CT_MESSAGE)
1534 adios (NULL, "internal error, invalid encoding");
1536 np = add (ENCODING_FIELD, NULL);
1537 vp = concat (" ", "8bit", "\n", NULL);
1538 add_header (ct, np, vp);
1542 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1543 adios (NULL, "internal error, invalid encoding");
1545 np = add (ENCODING_FIELD, NULL);
1546 vp = concat (" ", "quoted-printable", "\n", NULL);
1547 add_header (ct, np, vp);
1551 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1552 adios (NULL, "internal error, invalid encoding");
1554 np = add (ENCODING_FIELD, NULL);
1555 vp = concat (" ", "base64", "\n", NULL);
1556 add_header (ct, np, vp);
1560 if (ct->c_type == CT_MESSAGE)
1561 adios (NULL, "internal error, invalid encoding");
1563 np = add (ENCODING_FIELD, NULL);
1564 vp = concat (" ", "binary", "\n", NULL);
1565 add_header (ct, np, vp);
1569 adios (NULL, "unknown transfer encoding in content");
1574 * Additional content specific header processing
1576 switch (ct->c_type) {
1579 struct multipart *m;
1582 m = (struct multipart *) ct->c_ctparams;
1583 for (part = m->mp_parts; part; part = part->mp_next) {
1593 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1596 e = (struct exbody *) ct->c_ctparams;
1597 build_headers (e->eb_content);
1610 static char nib2b64[0x40+1] =
1611 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1614 calculate_digest (CT ct, int asciiP)
1617 char buffer[BUFSIZ], *vp, *op;
1619 unsigned char digest[16];
1620 unsigned char outbuf[25];
1623 CE ce = ct->c_cefile;
1626 if ((in = fopen (ce->ce_file, "r")) == NULL)
1627 adios (ce->ce_file, "unable to open for reading");
1629 /* Initialize md5 context */
1630 MD5Init (&mdContext);
1632 /* calculate md5 message digest */
1634 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1637 cp = buffer + strlen (buffer) - 1;
1638 if ((c = *cp) == '\n')
1641 MD5Update (&mdContext, (unsigned char *) buffer,
1642 (unsigned int) strlen (buffer));
1645 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1648 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1649 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1652 /* md5 finalization. Write digest and zero md5 context */
1653 MD5Final (digest, &mdContext);
1658 /* print debugging info */
1662 fprintf (stderr, "MD5 digest=");
1663 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1665 fprintf (stderr, "%02x", *dp & 0xff);
1666 fprintf (stderr, "\n");
1669 /* encode the digest using base64 */
1670 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1671 cc > 0; cc -= 3, op += 4) {
1675 bits = (*dp++ & 0xff) << 16;
1677 bits |= (*dp++ & 0xff) << 8;
1679 bits |= *dp++ & 0xff;
1682 for (bp = op + 4; bp > op; bits >>= 6)
1683 *--bp = nib2b64[bits & 0x3f];
1691 /* null terminate string */
1694 /* now make copy and return string */
1695 vp = concat (" ", outbuf, "\n", NULL);