]> diplodocus.org Git - nmh/blob - uip/mhbuildsbr.c
Add support for a -nosasl switch.
[nmh] / uip / mhbuildsbr.c
1
2 /*
3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
4 *
5 * This code is Copyright (c) 2002, by the authors of nmh. See the
6 * COPYRIGHT file in the root directory of the nmh distribution for
7 * complete copyright information.
8 */
9
10 /*
11 * This code was originally part of mhn.c. I split it into
12 * a separate program (mhbuild.c) and then later split it
13 * again (mhbuildsbr.c). But the code still has some of
14 * the mhn.c code in it. This program needs additional
15 * streamlining and removal of unneeded code.
16 */
17
18 #include <h/mh.h>
19 #include <fcntl.h>
20 #include <h/signals.h>
21 #include <h/md5.h>
22 #include <errno.h>
23 #include <signal.h>
24 #include <h/mts.h>
25 #include <h/tws.h>
26 #include <h/mime.h>
27 #include <h/mhparse.h>
28 #include <h/utils.h>
29
30 #ifdef HAVE_SYS_TIME_H
31 # include <sys/time.h>
32 #endif
33 #include <time.h>
34
35
36 extern int debugsw;
37 extern int verbosw;
38
39 extern int ebcdicsw;
40 extern int listsw;
41 extern int rfc934sw;
42 extern int contentidsw;
43
44 /* cache policies */
45 extern int rcachesw; /* mhcachesbr.c */
46 extern int wcachesw; /* mhcachesbr.c */
47
48 /*
49 * Directory to place tmp files. This must
50 * be set before these routines are called.
51 */
52 char *tmp;
53
54 pid_t xpid = 0;
55
56 static char prefix[] = "----- =_aaaaaaaaaa";
57
58
59 /* mhmisc.c */
60 void content_error (char *, CT, char *, ...);
61
62 /* mhcachesbr.c */
63 int find_cache (CT, int, int *, char *, char *, int);
64
65 /* mhfree.c */
66 void free_content (CT);
67 void free_ctinfo (CT);
68 void free_encoding (CT, int);
69
70 /*
71 * prototypes
72 */
73 CT build_mime (char *, int);
74
75 /*
76 * static prototypes
77 */
78 static int init_decoded_content (CT);
79 static char *fgetstr (char *, int, FILE *);
80 static int user_content (FILE *, char *, char *, CT *);
81 static void set_id (CT, int);
82 static int compose_content (CT);
83 static int scan_content (CT);
84 static int build_headers (CT);
85 static char *calculate_digest (CT, int);
86
87
88 static unsigned char directives_stack[32];
89 static unsigned int directives_index;
90
91 static int do_direct(void)
92 {
93 return directives_stack[directives_index];
94 }
95
96 static void directive_onoff(int onoff)
97 {
98 if (directives_index >= sizeof(directives_stack) - 1) {
99 fprintf(stderr, "mhbuild: #on/off overflow, continuing\n");
100 return;
101 }
102 directives_stack[++directives_index] = onoff;
103 }
104
105 static void directive_init(int onoff)
106 {
107 directives_index = 0;
108 directives_stack[0] = onoff;
109 }
110
111 static void directive_pop(void)
112 {
113 if (directives_index > 0)
114 directives_index--;
115 else
116 fprintf(stderr, "mhbuild: #pop underflow, continuing\n");
117 }
118
119 /*
120 * Main routine for translating composition file
121 * into valid MIME message. It translates the draft
122 * into a content structure (actually a tree of content
123 * structures). This message then can be manipulated
124 * in various ways, including being output via
125 * output_message().
126 */
127
128 CT
129 build_mime (char *infile, int directives)
130 {
131 int compnum, state;
132 char buf[BUFSIZ], name[NAMESZ];
133 char *cp, *np, *vp;
134 struct multipart *m;
135 struct part **pp;
136 CT ct;
137 FILE *in;
138
139 directive_init(directives);
140
141 umask (~m_gmprot ());
142
143 /* open the composition draft */
144 if ((in = fopen (infile, "r")) == NULL)
145 adios (infile, "unable to open for reading");
146
147 /*
148 * Allocate space for primary (outside) content
149 */
150 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
151 adios (NULL, "out of memory");
152
153 /*
154 * Allocate structure for handling decoded content
155 * for this part. We don't really need this, but
156 * allocate it to remain consistent.
157 */
158 init_decoded_content (ct);
159
160 /*
161 * Parse some of the header fields in the composition
162 * draft into the linked list of header fields for
163 * the new MIME message.
164 */
165 for (compnum = 1, state = FLD;;) {
166 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
167 case FLD:
168 case FLDPLUS:
169 case FLDEOF:
170 compnum++;
171
172 /* abort if draft has Mime-Version header field */
173 if (!mh_strcasecmp (name, VRSN_FIELD))
174 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
175
176 /* abort if draft has Content-Transfer-Encoding header field */
177 if (!mh_strcasecmp (name, ENCODING_FIELD))
178 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
179
180 /* ignore any Content-Type fields in the header */
181 if (!mh_strcasecmp (name, TYPE_FIELD)) {
182 while (state == FLDPLUS)
183 state = m_getfld (state, name, buf, sizeof(buf), in);
184 goto finish_field;
185 }
186
187 /* get copies of the buffers */
188 np = add (name, NULL);
189 vp = add (buf, NULL);
190
191 /* if necessary, get rest of field */
192 while (state == FLDPLUS) {
193 state = m_getfld (state, name, buf, sizeof(buf), in);
194 vp = add (buf, vp); /* add to previous value */
195 }
196
197 /* Now add the header data to the list */
198 add_header (ct, np, vp);
199
200 finish_field:
201 /* if this wasn't the last header field, then continue */
202 if (state != FLDEOF)
203 continue;
204 /* else fall... */
205
206 case FILEEOF:
207 adios (NULL, "draft has empty body -- no directives!");
208 /* NOTREACHED */
209
210 case BODY:
211 case BODYEOF:
212 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
213 break;
214
215 case LENERR:
216 case FMTERR:
217 adios (NULL, "message format error in component #%d", compnum);
218
219 default:
220 adios (NULL, "getfld() returned %d", state);
221 }
222 break;
223 }
224
225 /*
226 * Now add the MIME-Version header field
227 * to the list of header fields.
228 */
229 np = add (VRSN_FIELD, NULL);
230 vp = concat (" ", VRSN_VALUE, "\n", NULL);
231 add_header (ct, np, vp);
232
233 /*
234 * We initally assume we will find multiple contents in the
235 * draft. So create a multipart/mixed content to hold everything.
236 * We can remove this later, if it is not needed.
237 */
238 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
239 done (1);
240 ct->c_type = CT_MULTIPART;
241 ct->c_subtype = MULTI_MIXED;
242 ct->c_file = add (infile, NULL);
243
244 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
245 adios (NULL, "out of memory");
246 ct->c_ctparams = (void *) m;
247 pp = &m->mp_parts;
248
249 /*
250 * read and parse the composition file
251 * and the directives it contains.
252 */
253 while (fgetstr (buf, sizeof(buf) - 1, in)) {
254 struct part *part;
255 CT p;
256
257 if (user_content (in, infile, buf, &p) == DONE) {
258 admonish (NULL, "ignoring spurious #end");
259 continue;
260 }
261 if (!p)
262 continue;
263
264 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
265 adios (NULL, "out of memory");
266 *pp = part;
267 pp = &part->mp_next;
268 part->mp_part = p;
269 }
270
271 /*
272 * close the composition draft since
273 * it's not needed any longer.
274 */
275 fclose (in);
276
277 /* check if any contents were found */
278 if (!m->mp_parts)
279 adios (NULL, "no content directives found");
280
281 /*
282 * If only one content was found, then remove and
283 * free the outer multipart content.
284 */
285 if (!m->mp_parts->mp_next) {
286 CT p;
287
288 p = m->mp_parts->mp_part;
289 m->mp_parts->mp_part = NULL;
290
291 /* move header fields */
292 p->c_first_hf = ct->c_first_hf;
293 p->c_last_hf = ct->c_last_hf;
294 ct->c_first_hf = NULL;
295 ct->c_last_hf = NULL;
296
297 free_content (ct);
298 ct = p;
299 } else {
300 set_id (ct, 1);
301 }
302
303 /*
304 * Fill out, or expand directives. Parse and execute
305 * commands specified by profile composition strings.
306 */
307 compose_content (ct);
308
309 if ((cp = strchr(prefix, 'a')) == NULL)
310 adios (NULL, "internal error(4)");
311
312 /*
313 * Scan the contents. Choose a transfer encoding, and
314 * check if prefix for multipart boundary clashes with
315 * any of the contents.
316 */
317 while (scan_content (ct) == NOTOK) {
318 if (*cp < 'z') {
319 (*cp)++;
320 } else {
321 if (*++cp == 0)
322 adios (NULL, "giving up trying to find a unique delimiter string");
323 else
324 (*cp)++;
325 }
326 }
327
328 /* Build the rest of the header field structures */
329 build_headers (ct);
330
331 return ct;
332 }
333
334
335 /*
336 * Set up structures for placing unencoded
337 * content when building parts.
338 */
339
340 static int
341 init_decoded_content (CT ct)
342 {
343 CE ce;
344
345 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
346 adios (NULL, "out of memory");
347
348 ct->c_cefile = ce;
349 ct->c_ceopenfnx = open7Bit; /* since unencoded */
350 ct->c_ceclosefnx = close_encoding;
351 ct->c_cesizefnx = NULL; /* since unencoded */
352
353 return OK;
354 }
355
356
357 static char *
358 fgetstr (char *s, int n, FILE *stream)
359 {
360 char *cp, *ep;
361 int o_n = n;
362
363 while(1) {
364 for (ep = (cp = s) + o_n; cp < ep; ) {
365 int i;
366
367 if (!fgets (cp, n, stream))
368 return (cp != s ? s : NULL);
369
370 if (cp == s && *cp != '#')
371 return s;
372
373 cp += (i = strlen (cp)) - 1;
374 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
375 break;
376 *cp = '\0';
377 n -= (i - 2);
378 }
379
380 if (strcmp(s, "#on\n") == 0) {
381 directive_onoff(1);
382 } else if (strcmp(s, "#off\n") == 0) {
383 directive_onoff(0);
384 } else if (strcmp(s, "#pop\n") == 0) {
385 directive_pop();
386 } else {
387 break;
388 }
389 }
390
391 return s;
392 }
393
394
395 /*
396 * Parse the composition draft for text and directives.
397 * Do initial setup of Content structure.
398 */
399
400 static int
401 user_content (FILE *in, char *file, char *buf, CT *ctp)
402 {
403 int extrnal, vrsn;
404 unsigned char *cp;
405 char **ap;
406 char buffer[BUFSIZ];
407 struct multipart *m;
408 struct part **pp;
409 struct stat st;
410 struct str2init *s2i;
411 CI ci;
412 CT ct;
413 CE ce;
414
415 if (buf[0] == '\n' || (do_direct() && strcmp (buf, "#\n") == 0)) {
416 *ctp = NULL;
417 return OK;
418 }
419
420 /* allocate basic Content structure */
421 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
422 adios (NULL, "out of memory");
423 *ctp = ct;
424
425 /* allocate basic structure for handling decoded content */
426 init_decoded_content (ct);
427 ce = ct->c_cefile;
428
429 ci = &ct->c_ctinfo;
430 set_id (ct, 0);
431
432 /*
433 * Handle inline text. Check if line
434 * is one of the following forms:
435 *
436 * 1) doesn't begin with '#' (implicit directive)
437 * 2) begins with "##" (implicit directive)
438 * 3) begins with "#<"
439 */
440 if (!do_direct() || buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
441 int headers;
442 int inlineD;
443 long pos;
444 char content[BUFSIZ];
445 FILE *out;
446 char *cp;
447
448 cp = m_mktemp2(NULL, invo_name, NULL, &out);
449 if (cp == NULL) adios("mhbuildsbr", "unable to create temporary file");
450
451 /* use a temp file to collect the plain text lines */
452 ce->ce_file = add (cp, NULL);
453 ce->ce_unlink = 1;
454
455 if (do_direct() && (buf[0] == '#' && buf[1] == '<')) {
456 strncpy (content, buf + 2, sizeof(content));
457 inlineD = 1;
458 goto rock_and_roll;
459 } else {
460 inlineD = 0;
461 }
462
463 /* the directive is implicit */
464 strncpy (content, "text/plain", sizeof(content));
465 headers = 0;
466 strncpy (buffer, (!do_direct() || buf[0] != '#') ? buf : buf + 1, sizeof(buffer));
467 for (;;) {
468 int i;
469
470 if (headers >= 0 && do_direct() && uprf (buffer, DESCR_FIELD)
471 && buffer[i = strlen (DESCR_FIELD)] == ':') {
472 headers = 1;
473
474 again_descr:
475 ct->c_descr = add (buffer + i + 1, ct->c_descr);
476 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
477 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
478 switch (buffer[0]) {
479 case ' ':
480 case '\t':
481 i = -1;
482 goto again_descr;
483
484 case '#':
485 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
486 /* NOTREACHED */
487
488 default:
489 break;
490 }
491 }
492
493 if (headers >= 0 && do_direct() && uprf (buffer, DISPO_FIELD)
494 && buffer[i = strlen (DISPO_FIELD)] == ':') {
495 headers = 1;
496
497 again_dispo:
498 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
499 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
500 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
501 switch (buffer[0]) {
502 case ' ':
503 case '\t':
504 i = -1;
505 goto again_dispo;
506
507 case '#':
508 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
509 /* NOTREACHED */
510
511 default:
512 break;
513 }
514 }
515
516 if (headers != 1 || buffer[0] != '\n')
517 fputs (buffer, out);
518
519 rock_and_roll:
520 headers = -1;
521 pos = ftell (in);
522 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
523 break;
524 if (do_direct() && buffer[0] == '#') {
525 char *bp;
526
527 if (buffer[1] != '#')
528 break;
529 for (cp = (bp = buffer) + 1; *cp; cp++)
530 *bp++ = *cp;
531 *bp = '\0';
532 }
533 }
534
535 if (listsw)
536 ct->c_end = ftell (out);
537 fclose (out);
538
539 /* parse content type */
540 if (get_ctinfo (content, ct, inlineD) == NOTOK)
541 done (1);
542
543 for (s2i = str2cts; s2i->si_key; s2i++)
544 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
545 break;
546 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
547 s2i++;
548
549 /*
550 * check type specified (possibly implicitly)
551 */
552 switch (ct->c_type = s2i->si_val) {
553 case CT_MESSAGE:
554 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
555 ct->c_encoding = CE_7BIT;
556 goto call_init;
557 }
558 /* else fall... */
559 case CT_MULTIPART:
560 adios (NULL, "it doesn't make sense to define an in-line %s content",
561 ct->c_type == CT_MESSAGE ? "message" : "multipart");
562 /* NOTREACHED */
563
564 default:
565 call_init:
566 if ((ct->c_ctinitfnx = s2i->si_init))
567 (*ct->c_ctinitfnx) (ct);
568 break;
569 }
570
571 if (cp)
572 fseek (in, pos, SEEK_SET);
573 return OK;
574 }
575
576 /*
577 * If we've reached this point, the next line
578 * must be some type of explicit directive.
579 */
580
581 /* check if directive is external-type */
582 extrnal = (buf[1] == '@');
583
584 /* parse directive */
585 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
586 done (1);
587
588 /* check directive against the list of MIME types */
589 for (s2i = str2cts; s2i->si_key; s2i++)
590 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
591 break;
592
593 /*
594 * Check if the directive specified a valid type.
595 * This will happen if it was one of the following forms:
596 *
597 * #type/subtype (or)
598 * #@type/subtype
599 */
600 if (s2i->si_key) {
601 if (!ci->ci_subtype)
602 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
603
604 switch (ct->c_type = s2i->si_val) {
605 case CT_MULTIPART:
606 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
607 ci->ci_type, ci->ci_subtype);
608 /* NOTREACHED */
609
610 case CT_MESSAGE:
611 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
612 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
613 ci->ci_type, ci->ci_subtype);
614 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
615 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
616 ci->ci_type, ci->ci_subtype);
617 use_forw:
618 adios (NULL,
619 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
620 ci->ci_type, ci->ci_subtype);
621 /* NOTREACHED */
622
623 default:
624 if ((ct->c_ctinitfnx = s2i->si_init))
625 (*ct->c_ctinitfnx) (ct);
626 break;
627 }
628
629 /*
630 * #@type/subtype (external types directive)
631 */
632 if (extrnal) {
633 struct exbody *e;
634 CT p;
635
636 if (!ci->ci_magic)
637 adios (NULL, "need external information for \"#@%s/%s\"",
638 ci->ci_type, ci->ci_subtype);
639 p = ct;
640
641 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
642 free (ci->ci_magic);
643 ci->ci_magic = NULL;
644
645 /*
646 * Since we are using the current Content structure to
647 * hold information about the type of the external
648 * reference, we need to create another Content structure
649 * for the message/external-body to wrap it in.
650 */
651 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
652 adios (NULL, "out of memory");
653 *ctp = ct;
654 ci = &ct->c_ctinfo;
655 if (get_ctinfo (buffer, ct, 0) == NOTOK)
656 done (1);
657 ct->c_type = CT_MESSAGE;
658 ct->c_subtype = MESSAGE_EXTERNAL;
659
660 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
661 adios (NULL, "out of memory");
662 ct->c_ctparams = (void *) e;
663
664 e->eb_parent = ct;
665 e->eb_content = p;
666 p->c_ctexbody = e;
667
668 if (params_external (ct, 1) == NOTOK)
669 done (1);
670
671 return OK;
672 }
673
674 /* Handle [file] argument */
675 if (ci->ci_magic) {
676 /* check if specifies command to execute */
677 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
678 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
679 continue;
680 if (!*cp)
681 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
682 cp = add (cp, NULL);
683 free (ci->ci_magic);
684 ci->ci_magic = cp;
685 } else {
686 /* record filename of decoded contents */
687 ce->ce_file = ci->ci_magic;
688 if (access (ce->ce_file, R_OK) == NOTOK)
689 adios ("reading", "unable to access %s for", ce->ce_file);
690 if (listsw && stat (ce->ce_file, &st) != NOTOK)
691 ct->c_end = (long) st.st_size;
692 ci->ci_magic = NULL;
693 }
694 return OK;
695 }
696
697 /*
698 * No [file] argument, so check profile for
699 * method to compose content.
700 */
701 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
702 invo_name, ci->ci_type, ci->ci_subtype);
703 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
704 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
705 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
706 content_error (NULL, ct, "don't know how to compose content");
707 done (1);
708 }
709 }
710 ci->ci_magic = add (cp, NULL);
711 return OK;
712 }
713
714 if (extrnal)
715 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
716
717 /*
718 * Message directive
719 * #forw [+folder] [msgs]
720 */
721 if (!mh_strcasecmp (ci->ci_type, "forw")) {
722 int msgnum;
723 char *folder, *arguments[MAXARGS];
724 struct msgs *mp;
725
726 if (ci->ci_magic) {
727 ap = brkstring (ci->ci_magic, " ", "\n");
728 copyip (ap, arguments, MAXARGS);
729 } else {
730 arguments[0] = "cur";
731 arguments[1] = NULL;
732 }
733 folder = NULL;
734
735 /* search the arguments for a folder name */
736 for (ap = arguments; *ap; ap++) {
737 cp = *ap;
738 if (*cp == '+' || *cp == '@') {
739 if (folder)
740 adios (NULL, "only one folder per #forw directive");
741 else
742 folder = pluspath (cp);
743 }
744 }
745
746 /* else, use the current folder */
747 if (!folder)
748 folder = add (getfolder (1), NULL);
749
750 if (!(mp = folder_read (folder)))
751 adios (NULL, "unable to read folder %s", folder);
752 for (ap = arguments; *ap; ap++) {
753 cp = *ap;
754 if (*cp != '+' && *cp != '@')
755 if (!m_convert (mp, cp))
756 done (1);
757 }
758 free (folder);
759 free_ctinfo (ct);
760
761 /*
762 * If there is more than one message to include, make this
763 * a content of type "multipart/digest" and insert each message
764 * as a subpart. If there is only one message, then make this
765 * a content of type "message/rfc822".
766 */
767 if (mp->numsel > 1) {
768 /* we are forwarding multiple messages */
769 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
770 done (1);
771 ct->c_type = CT_MULTIPART;
772 ct->c_subtype = MULTI_DIGEST;
773
774 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
775 adios (NULL, "out of memory");
776 ct->c_ctparams = (void *) m;
777 pp = &m->mp_parts;
778
779 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
780 if (is_selected(mp, msgnum)) {
781 struct part *part;
782 CT p;
783 CE pe;
784
785 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
786 adios (NULL, "out of memory");
787 init_decoded_content (p);
788 pe = p->c_cefile;
789 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
790 done (1);
791 p->c_type = CT_MESSAGE;
792 p->c_subtype = MESSAGE_RFC822;
793
794 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
795 pe->ce_file = add (buffer, NULL);
796 if (listsw && stat (pe->ce_file, &st) != NOTOK)
797 p->c_end = (long) st.st_size;
798
799 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
800 adios (NULL, "out of memory");
801 *pp = part;
802 pp = &part->mp_next;
803 part->mp_part = p;
804 }
805 }
806 } else {
807 /* we are forwarding one message */
808 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
809 done (1);
810 ct->c_type = CT_MESSAGE;
811 ct->c_subtype = MESSAGE_RFC822;
812
813 msgnum = mp->lowsel;
814 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
815 ce->ce_file = add (buffer, NULL);
816 if (listsw && stat (ce->ce_file, &st) != NOTOK)
817 ct->c_end = (long) st.st_size;
818 }
819
820 folder_free (mp); /* free folder/message structure */
821 return OK;
822 }
823
824 /*
825 * #end
826 */
827 if (!mh_strcasecmp (ci->ci_type, "end")) {
828 free_content (ct);
829 *ctp = NULL;
830 return DONE;
831 }
832
833 /*
834 * #begin [ alternative | parallel ]
835 */
836 if (!mh_strcasecmp (ci->ci_type, "begin")) {
837 if (!ci->ci_magic) {
838 vrsn = MULTI_MIXED;
839 cp = SubMultiPart[vrsn - 1].kv_key;
840 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
841 vrsn = MULTI_ALTERNATE;
842 cp = SubMultiPart[vrsn - 1].kv_key;
843 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
844 vrsn = MULTI_PARALLEL;
845 cp = SubMultiPart[vrsn - 1].kv_key;
846 } else if (uprf (ci->ci_magic, "digest")) {
847 goto use_forw;
848 } else {
849 vrsn = MULTI_UNKNOWN;
850 cp = ci->ci_magic;
851 }
852
853 free_ctinfo (ct);
854 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
855 if (get_ctinfo (buffer, ct, 0) == NOTOK)
856 done (1);
857 ct->c_type = CT_MULTIPART;
858 ct->c_subtype = vrsn;
859
860 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
861 adios (NULL, "out of memory");
862 ct->c_ctparams = (void *) m;
863
864 pp = &m->mp_parts;
865 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
866 struct part *part;
867 CT p;
868
869 if (user_content (in, file, buffer, &p) == DONE) {
870 if (!m->mp_parts)
871 adios (NULL, "empty \"#begin ... #end\" sequence");
872 return OK;
873 }
874 if (!p)
875 continue;
876
877 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
878 adios (NULL, "out of memory");
879 *pp = part;
880 pp = &part->mp_next;
881 part->mp_part = p;
882 }
883 admonish (NULL, "premature end-of-file, missing #end");
884 return OK;
885 }
886
887 /*
888 * Unknown directive
889 */
890 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
891 return NOTOK; /* NOT REACHED */
892 }
893
894
895 static void
896 set_id (CT ct, int top)
897 {
898 char contentid[BUFSIZ];
899 static int partno;
900 static time_t clock = 0;
901 static char *msgfmt;
902
903 if (clock == 0) {
904 time (&clock);
905 snprintf (contentid, sizeof(contentid), "%s\n", message_id (clock, 1));
906 partno = 0;
907 msgfmt = getcpy(contentid);
908 }
909 snprintf (contentid, sizeof(contentid), msgfmt, top ? 0 : ++partno);
910 ct->c_id = getcpy (contentid);
911 }
912
913
914 static char ebcdicsafe[0x100] = {
915 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
916 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
917 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
918 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
919 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
920 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
921 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
922 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
923 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
924 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
925 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
926 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
927 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
928 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
929 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
930 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
931 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
932 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
933 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
934 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
935 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
936 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
937 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
938 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
939 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
940 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
941 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
942 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
943 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
944 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
945 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
946 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
947 };
948
949
950 /*
951 * Fill out, or expand the various contents in the composition
952 * draft. Read-in any necessary files. Parse and execute any
953 * commands specified by profile composition strings.
954 */
955
956 static int
957 compose_content (CT ct)
958 {
959 CE ce = ct->c_cefile;
960
961 switch (ct->c_type) {
962 case CT_MULTIPART:
963 {
964 int partnum;
965 char *pp;
966 char partnam[BUFSIZ];
967 struct multipart *m = (struct multipart *) ct->c_ctparams;
968 struct part *part;
969
970 if (ct->c_partno) {
971 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
972 pp = partnam + strlen (partnam);
973 } else {
974 pp = partnam;
975 }
976
977 /* first, we call compose_content on all the subparts */
978 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
979 CT p = part->mp_part;
980
981 sprintf (pp, "%d", partnum);
982 p->c_partno = add (partnam, NULL);
983 if (compose_content (p) == NOTOK)
984 return NOTOK;
985 }
986
987 /*
988 * If the -rfc934mode switch is given, then check all
989 * the subparts of a multipart/digest. If they are all
990 * message/rfc822, then mark this content and all
991 * subparts with the rfc934 compatibility mode flag.
992 */
993 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
994 int is934 = 1;
995
996 for (part = m->mp_parts; part; part = part->mp_next) {
997 CT p = part->mp_part;
998
999 if (p->c_subtype != MESSAGE_RFC822) {
1000 is934 = 0;
1001 break;
1002 }
1003 }
1004 ct->c_rfc934 = is934;
1005 for (part = m->mp_parts; part; part = part->mp_next) {
1006 CT p = part->mp_part;
1007
1008 if ((p->c_rfc934 = is934))
1009 p->c_end++;
1010 }
1011 }
1012
1013 if (listsw) {
1014 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
1015 if (ct->c_rfc934)
1016 ct->c_end += 1;
1017
1018 for (part = m->mp_parts; part; part = part->mp_next)
1019 ct->c_end += part->mp_part->c_end + partnum;
1020 }
1021 }
1022 break;
1023
1024 case CT_MESSAGE:
1025 /* Nothing to do for type message */
1026 break;
1027
1028 /*
1029 * Discrete types (text/application/audio/image/video)
1030 */
1031 default:
1032 if (!ce->ce_file) {
1033 pid_t child_id;
1034 int i, xstdout, len, buflen;
1035 char *bp, **ap, *cp;
1036 char *vec[4], buffer[BUFSIZ];
1037 FILE *out;
1038 CI ci = &ct->c_ctinfo;
1039 char *tfile = NULL;
1040
1041 if (!(cp = ci->ci_magic))
1042 adios (NULL, "internal error(5)");
1043
1044 tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
1045 if (tfile == NULL) {
1046 adios("mhbuildsbr", "unable to create temporary file");
1047 }
1048 ce->ce_file = add (tfile, NULL);
1049 ce->ce_unlink = 1;
1050
1051 xstdout = 0;
1052
1053 /* Get buffer ready to go */
1054 bp = buffer;
1055 bp[0] = '\0';
1056 buflen = sizeof(buffer);
1057
1058 /*
1059 * Parse composition string into buffer
1060 */
1061 for ( ; *cp; cp++) {
1062 if (*cp == '%') {
1063 switch (*++cp) {
1064 case 'a':
1065 {
1066 /* insert parameters from directive */
1067 char **ep;
1068 char *s = "";
1069
1070 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1071 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1072 len = strlen (bp);
1073 bp += len;
1074 buflen -= len;
1075 s = " ";
1076 }
1077 }
1078 break;
1079
1080 case 'F':
1081 /* %f, and stdout is not-redirected */
1082 xstdout = 1;
1083 /* and fall... */
1084
1085 case 'f':
1086 /*
1087 * insert temporary filename where
1088 * content should be written
1089 */
1090 snprintf (bp, buflen, "%s", ce->ce_file);
1091 break;
1092
1093 case 's':
1094 /* insert content subtype */
1095 strncpy (bp, ci->ci_subtype, buflen);
1096 break;
1097
1098 case '%':
1099 /* insert character % */
1100 goto raw;
1101
1102 default:
1103 *bp++ = *--cp;
1104 *bp = '\0';
1105 buflen--;
1106 continue;
1107 }
1108 len = strlen (bp);
1109 bp += len;
1110 buflen -= len;
1111 } else {
1112 raw:
1113 *bp++ = *cp;
1114 *bp = '\0';
1115 buflen--;
1116 }
1117 }
1118
1119 if (verbosw)
1120 printf ("composing content %s/%s from command\n\t%s\n",
1121 ci->ci_type, ci->ci_subtype, buffer);
1122
1123 fflush (stdout); /* not sure if need for -noverbose */
1124
1125 vec[0] = "/bin/sh";
1126 vec[1] = "-c";
1127 vec[2] = buffer;
1128 vec[3] = NULL;
1129
1130 if ((out = fopen (ce->ce_file, "w")) == NULL)
1131 adios (ce->ce_file, "unable to open for writing");
1132
1133 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1134 sleep (5);
1135 switch (child_id) {
1136 case NOTOK:
1137 adios ("fork", "unable to fork");
1138 /* NOTREACHED */
1139
1140 case OK:
1141 if (!xstdout)
1142 dup2 (fileno (out), 1);
1143 close (fileno (out));
1144 execvp ("/bin/sh", vec);
1145 fprintf (stderr, "unable to exec ");
1146 perror ("/bin/sh");
1147 _exit (-1);
1148 /* NOTREACHED */
1149
1150 default:
1151 fclose (out);
1152 if (pidXwait(child_id, NULL))
1153 done (1);
1154 break;
1155 }
1156 }
1157
1158 /* Check size of file */
1159 if (listsw && ct->c_end == 0L) {
1160 struct stat st;
1161
1162 if (stat (ce->ce_file, &st) != NOTOK)
1163 ct->c_end = (long) st.st_size;
1164 }
1165 break;
1166 }
1167
1168 return OK;
1169 }
1170
1171
1172 /*
1173 * Scan the content.
1174 *
1175 * 1) choose a transfer encoding.
1176 * 2) check for clashes with multipart boundary string.
1177 * 3) for text content, figure out which character set is being used.
1178 *
1179 * If there is a clash with one of the contents and the multipart boundary,
1180 * this function will exit with NOTOK. This will cause the scanning process
1181 * to be repeated with a different multipart boundary. It is possible
1182 * (although highly unlikely) that this scan will be repeated multiple times.
1183 */
1184
1185 static int
1186 scan_content (CT ct)
1187 {
1188 int len;
1189 int check8bit = 0, contains8bit = 0; /* check if contains 8bit data */
1190 int checklinelen = 0, linelen = 0; /* check for long lines */
1191 int checkboundary = 0, boundaryclash = 0; /* check if clashes with multipart boundary */
1192 int checklinespace = 0, linespace = 0; /* check if any line ends with space */
1193 int checkebcdic = 0, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1194 unsigned char *cp = NULL, buffer[BUFSIZ];
1195 struct text *t = NULL;
1196 FILE *in = NULL;
1197 CE ce = ct->c_cefile;
1198
1199 /*
1200 * handle multipart by scanning all subparts
1201 * and then checking their encoding.
1202 */
1203 if (ct->c_type == CT_MULTIPART) {
1204 struct multipart *m = (struct multipart *) ct->c_ctparams;
1205 struct part *part;
1206
1207 /* initially mark the domain of enclosing multipart as 7bit */
1208 ct->c_encoding = CE_7BIT;
1209
1210 for (part = m->mp_parts; part; part = part->mp_next) {
1211 CT p = part->mp_part;
1212
1213 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1214 return NOTOK;
1215
1216 /* if necessary, enlarge encoding for enclosing multipart */
1217 if (p->c_encoding == CE_BINARY)
1218 ct->c_encoding = CE_BINARY;
1219 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1220 ct->c_encoding = CE_8BIT;
1221 }
1222
1223 return OK;
1224 }
1225
1226 /*
1227 * Decide what to check while scanning this content.
1228 */
1229 switch (ct->c_type) {
1230 case CT_TEXT:
1231 check8bit = 1;
1232 checkboundary = 1;
1233 if (ct->c_subtype == TEXT_PLAIN) {
1234 checkebcdic = 0;
1235 checklinelen = 0;
1236 checklinespace = 0;
1237 } else {
1238 checkebcdic = ebcdicsw;
1239 checklinelen = 1;
1240 checklinespace = 1;
1241 }
1242 break;
1243
1244 case CT_APPLICATION:
1245 check8bit = 1;
1246 checkebcdic = ebcdicsw;
1247 checklinelen = 1;
1248 checklinespace = 1;
1249 checkboundary = 1;
1250 break;
1251
1252 case CT_MESSAGE:
1253 check8bit = 0;
1254 checkebcdic = 0;
1255 checklinelen = 0;
1256 checklinespace = 0;
1257
1258 /* don't check anything for message/external */
1259 if (ct->c_subtype == MESSAGE_EXTERNAL)
1260 checkboundary = 0;
1261 else
1262 checkboundary = 1;
1263 break;
1264
1265 case CT_AUDIO:
1266 case CT_IMAGE:
1267 case CT_VIDEO:
1268 /*
1269 * Don't check anything for these types,
1270 * since we are forcing use of base64.
1271 */
1272 check8bit = 0;
1273 checkebcdic = 0;
1274 checklinelen = 0;
1275 checklinespace = 0;
1276 checkboundary = 0;
1277 break;
1278 }
1279
1280 /*
1281 * Scan the unencoded content
1282 */
1283 if (check8bit || checklinelen || checklinespace || checkboundary) {
1284 if ((in = fopen (ce->ce_file, "r")) == NULL)
1285 adios (ce->ce_file, "unable to open for reading");
1286 len = strlen (prefix);
1287
1288 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1289 /*
1290 * Check for 8bit data.
1291 */
1292 if (check8bit) {
1293 for (cp = buffer; *cp; cp++) {
1294 if (!isascii (*cp)) {
1295 contains8bit = 1;
1296 check8bit = 0; /* no need to keep checking */
1297 }
1298 /*
1299 * Check if character is ebcdic-safe. We only check
1300 * this if also checking for 8bit data.
1301 */
1302 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1303 ebcdicunsafe = 1;
1304 checkebcdic = 0; /* no need to keep checking */
1305 }
1306 }
1307 }
1308
1309 /*
1310 * Check line length.
1311 */
1312 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1313 linelen = 1;
1314 checklinelen = 0; /* no need to keep checking */
1315 }
1316
1317 /*
1318 * Check if line ends with a space.
1319 */
1320 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1321 linespace = 1;
1322 checklinespace = 0; /* no need to keep checking */
1323 }
1324
1325 /*
1326 * Check if content contains a line that clashes
1327 * with our standard boundary for multipart messages.
1328 */
1329 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1330 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1331 if (!isspace (*cp))
1332 break;
1333 *++cp = '\0';
1334 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1335 boundaryclash = 1;
1336 checkboundary = 0; /* no need to keep checking */
1337 }
1338 }
1339 }
1340 fclose (in);
1341 }
1342
1343 /*
1344 * Decide which transfer encoding to use.
1345 */
1346 switch (ct->c_type) {
1347 case CT_TEXT:
1348 /*
1349 * If the text content didn't specify a character
1350 * set, we need to figure out which one was used.
1351 */
1352 t = (struct text *) ct->c_ctparams;
1353 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1354 CI ci = &ct->c_ctinfo;
1355 char **ap, **ep;
1356
1357 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1358 continue;
1359
1360 if (contains8bit) {
1361 *ap = concat ("charset=", write_charset_8bit(), NULL);
1362 } else {
1363 *ap = add ("charset=us-ascii", NULL);
1364 }
1365 t->tx_charset = CHARSET_SPECIFIED;
1366
1367 cp = strchr(*ap++, '=');
1368 *ap = NULL;
1369 *cp++ = '\0';
1370 *ep = cp;
1371 }
1372
1373 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1374 ct->c_encoding = CE_QUOTED;
1375 else
1376 ct->c_encoding = CE_7BIT;
1377 break;
1378
1379 case CT_APPLICATION:
1380 /* For application type, use base64, except when postscript */
1381 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1382 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1383 ? CE_QUOTED : CE_BASE64;
1384 else
1385 ct->c_encoding = CE_7BIT;
1386 break;
1387
1388 case CT_MESSAGE:
1389 ct->c_encoding = CE_7BIT;
1390 break;
1391
1392 case CT_AUDIO:
1393 case CT_IMAGE:
1394 case CT_VIDEO:
1395 /* For audio, image, and video contents, just use base64 */
1396 ct->c_encoding = CE_BASE64;
1397 break;
1398 }
1399
1400 return (boundaryclash ? NOTOK : OK);
1401 }
1402
1403
1404 /*
1405 * Scan the content structures, and build header
1406 * fields that will need to be output into the
1407 * message.
1408 */
1409
1410 static int
1411 build_headers (CT ct)
1412 {
1413 int cc, mailbody, len;
1414 char **ap, **ep;
1415 char *np, *vp, buffer[BUFSIZ];
1416 CI ci = &ct->c_ctinfo;
1417
1418 /*
1419 * If message is type multipart, then add the multipart
1420 * boundary to the list of attribute/value pairs.
1421 */
1422 if (ct->c_type == CT_MULTIPART) {
1423 char *cp;
1424 static int level = 0; /* store nesting level */
1425
1426 ap = ci->ci_attrs;
1427 ep = ci->ci_values;
1428 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1429 cp = strchr(*ap++ = add (buffer, NULL), '=');
1430 *ap = NULL;
1431 *cp++ = '\0';
1432 *ep = cp;
1433 }
1434
1435 /*
1436 * Skip the output of Content-Type, parameters, content
1437 * description and disposition, and Content-ID if the
1438 * content is of type "message" and the rfc934 compatibility
1439 * flag is set (which means we are inside multipart/digest
1440 * and the switch -rfc934mode was given).
1441 */
1442 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1443 goto skip_headers;
1444
1445 /*
1446 * output the content type and subtype
1447 */
1448 np = add (TYPE_FIELD, NULL);
1449 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1450
1451 /* keep track of length of line */
1452 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1453 + strlen (ci->ci_subtype) + 3;
1454
1455 mailbody = ct->c_type == CT_MESSAGE
1456 && ct->c_subtype == MESSAGE_EXTERNAL
1457 && ((struct exbody *) ct->c_ctparams)->eb_body;
1458
1459 /*
1460 * Append the attribute/value pairs to
1461 * the end of the Content-Type line.
1462 */
1463 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1464 if (mailbody && !mh_strcasecmp (*ap, "body"))
1465 continue;
1466
1467 vp = add (";", vp);
1468 len++;
1469
1470 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1471 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1472 vp = add ("\n\t", vp);
1473 len = 8;
1474 } else {
1475 vp = add (" ", vp);
1476 len++;
1477 }
1478 vp = add (buffer, vp);
1479 len += cc;
1480 }
1481
1482 /*
1483 * Append any RFC-822 comment to the end of
1484 * the Content-Type line.
1485 */
1486 if (ci->ci_comment) {
1487 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1488 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1489 vp = add ("\n\t", vp);
1490 len = 8;
1491 } else {
1492 vp = add (" ", vp);
1493 len++;
1494 }
1495 vp = add (buffer, vp);
1496 len += cc;
1497 }
1498 vp = add ("\n", vp);
1499 add_header (ct, np, vp);
1500
1501 /*
1502 * output the Content-ID, unless disabled by -nocontentid
1503 */
1504 if (contentidsw && ct->c_id) {
1505 np = add (ID_FIELD, NULL);
1506 vp = concat (" ", ct->c_id, NULL);
1507 add_header (ct, np, vp);
1508 }
1509
1510 /*
1511 * output the Content-Description
1512 */
1513 if (ct->c_descr) {
1514 np = add (DESCR_FIELD, NULL);
1515 vp = concat (" ", ct->c_descr, NULL);
1516 add_header (ct, np, vp);
1517 }
1518
1519 /*
1520 * output the Content-Disposition
1521 */
1522 if (ct->c_dispo) {
1523 np = add (DISPO_FIELD, NULL);
1524 vp = concat (" ", ct->c_dispo, NULL);
1525 add_header (ct, np, vp);
1526 }
1527
1528 skip_headers:
1529 /*
1530 * If this is the internal content structure for a
1531 * "message/external", then we are done with the
1532 * headers (since it has no body).
1533 */
1534 if (ct->c_ctexbody)
1535 return OK;
1536
1537 /*
1538 * output the Content-MD5
1539 */
1540 if (checksw) {
1541 np = add (MD5_FIELD, NULL);
1542 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1543 add_header (ct, np, vp);
1544 }
1545
1546 /*
1547 * output the Content-Transfer-Encoding
1548 */
1549 switch (ct->c_encoding) {
1550 case CE_7BIT:
1551 /* Nothing to output */
1552 #if 0
1553 np = add (ENCODING_FIELD, NULL);
1554 vp = concat (" ", "7bit", "\n", NULL);
1555 add_header (ct, np, vp);
1556 #endif
1557 break;
1558
1559 case CE_8BIT:
1560 if (ct->c_type == CT_MESSAGE)
1561 adios (NULL, "internal error, invalid encoding");
1562
1563 np = add (ENCODING_FIELD, NULL);
1564 vp = concat (" ", "8bit", "\n", NULL);
1565 add_header (ct, np, vp);
1566 break;
1567
1568 case CE_QUOTED:
1569 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1570 adios (NULL, "internal error, invalid encoding");
1571
1572 np = add (ENCODING_FIELD, NULL);
1573 vp = concat (" ", "quoted-printable", "\n", NULL);
1574 add_header (ct, np, vp);
1575 break;
1576
1577 case CE_BASE64:
1578 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1579 adios (NULL, "internal error, invalid encoding");
1580
1581 np = add (ENCODING_FIELD, NULL);
1582 vp = concat (" ", "base64", "\n", NULL);
1583 add_header (ct, np, vp);
1584 break;
1585
1586 case CE_BINARY:
1587 if (ct->c_type == CT_MESSAGE)
1588 adios (NULL, "internal error, invalid encoding");
1589
1590 np = add (ENCODING_FIELD, NULL);
1591 vp = concat (" ", "binary", "\n", NULL);
1592 add_header (ct, np, vp);
1593 break;
1594
1595 default:
1596 adios (NULL, "unknown transfer encoding in content");
1597 break;
1598 }
1599
1600 /*
1601 * Additional content specific header processing
1602 */
1603 switch (ct->c_type) {
1604 case CT_MULTIPART:
1605 {
1606 struct multipart *m;
1607 struct part *part;
1608
1609 m = (struct multipart *) ct->c_ctparams;
1610 for (part = m->mp_parts; part; part = part->mp_next) {
1611 CT p;
1612
1613 p = part->mp_part;
1614 build_headers (p);
1615 }
1616 }
1617 break;
1618
1619 case CT_MESSAGE:
1620 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1621 struct exbody *e;
1622
1623 e = (struct exbody *) ct->c_ctparams;
1624 build_headers (e->eb_content);
1625 }
1626 break;
1627
1628 default:
1629 /* Nothing to do */
1630 break;
1631 }
1632
1633 return OK;
1634 }
1635
1636
1637 static char nib2b64[0x40+1] =
1638 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1639
1640 static char *
1641 calculate_digest (CT ct, int asciiP)
1642 {
1643 int cc;
1644 char buffer[BUFSIZ], *vp, *op;
1645 unsigned char *dp;
1646 unsigned char digest[16];
1647 unsigned char outbuf[25];
1648 MD5_CTX mdContext;
1649 CE ce = ct->c_cefile;
1650 char *infilename = ce->ce_file ? ce->ce_file : ct->c_file;
1651 FILE *in;
1652
1653 /* open content */
1654 if ((in = fopen (infilename, "r")) == NULL)
1655 adios (infilename, "unable to open for reading");
1656
1657 /* Initialize md5 context */
1658 MD5Init (&mdContext);
1659
1660 /* calculate md5 message digest */
1661 if (asciiP) {
1662 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1663 char c, *cp;
1664
1665 cp = buffer + strlen (buffer) - 1;
1666 if ((c = *cp) == '\n')
1667 *cp = '\0';
1668
1669 MD5Update (&mdContext, (unsigned char *) buffer,
1670 (unsigned int) strlen (buffer));
1671
1672 if (c == '\n')
1673 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1674 }
1675 } else {
1676 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1677 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1678 }
1679
1680 /* md5 finalization. Write digest and zero md5 context */
1681 MD5Final (digest, &mdContext);
1682
1683 /* close content */
1684 fclose (in);
1685
1686 /* print debugging info */
1687 if (debugsw) {
1688 unsigned char *ep;
1689
1690 fprintf (stderr, "MD5 digest=");
1691 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1692 dp < ep; dp++)
1693 fprintf (stderr, "%02x", *dp & 0xff);
1694 fprintf (stderr, "\n");
1695 }
1696
1697 /* encode the digest using base64 */
1698 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1699 cc > 0; cc -= 3, op += 4) {
1700 unsigned long bits;
1701 char *bp;
1702
1703 bits = (*dp++ & 0xff) << 16;
1704 if (cc > 1) {
1705 bits |= (*dp++ & 0xff) << 8;
1706 if (cc > 2)
1707 bits |= *dp++ & 0xff;
1708 }
1709
1710 for (bp = op + 4; bp > op; bits >>= 6)
1711 *--bp = nib2b64[bits & 0x3f];
1712 if (cc < 3) {
1713 *(op + 3) = '=';
1714 if (cc < 2)
1715 *(op + 2) = '=';
1716 }
1717 }
1718
1719 /* null terminate string */
1720 outbuf[24] = '\0';
1721
1722 /* now make copy and return string */
1723 vp = concat (" ", outbuf, "\n", NULL);
1724 return vp;
1725 }