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)
900 static time_t clock = 0;
905 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
906 (int) getpid(), (long) clock, LocalName(1));
908 msgfmt = getcpy(msgid);
910 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
911 ct->c_id = getcpy (msgid);
915 static char ebcdicsafe[0x100] = {
916 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
917 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
919 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
920 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
921 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
922 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
923 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
924 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
925 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
926 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
927 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
928 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
929 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
930 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
931 0x01, 0x01, 0x01, 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,
947 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
952 * Fill out, or expand the various contents in the composition
953 * draft. Read-in any necessary files. Parse and execute any
954 * commands specified by profile composition strings.
958 compose_content (CT ct)
960 CE ce = ct->c_cefile;
962 switch (ct->c_type) {
967 char partnam[BUFSIZ];
968 struct multipart *m = (struct multipart *) ct->c_ctparams;
972 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
973 pp = partnam + strlen (partnam);
978 /* first, we call compose_content on all the subparts */
979 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
980 CT p = part->mp_part;
982 sprintf (pp, "%d", partnum);
983 p->c_partno = add (partnam, NULL);
984 if (compose_content (p) == NOTOK)
989 * If the -rfc934mode switch is given, then check all
990 * the subparts of a multipart/digest. If they are all
991 * message/rfc822, then mark this content and all
992 * subparts with the rfc934 compatibility mode flag.
994 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
997 for (part = m->mp_parts; part; part = part->mp_next) {
998 CT p = part->mp_part;
1000 if (p->c_subtype != MESSAGE_RFC822) {
1005 ct->c_rfc934 = is934;
1006 for (part = m->mp_parts; part; part = part->mp_next) {
1007 CT p = part->mp_part;
1009 if ((p->c_rfc934 = is934))
1015 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
1019 for (part = m->mp_parts; part; part = part->mp_next)
1020 ct->c_end += part->mp_part->c_end + partnum;
1026 /* Nothing to do for type message */
1030 * Discrete types (text/application/audio/image/video)
1035 int i, xstdout, len, buflen;
1036 char *bp, **ap, *cp;
1037 char *vec[4], buffer[BUFSIZ];
1039 CI ci = &ct->c_ctinfo;
1042 if (!(cp = ci->ci_magic))
1043 adios (NULL, "internal error(5)");
1045 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1046 if (tfile == NULL) {
1047 adios("mhbuildsbr", "unable to create temporary file");
1049 ce->ce_file = add (tfile, NULL);
1054 /* Get buffer ready to go */
1057 buflen = sizeof(buffer);
1060 * Parse composition string into buffer
1062 for ( ; *cp; cp++) {
1067 /* insert parameters from directive */
1071 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1072 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1082 /* %f, and stdout is not-redirected */
1088 * insert temporary filename where
1089 * content should be written
1091 snprintf (bp, buflen, "%s", ce->ce_file);
1095 /* insert content subtype */
1096 strncpy (bp, ci->ci_subtype, buflen);
1100 /* insert character % */
1121 printf ("composing content %s/%s from command\n\t%s\n",
1122 ci->ci_type, ci->ci_subtype, buffer);
1124 fflush (stdout); /* not sure if need for -noverbose */
1131 if ((out = fopen (ce->ce_file, "w")) == NULL)
1132 adios (ce->ce_file, "unable to open for writing");
1134 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1138 adios ("fork", "unable to fork");
1143 dup2 (fileno (out), 1);
1144 close (fileno (out));
1145 execvp ("/bin/sh", vec);
1146 fprintf (stderr, "unable to exec ");
1153 if (pidXwait(child_id, NULL))
1159 /* Check size of file */
1160 if (listsw && ct->c_end == 0L) {
1163 if (stat (ce->ce_file, &st) != NOTOK)
1164 ct->c_end = (long) st.st_size;
1176 * 1) choose a transfer encoding.
1177 * 2) check for clashes with multipart boundary string.
1178 * 3) for text content, figure out which character set is being used.
1180 * If there is a clash with one of the contents and the multipart boundary,
1181 * this function will exit with NOTOK. This will cause the scanning process
1182 * to be repeated with a different multipart boundary. It is possible
1183 * (although highly unlikely) that this scan will be repeated multiple times.
1187 scan_content (CT ct)
1190 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1191 int checklinelen = 0, linelen = 0; /* check for long lines */
1192 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1193 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1194 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1195 unsigned char *cp = NULL, buffer[BUFSIZ];
1196 struct text *t = NULL;
1198 CE ce = ct->c_cefile;
1201 * handle multipart by scanning all subparts
1202 * and then checking their encoding.
1204 if (ct->c_type == CT_MULTIPART) {
1205 struct multipart *m = (struct multipart *) ct->c_ctparams;
1208 /* initially mark the domain of enclosing multipart as 7bit */
1209 ct->c_encoding = CE_7BIT;
1211 for (part = m->mp_parts; part; part = part->mp_next) {
1212 CT p = part->mp_part;
1214 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1217 /* if necessary, enlarge encoding for enclosing multipart */
1218 if (p->c_encoding == CE_BINARY)
1219 ct->c_encoding = CE_BINARY;
1220 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1221 ct->c_encoding = CE_8BIT;
1228 * Decide what to check while scanning this content.
1230 switch (ct->c_type) {
1234 if (ct->c_subtype == TEXT_PLAIN) {
1239 checkebcdic = ebcdicsw;
1245 case CT_APPLICATION:
1247 checkebcdic = ebcdicsw;
1259 /* don't check anything for message/external */
1260 if (ct->c_subtype == MESSAGE_EXTERNAL)
1270 * Don't check anything for these types,
1271 * since we are forcing use of base64.
1282 * Scan the unencoded content
1284 if (check8bit || checklinelen || checklinespace || checkboundary) {
1285 if ((in = fopen (ce->ce_file, "r")) == NULL)
1286 adios (ce->ce_file, "unable to open for reading");
1287 len = strlen (prefix);
1289 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1291 * Check for 8bit data.
1294 for (cp = buffer; *cp; cp++) {
1295 if (!isascii (*cp)) {
1297 check8bit = 0; /* no need to keep checking */
1300 * Check if character is ebcdic-safe. We only check
1301 * this if also checking for 8bit data.
1303 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1305 checkebcdic = 0; /* no need to keep checking */
1311 * Check line length.
1313 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1315 checklinelen = 0; /* no need to keep checking */
1319 * Check if line ends with a space.
1321 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1323 checklinespace = 0; /* no need to keep checking */
1327 * Check if content contains a line that clashes
1328 * with our standard boundary for multipart messages.
1330 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1331 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1335 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1337 checkboundary = 0; /* no need to keep checking */
1345 * Decide which transfer encoding to use.
1347 switch (ct->c_type) {
1350 * If the text content didn't specify a character
1351 * set, we need to figure out which one was used.
1353 t = (struct text *) ct->c_ctparams;
1354 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1355 CI ci = &ct->c_ctinfo;
1358 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1362 t->tx_charset = CHARSET_UNKNOWN;
1363 *ap = concat ("charset=", write_charset_8bit(), NULL);
1365 t->tx_charset = CHARSET_USASCII;
1366 *ap = add ("charset=us-ascii", NULL);
1369 cp = strchr(*ap++, '=');
1375 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1376 ct->c_encoding = CE_QUOTED;
1378 ct->c_encoding = CE_7BIT;
1381 case CT_APPLICATION:
1382 /* For application type, use base64, except when postscript */
1383 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1384 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1385 ? CE_QUOTED : CE_BASE64;
1387 ct->c_encoding = CE_7BIT;
1391 ct->c_encoding = CE_7BIT;
1397 /* For audio, image, and video contents, just use base64 */
1398 ct->c_encoding = CE_BASE64;
1402 return (boundaryclash ? NOTOK : OK);
1407 * Scan the content structures, and build header
1408 * fields that will need to be output into the
1413 build_headers (CT ct)
1415 int cc, mailbody, len;
1417 char *np, *vp, buffer[BUFSIZ];
1418 CI ci = &ct->c_ctinfo;
1421 * If message is type multipart, then add the multipart
1422 * boundary to the list of attribute/value pairs.
1424 if (ct->c_type == CT_MULTIPART) {
1426 static int level = 0; /* store nesting level */
1430 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1431 cp = strchr(*ap++ = add (buffer, NULL), '=');
1438 * Skip the output of Content-Type, parameters, content
1439 * description and disposition, and Content-ID if the
1440 * content is of type "message" and the rfc934 compatibility
1441 * flag is set (which means we are inside multipart/digest
1442 * and the switch -rfc934mode was given).
1444 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1448 * output the content type and subtype
1450 np = add (TYPE_FIELD, NULL);
1451 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1453 /* keep track of length of line */
1454 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1455 + strlen (ci->ci_subtype) + 3;
1457 mailbody = ct->c_type == CT_MESSAGE
1458 && ct->c_subtype == MESSAGE_EXTERNAL
1459 && ((struct exbody *) ct->c_ctparams)->eb_body;
1462 * Append the attribute/value pairs to
1463 * the end of the Content-Type line.
1465 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1466 if (mailbody && !mh_strcasecmp (*ap, "body"))
1472 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1473 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1474 vp = add ("\n\t", vp);
1480 vp = add (buffer, vp);
1485 * Append any RFC-822 comment to the end of
1486 * the Content-Type line.
1488 if (ci->ci_comment) {
1489 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1490 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1491 vp = add ("\n\t", vp);
1497 vp = add (buffer, vp);
1500 vp = add ("\n", vp);
1501 add_header (ct, np, vp);
1504 * output the Content-ID, unless disabled by -nocontentid
1506 if (contentidsw && ct->c_id) {
1507 np = add (ID_FIELD, NULL);
1508 vp = concat (" ", ct->c_id, NULL);
1509 add_header (ct, np, vp);
1513 * output the Content-Description
1516 np = add (DESCR_FIELD, NULL);
1517 vp = concat (" ", ct->c_descr, NULL);
1518 add_header (ct, np, vp);
1522 * output the Content-Disposition
1525 np = add (DISPO_FIELD, NULL);
1526 vp = concat (" ", ct->c_dispo, NULL);
1527 add_header (ct, np, vp);
1532 * If this is the internal content structure for a
1533 * "message/external", then we are done with the
1534 * headers (since it has no body).
1540 * output the Content-MD5
1543 np = add (MD5_FIELD, NULL);
1544 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1545 add_header (ct, np, vp);
1549 * output the Content-Transfer-Encoding
1551 switch (ct->c_encoding) {
1553 /* Nothing to output */
1555 np = add (ENCODING_FIELD, NULL);
1556 vp = concat (" ", "7bit", "\n", NULL);
1557 add_header (ct, np, vp);
1562 if (ct->c_type == CT_MESSAGE)
1563 adios (NULL, "internal error, invalid encoding");
1565 np = add (ENCODING_FIELD, NULL);
1566 vp = concat (" ", "8bit", "\n", NULL);
1567 add_header (ct, np, vp);
1571 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1572 adios (NULL, "internal error, invalid encoding");
1574 np = add (ENCODING_FIELD, NULL);
1575 vp = concat (" ", "quoted-printable", "\n", NULL);
1576 add_header (ct, np, vp);
1580 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1581 adios (NULL, "internal error, invalid encoding");
1583 np = add (ENCODING_FIELD, NULL);
1584 vp = concat (" ", "base64", "\n", NULL);
1585 add_header (ct, np, vp);
1589 if (ct->c_type == CT_MESSAGE)
1590 adios (NULL, "internal error, invalid encoding");
1592 np = add (ENCODING_FIELD, NULL);
1593 vp = concat (" ", "binary", "\n", NULL);
1594 add_header (ct, np, vp);
1598 adios (NULL, "unknown transfer encoding in content");
1603 * Additional content specific header processing
1605 switch (ct->c_type) {
1608 struct multipart *m;
1611 m = (struct multipart *) ct->c_ctparams;
1612 for (part = m->mp_parts; part; part = part->mp_next) {
1622 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1625 e = (struct exbody *) ct->c_ctparams;
1626 build_headers (e->eb_content);
1639 static char nib2b64[0x40+1] =
1640 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1643 calculate_digest (CT ct, int asciiP)
1646 char buffer[BUFSIZ], *vp, *op;
1648 unsigned char digest[16];
1649 unsigned char outbuf[25];
1651 CE ce = ct->c_cefile;
1652 char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
1656 if ((in = fopen (infilename, "r")) == NULL)
1657 adios (infilename, "unable to open for reading");
1659 /* Initialize md5 context */
1660 MD5Init (&mdContext);
1662 /* calculate md5 message digest */
1664 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1667 cp = buffer + strlen (buffer) - 1;
1668 if ((c = *cp) == '\n')
1671 MD5Update (&mdContext, (unsigned char *) buffer,
1672 (unsigned int) strlen (buffer));
1675 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1678 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1679 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1682 /* md5 finalization. Write digest and zero md5 context */
1683 MD5Final (digest, &mdContext);
1688 /* print debugging info */
1692 fprintf (stderr, "MD5 digest=");
1693 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1695 fprintf (stderr, "%02x", *dp & 0xff);
1696 fprintf (stderr, "\n");
1699 /* encode the digest using base64 */
1700 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1701 cc > 0; cc -= 3, op += 4) {
1705 bits = (*dp++ & 0xff) << 16;
1707 bits |= (*dp++ & 0xff) << 8;
1709 bits |= *dp++ & 0xff;
1712 for (bp = op + 4; bp > op; bits >>= 6)
1713 *--bp = nib2b64[bits & 0x3f];
1721 /* null terminate string */
1724 /* now make copy and return string */
1725 vp = concat (" ", outbuf, "\n", NULL);