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>
26 #ifdef HAVE_SYS_TIME_H
27 # include <sys/time.h>
31 static struct swit switches[] = {
35 { "noebcdicsafe", 0 },
51 { "norfc934mode", 0 },
56 #define CONTENTIDSW 12
58 #define NCONTENTIDSW 13
71 ** Directory to place tmp files. This must
72 ** be set before these routines are called.
78 static char prefix[] = "----- =_aaaaaaaaaa";
86 int make_intermediates(char *);
87 void content_error(char *, CT, char *, ...);
90 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
93 void free_content(CT);
95 void free_encoding(CT, int);
100 static int init_decoded_content(CT);
101 static char *fgetstr(char *, int, FILE *);
102 static int user_content(FILE *, char *, char *, CT *);
103 static void set_id(CT, int);
104 static int compose_content(CT);
105 static int scan_content(CT);
106 static int build_headers(CT);
107 static CT build_mime(char *);
121 static char infile[BUFSIZ];
122 static int unlink_infile = 0;
124 static char outfile[BUFSIZ];
125 static int unlink_outfile = 0;
127 static void unlink_done(int) NORETURN;
130 int output_message(CT, char *);
131 int output_message_fp(CT, FILE *, char*);
134 int list_all_messages(CT *, int, int, int, int);
137 void set_endian(void);
140 void free_content(CT);
144 main(int argc, char **argv)
146 int sizesw = 1, headsw = 1;
147 char *cp, buf[BUFSIZ];
148 char buffer[BUFSIZ], *compfile = NULL;
149 char **argp, **arguments;
157 setlocale(LC_ALL, "");
159 invo_name = mhbasename(argv[0]);
161 /* read user profile/context */
164 arguments = getarguments(invo_name, argc, argv, 1);
167 while ((cp = *argp++)) {
168 if (cp[0] == '-' && cp[1] == '\0') {
170 adios(NULL, "cannot specify both standard input and a file");
173 listsw = 0; /* turn off -list if using std in/out */
174 verbosw = 0; /* turn off -verbose listings */
178 switch (smatch(++cp, switches)) {
180 ambigsw(cp, switches);
183 adios(NULL, "-%s unknown", cp);
186 snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
187 print_help(buf, switches, 1);
190 print_version(invo_name);
247 adios(NULL, "only one composition file allowed");
254 if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
258 ** Check if we've specified an additional profile
260 if ((cp = getenv("MHBUILD"))) {
261 if ((fp = fopen(cp, "r"))) {
262 readconfig((struct node **) 0, fp, cp, 0);
265 admonish("", "unable to read $MHBUILD profile (%s)",
271 ** Read the standard profile setup
273 if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
274 readconfig((struct node **) 0, fp, cp, 0);
279 ** Check for storage directory. If defined, we
280 ** will store temporary files there. Else we
281 ** store them in standard nmh directory.
283 if ((cp = context_find(nmhstorage)) && *cp)
284 tmp = concat(cp, "/", invo_name, NULL);
286 tmp = getcpy(toabsdir(invo_name));
288 /* Check if we have a file to process */
290 adios(NULL, "need to specify a %s composition file",
294 ** Process the composition file from standard input.
296 if (compfile[0] == '-' && compfile[1] == '\0') {
297 /* copy standard input to temporary file */
298 strncpy(infile, m_mktemp(invo_name, NULL, &fp),
300 while (fgets(buffer, BUFSIZ, stdin))
305 /* build the content structures for MIME message */
306 ct = build_mime(infile);
310 /* output MIME message to this temporary file */
311 strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
315 /* output the message */
316 output_message_fp(ct, fp_out, outfile);
319 /* output the temp file to standard output */
320 if ((fp = fopen(outfile, "r")) == NULL)
321 adios(outfile, "unable to open");
322 while (fgets(buffer, BUFSIZ, fp))
323 fputs(buffer, stdout);
337 ** Process the composition file from a file.
340 /* build the content structures for MIME message */
341 ct = build_mime(compfile);
345 /* output MIME message to this temporary file */
346 strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
350 /* output the message */
351 output_message_fp(ct, fp_out, outfile);
355 ** List the message info
358 list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
360 /* Rename composition draft */
361 snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
362 if (rename(compfile, buffer) == NOTOK) {
363 adios(compfile, "unable to rename comp draft %s to", buffer);
366 /* Rename output file to take its place */
367 if (rename(outfile, compfile) == NOTOK) {
368 advise(outfile, "unable to rename output %s to", compfile);
369 rename(buffer, compfile);
381 unlink_done(int status)
384 ** Check if we need to remove stray temporary files.
395 ** Main routine for translating composition file
396 ** into valid MIME message. It translates the draft
397 ** into a content structure (actually a tree of content
398 ** structures). This message then can be manipulated
399 ** in various ways, including being output via
403 build_mime(char *infile)
406 char buf[BUFSIZ], name[NAMESZ];
415 /* open the composition draft */
416 if ((in = fopen(infile, "r")) == NULL)
417 adios(infile, "unable to open for reading");
420 ** Allocate space for primary (outside) content
422 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
423 adios(NULL, "out of memory");
426 ** Allocate structure for handling decoded content
427 ** for this part. We don't really need this, but
428 ** allocate it to remain consistent.
430 init_decoded_content(ct);
433 ** Parse some of the header fields in the composition
434 ** draft into the linked list of header fields for
435 ** the new MIME message.
437 for (compnum = 1, state = FLD;;) {
438 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
444 /* abort if draft has Mime-Version header field */
445 if (!mh_strcasecmp(name, VRSN_FIELD))
446 adios(NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
449 ** abort if draft has Content-Transfer-Encoding
452 if (!mh_strcasecmp(name, ENCODING_FIELD))
453 adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
455 /* ignore any Content-Type fields in the header */
456 if (!mh_strcasecmp(name, TYPE_FIELD)) {
457 while (state == FLDPLUS)
458 state = m_getfld(state, name, buf,
463 /* get copies of the buffers */
467 /* if necessary, get rest of field */
468 while (state == FLDPLUS) {
469 state = m_getfld(state, name, buf,
471 vp = add(buf, vp); /* add to prev value */
474 /* Now add the header data to the list */
475 add_header(ct, np, vp);
478 /* if this wasn't the last hdr field, then continue */
484 adios(NULL, "draft has empty body -- no directives!");
489 fseek(in, (long) (-strlen(buf)), SEEK_CUR);
494 adios(NULL, "message format error in component #%d",
498 adios(NULL, "getfld() returned %d", state);
504 ** Now add the MIME-Version header field
505 ** to the list of header fields.
507 np = getcpy(VRSN_FIELD);
508 vp = concat(" ", VRSN_VALUE, "\n", NULL);
509 add_header(ct, np, vp);
512 ** We initally assume we will find multiple contents in the
513 ** draft. So create a multipart/mixed content to hold everything.
514 ** We can remove this later, if it is not needed.
516 if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
518 ct->c_type = CT_MULTIPART;
519 ct->c_subtype = MULTI_MIXED;
520 ct->c_file = getcpy(infile);
522 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
523 adios(NULL, "out of memory");
524 ct->c_ctparams = (void *) m;
528 ** read and parse the composition file
529 ** and the directives it contains.
531 while (fgetstr(buf, sizeof(buf) - 1, in)) {
535 if (user_content(in, infile, buf, &p) == DONE) {
536 admonish(NULL, "ignoring spurious #end");
542 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
543 adios(NULL, "out of memory");
550 ** close the composition draft since
551 ** it's not needed any longer.
555 /* check if any contents were found */
557 adios(NULL, "no content directives found");
560 ** If only one content was found, then remove and
561 ** free the outer multipart content.
563 if (!m->mp_parts->mp_next) {
566 p = m->mp_parts->mp_part;
567 m->mp_parts->mp_part = NULL;
569 /* move header fields */
570 p->c_first_hf = ct->c_first_hf;
571 p->c_last_hf = ct->c_last_hf;
572 ct->c_first_hf = NULL;
573 ct->c_last_hf = NULL;
582 ** Fill out, or expand directives. Parse and execute
583 ** commands specified by profile composition strings.
587 if ((cp = strchr(prefix, 'a')) == NULL)
588 adios(NULL, "internal error(4)");
591 ** Scan the contents. Choose a transfer encoding, and
592 ** check if prefix for multipart boundary clashes with
593 ** any of the contents.
595 while (scan_content(ct) == NOTOK) {
600 adios(NULL, "giving up trying to find a unique delimiter string");
606 /* Build the rest of the header field structures */
614 ** Set up structures for placing unencoded
615 ** content when building parts.
619 init_decoded_content(CT ct)
623 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
624 adios(NULL, "out of memory");
627 ct->c_ceopenfnx = open7Bit; /* since unencoded */
628 ct->c_ceclosefnx = close_encoding;
629 ct->c_cesizefnx = NULL; /* since unencoded */
636 fgetstr(char *s, int n, FILE *stream)
640 for (ep = (cp = s) + n; cp < ep; ) {
643 if (!fgets(cp, n, stream))
644 return (cp != s ? s : NULL);
645 if (cp == s && *cp != '#')
648 cp += (i = strlen(cp)) - 1;
649 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
660 ** Parse the composition draft for text and directives.
661 ** Do initial setup of Content structure.
665 user_content(FILE *in, char *file, char *buf, CT *ctp)
674 struct str2init *s2i;
679 if (buf[0] == '\n' || strcmp(buf, "#\n") == 0) {
684 /* allocate basic Content structure */
685 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
686 adios(NULL, "out of memory");
689 /* allocate basic structure for handling decoded content */
690 init_decoded_content(ct);
697 ** Handle inline text. Check if line
698 ** is one of the following forms:
700 ** 1) doesn't begin with '#' (implicit directive)
701 ** 2) begins with "##" (implicit directive)
702 ** 3) begins with "#<"
704 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
708 char content[BUFSIZ];
712 cp = m_mktemp2(NULL, invo_name, NULL, &out);
714 adios("mhbuild", "unable to create temporary file");
716 /* use a temp file to collect the plain text lines */
717 ce->ce_file = getcpy(cp);
720 if (buf[0] == '#' && buf[1] == '<') {
721 strncpy(content, buf + 2, sizeof(content));
728 /* the directive is implicit */
729 strncpy(content, "text/plain", sizeof(content));
731 strncpy(buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
735 if (headers >= 0 && uprf(buffer, DESCR_FIELD) &&
736 buffer[i=strlen(DESCR_FIELD)] == ':') {
740 ct->c_descr = add(buffer + i + 1, ct->c_descr);
741 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
742 adios(NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
750 adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
758 if (headers >= 0 && uprf(buffer, DISPO_FIELD)
759 && buffer[i = strlen(DISPO_FIELD)] == ':') {
763 ct->c_dispo = add(buffer + i + 1, ct->c_dispo);
764 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
765 adios(NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
773 adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
781 if (headers != 1 || buffer[0] != '\n')
787 if ((cp = fgetstr(buffer, sizeof(buffer) - 1, in))
790 if (buffer[0] == '#') {
793 if (buffer[1] != '#')
795 for (cp = (bp = buffer) + 1; *cp; cp++)
802 ct->c_end = ftell(out);
805 /* parse content type */
806 if (get_ctinfo(content, ct, inlineD) == NOTOK)
809 for (s2i = str2cts; s2i->si_key; s2i++)
810 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
812 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
816 ** check type specified (possibly implicitly)
818 switch (ct->c_type = s2i->si_val) {
820 if (!mh_strcasecmp(ci->ci_subtype, "rfc822")) {
821 ct->c_encoding = CE_7BIT;
826 adios(NULL, "it doesn't make sense to define an in-line %s content",
827 ct->c_type == CT_MESSAGE ? "message" :
833 if ((ct->c_ctinitfnx = s2i->si_init))
834 (*ct->c_ctinitfnx) (ct);
839 fseek(in, pos, SEEK_SET);
844 ** If we've reached this point, the next line
845 ** must be some type of explicit directive.
848 /* check if directive is external-type */
849 extrnal = (buf[1] == '@');
851 /* parse directive */
852 if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
855 /* check directive against the list of MIME types */
856 for (s2i = str2cts; s2i->si_key; s2i++)
857 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
861 ** Check if the directive specified a valid type.
862 ** This will happen if it was one of the following forms:
864 ** #type/subtype (or)
869 adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
871 switch (ct->c_type = s2i->si_val) {
873 adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
877 if (!mh_strcasecmp(ci->ci_subtype, "partial"))
878 adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
879 if (!mh_strcasecmp(ci->ci_subtype, "external-body"))
880 adios(NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
882 adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
886 if ((ct->c_ctinitfnx = s2i->si_init))
887 (*ct->c_ctinitfnx) (ct);
892 ** #@type/subtype (external types directive)
899 adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
902 snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
907 ** Since we are using the current Content structure to
908 ** hold information about the type of the external
909 ** reference, we need to create another Content
910 ** structure for the message/external-body to wrap
913 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
914 adios(NULL, "out of memory");
917 if (get_ctinfo(buffer, ct, 0) == NOTOK)
919 ct->c_type = CT_MESSAGE;
920 ct->c_subtype = MESSAGE_EXTERNAL;
922 if ((e = (struct exbody *)
923 calloc(1, sizeof(*e))) == NULL)
924 adios(NULL, "out of memory");
925 ct->c_ctparams = (void *) e;
931 if (params_external(ct, 1) == NOTOK)
937 /* Handle [file] argument */
939 /* check if specifies command to execute */
940 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
941 for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
944 adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
949 /* record filename of decoded contents */
950 ce->ce_file = ci->ci_magic;
951 if (access(ce->ce_file, R_OK) == NOTOK)
952 adios("reading", "unable to access %s for", ce->ce_file);
953 if (listsw && stat(ce->ce_file, &st) != NOTOK)
954 ct->c_end = (long) st.st_size;
961 ** No [file] argument, so check profile for
962 ** method to compose content.
964 snprintf(buffer, sizeof(buffer), "%s-compose-%s/%s",
965 invo_name, ci->ci_type, ci->ci_subtype);
966 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
967 snprintf(buffer, sizeof(buffer), "%s-compose-%s",
968 invo_name, ci->ci_type);
969 if ((cp = context_find(buffer)) == NULL ||
971 content_error(NULL, ct, "don't know how to compose content");
975 ci->ci_magic = getcpy(cp);
980 adios(NULL, "external definition not allowed for \"#%s\"",
985 ** #forw [+folder] [msgs]
987 if (!mh_strcasecmp(ci->ci_type, "forw")) {
989 char *folder, *arguments[MAXARGS];
995 ap = brkstring(ci->ci_magic, " ", "\n");
996 for (i=0; ap[i] && i<MAXARGS-1; i++) {
997 arguments[i] = ap[i];
1002 arguments[0] = seq_cur;
1003 arguments[1] = NULL;
1007 /* search the arguments for a folder name */
1008 for (ap = arguments; *ap; ap++) {
1010 if (*cp == '+' || *cp == '@') {
1012 adios(NULL, "only one folder per #forw directive");
1014 folder = getcpy(expandfol(cp));
1018 /* else, use the current folder */
1020 folder = getcpy(getcurfol());
1022 if (!(mp = folder_read(folder)))
1023 adios(NULL, "unable to read folder %s", folder);
1024 for (ap = arguments; *ap; ap++) {
1026 if (*cp != '+' && *cp != '@')
1027 if (!m_convert(mp, cp))
1034 ** If there is more than one message to include, make this
1035 ** a content of type "multipart/digest" and insert each message
1036 ** as a subpart. If there is only one message, then make this
1037 ** a content of type "message/rfc822".
1039 if (mp->numsel > 1) {
1040 /* we are forwarding multiple messages */
1041 if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
1043 ct->c_type = CT_MULTIPART;
1044 ct->c_subtype = MULTI_DIGEST;
1046 if ((m = (struct multipart *)
1047 calloc(1, sizeof(*m))) == NULL)
1048 adios(NULL, "out of memory");
1049 ct->c_ctparams = (void *) m;
1052 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
1053 if (is_selected(mp, msgnum)) {
1058 if ((p = (CT) calloc(1, sizeof(*p)))
1060 adios(NULL, "out of memory");
1061 init_decoded_content(p);
1063 if (get_ctinfo("message/rfc822", p, 0)
1066 p->c_type = CT_MESSAGE;
1067 p->c_subtype = MESSAGE_RFC822;
1069 snprintf(buffer, sizeof(buffer),
1070 "%s/%d", mp->foldpath,
1072 pe->ce_file = getcpy(buffer);
1073 if (listsw && stat(pe->ce_file, &st)
1075 p->c_end = (long) st.st_size;
1077 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
1078 adios(NULL, "out of memory");
1080 pp = &part->mp_next;
1085 /* we are forwarding one message */
1086 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
1088 ct->c_type = CT_MESSAGE;
1089 ct->c_subtype = MESSAGE_RFC822;
1091 msgnum = mp->lowsel;
1092 snprintf(buffer, sizeof(buffer), "%s/%d",
1093 mp->foldpath, msgnum);
1094 ce->ce_file = getcpy(buffer);
1095 if (listsw && stat(ce->ce_file, &st) != NOTOK)
1096 ct->c_end = (long) st.st_size;
1099 folder_free(mp); /* free folder/message structure */
1106 if (!mh_strcasecmp(ci->ci_type, "end")) {
1113 ** #begin [ alternative | parallel ]
1115 if (!mh_strcasecmp(ci->ci_type, "begin")) {
1116 if (!ci->ci_magic) {
1118 cp = SubMultiPart[vrsn - 1].kv_key;
1119 } else if (!mh_strcasecmp(ci->ci_magic, "alternative")) {
1120 vrsn = MULTI_ALTERNATE;
1121 cp = SubMultiPart[vrsn - 1].kv_key;
1122 } else if (!mh_strcasecmp(ci->ci_magic, "parallel")) {
1123 vrsn = MULTI_PARALLEL;
1124 cp = SubMultiPart[vrsn - 1].kv_key;
1125 } else if (uprf(ci->ci_magic, "digest")) {
1128 vrsn = MULTI_UNKNOWN;
1133 snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
1134 if (get_ctinfo(buffer, ct, 0) == NOTOK)
1136 ct->c_type = CT_MULTIPART;
1137 ct->c_subtype = vrsn;
1139 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1140 adios(NULL, "out of memory");
1141 ct->c_ctparams = (void *) m;
1144 while (fgetstr(buffer, sizeof(buffer) - 1, in)) {
1148 if (user_content(in, file, buffer, &p) == DONE) {
1150 adios(NULL, "empty \"#begin ... #end\" sequence");
1156 if ((part = (struct part *)
1157 calloc(1, sizeof(*part))) == NULL)
1158 adios(NULL, "out of memory");
1160 pp = &part->mp_next;
1163 admonish(NULL, "premature end-of-file, missing #end");
1168 ** Unknown directive
1170 adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
1171 return NOTOK; /* NOT REACHED */
1176 set_id(CT ct, int top)
1180 static time_t clock = 0;
1181 static char *msgfmt;
1185 snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
1186 (int) getpid(), (long) clock, LocalName());
1188 msgfmt = getcpy(msgid);
1190 snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
1191 ct->c_id = getcpy(msgid);
1195 static char ebcdicsafe[0x100] = {
1196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1197 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1198 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1199 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1200 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
1201 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1202 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1203 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1204 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1205 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1206 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1207 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
1208 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1209 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1210 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1211 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1226 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1227 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1232 ** Fill out, or expand the various contents in the composition
1233 ** draft. Read-in any necessary files. Parse and execute any
1234 ** commands specified by profile composition strings.
1238 compose_content(CT ct)
1240 CE ce = ct->c_cefile;
1242 switch (ct->c_type) {
1247 char partnam[BUFSIZ];
1248 struct multipart *m = (struct multipart *) ct->c_ctparams;
1252 snprintf(partnam, sizeof(partnam), "%s.",
1254 pp = partnam + strlen(partnam);
1259 /* first, we call compose_content on all the subparts */
1260 for (part = m->mp_parts, partnum = 1; part;
1261 part = part->mp_next, partnum++) {
1262 CT p = part->mp_part;
1264 sprintf(pp, "%d", partnum);
1265 p->c_partno = getcpy(partnam);
1266 if (compose_content(p) == NOTOK)
1271 ** If the -rfc934mode switch is given, then check all
1272 ** the subparts of a multipart/digest. If they are all
1273 ** message/rfc822, then mark this content and all
1274 ** subparts with the rfc934 compatibility mode flag.
1276 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
1279 for (part = m->mp_parts; part; part = part->mp_next) {
1280 CT p = part->mp_part;
1282 if (p->c_subtype != MESSAGE_RFC822) {
1287 ct->c_rfc934 = is934;
1288 for (part = m->mp_parts; part; part = part->mp_next) {
1289 CT p = part->mp_part;
1291 if ((p->c_rfc934 = is934))
1297 ct->c_end = (partnum = strlen(prefix) + 2) + 2;
1301 for (part = m->mp_parts; part; part = part->mp_next)
1302 ct->c_end += part->mp_part->c_end + partnum;
1308 /* Nothing to do for type message */
1312 ** Discrete types (text/application/audio/image/video)
1317 int xstdout, len, buflen;
1318 char *bp, **ap, *cp;
1319 char *vec[4], buffer[BUFSIZ];
1321 CI ci = &ct->c_ctinfo;
1324 if (!(cp = ci->ci_magic))
1325 adios(NULL, "internal error(5)");
1327 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1328 if (tfile == NULL) {
1329 adios("mhbuild", "unable to create temporary file");
1331 ce->ce_file = getcpy(tfile);
1336 /* Get buffer ready to go */
1339 buflen = sizeof(buffer);
1342 ** Parse composition string into buffer
1344 for ( ; *cp; cp++) {
1350 ** insert parameters from
1356 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1357 snprintf(bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1367 /* %f, and stdout is not-redirected */
1373 ** insert temporary filename
1374 ** where content should be
1377 snprintf(bp, buflen, "%s", ce->ce_file);
1381 /* insert content subtype */
1382 strncpy(bp, ci->ci_subtype, buflen);
1386 /* insert character % */
1407 printf("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer);
1409 fflush(stdout); /* not sure if need for -noverbose */
1416 if ((out = fopen(ce->ce_file, "w")) == NULL)
1417 adios(ce->ce_file, "unable to open for writing");
1419 switch (child_id = fork()) {
1421 adios("fork", "unable to fork");
1426 dup2(fileno(out), 1);
1428 execvp("/bin/sh", vec);
1429 fprintf(stderr, "unable to exec ");
1436 if (pidXwait(child_id, NULL))
1442 /* Check size of file */
1443 if (listsw && ct->c_end == 0L) {
1446 if (stat(ce->ce_file, &st) != NOTOK)
1447 ct->c_end = (long) st.st_size;
1457 ** Scan the content.
1459 ** 1) choose a transfer encoding.
1460 ** 2) check for clashes with multipart boundary string.
1461 ** 3) for text content, figure out which character set is being used.
1463 ** If there is a clash with one of the contents and the multipart boundary,
1464 ** this function will exit with NOTOK. This will cause the scanning process
1465 ** to be repeated with a different multipart boundary. It is possible
1466 ** (although highly unlikely) that this scan will be repeated multiple times.
1473 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1474 int checklinelen = 0, linelen = 0; /* check for long lines */
1475 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1476 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1477 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1478 unsigned char *cp = NULL, buffer[BUFSIZ];
1479 struct text *t = NULL;
1481 CE ce = ct->c_cefile;
1484 ** handle multipart by scanning all subparts
1485 ** and then checking their encoding.
1487 if (ct->c_type == CT_MULTIPART) {
1488 struct multipart *m = (struct multipart *) ct->c_ctparams;
1491 /* initially mark the domain of enclosing multipart as 7bit */
1492 ct->c_encoding = CE_7BIT;
1494 for (part = m->mp_parts; part; part = part->mp_next) {
1495 CT p = part->mp_part;
1497 if (scan_content(p) == NOTOK) {
1498 /* choose encoding for subpart */
1503 ** if necessary, enlarge encoding for enclosing
1506 if (p->c_encoding == CE_BINARY)
1507 ct->c_encoding = CE_BINARY;
1508 if (p->c_encoding == CE_8BIT &&
1509 ct->c_encoding != CE_BINARY)
1510 ct->c_encoding = CE_8BIT;
1517 ** Decide what to check while scanning this content.
1519 switch (ct->c_type) {
1523 if (ct->c_subtype == TEXT_PLAIN) {
1528 checkebcdic = ebcdicsw;
1534 case CT_APPLICATION:
1536 checkebcdic = ebcdicsw;
1548 /* don't check anything for message/external */
1549 if (ct->c_subtype == MESSAGE_EXTERNAL)
1559 ** Don't check anything for these types,
1560 ** since we are forcing use of base64.
1571 ** Scan the unencoded content
1573 if (check8bit || checklinelen || checklinespace || checkboundary) {
1574 if ((in = fopen(ce->ce_file, "r")) == NULL)
1575 adios(ce->ce_file, "unable to open for reading");
1576 len = strlen(prefix);
1578 while (fgets(buffer, sizeof(buffer) - 1, in)) {
1580 ** Check for 8bit data.
1583 for (cp = buffer; *cp; cp++) {
1584 if (!isascii(*cp)) {
1586 /* no need to keep checking */
1590 ** Check if character is ebcdic-safe.
1591 ** We only check this if also checking
1594 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1596 /* no need to keep checking */
1603 ** Check line length.
1605 if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
1607 checklinelen = 0; /* no need to keep checking */
1611 ** Check if line ends with a space.
1613 if (checklinespace &&
1614 (cp = buffer + strlen(buffer) - 2) >
1615 buffer && isspace(*cp)) {
1617 /* no need to keep checking */
1622 ** Check if content contains a line that clashes
1623 ** with our standard boundary for multipart messages.
1625 if (checkboundary && buffer[0] == '-' &&
1627 for (cp = buffer + strlen(buffer) - 1;
1632 if (strncmp(buffer + 2, prefix, len)==0 &&
1633 isdigit(buffer[2 + len])) {
1635 /* no need to keep checking */
1644 ** Decide which transfer encoding to use.
1646 switch (ct->c_type) {
1649 ** If the text content didn't specify a character
1650 ** set, we need to figure out which one was used.
1652 t = (struct text *) ct->c_ctparams;
1653 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1654 CI ci = &ct->c_ctinfo;
1657 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1661 t->tx_charset = CHARSET_UNKNOWN;
1662 *ap = concat("charset=", write_charset_8bit(),
1665 t->tx_charset = CHARSET_USASCII;
1666 *ap = getcpy("charset=us-ascii");
1669 cp = strchr(*ap++, '=');
1675 if (contains8bit || ebcdicunsafe || linelen || linespace)
1676 ct->c_encoding = CE_QUOTED;
1678 ct->c_encoding = CE_7BIT;
1681 case CT_APPLICATION:
1682 /* For application type, use base64, except when postscript */
1683 if (contains8bit || ebcdicunsafe || linelen || linespace)
1684 ct->c_encoding = (ct->c_subtype ==
1685 APPLICATION_POSTSCRIPT) ?
1686 CE_QUOTED : CE_BASE64;
1688 ct->c_encoding = CE_7BIT;
1692 ct->c_encoding = CE_7BIT;
1698 /* For audio, image, and video contents, just use base64 */
1699 ct->c_encoding = CE_BASE64;
1703 return (boundaryclash ? NOTOK : OK);
1708 ** Scan the content structures, and build header
1709 ** fields that will need to be output into the
1714 build_headers(CT ct)
1716 int cc, mailbody, len;
1718 char *np, *vp, buffer[BUFSIZ];
1719 CI ci = &ct->c_ctinfo;
1722 ** If message is type multipart, then add the multipart
1723 ** boundary to the list of attribute/value pairs.
1725 if (ct->c_type == CT_MULTIPART) {
1727 static int level = 0; /* store nesting level */
1731 snprintf(buffer, sizeof(buffer), "boundary=%s%d",
1733 cp = strchr(*ap++ = getcpy(buffer), '=');
1740 ** Skip the output of Content-Type, parameters, content
1741 ** description and disposition, and Content-ID if the
1742 ** content is of type "message" and the rfc934 compatibility
1743 ** flag is set (which means we are inside multipart/digest
1744 ** and the switch -rfc934mode was given).
1746 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1750 ** output the content type and subtype
1752 np = getcpy(TYPE_FIELD);
1753 vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1755 /* keep track of length of line */
1756 len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
1757 strlen(ci->ci_subtype) + 3;
1759 mailbody = ct->c_type == CT_MESSAGE &&
1760 ct->c_subtype == MESSAGE_EXTERNAL &&
1761 ((struct exbody *) ct->c_ctparams)->eb_body;
1764 ** Append the attribute/value pairs to
1765 ** the end of the Content-Type line.
1767 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1768 if (mailbody && !mh_strcasecmp(*ap, "body"))
1774 snprintf(buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1775 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
1776 vp = add("\n\t", vp);
1782 vp = add(buffer, vp);
1787 ** Append any RFC-822 comment to the end of
1788 ** the Content-Type line.
1790 if (ci->ci_comment) {
1791 snprintf(buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1792 if (len + 1 + (cc = 2 + strlen(ci->ci_comment)) >= CPERLIN) {
1793 vp = add("\n\t", vp);
1799 vp = add(buffer, vp);
1803 add_header(ct, np, vp);
1806 ** output the Content-ID, unless disabled by -nocontentid
1808 if (contentidsw && ct->c_id) {
1809 np = getcpy(ID_FIELD);
1810 vp = concat(" ", ct->c_id, NULL);
1811 add_header(ct, np, vp);
1815 ** output the Content-Description
1818 np = getcpy(DESCR_FIELD);
1819 vp = concat(" ", ct->c_descr, NULL);
1820 add_header(ct, np, vp);
1824 ** output the Content-Disposition
1827 np = getcpy(DISPO_FIELD);
1828 vp = concat(" ", ct->c_dispo, NULL);
1829 add_header(ct, np, vp);
1834 ** If this is the internal content structure for a
1835 ** "message/external", then we are done with the
1836 ** headers (since it has no body).
1842 ** output the Content-Transfer-Encoding
1844 switch (ct->c_encoding) {
1846 /* Nothing to output */
1850 if (ct->c_type == CT_MESSAGE)
1851 adios(NULL, "internal error, invalid encoding");
1853 np = getcpy(ENCODING_FIELD);
1854 vp = concat(" ", "8bit", "\n", NULL);
1855 add_header(ct, np, vp);
1859 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1860 adios(NULL, "internal error, invalid encoding");
1862 np = getcpy(ENCODING_FIELD);
1863 vp = concat(" ", "quoted-printable", "\n", NULL);
1864 add_header(ct, np, vp);
1868 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1869 adios(NULL, "internal error, invalid encoding");
1871 np = getcpy(ENCODING_FIELD);
1872 vp = concat(" ", "base64", "\n", NULL);
1873 add_header(ct, np, vp);
1877 if (ct->c_type == CT_MESSAGE)
1878 adios(NULL, "internal error, invalid encoding");
1880 np = getcpy(ENCODING_FIELD);
1881 vp = concat(" ", "binary", "\n", NULL);
1882 add_header(ct, np, vp);
1886 adios(NULL, "unknown transfer encoding in content");
1891 ** Additional content specific header processing
1893 switch (ct->c_type) {
1896 struct multipart *m;
1899 m = (struct multipart *) ct->c_ctparams;
1900 for (part = m->mp_parts; part; part = part->mp_next) {
1910 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1913 e = (struct exbody *) ct->c_ctparams;
1914 build_headers(e->eb_content);