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;
44 extern int endian; /* mhmisc.c */
47 extern int rcachesw; /* mhcachesbr.c */
48 extern int wcachesw; /* mhcachesbr.c */
51 * Directory to place tmp files. This must
52 * be set before these routines are called.
58 static char prefix[] = "----- =_aaaaaaaaaa";
62 void content_error (char *, CT, char *, ...);
65 int find_cache (CT, int, int *, char *, char *, int);
68 void free_content (CT);
69 void free_ctinfo (CT);
70 void free_encoding (CT, int);
75 CT build_mime (char *, int);
80 static int init_decoded_content (CT);
81 static char *fgetstr (char *, int, FILE *);
82 static int user_content (FILE *, char *, char *, CT *);
83 static void set_id (CT, int);
84 static int compose_content (CT);
85 static int scan_content (CT);
86 static int build_headers (CT);
87 static char *calculate_digest (CT, int);
90 static unsigned char directives_stack[32];
91 static unsigned int directives_index;
93 static int do_direct(void)
95 return directives_stack[directives_index];
98 static void directive_onoff(int onoff)
100 if (directives_index >= sizeof(directives_stack) - 1) {
101 fprintf(stderr, "mhbuild: #on/off overflow, continuing\n");
104 directives_stack[++directives_index] = onoff;
107 static void directive_init(int onoff)
109 directives_index = 0;
110 directives_stack[0] = onoff;
113 static void directive_pop(void)
115 if (directives_index > 0)
118 fprintf(stderr, "mhbuild: #pop underflow, continuing\n");
122 * Main routine for translating composition file
123 * into valid MIME message. It translates the draft
124 * into a content structure (actually a tree of content
125 * structures). This message then can be manipulated
126 * in various ways, including being output via
131 build_mime (char *infile, int directives)
134 char buf[BUFSIZ], name[NAMESZ];
141 directive_init(directives);
143 umask (~m_gmprot ());
145 /* open the composition draft */
146 if ((in = fopen (infile, "r")) == NULL)
147 adios (infile, "unable to open for reading");
150 * Allocate space for primary (outside) content
152 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
153 adios (NULL, "out of memory");
156 * Allocate structure for handling decoded content
157 * for this part. We don't really need this, but
158 * allocate it to remain consistent.
160 init_decoded_content (ct);
163 * Parse some of the header fields in the composition
164 * draft into the linked list of header fields for
165 * the new MIME message.
167 for (compnum = 1, state = FLD;;) {
168 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
174 /* abort if draft has Mime-Version header field */
175 if (!mh_strcasecmp (name, VRSN_FIELD))
176 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
178 /* abort if draft has Content-Transfer-Encoding header field */
179 if (!mh_strcasecmp (name, ENCODING_FIELD))
180 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
182 /* ignore any Content-Type fields in the header */
183 if (!mh_strcasecmp (name, TYPE_FIELD)) {
184 while (state == FLDPLUS)
185 state = m_getfld (state, name, buf, sizeof(buf), in);
189 /* get copies of the buffers */
190 np = add (name, NULL);
191 vp = add (buf, NULL);
193 /* if necessary, get rest of field */
194 while (state == FLDPLUS) {
195 state = m_getfld (state, name, buf, sizeof(buf), in);
196 vp = add (buf, vp); /* add to previous value */
199 /* Now add the header data to the list */
200 add_header (ct, np, vp);
203 /* if this wasn't the last header field, then continue */
209 adios (NULL, "draft has empty body -- no directives!");
214 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
219 adios (NULL, "message format error in component #%d", compnum);
222 adios (NULL, "getfld() returned %d", state);
228 * Now add the MIME-Version header field
229 * to the list of header fields.
231 np = add (VRSN_FIELD, NULL);
232 vp = concat (" ", VRSN_VALUE, "\n", NULL);
233 add_header (ct, np, vp);
236 * We initally assume we will find multiple contents in the
237 * draft. So create a multipart/mixed content to hold everything.
238 * We can remove this later, if it is not needed.
240 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
242 ct->c_type = CT_MULTIPART;
243 ct->c_subtype = MULTI_MIXED;
244 ct->c_file = add (infile, NULL);
246 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
247 adios (NULL, "out of memory");
248 ct->c_ctparams = (void *) m;
252 * read and parse the composition file
253 * and the directives it contains.
255 while (fgetstr (buf, sizeof(buf) - 1, in)) {
259 if (user_content (in, infile, buf, &p) == DONE) {
260 admonish (NULL, "ignoring spurious #end");
266 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
267 adios (NULL, "out of memory");
274 * close the composition draft since
275 * it's not needed any longer.
279 /* check if any contents were found */
281 adios (NULL, "no content directives found");
284 * If only one content was found, then remove and
285 * free the outer multipart content.
287 if (!m->mp_parts->mp_next) {
290 p = m->mp_parts->mp_part;
291 m->mp_parts->mp_part = NULL;
293 /* move header fields */
294 p->c_first_hf = ct->c_first_hf;
295 p->c_last_hf = ct->c_last_hf;
296 ct->c_first_hf = NULL;
297 ct->c_last_hf = NULL;
306 * Fill out, or expand directives. Parse and execute
307 * commands specified by profile composition strings.
309 compose_content (ct);
311 if ((cp = strchr(prefix, 'a')) == NULL)
312 adios (NULL, "internal error(4)");
315 * Scan the contents. Choose a transfer encoding, and
316 * check if prefix for multipart boundary clashes with
317 * any of the contents.
319 while (scan_content (ct) == NOTOK) {
324 adios (NULL, "giving up trying to find a unique delimiter string");
330 /* Build the rest of the header field structures */
338 * Set up structures for placing unencoded
339 * content when building parts.
343 init_decoded_content (CT ct)
347 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
348 adios (NULL, "out of memory");
351 ct->c_ceopenfnx = open7Bit; /* since unencoded */
352 ct->c_ceclosefnx = close_encoding;
353 ct->c_cesizefnx = NULL; /* since unencoded */
360 fgetstr (char *s, int n, FILE *stream)
366 for (ep = (cp = s) + o_n; cp < ep; ) {
369 if (!fgets (cp, n, stream))
370 return (cp != s ? s : NULL);
372 if (cp == s && *cp != '#')
375 cp += (i = strlen (cp)) - 1;
376 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
382 if (strcmp(s, "#on\n") == 0) {
384 } else if (strcmp(s, "#off\n") == 0) {
386 } else if (strcmp(s, "#pop\n") == 0) {
398 * Parse the composition draft for text and directives.
399 * Do initial setup of Content structure.
403 user_content (FILE *in, char *file, char *buf, CT *ctp)
412 struct str2init *s2i;
417 if (buf[0] == '\n' || (do_direct() && strcmp (buf, "#\n") == 0)) {
422 /* allocate basic Content structure */
423 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
424 adios (NULL, "out of memory");
427 /* allocate basic structure for handling decoded content */
428 init_decoded_content (ct);
435 * Handle inline text. Check if line
436 * is one of the following forms:
438 * 1) doesn't begin with '#' (implicit directive)
439 * 2) begins with "##" (implicit directive)
440 * 3) begins with "#<"
442 if (!do_direct() || buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
446 char content[BUFSIZ];
450 cp = m_mktemp2(NULL, invo_name, NULL, &out);
451 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
453 /* use a temp file to collect the plain text lines */
454 ce->ce_file = add (cp, NULL);
457 if (do_direct() && (buf[0] == '#' && buf[1] == '<')) {
458 strncpy (content, buf + 2, sizeof(content));
465 /* the directive is implicit */
466 strncpy (content, "text/plain", sizeof(content));
468 strncpy (buffer, (!do_direct() || buf[0] != '#') ? buf : buf + 1, sizeof(buffer));
472 if (headers >= 0 && do_direct() && uprf (buffer, DESCR_FIELD)
473 && buffer[i = strlen (DESCR_FIELD)] == ':') {
477 ct->c_descr = add (buffer + i + 1, ct->c_descr);
478 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
479 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
487 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
495 if (headers >= 0 && do_direct() && uprf (buffer, DISPO_FIELD)
496 && buffer[i = strlen (DISPO_FIELD)] == ':') {
500 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
501 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
502 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
510 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
518 if (headers != 1 || buffer[0] != '\n')
524 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
526 if (do_direct() && buffer[0] == '#') {
529 if (buffer[1] != '#')
531 for (cp = (bp = buffer) + 1; *cp; cp++)
538 ct->c_end = ftell (out);
541 /* parse content type */
542 if (get_ctinfo (content, ct, inlineD) == NOTOK)
545 for (s2i = str2cts; s2i->si_key; s2i++)
546 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
548 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
552 * check type specified (possibly implicitly)
554 switch (ct->c_type = s2i->si_val) {
556 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
557 ct->c_encoding = CE_7BIT;
562 adios (NULL, "it doesn't make sense to define an in-line %s content",
563 ct->c_type == CT_MESSAGE ? "message" : "multipart");
568 if ((ct->c_ctinitfnx = s2i->si_init))
569 (*ct->c_ctinitfnx) (ct);
574 fseek (in, pos, SEEK_SET);
579 * If we've reached this point, the next line
580 * must be some type of explicit directive.
583 /* check if directive is external-type */
584 extrnal = (buf[1] == '@');
586 /* parse directive */
587 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
590 /* check directive against the list of MIME types */
591 for (s2i = str2cts; s2i->si_key; s2i++)
592 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
596 * Check if the directive specified a valid type.
597 * This will happen if it was one of the following forms:
604 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
606 switch (ct->c_type = s2i->si_val) {
608 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
609 ci->ci_type, ci->ci_subtype);
613 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
614 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
615 ci->ci_type, ci->ci_subtype);
616 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
617 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
618 ci->ci_type, ci->ci_subtype);
621 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
622 ci->ci_type, ci->ci_subtype);
626 if ((ct->c_ctinitfnx = s2i->si_init))
627 (*ct->c_ctinitfnx) (ct);
632 * #@type/subtype (external types directive)
639 adios (NULL, "need external information for \"#@%s/%s\"",
640 ci->ci_type, ci->ci_subtype);
643 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
648 * Since we are using the current Content structure to
649 * hold information about the type of the external
650 * reference, we need to create another Content structure
651 * for the message/external-body to wrap it in.
653 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
654 adios (NULL, "out of memory");
657 if (get_ctinfo (buffer, ct, 0) == NOTOK)
659 ct->c_type = CT_MESSAGE;
660 ct->c_subtype = MESSAGE_EXTERNAL;
662 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
663 adios (NULL, "out of memory");
664 ct->c_ctparams = (void *) e;
670 if (params_external (ct, 1) == NOTOK)
676 /* Handle [file] argument */
678 /* check if specifies command to execute */
679 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
680 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
683 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
688 /* record filename of decoded contents */
689 ce->ce_file = ci->ci_magic;
690 if (access (ce->ce_file, R_OK) == NOTOK)
691 adios ("reading", "unable to access %s for", ce->ce_file);
692 if (listsw && stat (ce->ce_file, &st) != NOTOK)
693 ct->c_end = (long) st.st_size;
700 * No [file] argument, so check profile for
701 * method to compose content.
703 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
704 invo_name, ci->ci_type, ci->ci_subtype);
705 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
706 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
707 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
708 content_error (NULL, ct, "don't know how to compose content");
712 ci->ci_magic = add (cp, NULL);
717 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
721 * #forw [+folder] [msgs]
723 if (!mh_strcasecmp (ci->ci_type, "forw")) {
725 char *folder, *arguments[MAXARGS];
729 ap = brkstring (ci->ci_magic, " ", "\n");
730 copyip (ap, arguments, MAXARGS);
732 arguments[0] = "cur";
737 /* search the arguments for a folder name */
738 for (ap = arguments; *ap; ap++) {
740 if (*cp == '+' || *cp == '@') {
742 adios (NULL, "only one folder per #forw directive");
744 folder = pluspath (cp);
748 /* else, use the current folder */
750 folder = add (getfolder (1), NULL);
752 if (!(mp = folder_read (folder)))
753 adios (NULL, "unable to read folder %s", folder);
754 for (ap = arguments; *ap; ap++) {
756 if (*cp != '+' && *cp != '@')
757 if (!m_convert (mp, cp))
764 * If there is more than one message to include, make this
765 * a content of type "multipart/digest" and insert each message
766 * as a subpart. If there is only one message, then make this
767 * a content of type "message/rfc822".
769 if (mp->numsel > 1) {
770 /* we are forwarding multiple messages */
771 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
773 ct->c_type = CT_MULTIPART;
774 ct->c_subtype = MULTI_DIGEST;
776 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
777 adios (NULL, "out of memory");
778 ct->c_ctparams = (void *) m;
781 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
782 if (is_selected(mp, msgnum)) {
787 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
788 adios (NULL, "out of memory");
789 init_decoded_content (p);
791 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
793 p->c_type = CT_MESSAGE;
794 p->c_subtype = MESSAGE_RFC822;
796 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
797 pe->ce_file = add (buffer, NULL);
798 if (listsw && stat (pe->ce_file, &st) != NOTOK)
799 p->c_end = (long) st.st_size;
801 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
802 adios (NULL, "out of memory");
809 /* we are forwarding one message */
810 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
812 ct->c_type = CT_MESSAGE;
813 ct->c_subtype = MESSAGE_RFC822;
816 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
817 ce->ce_file = add (buffer, NULL);
818 if (listsw && stat (ce->ce_file, &st) != NOTOK)
819 ct->c_end = (long) st.st_size;
822 folder_free (mp); /* free folder/message structure */
829 if (!mh_strcasecmp (ci->ci_type, "end")) {
836 * #begin [ alternative | parallel ]
838 if (!mh_strcasecmp (ci->ci_type, "begin")) {
841 cp = SubMultiPart[vrsn - 1].kv_key;
842 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
843 vrsn = MULTI_ALTERNATE;
844 cp = SubMultiPart[vrsn - 1].kv_key;
845 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
846 vrsn = MULTI_PARALLEL;
847 cp = SubMultiPart[vrsn - 1].kv_key;
848 } else if (uprf (ci->ci_magic, "digest")) {
851 vrsn = MULTI_UNKNOWN;
856 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
857 if (get_ctinfo (buffer, ct, 0) == NOTOK)
859 ct->c_type = CT_MULTIPART;
860 ct->c_subtype = vrsn;
862 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
863 adios (NULL, "out of memory");
864 ct->c_ctparams = (void *) m;
867 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
871 if (user_content (in, file, buffer, &p) == DONE) {
873 adios (NULL, "empty \"#begin ... #end\" sequence");
879 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
880 adios (NULL, "out of memory");
885 admonish (NULL, "premature end-of-file, missing #end");
892 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
893 return NOTOK; /* NOT REACHED */
898 set_id (CT ct, int top)
902 static time_t clock = 0;
907 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
908 (int) getpid(), (long) clock, LocalName(1));
910 msgfmt = getcpy(msgid);
912 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
913 ct->c_id = getcpy (msgid);
917 static char ebcdicsafe[0x100] = {
918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
919 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
920 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
921 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
922 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
923 0x01, 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 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
927 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
928 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
929 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
930 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
931 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
932 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
933 0x01, 0x01, 0x01, 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,
948 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
949 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
954 * Fill out, or expand the various contents in the composition
955 * draft. Read-in any necessary files. Parse and execute any
956 * commands specified by profile composition strings.
960 compose_content (CT ct)
962 CE ce = ct->c_cefile;
964 switch (ct->c_type) {
969 char partnam[BUFSIZ];
970 struct multipart *m = (struct multipart *) ct->c_ctparams;
974 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
975 pp = partnam + strlen (partnam);
980 /* first, we call compose_content on all the subparts */
981 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
982 CT p = part->mp_part;
984 sprintf (pp, "%d", partnum);
985 p->c_partno = add (partnam, NULL);
986 if (compose_content (p) == NOTOK)
991 * If the -rfc934mode switch is given, then check all
992 * the subparts of a multipart/digest. If they are all
993 * message/rfc822, then mark this content and all
994 * subparts with the rfc934 compatibility mode flag.
996 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
999 for (part = m->mp_parts; part; part = part->mp_next) {
1000 CT p = part->mp_part;
1002 if (p->c_subtype != MESSAGE_RFC822) {
1007 ct->c_rfc934 = is934;
1008 for (part = m->mp_parts; part; part = part->mp_next) {
1009 CT p = part->mp_part;
1011 if ((p->c_rfc934 = is934))
1017 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
1021 for (part = m->mp_parts; part; part = part->mp_next)
1022 ct->c_end += part->mp_part->c_end + partnum;
1028 /* Nothing to do for type message */
1032 * Discrete types (text/application/audio/image/video)
1037 int i, xstdout, len, buflen;
1038 char *bp, **ap, *cp;
1039 char *vec[4], buffer[BUFSIZ];
1041 CI ci = &ct->c_ctinfo;
1044 if (!(cp = ci->ci_magic))
1045 adios (NULL, "internal error(5)");
1047 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1048 if (tfile == NULL) {
1049 adios("mhbuildsbr", "unable to create temporary file");
1051 ce->ce_file = add (tfile, NULL);
1056 /* Get buffer ready to go */
1059 buflen = sizeof(buffer);
1062 * Parse composition string into buffer
1064 for ( ; *cp; cp++) {
1069 /* insert parameters from directive */
1073 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1074 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1084 /* %f, and stdout is not-redirected */
1090 * insert temporary filename where
1091 * content should be written
1093 snprintf (bp, buflen, "%s", ce->ce_file);
1097 /* insert content subtype */
1098 strncpy (bp, ci->ci_subtype, buflen);
1102 /* insert character % */
1123 printf ("composing content %s/%s from command\n\t%s\n",
1124 ci->ci_type, ci->ci_subtype, buffer);
1126 fflush (stdout); /* not sure if need for -noverbose */
1133 if ((out = fopen (ce->ce_file, "w")) == NULL)
1134 adios (ce->ce_file, "unable to open for writing");
1136 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1140 adios ("fork", "unable to fork");
1145 dup2 (fileno (out), 1);
1146 close (fileno (out));
1147 execvp ("/bin/sh", vec);
1148 fprintf (stderr, "unable to exec ");
1155 if (pidXwait(child_id, NULL))
1161 /* Check size of file */
1162 if (listsw && ct->c_end == 0L) {
1165 if (stat (ce->ce_file, &st) != NOTOK)
1166 ct->c_end = (long) st.st_size;
1178 * 1) choose a transfer encoding.
1179 * 2) check for clashes with multipart boundary string.
1180 * 3) for text content, figure out which character set is being used.
1182 * If there is a clash with one of the contents and the multipart boundary,
1183 * this function will exit with NOTOK. This will cause the scanning process
1184 * to be repeated with a different multipart boundary. It is possible
1185 * (although highly unlikely) that this scan will be repeated multiple times.
1189 scan_content (CT ct)
1192 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1193 int checklinelen = 0, linelen = 0; /* check for long lines */
1194 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1195 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1196 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1197 unsigned char *cp = NULL, buffer[BUFSIZ];
1198 struct text *t = NULL;
1200 CE ce = ct->c_cefile;
1203 * handle multipart by scanning all subparts
1204 * and then checking their encoding.
1206 if (ct->c_type == CT_MULTIPART) {
1207 struct multipart *m = (struct multipart *) ct->c_ctparams;
1210 /* initially mark the domain of enclosing multipart as 7bit */
1211 ct->c_encoding = CE_7BIT;
1213 for (part = m->mp_parts; part; part = part->mp_next) {
1214 CT p = part->mp_part;
1216 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1219 /* if necessary, enlarge encoding for enclosing multipart */
1220 if (p->c_encoding == CE_BINARY)
1221 ct->c_encoding = CE_BINARY;
1222 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1223 ct->c_encoding = CE_8BIT;
1230 * Decide what to check while scanning this content.
1232 switch (ct->c_type) {
1236 if (ct->c_subtype == TEXT_PLAIN) {
1241 checkebcdic = ebcdicsw;
1247 case CT_APPLICATION:
1249 checkebcdic = ebcdicsw;
1261 /* don't check anything for message/external */
1262 if (ct->c_subtype == MESSAGE_EXTERNAL)
1272 * Don't check anything for these types,
1273 * since we are forcing use of base64.
1284 * Scan the unencoded content
1286 if (check8bit || checklinelen || checklinespace || checkboundary) {
1287 if ((in = fopen (ce->ce_file, "r")) == NULL)
1288 adios (ce->ce_file, "unable to open for reading");
1289 len = strlen (prefix);
1291 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1293 * Check for 8bit data.
1296 for (cp = buffer; *cp; cp++) {
1297 if (!isascii (*cp)) {
1299 check8bit = 0; /* no need to keep checking */
1302 * Check if character is ebcdic-safe. We only check
1303 * this if also checking for 8bit data.
1305 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1307 checkebcdic = 0; /* no need to keep checking */
1313 * Check line length.
1315 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1317 checklinelen = 0; /* no need to keep checking */
1321 * Check if line ends with a space.
1323 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1325 checklinespace = 0; /* no need to keep checking */
1329 * Check if content contains a line that clashes
1330 * with our standard boundary for multipart messages.
1332 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1333 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1337 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1339 checkboundary = 0; /* no need to keep checking */
1347 * Decide which transfer encoding to use.
1349 switch (ct->c_type) {
1352 * If the text content didn't specify a character
1353 * set, we need to figure out which one was used.
1355 t = (struct text *) ct->c_ctparams;
1356 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1357 CI ci = &ct->c_ctinfo;
1360 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1364 t->tx_charset = CHARSET_UNKNOWN;
1365 *ap = concat ("charset=", write_charset_8bit(), NULL);
1367 t->tx_charset = CHARSET_USASCII;
1368 *ap = add ("charset=us-ascii", NULL);
1371 cp = strchr(*ap++, '=');
1377 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1378 ct->c_encoding = CE_QUOTED;
1380 ct->c_encoding = CE_7BIT;
1383 case CT_APPLICATION:
1384 /* For application type, use base64, except when postscript */
1385 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1386 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1387 ? CE_QUOTED : CE_BASE64;
1389 ct->c_encoding = CE_7BIT;
1393 ct->c_encoding = CE_7BIT;
1399 /* For audio, image, and video contents, just use base64 */
1400 ct->c_encoding = CE_BASE64;
1404 return (boundaryclash ? NOTOK : OK);
1409 * Scan the content structures, and build header
1410 * fields that will need to be output into the
1415 build_headers (CT ct)
1417 int cc, mailbody, len;
1419 char *np, *vp, buffer[BUFSIZ];
1420 CI ci = &ct->c_ctinfo;
1423 * If message is type multipart, then add the multipart
1424 * boundary to the list of attribute/value pairs.
1426 if (ct->c_type == CT_MULTIPART) {
1428 static int level = 0; /* store nesting level */
1432 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1433 cp = strchr(*ap++ = add (buffer, NULL), '=');
1440 * Skip the output of Content-Type, parameters, content
1441 * description and disposition, and Content-ID if the
1442 * content is of type "message" and the rfc934 compatibility
1443 * flag is set (which means we are inside multipart/digest
1444 * and the switch -rfc934mode was given).
1446 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1450 * output the content type and subtype
1452 np = add (TYPE_FIELD, NULL);
1453 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1455 /* keep track of length of line */
1456 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1457 + strlen (ci->ci_subtype) + 3;
1459 mailbody = ct->c_type == CT_MESSAGE
1460 && ct->c_subtype == MESSAGE_EXTERNAL
1461 && ((struct exbody *) ct->c_ctparams)->eb_body;
1464 * Append the attribute/value pairs to
1465 * the end of the Content-Type line.
1467 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1468 if (mailbody && !mh_strcasecmp (*ap, "body"))
1474 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1475 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1476 vp = add ("\n\t", vp);
1482 vp = add (buffer, vp);
1487 * Append any RFC-822 comment to the end of
1488 * the Content-Type line.
1490 if (ci->ci_comment) {
1491 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1492 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1493 vp = add ("\n\t", vp);
1499 vp = add (buffer, vp);
1502 vp = add ("\n", vp);
1503 add_header (ct, np, vp);
1506 * output the Content-ID, unless disabled by -nocontentid
1508 if (contentidsw && ct->c_id) {
1509 np = add (ID_FIELD, NULL);
1510 vp = concat (" ", ct->c_id, NULL);
1511 add_header (ct, np, vp);
1515 * output the Content-Description
1518 np = add (DESCR_FIELD, NULL);
1519 vp = concat (" ", ct->c_descr, NULL);
1520 add_header (ct, np, vp);
1524 * output the Content-Disposition
1527 np = add (DISPO_FIELD, NULL);
1528 vp = concat (" ", ct->c_dispo, NULL);
1529 add_header (ct, np, vp);
1534 * If this is the internal content structure for a
1535 * "message/external", then we are done with the
1536 * headers (since it has no body).
1542 * output the Content-MD5
1545 np = add (MD5_FIELD, NULL);
1546 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1547 add_header (ct, np, vp);
1551 * output the Content-Transfer-Encoding
1553 switch (ct->c_encoding) {
1555 /* Nothing to output */
1557 np = add (ENCODING_FIELD, NULL);
1558 vp = concat (" ", "7bit", "\n", NULL);
1559 add_header (ct, np, vp);
1564 if (ct->c_type == CT_MESSAGE)
1565 adios (NULL, "internal error, invalid encoding");
1567 np = add (ENCODING_FIELD, NULL);
1568 vp = concat (" ", "8bit", "\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 (" ", "quoted-printable", "\n", NULL);
1578 add_header (ct, np, vp);
1582 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1583 adios (NULL, "internal error, invalid encoding");
1585 np = add (ENCODING_FIELD, NULL);
1586 vp = concat (" ", "base64", "\n", NULL);
1587 add_header (ct, np, vp);
1591 if (ct->c_type == CT_MESSAGE)
1592 adios (NULL, "internal error, invalid encoding");
1594 np = add (ENCODING_FIELD, NULL);
1595 vp = concat (" ", "binary", "\n", NULL);
1596 add_header (ct, np, vp);
1600 adios (NULL, "unknown transfer encoding in content");
1605 * Additional content specific header processing
1607 switch (ct->c_type) {
1610 struct multipart *m;
1613 m = (struct multipart *) ct->c_ctparams;
1614 for (part = m->mp_parts; part; part = part->mp_next) {
1624 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1627 e = (struct exbody *) ct->c_ctparams;
1628 build_headers (e->eb_content);
1641 static char nib2b64[0x40+1] =
1642 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1645 calculate_digest (CT ct, int asciiP)
1648 char buffer[BUFSIZ], *vp, *op;
1650 unsigned char digest[16];
1651 unsigned char outbuf[25];
1653 CE ce = ct->c_cefile;
1654 char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
1658 if ((in = fopen (infilename, "r")) == NULL)
1659 adios (infilename, "unable to open for reading");
1661 /* Initialize md5 context */
1662 MD5Init (&mdContext);
1664 /* calculate md5 message digest */
1666 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1669 cp = buffer + strlen (buffer) - 1;
1670 if ((c = *cp) == '\n')
1673 MD5Update (&mdContext, (unsigned char *) buffer,
1674 (unsigned int) strlen (buffer));
1677 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1680 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1681 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1684 /* md5 finalization. Write digest and zero md5 context */
1685 MD5Final (digest, &mdContext);
1690 /* print debugging info */
1694 fprintf (stderr, "MD5 digest=");
1695 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1697 fprintf (stderr, "%02x", *dp & 0xff);
1698 fprintf (stderr, "\n");
1701 /* encode the digest using base64 */
1702 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1703 cc > 0; cc -= 3, op += 4) {
1707 bits = (*dp++ & 0xff) << 16;
1709 bits |= (*dp++ & 0xff) << 8;
1711 bits |= *dp++ & 0xff;
1714 for (bp = op + 4; bp > op; bits >>= 6)
1715 *--bp = nib2b64[bits & 0x3f];
1723 /* null terminate string */
1726 /* now make copy and return string */
1727 vp = concat (" ", outbuf, "\n", NULL);