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.
849 adios(NULL, "The #@ directive i.e. message/external-body "
850 "is not supported anymore.");
853 /* parse directive */
854 if (get_ctinfo(buf+1, ct, 1) == NOTOK)
857 /* check directive against the list of MIME types */
858 for (s2i = str2cts; s2i->si_key; s2i++)
859 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
863 ** Check if the directive specified a valid type.
864 ** This will happen if it was one of the following forms:
870 adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
872 switch (ct->c_type = s2i->si_val) {
874 adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
878 if (!mh_strcasecmp(ci->ci_subtype, "partial") ||
879 !mh_strcasecmp(ci->ci_subtype,
881 adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
884 adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
888 if ((ct->c_ctinitfnx = s2i->si_init))
889 (*ct->c_ctinitfnx) (ct);
893 /* Handle [file] argument */
895 /* check if specifies command to execute */
896 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
897 for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
900 adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
905 /* record filename of decoded contents */
906 ce->ce_file = ci->ci_magic;
907 if (access(ce->ce_file, R_OK) == NOTOK)
908 adios("reading", "unable to access %s for", ce->ce_file);
909 if (listsw && stat(ce->ce_file, &st) != NOTOK)
910 ct->c_end = (long) st.st_size;
917 ** No [file] argument, so check profile for
918 ** method to compose content.
920 snprintf(buffer, sizeof(buffer), "%s-compose-%s/%s",
921 invo_name, ci->ci_type, ci->ci_subtype);
922 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
923 snprintf(buffer, sizeof(buffer), "%s-compose-%s",
924 invo_name, ci->ci_type);
925 if ((cp = context_find(buffer)) == NULL ||
927 content_error(NULL, ct, "don't know how to compose content");
931 ci->ci_magic = getcpy(cp);
937 ** #forw [+folder] [msgs]
939 if (!mh_strcasecmp(ci->ci_type, "forw")) {
941 char *folder, *arguments[MAXARGS];
947 ap = brkstring(ci->ci_magic, " ", "\n");
948 for (i=0; ap[i] && i<MAXARGS-1; i++) {
949 arguments[i] = ap[i];
954 arguments[0] = seq_cur;
959 /* search the arguments for a folder name */
960 for (ap = arguments; *ap; ap++) {
962 if (*cp == '+' || *cp == '@') {
964 adios(NULL, "only one folder per #forw directive");
966 folder = getcpy(expandfol(cp));
970 /* else, use the current folder */
972 folder = getcpy(getcurfol());
974 if (!(mp = folder_read(folder)))
975 adios(NULL, "unable to read folder %s", folder);
976 for (ap = arguments; *ap; ap++) {
978 if (*cp != '+' && *cp != '@')
979 if (!m_convert(mp, cp))
986 ** If there is more than one message to include, make this
987 ** a content of type "multipart/digest" and insert each message
988 ** as a subpart. If there is only one message, then make this
989 ** a content of type "message/rfc822".
991 if (mp->numsel > 1) {
992 /* we are forwarding multiple messages */
993 if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
995 ct->c_type = CT_MULTIPART;
996 ct->c_subtype = MULTI_DIGEST;
998 if ((m = (struct multipart *)
999 calloc(1, sizeof(*m))) == NULL)
1000 adios(NULL, "out of memory");
1001 ct->c_ctparams = (void *) m;
1004 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
1005 if (is_selected(mp, msgnum)) {
1010 if ((p = (CT) calloc(1, sizeof(*p)))
1012 adios(NULL, "out of memory");
1013 init_decoded_content(p);
1015 if (get_ctinfo("message/rfc822", p, 0)
1018 p->c_type = CT_MESSAGE;
1019 p->c_subtype = MESSAGE_RFC822;
1021 snprintf(buffer, sizeof(buffer),
1022 "%s/%d", mp->foldpath,
1024 pe->ce_file = getcpy(buffer);
1025 if (listsw && stat(pe->ce_file, &st)
1027 p->c_end = (long) st.st_size;
1029 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
1030 adios(NULL, "out of memory");
1032 pp = &part->mp_next;
1037 /* we are forwarding one message */
1038 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
1040 ct->c_type = CT_MESSAGE;
1041 ct->c_subtype = MESSAGE_RFC822;
1043 msgnum = mp->lowsel;
1044 snprintf(buffer, sizeof(buffer), "%s/%d",
1045 mp->foldpath, msgnum);
1046 ce->ce_file = getcpy(buffer);
1047 if (listsw && stat(ce->ce_file, &st) != NOTOK)
1048 ct->c_end = (long) st.st_size;
1051 folder_free(mp); /* free folder/message structure */
1058 if (!mh_strcasecmp(ci->ci_type, "end")) {
1065 ** #begin [ alternative | parallel ]
1067 if (!mh_strcasecmp(ci->ci_type, "begin")) {
1068 if (!ci->ci_magic) {
1070 cp = SubMultiPart[vrsn - 1].kv_key;
1071 } else if (!mh_strcasecmp(ci->ci_magic, "alternative")) {
1072 vrsn = MULTI_ALTERNATE;
1073 cp = SubMultiPart[vrsn - 1].kv_key;
1074 } else if (!mh_strcasecmp(ci->ci_magic, "parallel")) {
1075 vrsn = MULTI_PARALLEL;
1076 cp = SubMultiPart[vrsn - 1].kv_key;
1077 } else if (uprf(ci->ci_magic, "digest")) {
1080 vrsn = MULTI_UNKNOWN;
1085 snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
1086 if (get_ctinfo(buffer, ct, 0) == NOTOK)
1088 ct->c_type = CT_MULTIPART;
1089 ct->c_subtype = vrsn;
1091 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1092 adios(NULL, "out of memory");
1093 ct->c_ctparams = (void *) m;
1096 while (fgetstr(buffer, sizeof(buffer) - 1, in)) {
1100 if (user_content(in, file, buffer, &p) == DONE) {
1102 adios(NULL, "empty \"#begin ... #end\" sequence");
1108 if ((part = (struct part *)
1109 calloc(1, sizeof(*part))) == NULL)
1110 adios(NULL, "out of memory");
1112 pp = &part->mp_next;
1115 admonish(NULL, "premature end-of-file, missing #end");
1120 ** Unknown directive
1122 adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
1123 return NOTOK; /* NOT REACHED */
1128 set_id(CT ct, int top)
1132 static time_t clock = 0;
1133 static char *msgfmt;
1137 snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
1138 (int) getpid(), (long) clock, LocalName());
1140 msgfmt = getcpy(msgid);
1142 snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
1143 ct->c_id = getcpy(msgid);
1147 static char ebcdicsafe[0x100] = {
1148 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1149 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1150 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1151 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1152 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
1153 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1154 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1155 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1156 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1157 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1158 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1159 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
1160 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1161 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1162 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1163 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1164 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1165 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1166 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1167 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1168 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1169 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1170 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1171 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1172 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1173 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1174 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1176 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1178 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1179 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1184 ** Fill out, or expand the various contents in the composition
1185 ** draft. Read-in any necessary files. Parse and execute any
1186 ** commands specified by profile composition strings.
1190 compose_content(CT ct)
1192 CE ce = ct->c_cefile;
1194 switch (ct->c_type) {
1199 char partnam[BUFSIZ];
1200 struct multipart *m = (struct multipart *) ct->c_ctparams;
1204 snprintf(partnam, sizeof(partnam), "%s.",
1206 pp = partnam + strlen(partnam);
1211 /* first, we call compose_content on all the subparts */
1212 for (part = m->mp_parts, partnum = 1; part;
1213 part = part->mp_next, partnum++) {
1214 CT p = part->mp_part;
1216 sprintf(pp, "%d", partnum);
1217 p->c_partno = getcpy(partnam);
1218 if (compose_content(p) == NOTOK)
1223 ** If the -rfc934mode switch is given, then check all
1224 ** the subparts of a multipart/digest. If they are all
1225 ** message/rfc822, then mark this content and all
1226 ** subparts with the rfc934 compatibility mode flag.
1228 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
1231 for (part = m->mp_parts; part; part = part->mp_next) {
1232 CT p = part->mp_part;
1234 if (p->c_subtype != MESSAGE_RFC822) {
1239 ct->c_rfc934 = is934;
1240 for (part = m->mp_parts; part; part = part->mp_next) {
1241 CT p = part->mp_part;
1243 if ((p->c_rfc934 = is934))
1249 ct->c_end = (partnum = strlen(prefix) + 2) + 2;
1253 for (part = m->mp_parts; part; part = part->mp_next)
1254 ct->c_end += part->mp_part->c_end + partnum;
1260 /* Nothing to do for type message */
1264 ** Discrete types (text/application/audio/image/video)
1269 int xstdout, len, buflen;
1270 char *bp, **ap, *cp;
1271 char *vec[4], buffer[BUFSIZ];
1273 CI ci = &ct->c_ctinfo;
1276 if (!(cp = ci->ci_magic))
1277 adios(NULL, "internal error(5)");
1279 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1280 if (tfile == NULL) {
1281 adios("mhbuild", "unable to create temporary file");
1283 ce->ce_file = getcpy(tfile);
1288 /* Get buffer ready to go */
1291 buflen = sizeof(buffer);
1294 ** Parse composition string into buffer
1296 for ( ; *cp; cp++) {
1302 ** insert parameters from
1308 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1309 snprintf(bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1319 /* %f, and stdout is not-redirected */
1325 ** insert temporary filename
1326 ** where content should be
1329 snprintf(bp, buflen, "%s", ce->ce_file);
1333 /* insert content subtype */
1334 strncpy(bp, ci->ci_subtype, buflen);
1338 /* insert character % */
1359 printf("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer);
1361 fflush(stdout); /* not sure if need for -noverbose */
1368 if ((out = fopen(ce->ce_file, "w")) == NULL)
1369 adios(ce->ce_file, "unable to open for writing");
1371 switch (child_id = fork()) {
1373 adios("fork", "unable to fork");
1378 dup2(fileno(out), 1);
1380 execvp("/bin/sh", vec);
1381 fprintf(stderr, "unable to exec ");
1388 if (pidXwait(child_id, NULL))
1394 /* Check size of file */
1395 if (listsw && ct->c_end == 0L) {
1398 if (stat(ce->ce_file, &st) != NOTOK)
1399 ct->c_end = (long) st.st_size;
1409 ** Scan the content.
1411 ** 1) choose a transfer encoding.
1412 ** 2) check for clashes with multipart boundary string.
1413 ** 3) for text content, figure out which character set is being used.
1415 ** If there is a clash with one of the contents and the multipart boundary,
1416 ** this function will exit with NOTOK. This will cause the scanning process
1417 ** to be repeated with a different multipart boundary. It is possible
1418 ** (although highly unlikely) that this scan will be repeated multiple times.
1425 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1426 int checklinelen = 0, linelen = 0; /* check for long lines */
1427 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1428 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1429 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1430 unsigned char *cp = NULL, buffer[BUFSIZ];
1431 struct text *t = NULL;
1433 CE ce = ct->c_cefile;
1436 ** handle multipart by scanning all subparts
1437 ** and then checking their encoding.
1439 if (ct->c_type == CT_MULTIPART) {
1440 struct multipart *m = (struct multipart *) ct->c_ctparams;
1443 /* initially mark the domain of enclosing multipart as 7bit */
1444 ct->c_encoding = CE_7BIT;
1446 for (part = m->mp_parts; part; part = part->mp_next) {
1447 CT p = part->mp_part;
1449 if (scan_content(p) == NOTOK) {
1450 /* choose encoding for subpart */
1455 ** if necessary, enlarge encoding for enclosing
1458 if (p->c_encoding == CE_BINARY)
1459 ct->c_encoding = CE_BINARY;
1460 if (p->c_encoding == CE_8BIT &&
1461 ct->c_encoding != CE_BINARY)
1462 ct->c_encoding = CE_8BIT;
1469 ** Decide what to check while scanning this content.
1471 switch (ct->c_type) {
1475 if (ct->c_subtype == TEXT_PLAIN) {
1480 checkebcdic = ebcdicsw;
1486 case CT_APPLICATION:
1488 checkebcdic = ebcdicsw;
1506 ** Don't check anything for these types,
1507 ** since we are forcing use of base64.
1518 ** Scan the unencoded content
1520 if (check8bit || checklinelen || checklinespace || checkboundary) {
1521 if ((in = fopen(ce->ce_file, "r")) == NULL)
1522 adios(ce->ce_file, "unable to open for reading");
1523 len = strlen(prefix);
1525 while (fgets(buffer, sizeof(buffer) - 1, in)) {
1527 ** Check for 8bit data.
1530 for (cp = buffer; *cp; cp++) {
1531 if (!isascii(*cp)) {
1533 /* no need to keep checking */
1537 ** Check if character is ebcdic-safe.
1538 ** We only check this if also checking
1541 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1543 /* no need to keep checking */
1550 ** Check line length.
1552 if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
1554 checklinelen = 0; /* no need to keep checking */
1558 ** Check if line ends with a space.
1560 if (checklinespace &&
1561 (cp = buffer + strlen(buffer) - 2) >
1562 buffer && isspace(*cp)) {
1564 /* no need to keep checking */
1569 ** Check if content contains a line that clashes
1570 ** with our standard boundary for multipart messages.
1572 if (checkboundary && buffer[0] == '-' &&
1574 for (cp = buffer + strlen(buffer) - 1;
1579 if (strncmp(buffer + 2, prefix, len)==0 &&
1580 isdigit(buffer[2 + len])) {
1582 /* no need to keep checking */
1591 ** Decide which transfer encoding to use.
1593 switch (ct->c_type) {
1596 ** If the text content didn't specify a character
1597 ** set, we need to figure out which one was used.
1599 t = (struct text *) ct->c_ctparams;
1600 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1601 CI ci = &ct->c_ctinfo;
1604 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1608 t->tx_charset = CHARSET_UNKNOWN;
1609 *ap = concat("charset=", write_charset_8bit(),
1612 t->tx_charset = CHARSET_USASCII;
1613 *ap = getcpy("charset=us-ascii");
1616 cp = strchr(*ap++, '=');
1622 if (contains8bit || ebcdicunsafe || linelen || linespace)
1623 ct->c_encoding = CE_QUOTED;
1625 ct->c_encoding = CE_7BIT;
1628 case CT_APPLICATION:
1629 /* For application type, use base64, except when postscript */
1630 if (contains8bit || ebcdicunsafe || linelen || linespace)
1631 ct->c_encoding = (ct->c_subtype ==
1632 APPLICATION_POSTSCRIPT) ?
1633 CE_QUOTED : CE_BASE64;
1635 ct->c_encoding = CE_7BIT;
1639 ct->c_encoding = CE_7BIT;
1645 /* For audio, image, and video contents, just use base64 */
1646 ct->c_encoding = CE_BASE64;
1650 return (boundaryclash ? NOTOK : OK);
1655 ** Scan the content structures, and build header
1656 ** fields that will need to be output into the
1661 build_headers(CT ct)
1665 char *np, *vp, buffer[BUFSIZ];
1666 CI ci = &ct->c_ctinfo;
1669 ** If message is type multipart, then add the multipart
1670 ** boundary to the list of attribute/value pairs.
1672 if (ct->c_type == CT_MULTIPART) {
1674 static int level = 0; /* store nesting level */
1678 snprintf(buffer, sizeof(buffer), "boundary=%s%d",
1680 cp = strchr(*ap++ = getcpy(buffer), '=');
1687 ** Skip the output of Content-Type, parameters, content
1688 ** description and disposition, and Content-ID if the
1689 ** content is of type "message" and the rfc934 compatibility
1690 ** flag is set (which means we are inside multipart/digest
1691 ** and the switch -rfc934mode was given).
1693 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1697 ** output the content type and subtype
1699 np = getcpy(TYPE_FIELD);
1700 vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1702 /* keep track of length of line */
1703 len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
1704 strlen(ci->ci_subtype) + 3;
1707 ** Append the attribute/value pairs to
1708 ** the end of the Content-Type line.
1710 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1714 snprintf(buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1715 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
1716 vp = add("\n\t", vp);
1722 vp = add(buffer, vp);
1727 ** Append any RFC-822 comment to the end of
1728 ** the Content-Type line.
1730 if (ci->ci_comment) {
1731 snprintf(buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1732 if (len + 1 + (cc = 2 + strlen(ci->ci_comment)) >= CPERLIN) {
1733 vp = add("\n\t", vp);
1739 vp = add(buffer, vp);
1743 add_header(ct, np, vp);
1746 ** output the Content-ID, unless disabled by -nocontentid
1748 if (contentidsw && ct->c_id) {
1749 np = getcpy(ID_FIELD);
1750 vp = concat(" ", ct->c_id, NULL);
1751 add_header(ct, np, vp);
1755 ** output the Content-Description
1758 np = getcpy(DESCR_FIELD);
1759 vp = concat(" ", ct->c_descr, NULL);
1760 add_header(ct, np, vp);
1764 ** output the Content-Disposition
1767 np = getcpy(DISPO_FIELD);
1768 vp = concat(" ", ct->c_dispo, NULL);
1769 add_header(ct, np, vp);
1774 ** output the Content-Transfer-Encoding
1776 switch (ct->c_encoding) {
1778 /* Nothing to output */
1782 if (ct->c_type == CT_MESSAGE)
1783 adios(NULL, "internal error, invalid encoding");
1785 np = getcpy(ENCODING_FIELD);
1786 vp = concat(" ", "8bit", "\n", NULL);
1787 add_header(ct, np, vp);
1791 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1792 adios(NULL, "internal error, invalid encoding");
1794 np = getcpy(ENCODING_FIELD);
1795 vp = concat(" ", "quoted-printable", "\n", NULL);
1796 add_header(ct, np, vp);
1800 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1801 adios(NULL, "internal error, invalid encoding");
1803 np = getcpy(ENCODING_FIELD);
1804 vp = concat(" ", "base64", "\n", NULL);
1805 add_header(ct, np, vp);
1809 if (ct->c_type == CT_MESSAGE)
1810 adios(NULL, "internal error, invalid encoding");
1812 np = getcpy(ENCODING_FIELD);
1813 vp = concat(" ", "binary", "\n", NULL);
1814 add_header(ct, np, vp);
1818 adios(NULL, "unknown transfer encoding in content");
1823 ** Additional content specific header processing
1825 switch (ct->c_type) {
1828 struct multipart *m;
1831 m = (struct multipart *) ct->c_ctparams;
1832 for (part = m->mp_parts; part; part = part->mp_next) {