3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
11 * This code was originally part of mhn.c. I split it into
12 * a separate program (mhbuild.c) and then later split it
13 * again (mhbuildsbr.c). But the code still has some of
14 * the mhn.c code in it. This program needs additional
15 * streamlining and removal of unneeded code.
20 #include <h/signals.h>
27 #include <h/mhparse.h>
30 #ifdef HAVE_SYS_TIME_H
31 # include <sys/time.h>
42 extern int contentidsw;
45 extern int rcachesw; /* mhcachesbr.c */
46 extern int wcachesw; /* mhcachesbr.c */
49 * Directory to place tmp files. This must
50 * be set before these routines are called.
56 static char prefix[] = "----- =_aaaaaaaaaa";
60 void content_error (char *, CT, char *, ...);
63 int find_cache (CT, int, int *, char *, char *, int);
66 void free_content (CT);
67 void free_ctinfo (CT);
68 void free_encoding (CT, int);
73 CT build_mime (char *, int);
78 static int init_decoded_content (CT);
79 static char *fgetstr (char *, int, FILE *);
80 static int user_content (FILE *, char *, char *, CT *);
81 static void set_id (CT, int);
82 static int compose_content (CT);
83 static int scan_content (CT);
84 static int build_headers (CT);
85 static char *calculate_digest (CT, int);
88 static unsigned char directives_stack[32];
89 static unsigned int directives_index;
91 static int do_direct(void)
93 return directives_stack[directives_index];
96 static void directive_onoff(int onoff)
98 if (directives_index >= sizeof(directives_stack) - 1) {
99 fprintf(stderr, "mhbuild: #on/off overflow, continuing\n");
102 directives_stack[++directives_index] = onoff;
105 static void directive_init(int onoff)
107 directives_index = 0;
108 directives_stack[0] = onoff;
111 static void directive_pop(void)
113 if (directives_index > 0)
116 fprintf(stderr, "mhbuild: #pop underflow, continuing\n");
120 * Main routine for translating composition file
121 * into valid MIME message. It translates the draft
122 * into a content structure (actually a tree of content
123 * structures). This message then can be manipulated
124 * in various ways, including being output via
129 build_mime (char *infile, int directives)
132 char buf[BUFSIZ], name[NAMESZ];
139 directive_init(directives);
141 umask (~m_gmprot ());
143 /* open the composition draft */
144 if ((in = fopen (infile, "r")) == NULL)
145 adios (infile, "unable to open for reading");
148 * Allocate space for primary (outside) content
150 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
151 adios (NULL, "out of memory");
154 * Allocate structure for handling decoded content
155 * for this part. We don't really need this, but
156 * allocate it to remain consistent.
158 init_decoded_content (ct);
161 * Parse some of the header fields in the composition
162 * draft into the linked list of header fields for
163 * the new MIME message.
165 for (compnum = 1, state = FLD;;) {
166 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
172 /* abort if draft has Mime-Version header field */
173 if (!mh_strcasecmp (name, VRSN_FIELD))
174 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
176 /* abort if draft has Content-Transfer-Encoding header field */
177 if (!mh_strcasecmp (name, ENCODING_FIELD))
178 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
180 /* ignore any Content-Type fields in the header */
181 if (!mh_strcasecmp (name, TYPE_FIELD)) {
182 while (state == FLDPLUS)
183 state = m_getfld (state, name, buf, sizeof(buf), in);
187 /* get copies of the buffers */
188 np = add (name, NULL);
189 vp = add (buf, NULL);
191 /* if necessary, get rest of field */
192 while (state == FLDPLUS) {
193 state = m_getfld (state, name, buf, sizeof(buf), in);
194 vp = add (buf, vp); /* add to previous value */
197 /* Now add the header data to the list */
198 add_header (ct, np, vp);
201 /* if this wasn't the last header field, then continue */
207 adios (NULL, "draft has empty body -- no directives!");
212 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
217 adios (NULL, "message format error in component #%d", compnum);
220 adios (NULL, "getfld() returned %d", state);
226 * Now add the MIME-Version header field
227 * to the list of header fields.
229 np = add (VRSN_FIELD, NULL);
230 vp = concat (" ", VRSN_VALUE, "\n", NULL);
231 add_header (ct, np, vp);
234 * We initally assume we will find multiple contents in the
235 * draft. So create a multipart/mixed content to hold everything.
236 * We can remove this later, if it is not needed.
238 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
240 ct->c_type = CT_MULTIPART;
241 ct->c_subtype = MULTI_MIXED;
242 ct->c_file = add (infile, NULL);
244 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
245 adios (NULL, "out of memory");
246 ct->c_ctparams = (void *) m;
250 * read and parse the composition file
251 * and the directives it contains.
253 while (fgetstr (buf, sizeof(buf) - 1, in)) {
257 if (user_content (in, infile, buf, &p) == DONE) {
258 admonish (NULL, "ignoring spurious #end");
264 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
265 adios (NULL, "out of memory");
272 * close the composition draft since
273 * it's not needed any longer.
277 /* check if any contents were found */
279 adios (NULL, "no content directives found");
282 * If only one content was found, then remove and
283 * free the outer multipart content.
285 if (!m->mp_parts->mp_next) {
288 p = m->mp_parts->mp_part;
289 m->mp_parts->mp_part = NULL;
291 /* move header fields */
292 p->c_first_hf = ct->c_first_hf;
293 p->c_last_hf = ct->c_last_hf;
294 ct->c_first_hf = NULL;
295 ct->c_last_hf = NULL;
304 * Fill out, or expand directives. Parse and execute
305 * commands specified by profile composition strings.
307 compose_content (ct);
309 if ((cp = strchr(prefix, 'a')) == NULL)
310 adios (NULL, "internal error(4)");
313 * Scan the contents. Choose a transfer encoding, and
314 * check if prefix for multipart boundary clashes with
315 * any of the contents.
317 while (scan_content (ct) == NOTOK) {
322 adios (NULL, "giving up trying to find a unique delimiter string");
328 /* Build the rest of the header field structures */
336 * Set up structures for placing unencoded
337 * content when building parts.
341 init_decoded_content (CT ct)
345 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
346 adios (NULL, "out of memory");
349 ct->c_ceopenfnx = open7Bit; /* since unencoded */
350 ct->c_ceclosefnx = close_encoding;
351 ct->c_cesizefnx = NULL; /* since unencoded */
358 fgetstr (char *s, int n, FILE *stream)
364 for (ep = (cp = s) + o_n; cp < ep; ) {
367 if (!fgets (cp, n, stream))
368 return (cp != s ? s : NULL);
370 if (cp == s && *cp != '#')
373 cp += (i = strlen (cp)) - 1;
374 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
380 if (strcmp(s, "#on\n") == 0) {
382 } else if (strcmp(s, "#off\n") == 0) {
384 } else if (strcmp(s, "#pop\n") == 0) {
396 * Parse the composition draft for text and directives.
397 * Do initial setup of Content structure.
401 user_content (FILE *in, char *file, char *buf, CT *ctp)
410 struct str2init *s2i;
415 if (buf[0] == '\n' || (do_direct() && strcmp (buf, "#\n") == 0)) {
420 /* allocate basic Content structure */
421 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
422 adios (NULL, "out of memory");
425 /* allocate basic structure for handling decoded content */
426 init_decoded_content (ct);
433 * Handle inline text. Check if line
434 * is one of the following forms:
436 * 1) doesn't begin with '#' (implicit directive)
437 * 2) begins with "##" (implicit directive)
438 * 3) begins with "#<"
440 if (!do_direct() || buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
444 char content[BUFSIZ];
448 cp = m_mktemp2(NULL, invo_name, NULL, &out);
449 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
451 /* use a temp file to collect the plain text lines */
452 ce->ce_file = add (cp, NULL);
455 if (do_direct() && (buf[0] == '#' && buf[1] == '<')) {
456 strncpy (content, buf + 2, sizeof(content));
463 /* the directive is implicit */
464 strncpy (content, "text/plain", sizeof(content));
466 strncpy (buffer, (!do_direct() || buf[0] != '#') ? buf : buf + 1, sizeof(buffer));
470 if (headers >= 0 && do_direct() && uprf (buffer, DESCR_FIELD)
471 && buffer[i = strlen (DESCR_FIELD)] == ':') {
475 ct->c_descr = add (buffer + i + 1, ct->c_descr);
476 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
477 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
485 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
493 if (headers >= 0 && do_direct() && uprf (buffer, DISPO_FIELD)
494 && buffer[i = strlen (DISPO_FIELD)] == ':') {
498 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
499 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
500 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
508 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
516 if (headers != 1 || buffer[0] != '\n')
522 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
524 if (do_direct() && buffer[0] == '#') {
527 if (buffer[1] != '#')
529 for (cp = (bp = buffer) + 1; *cp; cp++)
536 ct->c_end = ftell (out);
539 /* parse content type */
540 if (get_ctinfo (content, ct, inlineD) == NOTOK)
543 for (s2i = str2cts; s2i->si_key; s2i++)
544 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
546 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
550 * check type specified (possibly implicitly)
552 switch (ct->c_type = s2i->si_val) {
554 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
555 ct->c_encoding = CE_7BIT;
560 adios (NULL, "it doesn't make sense to define an in-line %s content",
561 ct->c_type == CT_MESSAGE ? "message" : "multipart");
566 if ((ct->c_ctinitfnx = s2i->si_init))
567 (*ct->c_ctinitfnx) (ct);
572 fseek (in, pos, SEEK_SET);
577 * If we've reached this point, the next line
578 * must be some type of explicit directive.
581 /* check if directive is external-type */
582 extrnal = (buf[1] == '@');
584 /* parse directive */
585 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
588 /* check directive against the list of MIME types */
589 for (s2i = str2cts; s2i->si_key; s2i++)
590 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
594 * Check if the directive specified a valid type.
595 * This will happen if it was one of the following forms:
602 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
604 switch (ct->c_type = s2i->si_val) {
606 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
607 ci->ci_type, ci->ci_subtype);
611 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
612 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
613 ci->ci_type, ci->ci_subtype);
614 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
615 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
616 ci->ci_type, ci->ci_subtype);
619 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
620 ci->ci_type, ci->ci_subtype);
624 if ((ct->c_ctinitfnx = s2i->si_init))
625 (*ct->c_ctinitfnx) (ct);
630 * #@type/subtype (external types directive)
637 adios (NULL, "need external information for \"#@%s/%s\"",
638 ci->ci_type, ci->ci_subtype);
641 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
646 * Since we are using the current Content structure to
647 * hold information about the type of the external
648 * reference, we need to create another Content structure
649 * for the message/external-body to wrap it in.
651 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
652 adios (NULL, "out of memory");
655 if (get_ctinfo (buffer, ct, 0) == NOTOK)
657 ct->c_type = CT_MESSAGE;
658 ct->c_subtype = MESSAGE_EXTERNAL;
660 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
661 adios (NULL, "out of memory");
662 ct->c_ctparams = (void *) e;
668 if (params_external (ct, 1) == NOTOK)
674 /* Handle [file] argument */
676 /* check if specifies command to execute */
677 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
678 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
681 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
686 /* record filename of decoded contents */
687 ce->ce_file = ci->ci_magic;
688 if (access (ce->ce_file, R_OK) == NOTOK)
689 adios ("reading", "unable to access %s for", ce->ce_file);
690 if (listsw && stat (ce->ce_file, &st) != NOTOK)
691 ct->c_end = (long) st.st_size;
698 * No [file] argument, so check profile for
699 * method to compose content.
701 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
702 invo_name, ci->ci_type, ci->ci_subtype);
703 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
704 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
705 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
706 content_error (NULL, ct, "don't know how to compose content");
710 ci->ci_magic = add (cp, NULL);
715 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
719 * #forw [+folder] [msgs]
721 if (!mh_strcasecmp (ci->ci_type, "forw")) {
723 char *folder, *arguments[MAXARGS];
727 ap = brkstring (ci->ci_magic, " ", "\n");
728 copyip (ap, arguments, MAXARGS);
730 arguments[0] = "cur";
735 /* search the arguments for a folder name */
736 for (ap = arguments; *ap; ap++) {
738 if (*cp == '+' || *cp == '@') {
740 adios (NULL, "only one folder per #forw directive");
742 folder = pluspath (cp);
746 /* else, use the current folder */
748 folder = add (getfolder (1), NULL);
750 if (!(mp = folder_read (folder)))
751 adios (NULL, "unable to read folder %s", folder);
752 for (ap = arguments; *ap; ap++) {
754 if (*cp != '+' && *cp != '@')
755 if (!m_convert (mp, cp))
762 * If there is more than one message to include, make this
763 * a content of type "multipart/digest" and insert each message
764 * as a subpart. If there is only one message, then make this
765 * a content of type "message/rfc822".
767 if (mp->numsel > 1) {
768 /* we are forwarding multiple messages */
769 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
771 ct->c_type = CT_MULTIPART;
772 ct->c_subtype = MULTI_DIGEST;
774 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
775 adios (NULL, "out of memory");
776 ct->c_ctparams = (void *) m;
779 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
780 if (is_selected(mp, msgnum)) {
785 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
786 adios (NULL, "out of memory");
787 init_decoded_content (p);
789 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
791 p->c_type = CT_MESSAGE;
792 p->c_subtype = MESSAGE_RFC822;
794 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
795 pe->ce_file = add (buffer, NULL);
796 if (listsw && stat (pe->ce_file, &st) != NOTOK)
797 p->c_end = (long) st.st_size;
799 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
800 adios (NULL, "out of memory");
807 /* we are forwarding one message */
808 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
810 ct->c_type = CT_MESSAGE;
811 ct->c_subtype = MESSAGE_RFC822;
814 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
815 ce->ce_file = add (buffer, NULL);
816 if (listsw && stat (ce->ce_file, &st) != NOTOK)
817 ct->c_end = (long) st.st_size;
820 folder_free (mp); /* free folder/message structure */
827 if (!mh_strcasecmp (ci->ci_type, "end")) {
834 * #begin [ alternative | parallel ]
836 if (!mh_strcasecmp (ci->ci_type, "begin")) {
839 cp = SubMultiPart[vrsn - 1].kv_key;
840 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
841 vrsn = MULTI_ALTERNATE;
842 cp = SubMultiPart[vrsn - 1].kv_key;
843 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
844 vrsn = MULTI_PARALLEL;
845 cp = SubMultiPart[vrsn - 1].kv_key;
846 } else if (uprf (ci->ci_magic, "digest")) {
849 vrsn = MULTI_UNKNOWN;
854 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
855 if (get_ctinfo (buffer, ct, 0) == NOTOK)
857 ct->c_type = CT_MULTIPART;
858 ct->c_subtype = vrsn;
860 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
861 adios (NULL, "out of memory");
862 ct->c_ctparams = (void *) m;
865 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
869 if (user_content (in, file, buffer, &p) == DONE) {
871 adios (NULL, "empty \"#begin ... #end\" sequence");
877 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
878 adios (NULL, "out of memory");
883 admonish (NULL, "premature end-of-file, missing #end");
890 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
891 return NOTOK; /* NOT REACHED */
896 set_id (CT ct, int top)
898 char contentid[BUFSIZ];
900 static time_t clock = 0;
905 snprintf (contentid, sizeof(contentid), "%s\n", message_id (clock, 1));
907 msgfmt = getcpy(contentid);
909 snprintf (contentid, sizeof(contentid), msgfmt, top ? 0 : ++partno);
910 ct->c_id = getcpy (contentid);
914 static char ebcdicsafe[0x100] = {
915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
916 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
919 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
920 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
921 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
922 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
923 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
924 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
925 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
926 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
927 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
928 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
929 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
930 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
951 * Fill out, or expand the various contents in the composition
952 * draft. Read-in any necessary files. Parse and execute any
953 * commands specified by profile composition strings.
957 compose_content (CT ct)
959 CE ce = ct->c_cefile;
961 switch (ct->c_type) {
966 char partnam[BUFSIZ];
967 struct multipart *m = (struct multipart *) ct->c_ctparams;
971 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
972 pp = partnam + strlen (partnam);
977 /* first, we call compose_content on all the subparts */
978 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
979 CT p = part->mp_part;
981 sprintf (pp, "%d", partnum);
982 p->c_partno = add (partnam, NULL);
983 if (compose_content (p) == NOTOK)
988 * If the -rfc934mode switch is given, then check all
989 * the subparts of a multipart/digest. If they are all
990 * message/rfc822, then mark this content and all
991 * subparts with the rfc934 compatibility mode flag.
993 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
996 for (part = m->mp_parts; part; part = part->mp_next) {
997 CT p = part->mp_part;
999 if (p->c_subtype != MESSAGE_RFC822) {
1004 ct->c_rfc934 = is934;
1005 for (part = m->mp_parts; part; part = part->mp_next) {
1006 CT p = part->mp_part;
1008 if ((p->c_rfc934 = is934))
1014 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
1018 for (part = m->mp_parts; part; part = part->mp_next)
1019 ct->c_end += part->mp_part->c_end + partnum;
1025 /* Nothing to do for type message */
1029 * Discrete types (text/application/audio/image/video)
1034 int i, xstdout, len, buflen;
1035 char *bp, **ap, *cp;
1036 char *vec[4], buffer[BUFSIZ];
1038 CI ci = &ct->c_ctinfo;
1041 if (!(cp = ci->ci_magic))
1042 adios (NULL, "internal error(5)");
1044 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1045 if (tfile == NULL) {
1046 adios("mhbuildsbr", "unable to create temporary file");
1048 ce->ce_file = add (tfile, NULL);
1053 /* Get buffer ready to go */
1056 buflen = sizeof(buffer);
1059 * Parse composition string into buffer
1061 for ( ; *cp; cp++) {
1066 /* insert parameters from directive */
1070 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1071 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1081 /* %f, and stdout is not-redirected */
1087 * insert temporary filename where
1088 * content should be written
1090 snprintf (bp, buflen, "%s", ce->ce_file);
1094 /* insert content subtype */
1095 strncpy (bp, ci->ci_subtype, buflen);
1099 /* insert character % */
1120 printf ("composing content %s/%s from command\n\t%s\n",
1121 ci->ci_type, ci->ci_subtype, buffer);
1123 fflush (stdout); /* not sure if need for -noverbose */
1130 if ((out = fopen (ce->ce_file, "w")) == NULL)
1131 adios (ce->ce_file, "unable to open for writing");
1133 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1137 adios ("fork", "unable to fork");
1142 dup2 (fileno (out), 1);
1143 close (fileno (out));
1144 execvp ("/bin/sh", vec);
1145 fprintf (stderr, "unable to exec ");
1152 if (pidXwait(child_id, NULL))
1158 /* Check size of file */
1159 if (listsw && ct->c_end == 0L) {
1162 if (stat (ce->ce_file, &st) != NOTOK)
1163 ct->c_end = (long) st.st_size;
1175 * 1) choose a transfer encoding.
1176 * 2) check for clashes with multipart boundary string.
1177 * 3) for text content, figure out which character set is being used.
1179 * If there is a clash with one of the contents and the multipart boundary,
1180 * this function will exit with NOTOK. This will cause the scanning process
1181 * to be repeated with a different multipart boundary. It is possible
1182 * (although highly unlikely) that this scan will be repeated multiple times.
1186 scan_content (CT ct)
1189 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1190 int checklinelen = 0, linelen = 0; /* check for long lines */
1191 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1192 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1193 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1194 unsigned char *cp = NULL, buffer[BUFSIZ];
1195 struct text *t = NULL;
1197 CE ce = ct->c_cefile;
1200 * handle multipart by scanning all subparts
1201 * and then checking their encoding.
1203 if (ct->c_type == CT_MULTIPART) {
1204 struct multipart *m = (struct multipart *) ct->c_ctparams;
1207 /* initially mark the domain of enclosing multipart as 7bit */
1208 ct->c_encoding = CE_7BIT;
1210 for (part = m->mp_parts; part; part = part->mp_next) {
1211 CT p = part->mp_part;
1213 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1216 /* if necessary, enlarge encoding for enclosing multipart */
1217 if (p->c_encoding == CE_BINARY)
1218 ct->c_encoding = CE_BINARY;
1219 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1220 ct->c_encoding = CE_8BIT;
1227 * Decide what to check while scanning this content.
1229 switch (ct->c_type) {
1233 if (ct->c_subtype == TEXT_PLAIN) {
1238 checkebcdic = ebcdicsw;
1244 case CT_APPLICATION:
1246 checkebcdic = ebcdicsw;
1258 /* don't check anything for message/external */
1259 if (ct->c_subtype == MESSAGE_EXTERNAL)
1269 * Don't check anything for these types,
1270 * since we are forcing use of base64.
1281 * Scan the unencoded content
1283 if (check8bit || checklinelen || checklinespace || checkboundary) {
1284 if ((in = fopen (ce->ce_file, "r")) == NULL)
1285 adios (ce->ce_file, "unable to open for reading");
1286 len = strlen (prefix);
1288 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1290 * Check for 8bit data.
1293 for (cp = buffer; *cp; cp++) {
1294 if (!isascii (*cp)) {
1296 check8bit = 0; /* no need to keep checking */
1299 * Check if character is ebcdic-safe. We only check
1300 * this if also checking for 8bit data.
1302 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1304 checkebcdic = 0; /* no need to keep checking */
1310 * Check line length.
1312 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1314 checklinelen = 0; /* no need to keep checking */
1318 * Check if line ends with a space.
1320 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1322 checklinespace = 0; /* no need to keep checking */
1326 * Check if content contains a line that clashes
1327 * with our standard boundary for multipart messages.
1329 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1330 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1334 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1336 checkboundary = 0; /* no need to keep checking */
1344 * Decide which transfer encoding to use.
1346 switch (ct->c_type) {
1349 * If the text content didn't specify a character
1350 * set, we need to figure out which one was used.
1352 t = (struct text *) ct->c_ctparams;
1353 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1354 CI ci = &ct->c_ctinfo;
1357 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1361 t->tx_charset = CHARSET_UNKNOWN;
1362 *ap = concat ("charset=", write_charset_8bit(), NULL);
1364 t->tx_charset = CHARSET_USASCII;
1365 *ap = add ("charset=us-ascii", NULL);
1368 cp = strchr(*ap++, '=');
1374 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1375 ct->c_encoding = CE_QUOTED;
1377 ct->c_encoding = CE_7BIT;
1380 case CT_APPLICATION:
1381 /* For application type, use base64, except when postscript */
1382 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1383 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1384 ? CE_QUOTED : CE_BASE64;
1386 ct->c_encoding = CE_7BIT;
1390 ct->c_encoding = CE_7BIT;
1396 /* For audio, image, and video contents, just use base64 */
1397 ct->c_encoding = CE_BASE64;
1401 return (boundaryclash ? NOTOK : OK);
1406 * Scan the content structures, and build header
1407 * fields that will need to be output into the
1412 build_headers (CT ct)
1414 int cc, mailbody, len;
1416 char *np, *vp, buffer[BUFSIZ];
1417 CI ci = &ct->c_ctinfo;
1420 * If message is type multipart, then add the multipart
1421 * boundary to the list of attribute/value pairs.
1423 if (ct->c_type == CT_MULTIPART) {
1425 static int level = 0; /* store nesting level */
1429 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1430 cp = strchr(*ap++ = add (buffer, NULL), '=');
1437 * Skip the output of Content-Type, parameters, content
1438 * description and disposition, and Content-ID if the
1439 * content is of type "message" and the rfc934 compatibility
1440 * flag is set (which means we are inside multipart/digest
1441 * and the switch -rfc934mode was given).
1443 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1447 * output the content type and subtype
1449 np = add (TYPE_FIELD, NULL);
1450 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1452 /* keep track of length of line */
1453 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1454 + strlen (ci->ci_subtype) + 3;
1456 mailbody = ct->c_type == CT_MESSAGE
1457 && ct->c_subtype == MESSAGE_EXTERNAL
1458 && ((struct exbody *) ct->c_ctparams)->eb_body;
1461 * Append the attribute/value pairs to
1462 * the end of the Content-Type line.
1464 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1465 if (mailbody && !mh_strcasecmp (*ap, "body"))
1471 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1472 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1473 vp = add ("\n\t", vp);
1479 vp = add (buffer, vp);
1484 * Append any RFC-822 comment to the end of
1485 * the Content-Type line.
1487 if (ci->ci_comment) {
1488 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1489 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1490 vp = add ("\n\t", vp);
1496 vp = add (buffer, vp);
1499 vp = add ("\n", vp);
1500 add_header (ct, np, vp);
1503 * output the Content-ID, unless disabled by -nocontentid
1505 if (contentidsw && ct->c_id) {
1506 np = add (ID_FIELD, NULL);
1507 vp = concat (" ", ct->c_id, NULL);
1508 add_header (ct, np, vp);
1512 * output the Content-Description
1515 np = add (DESCR_FIELD, NULL);
1516 vp = concat (" ", ct->c_descr, NULL);
1517 add_header (ct, np, vp);
1521 * output the Content-Disposition
1524 np = add (DISPO_FIELD, NULL);
1525 vp = concat (" ", ct->c_dispo, NULL);
1526 add_header (ct, np, vp);
1531 * If this is the internal content structure for a
1532 * "message/external", then we are done with the
1533 * headers (since it has no body).
1539 * output the Content-MD5
1542 np = add (MD5_FIELD, NULL);
1543 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1544 add_header (ct, np, vp);
1548 * output the Content-Transfer-Encoding
1550 switch (ct->c_encoding) {
1552 /* Nothing to output */
1554 np = add (ENCODING_FIELD, NULL);
1555 vp = concat (" ", "7bit", "\n", NULL);
1556 add_header (ct, np, vp);
1561 if (ct->c_type == CT_MESSAGE)
1562 adios (NULL, "internal error, invalid encoding");
1564 np = add (ENCODING_FIELD, NULL);
1565 vp = concat (" ", "8bit", "\n", NULL);
1566 add_header (ct, np, vp);
1570 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1571 adios (NULL, "internal error, invalid encoding");
1573 np = add (ENCODING_FIELD, NULL);
1574 vp = concat (" ", "quoted-printable", "\n", NULL);
1575 add_header (ct, np, vp);
1579 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1580 adios (NULL, "internal error, invalid encoding");
1582 np = add (ENCODING_FIELD, NULL);
1583 vp = concat (" ", "base64", "\n", NULL);
1584 add_header (ct, np, vp);
1588 if (ct->c_type == CT_MESSAGE)
1589 adios (NULL, "internal error, invalid encoding");
1591 np = add (ENCODING_FIELD, NULL);
1592 vp = concat (" ", "binary", "\n", NULL);
1593 add_header (ct, np, vp);
1597 adios (NULL, "unknown transfer encoding in content");
1602 * Additional content specific header processing
1604 switch (ct->c_type) {
1607 struct multipart *m;
1610 m = (struct multipart *) ct->c_ctparams;
1611 for (part = m->mp_parts; part; part = part->mp_next) {
1621 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1624 e = (struct exbody *) ct->c_ctparams;
1625 build_headers (e->eb_content);
1638 static char nib2b64[0x40+1] =
1639 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1642 calculate_digest (CT ct, int asciiP)
1645 char buffer[BUFSIZ], *vp, *op;
1647 unsigned char digest[16];
1648 unsigned char outbuf[25];
1650 CE ce = ct->c_cefile;
1651 char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
1655 if ((in = fopen (infilename, "r")) == NULL)
1656 adios (infilename, "unable to open for reading");
1658 /* Initialize md5 context */
1659 MD5Init (&mdContext);
1661 /* calculate md5 message digest */
1663 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1666 cp = buffer + strlen (buffer) - 1;
1667 if ((c = *cp) == '\n')
1670 MD5Update (&mdContext, (unsigned char *) buffer,
1671 (unsigned int) strlen (buffer));
1674 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1677 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1678 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1681 /* md5 finalization. Write digest and zero md5 context */
1682 MD5Final (digest, &mdContext);
1687 /* print debugging info */
1691 fprintf (stderr, "MD5 digest=");
1692 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1694 fprintf (stderr, "%02x", *dp & 0xff);
1695 fprintf (stderr, "\n");
1698 /* encode the digest using base64 */
1699 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1700 cc > 0; cc -= 3, op += 4) {
1704 bits = (*dp++ & 0xff) << 16;
1706 bits |= (*dp++ & 0xff) << 8;
1708 bits |= *dp++ & 0xff;
1711 for (bp = op + 4; bp > op; bits >>= 6)
1712 *--bp = nib2b64[bits & 0x3f];
1720 /* null terminate string */
1723 /* now make copy and return string */
1724 vp = concat (" ", outbuf, "\n", NULL);