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