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