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