]> diplodocus.org Git - nmh/blob - uip/mhbuildsbr.c
b75034abdd84b6ee2309bdb0f65b2801fd2720ba
[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
418 /* use a temp file to collect the plain text lines */
419 ce->ce_file = add (m_tmpfil (invo_name), NULL);
420 ce->ce_unlink = 1;
421
422 if ((out = fopen (ce->ce_file, "w")) == NULL)
423 adios (ce->ce_file, "unable to open for writing");
424
425 if (buf[0] == '#' && buf[1] == '<') {
426 strncpy (content, buf + 2, sizeof(content));
427 inlineD = 1;
428 goto rock_and_roll;
429 } else {
430 inlineD = 0;
431 }
432
433 /* the directive is implicit */
434 strncpy (content, "text/plain", sizeof(content));
435 headers = 0;
436 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
437 for (;;) {
438 int i;
439
440 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
441 && buffer[i = strlen (DESCR_FIELD)] == ':') {
442 headers = 1;
443
444 again_descr:
445 ct->c_descr = add (buffer + i + 1, ct->c_descr);
446 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
447 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
448 switch (buffer[0]) {
449 case ' ':
450 case '\t':
451 i = -1;
452 goto again_descr;
453
454 case '#':
455 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
456 /* NOTREACHED */
457
458 default:
459 break;
460 }
461 }
462
463 if (headers >= 0 && uprf (buffer, DISPO_FIELD)
464 && buffer[i = strlen (DISPO_FIELD)] == ':') {
465 headers = 1;
466
467 again_dispo:
468 ct->c_dispo = add (buffer + i + 1, ct->c_dispo);
469 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
470 adios (NULL, "end-of-file after %s: field in plaintext", DISPO_FIELD);
471 switch (buffer[0]) {
472 case ' ':
473 case '\t':
474 i = -1;
475 goto again_dispo;
476
477 case '#':
478 adios (NULL, "#-directive after %s: field in plaintext", DISPO_FIELD);
479 /* NOTREACHED */
480
481 default:
482 break;
483 }
484 }
485
486 if (headers != 1 || buffer[0] != '\n')
487 fputs (buffer, out);
488
489 rock_and_roll:
490 headers = -1;
491 pos = ftell (in);
492 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
493 break;
494 if (buffer[0] == '#') {
495 char *bp;
496
497 if (buffer[1] != '#')
498 break;
499 for (cp = (bp = buffer) + 1; *cp; cp++)
500 *bp++ = *cp;
501 *bp = '\0';
502 }
503 }
504
505 if (listsw)
506 ct->c_end = ftell (out);
507 fclose (out);
508
509 /* parse content type */
510 if (get_ctinfo (content, ct, inlineD) == NOTOK)
511 done (1);
512
513 for (s2i = str2cts; s2i->si_key; s2i++)
514 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
515 break;
516 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
517 s2i++;
518
519 /*
520 * check type specified (possibly implicitly)
521 */
522 switch (ct->c_type = s2i->si_val) {
523 case CT_MESSAGE:
524 if (!mh_strcasecmp (ci->ci_subtype, "rfc822")) {
525 ct->c_encoding = CE_7BIT;
526 goto call_init;
527 }
528 /* else fall... */
529 case CT_MULTIPART:
530 adios (NULL, "it doesn't make sense to define an in-line %s content",
531 ct->c_type == CT_MESSAGE ? "message" : "multipart");
532 /* NOTREACHED */
533
534 default:
535 call_init:
536 if ((ct->c_ctinitfnx = s2i->si_init))
537 (*ct->c_ctinitfnx) (ct);
538 break;
539 }
540
541 if (cp)
542 fseek (in, pos, SEEK_SET);
543 return OK;
544 }
545
546 /*
547 * If we've reached this point, the next line
548 * must be some type of explicit directive.
549 */
550
551 /* check if directive is external-type */
552 extrnal = (buf[1] == '@');
553
554 /* parse directive */
555 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
556 done (1);
557
558 /* check directive against the list of MIME types */
559 for (s2i = str2cts; s2i->si_key; s2i++)
560 if (!mh_strcasecmp (ci->ci_type, s2i->si_key))
561 break;
562
563 /*
564 * Check if the directive specified a valid type.
565 * This will happen if it was one of the following forms:
566 *
567 * #type/subtype (or)
568 * #@type/subtype
569 */
570 if (s2i->si_key) {
571 if (!ci->ci_subtype)
572 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
573
574 switch (ct->c_type = s2i->si_val) {
575 case CT_MULTIPART:
576 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
577 ci->ci_type, ci->ci_subtype);
578 /* NOTREACHED */
579
580 case CT_MESSAGE:
581 if (!mh_strcasecmp (ci->ci_subtype, "partial"))
582 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
583 ci->ci_type, ci->ci_subtype);
584 if (!mh_strcasecmp (ci->ci_subtype, "external-body"))
585 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
586 ci->ci_type, ci->ci_subtype);
587 use_forw:
588 adios (NULL,
589 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
590 ci->ci_type, ci->ci_subtype);
591 /* NOTREACHED */
592
593 default:
594 if ((ct->c_ctinitfnx = s2i->si_init))
595 (*ct->c_ctinitfnx) (ct);
596 break;
597 }
598
599 /*
600 * #@type/subtype (external types directive)
601 */
602 if (extrnal) {
603 struct exbody *e;
604 CT p;
605
606 if (!ci->ci_magic)
607 adios (NULL, "need external information for \"#@%s/%s\"",
608 ci->ci_type, ci->ci_subtype);
609 p = ct;
610
611 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
612 free (ci->ci_magic);
613 ci->ci_magic = NULL;
614
615 /*
616 * Since we are using the current Content structure to
617 * hold information about the type of the external
618 * reference, we need to create another Content structure
619 * for the message/external-body to wrap it in.
620 */
621 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
622 adios (NULL, "out of memory");
623 *ctp = ct;
624 ci = &ct->c_ctinfo;
625 if (get_ctinfo (buffer, ct, 0) == NOTOK)
626 done (1);
627 ct->c_type = CT_MESSAGE;
628 ct->c_subtype = MESSAGE_EXTERNAL;
629
630 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
631 adios (NULL, "out of memory");
632 ct->c_ctparams = (void *) e;
633
634 e->eb_parent = ct;
635 e->eb_content = p;
636 p->c_ctexbody = e;
637
638 if (params_external (ct, 1) == NOTOK)
639 done (1);
640
641 return OK;
642 }
643
644 /* Handle [file] argument */
645 if (ci->ci_magic) {
646 /* check if specifies command to execute */
647 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
648 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
649 continue;
650 if (!*cp)
651 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
652 cp = add (cp, NULL);
653 free (ci->ci_magic);
654 ci->ci_magic = cp;
655 } else {
656 /* record filename of decoded contents */
657 ce->ce_file = ci->ci_magic;
658 if (access (ce->ce_file, R_OK) == NOTOK)
659 adios ("reading", "unable to access %s for", ce->ce_file);
660 if (listsw && stat (ce->ce_file, &st) != NOTOK)
661 ct->c_end = (long) st.st_size;
662 ci->ci_magic = NULL;
663 }
664 return OK;
665 }
666
667 /*
668 * No [file] argument, so check profile for
669 * method to compose content.
670 */
671 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
672 invo_name, ci->ci_type, ci->ci_subtype);
673 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
674 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
675 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
676 content_error (NULL, ct, "don't know how to compose content");
677 done (1);
678 }
679 }
680 ci->ci_magic = add (cp, NULL);
681 return OK;
682 }
683
684 if (extrnal)
685 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
686
687 /*
688 * Message directive
689 * #forw [+folder] [msgs]
690 */
691 if (!mh_strcasecmp (ci->ci_type, "forw")) {
692 int msgnum;
693 char *folder, *arguments[MAXARGS];
694 struct msgs *mp;
695
696 if (ci->ci_magic) {
697 ap = brkstring (ci->ci_magic, " ", "\n");
698 copyip (ap, arguments, MAXARGS);
699 } else {
700 arguments[0] = "cur";
701 arguments[1] = NULL;
702 }
703 folder = NULL;
704
705 /* search the arguments for a folder name */
706 for (ap = arguments; *ap; ap++) {
707 cp = *ap;
708 if (*cp == '+' || *cp == '@') {
709 if (folder)
710 adios (NULL, "only one folder per #forw directive");
711 else
712 folder = pluspath (cp);
713 }
714 }
715
716 /* else, use the current folder */
717 if (!folder)
718 folder = add (getfolder (1), NULL);
719
720 if (!(mp = folder_read (folder)))
721 adios (NULL, "unable to read folder %s", folder);
722 for (ap = arguments; *ap; ap++) {
723 cp = *ap;
724 if (*cp != '+' && *cp != '@')
725 if (!m_convert (mp, cp))
726 done (1);
727 }
728 free (folder);
729 free_ctinfo (ct);
730
731 /*
732 * If there is more than one message to include, make this
733 * a content of type "multipart/digest" and insert each message
734 * as a subpart. If there is only one message, then make this
735 * a content of type "message/rfc822".
736 */
737 if (mp->numsel > 1) {
738 /* we are forwarding multiple messages */
739 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
740 done (1);
741 ct->c_type = CT_MULTIPART;
742 ct->c_subtype = MULTI_DIGEST;
743
744 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
745 adios (NULL, "out of memory");
746 ct->c_ctparams = (void *) m;
747 pp = &m->mp_parts;
748
749 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
750 if (is_selected(mp, msgnum)) {
751 struct part *part;
752 CT p;
753 CE pe;
754
755 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
756 adios (NULL, "out of memory");
757 init_decoded_content (p);
758 pe = p->c_cefile;
759 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
760 done (1);
761 p->c_type = CT_MESSAGE;
762 p->c_subtype = MESSAGE_RFC822;
763
764 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
765 pe->ce_file = add (buffer, NULL);
766 if (listsw && stat (pe->ce_file, &st) != NOTOK)
767 p->c_end = (long) st.st_size;
768
769 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
770 adios (NULL, "out of memory");
771 *pp = part;
772 pp = &part->mp_next;
773 part->mp_part = p;
774 }
775 }
776 } else {
777 /* we are forwarding one message */
778 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
779 done (1);
780 ct->c_type = CT_MESSAGE;
781 ct->c_subtype = MESSAGE_RFC822;
782
783 msgnum = mp->lowsel;
784 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
785 ce->ce_file = add (buffer, NULL);
786 if (listsw && stat (ce->ce_file, &st) != NOTOK)
787 ct->c_end = (long) st.st_size;
788 }
789
790 folder_free (mp); /* free folder/message structure */
791 return OK;
792 }
793
794 /*
795 * #end
796 */
797 if (!mh_strcasecmp (ci->ci_type, "end")) {
798 free_content (ct);
799 *ctp = NULL;
800 return DONE;
801 }
802
803 /*
804 * #begin [ alternative | parallel ]
805 */
806 if (!mh_strcasecmp (ci->ci_type, "begin")) {
807 if (!ci->ci_magic) {
808 vrsn = MULTI_MIXED;
809 cp = SubMultiPart[vrsn - 1].kv_key;
810 } else if (!mh_strcasecmp (ci->ci_magic, "alternative")) {
811 vrsn = MULTI_ALTERNATE;
812 cp = SubMultiPart[vrsn - 1].kv_key;
813 } else if (!mh_strcasecmp (ci->ci_magic, "parallel")) {
814 vrsn = MULTI_PARALLEL;
815 cp = SubMultiPart[vrsn - 1].kv_key;
816 } else if (uprf (ci->ci_magic, "digest")) {
817 goto use_forw;
818 } else {
819 vrsn = MULTI_UNKNOWN;
820 cp = ci->ci_magic;
821 }
822
823 free_ctinfo (ct);
824 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
825 if (get_ctinfo (buffer, ct, 0) == NOTOK)
826 done (1);
827 ct->c_type = CT_MULTIPART;
828 ct->c_subtype = vrsn;
829
830 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
831 adios (NULL, "out of memory");
832 ct->c_ctparams = (void *) m;
833
834 pp = &m->mp_parts;
835 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
836 struct part *part;
837 CT p;
838
839 if (user_content (in, file, buffer, &p) == DONE) {
840 if (!m->mp_parts)
841 adios (NULL, "empty \"#begin ... #end\" sequence");
842 return OK;
843 }
844 if (!p)
845 continue;
846
847 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
848 adios (NULL, "out of memory");
849 *pp = part;
850 pp = &part->mp_next;
851 part->mp_part = p;
852 }
853 admonish (NULL, "premature end-of-file, missing #end");
854 return OK;
855 }
856
857 /*
858 * Unknown directive
859 */
860 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
861 return NOTOK; /* NOT REACHED */
862 }
863
864
865 static void
866 set_id (CT ct, int top)
867 {
868 char msgid[BUFSIZ];
869 static int partno;
870 static time_t clock = 0;
871 static char *msgfmt;
872
873 if (clock == 0) {
874 time (&clock);
875 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
876 (int) getpid(), (long) clock, LocalName());
877 partno = 0;
878 msgfmt = getcpy(msgid);
879 }
880 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
881 ct->c_id = getcpy (msgid);
882 }
883
884
885 static char ebcdicsafe[0x100] = {
886 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
887 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
888 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
889 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
890 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
891 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
892 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
893 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
894 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
895 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
896 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
897 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
898 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
899 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
900 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
901 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
902 0x00, 0x00, 0x00, 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 };
919
920
921 /*
922 * Fill out, or expand the various contents in the composition
923 * draft. Read-in any necessary files. Parse and execute any
924 * commands specified by profile composition strings.
925 */
926
927 static int
928 compose_content (CT ct)
929 {
930 CE ce = ct->c_cefile;
931
932 switch (ct->c_type) {
933 case CT_MULTIPART:
934 {
935 int partnum;
936 char *pp;
937 char partnam[BUFSIZ];
938 struct multipart *m = (struct multipart *) ct->c_ctparams;
939 struct part *part;
940
941 if (ct->c_partno) {
942 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
943 pp = partnam + strlen (partnam);
944 } else {
945 pp = partnam;
946 }
947
948 /* first, we call compose_content on all the subparts */
949 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
950 CT p = part->mp_part;
951
952 sprintf (pp, "%d", partnum);
953 p->c_partno = add (partnam, NULL);
954 if (compose_content (p) == NOTOK)
955 return NOTOK;
956 }
957
958 /*
959 * If the -rfc934mode switch is given, then check all
960 * the subparts of a multipart/digest. If they are all
961 * message/rfc822, then mark this content and all
962 * subparts with the rfc934 compatibility mode flag.
963 */
964 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
965 int is934 = 1;
966
967 for (part = m->mp_parts; part; part = part->mp_next) {
968 CT p = part->mp_part;
969
970 if (p->c_subtype != MESSAGE_RFC822) {
971 is934 = 0;
972 break;
973 }
974 }
975 ct->c_rfc934 = is934;
976 for (part = m->mp_parts; part; part = part->mp_next) {
977 CT p = part->mp_part;
978
979 if ((p->c_rfc934 = is934))
980 p->c_end++;
981 }
982 }
983
984 if (listsw) {
985 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
986 if (ct->c_rfc934)
987 ct->c_end += 1;
988
989 for (part = m->mp_parts; part; part = part->mp_next)
990 ct->c_end += part->mp_part->c_end + partnum;
991 }
992 }
993 break;
994
995 case CT_MESSAGE:
996 /* Nothing to do for type message */
997 break;
998
999 /*
1000 * Discrete types (text/application/audio/image/video)
1001 */
1002 default:
1003 if (!ce->ce_file) {
1004 pid_t child_id;
1005 int i, xstdout, len, buflen;
1006 char *bp, **ap, *cp;
1007 char *vec[4], buffer[BUFSIZ];
1008 FILE *out;
1009 CI ci = &ct->c_ctinfo;
1010
1011 if (!(cp = ci->ci_magic))
1012 adios (NULL, "internal error(5)");
1013
1014 ce->ce_file = add (m_tmpfil (invo_name), NULL);
1015 ce->ce_unlink = 1;
1016
1017 xstdout = 0;
1018
1019 /* Get buffer ready to go */
1020 bp = buffer;
1021 bp[0] = '\0';
1022 buflen = sizeof(buffer);
1023
1024 /*
1025 * Parse composition string into buffer
1026 */
1027 for ( ; *cp; cp++) {
1028 if (*cp == '%') {
1029 switch (*++cp) {
1030 case 'a':
1031 {
1032 /* insert parameters from directive */
1033 char **ep;
1034 char *s = "";
1035
1036 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1037 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
1038 len = strlen (bp);
1039 bp += len;
1040 buflen -= len;
1041 s = " ";
1042 }
1043 }
1044 break;
1045
1046 case 'F':
1047 /* %f, and stdout is not-redirected */
1048 xstdout = 1;
1049 /* and fall... */
1050
1051 case 'f':
1052 /*
1053 * insert temporary filename where
1054 * content should be written
1055 */
1056 snprintf (bp, buflen, "%s", ce->ce_file);
1057 break;
1058
1059 case 's':
1060 /* insert content subtype */
1061 strncpy (bp, ci->ci_subtype, buflen);
1062 break;
1063
1064 case '%':
1065 /* insert character % */
1066 goto raw;
1067
1068 default:
1069 *bp++ = *--cp;
1070 *bp = '\0';
1071 buflen--;
1072 continue;
1073 }
1074 len = strlen (bp);
1075 bp += len;
1076 buflen -= len;
1077 } else {
1078 raw:
1079 *bp++ = *cp;
1080 *bp = '\0';
1081 buflen--;
1082 }
1083 }
1084
1085 if (verbosw)
1086 printf ("composing content %s/%s from command\n\t%s\n",
1087 ci->ci_type, ci->ci_subtype, buffer);
1088
1089 fflush (stdout); /* not sure if need for -noverbose */
1090
1091 vec[0] = "/bin/sh";
1092 vec[1] = "-c";
1093 vec[2] = buffer;
1094 vec[3] = NULL;
1095
1096 if ((out = fopen (ce->ce_file, "w")) == NULL)
1097 adios (ce->ce_file, "unable to open for writing");
1098
1099 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
1100 sleep (5);
1101 switch (child_id) {
1102 case NOTOK:
1103 adios ("fork", "unable to fork");
1104 /* NOTREACHED */
1105
1106 case OK:
1107 if (!xstdout)
1108 dup2 (fileno (out), 1);
1109 close (fileno (out));
1110 execvp ("/bin/sh", vec);
1111 fprintf (stderr, "unable to exec ");
1112 perror ("/bin/sh");
1113 _exit (-1);
1114 /* NOTREACHED */
1115
1116 default:
1117 fclose (out);
1118 if (pidXwait(child_id, NULL))
1119 done (1);
1120 break;
1121 }
1122 }
1123
1124 /* Check size of file */
1125 if (listsw && ct->c_end == 0L) {
1126 struct stat st;
1127
1128 if (stat (ce->ce_file, &st) != NOTOK)
1129 ct->c_end = (long) st.st_size;
1130 }
1131 break;
1132 }
1133
1134 return OK;
1135 }
1136
1137
1138 /*
1139 * Scan the content.
1140 *
1141 * 1) choose a transfer encoding.
1142 * 2) check for clashes with multipart boundary string.
1143 * 3) for text content, figure out which character set is being used.
1144 *
1145 * If there is a clash with one of the contents and the multipart boundary,
1146 * this function will exit with NOTOK. This will cause the scanning process
1147 * to be repeated with a different multipart boundary. It is possible
1148 * (although highly unlikely) that this scan will be repeated multiple times.
1149 */
1150
1151 static int
1152 scan_content (CT ct)
1153 {
1154 int len;
1155 int check8bit, contains8bit = 0; /* check if contains 8bit data */
1156 int checklinelen, linelen = 0; /* check for long lines */
1157 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
1158 int checklinespace, linespace = 0; /* check if any line ends with space */
1159 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
1160 unsigned char *cp, buffer[BUFSIZ];
1161 struct text *t;
1162 FILE *in;
1163 CE ce = ct->c_cefile;
1164
1165 /*
1166 * handle multipart by scanning all subparts
1167 * and then checking their encoding.
1168 */
1169 if (ct->c_type == CT_MULTIPART) {
1170 struct multipart *m = (struct multipart *) ct->c_ctparams;
1171 struct part *part;
1172
1173 /* initially mark the domain of enclosing multipart as 7bit */
1174 ct->c_encoding = CE_7BIT;
1175
1176 for (part = m->mp_parts; part; part = part->mp_next) {
1177 CT p = part->mp_part;
1178
1179 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
1180 return NOTOK;
1181
1182 /* if necessary, enlarge encoding for enclosing multipart */
1183 if (p->c_encoding == CE_BINARY)
1184 ct->c_encoding = CE_BINARY;
1185 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
1186 ct->c_encoding = CE_8BIT;
1187 }
1188
1189 return OK;
1190 }
1191
1192 /*
1193 * Decide what to check while scanning this content.
1194 */
1195 switch (ct->c_type) {
1196 case CT_TEXT:
1197 check8bit = 1;
1198 checkboundary = 1;
1199 if (ct->c_subtype == TEXT_PLAIN) {
1200 checkebcdic = 0;
1201 checklinelen = 0;
1202 checklinespace = 0;
1203 } else {
1204 checkebcdic = ebcdicsw;
1205 checklinelen = 1;
1206 checklinespace = 1;
1207 }
1208 break;
1209
1210 case CT_APPLICATION:
1211 check8bit = 1;
1212 checkebcdic = ebcdicsw;
1213 checklinelen = 1;
1214 checklinespace = 1;
1215 checkboundary = 1;
1216 break;
1217
1218 case CT_MESSAGE:
1219 check8bit = 0;
1220 checkebcdic = 0;
1221 checklinelen = 0;
1222 checklinespace = 0;
1223
1224 /* don't check anything for message/external */
1225 if (ct->c_subtype == MESSAGE_EXTERNAL)
1226 checkboundary = 0;
1227 else
1228 checkboundary = 1;
1229 break;
1230
1231 case CT_AUDIO:
1232 case CT_IMAGE:
1233 case CT_VIDEO:
1234 /*
1235 * Don't check anything for these types,
1236 * since we are forcing use of base64.
1237 */
1238 check8bit = 0;
1239 checkebcdic = 0;
1240 checklinelen = 0;
1241 checklinespace = 0;
1242 checkboundary = 0;
1243 break;
1244 }
1245
1246 /*
1247 * Scan the unencoded content
1248 */
1249 if (check8bit || checklinelen || checklinespace || checkboundary) {
1250 if ((in = fopen (ce->ce_file, "r")) == NULL)
1251 adios (ce->ce_file, "unable to open for reading");
1252 len = strlen (prefix);
1253
1254 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1255 /*
1256 * Check for 8bit data.
1257 */
1258 if (check8bit) {
1259 for (cp = buffer; *cp; cp++) {
1260 if (!isascii (*cp)) {
1261 contains8bit = 1;
1262 check8bit = 0; /* no need to keep checking */
1263 }
1264 /*
1265 * Check if character is ebcdic-safe. We only check
1266 * this if also checking for 8bit data.
1267 */
1268 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
1269 ebcdicunsafe = 1;
1270 checkebcdic = 0; /* no need to keep checking */
1271 }
1272 }
1273 }
1274
1275 /*
1276 * Check line length.
1277 */
1278 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
1279 linelen = 1;
1280 checklinelen = 0; /* no need to keep checking */
1281 }
1282
1283 /*
1284 * Check if line ends with a space.
1285 */
1286 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
1287 linespace = 1;
1288 checklinespace = 0; /* no need to keep checking */
1289 }
1290
1291 /*
1292 * Check if content contains a line that clashes
1293 * with our standard boundary for multipart messages.
1294 */
1295 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
1296 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1297 if (!isspace (*cp))
1298 break;
1299 *++cp = '\0';
1300 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
1301 boundaryclash = 1;
1302 checkboundary = 0; /* no need to keep checking */
1303 }
1304 }
1305 }
1306 fclose (in);
1307 }
1308
1309 /*
1310 * Decide which transfer encoding to use.
1311 */
1312 switch (ct->c_type) {
1313 case CT_TEXT:
1314 /*
1315 * If the text content didn't specify a character
1316 * set, we need to figure out which one was used.
1317 */
1318 t = (struct text *) ct->c_ctparams;
1319 if (t->tx_charset == CHARSET_UNSPECIFIED) {
1320 CI ci = &ct->c_ctinfo;
1321 char **ap, **ep;
1322
1323 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1324 continue;
1325
1326 if (contains8bit) {
1327 t->tx_charset = CHARSET_UNKNOWN;
1328 *ap = concat ("charset=", write_charset_8bit(), NULL);
1329 } else {
1330 t->tx_charset = CHARSET_USASCII;
1331 *ap = add ("charset=us-ascii", NULL);
1332 }
1333
1334 cp = strchr(*ap++, '=');
1335 *ap = NULL;
1336 *cp++ = '\0';
1337 *ep = cp;
1338 }
1339
1340 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1341 ct->c_encoding = CE_QUOTED;
1342 else
1343 ct->c_encoding = CE_7BIT;
1344 break;
1345
1346 case CT_APPLICATION:
1347 /* For application type, use base64, except when postscript */
1348 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
1349 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
1350 ? CE_QUOTED : CE_BASE64;
1351 else
1352 ct->c_encoding = CE_7BIT;
1353 break;
1354
1355 case CT_MESSAGE:
1356 ct->c_encoding = CE_7BIT;
1357 break;
1358
1359 case CT_AUDIO:
1360 case CT_IMAGE:
1361 case CT_VIDEO:
1362 /* For audio, image, and video contents, just use base64 */
1363 ct->c_encoding = CE_BASE64;
1364 break;
1365 }
1366
1367 return (boundaryclash ? NOTOK : OK);
1368 }
1369
1370
1371 /*
1372 * Scan the content structures, and build header
1373 * fields that will need to be output into the
1374 * message.
1375 */
1376
1377 static int
1378 build_headers (CT ct)
1379 {
1380 int cc, mailbody, len;
1381 char **ap, **ep;
1382 char *np, *vp, buffer[BUFSIZ];
1383 CI ci = &ct->c_ctinfo;
1384
1385 /*
1386 * If message is type multipart, then add the multipart
1387 * boundary to the list of attribute/value pairs.
1388 */
1389 if (ct->c_type == CT_MULTIPART) {
1390 char *cp;
1391 static int level = 0; /* store nesting level */
1392
1393 ap = ci->ci_attrs;
1394 ep = ci->ci_values;
1395 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
1396 cp = strchr(*ap++ = add (buffer, NULL), '=');
1397 *ap = NULL;
1398 *cp++ = '\0';
1399 *ep = cp;
1400 }
1401
1402 /*
1403 * Skip the output of Content-Type, parameters, content
1404 * description and disposition, and Content-ID if the
1405 * content is of type "message" and the rfc934 compatibility
1406 * flag is set (which means we are inside multipart/digest
1407 * and the switch -rfc934mode was given).
1408 */
1409 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
1410 goto skip_headers;
1411
1412 /*
1413 * output the content type and subtype
1414 */
1415 np = add (TYPE_FIELD, NULL);
1416 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
1417
1418 /* keep track of length of line */
1419 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
1420 + strlen (ci->ci_subtype) + 3;
1421
1422 mailbody = ct->c_type == CT_MESSAGE
1423 && ct->c_subtype == MESSAGE_EXTERNAL
1424 && ((struct exbody *) ct->c_ctparams)->eb_body;
1425
1426 /*
1427 * Append the attribute/value pairs to
1428 * the end of the Content-Type line.
1429 */
1430 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1431 if (mailbody && !mh_strcasecmp (*ap, "body"))
1432 continue;
1433
1434 vp = add (";", vp);
1435 len++;
1436
1437 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
1438 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
1439 vp = add ("\n\t", vp);
1440 len = 8;
1441 } else {
1442 vp = add (" ", vp);
1443 len++;
1444 }
1445 vp = add (buffer, vp);
1446 len += cc;
1447 }
1448
1449 /*
1450 * Append any RFC-822 comment to the end of
1451 * the Content-Type line.
1452 */
1453 if (ci->ci_comment) {
1454 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
1455 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
1456 vp = add ("\n\t", vp);
1457 len = 8;
1458 } else {
1459 vp = add (" ", vp);
1460 len++;
1461 }
1462 vp = add (buffer, vp);
1463 len += cc;
1464 }
1465 vp = add ("\n", vp);
1466 add_header (ct, np, vp);
1467
1468 /*
1469 * output the Content-ID, unless disabled by -nocontentid
1470 */
1471 if (contentidsw && ct->c_id) {
1472 np = add (ID_FIELD, NULL);
1473 vp = concat (" ", ct->c_id, NULL);
1474 add_header (ct, np, vp);
1475 }
1476
1477 /*
1478 * output the Content-Description
1479 */
1480 if (ct->c_descr) {
1481 np = add (DESCR_FIELD, NULL);
1482 vp = concat (" ", ct->c_descr, NULL);
1483 add_header (ct, np, vp);
1484 }
1485
1486 /*
1487 * output the Content-Disposition
1488 */
1489 if (ct->c_dispo) {
1490 np = add (DISPO_FIELD, NULL);
1491 vp = concat (" ", ct->c_dispo, NULL);
1492 add_header (ct, np, vp);
1493 }
1494
1495 skip_headers:
1496 /*
1497 * If this is the internal content structure for a
1498 * "message/external", then we are done with the
1499 * headers (since it has no body).
1500 */
1501 if (ct->c_ctexbody)
1502 return OK;
1503
1504 /*
1505 * output the Content-MD5
1506 */
1507 if (checksw) {
1508 np = add (MD5_FIELD, NULL);
1509 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
1510 add_header (ct, np, vp);
1511 }
1512
1513 /*
1514 * output the Content-Transfer-Encoding
1515 */
1516 switch (ct->c_encoding) {
1517 case CE_7BIT:
1518 /* Nothing to output */
1519 #if 0
1520 np = add (ENCODING_FIELD, NULL);
1521 vp = concat (" ", "7bit", "\n", NULL);
1522 add_header (ct, np, vp);
1523 #endif
1524 break;
1525
1526 case CE_8BIT:
1527 if (ct->c_type == CT_MESSAGE)
1528 adios (NULL, "internal error, invalid encoding");
1529
1530 np = add (ENCODING_FIELD, NULL);
1531 vp = concat (" ", "8bit", "\n", NULL);
1532 add_header (ct, np, vp);
1533 break;
1534
1535 case CE_QUOTED:
1536 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1537 adios (NULL, "internal error, invalid encoding");
1538
1539 np = add (ENCODING_FIELD, NULL);
1540 vp = concat (" ", "quoted-printable", "\n", NULL);
1541 add_header (ct, np, vp);
1542 break;
1543
1544 case CE_BASE64:
1545 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
1546 adios (NULL, "internal error, invalid encoding");
1547
1548 np = add (ENCODING_FIELD, NULL);
1549 vp = concat (" ", "base64", "\n", NULL);
1550 add_header (ct, np, vp);
1551 break;
1552
1553 case CE_BINARY:
1554 if (ct->c_type == CT_MESSAGE)
1555 adios (NULL, "internal error, invalid encoding");
1556
1557 np = add (ENCODING_FIELD, NULL);
1558 vp = concat (" ", "binary", "\n", NULL);
1559 add_header (ct, np, vp);
1560 break;
1561
1562 default:
1563 adios (NULL, "unknown transfer encoding in content");
1564 break;
1565 }
1566
1567 /*
1568 * Additional content specific header processing
1569 */
1570 switch (ct->c_type) {
1571 case CT_MULTIPART:
1572 {
1573 struct multipart *m;
1574 struct part *part;
1575
1576 m = (struct multipart *) ct->c_ctparams;
1577 for (part = m->mp_parts; part; part = part->mp_next) {
1578 CT p;
1579
1580 p = part->mp_part;
1581 build_headers (p);
1582 }
1583 }
1584 break;
1585
1586 case CT_MESSAGE:
1587 if (ct->c_subtype == MESSAGE_EXTERNAL) {
1588 struct exbody *e;
1589
1590 e = (struct exbody *) ct->c_ctparams;
1591 build_headers (e->eb_content);
1592 }
1593 break;
1594
1595 default:
1596 /* Nothing to do */
1597 break;
1598 }
1599
1600 return OK;
1601 }
1602
1603
1604 static char nib2b64[0x40+1] =
1605 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1606
1607 static char *
1608 calculate_digest (CT ct, int asciiP)
1609 {
1610 int cc;
1611 char buffer[BUFSIZ], *vp, *op;
1612 unsigned char *dp;
1613 unsigned char digest[16];
1614 unsigned char outbuf[25];
1615 FILE *in;
1616 MD5_CTX mdContext;
1617 CE ce = ct->c_cefile;
1618
1619 /* open content */
1620 if ((in = fopen (ce->ce_file, "r")) == NULL)
1621 adios (ce->ce_file, "unable to open for reading");
1622
1623 /* Initialize md5 context */
1624 MD5Init (&mdContext);
1625
1626 /* calculate md5 message digest */
1627 if (asciiP) {
1628 while (fgets (buffer, sizeof(buffer) - 1, in)) {
1629 char c, *cp;
1630
1631 cp = buffer + strlen (buffer) - 1;
1632 if ((c = *cp) == '\n')
1633 *cp = '\0';
1634
1635 MD5Update (&mdContext, (unsigned char *) buffer,
1636 (unsigned int) strlen (buffer));
1637
1638 if (c == '\n')
1639 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
1640 }
1641 } else {
1642 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
1643 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
1644 }
1645
1646 /* md5 finalization. Write digest and zero md5 context */
1647 MD5Final (digest, &mdContext);
1648
1649 /* close content */
1650 fclose (in);
1651
1652 /* print debugging info */
1653 if (debugsw) {
1654 unsigned char *ep;
1655
1656 fprintf (stderr, "MD5 digest=");
1657 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
1658 dp < ep; dp++)
1659 fprintf (stderr, "%02x", *dp & 0xff);
1660 fprintf (stderr, "\n");
1661 }
1662
1663 /* encode the digest using base64 */
1664 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
1665 cc > 0; cc -= 3, op += 4) {
1666 unsigned long bits;
1667 char *bp;
1668
1669 bits = (*dp++ & 0xff) << 16;
1670 if (cc > 1) {
1671 bits |= (*dp++ & 0xff) << 8;
1672 if (cc > 2)
1673 bits |= *dp++ & 0xff;
1674 }
1675
1676 for (bp = op + 4; bp > op; bits >>= 6)
1677 *--bp = nib2b64[bits & 0x3f];
1678 if (cc < 3) {
1679 *(op + 3) = '=';
1680 if (cc < 2)
1681 *(op + 2) = '=';
1682 }
1683 }
1684
1685 /* null terminate string */
1686 outbuf[24] = '\0';
1687
1688 /* now make copy and return string */
1689 vp = concat (" ", outbuf, "\n", NULL);
1690 return vp;
1691 }