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