Clean up process wait to use POSIX waitpid() interface.
[mmh] / uip / mhbuild.c
1 /*
2 ** mhbuild.c -- expand/translate MIME composition files
3 **
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.
7 */
8
9 /*
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.
14 */
15
16 #include <h/mh.h>
17 #include <fcntl.h>
18 #include <h/signals.h>
19 #include <h/md5.h>
20 #include <errno.h>
21 #include <signal.h>
22 #include <h/tws.h>
23 #include <h/mime.h>
24 #include <h/mhparse.h>
25 #include <h/mhcachesbr.h>
26 #include <h/utils.h>
27
28 #ifdef TIME_WITH_SYS_TIME
29 # include <sys/time.h>
30 # include <time.h>
31 #else
32 # ifdef TM_IN_SYS_TIME
33 #  include <sys/time.h>
34 # else
35 #  include <time.h>
36 # endif
37 #endif
38
39 static struct swit switches[] = {
40 #define CHECKSW  0
41         { "check", 0 },
42 #define NCHECKSW  1
43         { "nocheck", 0 },
44 #define EBCDICSW  2
45         { "ebcdicsafe", 0 },
46 #define NEBCDICSW  3
47         { "noebcdicsafe", 0 },
48 #define HEADSW  4
49         { "headers", 0 },
50 #define NHEADSW  5
51         { "noheaders", 0 },
52 #define LISTSW  6
53         { "list", 0 },
54 #define NLISTSW  7
55         { "nolist", 0 },
56 #define SIZESW  8
57         { "realsize", 0 },
58 #define NSIZESW  9
59         { "norealsize", 0 },
60 #define RFC934SW  10
61         { "rfc934mode", 0 },
62 #define NRFC934SW  11
63         { "norfc934mode", 0 },
64 #define VERBSW  12
65         { "verbose", 0 },
66 #define NVERBSW  13
67         { "noverbose", 0 },
68 #define RCACHESW  14
69         { "rcache policy", 0 },
70 #define WCACHESW  15
71         { "wcache policy", 0 },
72 #define CONTENTIDSW  16
73         { "contentid", 0 },
74 #define NCONTENTIDSW  17
75         { "nocontentid", 0 },
76 #define VERSIONSW  18
77         { "version", 0 },
78 #define HELPSW  19
79         { "help", 0 },
80 #define DEBUGSW  20
81         { "debug", -5 },
82         { NULL, 0 }
83 };
84
85
86 /*
87 ** Directory to place tmp files.  This must
88 ** be set before these routines are called.
89 */
90 char *tmp;
91
92 pid_t xpid = 0;
93
94 static char prefix[] = "----- =_aaaaaaaaaa";
95
96
97 /*
98 ** prototypes
99 */
100
101 /* mhmisc.c */
102 int make_intermediates(char *);
103 void content_error(char *, CT, char *, ...);
104
105 /* mhcachesbr.c */
106 int find_cache(CT, int, int *, char *, char *, int);
107
108 /* ftpsbr.c */
109 int ftp_get(char *, char *, char *, char *, char *, char *, int, int);
110
111 /* mhfree.c */
112 void free_content(CT);
113 void free_ctinfo(CT);
114 void free_encoding(CT, int);
115
116 /*
117 ** static prototypes
118 */
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 *);
128
129
130
131 /* mhcachesbr.c */
132 extern int rcachesw;
133 extern int wcachesw;
134 extern char *cache_public;
135 extern char *cache_private;
136
137 int debugsw = 0;
138 int verbosw = 0;
139
140 int ebcdicsw = 0;
141 int listsw   = 0;
142 int rfc934sw = 0;
143 int contentidsw = 1;
144
145 /*
146 ** Temporary files
147 */
148 static char infile[BUFSIZ];
149 static int unlink_infile  = 0;
150
151 static char outfile[BUFSIZ];
152 static int unlink_outfile = 0;
153
154 static void unlink_done(int) NORETURN;
155
156 /* mhoutsbr.c */
157 int output_message(CT, char *);
158 int output_message_fp(CT, FILE *, char*);
159
160 /* mhlistsbr.c */
161 int list_all_messages(CT *, int, int, int, int);
162
163 /* mhmisc.c */
164 void set_endian(void);
165
166 /* mhfree.c */
167 void free_content(CT);
168
169
170 int
171 main(int argc, char **argv)
172 {
173         int sizesw = 1, headsw = 1;
174         int *icachesw;
175         char *cp, buf[BUFSIZ];
176         char buffer[BUFSIZ], *compfile = NULL;
177         char **argp, **arguments;
178         CT ct, cts[2];
179         FILE *fp = NULL;
180         FILE *fp_out = NULL;
181
182         done = unlink_done;
183
184 #ifdef LOCALE
185         setlocale(LC_ALL, "");
186 #endif
187         invo_name = mhbasename(argv[0]);
188
189         /* read user profile/context */
190         context_read();
191
192         arguments = getarguments(invo_name, argc, argv, 1);
193         argp = arguments;
194
195         while ((cp = *argp++)) {
196                 if (cp[0] == '-' && cp[1] == '\0') {
197                         if (compfile)
198                                 adios(NULL, "cannot specify both standard input and a file");
199                         else
200                                 compfile = cp;
201                         listsw = 0;  /* turn off -list if using std in/out */
202                         verbosw = 0;  /* turn off -verbose listings */
203                         break;
204                 }
205                 if (*cp == '-') {
206                         switch (smatch(++cp, switches)) {
207                         case AMBIGSW:
208                                 ambigsw(cp, switches);
209                                 done(1);
210                         case UNKWNSW:
211                                 adios(NULL, "-%s unknown", cp);
212
213                         case HELPSW:
214                                 snprintf(buf, sizeof(buf), "%s [switches] file", invo_name);
215                                 print_help(buf, switches, 1);
216                                 done(1);
217                         case VERSIONSW:
218                                 print_version(invo_name);
219                                 done(1);
220
221                         case RCACHESW:
222                                 icachesw = &rcachesw;
223                                 goto do_cache;
224                         case WCACHESW:
225                                 icachesw = &wcachesw;
226                         do_cache: ;
227                                 if (!(cp = *argp++) || *cp == '-')
228                                         adios(NULL, "missing argument to %s",
229                                                         argp[-2]);
230                                 switch (*icachesw = smatch(cp, caches)) {
231                                 case AMBIGSW:
232                                         ambigsw(cp, caches);
233                                         done(1);
234                                 case UNKWNSW:
235                                         adios(NULL, "%s unknown", cp);
236                                 default:
237                                         break;
238                                 }
239                                 continue;
240
241                         case CHECKSW:
242                                 checksw++;
243                                 continue;
244                         case NCHECKSW:
245                                 checksw = 0;
246                                 continue;
247
248                         case EBCDICSW:
249                                 ebcdicsw++;
250                                 continue;
251                         case NEBCDICSW:
252                                 ebcdicsw = 0;
253                                 continue;
254
255                         case HEADSW:
256                                 headsw++;
257                                 continue;
258                         case NHEADSW:
259                                 headsw = 0;
260                                 continue;
261
262                         case LISTSW:
263                                 listsw++;
264                                 continue;
265                         case NLISTSW:
266                                 listsw = 0;
267                                 continue;
268
269                         case RFC934SW:
270                                 rfc934sw++;
271                                 continue;
272                         case NRFC934SW:
273                                 rfc934sw = 0;
274                                 continue;
275
276                         case SIZESW:
277                                 sizesw++;
278                                 continue;
279                         case NSIZESW:
280                                 sizesw = 0;
281                                 continue;
282
283                         case CONTENTIDSW:
284                                 contentidsw = 1;
285                                 continue;
286                         case NCONTENTIDSW:
287                                 contentidsw = 0;
288                                 continue;
289
290                         case VERBSW:
291                                 verbosw++;
292                                 continue;
293                         case NVERBSW:
294                                 verbosw = 0;
295                                 continue;
296                         case DEBUGSW:
297                                 debugsw = 1;
298                                 continue;
299                         }
300                 }
301                 if (compfile)
302                         adios(NULL, "only one composition file allowed");
303                 else
304                         compfile = cp;
305         }
306
307         set_endian();
308
309         if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0)
310                 listsw  = 0;
311
312         /*
313         ** Check if we've specified an additional profile
314         */
315         if ((cp = getenv("MHBUILD"))) {
316                 if ((fp = fopen(cp, "r"))) {
317                         readconfig((struct node **) 0, fp, cp, 0);
318                         fclose(fp);
319                 } else {
320                         admonish("", "unable to read $MHBUILD profile (%s)",
321                                         cp);
322                 }
323         }
324
325         /*
326         ** Read the standard profile setup
327         */
328         if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
329                 readconfig((struct node **) 0, fp, cp, 0);
330                 fclose(fp);
331         }
332
333         /* Check for public cache location */
334         if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
335                 cache_public = NULL;
336
337         /* Check for private cache location */
338         if (!(cache_private = context_find(nmhprivcache)))
339                 cache_private = ".cache";
340         cache_private = getcpy(toabsdir(cache_private));
341
342         /*
343         ** Check for storage directory.  If defined, we
344         ** will store temporary files there.  Else we
345         ** store them in standard nmh directory.
346         */
347         if ((cp = context_find(nmhstorage)) && *cp)
348                 tmp = concat(cp, "/", invo_name, NULL);
349         else
350                 tmp = getcpy(toabsdir(invo_name));
351
352         /* Check if we have a file to process */
353         if (!compfile)
354                 adios(NULL, "need to specify a %s composition file",
355                                 invo_name);
356
357         /*
358         ** Process the composition file from standard input.
359         */
360         if (compfile[0] == '-' && compfile[1] == '\0') {
361                 /* copy standard input to temporary file */
362                 strncpy(infile, m_mktemp(invo_name, NULL, &fp),
363                                 sizeof(infile));
364                 while (fgets(buffer, BUFSIZ, stdin))
365                         fputs(buffer, fp);
366                 fclose(fp);
367                 unlink_infile = 1;
368
369                 /* build the content structures for MIME message */
370                 ct = build_mime(infile);
371                 cts[0] = ct;
372                 cts[1] = NULL;
373
374                 /* output MIME message to this temporary file */
375                 strncpy(outfile, m_mktemp(invo_name, NULL, &fp_out),
376                                 sizeof(outfile));
377                 unlink_outfile = 1;
378
379                 /* output the message */
380                 output_message_fp(ct, fp_out, outfile);
381                 fclose(fp_out);
382
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);
388                 fclose(fp);
389
390                 unlink(infile);
391                 unlink_infile = 0;
392
393                 unlink(outfile);
394                 unlink_outfile = 0;
395
396                 free_content(ct);
397                 done(0);
398         }
399
400         /*
401         ** Process the composition file from a file.
402         */
403
404         /* build the content structures for MIME message */
405         ct = build_mime(compfile);
406         cts[0] = ct;
407         cts[1] = NULL;
408
409         /* output MIME message to this temporary file */
410         strncpy(outfile, m_mktemp2(compfile, invo_name, NULL, &fp_out),
411                         sizeof(outfile));
412         unlink_outfile = 1;
413
414         /* output the message */
415         output_message_fp(ct, fp_out, outfile);
416         fclose(fp_out);
417
418         /*
419         ** List the message info
420         */
421         if (listsw)
422                 list_all_messages(cts, headsw, sizesw, verbosw, debugsw);
423
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);
428         }
429
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);
434                 done(1);
435         }
436         unlink_outfile = 0;
437
438         free_content(ct);
439         done(0);
440         return 1;
441 }
442
443
444 static void
445 unlink_done(int status)
446 {
447         /*
448         ** Check if we need to remove stray temporary files.
449         */
450         if (unlink_infile)
451                 unlink(infile);
452         if (unlink_outfile)
453                 unlink(outfile);
454
455         exit(status);
456 }
457
458 /*
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
464 ** output_message().
465 */
466 static CT
467 build_mime(char *infile)
468 {
469         int compnum, state;
470         char buf[BUFSIZ], name[NAMESZ];
471         char *cp, *np, *vp;
472         struct multipart *m;
473         struct part **pp;
474         CT ct;
475         FILE *in;
476
477         umask(~m_gmprot());
478
479         /* open the composition draft */
480         if ((in = fopen(infile, "r")) == NULL)
481                 adios(infile, "unable to open for reading");
482
483         /*
484         ** Allocate space for primary (outside) content
485         */
486         if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
487                 adios(NULL, "out of memory");
488
489         /*
490         ** Allocate structure for handling decoded content
491         ** for this part.  We don't really need this, but
492         ** allocate it to remain consistent.
493         */
494         init_decoded_content(ct);
495
496         /*
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.
500         */
501         for (compnum = 1, state = FLD;;) {
502                 switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
503                 case FLD:
504                 case FLDPLUS:
505                 case FLDEOF:
506                         compnum++;
507
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);
511
512                         /*
513                         ** abort if draft has Content-Transfer-Encoding
514                         ** header field
515                         */
516                         if (!mh_strcasecmp(name, ENCODING_FIELD))
517                                 adios(NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
518
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,
523                                                         sizeof(buf), in);
524                                 goto finish_field;
525                         }
526
527                         /* get copies of the buffers */
528                         np = getcpy(name);
529                         vp = getcpy(buf);
530
531                         /* if necessary, get rest of field */
532                         while (state == FLDPLUS) {
533                                 state = m_getfld(state, name, buf,
534                                                 sizeof(buf), in);
535                                 vp = add(buf, vp);  /* add to prev value */
536                         }
537
538                         /* Now add the header data to the list */
539                         add_header(ct, np, vp);
540
541 finish_field:
542                         /* if this wasn't the last hdr field, then continue */
543                         if (state != FLDEOF)
544                                 continue;
545                         /* else fall... */
546
547                 case FILEEOF:
548                         adios(NULL, "draft has empty body -- no directives!");
549                         /* NOTREACHED */
550
551                 case BODY:
552                 case BODYEOF:
553                         fseek(in, (long) (-strlen(buf)), SEEK_CUR);
554                         break;
555
556                 case LENERR:
557                 case FMTERR:
558                         adios(NULL, "message format error in component #%d",
559                                         compnum);
560
561                 default:
562                         adios(NULL, "getfld() returned %d", state);
563                 }
564                 break;
565         }
566
567         /*
568         ** Now add the MIME-Version header field
569         ** to the list of header fields.
570         */
571         np = getcpy(VRSN_FIELD);
572         vp = concat(" ", VRSN_VALUE, "\n", NULL);
573         add_header(ct, np, vp);
574
575         /*
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.
579         */
580         if (get_ctinfo("multipart/mixed", ct, 0) == NOTOK)
581                 done(1);
582         ct->c_type = CT_MULTIPART;
583         ct->c_subtype = MULTI_MIXED;
584         ct->c_file = getcpy(infile);
585
586         if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
587                 adios(NULL, "out of memory");
588         ct->c_ctparams = (void *) m;
589         pp = &m->mp_parts;
590
591         /*
592         ** read and parse the composition file
593         ** and the directives it contains.
594         */
595         while (fgetstr(buf, sizeof(buf) - 1, in)) {
596                 struct part *part;
597                 CT p;
598
599                 if (user_content(in, infile, buf, &p) == DONE) {
600                         admonish(NULL, "ignoring spurious #end");
601                         continue;
602                 }
603                 if (!p)
604                         continue;
605
606                 if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
607                         adios(NULL, "out of memory");
608                 *pp = part;
609                 pp = &part->mp_next;
610                 part->mp_part = p;
611         }
612
613         /*
614         ** close the composition draft since
615         ** it's not needed any longer.
616         */
617         fclose(in);
618
619         /* check if any contents were found */
620         if (!m->mp_parts)
621                 adios(NULL, "no content directives found");
622
623         /*
624         ** If only one content was found, then remove and
625         ** free the outer multipart content.
626         */
627         if (!m->mp_parts->mp_next) {
628                 CT p;
629
630                 p = m->mp_parts->mp_part;
631                 m->mp_parts->mp_part = NULL;
632
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;
638
639                 free_content(ct);
640                 ct = p;
641         } else {
642                 set_id(ct, 1);
643         }
644
645         /*
646         ** Fill out, or expand directives.  Parse and execute
647         ** commands specified by profile composition strings.
648         */
649         compose_content(ct);
650
651         if ((cp = strchr(prefix, 'a')) == NULL)
652                 adios(NULL, "internal error(4)");
653
654         /*
655         ** Scan the contents.  Choose a transfer encoding, and
656         ** check if prefix for multipart boundary clashes with
657         ** any of the contents.
658         */
659         while (scan_content(ct) == NOTOK) {
660                 if (*cp < 'z') {
661                         (*cp)++;
662                 } else {
663                         if (*++cp == 0)
664                                 adios(NULL, "giving up trying to find a unique delimiter string");
665                                 else
666                                 (*cp)++;
667                 }
668         }
669
670         /* Build the rest of the header field structures */
671         build_headers(ct);
672
673         return ct;
674 }
675
676
677 /*
678 ** Set up structures for placing unencoded
679 ** content when building parts.
680 */
681
682 static int
683 init_decoded_content(CT ct)
684 {
685         CE ce;
686
687         if ((ce = (CE) calloc(1, sizeof(*ce))) == NULL)
688                 adios(NULL, "out of memory");
689
690         ct->c_cefile     = ce;
691         ct->c_ceopenfnx  = open7Bit;  /* since unencoded */
692         ct->c_ceclosefnx = close_encoding;
693         ct->c_cesizefnx  = NULL;  /* since unencoded */
694
695         return OK;
696 }
697
698
699 static char *
700 fgetstr(char *s, int n, FILE *stream)
701 {
702         char *cp, *ep;
703
704         for (ep = (cp = s) + n; cp < ep; ) {
705                 int i;
706
707                 if (!fgets(cp, n, stream))
708                         return (cp != s ? s : NULL);
709                 if (cp == s && *cp != '#')
710                         return s;
711
712                 cp += (i = strlen(cp)) - 1;
713                 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
714                         break;
715                 *cp = '\0';
716                 n -= (i - 2);
717         }
718
719         return s;
720 }
721
722
723 /*
724 ** Parse the composition draft for text and directives.
725 ** Do initial setup of Content structure.
726 */
727
728 static int
729 user_content(FILE *in, char *file, char *buf, CT *ctp)
730 {
731         int extrnal, vrsn;
732         unsigned char *cp;
733         char **ap;
734         char buffer[BUFSIZ];
735         struct multipart *m;
736         struct part **pp;
737         struct stat st;
738         struct str2init *s2i;
739         CI ci;
740         CT ct;
741         CE ce;
742
743         if (buf[0] == '\n' || strcmp(buf, "#\n") == 0) {
744                 *ctp = NULL;
745                 return OK;
746         }
747
748         /* allocate basic Content structure */
749         if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
750                 adios(NULL, "out of memory");
751         *ctp = ct;
752
753         /* allocate basic structure for handling decoded content */
754         init_decoded_content(ct);
755         ce = ct->c_cefile;
756
757         ci = &ct->c_ctinfo;
758         set_id(ct, 0);
759
760         /*
761         ** Handle inline text.  Check if line
762         ** is one of the following forms:
763         **
764         ** 1) doesn't begin with '#'  (implicit directive)
765         ** 2) begins with "##"        (implicit directive)
766         ** 3) begins with "#<"
767         */
768         if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
769                 int headers;
770                 int inlineD;
771                 long pos;
772                 char content[BUFSIZ];
773                 FILE *out;
774                 char *cp;
775
776                 cp = m_mktemp2(NULL, invo_name, NULL, &out);
777                 if (cp == NULL)
778                         adios("mhbuild", "unable to create temporary file");
779
780                 /* use a temp file to collect the plain text lines */
781                 ce->ce_file = getcpy(cp);
782                 ce->ce_unlink = 1;
783
784                 if (buf[0] == '#' && buf[1] == '<') {
785                         strncpy(content, buf + 2, sizeof(content));
786                         inlineD = 1;
787                         goto rock_and_roll;
788                 } else {
789                         inlineD = 0;
790                 }
791
792                 /* the directive is implicit */
793                 strncpy(content, "text/plain", sizeof(content));
794                 headers = 0;
795                 strncpy(buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
796                 for (;;) {
797                         int i;
798
799                         if (headers >= 0 && uprf(buffer, DESCR_FIELD) &&
800                                         buffer[i=strlen(DESCR_FIELD)] == ':') {
801                                 headers = 1;
802
803 again_descr:
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);
807                                 switch (buffer[0]) {
808                                 case ' ':
809                                 case '\t':
810                                         i = -1;
811                                         goto again_descr;
812
813                                 case '#':
814                                         adios(NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
815                                         /* NOTREACHED */
816
817                                 default:
818                                         break;
819                                 }
820                         }
821
822                         if (headers >= 0 && uprf(buffer, DISPO_FIELD)
823                                 && buffer[i = strlen(DISPO_FIELD)] == ':') {
824                                 headers = 1;
825
826 again_dispo:
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);
830                                 switch (buffer[0]) {
831                                 case ' ':
832                                 case '\t':
833                                         i = -1;
834                                         goto again_dispo;
835
836                                 case '#':
837                                         adios(NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
838                                         /* NOTREACHED */
839
840                                 default:
841                                         break;
842                                 }
843                         }
844
845                         if (headers != 1 || buffer[0] != '\n')
846                                 fputs(buffer, out);
847
848 rock_and_roll:
849                         headers = -1;
850                         pos = ftell(in);
851                         if ((cp = fgetstr(buffer, sizeof(buffer) - 1, in))
852                                         == NULL)
853                                 break;
854                         if (buffer[0] == '#') {
855                                 char *bp;
856
857                                 if (buffer[1] != '#')
858                                         break;
859                                 for (cp = (bp = buffer) + 1; *cp; cp++)
860                                         *bp++ = *cp;
861                                 *bp = '\0';
862                         }
863                 }
864
865                 if (listsw)
866                         ct->c_end = ftell(out);
867                 fclose(out);
868
869                 /* parse content type */
870                 if (get_ctinfo(content, ct, inlineD) == NOTOK)
871                         done(1);
872
873                 for (s2i = str2cts; s2i->si_key; s2i++)
874                         if (!mh_strcasecmp(ci->ci_type, s2i->si_key))
875                                 break;
876                 if (!s2i->si_key && !uprf(ci->ci_type, "X-"))
877                         s2i++;
878
879                 /*
880                 ** check type specified (possibly implicitly)
881                 */
882                 switch (ct->c_type = s2i->si_val) {
883                 case CT_MESSAGE:
884                         if (!mh_strcasecmp(ci->ci_subtype, "rfc822")) {
885                                 ct->c_encoding = CE_7BIT;
886                                 goto call_init;
887                         }
888                         /* else fall... */
889                 case CT_MULTIPART:
890                         adios(NULL, "it doesn't make sense to define an in-line %s content",
891                                         ct->c_type == CT_MESSAGE ? "message" :
892                                         "multipart");
893                         /* NOTREACHED */
894
895                 default:
896 call_init:
897                         if ((ct->c_ctinitfnx = s2i->si_init))
898                                 (*ct->c_ctinitfnx) (ct);
899                         break;
900                 }
901
902                 if (cp)
903                         fseek(in, pos, SEEK_SET);
904                 return OK;
905         }
906
907         /*
908         ** If we've reached this point, the next line
909         ** must be some type of explicit directive.
910         */
911
912         /* check if directive is external-type */
913         extrnal = (buf[1] == '@');
914
915         /* parse directive */
916         if (get_ctinfo(buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
917                 done(1);
918
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))
922                         break;
923
924         /*
925         ** Check if the directive specified a valid type.
926         ** This will happen if it was one of the following forms:
927         **
928         **    #type/subtype  (or)
929         **    #@type/subtype
930         */
931         if (s2i->si_key) {
932                 if (!ci->ci_subtype)
933                         adios(NULL, "missing subtype in \"#%s\"", ci->ci_type);
934
935                 switch (ct->c_type = s2i->si_val) {
936                 case CT_MULTIPART:
937                         adios(NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
938                         /* NOTREACHED */
939
940                 case CT_MESSAGE:
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);
945 use_forw:
946                         adios(NULL, "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"", ci->ci_type, ci->ci_subtype);
947                         /* NOTREACHED */
948
949                 default:
950                         if ((ct->c_ctinitfnx = s2i->si_init))
951                                 (*ct->c_ctinitfnx) (ct);
952                         break;
953                 }
954
955                 /*
956                 ** #@type/subtype (external types directive)
957                 */
958                 if (extrnal) {
959                         struct exbody *e;
960                         CT p;
961
962                         if (!ci->ci_magic)
963                                 adios(NULL, "need external information for \"#@%s/%s\"", ci->ci_type, ci->ci_subtype);
964                         p = ct;
965
966                         snprintf(buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
967                         free(ci->ci_magic);
968                         ci->ci_magic = NULL;
969
970                         /*
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
975                         ** it in.
976                         */
977                         if ((ct = (CT) calloc(1, sizeof(*ct))) == NULL)
978                                 adios(NULL, "out of memory");
979                         *ctp = ct;
980                         ci = &ct->c_ctinfo;
981                         if (get_ctinfo(buffer, ct, 0) == NOTOK)
982                                 done(1);
983                         ct->c_type = CT_MESSAGE;
984                         ct->c_subtype = MESSAGE_EXTERNAL;
985
986                         if ((e = (struct exbody *)
987                                         calloc(1, sizeof(*e))) == NULL)
988                                 adios(NULL, "out of memory");
989                         ct->c_ctparams = (void *) e;
990
991                         e->eb_parent = ct;
992                         e->eb_content = p;
993                         p->c_ctexbody = e;
994
995                         if (params_external(ct, 1) == NOTOK)
996                                 done(1);
997
998                         return OK;
999                 }
1000
1001                 /* Handle [file] argument */
1002                 if (ci->ci_magic) {
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++)
1006                                         continue;
1007                                 if (!*cp)
1008                                         adios(NULL, "empty pipe command for #%s directive", ci->ci_type);
1009                                 cp = getcpy(cp);
1010                                 free(ci->ci_magic);
1011                                 ci->ci_magic = cp;
1012                         } else {
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;
1020                         }
1021                         return OK;
1022                 }
1023
1024                 /*
1025                 ** No [file] argument, so check profile for
1026                 ** method to compose content.
1027                 */
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 ||
1034                                         *cp == '\0') {
1035                                 content_error(NULL, ct, "don't know how to compose content");
1036                                 done(1);
1037                         }
1038                 }
1039                 ci->ci_magic = getcpy(cp);
1040                 return OK;
1041         }
1042
1043         if (extrnal)
1044                 adios(NULL, "external definition not allowed for \"#%s\"",
1045                                 ci->ci_type);
1046
1047         /*
1048         ** Message directive
1049         ** #forw [+folder] [msgs]
1050         */
1051         if (!mh_strcasecmp(ci->ci_type, "forw")) {
1052                 int msgnum;
1053                 char *folder, *arguments[MAXARGS];
1054                 struct msgs *mp;
1055
1056                 if (ci->ci_magic) {
1057                         int i;
1058
1059                         ap = brkstring(ci->ci_magic, " ", "\n");
1060                         for (i=0; ap[i] && i<MAXARGS-1; i++) {
1061                                 arguments[i] = ap[i];
1062                         }
1063                         arguments[i] = NULL;
1064
1065                 } else {
1066                         arguments[0] = seq_cur;
1067                         arguments[1] = NULL;
1068                 }
1069                 folder = NULL;
1070
1071                 /* search the arguments for a folder name */
1072                 for (ap = arguments; *ap; ap++) {
1073                         cp = *ap;
1074                         if (*cp == '+' || *cp == '@') {
1075                                 if (folder)
1076                                         adios(NULL, "only one folder per #forw directive");
1077                                 else
1078                                         folder = getcpy(expandfol(cp));
1079                         }
1080                 }
1081
1082                 /* else, use the current folder */
1083                 if (!folder)
1084                         folder = getcpy(getcurfol());
1085
1086                 if (!(mp = folder_read(folder)))
1087                         adios(NULL, "unable to read folder %s", folder);
1088                 for (ap = arguments; *ap; ap++) {
1089                         cp = *ap;
1090                         if (*cp != '+' && *cp != '@')
1091                                 if (!m_convert(mp, cp))
1092                                         done(1);
1093                 }
1094                 free(folder);
1095                 free_ctinfo(ct);
1096
1097                 /*
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".
1102                 */
1103                 if (mp->numsel > 1) {
1104                         /* we are forwarding multiple messages */
1105                         if (get_ctinfo("multipart/digest", ct, 0) == NOTOK)
1106                                 done(1);
1107                         ct->c_type = CT_MULTIPART;
1108                         ct->c_subtype = MULTI_DIGEST;
1109
1110                         if ((m = (struct multipart *)
1111                                         calloc(1, sizeof(*m))) == NULL)
1112                                 adios(NULL, "out of memory");
1113                         ct->c_ctparams = (void *) m;
1114                         pp = &m->mp_parts;
1115
1116                         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
1117                                 if (is_selected(mp, msgnum)) {
1118                                         struct part *part;
1119                                         CT p;
1120                                         CE pe;
1121
1122                                         if ((p = (CT) calloc(1, sizeof(*p)))
1123                                                         == NULL)
1124                                                 adios(NULL, "out of memory");
1125                                         init_decoded_content(p);
1126                                         pe = p->c_cefile;
1127                                         if (get_ctinfo("message/rfc822", p, 0)
1128                                                         == NOTOK)
1129                                                 done(1);
1130                                         p->c_type = CT_MESSAGE;
1131                                         p->c_subtype = MESSAGE_RFC822;
1132
1133                                         snprintf(buffer, sizeof(buffer),
1134                                                         "%s/%d", mp->foldpath,
1135                                                         msgnum);
1136                                         pe->ce_file = getcpy(buffer);
1137                                         if (listsw && stat(pe->ce_file, &st)
1138                                                         != NOTOK)
1139                                                 p->c_end = (long) st.st_size;
1140
1141                                         if ((part = (struct part *) calloc(1, sizeof(*part))) == NULL)
1142                                                 adios(NULL, "out of memory");
1143                                         *pp = part;
1144                                         pp = &part->mp_next;
1145                                         part->mp_part = p;
1146                                 }
1147                         }
1148                 } else {
1149                         /* we are forwarding one message */
1150                         if (get_ctinfo("message/rfc822", ct, 0) == NOTOK)
1151                                 done(1);
1152                         ct->c_type = CT_MESSAGE;
1153                         ct->c_subtype = MESSAGE_RFC822;
1154
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;
1161                 }
1162
1163                 folder_free(mp);  /* free folder/message structure */
1164                 return OK;
1165         }
1166
1167         /*
1168         ** #end
1169         */
1170         if (!mh_strcasecmp(ci->ci_type, "end")) {
1171                 free_content(ct);
1172                 *ctp = NULL;
1173                 return DONE;
1174         }
1175
1176         /*
1177         ** #begin [ alternative | parallel ]
1178         */
1179         if (!mh_strcasecmp(ci->ci_type, "begin")) {
1180                 if (!ci->ci_magic) {
1181                         vrsn = MULTI_MIXED;
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")) {
1190                         goto use_forw;
1191                 } else {
1192                         vrsn = MULTI_UNKNOWN;
1193                         cp = ci->ci_magic;
1194                 }
1195
1196                 free_ctinfo(ct);
1197                 snprintf(buffer, sizeof(buffer), "multipart/%s", cp);
1198                 if (get_ctinfo(buffer, ct, 0) == NOTOK)
1199                         done(1);
1200                 ct->c_type = CT_MULTIPART;
1201                 ct->c_subtype = vrsn;
1202
1203                 if ((m = (struct multipart *) calloc(1, sizeof(*m))) == NULL)
1204                         adios(NULL, "out of memory");
1205                 ct->c_ctparams = (void *) m;
1206
1207                 pp = &m->mp_parts;
1208                 while (fgetstr(buffer, sizeof(buffer) - 1, in)) {
1209                         struct part *part;
1210                         CT p;
1211
1212                         if (user_content(in, file, buffer, &p) == DONE) {
1213                                 if (!m->mp_parts)
1214                                         adios(NULL, "empty \"#begin ... #end\" sequence");
1215                                 return OK;
1216                         }
1217                         if (!p)
1218                                 continue;
1219
1220                         if ((part = (struct part *)
1221                                         calloc(1, sizeof(*part))) == NULL)
1222                                 adios(NULL, "out of memory");
1223                         *pp = part;
1224                         pp = &part->mp_next;
1225                         part->mp_part = p;
1226                 }
1227                 admonish(NULL, "premature end-of-file, missing #end");
1228                 return OK;
1229         }
1230
1231         /*
1232         ** Unknown directive
1233         */
1234         adios(NULL, "unknown directive \"#%s\"", ci->ci_type);
1235         return NOTOK;  /* NOT REACHED */
1236 }
1237
1238
1239 static void
1240 set_id(CT ct, int top)
1241 {
1242         char msgid[BUFSIZ];
1243         static int partno;
1244         static time_t clock = 0;
1245         static char *msgfmt;
1246
1247         if (clock == 0) {
1248                 time(&clock);
1249                 snprintf(msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
1250                                 (int) getpid(), (long) clock, LocalName());
1251                 partno = 0;
1252                 msgfmt = getcpy(msgid);
1253         }
1254         snprintf(msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
1255         ct->c_id = getcpy(msgid);
1256 }
1257
1258
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
1292 };
1293
1294
1295 /*
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.
1299 */
1300
1301 static int
1302 compose_content(CT ct)
1303 {
1304         CE ce = ct->c_cefile;
1305
1306         switch (ct->c_type) {
1307         case CT_MULTIPART:
1308         {
1309                 int partnum;
1310                 char *pp;
1311                 char partnam[BUFSIZ];
1312                 struct multipart *m = (struct multipart *) ct->c_ctparams;
1313                 struct part *part;
1314
1315                 if (ct->c_partno) {
1316                         snprintf(partnam, sizeof(partnam), "%s.",
1317                                         ct->c_partno);
1318                         pp = partnam + strlen(partnam);
1319                 } else {
1320                         pp = partnam;
1321                 }
1322
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;
1327
1328                         sprintf(pp, "%d", partnum);
1329                         p->c_partno = getcpy(partnam);
1330                         if (compose_content(p) == NOTOK)
1331                                 return NOTOK;
1332                 }
1333
1334                 /*
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.
1339                 */
1340                 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
1341                         int is934 = 1;
1342
1343                         for (part = m->mp_parts; part; part = part->mp_next) {
1344                                 CT p = part->mp_part;
1345
1346                                 if (p->c_subtype != MESSAGE_RFC822) {
1347                                         is934 = 0;
1348                                         break;
1349                                 }
1350                         }
1351                         ct->c_rfc934 = is934;
1352                         for (part = m->mp_parts; part; part = part->mp_next) {
1353                                 CT p = part->mp_part;
1354
1355                                 if ((p->c_rfc934 = is934))
1356                                         p->c_end++;
1357                         }
1358                 }
1359
1360                 if (listsw) {
1361                         ct->c_end = (partnum = strlen(prefix) + 2) + 2;
1362                         if (ct->c_rfc934)
1363                                 ct->c_end += 1;
1364
1365                         for (part = m->mp_parts; part; part = part->mp_next)
1366                                 ct->c_end += part->mp_part->c_end + partnum;
1367                 }
1368         }
1369         break;
1370
1371         case CT_MESSAGE:
1372                 /* Nothing to do for type message */
1373                 break;
1374
1375         /*
1376         ** Discrete types (text/application/audio/image/video)
1377         */
1378         default:
1379                 if (!ce->ce_file) {
1380                         pid_t child_id;
1381                         int xstdout, len, buflen;
1382                         char *bp, **ap, *cp;
1383                         char *vec[4], buffer[BUFSIZ];
1384                         FILE *out;
1385                         CI ci = &ct->c_ctinfo;
1386                         char *tfile = NULL;
1387
1388                         if (!(cp = ci->ci_magic))
1389                                 adios(NULL, "internal error(5)");
1390
1391                         tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1392                         if (tfile == NULL) {
1393                                 adios("mhbuild", "unable to create temporary file");
1394                         }
1395                         ce->ce_file = getcpy(tfile);
1396                         ce->ce_unlink = 1;
1397
1398                         xstdout = 0;
1399
1400                         /* Get buffer ready to go */
1401                         bp = buffer;
1402                         bp[0] = '\0';
1403                         buflen = sizeof(buffer);
1404
1405                         /*
1406                         ** Parse composition string into buffer
1407                         */
1408                         for ( ; *cp; cp++) {
1409                                 if (*cp == '%') {
1410                                         switch (*++cp) {
1411                                         case 'a':
1412                                         {
1413                                                 /*
1414                                                 ** insert parameters from
1415                                                 ** directive
1416                                                 */
1417                                                 char **ep;
1418                                                 char *s = "";
1419
1420                                                 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1421                                                         snprintf(bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1422                                                         len = strlen(bp);
1423                                                         bp += len;
1424                                                         buflen -= len;
1425                                                         s = " ";
1426                                                 }
1427                                         }
1428                                         break;
1429
1430                                         case 'F':
1431                                                 /* %f, and stdout is not-redirected */
1432                                                 xstdout = 1;
1433                                                 /* and fall... */
1434
1435                                         case 'f':
1436                                                 /*
1437                                                 ** insert temporary filename
1438                                                 ** where content should be
1439                                                 ** written
1440                                                 */
1441                                                 snprintf(bp, buflen, "%s", ce->ce_file);
1442                                                 break;
1443
1444                                         case 's':
1445                                                 /* insert content subtype */
1446                                                 strncpy(bp, ci->ci_subtype, buflen);
1447                                                 break;
1448
1449                                         case '%':
1450                                                 /* insert character % */
1451                                                 goto raw;
1452
1453                                         default:
1454                                                 *bp++ = *--cp;
1455                                                 *bp = '\0';
1456                                                 buflen--;
1457                                                 continue;
1458                                         }
1459                                         len = strlen(bp);
1460                                         bp += len;
1461                                         buflen -= len;
1462                                 } else {
1463 raw:
1464                                         *bp++ = *cp;
1465                                         *bp = '\0';
1466                                         buflen--;
1467                                 }
1468                         }
1469
1470                         if (verbosw)
1471                                 printf("composing content %s/%s from command\n\t%s\n", ci->ci_type, ci->ci_subtype, buffer);
1472
1473                         fflush(stdout);  /* not sure if need for -noverbose */
1474
1475                         vec[0] = "/bin/sh";
1476                         vec[1] = "-c";
1477                         vec[2] = buffer;
1478                         vec[3] = NULL;
1479
1480                         if ((out = fopen(ce->ce_file, "w")) == NULL)
1481                                 adios(ce->ce_file, "unable to open for writing");
1482
1483                         switch (child_id = fork()) {
1484                         case NOTOK:
1485                                 adios("fork", "unable to fork");
1486                                 /* NOTREACHED */
1487
1488                         case OK:
1489                                 if (!xstdout)
1490                                         dup2(fileno(out), 1);
1491                                 close(fileno(out));
1492                                 execvp("/bin/sh", vec);
1493                                 fprintf(stderr, "unable to exec ");
1494                                 perror("/bin/sh");
1495                                 _exit(-1);
1496                                 /* NOTREACHED */
1497
1498                         default:
1499                                 fclose(out);
1500                                 if (pidXwait(child_id, NULL))
1501                                         done(1);
1502                                 break;
1503                         }
1504                 }
1505
1506                 /* Check size of file */
1507                 if (listsw && ct->c_end == 0L) {
1508                         struct stat st;
1509
1510                         if (stat(ce->ce_file, &st) != NOTOK)
1511                                 ct->c_end = (long) st.st_size;
1512                 }
1513                 break;
1514         }
1515
1516         return OK;
1517 }
1518
1519
1520 /*
1521 ** Scan the content.
1522 **
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.
1526 **
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.
1531 */
1532
1533 static int
1534 scan_content(CT ct)
1535 {
1536         int len;
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;
1544         FILE *in = NULL;
1545         CE ce = ct->c_cefile;
1546
1547         /*
1548         ** handle multipart by scanning all subparts
1549         ** and then checking their encoding.
1550         */
1551         if (ct->c_type == CT_MULTIPART) {
1552                 struct multipart *m = (struct multipart *) ct->c_ctparams;
1553                 struct part *part;
1554
1555                 /* initially mark the domain of enclosing multipart as 7bit */
1556                 ct->c_encoding = CE_7BIT;
1557
1558                 for (part = m->mp_parts; part; part = part->mp_next) {
1559                         CT p = part->mp_part;
1560
1561                         if (scan_content(p) == NOTOK) {
1562                                 /* choose encoding for subpart */
1563                                 return NOTOK;
1564                         }
1565
1566                         /*
1567                         ** if necessary, enlarge encoding for enclosing
1568                         ** multipart
1569                         */
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;
1575                 }
1576
1577                 return OK;
1578         }
1579
1580         /*
1581         ** Decide what to check while scanning this content.
1582         */
1583         switch (ct->c_type) {
1584         case CT_TEXT:
1585                 check8bit = 1;
1586                 checkboundary = 1;
1587                 if (ct->c_subtype == TEXT_PLAIN) {
1588                         checkebcdic = 0;
1589                         checklinelen = 0;
1590                         checklinespace = 0;
1591                 } else {
1592                         checkebcdic = ebcdicsw;
1593                         checklinelen = 1;
1594                         checklinespace = 1;
1595                 }
1596                 break;
1597
1598         case CT_APPLICATION:
1599                 check8bit = 1;
1600                 checkebcdic = ebcdicsw;
1601                 checklinelen = 1;
1602                 checklinespace = 1;
1603                 checkboundary = 1;
1604                 break;
1605
1606         case CT_MESSAGE:
1607                 check8bit = 0;
1608                 checkebcdic = 0;
1609                 checklinelen = 0;
1610                 checklinespace = 0;
1611
1612                 /* don't check anything for message/external */
1613                 if (ct->c_subtype == MESSAGE_EXTERNAL)
1614                         checkboundary = 0;
1615                 else
1616                         checkboundary = 1;
1617                 break;
1618
1619         case CT_AUDIO:
1620         case CT_IMAGE:
1621         case CT_VIDEO:
1622                 /*
1623                 ** Don't check anything for these types,
1624                 ** since we are forcing use of base64.
1625                 */
1626                 check8bit = 0;
1627                 checkebcdic = 0;
1628                 checklinelen = 0;
1629                 checklinespace = 0;
1630                 checkboundary = 0;
1631                 break;
1632         }
1633
1634         /*
1635         ** Scan the unencoded content
1636         */
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);
1641
1642                 while (fgets(buffer, sizeof(buffer) - 1, in)) {
1643                         /*
1644                         ** Check for 8bit data.
1645                         */
1646                         if (check8bit) {
1647                                 for (cp = buffer; *cp; cp++) {
1648                                         if (!isascii(*cp)) {
1649                                                 contains8bit = 1;
1650                                                 /* no need to keep checking */
1651                                                 check8bit = 0;
1652                                         }
1653                                         /*
1654                                         ** Check if character is ebcdic-safe.
1655                                         ** We only check this if also checking
1656                                         ** for 8bit data.
1657                                         */
1658                                         if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1659                                                 ebcdicunsafe = 1;
1660                                                 /* no need to keep checking */
1661                                                 checkebcdic = 0;
1662                                         }
1663                                 }
1664                         }
1665
1666                         /*
1667                         ** Check line length.
1668                         */
1669                         if (checklinelen && (strlen(buffer) > CPERLIN + 1)) {
1670                                 linelen = 1;
1671                                 checklinelen = 0;  /* no need to keep checking */
1672                         }
1673
1674                         /*
1675                         ** Check if line ends with a space.
1676                         */
1677                         if (checklinespace &&
1678                                         (cp = buffer + strlen(buffer) - 2) >
1679                                         buffer && isspace(*cp)) {
1680                                 linespace = 1;
1681                                 /* no need to keep checking */
1682                                 checklinespace = 0;
1683                         }
1684
1685                         /*
1686                         ** Check if content contains a line that clashes
1687                         ** with our standard boundary for multipart messages.
1688                         */
1689                         if (checkboundary && buffer[0] == '-' &&
1690                                         buffer[1] == '-') {
1691                                 for (cp = buffer + strlen(buffer) - 1;
1692                                                 cp >= buffer; cp--)
1693                                         if (!isspace(*cp))
1694                                                 break;
1695                                 *++cp = '\0';
1696                                 if (strncmp(buffer + 2, prefix, len)==0 &&
1697                                                 isdigit(buffer[2 + len])) {
1698                                         boundaryclash = 1;
1699                                         /* no need to keep checking */
1700                                         checkboundary = 0;
1701                                 }
1702                         }
1703                 }
1704                 fclose(in);
1705         }
1706
1707         /*
1708         ** Decide which transfer encoding to use.
1709         */
1710         switch (ct->c_type) {
1711         case CT_TEXT:
1712                 /*
1713                 ** If the text content didn't specify a character
1714                 ** set, we need to figure out which one was used.
1715                 */
1716                 t = (struct text *) ct->c_ctparams;
1717                 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1718                         CI ci = &ct->c_ctinfo;
1719                         char **ap, **ep;
1720
1721                         for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1722                                 continue;
1723
1724                         if (contains8bit) {
1725                                 t->tx_charset = CHARSET_UNKNOWN;
1726                                 *ap = concat("charset=", write_charset_8bit(),
1727                                                 NULL);
1728                         } else {
1729                                 t->tx_charset = CHARSET_USASCII;
1730                                 *ap = getcpy("charset=us-ascii");
1731                         }
1732
1733                         cp = strchr(*ap++, '=');
1734                         *ap = NULL;
1735                         *cp++ = '\0';
1736                         *ep = cp;
1737                 }
1738
1739                 if (contains8bit || ebcdicunsafe || linelen || linespace ||
1740                                 checksw)
1741                         ct->c_encoding = CE_QUOTED;
1742                 else
1743                         ct->c_encoding = CE_7BIT;
1744                 break;
1745
1746         case CT_APPLICATION:
1747                 /* For application type, use base64, except when postscript */
1748                 if (contains8bit || ebcdicunsafe || linelen || linespace ||
1749                                 checksw)
1750                         ct->c_encoding = (ct->c_subtype ==
1751                                         APPLICATION_POSTSCRIPT) ?
1752                                         CE_QUOTED : CE_BASE64;
1753                 else
1754                         ct->c_encoding = CE_7BIT;
1755                 break;
1756
1757         case CT_MESSAGE:
1758                 ct->c_encoding = CE_7BIT;
1759                 break;
1760
1761         case CT_AUDIO:
1762         case CT_IMAGE:
1763         case CT_VIDEO:
1764                 /* For audio, image, and video contents, just use base64 */
1765                 ct->c_encoding = CE_BASE64;
1766                 break;
1767         }
1768
1769         return (boundaryclash ? NOTOK : OK);
1770 }
1771
1772
1773 /*
1774 ** Scan the content structures, and build header
1775 ** fields that will need to be output into the
1776 ** message.
1777 */
1778
1779 static int
1780 build_headers(CT ct)
1781 {
1782         int cc, mailbody, len;
1783         char **ap, **ep;
1784         char *np, *vp, buffer[BUFSIZ];
1785         CI ci = &ct->c_ctinfo;
1786
1787         /*
1788         ** If message is type multipart, then add the multipart
1789         ** boundary to the list of attribute/value pairs.
1790         */
1791         if (ct->c_type == CT_MULTIPART) {
1792                 char *cp;
1793                 static int level = 0;  /* store nesting level */
1794
1795                 ap = ci->ci_attrs;
1796                 ep = ci->ci_values;
1797                 snprintf(buffer, sizeof(buffer), "boundary=%s%d",
1798                                 prefix, level++);
1799                 cp = strchr(*ap++ = getcpy(buffer), '=');
1800                 *ap = NULL;
1801                 *cp++ = '\0';
1802                 *ep = cp;
1803         }
1804
1805         /*
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).
1811         */
1812         if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1813                 goto skip_headers;
1814
1815         /*
1816         ** output the content type and subtype
1817         */
1818         np = getcpy(TYPE_FIELD);
1819         vp = concat(" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1820
1821         /* keep track of length of line */
1822         len = strlen(TYPE_FIELD) + strlen(ci->ci_type) +
1823                         strlen(ci->ci_subtype) + 3;
1824
1825         mailbody = ct->c_type == CT_MESSAGE &&
1826                         ct->c_subtype == MESSAGE_EXTERNAL &&
1827                         ((struct exbody *) ct->c_ctparams)->eb_body;
1828
1829         /*
1830         ** Append the attribute/value pairs to
1831         ** the end of the Content-Type line.
1832         */
1833         for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1834                 if (mailbody && !mh_strcasecmp(*ap, "body"))
1835                         continue;
1836
1837                 vp = add(";", vp);
1838                 len++;
1839
1840                 snprintf(buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1841                 if (len + 1 + (cc = strlen(buffer)) >= CPERLIN) {
1842                         vp = add("\n\t", vp);
1843                         len = 8;
1844                 } else {
1845                         vp = add(" ", vp);
1846                         len++;
1847                 }
1848                 vp = add(buffer, vp);
1849                 len += cc;
1850         }
1851
1852         /*
1853         ** Append any RFC-822 comment to the end of
1854         ** the Content-Type line.
1855         */
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);
1860                         len = 8;
1861                 } else {
1862                         vp = add(" ", vp);
1863                         len++;
1864                 }
1865                 vp = add(buffer, vp);
1866                 len += cc;
1867         }
1868         vp = add("\n", vp);
1869         add_header(ct, np, vp);
1870
1871         /*
1872         ** output the Content-ID, unless disabled by -nocontentid
1873         */
1874         if (contentidsw && ct->c_id) {
1875                 np = getcpy(ID_FIELD);
1876                 vp = concat(" ", ct->c_id, NULL);
1877                 add_header(ct, np, vp);
1878         }
1879
1880         /*
1881         ** output the Content-Description
1882         */
1883         if (ct->c_descr) {
1884                 np = getcpy(DESCR_FIELD);
1885                 vp = concat(" ", ct->c_descr, NULL);
1886                 add_header(ct, np, vp);
1887         }
1888
1889         /*
1890         ** output the Content-Disposition
1891         */
1892         if (ct->c_dispo) {
1893                 np = getcpy(DISPO_FIELD);
1894                 vp = concat(" ", ct->c_dispo, NULL);
1895                 add_header(ct, np, vp);
1896         }
1897
1898 skip_headers:
1899         /*
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).
1903         */
1904         if (ct->c_ctexbody)
1905                 return OK;
1906
1907         /*
1908         ** output the Content-MD5
1909         */
1910         if (checksw) {
1911                 np = getcpy(MD5_FIELD);
1912                 vp = calculate_digest(ct, (ct->c_encoding == CE_QUOTED) ?
1913                                 1 : 0);
1914                 add_header(ct, np, vp);
1915         }
1916
1917         /*
1918         ** output the Content-Transfer-Encoding
1919         */
1920         switch (ct->c_encoding) {
1921         case CE_7BIT:
1922                 /* Nothing to output */
1923                 break;
1924
1925         case CE_8BIT:
1926                 if (ct->c_type == CT_MESSAGE)
1927                         adios(NULL, "internal error, invalid encoding");
1928
1929                 np = getcpy(ENCODING_FIELD);
1930                 vp = concat(" ", "8bit", "\n", NULL);
1931                 add_header(ct, np, vp);
1932                 break;
1933
1934         case CE_QUOTED:
1935                 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1936                         adios(NULL, "internal error, invalid encoding");
1937
1938                 np = getcpy(ENCODING_FIELD);
1939                 vp = concat(" ", "quoted-printable", "\n", NULL);
1940                 add_header(ct, np, vp);
1941                 break;
1942
1943         case CE_BASE64:
1944                 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1945                         adios(NULL, "internal error, invalid encoding");
1946
1947                 np = getcpy(ENCODING_FIELD);
1948                 vp = concat(" ", "base64", "\n", NULL);
1949                 add_header(ct, np, vp);
1950                 break;
1951
1952         case CE_BINARY:
1953                 if (ct->c_type == CT_MESSAGE)
1954                         adios(NULL, "internal error, invalid encoding");
1955
1956                 np = getcpy(ENCODING_FIELD);
1957                 vp = concat(" ", "binary", "\n", NULL);
1958                 add_header(ct, np, vp);
1959                 break;
1960
1961         default:
1962                 adios(NULL, "unknown transfer encoding in content");
1963                 break;
1964         }
1965
1966         /*
1967         ** Additional content specific header processing
1968         */
1969         switch (ct->c_type) {
1970         case CT_MULTIPART:
1971         {
1972                 struct multipart *m;
1973                 struct part *part;
1974
1975                 m = (struct multipart *) ct->c_ctparams;
1976                 for (part = m->mp_parts; part; part = part->mp_next) {
1977                         CT p;
1978
1979                         p = part->mp_part;
1980                         build_headers(p);
1981                 }
1982         }
1983                 break;
1984
1985         case CT_MESSAGE:
1986                 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1987                         struct exbody *e;
1988
1989                         e = (struct exbody *) ct->c_ctparams;
1990                         build_headers(e->eb_content);
1991                 }
1992                 break;
1993
1994         default:
1995                 /* Nothing to do */
1996                 break;
1997         }
1998
1999         return OK;
2000 }
2001
2002
2003 static char nib2b64[0x40+1] =
2004         "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
2005
2006 static char *
2007 calculate_digest(CT ct, int asciiP)
2008 {
2009         int cc;
2010         char buffer[BUFSIZ], *vp, *op;
2011         unsigned char *dp;
2012         unsigned char digest[16];
2013         unsigned char outbuf[25];
2014         FILE *in;
2015         MD5_CTX mdContext;
2016         CE ce = ct->c_cefile;
2017
2018         /* open content */
2019         if ((in = fopen(ce->ce_file, "r")) == NULL)
2020                 adios(ce->ce_file, "unable to open for reading");
2021
2022         /* Initialize md5 context */
2023         MD5Init(&mdContext);
2024
2025         /* calculate md5 message digest */
2026         if (asciiP) {
2027                 while (fgets(buffer, sizeof(buffer) - 1, in)) {
2028                         char c, *cp;
2029
2030                         cp = buffer + strlen(buffer) - 1;
2031                         if ((c = *cp) == '\n')
2032                                 *cp = '\0';
2033
2034                         MD5Update(&mdContext, (unsigned char *) buffer,
2035                                         (unsigned int) strlen(buffer));
2036
2037                         if (c == '\n')
2038                                 MD5Update(&mdContext, (unsigned char *) "\r\n",
2039                                                  2);
2040                 }
2041         } else {
2042                 while ((cc = fread(buffer, sizeof(*buffer), sizeof(buffer),
2043                                 in)) > 0)
2044                         MD5Update(&mdContext, (unsigned char *) buffer,
2045                                         (unsigned int) cc);
2046         }
2047
2048         /* md5 finalization.  Write digest and zero md5 context */
2049         MD5Final(digest, &mdContext);
2050
2051         /* close content */
2052         fclose(in);
2053
2054         /* print debugging info */
2055         if (debugsw) {
2056                 unsigned char *ep;
2057
2058                 fprintf(stderr, "MD5 digest=");
2059                 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
2060                         dp < ep; dp++)
2061                         fprintf(stderr, "%02x", *dp & 0xff);
2062                 fprintf(stderr, "\n");
2063         }
2064
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) {
2068                 unsigned long bits;
2069                 char *bp;
2070
2071                 bits = (*dp++ & 0xff) << 16;
2072                 if (cc > 1) {
2073                         bits |= (*dp++ & 0xff) << 8;
2074                         if (cc > 2)
2075                                 bits |= *dp++ & 0xff;
2076                 }
2077
2078                 for (bp = op + 4; bp > op; bits >>= 6)
2079                         *--bp = nib2b64[bits & 0x3f];
2080                 if (cc < 3) {
2081                         *(op + 3) = '=';
2082                         if (cc < 2)
2083                                 *(op + 2) = '=';
2084                 }
2085         }
2086
2087         /* null terminate string */
2088         outbuf[24] = '\0';
2089
2090         /* now make copy and return string */
2091         vp = concat(" ", outbuf, "\n", NULL);
2092         return vp;
2093 }