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 TIME_WITH_SYS_TIME
31 # include <sys/time.h>
34 # ifdef TM_IN_SYS_TIME
35 # include <sys/time.h>
48 extern int contentidsw;
50 extern int endian; /* mhmisc.c */
53 extern int rcachesw; /* mhcachesbr.c */
54 extern int wcachesw; /* mhcachesbr.c */
57 * Directory to place tmp files. This must
58 * be set before these routines are called.
64 static char prefix[] = "----- =_aaaaaaaaaa";
68 int make_intermediates (char *);
69 void content_error (char *, CT, char *, ...);
72 int find_cache (CT, int, int *, char *, char *, int);
75 void free_content (CT);
76 void free_ctinfo (CT);
77 void free_encoding (CT, int);
82 CT build_mime (char *);
87 static int init_decoded_content (CT);
88 static char *fgetstr (char *, int, FILE *);
89 static int user_content (FILE *, char *, char *, CT *);
90 static void set_id (CT, int);
91 static int compose_content (CT);
92 static int scan_content (CT);
93 static int build_headers (CT);
94 static char *calculate_digest (CT, int);
98 * Main routine for translating composition file
99 * into valid MIME message. It translates the draft
100 * into a content structure (actually a tree of content
101 * structures). This message then can be manipulated
102 * in various ways, including being output via
107 build_mime (char *infile)
110 char buf[BUFSIZ], name[NAMESZ];
117 umask (~m_gmprot ());
119 /* open the composition draft */
120 if ((in = fopen (infile, "r")) == NULL)
121 adios (infile, "unable to open for reading");
124 * Allocate space for primary (outside) content
126 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
127 adios (NULL, "out of memory");
130 * Allocate structure for handling decoded content
131 * for this part. We don't really need this, but
132 * allocate it to remain consistent.
134 init_decoded_content (ct);
137 * Parse some of the header fields in the composition
138 * draft into the linked list of header fields for
139 * the new MIME message.
141 for (compnum = 1, state = FLD;;) {
142 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
148 /* abort if draft has Mime-Version header field */
149 if (!mh_strcasecmp (name, VRSN_FIELD))
150 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
152 /* abort if draft has Content-Transfer-Encoding header field */
153 if (!mh_strcasecmp (name, ENCODING_FIELD))
154 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
156 /* ignore any Content-Type fields in the header */
157 if (!mh_strcasecmp (name, TYPE_FIELD)) {
158 while (state == FLDPLUS)
159 state = m_getfld (state, name, buf, sizeof(buf), in);
163 /* get copies of the buffers */
164 np = add (name, NULL);
165 vp = add (buf, NULL);
167 /* if necessary, get rest of field */
168 while (state == FLDPLUS) {
169 state = m_getfld (state, name, buf, sizeof(buf), in);
170 vp = add (buf, vp); /* add to previous value */
173 /* Now add the header data to the list */
174 add_header (ct, np, vp);
177 /* if this wasn't the last header field, then continue */
183 adios (NULL, "draft has empty body -- no directives!");
188 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
193 adios (NULL, "message format error in component #%d", compnum);
196 adios (NULL, "getfld() returned %d", state);
202 * Now add the MIME-Version header field
203 * to the list of header fields.
205 np = add (VRSN_FIELD, NULL);
206 vp = concat (" ", VRSN_VALUE, "\n", NULL);
207 add_header (ct, np, vp);
210 * We initally assume we will find multiple contents in the
211 * draft. So create a multipart/mixed content to hold everything.
212 * We can remove this later, if it is not needed.
214 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
216 ct->c_type = CT_MULTIPART;
217 ct->c_subtype = MULTI_MIXED;
218 ct->c_file = add (infile, NULL);
220 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
221 adios (NULL, "out of memory");
222 ct->c_ctparams = (void *) m;
226 * read and parse the composition file
227 * and the directives it contains.
229 while (fgetstr (buf, sizeof(buf) - 1, in)) {
233 if (user_content (in, infile, buf, &p) == DONE) {
234 admonish (NULL, "ignoring spurious #end");
240 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
241 adios (NULL, "out of memory");
248 * close the composition draft since
249 * it's not needed any longer.
253 /* check if any contents were found */
255 adios (NULL, "no content directives found");
258 * If only one content was found, then remove and
259 * free the outer multipart content.
261 if (!m->mp_parts->mp_next) {
264 p = m->mp_parts->mp_part;
265 m->mp_parts->mp_part = NULL;
267 /* move header fields */
268 p->c_first_hf = ct->c_first_hf;
269 p->c_last_hf = ct->c_last_hf;
270 ct->c_first_hf = NULL;
271 ct->c_last_hf = NULL;
280 * Fill out, or expand directives. Parse and execute
281 * commands specified by profile composition strings.
283 compose_content (ct);
285 if ((cp = strchr(prefix, 'a')) == NULL)
286 adios (NULL, "internal error(4)");
289 * Scan the contents. Choose a transfer encoding, and
290 * check if prefix for multipart boundary clashes with
291 * any of the contents.
293 while (scan_content (ct) == NOTOK) {
298 adios (NULL, "giving up trying to find a unique delimiter string");
304 /* Build the rest of the header field structures */
312 * Set up structures for placing unencoded
313 * content when building parts.
317 init_decoded_content (CT ct)
321 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
322 adios (NULL, "out of memory");
325 ct->c_ceopenfnx = open7Bit; /* since unencoded */
326 ct->c_ceclosefnx = close_encoding;
327 ct->c_cesizefnx = NULL; /* since unencoded */
334 fgetstr (char *s, int n, FILE *stream)
338 for (ep = (cp = s) + n; cp < ep; ) {
341 if (!fgets (cp, n, stream))
342 return (cp != s ? s : NULL);
343 if (cp == s && *cp != '#')
346 cp += (i = strlen (cp)) - 1;
347 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
358 * Parse the composition draft for text and directives.
359 * Do initial setup of Content structure.
363 user_content (FILE *in, char *file, char *buf, CT *ctp)
372 struct str2init *s2i;
377 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
382 /* allocate basic Content structure */
383 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
384 adios (NULL, "out of memory");
387 /* allocate basic structure for handling decoded content */
388 init_decoded_content (ct);
395 * Handle inline text. Check if line
396 * is one of the following forms:
398 * 1) doesn't begin with '#' (implicit directive)
399 * 2) begins with "##" (implicit directive)
400 * 3) begins with "#<"
402 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
406 char content[BUFSIZ];
410 cp = m_mktemp2(NULL, invo_name, NULL, &out);
411 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
413 /* use a temp file to collect the plain text lines */
414 ce->ce_file = add (cp, NULL);
417 if (buf[0] == '#' && buf[1] == '<') {
418 strncpy (content, buf + 2, sizeof(content));
425 /* the directive is implicit */
426 strncpy (content, "text/plain", sizeof(content));
428 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
432 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
433 && buffer[i = strlen (DESCR_FIELD)] == ':') {
437 ct->c_descr = add (buffer + i + 1, ct->c_descr);
438 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
439 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
447 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
455 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
456 && buffer[i = strlen (DISPO_FIELD)] == ':') {
460 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
461 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
462 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
470 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
478 if (headers != 1 || buffer[0] != '\n')
484 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
486 if (buffer[0] == '#') {
489 if (buffer[1] != '#')
491 for (cp = (bp = buffer) + 1; *cp; cp++)
498 ct->c_end = ftell (out);
501 /* parse content type */
502 if (get_ctinfo (content, ct, inlineD) == NOTOK)
505 for (s2i = str2cts; s2i->si_key; s2i++)
506 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
508 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
512 * check type specified (possibly implicitly)
514 switch (ct->c_type = s2i->si_val) {
516 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
517 ct->c_encoding = CE_7BIT;
522 adios (NULL, "it doesn't make sense to define an in-line %s content",
523 ct->c_type == CT_MESSAGE ? "message" : "multipart");
528 if ((ct->c_ctinitfnx = s2i->si_init))
529 (*ct->c_ctinitfnx) (ct);
534 fseek (in, pos, SEEK_SET);
539 * If we've reached this point, the next line
540 * must be some type of explicit directive.
543 /* check if directive is external-type */
544 extrnal = (buf[1] == '@');
546 /* parse directive */
547 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
550 /* check directive against the list of MIME types */
551 for (s2i = str2cts; s2i->si_key; s2i++)
552 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
556 * Check if the directive specified a valid type.
557 * This will happen if it was one of the following forms:
564 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
566 switch (ct->c_type = s2i->si_val) {
568 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
569 ci->ci_type, ci->ci_subtype);
573 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
574 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
575 ci->ci_type, ci->ci_subtype);
576 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
577 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
578 ci->ci_type, ci->ci_subtype);
581 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
582 ci->ci_type, ci->ci_subtype);
586 if ((ct->c_ctinitfnx = s2i->si_init))
587 (*ct->c_ctinitfnx) (ct);
592 * #@type/subtype (external types directive)
599 adios (NULL, "need external information for \"#@%s/%s\"",
600 ci->ci_type, ci->ci_subtype);
603 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
608 * Since we are using the current Content structure to
609 * hold information about the type of the external
610 * reference, we need to create another Content structure
611 * for the message/external-body to wrap it in.
613 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
614 adios (NULL, "out of memory");
617 if (get_ctinfo (buffer, ct, 0) == NOTOK)
619 ct->c_type = CT_MESSAGE;
620 ct->c_subtype = MESSAGE_EXTERNAL;
622 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
623 adios (NULL, "out of memory");
624 ct->c_ctparams = (void *) e;
630 if (params_external (ct, 1) == NOTOK)
636 /* Handle [file] argument */
638 /* check if specifies command to execute */
639 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
640 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
643 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
648 /* record filename of decoded contents */
649 ce->ce_file = ci->ci_magic;
650 if (access (ce->ce_file, R_OK) == NOTOK)
651 adios ("reading", "unable to access %s for", ce->ce_file);
652 if (listsw && stat (ce->ce_file, &st) != NOTOK)
653 ct->c_end = (long) st.st_size;
660 * No [file] argument, so check profile for
661 * method to compose content.
663 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
664 invo_name, ci->ci_type, ci->ci_subtype);
665 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
666 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
667 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
668 content_error (NULL, ct, "don't know how to compose content");
672 ci->ci_magic = add (cp, NULL);
677 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
681 * #forw [+folder] [msgs]
683 if (!mh_strcasecmp (ci->ci_type, "forw")) {
685 char *folder, *arguments[MAXARGS];
689 ap = brkstring (ci->ci_magic, " ", "\n");
690 copyip (ap, arguments, MAXARGS);
692 arguments[0] = "cur";
697 /* search the arguments for a folder name */
698 for (ap = arguments; *ap; ap++) {
700 if (*cp == '+' || *cp == '@') {
702 adios (NULL, "only one folder per #forw directive");
704 folder = pluspath (cp);
708 /* else, use the current folder */
710 folder = add (getfolder (1), NULL);
712 if (!(mp = folder_read (folder)))
713 adios (NULL, "unable to read folder %s", folder);
714 for (ap = arguments; *ap; ap++) {
716 if (*cp != '+' && *cp != '@')
717 if (!m_convert (mp, cp))
724 * If there is more than one message to include, make this
725 * a content of type "multipart/digest" and insert each message
726 * as a subpart. If there is only one message, then make this
727 * a content of type "message/rfc822".
729 if (mp->numsel > 1) {
730 /* we are forwarding multiple messages */
731 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
733 ct->c_type = CT_MULTIPART;
734 ct->c_subtype = MULTI_DIGEST;
736 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
737 adios (NULL, "out of memory");
738 ct->c_ctparams = (void *) m;
741 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
742 if (is_selected(mp, msgnum)) {
747 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
748 adios (NULL, "out of memory");
749 init_decoded_content (p);
751 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
753 p->c_type = CT_MESSAGE;
754 p->c_subtype = MESSAGE_RFC822;
756 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
757 pe->ce_file = add (buffer, NULL);
758 if (listsw && stat (pe->ce_file, &st) != NOTOK)
759 p->c_end = (long) st.st_size;
761 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
762 adios (NULL, "out of memory");
769 /* we are forwarding one message */
770 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
772 ct->c_type = CT_MESSAGE;
773 ct->c_subtype = MESSAGE_RFC822;
776 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
777 ce->ce_file = add (buffer, NULL);
778 if (listsw && stat (ce->ce_file, &st) != NOTOK)
779 ct->c_end = (long) st.st_size;
782 folder_free (mp); /* free folder/message structure */
789 if (!mh_strcasecmp (ci->ci_type, "end")) {
796 * #begin [ alternative | parallel ]
798 if (!mh_strcasecmp (ci->ci_type, "begin")) {
801 cp = SubMultiPart[vrsn - 1].kv_key;
802 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
803 vrsn = MULTI_ALTERNATE;
804 cp = SubMultiPart[vrsn - 1].kv_key;
805 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
806 vrsn = MULTI_PARALLEL;
807 cp = SubMultiPart[vrsn - 1].kv_key;
808 } else if (uprf (ci->ci_magic, "digest")) {
811 vrsn = MULTI_UNKNOWN;
816 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
817 if (get_ctinfo (buffer, ct, 0) == NOTOK)
819 ct->c_type = CT_MULTIPART;
820 ct->c_subtype = vrsn;
822 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
823 adios (NULL, "out of memory");
824 ct->c_ctparams = (void *) m;
827 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
831 if (user_content (in, file, buffer, &p) == DONE) {
833 adios (NULL, "empty \"#begin ... #end\" sequence");
839 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
840 adios (NULL, "out of memory");
845 admonish (NULL, "premature end-of-file, missing #end");
852 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
853 return NOTOK; /* NOT REACHED */
858 set_id (CT ct, int top)
862 static time_t clock = 0;
867 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
868 (int) getpid(), (long) clock, LocalName());
870 msgfmt = getcpy(msgid);
872 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
873 ct->c_id = getcpy (msgid);
877 static char ebcdicsafe[0x100] = {
878 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
879 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
880 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
881 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
882 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
883 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
884 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
885 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
886 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
887 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
888 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
889 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
890 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
891 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
892 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
893 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
894 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
895 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
896 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
897 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
898 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
899 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
900 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
901 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
902 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
903 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
904 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
905 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
907 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
909 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
914 * Fill out, or expand the various contents in the composition
915 * draft. Read-in any necessary files. Parse and execute any
916 * commands specified by profile composition strings.
920 compose_content (CT ct)
922 CE ce = ct->c_cefile;
924 switch (ct->c_type) {
929 char partnam[BUFSIZ];
930 struct multipart *m = (struct multipart *) ct->c_ctparams;
934 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
935 pp = partnam + strlen (partnam);
940 /* first, we call compose_content on all the subparts */
941 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
942 CT p = part->mp_part;
944 sprintf (pp, "%d", partnum);
945 p->c_partno = add (partnam, NULL);
946 if (compose_content (p) == NOTOK)
951 * If the -rfc934mode switch is given, then check all
952 * the subparts of a multipart/digest. If they are all
953 * message/rfc822, then mark this content and all
954 * subparts with the rfc934 compatibility mode flag.
956 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
959 for (part = m->mp_parts; part; part = part->mp_next) {
960 CT p = part->mp_part;
962 if (p->c_subtype != MESSAGE_RFC822) {
967 ct->c_rfc934 = is934;
968 for (part = m->mp_parts; part; part = part->mp_next) {
969 CT p = part->mp_part;
971 if ((p->c_rfc934 = is934))
977 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
981 for (part = m->mp_parts; part; part = part->mp_next)
982 ct->c_end += part->mp_part->c_end + partnum;
988 /* Nothing to do for type message */
992 * Discrete types (text/application/audio/image/video)
997 int i, xstdout, len, buflen;
999 char *vec[4], buffer[BUFSIZ];
1001 CI ci = &ct->c_ctinfo;
1004 if (!(cp = ci->ci_magic))
1005 adios (NULL, "internal error(5)");
1007 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1008 if (tfile == NULL) {
1009 adios("mhbuildsbr", "unable to create temporary file");
1011 ce->ce_file = add (tfile, NULL);
1016 /* Get buffer ready to go */
1019 buflen = sizeof(buffer);
1022 * Parse composition string into buffer
1024 for ( ; *cp; cp++) {
1029 /* insert parameters from directive */
1033 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1034 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1044 /* %f, and stdout is not-redirected */
1050 * insert temporary filename where
1051 * content should be written
1053 snprintf (bp, buflen, "%s", ce->ce_file);
1057 /* insert content subtype */
1058 strncpy (bp, ci->ci_subtype, buflen);
1062 /* insert character % */
1083 printf ("composing content %s/%s from command\n\t%s\n",
1084 ci->ci_type, ci->ci_subtype, buffer);
1086 fflush (stdout); /* not sure if need for -noverbose */
1093 if ((out = fopen (ce->ce_file, "w")) == NULL)
1094 adios (ce->ce_file, "unable to open for writing");
1096 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1100 adios ("fork", "unable to fork");
1105 dup2 (fileno (out), 1);
1106 close (fileno (out));
1107 execvp ("/bin/sh", vec);
1108 fprintf (stderr, "unable to exec ");
1115 if (pidXwait(child_id, NULL))
1121 /* Check size of file */
1122 if (listsw && ct->c_end == 0L) {
1125 if (stat (ce->ce_file, &st) != NOTOK)
1126 ct->c_end = (long) st.st_size;
1138 * 1) choose a transfer encoding.
1139 * 2) check for clashes with multipart boundary string.
1140 * 3) for text content, figure out which character set is being used.
1142 * If there is a clash with one of the contents and the multipart boundary,
1143 * this function will exit with NOTOK. This will cause the scanning process
1144 * to be repeated with a different multipart boundary. It is possible
1145 * (although highly unlikely) that this scan will be repeated multiple times.
1149 scan_content (CT ct)
1152 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1153 int checklinelen = 0, linelen = 0; /* check for long lines */
1154 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1155 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1156 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1157 unsigned char *cp = NULL, buffer[BUFSIZ];
1158 struct text *t = NULL;
1160 CE ce = ct->c_cefile;
1163 * handle multipart by scanning all subparts
1164 * and then checking their encoding.
1166 if (ct->c_type == CT_MULTIPART) {
1167 struct multipart *m = (struct multipart *) ct->c_ctparams;
1170 /* initially mark the domain of enclosing multipart as 7bit */
1171 ct->c_encoding = CE_7BIT;
1173 for (part = m->mp_parts; part; part = part->mp_next) {
1174 CT p = part->mp_part;
1176 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1179 /* if necessary, enlarge encoding for enclosing multipart */
1180 if (p->c_encoding == CE_BINARY)
1181 ct->c_encoding = CE_BINARY;
1182 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1183 ct->c_encoding = CE_8BIT;
1190 * Decide what to check while scanning this content.
1192 switch (ct->c_type) {
1196 if (ct->c_subtype == TEXT_PLAIN) {
1201 checkebcdic = ebcdicsw;
1207 case CT_APPLICATION:
1209 checkebcdic = ebcdicsw;
1221 /* don't check anything for message/external */
1222 if (ct->c_subtype == MESSAGE_EXTERNAL)
1232 * Don't check anything for these types,
1233 * since we are forcing use of base64.
1244 * Scan the unencoded content
1246 if (check8bit || checklinelen || checklinespace || checkboundary) {
1247 if ((in = fopen (ce->ce_file, "r")) == NULL)
1248 adios (ce->ce_file, "unable to open for reading");
1249 len = strlen (prefix);
1251 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1253 * Check for 8bit data.
1256 for (cp = buffer; *cp; cp++) {
1257 if (!isascii (*cp)) {
1259 check8bit = 0; /* no need to keep checking */
1262 * Check if character is ebcdic-safe. We only check
1263 * this if also checking for 8bit data.
1265 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1267 checkebcdic = 0; /* no need to keep checking */
1273 * Check line length.
1275 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1277 checklinelen = 0; /* no need to keep checking */
1281 * Check if line ends with a space.
1283 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1285 checklinespace = 0; /* no need to keep checking */
1289 * Check if content contains a line that clashes
1290 * with our standard boundary for multipart messages.
1292 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1293 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1297 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1299 checkboundary = 0; /* no need to keep checking */
1307 * Decide which transfer encoding to use.
1309 switch (ct->c_type) {
1312 * If the text content didn't specify a character
1313 * set, we need to figure out which one was used.
1315 t = (struct text *) ct->c_ctparams;
1316 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1317 CI ci = &ct->c_ctinfo;
1320 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1324 t->tx_charset = CHARSET_UNKNOWN;
1325 *ap = concat ("charset=", write_charset_8bit(), NULL);
1327 t->tx_charset = CHARSET_USASCII;
1328 *ap = add ("charset=us-ascii", NULL);
1331 cp = strchr(*ap++, '=');
1337 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1338 ct->c_encoding = CE_QUOTED;
1340 ct->c_encoding = CE_7BIT;
1343 case CT_APPLICATION:
1344 /* For application type, use base64, except when postscript */
1345 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1346 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1347 ? CE_QUOTED : CE_BASE64;
1349 ct->c_encoding = CE_7BIT;
1353 ct->c_encoding = CE_7BIT;
1359 /* For audio, image, and video contents, just use base64 */
1360 ct->c_encoding = CE_BASE64;
1364 return (boundaryclash ? NOTOK : OK);
1369 * Scan the content structures, and build header
1370 * fields that will need to be output into the
1375 build_headers (CT ct)
1377 int cc, mailbody, len;
1379 char *np, *vp, buffer[BUFSIZ];
1380 CI ci = &ct->c_ctinfo;
1383 * If message is type multipart, then add the multipart
1384 * boundary to the list of attribute/value pairs.
1386 if (ct->c_type == CT_MULTIPART) {
1388 static int level = 0; /* store nesting level */
1392 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1393 cp = strchr(*ap++ = add (buffer, NULL), '=');
1400 * Skip the output of Content-Type, parameters, content
1401 * description and disposition, and Content-ID if the
1402 * content is of type "message" and the rfc934 compatibility
1403 * flag is set (which means we are inside multipart/digest
1404 * and the switch -rfc934mode was given).
1406 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1410 * output the content type and subtype
1412 np = add (TYPE_FIELD, NULL);
1413 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1415 /* keep track of length of line */
1416 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1417 + strlen (ci->ci_subtype) + 3;
1419 mailbody = ct->c_type == CT_MESSAGE
1420 && ct->c_subtype == MESSAGE_EXTERNAL
1421 && ((struct exbody *) ct->c_ctparams)->eb_body;
1424 * Append the attribute/value pairs to
1425 * the end of the Content-Type line.
1427 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1428 if (mailbody && !mh_strcasecmp (*ap, "body"))
1434 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1435 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1436 vp = add ("\n\t", vp);
1442 vp = add (buffer, vp);
1447 * Append any RFC-822 comment to the end of
1448 * the Content-Type line.
1450 if (ci->ci_comment) {
1451 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1452 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1453 vp = add ("\n\t", vp);
1459 vp = add (buffer, vp);
1462 vp = add ("\n", vp);
1463 add_header (ct, np, vp);
1466 * output the Content-ID, unless disabled by -nocontentid
1468 if (contentidsw && ct->c_id) {
1469 np = add (ID_FIELD, NULL);
1470 vp = concat (" ", ct->c_id, NULL);
1471 add_header (ct, np, vp);
1475 * output the Content-Description
1478 np = add (DESCR_FIELD, NULL);
1479 vp = concat (" ", ct->c_descr, NULL);
1480 add_header (ct, np, vp);
1484 * output the Content-Disposition
1487 np = add (DISPO_FIELD, NULL);
1488 vp = concat (" ", ct->c_dispo, NULL);
1489 add_header (ct, np, vp);
1494 * If this is the internal content structure for a
1495 * "message/external", then we are done with the
1496 * headers (since it has no body).
1502 * output the Content-MD5
1505 np = add (MD5_FIELD, NULL);
1506 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1507 add_header (ct, np, vp);
1511 * output the Content-Transfer-Encoding
1513 switch (ct->c_encoding) {
1515 /* Nothing to output */
1517 np = add (ENCODING_FIELD, NULL);
1518 vp = concat (" ", "7bit", "\n", NULL);
1519 add_header (ct, np, vp);
1524 if (ct->c_type == CT_MESSAGE)
1525 adios (NULL, "internal error, invalid encoding");
1527 np = add (ENCODING_FIELD, NULL);
1528 vp = concat (" ", "8bit", "\n", NULL);
1529 add_header (ct, np, vp);
1533 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1534 adios (NULL, "internal error, invalid encoding");
1536 np = add (ENCODING_FIELD, NULL);
1537 vp = concat (" ", "quoted-printable", "\n", NULL);
1538 add_header (ct, np, vp);
1542 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1543 adios (NULL, "internal error, invalid encoding");
1545 np = add (ENCODING_FIELD, NULL);
1546 vp = concat (" ", "base64", "\n", NULL);
1547 add_header (ct, np, vp);
1551 if (ct->c_type == CT_MESSAGE)
1552 adios (NULL, "internal error, invalid encoding");
1554 np = add (ENCODING_FIELD, NULL);
1555 vp = concat (" ", "binary", "\n", NULL);
1556 add_header (ct, np, vp);
1560 adios (NULL, "unknown transfer encoding in content");
1565 * Additional content specific header processing
1567 switch (ct->c_type) {
1570 struct multipart *m;
1573 m = (struct multipart *) ct->c_ctparams;
1574 for (part = m->mp_parts; part; part = part->mp_next) {
1584 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1587 e = (struct exbody *) ct->c_ctparams;
1588 build_headers (e->eb_content);
1601 static char nib2b64[0x40+1] =
1602 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1605 calculate_digest (CT ct, int asciiP)
1608 char buffer[BUFSIZ], *vp, *op;
1610 unsigned char digest[16];
1611 unsigned char outbuf[25];
1614 CE ce = ct->c_cefile;
1617 if ((in = fopen (ce->ce_file, "r")) == NULL)
1618 adios (ce->ce_file, "unable to open for reading");
1620 /* Initialize md5 context */
1621 MD5Init (&mdContext);
1623 /* calculate md5 message digest */
1625 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1628 cp = buffer + strlen (buffer) - 1;
1629 if ((c = *cp) == '\n')
1632 MD5Update (&mdContext, (unsigned char *) buffer,
1633 (unsigned int) strlen (buffer));
1636 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1639 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1640 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1643 /* md5 finalization. Write digest and zero md5 context */
1644 MD5Final (digest, &mdContext);
1649 /* print debugging info */
1653 fprintf (stderr, "MD5 digest=");
1654 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1656 fprintf (stderr, "%02x", *dp & 0xff);
1657 fprintf (stderr, "\n");
1660 /* encode the digest using base64 */
1661 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1662 cc > 0; cc -= 3, op += 4) {
1666 bits = (*dp++ & 0xff) << 16;
1668 bits |= (*dp++ & 0xff) << 8;
1670 bits |= *dp++ & 0xff;
1673 for (bp = op + 4; bp > op; bits >>= 6)
1674 *--bp = nib2b64[bits & 0x3f];
1682 /* null terminate string */
1685 /* now make copy and return string */
1686 vp = concat (" ", outbuf, "\n", NULL);