2 * mhbuildsbr.c -- routines to expand/translate MIME composition files
4 * This code is Copyright (c) 2002, by the authors of nmh. See the
5 * COPYRIGHT file in the root directory of the nmh distribution for
6 * complete copyright information.
10 * This code was originally part of mhn.c. I split it into
11 * a separate program (mhbuild.c) and then later split it
12 * again (mhbuildsbr.c). But the code still has some of
13 * the mhn.c code in it. This program needs additional
14 * streamlining and removal of unneeded code.
19 #include <h/signals.h>
26 #include <h/mhparse.h>
29 #ifdef TIME_WITH_SYS_TIME
30 # include <sys/time.h>
33 # ifdef TM_IN_SYS_TIME
34 # include <sys/time.h>
40 #ifdef HAVE_SYS_WAIT_H
41 # include <sys/wait.h>
51 extern int contentidsw;
53 extern int endian; /* mhmisc.c */
56 extern int rcachesw; /* mhcachesbr.c */
57 extern int wcachesw; /* mhcachesbr.c */
60 * Directory to place tmp files. This must
61 * be set before these routines are called.
67 static char prefix[] = "----- =_aaaaaaaaaa";
71 int make_intermediates (char *);
72 void content_error (char *, CT, char *, ...);
75 int find_cache (CT, int, int *, char *, char *, int);
78 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
81 void free_content (CT);
82 void free_ctinfo (CT);
83 void free_encoding (CT, int);
88 CT build_mime (char *);
93 static int init_decoded_content (CT);
94 static char *fgetstr (char *, int, FILE *);
95 static int user_content (FILE *, char *, char *, CT *);
96 static void set_id (CT, int);
97 static int compose_content (CT);
98 static int scan_content (CT);
99 static int build_headers (CT);
100 static char *calculate_digest (CT, int);
104 * Main routine for translating composition file
105 * into valid MIME message. It translates the draft
106 * into a content structure (actually a tree of content
107 * structures). This message then can be manipulated
108 * in various ways, including being output via
113 build_mime (char *infile)
116 char buf[BUFSIZ], name[NAMESZ];
123 umask (~m_gmprot ());
125 /* open the composition draft */
126 if ((in = fopen (infile, "r")) == NULL)
127 adios (infile, "unable to open for reading");
130 * Allocate space for primary (outside) content
132 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
133 adios (NULL, "out of memory");
136 * Allocate structure for handling decoded content
137 * for this part. We don't really need this, but
138 * allocate it to remain consistent.
140 init_decoded_content (ct);
143 * Parse some of the header fields in the composition
144 * draft into the linked list of header fields for
145 * the new MIME message.
147 for (compnum = 1, state = FLD;;) {
148 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
154 /* abort if draft has Mime-Version header field */
155 if (!mh_strcasecmp (name, VRSN_FIELD))
156 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
158 /* abort if draft has Content-Transfer-Encoding header field */
159 if (!mh_strcasecmp (name, ENCODING_FIELD))
160 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
162 /* ignore any Content-Type fields in the header */
163 if (!mh_strcasecmp (name, TYPE_FIELD)) {
164 while (state == FLDPLUS)
165 state = m_getfld (state, name, buf, sizeof(buf), in);
169 /* get copies of the buffers */
170 np = add (name, NULL);
171 vp = add (buf, NULL);
173 /* if necessary, get rest of field */
174 while (state == FLDPLUS) {
175 state = m_getfld (state, name, buf, sizeof(buf), in);
176 vp = add (buf, vp); /* add to previous value */
179 /* Now add the header data to the list */
180 add_header (ct, np, vp);
183 /* if this wasn't the last header field, then continue */
189 adios (NULL, "draft has empty body -- no directives!");
194 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
199 adios (NULL, "message format error in component #%d", compnum);
202 adios (NULL, "getfld() returned %d", state);
208 * Now add the MIME-Version header field
209 * to the list of header fields.
211 np = add (VRSN_FIELD, NULL);
212 vp = concat (" ", VRSN_VALUE, "\n", NULL);
213 add_header (ct, np, vp);
216 * We initally assume we will find multiple contents in the
217 * draft. So create a multipart/mixed content to hold everything.
218 * We can remove this later, if it is not needed.
220 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
222 ct->c_type = CT_MULTIPART;
223 ct->c_subtype = MULTI_MIXED;
224 ct->c_file = add (infile, NULL);
226 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
227 adios (NULL, "out of memory");
228 ct->c_ctparams = (void *) m;
232 * read and parse the composition file
233 * and the directives it contains.
235 while (fgetstr (buf, sizeof(buf) - 1, in)) {
239 if (user_content (in, infile, buf, &p) == DONE) {
240 admonish (NULL, "ignoring spurious #end");
246 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
247 adios (NULL, "out of memory");
254 * close the composition draft since
255 * it's not needed any longer.
259 /* check if any contents were found */
261 adios (NULL, "no content directives found");
264 * If only one content was found, then remove and
265 * free the outer multipart content.
267 if (!m->mp_parts->mp_next) {
270 p = m->mp_parts->mp_part;
271 m->mp_parts->mp_part = NULL;
273 /* move header fields */
274 p->c_first_hf = ct->c_first_hf;
275 p->c_last_hf = ct->c_last_hf;
276 ct->c_first_hf = NULL;
277 ct->c_last_hf = NULL;
286 * Fill out, or expand directives. Parse and execute
287 * commands specified by profile composition strings.
289 compose_content (ct);
291 if ((cp = strchr(prefix, 'a')) == NULL)
292 adios (NULL, "internal error(4)");
295 * Scan the contents. Choose a transfer encoding, and
296 * check if prefix for multipart boundary clashes with
297 * any of the contents.
299 while (scan_content (ct) == NOTOK) {
304 adios (NULL, "giving up trying to find a unique delimiter string");
310 /* Build the rest of the header field structures */
318 * Set up structures for placing unencoded
319 * content when building parts.
323 init_decoded_content (CT ct)
327 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
328 adios (NULL, "out of memory");
331 ct->c_ceopenfnx = open7Bit; /* since unencoded */
332 ct->c_ceclosefnx = close_encoding;
333 ct->c_cesizefnx = NULL; /* since unencoded */
340 fgetstr (char *s, int n, FILE *stream)
344 for (ep = (cp = s) + n; cp < ep; ) {
347 if (!fgets (cp, n, stream))
348 return (cp != s ? s : NULL);
349 if (cp == s && *cp != '#')
352 cp += (i = strlen (cp)) - 1;
353 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
364 * Parse the composition draft for text and directives.
365 * Do initial setup of Content structure.
369 user_content (FILE *in, char *file, char *buf, CT *ctp)
378 struct str2init *s2i;
383 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
388 /* allocate basic Content structure */
389 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
390 adios (NULL, "out of memory");
393 /* allocate basic structure for handling decoded content */
394 init_decoded_content (ct);
401 * Handle inline text. Check if line
402 * is one of the following forms:
404 * 1) doesn't begin with '#' (implicit directive)
405 * 2) begins with "##" (implicit directive)
406 * 3) begins with "#<"
408 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
412 char content[BUFSIZ];
416 cp = m_mktemp2(NULL, invo_name, NULL, &out);
417 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
419 /* use a temp file to collect the plain text lines */
420 ce->ce_file = add (cp, NULL);
423 if (buf[0] == '#' && buf[1] == '<') {
424 strncpy (content, buf + 2, sizeof(content));
431 /* the directive is implicit */
432 strncpy (content, "text/plain", sizeof(content));
434 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
438 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
439 && buffer[i = strlen (DESCR_FIELD)] == ':') {
443 ct->c_descr = add (buffer + i + 1, ct->c_descr);
444 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
445 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
453 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
461 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
462 && buffer[i = strlen (DISPO_FIELD)] == ':') {
466 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
467 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
468 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
476 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
484 if (headers != 1 || buffer[0] != '\n')
490 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
492 if (buffer[0] == '#') {
495 if (buffer[1] != '#')
497 for (cp = (bp = buffer) + 1; *cp; cp++)
504 ct->c_end = ftell (out);
507 /* parse content type */
508 if (get_ctinfo (content, ct, inlineD) == NOTOK)
511 for (s2i = str2cts; s2i->si_key; s2i++)
512 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
514 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
518 * check type specified (possibly implicitly)
520 switch (ct->c_type = s2i->si_val) {
522 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
523 ct->c_encoding = CE_7BIT;
528 adios (NULL, "it doesn't make sense to define an in-line %s content",
529 ct->c_type == CT_MESSAGE ? "message" : "multipart");
534 if ((ct->c_ctinitfnx = s2i->si_init))
535 (*ct->c_ctinitfnx) (ct);
540 fseek (in, pos, SEEK_SET);
545 * If we've reached this point, the next line
546 * must be some type of explicit directive.
549 /* check if directive is external-type */
550 extrnal = (buf[1] == '@');
552 /* parse directive */
553 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
556 /* check directive against the list of MIME types */
557 for (s2i = str2cts; s2i->si_key; s2i++)
558 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
562 * Check if the directive specified a valid type.
563 * This will happen if it was one of the following forms:
570 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
572 switch (ct->c_type = s2i->si_val) {
574 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
575 ci->ci_type, ci->ci_subtype);
579 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
580 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
581 ci->ci_type, ci->ci_subtype);
582 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
583 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
584 ci->ci_type, ci->ci_subtype);
587 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
588 ci->ci_type, ci->ci_subtype);
592 if ((ct->c_ctinitfnx = s2i->si_init))
593 (*ct->c_ctinitfnx) (ct);
598 * #@type/subtype (external types directive)
605 adios (NULL, "need external information for \"#@%s/%s\"",
606 ci->ci_type, ci->ci_subtype);
609 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
614 * Since we are using the current Content structure to
615 * hold information about the type of the external
616 * reference, we need to create another Content structure
617 * for the message/external-body to wrap it in.
619 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
620 adios (NULL, "out of memory");
623 if (get_ctinfo (buffer, ct, 0) == NOTOK)
625 ct->c_type = CT_MESSAGE;
626 ct->c_subtype = MESSAGE_EXTERNAL;
628 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
629 adios (NULL, "out of memory");
630 ct->c_ctparams = (void *) e;
636 if (params_external (ct, 1) == NOTOK)
642 /* Handle [file] argument */
644 /* check if specifies command to execute */
645 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
646 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
649 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
654 /* record filename of decoded contents */
655 ce->ce_file = ci->ci_magic;
656 if (access (ce->ce_file, R_OK) == NOTOK)
657 adios ("reading", "unable to access %s for", ce->ce_file);
658 if (listsw && stat (ce->ce_file, &st) != NOTOK)
659 ct->c_end = (long) st.st_size;
666 * No [file] argument, so check profile for
667 * method to compose content.
669 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
670 invo_name, ci->ci_type, ci->ci_subtype);
671 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
672 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
673 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
674 content_error (NULL, ct, "don't know how to compose content");
678 ci->ci_magic = add (cp, NULL);
683 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
687 * #forw [+folder] [msgs]
689 if (!mh_strcasecmp (ci->ci_type, "forw")) {
691 char *folder, *arguments[MAXARGS];
695 ap = brkstring (ci->ci_magic, " ", "\n");
696 copyip (ap, arguments, MAXARGS);
698 arguments[0] = "cur";
703 /* search the arguments for a folder name */
704 for (ap = arguments; *ap; ap++) {
706 if (*cp == '+' || *cp == '@') {
708 adios (NULL, "only one folder per #forw directive");
710 folder = pluspath (cp);
714 /* else, use the current folder */
716 folder = add (getfolder (1), NULL);
718 if (!(mp = folder_read (folder)))
719 adios (NULL, "unable to read folder %s", folder);
720 for (ap = arguments; *ap; ap++) {
722 if (*cp != '+' && *cp != '@')
723 if (!m_convert (mp, cp))
730 * If there is more than one message to include, make this
731 * a content of type "multipart/digest" and insert each message
732 * as a subpart. If there is only one message, then make this
733 * a content of type "message/rfc822".
735 if (mp->numsel > 1) {
736 /* we are forwarding multiple messages */
737 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
739 ct->c_type = CT_MULTIPART;
740 ct->c_subtype = MULTI_DIGEST;
742 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
743 adios (NULL, "out of memory");
744 ct->c_ctparams = (void *) m;
747 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
748 if (is_selected(mp, msgnum)) {
753 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
754 adios (NULL, "out of memory");
755 init_decoded_content (p);
757 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
759 p->c_type = CT_MESSAGE;
760 p->c_subtype = MESSAGE_RFC822;
762 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
763 pe->ce_file = add (buffer, NULL);
764 if (listsw && stat (pe->ce_file, &st) != NOTOK)
765 p->c_end = (long) st.st_size;
767 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
768 adios (NULL, "out of memory");
775 /* we are forwarding one message */
776 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
778 ct->c_type = CT_MESSAGE;
779 ct->c_subtype = MESSAGE_RFC822;
782 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
783 ce->ce_file = add (buffer, NULL);
784 if (listsw && stat (ce->ce_file, &st) != NOTOK)
785 ct->c_end = (long) st.st_size;
788 folder_free (mp); /* free folder/message structure */
795 if (!mh_strcasecmp (ci->ci_type, "end")) {
802 * #begin [ alternative | parallel ]
804 if (!mh_strcasecmp (ci->ci_type, "begin")) {
807 cp = SubMultiPart[vrsn - 1].kv_key;
808 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
809 vrsn = MULTI_ALTERNATE;
810 cp = SubMultiPart[vrsn - 1].kv_key;
811 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
812 vrsn = MULTI_PARALLEL;
813 cp = SubMultiPart[vrsn - 1].kv_key;
814 } else if (uprf (ci->ci_magic, "digest")) {
817 vrsn = MULTI_UNKNOWN;
822 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
823 if (get_ctinfo (buffer, ct, 0) == NOTOK)
825 ct->c_type = CT_MULTIPART;
826 ct->c_subtype = vrsn;
828 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
829 adios (NULL, "out of memory");
830 ct->c_ctparams = (void *) m;
833 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
837 if (user_content (in, file, buffer, &p) == DONE) {
839 adios (NULL, "empty \"#begin ... #end\" sequence");
845 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
846 adios (NULL, "out of memory");
851 admonish (NULL, "premature end-of-file, missing #end");
858 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
859 return NOTOK; /* NOT REACHED */
864 set_id (CT ct, int top)
868 static time_t clock = 0;
873 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
874 (int) getpid(), (long) clock, LocalName());
876 msgfmt = getcpy(msgid);
878 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
879 ct->c_id = getcpy (msgid);
883 static char ebcdicsafe[0x100] = {
884 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
885 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
886 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
887 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
888 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
889 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
890 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
891 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
892 0x00, 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 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
896 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
897 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
898 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
899 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
900 0x00, 0x00, 0x00, 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
920 * Fill out, or expand the various contents in the composition
921 * draft. Read-in any necessary files. Parse and execute any
922 * commands specified by profile composition strings.
926 compose_content (CT ct)
928 CE ce = ct->c_cefile;
930 switch (ct->c_type) {
935 char partnam[BUFSIZ];
936 struct multipart *m = (struct multipart *) ct->c_ctparams;
940 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
941 pp = partnam + strlen (partnam);
946 /* first, we call compose_content on all the subparts */
947 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
948 CT p = part->mp_part;
950 sprintf (pp, "%d", partnum);
951 p->c_partno = add (partnam, NULL);
952 if (compose_content (p) == NOTOK)
957 * If the -rfc934mode switch is given, then check all
958 * the subparts of a multipart/digest. If they are all
959 * message/rfc822, then mark this content and all
960 * subparts with the rfc934 compatibility mode flag.
962 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
965 for (part = m->mp_parts; part; part = part->mp_next) {
966 CT p = part->mp_part;
968 if (p->c_subtype != MESSAGE_RFC822) {
973 ct->c_rfc934 = is934;
974 for (part = m->mp_parts; part; part = part->mp_next) {
975 CT p = part->mp_part;
977 if ((p->c_rfc934 = is934))
983 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
987 for (part = m->mp_parts; part; part = part->mp_next)
988 ct->c_end += part->mp_part->c_end + partnum;
994 /* Nothing to do for type message */
998 * Discrete types (text/application/audio/image/video)
1003 int i, xstdout, len, buflen;
1004 char *bp, **ap, *cp;
1005 char *vec[4], buffer[BUFSIZ];
1007 CI ci = &ct->c_ctinfo;
1010 if (!(cp = ci->ci_magic))
1011 adios (NULL, "internal error(5)");
1013 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1014 if (tfile == NULL) {
1015 adios("mhbuildsbr", "unable to create temporary file");
1017 ce->ce_file = add (tfile, NULL);
1022 /* Get buffer ready to go */
1025 buflen = sizeof(buffer);
1028 * Parse composition string into buffer
1030 for ( ; *cp; cp++) {
1035 /* insert parameters from directive */
1039 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1040 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1050 /* %f, and stdout is not-redirected */
1056 * insert temporary filename where
1057 * content should be written
1059 snprintf (bp, buflen, "%s", ce->ce_file);
1063 /* insert content subtype */
1064 strncpy (bp, ci->ci_subtype, buflen);
1068 /* insert character % */
1089 printf ("composing content %s/%s from command\n\t%s\n",
1090 ci->ci_type, ci->ci_subtype, buffer);
1092 fflush (stdout); /* not sure if need for -noverbose */
1099 if ((out = fopen (ce->ce_file, "w")) == NULL)
1100 adios (ce->ce_file, "unable to open for writing");
1102 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1106 adios ("fork", "unable to fork");
1111 dup2 (fileno (out), 1);
1112 close (fileno (out));
1113 execvp ("/bin/sh", vec);
1114 fprintf (stderr, "unable to exec ");
1121 if (pidXwait(child_id, NULL))
1127 /* Check size of file */
1128 if (listsw && ct->c_end == 0L) {
1131 if (stat (ce->ce_file, &st) != NOTOK)
1132 ct->c_end = (long) st.st_size;
1144 * 1) choose a transfer encoding.
1145 * 2) check for clashes with multipart boundary string.
1146 * 3) for text content, figure out which character set is being used.
1148 * If there is a clash with one of the contents and the multipart boundary,
1149 * this function will exit with NOTOK. This will cause the scanning process
1150 * to be repeated with a different multipart boundary. It is possible
1151 * (although highly unlikely) that this scan will be repeated multiple times.
1155 scan_content (CT ct)
1158 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1159 int checklinelen = 0, linelen = 0; /* check for long lines */
1160 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1161 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1162 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1163 unsigned char *cp = NULL, buffer[BUFSIZ];
1164 struct text *t = NULL;
1166 CE ce = ct->c_cefile;
1169 * handle multipart by scanning all subparts
1170 * and then checking their encoding.
1172 if (ct->c_type == CT_MULTIPART) {
1173 struct multipart *m = (struct multipart *) ct->c_ctparams;
1176 /* initially mark the domain of enclosing multipart as 7bit */
1177 ct->c_encoding = CE_7BIT;
1179 for (part = m->mp_parts; part; part = part->mp_next) {
1180 CT p = part->mp_part;
1182 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1185 /* if necessary, enlarge encoding for enclosing multipart */
1186 if (p->c_encoding == CE_BINARY)
1187 ct->c_encoding = CE_BINARY;
1188 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1189 ct->c_encoding = CE_8BIT;
1196 * Decide what to check while scanning this content.
1198 switch (ct->c_type) {
1202 if (ct->c_subtype == TEXT_PLAIN) {
1207 checkebcdic = ebcdicsw;
1213 case CT_APPLICATION:
1215 checkebcdic = ebcdicsw;
1227 /* don't check anything for message/external */
1228 if (ct->c_subtype == MESSAGE_EXTERNAL)
1238 * Don't check anything for these types,
1239 * since we are forcing use of base64.
1250 * Scan the unencoded content
1252 if (check8bit || checklinelen || checklinespace || checkboundary) {
1253 if ((in = fopen (ce->ce_file, "r")) == NULL)
1254 adios (ce->ce_file, "unable to open for reading");
1255 len = strlen (prefix);
1257 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1259 * Check for 8bit data.
1262 for (cp = buffer; *cp; cp++) {
1263 if (!isascii (*cp)) {
1265 check8bit = 0; /* no need to keep checking */
1268 * Check if character is ebcdic-safe. We only check
1269 * this if also checking for 8bit data.
1271 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1273 checkebcdic = 0; /* no need to keep checking */
1279 * Check line length.
1281 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1283 checklinelen = 0; /* no need to keep checking */
1287 * Check if line ends with a space.
1289 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1291 checklinespace = 0; /* no need to keep checking */
1295 * Check if content contains a line that clashes
1296 * with our standard boundary for multipart messages.
1298 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1299 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1303 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1305 checkboundary = 0; /* no need to keep checking */
1313 * Decide which transfer encoding to use.
1315 switch (ct->c_type) {
1318 * If the text content didn't specify a character
1319 * set, we need to figure out which one was used.
1321 t = (struct text *) ct->c_ctparams;
1322 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1323 CI ci = &ct->c_ctinfo;
1326 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1330 t->tx_charset = CHARSET_UNKNOWN;
1331 *ap = concat ("charset=", write_charset_8bit(), NULL);
1333 t->tx_charset = CHARSET_USASCII;
1334 *ap = add ("charset=us-ascii", NULL);
1337 cp = strchr(*ap++, '=');
1343 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1344 ct->c_encoding = CE_QUOTED;
1346 ct->c_encoding = CE_7BIT;
1349 case CT_APPLICATION:
1350 /* For application type, use base64, except when postscript */
1351 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1352 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1353 ? CE_QUOTED : CE_BASE64;
1355 ct->c_encoding = CE_7BIT;
1359 ct->c_encoding = CE_7BIT;
1365 /* For audio, image, and video contents, just use base64 */
1366 ct->c_encoding = CE_BASE64;
1370 return (boundaryclash ? NOTOK : OK);
1375 * Scan the content structures, and build header
1376 * fields that will need to be output into the
1381 build_headers (CT ct)
1383 int cc, mailbody, len;
1385 char *np, *vp, buffer[BUFSIZ];
1386 CI ci = &ct->c_ctinfo;
1389 * If message is type multipart, then add the multipart
1390 * boundary to the list of attribute/value pairs.
1392 if (ct->c_type == CT_MULTIPART) {
1394 static int level = 0; /* store nesting level */
1398 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1399 cp = strchr(*ap++ = add (buffer, NULL), '=');
1406 * Skip the output of Content-Type, parameters, content
1407 * description and disposition, and Content-ID if the
1408 * content is of type "message" and the rfc934 compatibility
1409 * flag is set (which means we are inside multipart/digest
1410 * and the switch -rfc934mode was given).
1412 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1416 * output the content type and subtype
1418 np = add (TYPE_FIELD, NULL);
1419 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1421 /* keep track of length of line */
1422 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1423 + strlen (ci->ci_subtype) + 3;
1425 mailbody = ct->c_type == CT_MESSAGE
1426 && ct->c_subtype == MESSAGE_EXTERNAL
1427 && ((struct exbody *) ct->c_ctparams)->eb_body;
1430 * Append the attribute/value pairs to
1431 * the end of the Content-Type line.
1433 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1434 if (mailbody && !mh_strcasecmp (*ap, "body"))
1440 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1441 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1442 vp = add ("\n\t", vp);
1448 vp = add (buffer, vp);
1453 * Append any RFC-822 comment to the end of
1454 * the Content-Type line.
1456 if (ci->ci_comment) {
1457 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1458 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1459 vp = add ("\n\t", vp);
1465 vp = add (buffer, vp);
1468 vp = add ("\n", vp);
1469 add_header (ct, np, vp);
1472 * output the Content-ID, unless disabled by -nocontentid
1474 if (contentidsw && ct->c_id) {
1475 np = add (ID_FIELD, NULL);
1476 vp = concat (" ", ct->c_id, NULL);
1477 add_header (ct, np, vp);
1481 * output the Content-Description
1484 np = add (DESCR_FIELD, NULL);
1485 vp = concat (" ", ct->c_descr, NULL);
1486 add_header (ct, np, vp);
1490 * output the Content-Disposition
1493 np = add (DISPO_FIELD, NULL);
1494 vp = concat (" ", ct->c_dispo, NULL);
1495 add_header (ct, np, vp);
1500 * If this is the internal content structure for a
1501 * "message/external", then we are done with the
1502 * headers (since it has no body).
1508 * output the Content-MD5
1511 np = add (MD5_FIELD, NULL);
1512 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1513 add_header (ct, np, vp);
1517 * output the Content-Transfer-Encoding
1519 switch (ct->c_encoding) {
1521 /* Nothing to output */
1523 np = add (ENCODING_FIELD, NULL);
1524 vp = concat (" ", "7bit", "\n", NULL);
1525 add_header (ct, np, vp);
1530 if (ct->c_type == CT_MESSAGE)
1531 adios (NULL, "internal error, invalid encoding");
1533 np = add (ENCODING_FIELD, NULL);
1534 vp = concat (" ", "8bit", "\n", NULL);
1535 add_header (ct, np, vp);
1539 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1540 adios (NULL, "internal error, invalid encoding");
1542 np = add (ENCODING_FIELD, NULL);
1543 vp = concat (" ", "quoted-printable", "\n", NULL);
1544 add_header (ct, np, vp);
1548 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1549 adios (NULL, "internal error, invalid encoding");
1551 np = add (ENCODING_FIELD, NULL);
1552 vp = concat (" ", "base64", "\n", NULL);
1553 add_header (ct, np, vp);
1557 if (ct->c_type == CT_MESSAGE)
1558 adios (NULL, "internal error, invalid encoding");
1560 np = add (ENCODING_FIELD, NULL);
1561 vp = concat (" ", "binary", "\n", NULL);
1562 add_header (ct, np, vp);
1566 adios (NULL, "unknown transfer encoding in content");
1571 * Additional content specific header processing
1573 switch (ct->c_type) {
1576 struct multipart *m;
1579 m = (struct multipart *) ct->c_ctparams;
1580 for (part = m->mp_parts; part; part = part->mp_next) {
1590 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1593 e = (struct exbody *) ct->c_ctparams;
1594 build_headers (e->eb_content);
1607 static char nib2b64[0x40+1] =
1608 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1611 calculate_digest (CT ct, int asciiP)
1614 char buffer[BUFSIZ], *vp, *op;
1616 unsigned char digest[16];
1617 unsigned char outbuf[25];
1620 CE ce = ct->c_cefile;
1623 if ((in = fopen (ce->ce_file, "r")) == NULL)
1624 adios (ce->ce_file, "unable to open for reading");
1626 /* Initialize md5 context */
1627 MD5Init (&mdContext);
1629 /* calculate md5 message digest */
1631 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1634 cp = buffer + strlen (buffer) - 1;
1635 if ((c = *cp) == '\n')
1638 MD5Update (&mdContext, (unsigned char *) buffer,
1639 (unsigned int) strlen (buffer));
1642 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1645 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1646 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1649 /* md5 finalization. Write digest and zero md5 context */
1650 MD5Final (digest, &mdContext);
1655 /* print debugging info */
1659 fprintf (stderr, "MD5 digest=");
1660 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1662 fprintf (stderr, "%02x", *dp & 0xff);
1663 fprintf (stderr, "\n");
1666 /* encode the digest using base64 */
1667 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1668 cc > 0; cc -= 3, op += 4) {
1672 bits = (*dp++ & 0xff) << 16;
1674 bits |= (*dp++ & 0xff) << 8;
1676 bits |= *dp++ & 0xff;
1679 for (bp = op + 4; bp > op; bits >>= 6)
1680 *--bp = nib2b64[bits & 0x3f];
1688 /* null terminate string */
1691 /* now make copy and return string */
1692 vp = concat (" ", outbuf, "\n", NULL);