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