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