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>
24 #include <h/mhparse.h>
25 #include <h/mhcachesbr.h>
28 #ifdef TIME_WITH_SYS_TIME
29 # include <sys/time.h>
32 # ifdef TM_IN_SYS_TIME
33 # include <sys/time.h>
39 static struct swit switches[] = {
47 { "noebcdicsafe", 0 },
63 { "norfc934mode", 0 },
69 { "rcache policy", 0 },
71 { "wcache policy", 0 },
72 #define CONTENTIDSW 16
74 #define NCONTENTIDSW 17
87 ** Directory to place tmp files. This must
88 ** be set before these routines are called.
94 static char prefix[] = "----- =_aaaaaaaaaa";
102 int make_intermediates(char *);
103 void content_error(char *, CT, char *, ...);
106 int find_cache(CT, int, int *, char *, char *, int);
109 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
112 void free_content(CT);
113 void free_ctinfo(CT);
114 void free_encoding(CT, int);
119 static int init_decoded_content(CT);
120 static char *fgetstr(char *, int, FILE *);
121 static int user_content(FILE *, char *, char *, CT *);
122 static void set_id(CT, int);
123 static int compose_content(CT);
124 static int scan_content(CT);
125 static int build_headers(CT);
126 static char *calculate_digest(CT, int);
127 static CT build_mime(char *);
134 extern char *cache_public;
135 extern char *cache_private;
148 static char infile[BUFSIZ];
149 static int unlink_infile = 0;
151 static char outfile[BUFSIZ];
152 static int unlink_outfile = 0;
154 static void unlink_done(int) NORETURN;
157 int output_message(CT, char *);
158 int output_message_fp(CT, FILE *, char*);
161 int list_all_messages(CT *, int, int, int, int);
164 void set_endian(void);
167 void free_content(CT);
171 main(int argc, char **argv)
173 int sizesw = 1, headsw = 1;
175 char *cp, buf[BUFSIZ];
176 char buffer[BUFSIZ], *compfile = NULL;
177 char **argp, **arguments;
185 setlocale(LC_ALL, "");
187 invo_name = mhbasename(argv[0]);
189 /* read user profile/context */
192 arguments = getarguments(invo_name, argc, argv, 1);
195 while ((cp = *argp++)) {
196 if (cp[0] == '-' && cp[1] == '\0') {
198 adios(NULL, "cannot specify both standard input and a file");
201 listsw = 0; /* turn off -list if using std in/out */
202 verbosw = 0; /* turn off -verbose listings */
206 switch (smatch(++cp, switches)) {
208 ambigsw(cp, switches);
211 adios(NULL, "-%s unknown", cp);
214 snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
215 print_help(buf, switches, 1);
218 print_version(invo_name);
222 icachesw = &rcachesw;
225 icachesw = &wcachesw;
227 if (!(cp = *argp++) || *cp == '-')
228 adios(NULL, "missing argument to %s",
230 switch (*icachesw = smatch(cp, caches)) {
235 adios(NULL, "%s unknown", cp);
302 adios(NULL, "only one composition file allowed");
309 if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
313 ** Check if we've specified an additional profile
315 if ((cp = getenv("MHBUILD"))) {
316 if ((fp = fopen(cp, "r"))) {
317 readconfig((struct node **) 0, fp, cp, 0);
320 admonish("", "unable to read $MHBUILD profile (%s)",
326 ** Read the standard profile setup
328 if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
329 readconfig((struct node **) 0, fp, cp, 0);
333 /* Check for public cache location */
334 if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
337 /* Check for private cache location */
338 if (!(cache_private = context_find(nmhprivcache)))
339 cache_private = ".cache";
340 cache_private = getcpy(toabsdir(cache_private));
343 ** Check for storage directory. If defined, we
344 ** will store temporary files there. Else we
345 ** store them in standard nmh directory.
347 if ((cp = context_find(nmhstorage)) && *cp)
348 tmp = concat(cp, "/", invo_name, NULL);
350 tmp = getcpy(toabsdir(invo_name));
352 /* Check if we have a file to process */
354 adios(NULL, "need to specify a %s composition file",
358 ** Process the composition file from standard input.
360 if (compfile[0] == '-' && compfile[1] == '\0') {
361 /* copy standard input to temporary file */
362 strncpy(infile, m_mktemp(invo_name, NULL, &fp),
364 while (fgets(buffer, BUFSIZ, stdin))
369 /* build the content structures for MIME message */
370 ct = build_mime(infile);
374 /* output MIME message to this temporary file */
375 strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
379 /* output the message */
380 output_message_fp(ct, fp_out, outfile);
383 /* output the temp file to standard output */
384 if ((fp = fopen(outfile, "r")) == NULL)
385 adios(outfile, "unable to open");
386 while (fgets(buffer, BUFSIZ, fp))
387 fputs(buffer, stdout);
401 ** Process the composition file from a file.
404 /* build the content structures for MIME message */
405 ct = build_mime(compfile);
409 /* output MIME message to this temporary file */
410 strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
414 /* output the message */
415 output_message_fp(ct, fp_out, outfile);
419 ** List the message info
422 list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
424 /* Rename composition draft */
425 snprintf(buffer, sizeof(buffer), "%s.orig", m_backup(compfile));
426 if (rename(compfile, buffer) == NOTOK) {
427 adios(compfile, "unable to rename comp draft %s to", buffer);
430 /* Rename output file to take its place */
431 if (rename(outfile, compfile) == NOTOK) {
432 advise(outfile, "unable to rename output %s to", compfile);
433 rename(buffer, compfile);
445 unlink_done(int status)
448 ** Check if we need to remove stray temporary files.
459 ** Main routine for translating composition file
460 ** into valid MIME message. It translates the draft
461 ** into a content structure (actually a tree of content
462 ** structures). This message then can be manipulated
463 ** in various ways, including being output via
467 build_mime(char *infile)
470 char buf[BUFSIZ], name[NAMESZ];
479 /* open the composition draft */
480 if ((in = fopen(infile, "r")) == NULL)
481 adios(infile, "unable to open for reading");
484 ** Allocate space for primary (outside) content
486 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
487 adios(NULL, "out of memory");
490 ** Allocate structure for handling decoded content
491 ** for this part. We don't really need this, but
492 ** allocate it to remain consistent.
494 init_decoded_content(ct);
497 ** Parse some of the header fields in the composition
498 ** draft into the linked list of header fields for
499 ** the new MIME message.
501 for (compnum = 1, state = FLD;;) {
502 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
508 /* abort if draft has Mime-Version header field */
509 if (!mh_strcasecmp(name, VRSN_FIELD))
510 adios(NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
513 ** abort if draft has Content-Transfer-Encoding
516 if (!mh_strcasecmp(name, ENCODING_FIELD))
517 adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
519 /* ignore any Content-Type fields in the header */
520 if (!mh_strcasecmp(name, TYPE_FIELD)) {
521 while (state == FLDPLUS)
522 state = m_getfld(state, name, buf,
527 /* get copies of the buffers */
531 /* if necessary, get rest of field */
532 while (state == FLDPLUS) {
533 state = m_getfld(state, name, buf,
535 vp = add(buf, vp); /* add to prev value */
538 /* Now add the header data to the list */
539 add_header(ct, np, vp);
542 /* if this wasn't the last hdr field, then continue */
548 adios(NULL, "draft has empty body -- no directives!");
553 fseek(in, (long) (-strlen(buf)), SEEK_CUR);
558 adios(NULL, "message format error in component #%d",
562 adios(NULL, "getfld() returned %d", state);
568 ** Now add the MIME-Version header field
569 ** to the list of header fields.
571 np = getcpy(VRSN_FIELD);
572 vp = concat(" ", VRSN_VALUE, "\n", NULL);
573 add_header(ct, np, vp);
576 ** We initally assume we will find multiple contents in the
577 ** draft. So create a multipart/mixed content to hold everything.
578 ** We can remove this later, if it is not needed.
580 if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
582 ct->c_type = CT_MULTIPART;
583 ct->c_subtype = MULTI_MIXED;
584 ct->c_file = getcpy(infile);
586 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
587 adios(NULL, "out of memory");
588 ct->c_ctparams = (void *) m;
592 ** read and parse the composition file
593 ** and the directives it contains.
595 while (fgetstr(buf, sizeof(buf) - 1, in)) {
599 if (user_content(in, infile, buf, &p) == DONE) {
600 admonish(NULL, "ignoring spurious #end");
606 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
607 adios(NULL, "out of memory");
614 ** close the composition draft since
615 ** it's not needed any longer.
619 /* check if any contents were found */
621 adios(NULL, "no content directives found");
624 ** If only one content was found, then remove and
625 ** free the outer multipart content.
627 if (!m->mp_parts->mp_next) {
630 p = m->mp_parts->mp_part;
631 m->mp_parts->mp_part = NULL;
633 /* move header fields */
634 p->c_first_hf = ct->c_first_hf;
635 p->c_last_hf = ct->c_last_hf;
636 ct->c_first_hf = NULL;
637 ct->c_last_hf = NULL;
646 ** Fill out, or expand directives. Parse and execute
647 ** commands specified by profile composition strings.
651 if ((cp = strchr(prefix, 'a')) == NULL)
652 adios(NULL, "internal error(4)");
655 ** Scan the contents. Choose a transfer encoding, and
656 ** check if prefix for multipart boundary clashes with
657 ** any of the contents.
659 while (scan_content(ct) == NOTOK) {
664 adios(NULL, "giving up trying to find a unique delimiter string");
670 /* Build the rest of the header field structures */
678 ** Set up structures for placing unencoded
679 ** content when building parts.
683 init_decoded_content(CT ct)
687 if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
688 adios(NULL, "out of memory");
691 ct->c_ceopenfnx = open7Bit; /* since unencoded */
692 ct->c_ceclosefnx = close_encoding;
693 ct->c_cesizefnx = NULL; /* since unencoded */
700 fgetstr(char *s, int n, FILE *stream)
704 for (ep = (cp = s) + n; cp < ep; ) {
707 if (!fgets(cp, n, stream))
708 return (cp != s ? s : NULL);
709 if (cp == s && *cp != '#')
712 cp += (i = strlen(cp)) - 1;
713 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
724 ** Parse the composition draft for text and directives.
725 ** Do initial setup of Content structure.
729 user_content(FILE *in, char *file, char *buf, CT *ctp)
738 struct str2init *s2i;
743 if (buf[0] == '\n' || strcmp(buf, "#\n") == 0) {
748 /* allocate basic Content structure */
749 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
750 adios(NULL, "out of memory");
753 /* allocate basic structure for handling decoded content */
754 init_decoded_content(ct);
761 ** Handle inline text. Check if line
762 ** is one of the following forms:
764 ** 1) doesn't begin with '#' (implicit directive)
765 ** 2) begins with "##" (implicit directive)
766 ** 3) begins with "#<"
768 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
772 char content[BUFSIZ];
776 cp = m_mktemp2(NULL, invo_name, NULL, &out);
778 adios("mhbuild", "unable to create temporary file");
780 /* use a temp file to collect the plain text lines */
781 ce->ce_file = getcpy(cp);
784 if (buf[0] == '#' && buf[1] == '<') {
785 strncpy(content, buf + 2, sizeof(content));
792 /* the directive is implicit */
793 strncpy(content, "text/plain", sizeof(content));
795 strncpy(buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
799 if (headers >= 0 && uprf(buffer, DESCR_FIELD) &&
800 buffer[i=strlen(DESCR_FIELD)] == ':') {
804 ct->c_descr = add(buffer + i + 1, ct->c_descr);
805 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
806 adios(NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
814 adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
822 if (headers >= 0 && uprf(buffer, DISPO_FIELD)
823 && buffer[i = strlen(DISPO_FIELD)] == ':') {
827 ct->c_dispo = add(buffer + i + 1, ct->c_dispo);
828 if (!fgetstr(buffer, sizeof(buffer) - 1, in))
829 adios(NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
837 adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
845 if (headers != 1 || buffer[0] != '\n')
851 if ((cp = fgetstr(buffer, sizeof(buffer) - 1, in))
854 if (buffer[0] == '#') {
857 if (buffer[1] != '#')
859 for (cp = (bp = buffer) + 1; *cp; cp++)
866 ct->c_end = ftell(out);
869 /* parse content type */
870 if (get_ctinfo(content, ct, inlineD) == NOTOK)
873 for (s2i = str2cts; s2i->si_key; s2i++)
874 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
876 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
880 ** check type specified (possibly implicitly)
882 switch (ct->c_type = s2i->si_val) {
884 if (!mh_strcasecmp(ci->ci_subtype, "rfc822")) {
885 ct->c_encoding = CE_7BIT;
890 adios(NULL, "it doesn't make sense to define an in-line %s content",
891 ct->c_type == CT_MESSAGE ? "message" :
897 if ((ct->c_ctinitfnx = s2i->si_init))
898 (*ct->c_ctinitfnx) (ct);
903 fseek(in, pos, SEEK_SET);
908 ** If we've reached this point, the next line
909 ** must be some type of explicit directive.
912 /* check if directive is external-type */
913 extrnal = (buf[1] == '@');
915 /* parse directive */
916 if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
919 /* check directive against the list of MIME types */
920 for (s2i = str2cts; s2i->si_key; s2i++)
921 if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
925 ** Check if the directive specified a valid type.
926 ** This will happen if it was one of the following forms:
928 ** #type/subtype (or)
933 adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
935 switch (ct->c_type = s2i->si_val) {
937 adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
941 if (!mh_strcasecmp(ci->ci_subtype, "partial"))
942 adios(NULL, "sorry, \"#%s/%s\" isn't supported", ci->ci_type, ci->ci_subtype);
943 if (!mh_strcasecmp(ci->ci_subtype, "external-body"))
944 adios(NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
946 adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
950 if ((ct->c_ctinitfnx = s2i->si_init))
951 (*ct->c_ctinitfnx) (ct);
956 ** #@type/subtype (external types directive)
963 adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
966 snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
971 ** Since we are using the current Content structure to
972 ** hold information about the type of the external
973 ** reference, we need to create another Content
974 ** structure for the message/external-body to wrap
977 if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
978 adios(NULL, "out of memory");
981 if (get_ctinfo(buffer, ct, 0) == NOTOK)
983 ct->c_type = CT_MESSAGE;
984 ct->c_subtype = MESSAGE_EXTERNAL;
986 if ((e = (struct exbody *)
987 calloc(1, sizeof(*e))) == NULL)
988 adios(NULL, "out of memory");
989 ct->c_ctparams = (void *) e;
995 if (params_external(ct, 1) == NOTOK)
1001 /* Handle [file] argument */
1003 /* check if specifies command to execute */
1004 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
1005 for (cp = ci->ci_magic + 1; isspace(*cp); cp++)
1008 adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
1013 /* record filename of decoded contents */
1014 ce->ce_file = ci->ci_magic;
1015 if (access(ce->ce_file, R_OK) == NOTOK)
1016 adios("reading", "unable to access %s for", ce->ce_file);
1017 if (listsw && stat(ce->ce_file, &st) != NOTOK)
1018 ct->c_end = (long) st.st_size;
1019 ci->ci_magic = NULL;
1025 ** No [file] argument, so check profile for
1026 ** method to compose content.
1028 snprintf(buffer, sizeof(buffer), "%s-compose-%s/%s",
1029 invo_name, ci->ci_type, ci->ci_subtype);
1030 if ((cp = context_find(buffer)) == NULL || *cp == '\0') {
1031 snprintf(buffer, sizeof(buffer), "%s-compose-%s",
1032 invo_name, ci->ci_type);
1033 if ((cp = context_find(buffer)) == NULL ||
1035 content_error(NULL, ct, "don't know how to compose content");
1039 ci->ci_magic = getcpy(cp);
1044 adios(NULL, "external definition not allowed for \"#%s\"",
1048 ** Message directive
1049 ** #forw [+folder] [msgs]
1051 if (!mh_strcasecmp(ci->ci_type, "forw")) {
1053 char *folder, *arguments[MAXARGS];
1059 ap = brkstring(ci->ci_magic, " ", "\n");
1060 for (i=0; ap[i] && i<MAXARGS-1; i++) {
1061 arguments[i] = ap[i];
1063 arguments[i] = NULL;
1066 arguments[0] = seq_cur;
1067 arguments[1] = NULL;
1071 /* search the arguments for a folder name */
1072 for (ap = arguments; *ap; ap++) {
1074 if (*cp == '+' || *cp == '@') {
1076 adios(NULL, "only one folder per #forw directive");
1078 folder = getcpy(expandfol(cp));
1082 /* else, use the current folder */
1084 folder = getcpy(getcurfol());
1086 if (!(mp = folder_read(folder)))
1087 adios(NULL, "unable to read folder %s", folder);
1088 for (ap = arguments; *ap; ap++) {
1090 if (*cp != '+' && *cp != '@')
1091 if (!m_convert(mp, cp))
1098 ** If there is more than one message to include, make this
1099 ** a content of type "multipart/digest" and insert each message
1100 ** as a subpart. If there is only one message, then make this
1101 ** a content of type "message/rfc822".
1103 if (mp->numsel > 1) {
1104 /* we are forwarding multiple messages */
1105 if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
1107 ct->c_type = CT_MULTIPART;
1108 ct->c_subtype = MULTI_DIGEST;
1110 if ((m = (struct multipart *)
1111 calloc(1, sizeof(*m))) == NULL)
1112 adios(NULL, "out of memory");
1113 ct->c_ctparams = (void *) m;
1116 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
1117 if (is_selected(mp, msgnum)) {
1122 if ((p = (CT) calloc(1, sizeof(*p)))
1124 adios(NULL, "out of memory");
1125 init_decoded_content(p);
1127 if (get_ctinfo("message/rfc822", p, 0)
1130 p->c_type = CT_MESSAGE;
1131 p->c_subtype = MESSAGE_RFC822;
1133 snprintf(buffer, sizeof(buffer),
1134 "%s/%d", mp->foldpath,
1136 pe->ce_file = getcpy(buffer);
1137 if (listsw && stat(pe->ce_file, &st)
1139 p->c_end = (long) st.st_size;
1141 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
1142 adios(NULL, "out of memory");
1144 pp = &part->mp_next;
1149 /* we are forwarding one message */
1150 if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
1152 ct->c_type = CT_MESSAGE;
1153 ct->c_subtype = MESSAGE_RFC822;
1155 msgnum = mp->lowsel;
1156 snprintf(buffer, sizeof(buffer), "%s/%d",
1157 mp->foldpath, msgnum);
1158 ce->ce_file = getcpy(buffer);
1159 if (listsw && stat(ce->ce_file, &st) != NOTOK)
1160 ct->c_end = (long) st.st_size;
1163 folder_free(mp); /* free folder/message structure */
1170 if (!mh_strcasecmp(ci->ci_type, "end")) {
1177 ** #begin [ alternative | parallel ]
1179 if (!mh_strcasecmp(ci->ci_type, "begin")) {
1180 if (!ci->ci_magic) {
1182 cp = SubMultiPart[vrsn - 1].kv_key;
1183 } else if (!mh_strcasecmp(ci->ci_magic, "alternative")) {
1184 vrsn = MULTI_ALTERNATE;
1185 cp = SubMultiPart[vrsn - 1].kv_key;
1186 } else if (!mh_strcasecmp(ci->ci_magic, "parallel")) {
1187 vrsn = MULTI_PARALLEL;
1188 cp = SubMultiPart[vrsn - 1].kv_key;
1189 } else if (uprf(ci->ci_magic, "digest")) {
1192 vrsn = MULTI_UNKNOWN;
1197 snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
1198 if (get_ctinfo(buffer, ct, 0) == NOTOK)
1200 ct->c_type = CT_MULTIPART;
1201 ct->c_subtype = vrsn;
1203 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1204 adios(NULL, "out of memory");
1205 ct->c_ctparams = (void *) m;
1208 while (fgetstr(buffer, sizeof(buffer) - 1, in)) {
1212 if (user_content(in, file, buffer, &p) == DONE) {
1214 adios(NULL, "empty \"#begin ... #end\" sequence");
1220 if ((part = (struct part *)
1221 calloc(1, sizeof(*part))) == NULL)
1222 adios(NULL, "out of memory");
1224 pp = &part->mp_next;
1227 admonish(NULL, "premature end-of-file, missing #end");
1232 ** Unknown directive
1234 adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
1235 return NOTOK; /* NOT REACHED */
1240 set_id(CT ct, int top)
1244 static time_t clock = 0;
1245 static char *msgfmt;
1249 snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
1250 (int) getpid(), (long) clock, LocalName());
1252 msgfmt = getcpy(msgid);
1254 snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
1255 ct->c_id = getcpy(msgid);
1259 static char ebcdicsafe[0x100] = {
1260 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1261 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1262 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1263 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1264 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
1265 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1266 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1267 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1268 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1269 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1270 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1271 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
1272 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1273 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1274 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
1275 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
1276 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1277 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1278 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1279 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1280 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1281 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1282 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1283 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1284 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1285 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1286 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1287 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1288 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1289 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1290 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1291 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1296 ** Fill out, or expand the various contents in the composition
1297 ** draft. Read-in any necessary files. Parse and execute any
1298 ** commands specified by profile composition strings.
1302 compose_content(CT ct)
1304 CE ce = ct->c_cefile;
1306 switch (ct->c_type) {
1311 char partnam[BUFSIZ];
1312 struct multipart *m = (struct multipart *) ct->c_ctparams;
1316 snprintf(partnam, sizeof(partnam), "%s.",
1318 pp = partnam + strlen(partnam);
1323 /* first, we call compose_content on all the subparts */
1324 for (part = m->mp_parts, partnum = 1; part;
1325 part = part->mp_next, partnum++) {
1326 CT p = part->mp_part;
1328 sprintf(pp, "%d", partnum);
1329 p->c_partno = getcpy(partnam);
1330 if (compose_content(p) == NOTOK)
1335 ** If the -rfc934mode switch is given, then check all
1336 ** the subparts of a multipart/digest. If they are all
1337 ** message/rfc822, then mark this content and all
1338 ** subparts with the rfc934 compatibility mode flag.
1340 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
1343 for (part = m->mp_parts; part; part = part->mp_next) {
1344 CT p = part->mp_part;
1346 if (p->c_subtype != MESSAGE_RFC822) {
1351 ct->c_rfc934 = is934;
1352 for (part = m->mp_parts; part; part = part->mp_next) {
1353 CT p = part->mp_part;
1355 if ((p->c_rfc934 = is934))
1361 ct->c_end = (partnum = strlen(prefix) + 2) + 2;
1365 for (part = m->mp_parts; part; part = part->mp_next)
1366 ct->c_end += part->mp_part->c_end + partnum;
1372 /* Nothing to do for type message */
1376 ** Discrete types (text/application/audio/image/video)
1381 int xstdout, len, buflen;
1382 char *bp, **ap, *cp;
1383 char *vec[4], buffer[BUFSIZ];
1385 CI ci = &ct->c_ctinfo;
1388 if (!(cp = ci->ci_magic))
1389 adios(NULL, "internal error(5)");
1391 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1392 if (tfile == NULL) {
1393 adios("mhbuild", "unable to create temporary file");
1395 ce->ce_file = getcpy(tfile);
1400 /* Get buffer ready to go */
1403 buflen = sizeof(buffer);
1406 ** Parse composition string into buffer
1408 for ( ; *cp; cp++) {
1414 ** insert parameters from
1420 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1421 snprintf(bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1431 /* %f, and stdout is not-redirected */
1437 ** insert temporary filename
1438 ** where content should be
1441 snprintf(bp, buflen, "%s", ce->ce_file);
1445 /* insert content subtype */
1446 strncpy(bp, ci->ci_subtype, buflen);
1450 /* insert character % */
1471 printf("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer);
1473 fflush(stdout); /* not sure if need for -noverbose */
1480 if ((out = fopen(ce->ce_file, "w")) == NULL)
1481 adios(ce->ce_file, "unable to open for writing");
1483 switch (child_id = fork()) {
1485 adios("fork", "unable to fork");
1490 dup2(fileno(out), 1);
1492 execvp("/bin/sh", vec);
1493 fprintf(stderr, "unable to exec ");
1500 if (pidXwait(child_id, NULL))
1506 /* Check size of file */
1507 if (listsw && ct->c_end == 0L) {
1510 if (stat(ce->ce_file, &st) != NOTOK)
1511 ct->c_end = (long) st.st_size;
1521 ** Scan the content.
1523 ** 1) choose a transfer encoding.
1524 ** 2) check for clashes with multipart boundary string.
1525 ** 3) for text content, figure out which character set is being used.
1527 ** If there is a clash with one of the contents and the multipart boundary,
1528 ** this function will exit with NOTOK. This will cause the scanning process
1529 ** to be repeated with a different multipart boundary. It is possible
1530 ** (although highly unlikely) that this scan will be repeated multiple times.
1537 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1538 int checklinelen = 0, linelen = 0; /* check for long lines */
1539 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1540 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1541 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1542 unsigned char *cp = NULL, buffer[BUFSIZ];
1543 struct text *t = NULL;
1545 CE ce = ct->c_cefile;
1548 ** handle multipart by scanning all subparts
1549 ** and then checking their encoding.
1551 if (ct->c_type == CT_MULTIPART) {
1552 struct multipart *m = (struct multipart *) ct->c_ctparams;
1555 /* initially mark the domain of enclosing multipart as 7bit */
1556 ct->c_encoding = CE_7BIT;
1558 for (part = m->mp_parts; part; part = part->mp_next) {
1559 CT p = part->mp_part;
1561 if (scan_content(p) == NOTOK) {
1562 /* choose encoding for subpart */
1567 ** if necessary, enlarge encoding for enclosing
1570 if (p->c_encoding == CE_BINARY)
1571 ct->c_encoding = CE_BINARY;
1572 if (p->c_encoding == CE_8BIT &&
1573 ct->c_encoding != CE_BINARY)
1574 ct->c_encoding = CE_8BIT;
1581 ** Decide what to check while scanning this content.
1583 switch (ct->c_type) {
1587 if (ct->c_subtype == TEXT_PLAIN) {
1592 checkebcdic = ebcdicsw;
1598 case CT_APPLICATION:
1600 checkebcdic = ebcdicsw;
1612 /* don't check anything for message/external */
1613 if (ct->c_subtype == MESSAGE_EXTERNAL)
1623 ** Don't check anything for these types,
1624 ** since we are forcing use of base64.
1635 ** Scan the unencoded content
1637 if (check8bit || checklinelen || checklinespace || checkboundary) {
1638 if ((in = fopen(ce->ce_file, "r")) == NULL)
1639 adios(ce->ce_file, "unable to open for reading");
1640 len = strlen(prefix);
1642 while (fgets(buffer, sizeof(buffer) - 1, in)) {
1644 ** Check for 8bit data.
1647 for (cp = buffer; *cp; cp++) {
1648 if (!isascii(*cp)) {
1650 /* no need to keep checking */
1654 ** Check if character is ebcdic-safe.
1655 ** We only check this if also checking
1658 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1660 /* no need to keep checking */
1667 ** Check line length.
1669 if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
1671 checklinelen = 0; /* no need to keep checking */
1675 ** Check if line ends with a space.
1677 if (checklinespace &&
1678 (cp = buffer + strlen(buffer) - 2) >
1679 buffer && isspace(*cp)) {
1681 /* no need to keep checking */
1686 ** Check if content contains a line that clashes
1687 ** with our standard boundary for multipart messages.
1689 if (checkboundary && buffer[0] == '-' &&
1691 for (cp = buffer + strlen(buffer) - 1;
1696 if (strncmp(buffer + 2, prefix, len)==0 &&
1697 isdigit(buffer[2 + len])) {
1699 /* no need to keep checking */
1708 ** Decide which transfer encoding to use.
1710 switch (ct->c_type) {
1713 ** If the text content didn't specify a character
1714 ** set, we need to figure out which one was used.
1716 t = (struct text *) ct->c_ctparams;
1717 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1718 CI ci = &ct->c_ctinfo;
1721 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1725 t->tx_charset = CHARSET_UNKNOWN;
1726 *ap = concat("charset=", write_charset_8bit(),
1729 t->tx_charset = CHARSET_USASCII;
1730 *ap = getcpy("charset=us-ascii");
1733 cp = strchr(*ap++, '=');
1739 if (contains8bit || ebcdicunsafe || linelen || linespace ||
1741 ct->c_encoding = CE_QUOTED;
1743 ct->c_encoding = CE_7BIT;
1746 case CT_APPLICATION:
1747 /* For application type, use base64, except when postscript */
1748 if (contains8bit || ebcdicunsafe || linelen || linespace ||
1750 ct->c_encoding = (ct->c_subtype ==
1751 APPLICATION_POSTSCRIPT) ?
1752 CE_QUOTED : CE_BASE64;
1754 ct->c_encoding = CE_7BIT;
1758 ct->c_encoding = CE_7BIT;
1764 /* For audio, image, and video contents, just use base64 */
1765 ct->c_encoding = CE_BASE64;
1769 return (boundaryclash ? NOTOK : OK);
1774 ** Scan the content structures, and build header
1775 ** fields that will need to be output into the
1780 build_headers(CT ct)
1782 int cc, mailbody, len;
1784 char *np, *vp, buffer[BUFSIZ];
1785 CI ci = &ct->c_ctinfo;
1788 ** If message is type multipart, then add the multipart
1789 ** boundary to the list of attribute/value pairs.
1791 if (ct->c_type == CT_MULTIPART) {
1793 static int level = 0; /* store nesting level */
1797 snprintf(buffer, sizeof(buffer), "boundary=%s%d",
1799 cp = strchr(*ap++ = getcpy(buffer), '=');
1806 ** Skip the output of Content-Type, parameters, content
1807 ** description and disposition, and Content-ID if the
1808 ** content is of type "message" and the rfc934 compatibility
1809 ** flag is set (which means we are inside multipart/digest
1810 ** and the switch -rfc934mode was given).
1812 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1816 ** output the content type and subtype
1818 np = getcpy(TYPE_FIELD);
1819 vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1821 /* keep track of length of line */
1822 len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
1823 strlen(ci->ci_subtype) + 3;
1825 mailbody = ct->c_type == CT_MESSAGE &&
1826 ct->c_subtype == MESSAGE_EXTERNAL &&
1827 ((struct exbody *) ct->c_ctparams)->eb_body;
1830 ** Append the attribute/value pairs to
1831 ** the end of the Content-Type line.
1833 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1834 if (mailbody && !mh_strcasecmp(*ap, "body"))
1840 snprintf(buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1841 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
1842 vp = add("\n\t", vp);
1848 vp = add(buffer, vp);
1853 ** Append any RFC-822 comment to the end of
1854 ** the Content-Type line.
1856 if (ci->ci_comment) {
1857 snprintf(buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1858 if (len + 1 + (cc = 2 + strlen(ci->ci_comment)) >= CPERLIN) {
1859 vp = add("\n\t", vp);
1865 vp = add(buffer, vp);
1869 add_header(ct, np, vp);
1872 ** output the Content-ID, unless disabled by -nocontentid
1874 if (contentidsw && ct->c_id) {
1875 np = getcpy(ID_FIELD);
1876 vp = concat(" ", ct->c_id, NULL);
1877 add_header(ct, np, vp);
1881 ** output the Content-Description
1884 np = getcpy(DESCR_FIELD);
1885 vp = concat(" ", ct->c_descr, NULL);
1886 add_header(ct, np, vp);
1890 ** output the Content-Disposition
1893 np = getcpy(DISPO_FIELD);
1894 vp = concat(" ", ct->c_dispo, NULL);
1895 add_header(ct, np, vp);
1900 ** If this is the internal content structure for a
1901 ** "message/external", then we are done with the
1902 ** headers (since it has no body).
1908 ** output the Content-MD5
1911 np = getcpy(MD5_FIELD);
1912 vp = calculate_digest(ct, (ct->c_encoding == CE_QUOTED) ?
1914 add_header(ct, np, vp);
1918 ** output the Content-Transfer-Encoding
1920 switch (ct->c_encoding) {
1922 /* Nothing to output */
1926 if (ct->c_type == CT_MESSAGE)
1927 adios(NULL, "internal error, invalid encoding");
1929 np = getcpy(ENCODING_FIELD);
1930 vp = concat(" ", "8bit", "\n", NULL);
1931 add_header(ct, np, vp);
1935 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1936 adios(NULL, "internal error, invalid encoding");
1938 np = getcpy(ENCODING_FIELD);
1939 vp = concat(" ", "quoted-printable", "\n", NULL);
1940 add_header(ct, np, vp);
1944 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1945 adios(NULL, "internal error, invalid encoding");
1947 np = getcpy(ENCODING_FIELD);
1948 vp = concat(" ", "base64", "\n", NULL);
1949 add_header(ct, np, vp);
1953 if (ct->c_type == CT_MESSAGE)
1954 adios(NULL, "internal error, invalid encoding");
1956 np = getcpy(ENCODING_FIELD);
1957 vp = concat(" ", "binary", "\n", NULL);
1958 add_header(ct, np, vp);
1962 adios(NULL, "unknown transfer encoding in content");
1967 ** Additional content specific header processing
1969 switch (ct->c_type) {
1972 struct multipart *m;
1975 m = (struct multipart *) ct->c_ctparams;
1976 for (part = m->mp_parts; part; part = part->mp_next) {
1986 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1989 e = (struct exbody *) ct->c_ctparams;
1990 build_headers(e->eb_content);
2003 static char nib2b64[0x40+1] =
2004 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2007 calculate_digest(CT ct, int asciiP)
2010 char buffer[BUFSIZ], *vp, *op;
2012 unsigned char digest[16];
2013 unsigned char outbuf[25];
2016 CE ce = ct->c_cefile;
2019 if ((in = fopen(ce->ce_file, "r")) == NULL)
2020 adios(ce->ce_file, "unable to open for reading");
2022 /* Initialize md5 context */
2023 MD5Init(&mdContext);
2025 /* calculate md5 message digest */
2027 while (fgets(buffer, sizeof(buffer) - 1, in)) {
2030 cp = buffer + strlen(buffer) - 1;
2031 if ((c = *cp) == '\n')
2034 MD5Update(&mdContext, (unsigned char *) buffer,
2035 (unsigned int) strlen(buffer));
2038 MD5Update(&mdContext, (unsigned char *) "\r\n",
2042 while ((cc = fread(buffer, sizeof(*buffer), sizeof(buffer),
2044 MD5Update(&mdContext, (unsigned char *) buffer,
2048 /* md5 finalization. Write digest and zero md5 context */
2049 MD5Final(digest, &mdContext);
2054 /* print debugging info */
2058 fprintf(stderr, "MD5 digest=");
2059 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
2061 fprintf(stderr, "%02x", *dp & 0xff);
2062 fprintf(stderr, "\n");
2065 /* encode the digest using base64 */
2066 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
2067 cc > 0; cc -= 3, op += 4) {
2071 bits = (*dp++ & 0xff) << 16;
2073 bits |= (*dp++ & 0xff) << 8;
2075 bits |= *dp++ & 0xff;
2078 for (bp = op + 4; bp > op; bits >>= 6)
2079 *--bp = nib2b64[bits & 0x3f];
2087 /* null terminate string */
2090 /* now make copy and return string */
2091 vp = concat(" ", outbuf, "\n", NULL);