2 ** mhbuild.c -- expand/translate MIME composition files
4 ** This code is Copyright (c) 2002, by the authors of nmh. See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
10 ** This code was originally part of mhn.c. I split it into
11 ** a separate program (mhbuild.c). But the code still has some of
12 ** the mhn.c code in it. This program needs additional
13 ** streamlining and removal of unneeded code.
18 #include <h/signals.h>
23 #include <h/mhparse.h>
24 #include <h/mhcachesbr.h>
27 #ifdef HAVE_SYS_TIME_H
28 # include <sys/time.h>
32 static struct swit switches[] = {
36 { "noebcdicsafe", 0 },
52 { "norfc934mode", 0 },
58 { "rcache policy", 0 },
60 { "wcache policy", 0 },
61 #define CONTENTIDSW 14
63 #define NCONTENTIDSW 15
76 ** Directory to place tmp files. This must
77 ** be set before these routines are called.
83 static char prefix[] = "----- =_aaaaaaaaaa";
91 int make_intermediates(char *);
92 void content_error(char *, CT, char *, ...);
95 int find_cache(CT, int, int *, char *, char *, int);
98 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
101 void free_content(CT);
102 void free_ctinfo(CT);
103 void free_encoding(CT, int);
108 static int init_decoded_content(CT);
109 static char *fgetstr(char *, int, FILE *);
110 static int user_content(FILE *, char *, char *, CT *);
111 static void set_id(CT, int);
112 static int compose_content(CT);
113 static int scan_content(CT);
114 static int build_headers(CT);
115 static CT build_mime(char *);
122 extern char *cache_public;
123 extern char *cache_private;
136 static char infile[BUFSIZ];
137 static int unlink_infile = 0;
139 static char outfile[BUFSIZ];
140 static int unlink_outfile = 0;
142 static void unlink_done(int) NORETURN;
145 int output_message(CT, char *);
146 int output_message_fp(CT, FILE *, char*);
149 int list_all_messages(CT *, int, int, int, int);
152 void set_endian(void);
155 void free_content(CT);
159 main(int argc, char **argv)
161 int sizesw = 1, headsw = 1;
163 char *cp, buf[BUFSIZ];
164 char buffer[BUFSIZ], *compfile = NULL;
165 char **argp, **arguments;
173 setlocale(LC_ALL, "");
175 invo_name = mhbasename(argv[0]);
177 /* read user profile/context */
180 arguments = getarguments(invo_name, argc, argv, 1);
183 while ((cp = *argp++)) {
184 if (cp[0] == '-' && cp[1] == '\0') {
186 adios(NULL, "cannot specify both standard input and a file");
189 listsw = 0; /* turn off -list if using std in/out */
190 verbosw = 0; /* turn off -verbose listings */
194 switch (smatch(++cp, switches)) {
196 ambigsw(cp, switches);
199 adios(NULL, "-%s unknown", cp);
202 snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
203 print_help(buf, switches, 1);
206 print_version(invo_name);
210 icachesw = &rcachesw;
213 icachesw = &wcachesw;
215 if (!(cp = *argp++) || *cp == '-')
216 adios(NULL, "missing argument to %s",
218 switch (*icachesw = smatch(cp, caches)) {
223 adios(NULL, "%s unknown", cp);
283 adios(NULL, "only one composition file allowed");
290 if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
294 ** Check if we've specified an additional profile
296 if ((cp = getenv("MHBUILD"))) {
297 if ((fp = fopen(cp, "r"))) {
298 readconfig((struct node **) 0, fp, cp, 0);
301 admonish("", "unable to read $MHBUILD profile (%s)",
307 ** Read the standard profile setup
309 if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
310 readconfig((struct node **) 0, fp, cp, 0);
314 /* Check for public cache location */
315 if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
318 /* Check for private cache location */
319 if (!(cache_private = context_find(nmhprivcache)))
320 cache_private = ".cache";
321 cache_private = getcpy(toabsdir(cache_private));
324 ** Check for storage directory. If defined, we
325 ** will store temporary files there. Else we
326 ** store them in standard nmh directory.
328 if ((cp = context_find(nmhstorage)) && *cp)
329 tmp = concat(cp, "/", invo_name, NULL);
331 tmp = getcpy(toabsdir(invo_name));
333 /* Check if we have a file to process */
335 adios(NULL, "need to specify a %s composition file",
339 ** Process the composition file from standard input.
341 if (compfile[0] == '-' && compfile[1] == '\0') {
342 /* copy standard input to temporary file */
343 strncpy(infile, m_mktemp(invo_name, NULL, &fp),
345 while (fgets(buffer, BUFSIZ, stdin))
350 /* build the content structures for MIME message */
351 ct = build_mime(infile);
355 /* output MIME message to this temporary file */
356 strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
360 /* output the message */
361 output_message_fp(ct, fp_out, outfile);
364 /* output the temp file to standard output */
365 if ((fp = fopen(outfile, "r")) == NULL)
366 adios(outfile, "unable to open");
367 while (fgets(buffer, BUFSIZ, fp))
368 fputs(buffer, stdout);
382 ** Process the composition file from a file.
385 /* build the content structures for MIME message */
386 ct = build_mime(compfile);
390 /* output MIME message to this temporary file */
391 strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
395 /* output the message */
396 output_message_fp(ct, fp_out, outfile);
400 ** List the message info
403 list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
405 /* Rename composition draft */
406 snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
407 if (rename(compfile, buffer) == NOTOK) {
408 adios(compfile, "unable to rename comp draft %s to", buffer);
411 /* Rename output file to take its place */
412 if (rename(outfile, compfile) == NOTOK) {
413 advise(outfile, "unable to rename output %s to", compfile);
414 rename(buffer, compfile);
426 unlink_done(int status)
429 ** Check if we need to remove stray temporary files.
440 ** Main routine for translating composition file
441 ** into valid MIME message. It translates the draft
442 ** into a content structure (actually a tree of content
443 ** structures). This message then can be manipulated
444 ** in various ways, including being output via
448 build_mime(char *infile)
451 char buf[BUFSIZ], name[NAMESZ];
460 /* open the composition draft */
461 if ((in = fopen(infile, "r")) == NULL)
462 adios(infile, "unable to open for reading");
465 ** Allocate space for primary (outside) content
467 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
468 adios(NULL, "out of memory");
471 ** Allocate structure for handling decoded content
472 ** for this part. We don't really need this, but
473 ** allocate it to remain consistent.
475 init_decoded_content(ct);
478 ** Parse some of the header fields in the composition
479 ** draft into the linked list of header fields for
480 ** the new MIME message.
482 for (compnum = 1, state = FLD;;) {
483 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
489 /* abort if draft has Mime-Version header field */
490 if (!mh_strcasecmp(name, VRSN_FIELD))
491 adios(NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
494 ** abort if draft has Content-Transfer-Encoding
497 if (!mh_strcasecmp(name, ENCODING_FIELD))
498 adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
500 /* ignore any Content-Type fields in the header */
501 if (!mh_strcasecmp(name, TYPE_FIELD)) {
502 while (state == FLDPLUS)
503 state = m_getfld(state, name, buf,
508 /* get copies of the buffers */
512 /* if necessary, get rest of field */
513 while (state == FLDPLUS) {
514 state = m_getfld(state, name, buf,
516 vp = add(buf, vp); /* add to prev value */
519 /* Now add the header data to the list */
520 add_header(ct, np, vp);
523 /* if this wasn't the last hdr field, then continue */
529 adios(NULL, "draft has empty body -- no directives!");
534 fseek(in, (long) (-strlen(buf)), SEEK_CUR);
539 adios(NULL, "message format error in component #%d",
543 adios(NULL, "getfld() returned %d", state);
549 ** Now add the MIME-Version header field
550 ** to the list of header fields.
552 np = getcpy(VRSN_FIELD);
553 vp = concat(" ", VRSN_VALUE, "\n", NULL);
554 add_header(ct, np, vp);
557 ** We initally assume we will find multiple contents in the
558 ** draft. So create a multipart/mixed content to hold everything.
559 ** We can remove this later, if it is not needed.
561 if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
563 ct->c_type = CT_MULTIPART;
564 ct->c_subtype = MULTI_MIXED;
565 ct->c_file = getcpy(infile);
567 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
568 adios(NULL, "out of memory");
569 ct->c_ctparams = (void *) m;
573 ** read and parse the composition file
574 ** and the directives it contains.
576 while (fgetstr(buf, sizeof(buf) - 1, in)) {
580 if (user_content(in, infile, buf, &p) == DONE) {
581 admonish(NULL, "ignoring spurious #end");
587 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
588 adios(NULL, "out of memory");
595 ** close the composition draft since
596 ** it's not needed any longer.
600 /* check if any contents were found */
602 adios(NULL, "no content directives found");
605 ** If only one content was found, then remove and
606 ** free the outer multipart content.
608 if (!m->mp_parts->mp_next) {
611 p = m->mp_parts->mp_part;
612 m->mp_parts->mp_part = NULL;
614 /* move header fields */
615 p->c_first_hf = ct->c_first_hf;
616 p->c_last_hf = ct->c_last_hf;
617 ct->c_first_hf = NULL;
618 ct->c_last_hf = NULL;
627 ** Fill out, or expand directives. Parse and execute
628 ** commands specified by profile composition strings.
632 if ((cp = strchr(prefix, 'a')) == NULL)
633 adios(NULL, "internal error(4)");
636 ** Scan the contents. Choose a transfer encoding, and
637 ** check if prefix for multipart boundary clashes with
638 ** any of the contents.
640 while (scan_content(ct) == NOTOK) {
645 adios(NULL, "giving up trying to find a unique delimiter string");
651 /* Build the rest of the header field structures */
659 ** Set up structures for placing unencoded
660 ** content when building parts.
664 init_decoded_content(CT ct)
668 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
669 adios(NULL, "out of memory");
672 ct->c_ceopenfnx = open7Bit; /* since unencoded */
673 ct->c_ceclosefnx = close_encoding;
674 ct->c_cesizefnx = NULL; /* since unencoded */
681 fgetstr(char *s, int n, FILE *stream)
685 for (ep = (cp = s) + n; cp < ep; ) {
688 if (!fgets(cp, n, stream))
689 return (cp != s ? s : NULL);
690 if (cp == s && *cp != '#')
693 cp += (i = strlen(cp)) - 1;
694 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
705 ** Parse the composition draft for text and directives.
706 ** Do initial setup of Content structure.
710 user_content(FILE *in, char *file, char *buf, CT *ctp)
719 struct str2init *s2i;
724 if (buf[0] == '\n' || strcmp(buf, "#\n") == 0) {
729 /* allocate basic Content structure */
730 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
731 adios(NULL, "out of memory");
734 /* allocate basic structure for handling decoded content */
735 init_decoded_content(ct);
742 ** Handle inline text. Check if line
743 ** is one of the following forms:
745 ** 1) doesn't begin with '#' (implicit directive)
746 ** 2) begins with "##" (implicit directive)
747 ** 3) begins with "#<"
749 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
753 char content[BUFSIZ];
757 cp = m_mktemp2(NULL, invo_name, NULL, &out);
759 adios("mhbuild", "unable to create temporary file");
761 /* use a temp file to collect the plain text lines */
762 ce->ce_file = getcpy(cp);
765 if (buf[0] == '#' && buf[1] == '<') {
766 strncpy(content, buf + 2, sizeof(content));
773 /* the directive is implicit */
774 strncpy(content, "text/plain", sizeof(content));
776 strncpy(buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
780 if (headers >= 0 && uprf(buffer, DESCR_FIELD) &&
781 buffer[i=strlen(DESCR_FIELD)] == ':') {
785 ct->c_descr = add(buffer + i + 1, ct->c_descr);
786 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
787 adios(NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
795 adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
803 if (headers >= 0 && uprf(buffer, DISPO_FIELD)
804 && buffer[i = strlen(DISPO_FIELD)] == ':') {
808 ct->c_dispo = add(buffer + i + 1, ct->c_dispo);
809 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
810 adios(NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
818 adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
826 if (headers != 1 || buffer[0] != '\n')
832 if ((cp = fgetstr(buffer, sizeof(buffer) - 1, in))
835 if (buffer[0] == '#') {
838 if (buffer[1] != '#')
840 for (cp = (bp = buffer) + 1; *cp; cp++)
847 ct->c_end = ftell(out);
850 /* parse content type */
851 if (get_ctinfo(content, ct, inlineD) == NOTOK)
854 for (s2i = str2cts; s2i->si_key; s2i++)
855 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
857 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
861 ** check type specified (possibly implicitly)
863 switch (ct->c_type = s2i->si_val) {
865 if (!mh_strcasecmp(ci->ci_subtype, "rfc822")) {
866 ct->c_encoding = CE_7BIT;
871 adios(NULL, "it doesn't make sense to define an in-line %s content",
872 ct->c_type == CT_MESSAGE ? "message" :
878 if ((ct->c_ctinitfnx = s2i->si_init))
879 (*ct->c_ctinitfnx) (ct);
884 fseek(in, pos, SEEK_SET);
889 ** If we've reached this point, the next line
890 ** must be some type of explicit directive.
893 /* check if directive is external-type */
894 extrnal = (buf[1] == '@');
896 /* parse directive */
897 if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
900 /* check directive against the list of MIME types */
901 for (s2i = str2cts; s2i->si_key; s2i++)
902 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
906 ** Check if the directive specified a valid type.
907 ** This will happen if it was one of the following forms:
909 ** #type/subtype (or)
914 adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
916 switch (ct->c_type = s2i->si_val) {
918 adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
922 if (!mh_strcasecmp(ci->ci_subtype, "partial"))
923 adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
924 if (!mh_strcasecmp(ci->ci_subtype, "external-body"))
925 adios(NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
927 adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
931 if ((ct->c_ctinitfnx = s2i->si_init))
932 (*ct->c_ctinitfnx) (ct);
937 ** #@type/subtype (external types directive)
944 adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
947 snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
952 ** Since we are using the current Content structure to
953 ** hold information about the type of the external
954 ** reference, we need to create another Content
955 ** structure for the message/external-body to wrap
958 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
959 adios(NULL, "out of memory");
962 if (get_ctinfo(buffer, ct, 0) == NOTOK)
964 ct->c_type = CT_MESSAGE;
965 ct->c_subtype = MESSAGE_EXTERNAL;
967 if ((e = (struct exbody *)
968 calloc(1, sizeof(*e))) == NULL)
969 adios(NULL, "out of memory");
970 ct->c_ctparams = (void *) e;
976 if (params_external(ct, 1) == NOTOK)
982 /* Handle [file] argument */
984 /* check if specifies command to execute */
985 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
986 for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
989 adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
994 /* record filename of decoded contents */
995 ce->ce_file = ci->ci_magic;
996 if (access(ce->ce_file, R_OK) == NOTOK)
997 adios("reading", "unable to access %s for", ce->ce_file);
998 if (listsw && stat(ce->ce_file, &st) != NOTOK)
999 ct->c_end = (long) st.st_size;
1000 ci->ci_magic = NULL;
1006 ** No [file] argument, so check profile for
1007 ** method to compose content.
1009 snprintf(buffer, sizeof(buffer), "%s-compose-%s/%s",
1010 invo_name, ci->ci_type, ci->ci_subtype);
1011 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
1012 snprintf(buffer, sizeof(buffer), "%s-compose-%s",
1013 invo_name, ci->ci_type);
1014 if ((cp = context_find(buffer)) == NULL ||
1016 content_error(NULL, ct, "don't know how to compose content");
1020 ci->ci_magic = getcpy(cp);
1025 adios(NULL, "external definition not allowed for \"#%s\"",
1029 ** Message directive
1030 ** #forw [+folder] [msgs]
1032 if (!mh_strcasecmp(ci->ci_type, "forw")) {
1034 char *folder, *arguments[MAXARGS];
1040 ap = brkstring(ci->ci_magic, " ", "\n");
1041 for (i=0; ap[i] && i<MAXARGS-1; i++) {
1042 arguments[i] = ap[i];
1044 arguments[i] = NULL;
1047 arguments[0] = seq_cur;
1048 arguments[1] = NULL;
1052 /* search the arguments for a folder name */
1053 for (ap = arguments; *ap; ap++) {
1055 if (*cp == '+' || *cp == '@') {
1057 adios(NULL, "only one folder per #forw directive");
1059 folder = getcpy(expandfol(cp));
1063 /* else, use the current folder */
1065 folder = getcpy(getcurfol());
1067 if (!(mp = folder_read(folder)))
1068 adios(NULL, "unable to read folder %s", folder);
1069 for (ap = arguments; *ap; ap++) {
1071 if (*cp != '+' && *cp != '@')
1072 if (!m_convert(mp, cp))
1079 ** If there is more than one message to include, make this
1080 ** a content of type "multipart/digest" and insert each message
1081 ** as a subpart. If there is only one message, then make this
1082 ** a content of type "message/rfc822".
1084 if (mp->numsel > 1) {
1085 /* we are forwarding multiple messages */
1086 if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
1088 ct->c_type = CT_MULTIPART;
1089 ct->c_subtype = MULTI_DIGEST;
1091 if ((m = (struct multipart *)
1092 calloc(1, sizeof(*m))) == NULL)
1093 adios(NULL, "out of memory");
1094 ct->c_ctparams = (void *) m;
1097 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
1098 if (is_selected(mp, msgnum)) {
1103 if ((p = (CT) calloc(1, sizeof(*p)))
1105 adios(NULL, "out of memory");
1106 init_decoded_content(p);
1108 if (get_ctinfo("message/rfc822", p, 0)
1111 p->c_type = CT_MESSAGE;
1112 p->c_subtype = MESSAGE_RFC822;
1114 snprintf(buffer, sizeof(buffer),
1115 "%s/%d", mp->foldpath,
1117 pe->ce_file = getcpy(buffer);
1118 if (listsw && stat(pe->ce_file, &st)
1120 p->c_end = (long) st.st_size;
1122 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
1123 adios(NULL, "out of memory");
1125 pp = &part->mp_next;
1130 /* we are forwarding one message */
1131 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
1133 ct->c_type = CT_MESSAGE;
1134 ct->c_subtype = MESSAGE_RFC822;
1136 msgnum = mp->lowsel;
1137 snprintf(buffer, sizeof(buffer), "%s/%d",
1138 mp->foldpath, msgnum);
1139 ce->ce_file = getcpy(buffer);
1140 if (listsw && stat(ce->ce_file, &st) != NOTOK)
1141 ct->c_end = (long) st.st_size;
1144 folder_free(mp); /* free folder/message structure */
1151 if (!mh_strcasecmp(ci->ci_type, "end")) {
1158 ** #begin [ alternative | parallel ]
1160 if (!mh_strcasecmp(ci->ci_type, "begin")) {
1161 if (!ci->ci_magic) {
1163 cp = SubMultiPart[vrsn - 1].kv_key;
1164 } else if (!mh_strcasecmp(ci->ci_magic, "alternative")) {
1165 vrsn = MULTI_ALTERNATE;
1166 cp = SubMultiPart[vrsn - 1].kv_key;
1167 } else if (!mh_strcasecmp(ci->ci_magic, "parallel")) {
1168 vrsn = MULTI_PARALLEL;
1169 cp = SubMultiPart[vrsn - 1].kv_key;
1170 } else if (uprf(ci->ci_magic, "digest")) {
1173 vrsn = MULTI_UNKNOWN;
1178 snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
1179 if (get_ctinfo(buffer, ct, 0) == NOTOK)
1181 ct->c_type = CT_MULTIPART;
1182 ct->c_subtype = vrsn;
1184 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1185 adios(NULL, "out of memory");
1186 ct->c_ctparams = (void *) m;
1189 while (fgetstr(buffer, sizeof(buffer) - 1, in)) {
1193 if (user_content(in, file, buffer, &p) == DONE) {
1195 adios(NULL, "empty \"#begin ... #end\" sequence");
1201 if ((part = (struct part *)
1202 calloc(1, sizeof(*part))) == NULL)
1203 adios(NULL, "out of memory");
1205 pp = &part->mp_next;
1208 admonish(NULL, "premature end-of-file, missing #end");
1213 ** Unknown directive
1215 adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
1216 return NOTOK; /* NOT REACHED */
1221 set_id(CT ct, int top)
1225 static time_t clock = 0;
1226 static char *msgfmt;
1230 snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
1231 (int) getpid(), (long) clock, LocalName());
1233 msgfmt = getcpy(msgid);
1235 snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
1236 ct->c_id = getcpy(msgid);
1240 static char ebcdicsafe[0x100] = {
1241 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1242 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1243 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1244 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1245 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
1246 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1247 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1248 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1249 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1250 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1251 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1252 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
1253 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1254 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1255 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1256 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1257 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1258 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1259 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1261 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1264 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1265 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1266 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1267 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1268 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1269 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1270 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1271 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1272 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1277 ** Fill out, or expand the various contents in the composition
1278 ** draft. Read-in any necessary files. Parse and execute any
1279 ** commands specified by profile composition strings.
1283 compose_content(CT ct)
1285 CE ce = ct->c_cefile;
1287 switch (ct->c_type) {
1292 char partnam[BUFSIZ];
1293 struct multipart *m = (struct multipart *) ct->c_ctparams;
1297 snprintf(partnam, sizeof(partnam), "%s.",
1299 pp = partnam + strlen(partnam);
1304 /* first, we call compose_content on all the subparts */
1305 for (part = m->mp_parts, partnum = 1; part;
1306 part = part->mp_next, partnum++) {
1307 CT p = part->mp_part;
1309 sprintf(pp, "%d", partnum);
1310 p->c_partno = getcpy(partnam);
1311 if (compose_content(p) == NOTOK)
1316 ** If the -rfc934mode switch is given, then check all
1317 ** the subparts of a multipart/digest. If they are all
1318 ** message/rfc822, then mark this content and all
1319 ** subparts with the rfc934 compatibility mode flag.
1321 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
1324 for (part = m->mp_parts; part; part = part->mp_next) {
1325 CT p = part->mp_part;
1327 if (p->c_subtype != MESSAGE_RFC822) {
1332 ct->c_rfc934 = is934;
1333 for (part = m->mp_parts; part; part = part->mp_next) {
1334 CT p = part->mp_part;
1336 if ((p->c_rfc934 = is934))
1342 ct->c_end = (partnum = strlen(prefix) + 2) + 2;
1346 for (part = m->mp_parts; part; part = part->mp_next)
1347 ct->c_end += part->mp_part->c_end + partnum;
1353 /* Nothing to do for type message */
1357 ** Discrete types (text/application/audio/image/video)
1362 int xstdout, len, buflen;
1363 char *bp, **ap, *cp;
1364 char *vec[4], buffer[BUFSIZ];
1366 CI ci = &ct->c_ctinfo;
1369 if (!(cp = ci->ci_magic))
1370 adios(NULL, "internal error(5)");
1372 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1373 if (tfile == NULL) {
1374 adios("mhbuild", "unable to create temporary file");
1376 ce->ce_file = getcpy(tfile);
1381 /* Get buffer ready to go */
1384 buflen = sizeof(buffer);
1387 ** Parse composition string into buffer
1389 for ( ; *cp; cp++) {
1395 ** insert parameters from
1401 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1402 snprintf(bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1412 /* %f, and stdout is not-redirected */
1418 ** insert temporary filename
1419 ** where content should be
1422 snprintf(bp, buflen, "%s", ce->ce_file);
1426 /* insert content subtype */
1427 strncpy(bp, ci->ci_subtype, buflen);
1431 /* insert character % */
1452 printf("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer);
1454 fflush(stdout); /* not sure if need for -noverbose */
1461 if ((out = fopen(ce->ce_file, "w")) == NULL)
1462 adios(ce->ce_file, "unable to open for writing");
1464 switch (child_id = fork()) {
1466 adios("fork", "unable to fork");
1471 dup2(fileno(out), 1);
1473 execvp("/bin/sh", vec);
1474 fprintf(stderr, "unable to exec ");
1481 if (pidXwait(child_id, NULL))
1487 /* Check size of file */
1488 if (listsw && ct->c_end == 0L) {
1491 if (stat(ce->ce_file, &st) != NOTOK)
1492 ct->c_end = (long) st.st_size;
1502 ** Scan the content.
1504 ** 1) choose a transfer encoding.
1505 ** 2) check for clashes with multipart boundary string.
1506 ** 3) for text content, figure out which character set is being used.
1508 ** If there is a clash with one of the contents and the multipart boundary,
1509 ** this function will exit with NOTOK. This will cause the scanning process
1510 ** to be repeated with a different multipart boundary. It is possible
1511 ** (although highly unlikely) that this scan will be repeated multiple times.
1518 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1519 int checklinelen = 0, linelen = 0; /* check for long lines */
1520 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1521 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1522 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1523 unsigned char *cp = NULL, buffer[BUFSIZ];
1524 struct text *t = NULL;
1526 CE ce = ct->c_cefile;
1529 ** handle multipart by scanning all subparts
1530 ** and then checking their encoding.
1532 if (ct->c_type == CT_MULTIPART) {
1533 struct multipart *m = (struct multipart *) ct->c_ctparams;
1536 /* initially mark the domain of enclosing multipart as 7bit */
1537 ct->c_encoding = CE_7BIT;
1539 for (part = m->mp_parts; part; part = part->mp_next) {
1540 CT p = part->mp_part;
1542 if (scan_content(p) == NOTOK) {
1543 /* choose encoding for subpart */
1548 ** if necessary, enlarge encoding for enclosing
1551 if (p->c_encoding == CE_BINARY)
1552 ct->c_encoding = CE_BINARY;
1553 if (p->c_encoding == CE_8BIT &&
1554 ct->c_encoding != CE_BINARY)
1555 ct->c_encoding = CE_8BIT;
1562 ** Decide what to check while scanning this content.
1564 switch (ct->c_type) {
1568 if (ct->c_subtype == TEXT_PLAIN) {
1573 checkebcdic = ebcdicsw;
1579 case CT_APPLICATION:
1581 checkebcdic = ebcdicsw;
1593 /* don't check anything for message/external */
1594 if (ct->c_subtype == MESSAGE_EXTERNAL)
1604 ** Don't check anything for these types,
1605 ** since we are forcing use of base64.
1616 ** Scan the unencoded content
1618 if (check8bit || checklinelen || checklinespace || checkboundary) {
1619 if ((in = fopen(ce->ce_file, "r")) == NULL)
1620 adios(ce->ce_file, "unable to open for reading");
1621 len = strlen(prefix);
1623 while (fgets(buffer, sizeof(buffer) - 1, in)) {
1625 ** Check for 8bit data.
1628 for (cp = buffer; *cp; cp++) {
1629 if (!isascii(*cp)) {
1631 /* no need to keep checking */
1635 ** Check if character is ebcdic-safe.
1636 ** We only check this if also checking
1639 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1641 /* no need to keep checking */
1648 ** Check line length.
1650 if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
1652 checklinelen = 0; /* no need to keep checking */
1656 ** Check if line ends with a space.
1658 if (checklinespace &&
1659 (cp = buffer + strlen(buffer) - 2) >
1660 buffer && isspace(*cp)) {
1662 /* no need to keep checking */
1667 ** Check if content contains a line that clashes
1668 ** with our standard boundary for multipart messages.
1670 if (checkboundary && buffer[0] == '-' &&
1672 for (cp = buffer + strlen(buffer) - 1;
1677 if (strncmp(buffer + 2, prefix, len)==0 &&
1678 isdigit(buffer[2 + len])) {
1680 /* no need to keep checking */
1689 ** Decide which transfer encoding to use.
1691 switch (ct->c_type) {
1694 ** If the text content didn't specify a character
1695 ** set, we need to figure out which one was used.
1697 t = (struct text *) ct->c_ctparams;
1698 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1699 CI ci = &ct->c_ctinfo;
1702 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1706 t->tx_charset = CHARSET_UNKNOWN;
1707 *ap = concat("charset=", write_charset_8bit(),
1710 t->tx_charset = CHARSET_USASCII;
1711 *ap = getcpy("charset=us-ascii");
1714 cp = strchr(*ap++, '=');
1720 if (contains8bit || ebcdicunsafe || linelen || linespace)
1721 ct->c_encoding = CE_QUOTED;
1723 ct->c_encoding = CE_7BIT;
1726 case CT_APPLICATION:
1727 /* For application type, use base64, except when postscript */
1728 if (contains8bit || ebcdicunsafe || linelen || linespace)
1729 ct->c_encoding = (ct->c_subtype ==
1730 APPLICATION_POSTSCRIPT) ?
1731 CE_QUOTED : CE_BASE64;
1733 ct->c_encoding = CE_7BIT;
1737 ct->c_encoding = CE_7BIT;
1743 /* For audio, image, and video contents, just use base64 */
1744 ct->c_encoding = CE_BASE64;
1748 return (boundaryclash ? NOTOK : OK);
1753 ** Scan the content structures, and build header
1754 ** fields that will need to be output into the
1759 build_headers(CT ct)
1761 int cc, mailbody, len;
1763 char *np, *vp, buffer[BUFSIZ];
1764 CI ci = &ct->c_ctinfo;
1767 ** If message is type multipart, then add the multipart
1768 ** boundary to the list of attribute/value pairs.
1770 if (ct->c_type == CT_MULTIPART) {
1772 static int level = 0; /* store nesting level */
1776 snprintf(buffer, sizeof(buffer), "boundary=%s%d",
1778 cp = strchr(*ap++ = getcpy(buffer), '=');
1785 ** Skip the output of Content-Type, parameters, content
1786 ** description and disposition, and Content-ID if the
1787 ** content is of type "message" and the rfc934 compatibility
1788 ** flag is set (which means we are inside multipart/digest
1789 ** and the switch -rfc934mode was given).
1791 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1795 ** output the content type and subtype
1797 np = getcpy(TYPE_FIELD);
1798 vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1800 /* keep track of length of line */
1801 len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
1802 strlen(ci->ci_subtype) + 3;
1804 mailbody = ct->c_type == CT_MESSAGE &&
1805 ct->c_subtype == MESSAGE_EXTERNAL &&
1806 ((struct exbody *) ct->c_ctparams)->eb_body;
1809 ** Append the attribute/value pairs to
1810 ** the end of the Content-Type line.
1812 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1813 if (mailbody && !mh_strcasecmp(*ap, "body"))
1819 snprintf(buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1820 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
1821 vp = add("\n\t", vp);
1827 vp = add(buffer, vp);
1832 ** Append any RFC-822 comment to the end of
1833 ** the Content-Type line.
1835 if (ci->ci_comment) {
1836 snprintf(buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1837 if (len + 1 + (cc = 2 + strlen(ci->ci_comment)) >= CPERLIN) {
1838 vp = add("\n\t", vp);
1844 vp = add(buffer, vp);
1848 add_header(ct, np, vp);
1851 ** output the Content-ID, unless disabled by -nocontentid
1853 if (contentidsw && ct->c_id) {
1854 np = getcpy(ID_FIELD);
1855 vp = concat(" ", ct->c_id, NULL);
1856 add_header(ct, np, vp);
1860 ** output the Content-Description
1863 np = getcpy(DESCR_FIELD);
1864 vp = concat(" ", ct->c_descr, NULL);
1865 add_header(ct, np, vp);
1869 ** output the Content-Disposition
1872 np = getcpy(DISPO_FIELD);
1873 vp = concat(" ", ct->c_dispo, NULL);
1874 add_header(ct, np, vp);
1879 ** If this is the internal content structure for a
1880 ** "message/external", then we are done with the
1881 ** headers (since it has no body).
1887 ** output the Content-Transfer-Encoding
1889 switch (ct->c_encoding) {
1891 /* Nothing to output */
1895 if (ct->c_type == CT_MESSAGE)
1896 adios(NULL, "internal error, invalid encoding");
1898 np = getcpy(ENCODING_FIELD);
1899 vp = concat(" ", "8bit", "\n", NULL);
1900 add_header(ct, np, vp);
1904 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1905 adios(NULL, "internal error, invalid encoding");
1907 np = getcpy(ENCODING_FIELD);
1908 vp = concat(" ", "quoted-printable", "\n", NULL);
1909 add_header(ct, np, vp);
1913 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1914 adios(NULL, "internal error, invalid encoding");
1916 np = getcpy(ENCODING_FIELD);
1917 vp = concat(" ", "base64", "\n", NULL);
1918 add_header(ct, np, vp);
1922 if (ct->c_type == CT_MESSAGE)
1923 adios(NULL, "internal error, invalid encoding");
1925 np = getcpy(ENCODING_FIELD);
1926 vp = concat(" ", "binary", "\n", NULL);
1927 add_header(ct, np, vp);
1931 adios(NULL, "unknown transfer encoding in content");
1936 ** Additional content specific header processing
1938 switch (ct->c_type) {
1941 struct multipart *m;
1944 m = (struct multipart *) ct->c_ctparams;
1945 for (part = m->mp_parts; part; part = part->mp_next) {
1955 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1958 e = (struct exbody *) ct->c_ctparams;
1959 build_headers(e->eb_content);