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);
159 ** abort if draft has Content-Transfer-Encoding
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,
173 /* get copies of the buffers */
174 np = add (name, NULL);
175 vp = add (buf, NULL);
177 /* if necessary, get rest of field */
178 while (state == FLDPLUS) {
179 state = m_getfld (state, name, buf, sizeof(buf), in);
180 vp = add (buf, vp); /* add to prev value */
183 /* Now add the header data to the list */
184 add_header (ct, np, vp);
187 /* if this wasn't the last hdr field, then continue */
193 adios (NULL, "draft has empty body -- no directives!");
198 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
203 adios (NULL, "message format error in component #%d",
207 adios (NULL, "getfld() returned %d", state);
213 ** Now add the MIME-Version header field
214 ** to the list of header fields.
216 np = add (VRSN_FIELD, NULL);
217 vp = concat (" ", VRSN_VALUE, "\n", NULL);
218 add_header (ct, np, vp);
221 ** We initally assume we will find multiple contents in the
222 ** draft. So create a multipart/mixed content to hold everything.
223 ** We can remove this later, if it is not needed.
225 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
227 ct->c_type = CT_MULTIPART;
228 ct->c_subtype = MULTI_MIXED;
229 ct->c_file = add (infile, NULL);
231 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
232 adios (NULL, "out of memory");
233 ct->c_ctparams = (void *) m;
237 ** read and parse the composition file
238 ** and the directives it contains.
240 while (fgetstr (buf, sizeof(buf) - 1, in)) {
244 if (user_content (in, infile, buf, &p) == DONE) {
245 admonish (NULL, "ignoring spurious #end");
251 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
252 adios (NULL, "out of memory");
259 ** close the composition draft since
260 ** it's not needed any longer.
264 /* check if any contents were found */
266 adios (NULL, "no content directives found");
269 ** If only one content was found, then remove and
270 ** free the outer multipart content.
272 if (!m->mp_parts->mp_next) {
275 p = m->mp_parts->mp_part;
276 m->mp_parts->mp_part = NULL;
278 /* move header fields */
279 p->c_first_hf = ct->c_first_hf;
280 p->c_last_hf = ct->c_last_hf;
281 ct->c_first_hf = NULL;
282 ct->c_last_hf = NULL;
291 ** Fill out, or expand directives. Parse and execute
292 ** commands specified by profile composition strings.
294 compose_content (ct);
296 if ((cp = strchr(prefix, 'a')) == NULL)
297 adios (NULL, "internal error(4)");
300 ** Scan the contents. Choose a transfer encoding, and
301 ** check if prefix for multipart boundary clashes with
302 ** any of the contents.
304 while (scan_content (ct) == NOTOK) {
309 adios (NULL, "giving up trying to find a unique delimiter string");
315 /* Build the rest of the header field structures */
323 ** Set up structures for placing unencoded
324 ** content when building parts.
328 init_decoded_content (CT ct)
332 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
333 adios (NULL, "out of memory");
336 ct->c_ceopenfnx = open7Bit; /* since unencoded */
337 ct->c_ceclosefnx = close_encoding;
338 ct->c_cesizefnx = NULL; /* since unencoded */
345 fgetstr (char *s, int n, FILE *stream)
349 for (ep = (cp = s) + n; cp < ep; ) {
352 if (!fgets (cp, n, stream))
353 return (cp != s ? s : NULL);
354 if (cp == s && *cp != '#')
357 cp += (i = strlen (cp)) - 1;
358 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
369 ** Parse the composition draft for text and directives.
370 ** Do initial setup of Content structure.
374 user_content (FILE *in, char *file, char *buf, CT *ctp)
383 struct str2init *s2i;
388 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
393 /* allocate basic Content structure */
394 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
395 adios (NULL, "out of memory");
398 /* allocate basic structure for handling decoded content */
399 init_decoded_content (ct);
406 ** Handle inline text. Check if line
407 ** is one of the following forms:
409 ** 1) doesn't begin with '#' (implicit directive)
410 ** 2) begins with "##" (implicit directive)
411 ** 3) begins with "#<"
413 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
417 char content[BUFSIZ];
421 cp = m_mktemp2(NULL, invo_name, NULL, &out);
423 adios("mhbuildsbr", "unable to create temporary file");
425 /* use a temp file to collect the plain text lines */
426 ce->ce_file = add (cp, NULL);
429 if (buf[0] == '#' && buf[1] == '<') {
430 strncpy (content, buf + 2, sizeof(content));
437 /* the directive is implicit */
438 strncpy (content, "text/plain", sizeof(content));
440 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
444 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
445 && buffer[i = strlen (DESCR_FIELD)] == ':') {
449 ct->c_descr = add (buffer + i + 1, ct->c_descr);
450 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
451 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
459 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
467 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
468 && buffer[i = strlen (DISPO_FIELD)] == ':') {
472 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
473 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
474 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
482 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
490 if (headers != 1 || buffer[0] != '\n')
496 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
498 if (buffer[0] == '#') {
501 if (buffer[1] != '#')
503 for (cp = (bp = buffer) + 1; *cp; cp++)
510 ct->c_end = ftell (out);
513 /* parse content type */
514 if (get_ctinfo (content, ct, inlineD) == NOTOK)
517 for (s2i = str2cts; s2i->si_key; s2i++)
518 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
520 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
524 ** check type specified (possibly implicitly)
526 switch (ct->c_type = s2i->si_val) {
528 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
529 ct->c_encoding = CE_7BIT;
534 adios (NULL, "it doesn't make sense to define an in-line %s content",
535 ct->c_type == CT_MESSAGE ? "message" :
541 if ((ct->c_ctinitfnx = s2i->si_init))
542 (*ct->c_ctinitfnx) (ct);
547 fseek (in, pos, SEEK_SET);
552 ** If we've reached this point, the next line
553 ** must be some type of explicit directive.
556 /* check if directive is external-type */
557 extrnal = (buf[1] == '@');
559 /* parse directive */
560 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
563 /* check directive against the list of MIME types */
564 for (s2i = str2cts; s2i->si_key; s2i++)
565 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
569 ** Check if the directive specified a valid type.
570 ** This will happen if it was one of the following forms:
572 ** #type/subtype (or)
577 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
579 switch (ct->c_type = s2i->si_val) {
581 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
582 ci->ci_type, ci->ci_subtype);
586 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
587 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
588 ci->ci_type, ci->ci_subtype);
589 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
590 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
591 ci->ci_type, ci->ci_subtype);
594 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
595 ci->ci_type, ci->ci_subtype);
599 if ((ct->c_ctinitfnx = s2i->si_init))
600 (*ct->c_ctinitfnx) (ct);
605 ** #@type/subtype (external types directive)
612 adios (NULL, "need external information for \"#@%s/%s\"",
613 ci->ci_type, ci->ci_subtype);
616 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
621 ** Since we are using the current Content structure to
622 ** hold information about the type of the external
623 ** reference, we need to create another Content
624 ** structure for the message/external-body to wrap
627 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
628 adios (NULL, "out of memory");
631 if (get_ctinfo (buffer, ct, 0) == NOTOK)
633 ct->c_type = CT_MESSAGE;
634 ct->c_subtype = MESSAGE_EXTERNAL;
636 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
637 adios (NULL, "out of memory");
638 ct->c_ctparams = (void *) e;
644 if (params_external (ct, 1) == NOTOK)
650 /* Handle [file] argument */
652 /* check if specifies command to execute */
653 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
654 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
657 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
662 /* record filename of decoded contents */
663 ce->ce_file = ci->ci_magic;
664 if (access (ce->ce_file, R_OK) == NOTOK)
665 adios ("reading", "unable to access %s for", ce->ce_file);
666 if (listsw && stat (ce->ce_file, &st) != NOTOK)
667 ct->c_end = (long) st.st_size;
674 ** No [file] argument, so check profile for
675 ** method to compose content.
677 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
678 invo_name, ci->ci_type, ci->ci_subtype);
679 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
680 snprintf (buffer, sizeof(buffer), "%s-compose-%s",
681 invo_name, ci->ci_type);
682 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
683 content_error (NULL, ct, "don't know how to compose content");
687 ci->ci_magic = add (cp, NULL);
692 adios (NULL, "external definition not allowed for \"#%s\"",
697 ** #forw [+folder] [msgs]
699 if (!mh_strcasecmp (ci->ci_type, "forw")) {
701 char *folder, *arguments[MAXARGS];
705 ap = brkstring (ci->ci_magic, " ", "\n");
706 copyip (ap, arguments, MAXARGS);
708 arguments[0] = "cur";
713 /* search the arguments for a folder name */
714 for (ap = arguments; *ap; ap++) {
716 if (*cp == '+' || *cp == '@') {
718 adios (NULL, "only one folder per #forw directive");
720 folder = pluspath (cp);
724 /* else, use the current folder */
726 folder = add (getfolder (1), NULL);
728 if (!(mp = folder_read (folder)))
729 adios (NULL, "unable to read folder %s", folder);
730 for (ap = arguments; *ap; ap++) {
732 if (*cp != '+' && *cp != '@')
733 if (!m_convert (mp, cp))
740 ** If there is more than one message to include, make this
741 ** a content of type "multipart/digest" and insert each message
742 ** as a subpart. If there is only one message, then make this
743 ** a content of type "message/rfc822".
745 if (mp->numsel > 1) {
746 /* we are forwarding multiple messages */
747 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
749 ct->c_type = CT_MULTIPART;
750 ct->c_subtype = MULTI_DIGEST;
752 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
753 adios (NULL, "out of memory");
754 ct->c_ctparams = (void *) m;
757 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
758 if (is_selected(mp, msgnum)) {
763 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
764 adios (NULL, "out of memory");
765 init_decoded_content (p);
767 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
769 p->c_type = CT_MESSAGE;
770 p->c_subtype = MESSAGE_RFC822;
772 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
773 pe->ce_file = add (buffer, NULL);
774 if (listsw && stat (pe->ce_file, &st) != NOTOK)
775 p->c_end = (long) st.st_size;
777 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
778 adios (NULL, "out of memory");
785 /* we are forwarding one message */
786 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
788 ct->c_type = CT_MESSAGE;
789 ct->c_subtype = MESSAGE_RFC822;
792 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
793 ce->ce_file = add (buffer, NULL);
794 if (listsw && stat (ce->ce_file, &st) != NOTOK)
795 ct->c_end = (long) st.st_size;
798 folder_free (mp); /* free folder/message structure */
805 if (!mh_strcasecmp (ci->ci_type, "end")) {
812 ** #begin [ alternative | parallel ]
814 if (!mh_strcasecmp (ci->ci_type, "begin")) {
817 cp = SubMultiPart[vrsn - 1].kv_key;
818 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
819 vrsn = MULTI_ALTERNATE;
820 cp = SubMultiPart[vrsn - 1].kv_key;
821 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
822 vrsn = MULTI_PARALLEL;
823 cp = SubMultiPart[vrsn - 1].kv_key;
824 } else if (uprf (ci->ci_magic, "digest")) {
827 vrsn = MULTI_UNKNOWN;
832 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
833 if (get_ctinfo (buffer, ct, 0) == NOTOK)
835 ct->c_type = CT_MULTIPART;
836 ct->c_subtype = vrsn;
838 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
839 adios (NULL, "out of memory");
840 ct->c_ctparams = (void *) m;
843 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
847 if (user_content (in, file, buffer, &p) == DONE) {
849 adios (NULL, "empty \"#begin ... #end\" sequence");
855 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
856 adios (NULL, "out of memory");
861 admonish (NULL, "premature end-of-file, missing #end");
868 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
869 return NOTOK; /* NOT REACHED */
874 set_id (CT ct, int top)
878 static time_t clock = 0;
883 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
884 (int) getpid(), (long) clock, LocalName());
886 msgfmt = getcpy(msgid);
888 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
889 ct->c_id = getcpy (msgid);
893 static char ebcdicsafe[0x100] = {
894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
895 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
898 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
899 0x01, 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 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
903 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
904 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
905 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
906 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
907 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
908 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
909 0x01, 0x01, 0x01, 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,
919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
922 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
923 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
924 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
925 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
930 ** Fill out, or expand the various contents in the composition
931 ** draft. Read-in any necessary files. Parse and execute any
932 ** commands specified by profile composition strings.
936 compose_content (CT ct)
938 CE ce = ct->c_cefile;
940 switch (ct->c_type) {
945 char partnam[BUFSIZ];
946 struct multipart *m = (struct multipart *) ct->c_ctparams;
950 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
951 pp = partnam + strlen (partnam);
956 /* first, we call compose_content on all the subparts */
957 for (part = m->mp_parts, partnum = 1; part;
958 part = part->mp_next, partnum++) {
959 CT p = part->mp_part;
961 sprintf (pp, "%d", partnum);
962 p->c_partno = add (partnam, NULL);
963 if (compose_content (p) == NOTOK)
968 ** If the -rfc934mode switch is given, then check all
969 ** the subparts of a multipart/digest. If they are all
970 ** message/rfc822, then mark this content and all
971 ** subparts with the rfc934 compatibility mode flag.
973 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
976 for (part = m->mp_parts; part; part = part->mp_next) {
977 CT p = part->mp_part;
979 if (p->c_subtype != MESSAGE_RFC822) {
984 ct->c_rfc934 = is934;
985 for (part = m->mp_parts; part; part = part->mp_next) {
986 CT p = part->mp_part;
988 if ((p->c_rfc934 = is934))
994 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
998 for (part = m->mp_parts; part; part = part->mp_next)
999 ct->c_end += part->mp_part->c_end + partnum;
1005 /* Nothing to do for type message */
1009 ** Discrete types (text/application/audio/image/video)
1014 int i, xstdout, len, buflen;
1015 char *bp, **ap, *cp;
1016 char *vec[4], buffer[BUFSIZ];
1018 CI ci = &ct->c_ctinfo;
1021 if (!(cp = ci->ci_magic))
1022 adios (NULL, "internal error(5)");
1024 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1025 if (tfile == NULL) {
1026 adios("mhbuildsbr", "unable to create temporary file");
1028 ce->ce_file = add (tfile, NULL);
1033 /* Get buffer ready to go */
1036 buflen = sizeof(buffer);
1039 ** Parse composition string into buffer
1041 for ( ; *cp; cp++) {
1047 ** insert parameters from
1053 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1054 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1064 /* %f, and stdout is not-redirected */
1070 ** insert temporary filename
1071 ** where content should be
1074 snprintf (bp, buflen, "%s", ce->ce_file);
1078 /* insert content subtype */
1079 strncpy (bp, ci->ci_subtype, buflen);
1083 /* insert character % */
1104 printf ("composing content %s/%s from command\n\t%s\n",
1105 ci->ci_type, ci->ci_subtype, buffer);
1107 fflush (stdout); /* not sure if need for -noverbose */
1114 if ((out = fopen (ce->ce_file, "w")) == NULL)
1115 adios (ce->ce_file, "unable to open for writing");
1117 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1121 adios ("fork", "unable to fork");
1126 dup2 (fileno (out), 1);
1127 close (fileno (out));
1128 execvp ("/bin/sh", vec);
1129 fprintf (stderr, "unable to exec ");
1136 if (pidXwait(child_id, NULL))
1142 /* Check size of file */
1143 if (listsw && ct->c_end == 0L) {
1146 if (stat (ce->ce_file, &st) != NOTOK)
1147 ct->c_end = (long) st.st_size;
1157 ** Scan the content.
1159 ** 1) choose a transfer encoding.
1160 ** 2) check for clashes with multipart boundary string.
1161 ** 3) for text content, figure out which character set is being used.
1163 ** If there is a clash with one of the contents and the multipart boundary,
1164 ** this function will exit with NOTOK. This will cause the scanning process
1165 ** to be repeated with a different multipart boundary. It is possible
1166 ** (although highly unlikely) that this scan will be repeated multiple times.
1170 scan_content (CT ct)
1173 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1174 int checklinelen = 0, linelen = 0; /* check for long lines */
1175 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1176 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1177 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1178 unsigned char *cp = NULL, buffer[BUFSIZ];
1179 struct text *t = NULL;
1181 CE ce = ct->c_cefile;
1184 ** handle multipart by scanning all subparts
1185 ** and then checking their encoding.
1187 if (ct->c_type == CT_MULTIPART) {
1188 struct multipart *m = (struct multipart *) ct->c_ctparams;
1191 /* initially mark the domain of enclosing multipart as 7bit */
1192 ct->c_encoding = CE_7BIT;
1194 for (part = m->mp_parts; part; part = part->mp_next) {
1195 CT p = part->mp_part;
1197 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1201 ** if necessary, enlarge encoding for enclosing
1204 if (p->c_encoding == CE_BINARY)
1205 ct->c_encoding = CE_BINARY;
1206 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1207 ct->c_encoding = CE_8BIT;
1214 ** Decide what to check while scanning this content.
1216 switch (ct->c_type) {
1220 if (ct->c_subtype == TEXT_PLAIN) {
1225 checkebcdic = ebcdicsw;
1231 case CT_APPLICATION:
1233 checkebcdic = ebcdicsw;
1245 /* don't check anything for message/external */
1246 if (ct->c_subtype == MESSAGE_EXTERNAL)
1256 ** Don't check anything for these types,
1257 ** since we are forcing use of base64.
1268 ** Scan the unencoded content
1270 if (check8bit || checklinelen || checklinespace || checkboundary) {
1271 if ((in = fopen (ce->ce_file, "r")) == NULL)
1272 adios (ce->ce_file, "unable to open for reading");
1273 len = strlen (prefix);
1275 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1277 ** Check for 8bit data.
1280 for (cp = buffer; *cp; cp++) {
1281 if (!isascii (*cp)) {
1283 /* no need to keep checking */
1287 ** Check if character is ebcdic-safe.
1288 ** We only check this if also checking
1291 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1293 /* no need to keep checking */
1300 ** Check line length.
1302 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1304 checklinelen = 0; /* no need to keep checking */
1308 ** Check if line ends with a space.
1310 if (checklinespace &&
1311 (cp = buffer + strlen (buffer) - 2) >
1312 buffer && isspace (*cp)) {
1314 /* no need to keep checking */
1319 ** Check if content contains a line that clashes
1320 ** with our standard boundary for multipart messages.
1322 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1323 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1327 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1329 /* no need to keep checking */
1338 ** Decide which transfer encoding to use.
1340 switch (ct->c_type) {
1343 ** If the text content didn't specify a character
1344 ** set, we need to figure out which one was used.
1346 t = (struct text *) ct->c_ctparams;
1347 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1348 CI ci = &ct->c_ctinfo;
1351 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1355 t->tx_charset = CHARSET_UNKNOWN;
1356 *ap = concat ("charset=", write_charset_8bit(), NULL);
1358 t->tx_charset = CHARSET_USASCII;
1359 *ap = add ("charset=us-ascii", NULL);
1362 cp = strchr(*ap++, '=');
1368 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1369 ct->c_encoding = CE_QUOTED;
1371 ct->c_encoding = CE_7BIT;
1374 case CT_APPLICATION:
1375 /* For application type, use base64, except when postscript */
1376 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1377 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1378 ? CE_QUOTED : CE_BASE64;
1380 ct->c_encoding = CE_7BIT;
1384 ct->c_encoding = CE_7BIT;
1390 /* For audio, image, and video contents, just use base64 */
1391 ct->c_encoding = CE_BASE64;
1395 return (boundaryclash ? NOTOK : OK);
1400 ** Scan the content structures, and build header
1401 ** fields that will need to be output into the
1406 build_headers (CT ct)
1408 int cc, mailbody, len;
1410 char *np, *vp, buffer[BUFSIZ];
1411 CI ci = &ct->c_ctinfo;
1414 ** If message is type multipart, then add the multipart
1415 ** boundary to the list of attribute/value pairs.
1417 if (ct->c_type == CT_MULTIPART) {
1419 static int level = 0; /* store nesting level */
1423 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1424 cp = strchr(*ap++ = add (buffer, NULL), '=');
1431 ** Skip the output of Content-Type, parameters, content
1432 ** description and disposition, and Content-ID if the
1433 ** content is of type "message" and the rfc934 compatibility
1434 ** flag is set (which means we are inside multipart/digest
1435 ** and the switch -rfc934mode was given).
1437 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1441 ** output the content type and subtype
1443 np = add (TYPE_FIELD, NULL);
1444 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1446 /* keep track of length of line */
1447 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1448 + strlen (ci->ci_subtype) + 3;
1450 mailbody = ct->c_type == CT_MESSAGE
1451 && ct->c_subtype == MESSAGE_EXTERNAL
1452 && ((struct exbody *) ct->c_ctparams)->eb_body;
1455 ** Append the attribute/value pairs to
1456 ** the end of the Content-Type line.
1458 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1459 if (mailbody && !mh_strcasecmp (*ap, "body"))
1465 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1466 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1467 vp = add ("\n\t", vp);
1473 vp = add (buffer, vp);
1478 ** Append any RFC-822 comment to the end of
1479 ** the Content-Type line.
1481 if (ci->ci_comment) {
1482 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1483 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1484 vp = add ("\n\t", vp);
1490 vp = add (buffer, vp);
1493 vp = add ("\n", vp);
1494 add_header (ct, np, vp);
1497 ** output the Content-ID, unless disabled by -nocontentid
1499 if (contentidsw && ct->c_id) {
1500 np = add (ID_FIELD, NULL);
1501 vp = concat (" ", ct->c_id, NULL);
1502 add_header (ct, np, vp);
1506 ** output the Content-Description
1509 np = add (DESCR_FIELD, NULL);
1510 vp = concat (" ", ct->c_descr, NULL);
1511 add_header (ct, np, vp);
1515 ** output the Content-Disposition
1518 np = add (DISPO_FIELD, NULL);
1519 vp = concat (" ", ct->c_dispo, NULL);
1520 add_header (ct, np, vp);
1525 ** If this is the internal content structure for a
1526 ** "message/external", then we are done with the
1527 ** headers (since it has no body).
1533 ** output the Content-MD5
1536 np = add (MD5_FIELD, NULL);
1537 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1538 add_header (ct, np, vp);
1542 ** output the Content-Transfer-Encoding
1544 switch (ct->c_encoding) {
1546 /* Nothing to output */
1548 np = add (ENCODING_FIELD, NULL);
1549 vp = concat (" ", "7bit", "\n", NULL);
1550 add_header (ct, np, vp);
1555 if (ct->c_type == CT_MESSAGE)
1556 adios (NULL, "internal error, invalid encoding");
1558 np = add (ENCODING_FIELD, NULL);
1559 vp = concat (" ", "8bit", "\n", NULL);
1560 add_header (ct, np, vp);
1564 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1565 adios (NULL, "internal error, invalid encoding");
1567 np = add (ENCODING_FIELD, NULL);
1568 vp = concat (" ", "quoted-printable", "\n", NULL);
1569 add_header (ct, np, vp);
1573 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1574 adios (NULL, "internal error, invalid encoding");
1576 np = add (ENCODING_FIELD, NULL);
1577 vp = concat (" ", "base64", "\n", NULL);
1578 add_header (ct, np, vp);
1582 if (ct->c_type == CT_MESSAGE)
1583 adios (NULL, "internal error, invalid encoding");
1585 np = add (ENCODING_FIELD, NULL);
1586 vp = concat (" ", "binary", "\n", NULL);
1587 add_header (ct, np, vp);
1591 adios (NULL, "unknown transfer encoding in content");
1596 ** Additional content specific header processing
1598 switch (ct->c_type) {
1601 struct multipart *m;
1604 m = (struct multipart *) ct->c_ctparams;
1605 for (part = m->mp_parts; part; part = part->mp_next) {
1615 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1618 e = (struct exbody *) ct->c_ctparams;
1619 build_headers (e->eb_content);
1632 static char nib2b64[0x40+1] =
1633 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1636 calculate_digest (CT ct, int asciiP)
1639 char buffer[BUFSIZ], *vp, *op;
1641 unsigned char digest[16];
1642 unsigned char outbuf[25];
1645 CE ce = ct->c_cefile;
1648 if ((in = fopen (ce->ce_file, "r")) == NULL)
1649 adios (ce->ce_file, "unable to open for reading");
1651 /* Initialize md5 context */
1652 MD5Init (&mdContext);
1654 /* calculate md5 message digest */
1656 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1659 cp = buffer + strlen (buffer) - 1;
1660 if ((c = *cp) == '\n')
1663 MD5Update (&mdContext, (unsigned char *) buffer,
1664 (unsigned int) strlen (buffer));
1667 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1670 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1671 MD5Update (&mdContext, (unsigned char *) buffer,
1675 /* md5 finalization. Write digest and zero md5 context */
1676 MD5Final (digest, &mdContext);
1681 /* print debugging info */
1685 fprintf (stderr, "MD5 digest=");
1686 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1688 fprintf (stderr, "%02x", *dp & 0xff);
1689 fprintf (stderr, "\n");
1692 /* encode the digest using base64 */
1693 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1694 cc > 0; cc -= 3, op += 4) {
1698 bits = (*dp++ & 0xff) << 16;
1700 bits |= (*dp++ & 0xff) << 8;
1702 bits |= *dp++ & 0xff;
1705 for (bp = op + 4; bp > op; bits >>= 6)
1706 *--bp = nib2b64[bits & 0x3f];
1714 /* null terminate string */
1717 /* now make copy and return string */
1718 vp = concat (" ", outbuf, "\n", NULL);