]> diplodocus.org Git - nmh/blob - uip/mhbuildsbr.c
Avoid non-portable use of $< outside an inference rule.
[nmh] / uip / mhbuildsbr.c
1
2 /*
3 * mhbuildsbr.c -- routines to expand/translate MIME composition files
4 *
5 * $Id$
6 *
7 * This code is Copyright (c) 2002, by the authors of nmh. See the
8 * COPYRIGHT file in the root directory of the nmh distribution for
9 * complete copyright information.
10 */
11
12 /*
13 * This code was originally part of mhn.c. I split it into
14 * a separate program (mhbuild.c) and then later split it
15 * again (mhbuildsbr.c). But the code still has some of
16 * the mhn.c code in it. This program needs additional
17 * streamlining and removal of unneeded code.
18 */
19
20 #include <h/mh.h>
21 #include <fcntl.h>
22 #include <h/signals.h>
23 #include <h/md5.h>
24 #include <errno.h>
25 #include <signal.h>
26 #include <h/mts.h>
27 #include <h/tws.h>
28 #include <h/mime.h>
29 #include <h/mhparse.h>
30
31 #ifdef TIME_WITH_SYS_TIME
32 # include <sys/time.h>
33 # include <time.h>
34 #else
35 # ifdef TM_IN_SYS_TIME
36 # include <sys/time.h>
37 # else
38 # include <time.h>
39 # endif
40 #endif
41
42 #ifdef HAVE_SYS_WAIT_H
43 # include <sys/wait.h>
44 #endif
45
46
47 extern int debugsw;
48 extern int verbosw;
49
50 extern int ebcdicsw;
51 extern int listsw;
52 extern int rfc934sw;
53
54 extern int endian; /* mhmisc.c */
55
56 /* cache policies */
57 extern int rcachesw; /* mhcachesbr.c */
58 extern int wcachesw; /* mhcachesbr.c */
59
60 int checksw = 0; /* Add Content-MD5 field */
61
62 /*
63 * Directory to place tmp files. This must
64 * be set before these routines are called.
65 */
66 char *tmp;
67
68 pid_t xpid = 0;
69
70 static char prefix[] = "----- =_aaaaaaaaaa";
71
72 /*
73 * Structure for mapping types to their internal flags
74 */
75 struct k2v {
76 char *kv_key;
77 int kv_value;
78 };
79
80 /*
81 * Structures for TEXT messages
82 */
83 static struct k2v SubText[] = {
84 { "plain", TEXT_PLAIN },
85 { "richtext", TEXT_RICHTEXT }, /* defined in RFC-1341 */
86 { "enriched", TEXT_ENRICHED }, /* defined in RFC-1896 */
87 { NULL, TEXT_UNKNOWN } /* this one must be last! */
88 };
89
90 static struct k2v Charset[] = {
91 { "us-ascii", CHARSET_USASCII },
92 { "iso-8859-1", CHARSET_LATIN },
93 { NULL, CHARSET_UNKNOWN } /* this one must be last! */
94 };
95
96 /*
97 * Structures for MULTIPART messages
98 */
99 static struct k2v SubMultiPart[] = {
100 { "mixed", MULTI_MIXED },
101 { "alternative", MULTI_ALTERNATE },
102 { "digest", MULTI_DIGEST },
103 { "parallel", MULTI_PARALLEL },
104 { NULL, MULTI_UNKNOWN } /* this one must be last! */
105 };
106
107 /*
108 * Structures for MESSAGE messages
109 */
110 static struct k2v SubMessage[] = {
111 { "rfc822", MESSAGE_RFC822 },
112 { "partial", MESSAGE_PARTIAL },
113 { "external-body", MESSAGE_EXTERNAL },
114 { NULL, MESSAGE_UNKNOWN } /* this one must be last! */
115 };
116
117 /*
118 * Structure for APPLICATION messages
119 */
120 static struct k2v SubApplication[] = {
121 { "octet-stream", APPLICATION_OCTETS },
122 { "postscript", APPLICATION_POSTSCRIPT },
123 { NULL, APPLICATION_UNKNOWN } /* this one must be last! */
124 };
125
126
127 /* mhmisc.c */
128 int make_intermediates (char *);
129 void content_error (char *, CT, char *, ...);
130
131 /* mhcachesbr.c */
132 int find_cache (CT, int, int *, char *, char *, int);
133
134 /* ftpsbr.c */
135 int ftp_get (char *, char *, char *, char *, char *, char *, int, int);
136
137 /* mhfree.c */
138 void free_content (CT);
139 void free_ctinfo (CT);
140 void free_encoding (CT, int);
141
142 /*
143 * prototypes
144 */
145 CT build_mime (char *);
146 int pidcheck (int);
147
148 /*
149 * static prototypes
150 */
151 static CT get_content (FILE *, char *, int);
152 static int add_header (CT, char *, char *);
153 static int get_ctinfo (char *, CT, int);
154 static int get_comment (CT, char **, int);
155 static int InitGeneric (CT);
156 static int InitText (CT);
157 static int InitMultiPart (CT);
158 static void reverse_parts (CT);
159 static int InitMessage (CT);
160 static int params_external (CT, int);
161 static int InitApplication (CT);
162 static int init_decoded_content (CT);
163 static int init_encoding (CT, OpenCEFunc);
164 static void close_encoding (CT);
165 static unsigned long size_encoding (CT);
166 static int InitBase64 (CT);
167 static int openBase64 (CT, char **);
168 static int InitQuoted (CT);
169 static int openQuoted (CT, char **);
170 static int Init7Bit (CT);
171 static int open7Bit (CT, char **);
172 static int openExternal (CT, CT, CE, char **, int *);
173 static int InitFile (CT);
174 static int openFile (CT, char **);
175 static int InitFTP (CT);
176 static int openFTP (CT, char **);
177 static int InitMail (CT);
178 static int openMail (CT, char **);
179 static char *fgetstr (char *, int, FILE *);
180 static int user_content (FILE *, char *, char *, CT *);
181 static void set_id (CT, int);
182 static int compose_content (CT);
183 static int scan_content (CT);
184 static int build_headers (CT);
185 static char *calculate_digest (CT, int);
186 static int readDigest (CT, char *);
187
188 /*
189 * Structures for mapping (content) types to
190 * the functions to handle them.
191 */
192 struct str2init {
193 char *si_key;
194 int si_val;
195 InitFunc si_init;
196 };
197
198 static struct str2init str2cts[] = {
199 { "application", CT_APPLICATION, InitApplication },
200 { "audio", CT_AUDIO, InitGeneric },
201 { "image", CT_IMAGE, InitGeneric },
202 { "message", CT_MESSAGE, InitMessage },
203 { "multipart", CT_MULTIPART, InitMultiPart },
204 { "text", CT_TEXT, InitText },
205 { "video", CT_VIDEO, InitGeneric },
206 { NULL, CT_EXTENSION, NULL }, /* these two must be last! */
207 { NULL, CT_UNKNOWN, NULL },
208 };
209
210 static struct str2init str2ces[] = {
211 { "base64", CE_BASE64, InitBase64 },
212 { "quoted-printable", CE_QUOTED, InitQuoted },
213 { "8bit", CE_8BIT, Init7Bit },
214 { "7bit", CE_7BIT, Init7Bit },
215 { "binary", CE_BINARY, NULL },
216 { NULL, CE_EXTENSION, NULL }, /* these two must be last! */
217 { NULL, CE_UNKNOWN, NULL },
218 };
219
220 /*
221 * NOTE WELL: si_key MUST NOT have value of NOTOK
222 *
223 * si_key is 1 if access method is anonymous.
224 */
225 static struct str2init str2methods[] = {
226 { "afs", 1, InitFile },
227 { "anon-ftp", 1, InitFTP },
228 { "ftp", 0, InitFTP },
229 { "local-file", 0, InitFile },
230 { "mail-server", 0, InitMail },
231 { NULL, 0, NULL }
232 };
233
234
235 int
236 pidcheck (int status)
237 {
238 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
239 return status;
240
241 fflush (stdout);
242 fflush (stderr);
243 return done (1);
244 }
245
246
247 /*
248 * Main routine for translating composition file
249 * into valid MIME message. It translates the draft
250 * into a content structure (actually a tree of content
251 * structures). This message then can be manipulated
252 * in various ways, including being output via
253 * output_message().
254 */
255
256 CT
257 build_mime (char *infile)
258 {
259 int compnum, state;
260 char buf[BUFSIZ], name[NAMESZ];
261 char *cp, *np, *vp;
262 struct multipart *m;
263 struct part **pp;
264 CT ct;
265 FILE *in;
266
267 umask (~m_gmprot ());
268
269 /* open the composition draft */
270 if ((in = fopen (infile, "r")) == NULL)
271 adios (infile, "unable to open for reading");
272
273 /*
274 * Allocate space for primary (outside) content
275 */
276 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
277 adios (NULL, "out of memory");
278
279 /*
280 * Allocate structure for handling decoded content
281 * for this part. We don't really need this, but
282 * allocate it to remain consistent.
283 */
284 init_decoded_content (ct);
285
286 /*
287 * Parse some of the header fields in the composition
288 * draft into the linked list of header fields for
289 * the new MIME message.
290 */
291 for (compnum = 1, state = FLD;;) {
292 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
293 case FLD:
294 case FLDPLUS:
295 case FLDEOF:
296 compnum++;
297
298 /* abort if draft has Mime-Version header field */
299 if (!strcasecmp (name, VRSN_FIELD))
300 adios (NULL, "draft shouldn't contain %s: field", VRSN_FIELD);
301
302 /* abort if draft has Content-Transfer-Encoding header field */
303 if (!strcasecmp (name, ENCODING_FIELD))
304 adios (NULL, "draft shouldn't contain %s: field", ENCODING_FIELD);
305
306 /* ignore any Content-Type fields in the header */
307 if (!strcasecmp (name, TYPE_FIELD)) {
308 while (state == FLDPLUS)
309 state = m_getfld (state, name, buf, sizeof(buf), in);
310 goto finish_field;
311 }
312
313 /* get copies of the buffers */
314 np = add (name, NULL);
315 vp = add (buf, NULL);
316
317 /* if necessary, get rest of field */
318 while (state == FLDPLUS) {
319 state = m_getfld (state, name, buf, sizeof(buf), in);
320 vp = add (buf, vp); /* add to previous value */
321 }
322
323 /* Now add the header data to the list */
324 add_header (ct, np, vp);
325
326 finish_field:
327 /* if this wasn't the last header field, then continue */
328 if (state != FLDEOF)
329 continue;
330 /* else fall... */
331
332 case FILEEOF:
333 adios (NULL, "draft has empty body -- no directives!");
334 /* NOTREACHED */
335
336 case BODY:
337 case BODYEOF:
338 fseek (in, (long) (-strlen (buf)), SEEK_CUR);
339 break;
340
341 case LENERR:
342 case FMTERR:
343 adios (NULL, "message format error in component #%d", compnum);
344
345 default:
346 adios (NULL, "getfld() returned %d", state);
347 }
348 break;
349 }
350
351 /*
352 * Now add the MIME-Version header field
353 * to the list of header fields.
354 */
355 np = add (VRSN_FIELD, NULL);
356 vp = concat (" ", VRSN_VALUE, "\n", NULL);
357 add_header (ct, np, vp);
358
359 /*
360 * We initally assume we will find multiple contents in the
361 * draft. So create a multipart/mixed content to hold everything.
362 * We can remove this later, if it is not needed.
363 */
364 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
365 done (1);
366 ct->c_type = CT_MULTIPART;
367 ct->c_subtype = MULTI_MIXED;
368 ct->c_file = add (infile, NULL);
369
370 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
371 adios (NULL, "out of memory");
372 ct->c_ctparams = (void *) m;
373 pp = &m->mp_parts;
374
375 /*
376 * read and parse the composition file
377 * and the directives it contains.
378 */
379 while (fgetstr (buf, sizeof(buf) - 1, in)) {
380 struct part *part;
381 CT p;
382
383 if (user_content (in, infile, buf, &p) == DONE) {
384 admonish (NULL, "ignoring spurious #end");
385 continue;
386 }
387 if (!p)
388 continue;
389
390 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
391 adios (NULL, "out of memory");
392 *pp = part;
393 pp = &part->mp_next;
394 part->mp_part = p;
395 }
396
397 /*
398 * close the composition draft since
399 * it's not needed any longer.
400 */
401 fclose (in);
402
403 /* check if any contents were found */
404 if (!m->mp_parts)
405 adios (NULL, "no content directives found");
406
407 /*
408 * If only one content was found, then remove and
409 * free the outer multipart content.
410 */
411 if (!m->mp_parts->mp_next) {
412 CT p;
413
414 p = m->mp_parts->mp_part;
415 m->mp_parts->mp_part = NULL;
416
417 /* move header fields */
418 p->c_first_hf = ct->c_first_hf;
419 p->c_last_hf = ct->c_last_hf;
420 ct->c_first_hf = NULL;
421 ct->c_last_hf = NULL;
422
423 free_content (ct);
424 ct = p;
425 } else {
426 set_id (ct, 1);
427 }
428
429 /*
430 * Fill out, or expand directives. Parse and execute
431 * commands specified by profile composition strings.
432 */
433 compose_content (ct);
434
435 if ((cp = strchr(prefix, 'a')) == NULL)
436 adios (NULL, "internal error(4)");
437
438 /*
439 * Scan the contents. Choose a transfer encoding, and
440 * check if prefix for multipart boundary clashes with
441 * any of the contents.
442 */
443 while (scan_content (ct) == NOTOK) {
444 if (*cp < 'z') {
445 (*cp)++;
446 } else {
447 if (*++cp == 0)
448 adios (NULL, "giving up trying to find a unique delimiter string");
449 else
450 (*cp)++;
451 }
452 }
453
454 /* Build the rest of the header field structures */
455 build_headers (ct);
456
457 return ct;
458 }
459
460
461 /*
462 * Main routine for reading/parsing the headers
463 * of a message content.
464 *
465 * toplevel = 1 # we are at the top level of the message
466 * toplevel = 0 # we are inside message type or multipart type
467 * # other than multipart/digest
468 * toplevel = -1 # we are inside multipart/digest
469 */
470
471 static CT
472 get_content (FILE *in, char *file, int toplevel)
473 {
474 int compnum, state;
475 char buf[BUFSIZ], name[NAMESZ];
476 CT ct;
477
478 if (!(ct = (CT) calloc (1, sizeof(*ct))))
479 adios (NULL, "out of memory");
480
481 ct->c_fp = in;
482 ct->c_file = add (file, NULL);
483 ct->c_begin = ftell (ct->c_fp) + 1;
484
485 /*
486 * Read the content headers
487 */
488 for (compnum = 1, state = FLD;;) {
489 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
490 case FLD:
491 case FLDPLUS:
492 case FLDEOF:
493 compnum++;
494
495 /* Get MIME-Version field */
496 if (!strcasecmp (name, VRSN_FIELD)) {
497 int ucmp;
498 char c, *cp, *dp;
499
500 cp = add (buf, NULL);
501 while (state == FLDPLUS) {
502 state = m_getfld (state, name, buf, sizeof(buf), in);
503 cp = add (buf, cp);
504 }
505
506 if (ct->c_vrsn) {
507 advise (NULL, "message %s has multiple %s: fields (%s)",
508 ct->c_file, VRSN_FIELD, dp = trimcpy (cp));
509 free (dp);
510 free (cp);
511 goto out;
512 }
513
514 ct->c_vrsn = cp;
515 while (isspace (*cp))
516 cp++;
517 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
518 *dp++ = ' ';
519 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
520 if (!isspace (*dp))
521 break;
522 *++dp = '\0';
523 if (debugsw)
524 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
525
526 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
527 goto out;
528
529 for (dp = cp; istoken (*dp); dp++)
530 continue;
531 c = *dp, *dp = '\0';
532 ucmp = !strcasecmp (cp, VRSN_VALUE);
533 *dp = c;
534 if (!ucmp)
535 admonish (NULL,
536 "message %s has unknown value for %s: field (%s)",
537 ct->c_file, VRSN_FIELD, cp);
538 goto got_header;
539 }
540
541 /* Get Content-Type field */
542 if (!strcasecmp (name, TYPE_FIELD)) {
543 char *cp;
544 struct str2init *s2i;
545 CI ci = &ct->c_ctinfo;
546
547 cp = add (buf, NULL);
548 while (state == FLDPLUS) {
549 state = m_getfld (state, name, buf, sizeof(buf), in);
550 cp = add (buf, cp);
551 }
552
553 /* Check if we've already seen a Content-Type header */
554 if (ct->c_ctline) {
555 char *dp = trimcpy (cp);
556
557 advise (NULL, "message %s has multiple %s: fields (%s)",
558 ct->c_file, TYPE_FIELD, dp);
559 free (dp);
560 free (cp);
561 goto out;
562 }
563
564 /* Parse the Content-Type field */
565 if (get_ctinfo (cp, ct, 0) == NOTOK)
566 goto out;
567
568 /*
569 * Set the Init function and the internal
570 * flag for this content type.
571 */
572 for (s2i = str2cts; s2i->si_key; s2i++)
573 if (!strcasecmp (ci->ci_type, s2i->si_key))
574 break;
575 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
576 s2i++;
577 ct->c_type = s2i->si_val;
578 ct->c_ctinitfnx = s2i->si_init;
579 goto got_header;
580 }
581
582 /* Get Content-Transfer-Encoding field */
583 if (!strcasecmp (name, ENCODING_FIELD)) {
584 char *cp, *dp;
585 char c;
586 struct str2init *s2i;
587
588 cp = add (buf, NULL);
589 while (state == FLDPLUS) {
590 state = m_getfld (state, name, buf, sizeof(buf), in);
591 cp = add (buf, cp);
592 }
593
594 /*
595 * Check if we've already seen the
596 * Content-Transfer-Encoding field
597 */
598 if (ct->c_celine) {
599 advise (NULL, "message %s has multiple %s: fields (%s)",
600 ct->c_file, ENCODING_FIELD, dp = trimcpy (cp));
601 free (dp);
602 free (cp);
603 goto out;
604 }
605
606 ct->c_celine = cp; /* Save copy of this field */
607 while (isspace (*cp))
608 cp++;
609 for (dp = cp; istoken (*dp); dp++)
610 continue;
611 c = *dp;
612 *dp = '\0';
613
614 /*
615 * Find the internal flag and Init function
616 * for this transfer encoding.
617 */
618 for (s2i = str2ces; s2i->si_key; s2i++)
619 if (!strcasecmp (cp, s2i->si_key))
620 break;
621 if (!s2i->si_key && !uprf (cp, "X-"))
622 s2i++;
623 *dp = c;
624 ct->c_encoding = s2i->si_val;
625
626 /* Call the Init function for this encoding */
627 if (s2i->si_init && (*s2i->si_init) (ct) == NOTOK)
628 goto out;
629 goto got_header;
630 }
631
632 /* Get Content-ID field */
633 if (!strcasecmp (name, ID_FIELD)) {
634 ct->c_id = add (buf, ct->c_id);
635 while (state == FLDPLUS) {
636 state = m_getfld (state, name, buf, sizeof(buf), in);
637 ct->c_id = add (buf, ct->c_id);
638 }
639 goto got_header;
640 }
641
642 /* Get Content-Description field */
643 if (!strcasecmp (name, DESCR_FIELD)) {
644 ct->c_descr = add (buf, ct->c_descr);
645 while (state == FLDPLUS) {
646 state = m_getfld (state, name, buf, sizeof(buf), in);
647 ct->c_descr = add (buf, ct->c_descr);
648 }
649 goto got_header;
650 }
651
652 /* Get Content-MD5 field */
653 if (!strcasecmp (name, MD5_FIELD)) {
654 char *cp, *dp, *ep;
655
656 cp = add (buf, NULL);
657 while (state == FLDPLUS) {
658 state = m_getfld (state, name, buf, sizeof(buf), in);
659 cp = add (buf, cp);
660 }
661
662 if (!checksw) {
663 free (cp);
664 goto got_header;
665 }
666
667 if (ct->c_digested) {
668 advise (NULL, "message %s has multiple %s: fields (%s)",
669 ct->c_file, MD5_FIELD, dp = trimcpy (cp));
670 free (dp);
671 free (cp);
672 goto out;
673 }
674
675 ep = cp;
676 while (isspace (*cp))
677 cp++;
678 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
679 *dp++ = ' ';
680 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
681 if (!isspace (*dp))
682 break;
683 *++dp = '\0';
684 if (debugsw)
685 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
686
687 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
688 free (ep);
689 goto out;
690 }
691
692 for (dp = cp; *dp && !isspace (*dp); dp++)
693 continue;
694 *dp = '\0';
695
696 readDigest (ct, cp);
697 free (ep);
698 ct->c_digested++;
699 goto got_header;
700 }
701
702 #if 0
703 if (uprf (name, XXX_FIELD_PRF))
704 advise (NULL, "unknown field (%s) in message %s",
705 name, ct->c_file);
706 /* and fall... */
707 #endif
708
709 while (state == FLDPLUS)
710 state = m_getfld (state, name, buf, sizeof(buf), in);
711
712 got_header:
713 if (state != FLDEOF) {
714 ct->c_begin = ftell (in) + 1;
715 continue;
716 }
717 /* else fall... */
718
719 case BODY:
720 case BODYEOF:
721 ct->c_begin = ftell (in) - strlen (buf);
722 break;
723
724 case FILEEOF:
725 ct->c_begin = ftell (in);
726 break;
727
728 case LENERR:
729 case FMTERR:
730 adios (NULL, "message format error in component #%d", compnum);
731
732 default:
733 adios (NULL, "getfld() returned %d", state);
734 }
735 break;
736 }
737
738 /*
739 * Check if we saw a Content-Type field.
740 * If not, then assign a default value for
741 * it, and the Init function.
742 */
743 if (!ct->c_ctline) {
744 /*
745 * If we are inside a multipart/digest message,
746 * so default type is message/rfc822
747 */
748 if (toplevel < 0) {
749 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
750 goto out;
751 ct->c_type = CT_MESSAGE;
752 ct->c_ctinitfnx = InitMessage;
753 } else {
754 /*
755 * Else default type is text/plain
756 */
757 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
758 goto out;
759 ct->c_type = CT_TEXT;
760 ct->c_ctinitfnx = InitText;
761 }
762 }
763
764 /* Use default Transfer-Encoding, if necessary */
765 if (!ct->c_celine) {
766 ct->c_encoding = CE_7BIT;
767 Init7Bit (ct);
768 }
769
770 return ct;
771
772 out:
773 free_content (ct);
774 return NULL;
775 }
776
777
778 /*
779 * small routine to add header field to list
780 */
781
782 static int
783 add_header (CT ct, char *name, char *value)
784 {
785 HF hp;
786
787 /* allocate header field structure */
788 if (!(hp = malloc (sizeof(*hp))))
789 adios (NULL, "out of memory");
790
791 /* link data into header structure */
792 hp->name = name;
793 hp->value = value;
794 hp->next = NULL;
795
796 /* link header structure into the list */
797 if (ct->c_first_hf == NULL) {
798 ct->c_first_hf = hp; /* this is the first */
799 ct->c_last_hf = hp;
800 } else {
801 ct->c_last_hf->next = hp; /* add it to the end */
802 ct->c_last_hf = hp;
803 }
804
805 return 0;
806 }
807
808
809 /*
810 * Used to parse both:
811 * 1) Content-Type line
812 * 2) composition directives
813 *
814 * and fills in the information of the CTinfo structure.
815 */
816
817 static int
818 get_ctinfo (char *cp, CT ct, int magic)
819 {
820 int i;
821 char *dp, **ap, **ep;
822 char c;
823 CI ci;
824
825 ci = &ct->c_ctinfo;
826 i = strlen (invo_name) + 2;
827
828 /* store copy of Content-Type line */
829 cp = ct->c_ctline = add (cp, NULL);
830
831 while (isspace (*cp)) /* trim leading spaces */
832 cp++;
833
834 /* change newlines to spaces */
835 for (dp = strchr(cp, '\n'); dp; dp = strchr(dp, '\n'))
836 *dp++ = ' ';
837
838 /* trim trailing spaces */
839 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
840 if (!isspace (*dp))
841 break;
842 *++dp = '\0';
843
844 if (debugsw)
845 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
846
847 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
848 return NOTOK;
849
850 for (dp = cp; istoken (*dp); dp++)
851 continue;
852 c = *dp, *dp = '\0';
853 ci->ci_type = add (cp, NULL); /* store content type */
854 *dp = c, cp = dp;
855
856 if (!*ci->ci_type) {
857 advise (NULL, "invalid %s: field in message %s (empty type)",
858 TYPE_FIELD, ct->c_file);
859 return NOTOK;
860 }
861
862 /* down case the content type string */
863 for (dp = ci->ci_type; *dp; dp++)
864 if (isalpha(*dp) && isupper (*dp))
865 *dp = tolower (*dp);
866
867 while (isspace (*cp))
868 cp++;
869
870 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
871 return NOTOK;
872
873 if (*cp != '/') {
874 if (!magic)
875 ci->ci_subtype = add ("", NULL);
876 goto magic_skip;
877 }
878
879 cp++;
880 while (isspace (*cp))
881 cp++;
882
883 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
884 return NOTOK;
885
886 for (dp = cp; istoken (*dp); dp++)
887 continue;
888 c = *dp, *dp = '\0';
889 ci->ci_subtype = add (cp, NULL); /* store the content subtype */
890 *dp = c, cp = dp;
891
892 if (!*ci->ci_subtype) {
893 advise (NULL,
894 "invalid %s: field in message %s (empty subtype for \"%s\")",
895 TYPE_FIELD, ct->c_file, ci->ci_type);
896 return NOTOK;
897 }
898
899 /* down case the content subtype string */
900 for (dp = ci->ci_subtype; *dp; dp++)
901 if (isalpha(*dp) && isupper (*dp))
902 *dp = tolower (*dp);
903
904 magic_skip:
905 while (isspace (*cp))
906 cp++;
907
908 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
909 return NOTOK;
910
911 /*
912 * Parse attribute/value pairs given with Content-Type
913 */
914 ep = (ap = ci->ci_attrs) + NPARMS;
915 while (*cp == ';') {
916 char *vp, *up;
917
918 if (ap >= ep) {
919 advise (NULL,
920 "too many parameters in message %s's %s: field (%d max)",
921 ct->c_file, TYPE_FIELD, NPARMS);
922 return NOTOK;
923 }
924
925 cp++;
926 while (isspace (*cp))
927 cp++;
928
929 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
930 return NOTOK;
931
932 if (*cp == 0) {
933 advise (NULL,
934 "extraneous trailing ';' in message %s's %s: parameter list",
935 ct->c_file, TYPE_FIELD);
936 return OK;
937 }
938
939 /* down case the attribute name */
940 for (dp = cp; istoken (*dp); dp++)
941 if (isalpha(*dp) && isupper (*dp))
942 *dp = tolower (*dp);
943
944 for (up = dp; isspace (*dp); )
945 dp++;
946 if (dp == cp || *dp != '=') {
947 advise (NULL,
948 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
949 ct->c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
950 return NOTOK;
951 }
952
953 vp = (*ap = add (cp, NULL)) + (up - cp);
954 *vp = '\0';
955 for (dp++; isspace (*dp); )
956 dp++;
957
958 /* now add the attribute value */
959 ci->ci_values[ap - ci->ci_attrs] = vp = *ap + (dp - cp);
960
961 if (*dp == '"') {
962 for (cp = ++dp, dp = vp;;) {
963 switch (c = *cp++) {
964 case '\0':
965 bad_quote:
966 advise (NULL,
967 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
968 ct->c_file, TYPE_FIELD, i, i, "", *ap);
969 return NOTOK;
970
971 case '\\':
972 *dp++ = c;
973 if ((c = *cp++) == '\0')
974 goto bad_quote;
975 /* else fall... */
976
977 default:
978 *dp++ = c;
979 continue;
980
981 case '"':
982 *dp = '\0';
983 break;
984 }
985 break;
986 }
987 } else {
988 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
989 continue;
990 *dp = '\0';
991 }
992 if (!*vp) {
993 advise (NULL,
994 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
995 ct->c_file, TYPE_FIELD, i, i, "", *ap);
996 return NOTOK;
997 }
998 ap++;
999
1000 while (isspace (*cp))
1001 cp++;
1002
1003 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1004 return NOTOK;
1005 }
1006
1007 /*
1008 * Get any <Content-Id> given in buffer
1009 */
1010 if (magic && *cp == '<') {
1011 if (ct->c_id) {
1012 free (ct->c_id);
1013 ct->c_id = NULL;
1014 }
1015 if (!(dp = strchr(ct->c_id = ++cp, '>'))) {
1016 advise (NULL, "invalid ID in message %s", ct->c_file);
1017 return NOTOK;
1018 }
1019 c = *dp;
1020 *dp = '\0';
1021 if (*ct->c_id)
1022 ct->c_id = concat ("<", ct->c_id, ">\n", NULL);
1023 else
1024 ct->c_id = NULL;
1025 *dp++ = c;
1026 cp = dp;
1027
1028 while (isspace (*cp))
1029 cp++;
1030 }
1031
1032 /*
1033 * Get any [Content-Description] given in buffer.
1034 */
1035 if (magic && *cp == '[') {
1036 ct->c_descr = ++cp;
1037 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1038 if (*dp == ']')
1039 break;
1040 if (dp < cp) {
1041 advise (NULL, "invalid description in message %s", ct->c_file);
1042 ct->c_descr = NULL;
1043 return NOTOK;
1044 }
1045
1046 c = *dp;
1047 *dp = '\0';
1048 if (*ct->c_descr)
1049 ct->c_descr = concat (ct->c_descr, "\n", NULL);
1050 else
1051 ct->c_descr = NULL;
1052 *dp++ = c;
1053 cp = dp;
1054
1055 while (isspace (*cp))
1056 cp++;
1057 }
1058
1059 /*
1060 * Check if anything is left over
1061 */
1062 if (*cp) {
1063 if (magic)
1064 ci->ci_magic = add (cp, NULL);
1065 else
1066 advise (NULL,
1067 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1068 ct->c_file, TYPE_FIELD, i, i, "", cp);
1069 }
1070
1071 return OK;
1072 }
1073
1074
1075 static int
1076 get_comment (CT ct, char **ap, int istype)
1077 {
1078 int i;
1079 char *bp, *cp;
1080 char c, buffer[BUFSIZ], *dp;
1081 CI ci;
1082
1083 ci = &ct->c_ctinfo;
1084 cp = *ap;
1085 bp = buffer;
1086 cp++;
1087
1088 for (i = 0;;) {
1089 switch (c = *cp++) {
1090 case '\0':
1091 invalid:
1092 advise (NULL, "invalid comment in message %s's %s: field",
1093 ct->c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1094 return NOTOK;
1095
1096 case '\\':
1097 *bp++ = c;
1098 if ((c = *cp++) == '\0')
1099 goto invalid;
1100 *bp++ = c;
1101 continue;
1102
1103 case '(':
1104 i++;
1105 /* and fall... */
1106 default:
1107 *bp++ = c;
1108 continue;
1109
1110 case ')':
1111 if (--i < 0)
1112 break;
1113 *bp++ = c;
1114 continue;
1115 }
1116 break;
1117 }
1118 *bp = '\0';
1119
1120 if (istype) {
1121 if ((dp = ci->ci_comment)) {
1122 ci->ci_comment = concat (dp, " ", buffer, NULL);
1123 free (dp);
1124 } else {
1125 ci->ci_comment = add (buffer, NULL);
1126 }
1127 }
1128
1129 while (isspace (*cp))
1130 cp++;
1131
1132 *ap = cp;
1133 return OK;
1134 }
1135
1136
1137 /*
1138 * CONTENTS
1139 *
1140 * Handles content types audio, image, and video.
1141 * There's not much to do right here.
1142 */
1143
1144 static int
1145 InitGeneric (CT ct)
1146 {
1147 return OK; /* not much to do here */
1148 }
1149
1150
1151 /*
1152 * TEXT
1153 */
1154
1155 static int
1156 InitText (CT ct)
1157 {
1158 char **ap, **ep;
1159 struct k2v *kv;
1160 struct text *t;
1161 CI ci = &ct->c_ctinfo;
1162
1163 /* check for missing subtype */
1164 if (!*ci->ci_subtype)
1165 ci->ci_subtype = add ("plain", ci->ci_subtype);
1166
1167 /* match subtype */
1168 for (kv = SubText; kv->kv_key; kv++)
1169 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1170 break;
1171 ct->c_subtype = kv->kv_value;
1172
1173 /* allocate text character set structure */
1174 if ((t = (struct text *) calloc (1, sizeof(*t))) == NULL)
1175 adios (NULL, "out of memory");
1176 ct->c_ctparams = (void *) t;
1177
1178 /* initially mark character set as unspecified */
1179 t->tx_charset = CHARSET_UNSPECIFIED;
1180
1181 /* scan for charset parameter */
1182 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
1183 if (!strcasecmp (*ap, "charset"))
1184 break;
1185
1186 /* check if content specified a character set */
1187 if (*ap) {
1188 /* match character set or set to CHARSET_UNKNOWN */
1189 for (kv = Charset; kv->kv_key; kv++)
1190 if (!strcasecmp (*ep, kv->kv_key))
1191 break;
1192 t->tx_charset = kv->kv_value;
1193 }
1194
1195 return OK;
1196 }
1197
1198
1199 /*
1200 * MULTIPART
1201 */
1202
1203 static int
1204 InitMultiPart (CT ct)
1205 {
1206 int inout;
1207 long last, pos;
1208 char *cp, *dp, **ap, **ep;
1209 char *bp, buffer[BUFSIZ];
1210 struct multipart *m;
1211 struct k2v *kv;
1212 struct part *part, **next;
1213 CI ci = &ct->c_ctinfo;
1214 CT p;
1215 FILE *fp;
1216
1217 /*
1218 * The encoding for multipart messages must be either
1219 * 7bit, 8bit, or binary (per RFC2045).
1220 */
1221 if (ct->c_encoding != CE_7BIT && ct->c_encoding != CE_8BIT
1222 && ct->c_encoding != CE_BINARY) {
1223 admonish (NULL,
1224 "\"%s/%s\" type in message %s must be encoded in 7bit, 8bit, or binary",
1225 ci->ci_type, ci->ci_subtype, ct->c_file);
1226 return NOTOK;
1227 }
1228
1229 /* match subtype */
1230 for (kv = SubMultiPart; kv->kv_key; kv++)
1231 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1232 break;
1233 ct->c_subtype = kv->kv_value;
1234
1235 /*
1236 * Check for "boundary" parameter, which is
1237 * required for multipart messages.
1238 */
1239 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1240 if (!strcasecmp (*ap, "boundary")) {
1241 bp = *ep;
1242 break;
1243 }
1244 }
1245
1246 /* complain if boundary parameter is missing */
1247 if (!*ap) {
1248 advise (NULL,
1249 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
1250 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1251 return NOTOK;
1252 }
1253
1254 /* allocate primary structure for multipart info */
1255 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
1256 adios (NULL, "out of memory");
1257 ct->c_ctparams = (void *) m;
1258
1259 /* check if boundary parameter contains only whitespace characters */
1260 for (cp = bp; isspace (*cp); cp++)
1261 continue;
1262 if (!*cp) {
1263 advise (NULL, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
1264 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1265 return NOTOK;
1266 }
1267
1268 /* remove trailing whitespace from boundary parameter */
1269 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
1270 if (!isspace (*dp))
1271 break;
1272 *++dp = '\0';
1273
1274 /* record boundary separators */
1275 m->mp_start = concat (bp, "\n", NULL);
1276 m->mp_stop = concat (bp, "--\n", NULL);
1277
1278 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1279 advise (ct->c_file, "unable to open for reading");
1280 return NOTOK;
1281 }
1282
1283 fseek (fp = ct->c_fp, pos = ct->c_begin, SEEK_SET);
1284 last = ct->c_end;
1285 next = &m->mp_parts;
1286 part = NULL;
1287 inout = 1;
1288
1289 while (fgets (buffer, sizeof(buffer) - 1, fp)) {
1290 if (pos > last)
1291 break;
1292
1293 pos += strlen (buffer);
1294 if (buffer[0] != '-' || buffer[1] != '-')
1295 continue;
1296 if (inout) {
1297 if (strcmp (buffer + 2, m->mp_start))
1298 continue;
1299 next_part:
1300 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
1301 adios (NULL, "out of memory");
1302 *next = part;
1303 next = &part->mp_next;
1304
1305 if (!(p = get_content (fp, ct->c_file,
1306 rfc934sw && ct->c_subtype == MULTI_DIGEST ? -1 : 0))) {
1307 fclose (ct->c_fp);
1308 ct->c_fp = NULL;
1309 return NOTOK;
1310 }
1311 p->c_fp = NULL;
1312 part->mp_part = p;
1313 pos = p->c_begin;
1314 fseek (fp, pos, SEEK_SET);
1315 inout = 0;
1316 } else {
1317 if (strcmp (buffer + 2, m->mp_start) == 0) {
1318 inout = 1;
1319 end_part:
1320 p = part->mp_part;
1321 p->c_end = ftell(fp) - (strlen(buffer) + 1);
1322 if (p->c_end < p->c_begin)
1323 p->c_begin = p->c_end;
1324 if (inout)
1325 goto next_part;
1326 goto last_part;
1327 } else {
1328 if (strcmp (buffer + 2, m->mp_stop) == 0)
1329 goto end_part;
1330 }
1331 }
1332 }
1333
1334 advise (NULL, "bogus multipart content in message %s", ct->c_file);
1335 if (!inout && part) {
1336 p = part->mp_part;
1337 p->c_end = ct->c_end;
1338
1339 if (p->c_begin >= p->c_end) {
1340 for (next = &m->mp_parts; *next != part;
1341 next = &((*next)->mp_next))
1342 continue;
1343 *next = NULL;
1344 free_content (p);
1345 free ((char *) part);
1346 }
1347 }
1348
1349 last_part:
1350 /* reverse the order of the parts for multipart/alternative */
1351 if (ct->c_subtype == MULTI_ALTERNATE)
1352 reverse_parts (ct);
1353
1354 /*
1355 * label all subparts with part number, and
1356 * then initialize the content of the subpart.
1357 */
1358 {
1359 int partnum;
1360 char *pp;
1361 char partnam[BUFSIZ];
1362
1363 if (ct->c_partno) {
1364 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
1365 pp = partnam + strlen (partnam);
1366 } else {
1367 pp = partnam;
1368 }
1369
1370 for (part = m->mp_parts, partnum = 1; part;
1371 part = part->mp_next, partnum++) {
1372 p = part->mp_part;
1373
1374 sprintf (pp, "%d", partnum);
1375 p->c_partno = add (partnam, NULL);
1376
1377 /* initialize the content of the subparts */
1378 if (p->c_ctinitfnx && (*p->c_ctinitfnx) (p) == NOTOK) {
1379 fclose (ct->c_fp);
1380 ct->c_fp = NULL;
1381 return NOTOK;
1382 }
1383 }
1384 }
1385
1386 fclose (ct->c_fp);
1387 ct->c_fp = NULL;
1388 return OK;
1389 }
1390
1391
1392 /*
1393 * reverse the order of the parts of a multipart
1394 */
1395
1396 static void
1397 reverse_parts (CT ct)
1398 {
1399 int i;
1400 struct multipart *m;
1401 struct part **base, **bmp, **next, *part;
1402
1403 m = (struct multipart *) ct->c_ctparams;
1404
1405 /* if only one part, just return */
1406 if (!m->mp_parts || !m->mp_parts->mp_next)
1407 return;
1408
1409 /* count number of parts */
1410 i = 0;
1411 for (part = m->mp_parts; part; part = part->mp_next)
1412 i++;
1413
1414 /* allocate array of pointers to the parts */
1415 if (!(base = (struct part **) calloc ((size_t) (i + 1), sizeof(*base))))
1416 adios (NULL, "out of memory");
1417 bmp = base;
1418
1419 /* point at all the parts */
1420 for (part = m->mp_parts; part; part = part->mp_next)
1421 *bmp++ = part;
1422 *bmp = NULL;
1423
1424 /* reverse the order of the parts */
1425 next = &m->mp_parts;
1426 for (bmp--; bmp >= base; bmp--) {
1427 part = *bmp;
1428 *next = part;
1429 next = &part->mp_next;
1430 }
1431 *next = NULL;
1432
1433 /* free array of pointers */
1434 free ((char *) base);
1435 }
1436
1437
1438 /*
1439 * MESSAGE
1440 */
1441
1442 static int
1443 InitMessage (CT ct)
1444 {
1445 struct k2v *kv;
1446 CI ci = &ct->c_ctinfo;
1447
1448 if ((ct->c_encoding != CE_7BIT) && (ct->c_encoding != CE_8BIT)) {
1449 admonish (NULL,
1450 "\"%s/%s\" type in message %s should be encoded in 7bit or 8bit",
1451 ci->ci_type, ci->ci_subtype, ct->c_file);
1452 return NOTOK;
1453 }
1454
1455 /* check for missing subtype */
1456 if (!*ci->ci_subtype)
1457 ci->ci_subtype = add ("rfc822", ci->ci_subtype);
1458
1459 /* match subtype */
1460 for (kv = SubMessage; kv->kv_key; kv++)
1461 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1462 break;
1463 ct->c_subtype = kv->kv_value;
1464
1465 switch (ct->c_subtype) {
1466 case MESSAGE_RFC822:
1467 break;
1468
1469 case MESSAGE_PARTIAL:
1470 {
1471 char **ap, **ep;
1472 struct partial *p;
1473
1474 if ((p = (struct partial *) calloc (1, sizeof(*p))) == NULL)
1475 adios (NULL, "out of memory");
1476 ct->c_ctparams = (void *) p;
1477
1478 /* scan for parameters "id", "number", and "total" */
1479 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1480 if (!strcasecmp (*ap, "id")) {
1481 p->pm_partid = add (*ep, NULL);
1482 continue;
1483 }
1484 if (!strcasecmp (*ap, "number")) {
1485 if (sscanf (*ep, "%d", &p->pm_partno) != 1
1486 || p->pm_partno < 1) {
1487 invalid_param:
1488 advise (NULL,
1489 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
1490 *ap, ci->ci_type, ci->ci_subtype,
1491 ct->c_file, TYPE_FIELD);
1492 return NOTOK;
1493 }
1494 continue;
1495 }
1496 if (!strcasecmp (*ap, "total")) {
1497 if (sscanf (*ep, "%d", &p->pm_maxno) != 1
1498 || p->pm_maxno < 1)
1499 goto invalid_param;
1500 continue;
1501 }
1502 }
1503
1504 if (!p->pm_partid
1505 || !p->pm_partno
1506 || (p->pm_maxno && p->pm_partno > p->pm_maxno)) {
1507 advise (NULL,
1508 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1509 ci->ci_type, ci->ci_subtype,
1510 ct->c_file, TYPE_FIELD);
1511 return NOTOK;
1512 }
1513 }
1514 break;
1515
1516 case MESSAGE_EXTERNAL:
1517 {
1518 int exresult;
1519 struct exbody *e;
1520 CT p;
1521 FILE *fp;
1522
1523 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
1524 adios (NULL, "out of memory");
1525 ct->c_ctparams = (void *) e;
1526
1527 if (!ct->c_fp
1528 && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1529 advise (ct->c_file, "unable to open for reading");
1530 return NOTOK;
1531 }
1532
1533 fseek (fp = ct->c_fp, ct->c_begin, SEEK_SET);
1534
1535 if (!(p = get_content (fp, ct->c_file, 0))) {
1536 fclose (ct->c_fp);
1537 ct->c_fp = NULL;
1538 return NOTOK;
1539 }
1540
1541 e->eb_parent = ct;
1542 e->eb_content = p;
1543 p->c_ctexbody = e;
1544 if ((exresult = params_external (ct, 0)) != NOTOK
1545 && p->c_ceopenfnx == openMail) {
1546 int cc, size;
1547 char *bp;
1548
1549 if ((size = ct->c_end - p->c_begin) <= 0) {
1550 if (!e->eb_subject)
1551 content_error (NULL, ct,
1552 "empty body for access-type=mail-server");
1553 goto no_body;
1554 }
1555
1556 if ((e->eb_body = bp = malloc ((unsigned) size)) == NULL)
1557 adios (NULL, "out of memory");
1558 fseek (p->c_fp, p->c_begin, SEEK_SET);
1559 while (size > 0)
1560 switch (cc = fread (bp, sizeof(*bp), size, p->c_fp)) {
1561 case NOTOK:
1562 adios ("failed", "fread");
1563
1564 case OK:
1565 adios (NULL, "unexpected EOF from fread");
1566
1567 default:
1568 bp += cc, size -= cc;
1569 break;
1570 }
1571 *bp = 0;
1572 }
1573 no_body:
1574 p->c_fp = NULL;
1575 p->c_end = p->c_begin;
1576
1577 fclose (ct->c_fp);
1578 ct->c_fp = NULL;
1579
1580 if (exresult == NOTOK)
1581 return NOTOK;
1582 if (e->eb_flags == NOTOK)
1583 return OK;
1584
1585 switch (p->c_type) {
1586 case CT_MULTIPART:
1587 break;
1588
1589 case CT_MESSAGE:
1590 if (p->c_subtype != MESSAGE_RFC822)
1591 break;
1592 /* else fall... */
1593 default:
1594 e->eb_partno = ct->c_partno;
1595 if (p->c_ctinitfnx)
1596 (*p->c_ctinitfnx) (p);
1597 break;
1598 }
1599 }
1600 break;
1601
1602 default:
1603 break;
1604 }
1605
1606 return OK;
1607 }
1608
1609
1610 static int
1611 params_external (CT ct, int composing)
1612 {
1613 char **ap, **ep;
1614 struct exbody *e = (struct exbody *) ct->c_ctparams;
1615 CI ci = &ct->c_ctinfo;
1616
1617 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
1618 if (!strcasecmp (*ap, "access-type")) {
1619 struct str2init *s2i;
1620 CT p = e->eb_content;
1621
1622 for (s2i = str2methods; s2i->si_key; s2i++)
1623 if (!strcasecmp (*ep, s2i->si_key))
1624 break;
1625
1626 if (!s2i->si_key) {
1627 e->eb_access = *ep;
1628 e->eb_flags = NOTOK;
1629 p->c_encoding = CE_EXTERNAL;
1630 continue;
1631 }
1632 e->eb_access = s2i->si_key;
1633 e->eb_flags = s2i->si_val;
1634 p->c_encoding = CE_EXTERNAL;
1635
1636 /* Call the Init function for this external type */
1637 if ((*s2i->si_init)(p) == NOTOK)
1638 return NOTOK;
1639 continue;
1640 }
1641 if (!strcasecmp (*ap, "name")) {
1642 e->eb_name = *ep;
1643 continue;
1644 }
1645 if (!strcasecmp (*ap, "permission")) {
1646 e->eb_permission = *ep;
1647 continue;
1648 }
1649 if (!strcasecmp (*ap, "site")) {
1650 e->eb_site = *ep;
1651 continue;
1652 }
1653 if (!strcasecmp (*ap, "directory")) {
1654 e->eb_dir = *ep;
1655 continue;
1656 }
1657 if (!strcasecmp (*ap, "mode")) {
1658 e->eb_mode = *ep;
1659 continue;
1660 }
1661 if (!strcasecmp (*ap, "size")) {
1662 sscanf (*ep, "%lu", &e->eb_size);
1663 continue;
1664 }
1665 if (!strcasecmp (*ap, "server")) {
1666 e->eb_server = *ep;
1667 continue;
1668 }
1669 if (!strcasecmp (*ap, "subject")) {
1670 e->eb_subject = *ep;
1671 continue;
1672 }
1673 if (composing && !strcasecmp (*ap, "body")) {
1674 e->eb_body = getcpy (*ep);
1675 continue;
1676 }
1677 }
1678
1679 if (!e->eb_access) {
1680 advise (NULL,
1681 "invalid parameters for \"%s/%s\" type in message %s's %s field",
1682 ci->ci_type, ci->ci_subtype, ct->c_file, TYPE_FIELD);
1683 return NOTOK;
1684 }
1685
1686 return OK;
1687 }
1688
1689
1690 /*
1691 * APPLICATION
1692 */
1693
1694 static int
1695 InitApplication (CT ct)
1696 {
1697 struct k2v *kv;
1698 CI ci = &ct->c_ctinfo;
1699
1700 /* match subtype */
1701 for (kv = SubApplication; kv->kv_key; kv++)
1702 if (!strcasecmp (ci->ci_subtype, kv->kv_key))
1703 break;
1704 ct->c_subtype = kv->kv_value;
1705
1706 return OK;
1707 }
1708
1709
1710 /*
1711 * Set up structures for placing unencoded
1712 * content when building parts.
1713 */
1714
1715 static int
1716 init_decoded_content (CT ct)
1717 {
1718 CE ce;
1719
1720 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1721 adios (NULL, "out of memory");
1722
1723 ct->c_cefile = ce;
1724 ct->c_ceopenfnx = open7Bit; /* since unencoded */
1725 ct->c_ceclosefnx = close_encoding;
1726 ct->c_cesizefnx = NULL; /* since unencoded */
1727
1728 return OK;
1729 }
1730
1731
1732 /*
1733 * TRANSFER ENCODINGS
1734 */
1735
1736 static int
1737 init_encoding (CT ct, OpenCEFunc openfnx)
1738 {
1739 CE ce;
1740
1741 if ((ce = (CE) calloc (1, sizeof(*ce))) == NULL)
1742 adios (NULL, "out of memory");
1743
1744 ct->c_cefile = ce;
1745 ct->c_ceopenfnx = openfnx;
1746 ct->c_ceclosefnx = close_encoding;
1747 ct->c_cesizefnx = size_encoding;
1748
1749 return OK;
1750 }
1751
1752
1753 static void
1754 close_encoding (CT ct)
1755 {
1756 CE ce;
1757
1758 if (!(ce = ct->c_cefile))
1759 return;
1760
1761 if (ce->ce_fp) {
1762 fclose (ce->ce_fp);
1763 ce->ce_fp = NULL;
1764 }
1765 }
1766
1767
1768 static unsigned long
1769 size_encoding (CT ct)
1770 {
1771 int fd;
1772 unsigned long size;
1773 char *file;
1774 CE ce;
1775 struct stat st;
1776
1777 if (!(ce = ct->c_cefile))
1778 return (ct->c_end - ct->c_begin);
1779
1780 if (ce->ce_fp && fstat (fileno (ce->ce_fp), &st) != NOTOK)
1781 return (long) st.st_size;
1782
1783 if (ce->ce_file) {
1784 if (stat (ce->ce_file, &st) != NOTOK)
1785 return (long) st.st_size;
1786 else
1787 return 0L;
1788 }
1789
1790 if (ct->c_encoding == CE_EXTERNAL)
1791 return (ct->c_end - ct->c_begin);
1792
1793 file = NULL;
1794 if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
1795 return (ct->c_end - ct->c_begin);
1796
1797 if (fstat (fd, &st) != NOTOK)
1798 size = (long) st.st_size;
1799 else
1800 size = 0L;
1801
1802 (*ct->c_ceclosefnx) (ct);
1803 return size;
1804 }
1805
1806
1807 /*
1808 * BASE64
1809 */
1810
1811 static unsigned char b642nib[0x80] = {
1812 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1813 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1814 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1815 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1816 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1817 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
1818 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
1819 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
1820 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
1821 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
1822 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
1823 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
1824 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
1825 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
1826 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
1827 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
1828 };
1829
1830
1831 static int
1832 InitBase64 (CT ct)
1833 {
1834 return init_encoding (ct, openBase64);
1835 }
1836
1837
1838 static int
1839 openBase64 (CT ct, char **file)
1840 {
1841 int bitno, cc, digested;
1842 int fd, len, skip;
1843 unsigned long bits;
1844 unsigned char value, *b, *b1, *b2, *b3;
1845 char *cp, *ep, buffer[BUFSIZ];
1846 CE ce;
1847 MD5_CTX mdContext;
1848
1849 b = (unsigned char *) &bits;
1850 b1 = &b[endian > 0 ? 1 : 2];
1851 b2 = &b[endian > 0 ? 2 : 1];
1852 b3 = &b[endian > 0 ? 3 : 0];
1853
1854 ce = ct->c_cefile;
1855 if (ce->ce_fp) {
1856 fseek (ce->ce_fp, 0L, SEEK_SET);
1857 goto ready_to_go;
1858 }
1859
1860 if (ce->ce_file) {
1861 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
1862 content_error (ce->ce_file, ct, "unable to fopen for reading");
1863 return NOTOK;
1864 }
1865 goto ready_to_go;
1866 }
1867
1868 if (*file == NULL) {
1869 ce->ce_file = add (m_scratch ("", tmp), NULL);
1870 ce->ce_unlink = 1;
1871 } else {
1872 ce->ce_file = add (*file, NULL);
1873 ce->ce_unlink = 0;
1874 }
1875
1876 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
1877 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
1878 return NOTOK;
1879 }
1880
1881 if ((len = ct->c_end - ct->c_begin) < 0)
1882 adios (NULL, "internal error(1)");
1883
1884 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
1885 content_error (ct->c_file, ct, "unable to open for reading");
1886 return NOTOK;
1887 }
1888
1889 if ((digested = ct->c_digested))
1890 MD5Init (&mdContext);
1891
1892 bitno = 18;
1893 bits = 0L;
1894 skip = 0;
1895
1896 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
1897 while (len > 0) {
1898 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
1899 case NOTOK:
1900 content_error (ct->c_file, ct, "error reading from");
1901 goto clean_up;
1902
1903 case OK:
1904 content_error (NULL, ct, "premature eof");
1905 goto clean_up;
1906
1907 default:
1908 if (cc > len)
1909 cc = len;
1910 len -= cc;
1911
1912 for (ep = (cp = buffer) + cc; cp < ep; cp++) {
1913 switch (*cp) {
1914 default:
1915 if (isspace (*cp))
1916 break;
1917 if (skip || (*cp & 0x80)
1918 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
1919 if (debugsw) {
1920 fprintf (stderr, "*cp=0x%x pos=%ld skip=%d\n",
1921 *cp,
1922 (long) (lseek (fd, (off_t) 0, SEEK_CUR) - (ep - cp)),
1923 skip);
1924 }
1925 content_error (NULL, ct,
1926 "invalid BASE64 encoding -- continuing");
1927 continue;
1928 }
1929
1930 bits |= value << bitno;
1931 test_end:
1932 if ((bitno -= 6) < 0) {
1933 putc ((char) *b1, ce->ce_fp);
1934 if (digested)
1935 MD5Update (&mdContext, b1, 1);
1936 if (skip < 2) {
1937 putc ((char) *b2, ce->ce_fp);
1938 if (digested)
1939 MD5Update (&mdContext, b2, 1);
1940 if (skip < 1) {
1941 putc ((char) *b3, ce->ce_fp);
1942 if (digested)
1943 MD5Update (&mdContext, b3, 1);
1944 }
1945 }
1946
1947 if (ferror (ce->ce_fp)) {
1948 content_error (ce->ce_file, ct,
1949 "error writing to");
1950 goto clean_up;
1951 }
1952 bitno = 18, bits = 0L, skip = 0;
1953 }
1954 break;
1955
1956 case '=':
1957 if (++skip > 3)
1958 goto self_delimiting;
1959 goto test_end;
1960 }
1961 }
1962 }
1963 }
1964
1965 if (bitno != 18) {
1966 if (debugsw)
1967 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
1968
1969 content_error (NULL, ct, "invalid BASE64 encoding");
1970 goto clean_up;
1971 }
1972
1973 self_delimiting:
1974 fseek (ct->c_fp, 0L, SEEK_SET);
1975
1976 if (fflush (ce->ce_fp)) {
1977 content_error (ce->ce_file, ct, "error writing to");
1978 goto clean_up;
1979 }
1980
1981 if (digested) {
1982 unsigned char digest[16];
1983
1984 MD5Final (digest, &mdContext);
1985 if (memcmp((char *) digest, (char *) ct->c_digest,
1986 sizeof(digest) / sizeof(digest[0])))
1987 content_error (NULL, ct,
1988 "content integrity suspect (digest mismatch) -- continuing");
1989 else
1990 if (debugsw)
1991 fprintf (stderr, "content integrity confirmed\n");
1992 }
1993
1994 fseek (ce->ce_fp, 0L, SEEK_SET);
1995
1996 ready_to_go:
1997 *file = ce->ce_file;
1998 return fileno (ce->ce_fp);
1999
2000 clean_up:
2001 free_encoding (ct, 0);
2002 return NOTOK;
2003 }
2004
2005
2006 /*
2007 * QUOTED PRINTABLE
2008 */
2009
2010 static char hex2nib[0x80] = {
2011 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2012 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2013 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2014 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2015 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2016 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2017 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
2018 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2019 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
2020 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2021 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2022 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2023 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
2024 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2025 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
2026 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
2027 };
2028
2029
2030 static int
2031 InitQuoted (CT ct)
2032 {
2033 return init_encoding (ct, openQuoted);
2034 }
2035
2036
2037 static int
2038 openQuoted (CT ct, char **file)
2039 {
2040 int cc, digested, len, quoted;
2041 char *cp, *ep;
2042 char buffer[BUFSIZ];
2043 unsigned char mask;
2044 CE ce;
2045 MD5_CTX mdContext;
2046
2047 ce = ct->c_cefile;
2048 if (ce->ce_fp) {
2049 fseek (ce->ce_fp, 0L, SEEK_SET);
2050 goto ready_to_go;
2051 }
2052
2053 if (ce->ce_file) {
2054 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2055 content_error (ce->ce_file, ct, "unable to fopen for reading");
2056 return NOTOK;
2057 }
2058 goto ready_to_go;
2059 }
2060
2061 if (*file == NULL) {
2062 ce->ce_file = add (m_scratch ("", tmp), NULL);
2063 ce->ce_unlink = 1;
2064 } else {
2065 ce->ce_file = add (*file, NULL);
2066 ce->ce_unlink = 0;
2067 }
2068
2069 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2070 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2071 return NOTOK;
2072 }
2073
2074 if ((len = ct->c_end - ct->c_begin) < 0)
2075 adios (NULL, "internal error(2)");
2076
2077 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2078 content_error (ct->c_file, ct, "unable to open for reading");
2079 return NOTOK;
2080 }
2081
2082 if ((digested = ct->c_digested))
2083 MD5Init (&mdContext);
2084
2085 quoted = 0;
2086 #ifdef lint
2087 mask = 0;
2088 #endif
2089
2090 fseek (ct->c_fp, ct->c_begin, SEEK_SET);
2091 while (len > 0) {
2092 char *dp;
2093
2094 if (fgets (buffer, sizeof(buffer) - 1, ct->c_fp) == NULL) {
2095 content_error (NULL, ct, "premature eof");
2096 goto clean_up;
2097 }
2098
2099 if ((cc = strlen (buffer)) > len)
2100 cc = len;
2101 len -= cc;
2102
2103 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
2104 if (!isspace (*ep))
2105 break;
2106 *++ep = '\n', ep++;
2107
2108 for (; cp < ep; cp++) {
2109 if (quoted) {
2110 if (quoted > 1) {
2111 if (!isxdigit (*cp)) {
2112 invalid_hex:
2113 dp = "expecting hexidecimal-digit";
2114 goto invalid_encoding;
2115 }
2116 mask <<= 4;
2117 mask |= hex2nib[*cp & 0x7f];
2118 putc (mask, ce->ce_fp);
2119 if (digested)
2120 MD5Update (&mdContext, &mask, 1);
2121 } else {
2122 switch (*cp) {
2123 case ':':
2124 putc (*cp, ce->ce_fp);
2125 if (digested)
2126 MD5Update (&mdContext, (unsigned char *) ":", 1);
2127 break;
2128
2129 default:
2130 if (!isxdigit (*cp))
2131 goto invalid_hex;
2132 mask = hex2nib[*cp & 0x7f];
2133 quoted = 2;
2134 continue;
2135 }
2136 }
2137
2138 if (ferror (ce->ce_fp)) {
2139 content_error (ce->ce_file, ct, "error writing to");
2140 goto clean_up;
2141 }
2142 quoted = 0;
2143 continue;
2144 }
2145
2146 switch (*cp) {
2147 default:
2148 if (*cp < '!' || *cp > '~') {
2149 int i;
2150 dp = "expecting character in range [!..~]";
2151
2152 invalid_encoding:
2153 i = strlen (invo_name) + 2;
2154 content_error (NULL, ct,
2155 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
2156 dp, i, i, "", *cp);
2157 goto clean_up;
2158 }
2159 /* and fall...*/
2160 case ' ':
2161 case '\t':
2162 case '\n':
2163 putc (*cp, ce->ce_fp);
2164 if (digested) {
2165 if (*cp == '\n')
2166 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
2167 else
2168 MD5Update (&mdContext, (unsigned char *) cp, 1);
2169 }
2170 if (ferror (ce->ce_fp)) {
2171 content_error (ce->ce_file, ct, "error writing to");
2172 goto clean_up;
2173 }
2174 break;
2175
2176 case '=':
2177 if (*++cp != '\n') {
2178 quoted = 1;
2179 cp--;
2180 }
2181 break;
2182 }
2183 }
2184 }
2185 if (quoted) {
2186 content_error (NULL, ct,
2187 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
2188 goto clean_up;
2189 }
2190
2191 fseek (ct->c_fp, 0L, SEEK_SET);
2192
2193 if (fflush (ce->ce_fp)) {
2194 content_error (ce->ce_file, ct, "error writing to");
2195 goto clean_up;
2196 }
2197
2198 if (digested) {
2199 unsigned char digest[16];
2200
2201 MD5Final (digest, &mdContext);
2202 if (memcmp((char *) digest, (char *) ct->c_digest,
2203 sizeof(digest) / sizeof(digest[0])))
2204 content_error (NULL, ct,
2205 "content integrity suspect (digest mismatch) -- continuing");
2206 else
2207 if (debugsw)
2208 fprintf (stderr, "content integrity confirmed\n");
2209 }
2210
2211 fseek (ce->ce_fp, 0L, SEEK_SET);
2212
2213 ready_to_go:
2214 *file = ce->ce_file;
2215 return fileno (ce->ce_fp);
2216
2217 clean_up:
2218 free_encoding (ct, 0);
2219 return NOTOK;
2220 }
2221
2222
2223 /*
2224 * 7BIT
2225 */
2226
2227 static int
2228 Init7Bit (CT ct)
2229 {
2230 if (init_encoding (ct, open7Bit) == NOTOK)
2231 return NOTOK;
2232
2233 ct->c_cesizefnx = NULL; /* no need to decode for real size */
2234 return OK;
2235 }
2236
2237
2238 static int
2239 open7Bit (CT ct, char **file)
2240 {
2241 int cc, fd, len;
2242 char buffer[BUFSIZ];
2243 CE ce;
2244
2245 ce = ct->c_cefile;
2246 if (ce->ce_fp) {
2247 fseek (ce->ce_fp, 0L, SEEK_SET);
2248 goto ready_to_go;
2249 }
2250
2251 if (ce->ce_file) {
2252 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2253 content_error (ce->ce_file, ct, "unable to fopen for reading");
2254 return NOTOK;
2255 }
2256 goto ready_to_go;
2257 }
2258
2259 if (*file == NULL) {
2260 ce->ce_file = add (m_scratch ("", tmp), NULL);
2261 ce->ce_unlink = 1;
2262 } else {
2263 ce->ce_file = add (*file, NULL);
2264 ce->ce_unlink = 0;
2265 }
2266
2267 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2268 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2269 return NOTOK;
2270 }
2271
2272 if (ct->c_type == CT_MULTIPART) {
2273 char **ap, **ep;
2274 CI ci = &ct->c_ctinfo;
2275
2276 len = 0;
2277 fprintf (ce->ce_fp, "%s: %s/%s", TYPE_FIELD, ci->ci_type, ci->ci_subtype);
2278 len += strlen (TYPE_FIELD) + 2 + strlen (ci->ci_type)
2279 + 1 + strlen (ci->ci_subtype);
2280 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
2281 putc (';', ce->ce_fp);
2282 len++;
2283
2284 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
2285
2286 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
2287 fputs ("\n\t", ce->ce_fp);
2288 len = 8;
2289 } else {
2290 putc (' ', ce->ce_fp);
2291 len++;
2292 }
2293 fprintf (ce->ce_fp, "%s", buffer);
2294 len += cc;
2295 }
2296
2297 if (ci->ci_comment) {
2298 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
2299 fputs ("\n\t", ce->ce_fp);
2300 len = 8;
2301 }
2302 else {
2303 putc (' ', ce->ce_fp);
2304 len++;
2305 }
2306 fprintf (ce->ce_fp, "(%s)", ci->ci_comment);
2307 len += cc;
2308 }
2309 fprintf (ce->ce_fp, "\n");
2310 if (ct->c_id)
2311 fprintf (ce->ce_fp, "%s:%s", ID_FIELD, ct->c_id);
2312 if (ct->c_descr)
2313 fprintf (ce->ce_fp, "%s:%s", DESCR_FIELD, ct->c_descr);
2314 fprintf (ce->ce_fp, "\n");
2315 }
2316
2317 if ((len = ct->c_end - ct->c_begin) < 0)
2318 adios (NULL, "internal error(3)");
2319
2320 if (!ct->c_fp && (ct->c_fp = fopen (ct->c_file, "r")) == NULL) {
2321 content_error (ct->c_file, ct, "unable to open for reading");
2322 return NOTOK;
2323 }
2324
2325 lseek (fd = fileno (ct->c_fp), (off_t) ct->c_begin, SEEK_SET);
2326 while (len > 0)
2327 switch (cc = read (fd, buffer, sizeof(buffer) - 1)) {
2328 case NOTOK:
2329 content_error (ct->c_file, ct, "error reading from");
2330 goto clean_up;
2331
2332 case OK:
2333 content_error (NULL, ct, "premature eof");
2334 goto clean_up;
2335
2336 default:
2337 if (cc > len)
2338 cc = len;
2339 len -= cc;
2340
2341 fwrite (buffer, sizeof(*buffer), cc, ce->ce_fp);
2342 if (ferror (ce->ce_fp)) {
2343 content_error (ce->ce_file, ct, "error writing to");
2344 goto clean_up;
2345 }
2346 }
2347
2348 fseek (ct->c_fp, 0L, SEEK_SET);
2349
2350 if (fflush (ce->ce_fp)) {
2351 content_error (ce->ce_file, ct, "error writing to");
2352 goto clean_up;
2353 }
2354
2355 fseek (ce->ce_fp, 0L, SEEK_SET);
2356
2357 ready_to_go:
2358 *file = ce->ce_file;
2359 return fileno (ce->ce_fp);
2360
2361 clean_up:
2362 free_encoding (ct, 0);
2363 return NOTOK;
2364 }
2365
2366
2367 /*
2368 * External
2369 */
2370
2371 static int
2372 openExternal (CT ct, CT cb, CE ce, char **file, int *fd)
2373 {
2374 char cachefile[BUFSIZ];
2375
2376 if (ce->ce_fp) {
2377 fseek (ce->ce_fp, 0L, SEEK_SET);
2378 goto ready_already;
2379 }
2380
2381 if (ce->ce_file) {
2382 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2383 content_error (ce->ce_file, ct, "unable to fopen for reading");
2384 return NOTOK;
2385 }
2386 goto ready_already;
2387 }
2388
2389 if (find_cache (ct, rcachesw, (int *) 0, cb->c_id,
2390 cachefile, sizeof(cachefile)) != NOTOK) {
2391 if ((ce->ce_fp = fopen (cachefile, "r"))) {
2392 ce->ce_file = getcpy (cachefile);
2393 ce->ce_unlink = 0;
2394 goto ready_already;
2395 } else {
2396 admonish (cachefile, "unable to fopen for reading");
2397 }
2398 }
2399
2400 return OK;
2401
2402 ready_already:
2403 *file = ce->ce_file;
2404 *fd = fileno (ce->ce_fp);
2405 return DONE;
2406 }
2407
2408 /*
2409 * File
2410 */
2411
2412 static int
2413 InitFile (CT ct)
2414 {
2415 return init_encoding (ct, openFile);
2416 }
2417
2418
2419 static int
2420 openFile (CT ct, char **file)
2421 {
2422 int fd, cachetype;
2423 char cachefile[BUFSIZ];
2424 struct exbody *e = ct->c_ctexbody;
2425 CE ce = ct->c_cefile;
2426
2427 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2428 case NOTOK:
2429 return NOTOK;
2430
2431 case OK:
2432 break;
2433
2434 case DONE:
2435 return fd;
2436 }
2437
2438 if (!e->eb_name) {
2439 content_error (NULL, ct, "missing name parameter");
2440 return NOTOK;
2441 }
2442
2443 ce->ce_file = getcpy (e->eb_name);
2444 ce->ce_unlink = 0;
2445
2446 if ((ce->ce_fp = fopen (ce->ce_file, "r")) == NULL) {
2447 content_error (ce->ce_file, ct, "unable to fopen for reading");
2448 return NOTOK;
2449 }
2450
2451 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2452 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2453 cachefile, sizeof(cachefile)) != NOTOK) {
2454 int mask;
2455 FILE *fp;
2456
2457 mask = umask (cachetype ? ~m_gmprot () : 0222);
2458 if ((fp = fopen (cachefile, "w"))) {
2459 int cc;
2460 char buffer[BUFSIZ];
2461 FILE *gp = ce->ce_fp;
2462
2463 fseek (gp, 0L, SEEK_SET);
2464
2465 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2466 > 0)
2467 fwrite (buffer, sizeof(*buffer), cc, fp);
2468 fflush (fp);
2469
2470 if (ferror (gp)) {
2471 admonish (ce->ce_file, "error reading");
2472 unlink (cachefile);
2473 }
2474 else
2475 if (ferror (fp)) {
2476 admonish (cachefile, "error writing");
2477 unlink (cachefile);
2478 }
2479 fclose (fp);
2480 }
2481 umask (mask);
2482 }
2483
2484 fseek (ce->ce_fp, 0L, SEEK_SET);
2485 *file = ce->ce_file;
2486 return fileno (ce->ce_fp);
2487 }
2488
2489 /*
2490 * FTP
2491 */
2492
2493 static int
2494 InitFTP (CT ct)
2495 {
2496 return init_encoding (ct, openFTP);
2497 }
2498
2499
2500 static int
2501 openFTP (CT ct, char **file)
2502 {
2503 int cachetype, caching, fd;
2504 int len, buflen;
2505 char *bp, *ftp, *user, *pass;
2506 char buffer[BUFSIZ], cachefile[BUFSIZ];
2507 struct exbody *e;
2508 CE ce;
2509 static char *username = NULL;
2510 static char *password = NULL;
2511
2512 e = ct->c_ctexbody;
2513 ce = ct->c_cefile;
2514
2515 if ((ftp = context_find (nmhaccessftp)) && !*ftp)
2516 ftp = NULL;
2517
2518 #ifndef BUILTIN_FTP
2519 if (!ftp)
2520 return NOTOK;
2521 #endif
2522
2523 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2524 case NOTOK:
2525 return NOTOK;
2526
2527 case OK:
2528 break;
2529
2530 case DONE:
2531 return fd;
2532 }
2533
2534 if (!e->eb_name || !e->eb_site) {
2535 content_error (NULL, ct, "missing %s parameter",
2536 e->eb_name ? "site": "name");
2537 return NOTOK;
2538 }
2539
2540 if (xpid) {
2541 if (xpid < 0)
2542 xpid = -xpid;
2543 pidcheck (pidwait (xpid, NOTOK));
2544 xpid = 0;
2545 }
2546
2547 /* Get the buffer ready to go */
2548 bp = buffer;
2549 buflen = sizeof(buffer);
2550
2551 /*
2552 * Construct the query message for user
2553 */
2554 snprintf (bp, buflen, "Retrieve %s", e->eb_name);
2555 len = strlen (bp);
2556 bp += len;
2557 buflen -= len;
2558
2559 if (e->eb_partno) {
2560 snprintf (bp, buflen, " (content %s)", e->eb_partno);
2561 len = strlen (bp);
2562 bp += len;
2563 buflen -= len;
2564 }
2565
2566 snprintf (bp, buflen, "\n using %sFTP from site %s",
2567 e->eb_flags ? "anonymous " : "", e->eb_site);
2568 len = strlen (bp);
2569 bp += len;
2570 buflen -= len;
2571
2572 if (e->eb_size > 0) {
2573 snprintf (bp, buflen, " (%lu octets)", e->eb_size);
2574 len = strlen (bp);
2575 bp += len;
2576 buflen -= len;
2577 }
2578 snprintf (bp, buflen, "? ");
2579
2580 /*
2581 * Now, check the answer
2582 */
2583 if (!getanswer (buffer))
2584 return NOTOK;
2585
2586 if (e->eb_flags) {
2587 user = "anonymous";
2588 snprintf (buffer, sizeof(buffer), "%s@%s", getusername (), LocalName ());
2589 pass = buffer;
2590 } else {
2591 ruserpass (e->eb_site, &username, &password);
2592 user = username;
2593 pass = password;
2594 }
2595
2596 ce->ce_unlink = (*file == NULL);
2597 caching = 0;
2598 cachefile[0] = '\0';
2599 if ((!e->eb_permission || strcasecmp (e->eb_permission, "read-write"))
2600 && find_cache (NULL, wcachesw, &cachetype, e->eb_content->c_id,
2601 cachefile, sizeof(cachefile)) != NOTOK) {
2602 if (*file == NULL) {
2603 ce->ce_unlink = 0;
2604 caching = 1;
2605 }
2606 }
2607
2608 if (*file)
2609 ce->ce_file = add (*file, NULL);
2610 else if (caching)
2611 ce->ce_file = add (cachefile, NULL);
2612 else
2613 ce->ce_file = add (m_scratch ("", tmp), NULL);
2614
2615 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2616 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2617 return NOTOK;
2618 }
2619
2620 #ifdef BUILTIN_FTP
2621 if (ftp)
2622 #endif
2623 {
2624 int child_id, i, vecp;
2625 char *vec[9];
2626
2627 vecp = 0;
2628 vec[vecp++] = r1bindex (ftp, '/');
2629 vec[vecp++] = e->eb_site;
2630 vec[vecp++] = user;
2631 vec[vecp++] = pass;
2632 vec[vecp++] = e->eb_dir;
2633 vec[vecp++] = e->eb_name;
2634 vec[vecp++] = ce->ce_file,
2635 vec[vecp++] = e->eb_mode && !strcasecmp (e->eb_mode, "ascii")
2636 ? "ascii" : "binary";
2637 vec[vecp] = NULL;
2638
2639 fflush (stdout);
2640
2641 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2642 sleep (5);
2643 switch (child_id) {
2644 case NOTOK:
2645 adios ("fork", "unable to");
2646 /* NOTREACHED */
2647
2648 case OK:
2649 close (fileno (ce->ce_fp));
2650 execvp (ftp, vec);
2651 fprintf (stderr, "unable to exec ");
2652 perror (ftp);
2653 _exit (-1);
2654 /* NOTREACHED */
2655
2656 default:
2657 if (pidXwait (child_id, NULL)) {
2658 #ifdef BUILTIN_FTP
2659 losing_ftp:
2660 #endif
2661 username = password = NULL;
2662 ce->ce_unlink = 1;
2663 return NOTOK;
2664 }
2665 break;
2666 }
2667 }
2668 #ifdef BUILTIN_FTP
2669 else
2670 if (ftp_get (e->eb_site, user, pass, e->eb_dir, e->eb_name,
2671 ce->ce_file,
2672 e->eb_mode && !strcasecmp (e->eb_mode, "ascii"), 0)
2673 == NOTOK)
2674 goto losing_ftp;
2675 #endif
2676
2677 if (cachefile[0]) {
2678 if (caching)
2679 chmod (cachefile, cachetype ? m_gmprot () : 0444);
2680 else {
2681 int mask;
2682 FILE *fp;
2683
2684 mask = umask (cachetype ? ~m_gmprot () : 0222);
2685 if ((fp = fopen (cachefile, "w"))) {
2686 int cc;
2687 FILE *gp = ce->ce_fp;
2688
2689 fseek (gp, 0L, SEEK_SET);
2690
2691 while ((cc= fread (buffer, sizeof(*buffer), sizeof(buffer), gp))
2692 > 0)
2693 fwrite (buffer, sizeof(*buffer), cc, fp);
2694 fflush (fp);
2695
2696 if (ferror (gp)) {
2697 admonish (ce->ce_file, "error reading");
2698 unlink (cachefile);
2699 }
2700 else
2701 if (ferror (fp)) {
2702 admonish (cachefile, "error writing");
2703 unlink (cachefile);
2704 }
2705 fclose (fp);
2706 }
2707 umask (mask);
2708 }
2709 }
2710
2711 fseek (ce->ce_fp, 0L, SEEK_SET);
2712 *file = ce->ce_file;
2713 return fileno (ce->ce_fp);
2714 }
2715
2716
2717 /*
2718 * Mail
2719 */
2720
2721 static int
2722 InitMail (CT ct)
2723 {
2724 return init_encoding (ct, openMail);
2725 }
2726
2727
2728 static int
2729 openMail (CT ct, char **file)
2730 {
2731 int child_id, fd, i, vecp;
2732 int len, buflen;
2733 char *bp, buffer[BUFSIZ], *vec[7];
2734 struct exbody *e = ct->c_ctexbody;
2735 CE ce = ct->c_cefile;
2736
2737 switch (openExternal (e->eb_parent, e->eb_content, ce, file, &fd)) {
2738 case NOTOK:
2739 return NOTOK;
2740
2741 case OK:
2742 break;
2743
2744 case DONE:
2745 return fd;
2746 }
2747
2748 if (!e->eb_server) {
2749 content_error (NULL, ct, "missing server parameter");
2750 return NOTOK;
2751 }
2752
2753 if (xpid) {
2754 if (xpid < 0)
2755 xpid = -xpid;
2756 pidcheck (pidwait (xpid, NOTOK));
2757 xpid = 0;
2758 }
2759
2760 /* Get buffer ready to go */
2761 bp = buffer;
2762 buflen = sizeof(buffer);
2763
2764 /* Now construct query message */
2765 snprintf (bp, buflen, "Retrieve content");
2766 len = strlen (bp);
2767 bp += len;
2768 buflen -= len;
2769
2770 if (e->eb_partno) {
2771 snprintf (bp, buflen, " %s", e->eb_partno);
2772 len = strlen (bp);
2773 bp += len;
2774 buflen -= len;
2775 }
2776
2777 snprintf (bp, buflen, " by asking %s\n\n%s\n? ",
2778 e->eb_server,
2779 e->eb_subject ? e->eb_subject : e->eb_body);
2780
2781 /* Now, check answer */
2782 if (!getanswer (buffer))
2783 return NOTOK;
2784
2785 vecp = 0;
2786 vec[vecp++] = r1bindex (mailproc, '/');
2787 vec[vecp++] = e->eb_server;
2788 vec[vecp++] = "-subject";
2789 vec[vecp++] = e->eb_subject ? e->eb_subject : "mail-server request";
2790 vec[vecp++] = "-body";
2791 vec[vecp++] = e->eb_body;
2792 vec[vecp] = NULL;
2793
2794 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2795 sleep (5);
2796 switch (child_id) {
2797 case NOTOK:
2798 advise ("fork", "unable to");
2799 return NOTOK;
2800
2801 case OK:
2802 execvp (mailproc, vec);
2803 fprintf (stderr, "unable to exec ");
2804 perror (mailproc);
2805 _exit (-1);
2806 /* NOTREACHED */
2807
2808 default:
2809 if (pidXwait (child_id, NULL) == OK)
2810 advise (NULL, "request sent");
2811 break;
2812 }
2813
2814 if (*file == NULL) {
2815 ce->ce_file = add (m_scratch ("", tmp), NULL);
2816 ce->ce_unlink = 1;
2817 } else {
2818 ce->ce_file = add (*file, NULL);
2819 ce->ce_unlink = 0;
2820 }
2821
2822 if ((ce->ce_fp = fopen (ce->ce_file, "w+")) == NULL) {
2823 content_error (ce->ce_file, ct, "unable to fopen for reading/writing");
2824 return NOTOK;
2825 }
2826
2827 fseek (ce->ce_fp, 0L, SEEK_SET);
2828 *file = ce->ce_file;
2829 return fileno (ce->ce_fp);
2830 }
2831
2832
2833 static char *
2834 fgetstr (char *s, int n, FILE *stream)
2835 {
2836 char *cp, *ep;
2837
2838 for (ep = (cp = s) + n; cp < ep; ) {
2839 int i;
2840
2841 if (!fgets (cp, n, stream))
2842 return (cp != s ? s : NULL);
2843 if (cp == s && *cp != '#')
2844 return s;
2845
2846 cp += (i = strlen (cp)) - 1;
2847 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
2848 break;
2849 *cp = '\0';
2850 n -= (i - 2);
2851 }
2852
2853 return s;
2854 }
2855
2856
2857 /*
2858 * Parse the composition draft for text and directives.
2859 * Do initial setup of Content structure.
2860 */
2861
2862 static int
2863 user_content (FILE *in, char *file, char *buf, CT *ctp)
2864 {
2865 int extrnal, vrsn;
2866 char *cp, **ap;
2867 char buffer[BUFSIZ];
2868 struct multipart *m;
2869 struct part **pp;
2870 struct stat st;
2871 struct str2init *s2i;
2872 CI ci;
2873 CT ct;
2874 CE ce;
2875
2876 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
2877 *ctp = NULL;
2878 return OK;
2879 }
2880
2881 /* allocate basic Content structure */
2882 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
2883 adios (NULL, "out of memory");
2884 *ctp = ct;
2885
2886 /* allocate basic structure for handling decoded content */
2887 init_decoded_content (ct);
2888 ce = ct->c_cefile;
2889
2890 ci = &ct->c_ctinfo;
2891 set_id (ct, 0);
2892
2893 /*
2894 * Handle inline text. Check if line
2895 * is one of the following forms:
2896 *
2897 * 1) doesn't begin with '#' (implicit directive)
2898 * 2) begins with "##" (implicit directive)
2899 * 3) begins with "#<"
2900 */
2901 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
2902 int headers;
2903 int inlineD;
2904 long pos;
2905 char content[BUFSIZ];
2906 FILE *out;
2907
2908 /* use a temp file to collect the plain text lines */
2909 ce->ce_file = add (m_tmpfil (invo_name), NULL);
2910 ce->ce_unlink = 1;
2911
2912 if ((out = fopen (ce->ce_file, "w")) == NULL)
2913 adios (ce->ce_file, "unable to open for writing");
2914
2915 if (buf[0] == '#' && buf[1] == '<') {
2916 strncpy (content, buf + 2, sizeof(content));
2917 inlineD = 1;
2918 goto rock_and_roll;
2919 } else {
2920 inlineD = 0;
2921 }
2922
2923 /* the directive is implicit */
2924 strncpy (content, "text/plain", sizeof(content));
2925 headers = 0;
2926 strncpy (buffer, buf[0] != '#' ? buf : buf + 1, sizeof(buffer));
2927 for (;;) {
2928 int i;
2929
2930 if (headers >= 0 && uprf (buffer, DESCR_FIELD)
2931 && buffer[i = strlen (DESCR_FIELD)] == ':') {
2932 headers = 1;
2933
2934 again_descr:
2935 ct->c_descr = add (buffer + i + 1, ct->c_descr);
2936 if (!fgetstr (buffer, sizeof(buffer) - 1, in))
2937 adios (NULL, "end-of-file after %s: field in plaintext", DESCR_FIELD);
2938 switch (buffer[0]) {
2939 case ' ':
2940 case '\t':
2941 i = -1;
2942 goto again_descr;
2943
2944 case '#':
2945 adios (NULL, "#-directive after %s: field in plaintext", DESCR_FIELD);
2946 /* NOTREACHED */
2947
2948 default:
2949 break;
2950 }
2951 }
2952
2953 if (headers != 1 || buffer[0] != '\n')
2954 fputs (buffer, out);
2955
2956 rock_and_roll:
2957 headers = -1;
2958 pos = ftell (in);
2959 if ((cp = fgetstr (buffer, sizeof(buffer) - 1, in)) == NULL)
2960 break;
2961 if (buffer[0] == '#') {
2962 char *bp;
2963
2964 if (buffer[1] != '#')
2965 break;
2966 for (cp = (bp = buffer) + 1; *cp; cp++)
2967 *bp++ = *cp;
2968 *bp = '\0';
2969 }
2970 }
2971
2972 if (listsw)
2973 ct->c_end = ftell (out);
2974 fclose (out);
2975
2976 /* parse content type */
2977 if (get_ctinfo (content, ct, inlineD) == NOTOK)
2978 done (1);
2979
2980 for (s2i = str2cts; s2i->si_key; s2i++)
2981 if (!strcasecmp (ci->ci_type, s2i->si_key))
2982 break;
2983 if (!s2i->si_key && !uprf (ci->ci_type, "X-"))
2984 s2i++;
2985
2986 /*
2987 * check type specified (possibly implicitly)
2988 */
2989 switch (ct->c_type = s2i->si_val) {
2990 case CT_MESSAGE:
2991 if (!strcasecmp (ci->ci_subtype, "rfc822")) {
2992 ct->c_encoding = CE_7BIT;
2993 goto call_init;
2994 }
2995 /* else fall... */
2996 case CT_MULTIPART:
2997 adios (NULL, "it doesn't make sense to define an in-line %s content",
2998 ct->c_type == CT_MESSAGE ? "message" : "multipart");
2999 /* NOTREACHED */
3000
3001 default:
3002 call_init:
3003 if ((ct->c_ctinitfnx = s2i->si_init))
3004 (*ct->c_ctinitfnx) (ct);
3005 break;
3006 }
3007
3008 if (cp)
3009 fseek (in, pos, SEEK_SET);
3010 return OK;
3011 }
3012
3013 /*
3014 * If we've reached this point, the next line
3015 * must be some type of explicit directive.
3016 */
3017
3018 /* check if directive is external-type */
3019 extrnal = (buf[1] == '@');
3020
3021 /* parse directive */
3022 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
3023 done (1);
3024
3025 /* check directive against the list of MIME types */
3026 for (s2i = str2cts; s2i->si_key; s2i++)
3027 if (!strcasecmp (ci->ci_type, s2i->si_key))
3028 break;
3029
3030 /*
3031 * Check if the directive specified a valid type.
3032 * This will happen if it was one of the following forms:
3033 *
3034 * #type/subtype (or)
3035 * #@type/subtype
3036 */
3037 if (s2i->si_key) {
3038 if (!ci->ci_subtype)
3039 adios (NULL, "missing subtype in \"#%s\"", ci->ci_type);
3040
3041 switch (ct->c_type = s2i->si_val) {
3042 case CT_MULTIPART:
3043 adios (NULL, "use \"#begin ... #end\" instead of \"#%s/%s\"",
3044 ci->ci_type, ci->ci_subtype);
3045 /* NOTREACHED */
3046
3047 case CT_MESSAGE:
3048 if (!strcasecmp (ci->ci_subtype, "partial"))
3049 adios (NULL, "sorry, \"#%s/%s\" isn't supported",
3050 ci->ci_type, ci->ci_subtype);
3051 if (!strcasecmp (ci->ci_subtype, "external-body"))
3052 adios (NULL, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
3053 ci->ci_type, ci->ci_subtype);
3054 use_forw:
3055 adios (NULL,
3056 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
3057 ci->ci_type, ci->ci_subtype);
3058 /* NOTREACHED */
3059
3060 default:
3061 if ((ct->c_ctinitfnx = s2i->si_init))
3062 (*ct->c_ctinitfnx) (ct);
3063 break;
3064 }
3065
3066 /*
3067 * #@type/subtype (external types directive)
3068 */
3069 if (extrnal) {
3070 struct exbody *e;
3071 CT p;
3072
3073 if (!ci->ci_magic)
3074 adios (NULL, "need external information for \"#@%s/%s\"",
3075 ci->ci_type, ci->ci_subtype);
3076 p = ct;
3077
3078 snprintf (buffer, sizeof(buffer), "message/external-body; %s", ci->ci_magic);
3079 free (ci->ci_magic);
3080 ci->ci_magic = NULL;
3081
3082 /*
3083 * Since we are using the current Content structure to
3084 * hold information about the type of the external
3085 * reference, we need to create another Content structure
3086 * for the message/external-body to wrap it in.
3087 */
3088 if ((ct = (CT) calloc (1, sizeof(*ct))) == NULL)
3089 adios (NULL, "out of memory");
3090 *ctp = ct;
3091 ci = &ct->c_ctinfo;
3092 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3093 done (1);
3094 ct->c_type = CT_MESSAGE;
3095 ct->c_subtype = MESSAGE_EXTERNAL;
3096
3097 if ((e = (struct exbody *) calloc (1, sizeof(*e))) == NULL)
3098 adios (NULL, "out of memory");
3099 ct->c_ctparams = (void *) e;
3100
3101 e->eb_parent = ct;
3102 e->eb_content = p;
3103 p->c_ctexbody = e;
3104
3105 if (params_external (ct, 1) == NOTOK)
3106 done (1);
3107
3108 return OK;
3109 }
3110
3111 /* Handle [file] argument */
3112 if (ci->ci_magic) {
3113 /* check if specifies command to execute */
3114 if (*ci->ci_magic == '|' || *ci->ci_magic == '!') {
3115 for (cp = ci->ci_magic + 1; isspace (*cp); cp++)
3116 continue;
3117 if (!*cp)
3118 adios (NULL, "empty pipe command for #%s directive", ci->ci_type);
3119 cp = add (cp, NULL);
3120 free (ci->ci_magic);
3121 ci->ci_magic = cp;
3122 } else {
3123 /* record filename of decoded contents */
3124 ce->ce_file = ci->ci_magic;
3125 if (access (ce->ce_file, R_OK) == NOTOK)
3126 adios ("reading", "unable to access %s for", ce->ce_file);
3127 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3128 ct->c_end = (long) st.st_size;
3129 ci->ci_magic = NULL;
3130 }
3131 return OK;
3132 }
3133
3134 /*
3135 * No [file] argument, so check profile for
3136 * method to compose content.
3137 */
3138 snprintf (buffer, sizeof(buffer), "%s-compose-%s/%s",
3139 invo_name, ci->ci_type, ci->ci_subtype);
3140 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3141 snprintf (buffer, sizeof(buffer), "%s-compose-%s", invo_name, ci->ci_type);
3142 if ((cp = context_find (buffer)) == NULL || *cp == '\0') {
3143 content_error (NULL, ct, "don't know how to compose content");
3144 done (1);
3145 }
3146 }
3147 ci->ci_magic = add (cp, NULL);
3148 return OK;
3149 }
3150
3151 if (extrnal)
3152 adios (NULL, "external definition not allowed for \"#%s\"", ci->ci_type);
3153
3154 /*
3155 * Message directive
3156 * #forw [+folder] [msgs]
3157 */
3158 if (!strcasecmp (ci->ci_type, "forw")) {
3159 int msgnum;
3160 char *folder, *arguments[MAXARGS];
3161 struct msgs *mp;
3162
3163 if (ci->ci_magic) {
3164 ap = brkstring (ci->ci_magic, " ", "\n");
3165 copyip (ap, arguments, MAXARGS);
3166 } else {
3167 arguments[0] = "cur";
3168 arguments[1] = NULL;
3169 }
3170 folder = NULL;
3171
3172 /* search the arguments for a folder name */
3173 for (ap = arguments; *ap; ap++) {
3174 cp = *ap;
3175 if (*cp == '+' || *cp == '@') {
3176 if (folder)
3177 adios (NULL, "only one folder per #forw directive");
3178 else
3179 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
3180 }
3181 }
3182
3183 /* else, use the current folder */
3184 if (!folder)
3185 folder = add (getfolder (1), NULL);
3186
3187 if (!(mp = folder_read (folder)))
3188 adios (NULL, "unable to read folder %s", folder);
3189 for (ap = arguments; *ap; ap++) {
3190 cp = *ap;
3191 if (*cp != '+' && *cp != '@')
3192 if (!m_convert (mp, cp))
3193 done (1);
3194 }
3195 free (folder);
3196 free_ctinfo (ct);
3197
3198 /*
3199 * If there is more than one message to include, make this
3200 * a content of type "multipart/digest" and insert each message
3201 * as a subpart. If there is only one message, then make this
3202 * a content of type "message/rfc822".
3203 */
3204 if (mp->numsel > 1) {
3205 /* we are forwarding multiple messages */
3206 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
3207 done (1);
3208 ct->c_type = CT_MULTIPART;
3209 ct->c_subtype = MULTI_DIGEST;
3210
3211 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3212 adios (NULL, "out of memory");
3213 ct->c_ctparams = (void *) m;
3214 pp = &m->mp_parts;
3215
3216 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
3217 if (is_selected(mp, msgnum)) {
3218 struct part *part;
3219 CT p;
3220 CE pe;
3221
3222 if ((p = (CT) calloc (1, sizeof(*p))) == NULL)
3223 adios (NULL, "out of memory");
3224 init_decoded_content (p);
3225 pe = p->c_cefile;
3226 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
3227 done (1);
3228 p->c_type = CT_MESSAGE;
3229 p->c_subtype = MESSAGE_RFC822;
3230
3231 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3232 pe->ce_file = add (buffer, NULL);
3233 if (listsw && stat (pe->ce_file, &st) != NOTOK)
3234 p->c_end = (long) st.st_size;
3235
3236 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3237 adios (NULL, "out of memory");
3238 *pp = part;
3239 pp = &part->mp_next;
3240 part->mp_part = p;
3241 }
3242 }
3243 } else {
3244 /* we are forwarding one message */
3245 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
3246 done (1);
3247 ct->c_type = CT_MESSAGE;
3248 ct->c_subtype = MESSAGE_RFC822;
3249
3250 msgnum = mp->lowsel;
3251 snprintf (buffer, sizeof(buffer), "%s/%d", mp->foldpath, msgnum);
3252 ce->ce_file = add (buffer, NULL);
3253 if (listsw && stat (ce->ce_file, &st) != NOTOK)
3254 ct->c_end = (long) st.st_size;
3255 }
3256
3257 folder_free (mp); /* free folder/message structure */
3258 return OK;
3259 }
3260
3261 /*
3262 * #end
3263 */
3264 if (!strcasecmp (ci->ci_type, "end")) {
3265 free_content (ct);
3266 *ctp = NULL;
3267 return DONE;
3268 }
3269
3270 /*
3271 * #begin [ alternative | parallel ]
3272 */
3273 if (!strcasecmp (ci->ci_type, "begin")) {
3274 if (!ci->ci_magic) {
3275 vrsn = MULTI_MIXED;
3276 cp = SubMultiPart[vrsn - 1].kv_key;
3277 } else if (!strcasecmp (ci->ci_magic, "alternative")) {
3278 vrsn = MULTI_ALTERNATE;
3279 cp = SubMultiPart[vrsn - 1].kv_key;
3280 } else if (!strcasecmp (ci->ci_magic, "parallel")) {
3281 vrsn = MULTI_PARALLEL;
3282 cp = SubMultiPart[vrsn - 1].kv_key;
3283 } else if (uprf (ci->ci_magic, "digest")) {
3284 goto use_forw;
3285 } else {
3286 vrsn = MULTI_UNKNOWN;
3287 cp = ci->ci_magic;
3288 }
3289
3290 free_ctinfo (ct);
3291 snprintf (buffer, sizeof(buffer), "multipart/%s", cp);
3292 if (get_ctinfo (buffer, ct, 0) == NOTOK)
3293 done (1);
3294 ct->c_type = CT_MULTIPART;
3295 ct->c_subtype = vrsn;
3296
3297 if ((m = (struct multipart *) calloc (1, sizeof(*m))) == NULL)
3298 adios (NULL, "out of memory");
3299 ct->c_ctparams = (void *) m;
3300
3301 pp = &m->mp_parts;
3302 while (fgetstr (buffer, sizeof(buffer) - 1, in)) {
3303 struct part *part;
3304 CT p;
3305
3306 if (user_content (in, file, buffer, &p) == DONE) {
3307 if (!m->mp_parts)
3308 adios (NULL, "empty \"#begin ... #end\" sequence");
3309 return OK;
3310 }
3311 if (!p)
3312 continue;
3313
3314 if ((part = (struct part *) calloc (1, sizeof(*part))) == NULL)
3315 adios (NULL, "out of memory");
3316 *pp = part;
3317 pp = &part->mp_next;
3318 part->mp_part = p;
3319 }
3320 admonish (NULL, "premature end-of-file, missing #end");
3321 return OK;
3322 }
3323
3324 /*
3325 * Unknown directive
3326 */
3327 adios (NULL, "unknown directive \"#%s\"", ci->ci_type);
3328 return NOTOK; /* NOT REACHED */
3329 }
3330
3331
3332 static void
3333 set_id (CT ct, int top)
3334 {
3335 char msgid[BUFSIZ];
3336 static int partno;
3337 static time_t clock = 0;
3338 static char *msgfmt;
3339
3340 if (clock == 0) {
3341 time (&clock);
3342 snprintf (msgid, sizeof(msgid), "<%d.%ld.%%d@%s>\n",
3343 (int) getpid(), (long) clock, LocalName());
3344 partno = 0;
3345 msgfmt = getcpy(msgid);
3346 }
3347 snprintf (msgid, sizeof(msgid), msgfmt, top ? 0 : ++partno);
3348 ct->c_id = getcpy (msgid);
3349 }
3350
3351
3352 static char ebcdicsafe[0x100] = {
3353 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3354 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3355 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3356 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3357 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
3358 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3359 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3360 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3361 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3362 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3363 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3364 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
3365 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3366 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3367 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
3368 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
3369 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3370 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3371 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3372 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3373 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3374 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3375 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3376 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3377 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3378 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3379 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3380 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3381 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3382 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
3384 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
3385 };
3386
3387
3388 /*
3389 * Fill out, or expand the various contents in the composition
3390 * draft. Read-in any necessary files. Parse and execute any
3391 * commands specified by profile composition strings.
3392 */
3393
3394 static int
3395 compose_content (CT ct)
3396 {
3397 CE ce = ct->c_cefile;
3398
3399 switch (ct->c_type) {
3400 case CT_MULTIPART:
3401 {
3402 int partnum;
3403 char *pp;
3404 char partnam[BUFSIZ];
3405 struct multipart *m = (struct multipart *) ct->c_ctparams;
3406 struct part *part;
3407
3408 if (ct->c_partno) {
3409 snprintf (partnam, sizeof(partnam), "%s.", ct->c_partno);
3410 pp = partnam + strlen (partnam);
3411 } else {
3412 pp = partnam;
3413 }
3414
3415 /* first, we call compose_content on all the subparts */
3416 for (part = m->mp_parts, partnum = 1; part; part = part->mp_next, partnum++) {
3417 CT p = part->mp_part;
3418
3419 sprintf (pp, "%d", partnum);
3420 p->c_partno = add (partnam, NULL);
3421 if (compose_content (p) == NOTOK)
3422 return NOTOK;
3423 }
3424
3425 /*
3426 * If the -rfc934mode switch is given, then check all
3427 * the subparts of a multipart/digest. If they are all
3428 * message/rfc822, then mark this content and all
3429 * subparts with the rfc934 compatibility mode flag.
3430 */
3431 if (rfc934sw && ct->c_subtype == MULTI_DIGEST) {
3432 int is934 = 1;
3433
3434 for (part = m->mp_parts; part; part = part->mp_next) {
3435 CT p = part->mp_part;
3436
3437 if (p->c_subtype != MESSAGE_RFC822) {
3438 is934 = 0;
3439 break;
3440 }
3441 }
3442 ct->c_rfc934 = is934;
3443 for (part = m->mp_parts; part; part = part->mp_next) {
3444 CT p = part->mp_part;
3445
3446 if ((p->c_rfc934 = is934))
3447 p->c_end++;
3448 }
3449 }
3450
3451 if (listsw) {
3452 ct->c_end = (partnum = strlen (prefix) + 2) + 2;
3453 if (ct->c_rfc934)
3454 ct->c_end += 1;
3455
3456 for (part = m->mp_parts; part; part = part->mp_next)
3457 ct->c_end += part->mp_part->c_end + partnum;
3458 }
3459 }
3460 break;
3461
3462 case CT_MESSAGE:
3463 /* Nothing to do for type message */
3464 break;
3465
3466 /*
3467 * Discrete types (text/application/audio/image/video)
3468 */
3469 default:
3470 if (!ce->ce_file) {
3471 pid_t child_id;
3472 int i, xstdout, len, buflen;
3473 char *bp, **ap, *cp;
3474 char *vec[4], buffer[BUFSIZ];
3475 FILE *out;
3476 CI ci = &ct->c_ctinfo;
3477
3478 if (!(cp = ci->ci_magic))
3479 adios (NULL, "internal error(5)");
3480
3481 ce->ce_file = add (m_tmpfil (invo_name), NULL);
3482 ce->ce_unlink = 1;
3483
3484 xstdout = 0;
3485
3486 /* Get buffer ready to go */
3487 bp = buffer;
3488 bp[0] = '\0';
3489 buflen = sizeof(buffer);
3490
3491 /*
3492 * Parse composition string into buffer
3493 */
3494 for ( ; *cp; cp++) {
3495 if (*cp == '%') {
3496 switch (*++cp) {
3497 case 'a':
3498 {
3499 /* insert parameters from directive */
3500 char **ep;
3501 char *s = "";
3502
3503 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3504 snprintf (bp, buflen, "%s%s=\"%s\"", s, *ap, *ep);
3505 len = strlen (bp);
3506 bp += len;
3507 buflen -= len;
3508 s = " ";
3509 }
3510 }
3511 break;
3512
3513 case 'F':
3514 /* %f, and stdout is not-redirected */
3515 xstdout = 1;
3516 /* and fall... */
3517
3518 case 'f':
3519 /*
3520 * insert temporary filename where
3521 * content should be written
3522 */
3523 snprintf (bp, buflen, "%s", ce->ce_file);
3524 break;
3525
3526 case 's':
3527 /* insert content subtype */
3528 strncpy (bp, ci->ci_subtype, buflen);
3529 break;
3530
3531 case '%':
3532 /* insert character % */
3533 goto raw;
3534
3535 default:
3536 *bp++ = *--cp;
3537 *bp = '\0';
3538 buflen--;
3539 continue;
3540 }
3541 len = strlen (bp);
3542 bp += len;
3543 buflen -= len;
3544 } else {
3545 raw:
3546 *bp++ = *cp;
3547 *bp = '\0';
3548 buflen--;
3549 }
3550 }
3551
3552 if (verbosw)
3553 printf ("composing content %s/%s from command\n\t%s\n",
3554 ci->ci_type, ci->ci_subtype, buffer);
3555
3556 fflush (stdout); /* not sure if need for -noverbose */
3557
3558 vec[0] = "/bin/sh";
3559 vec[1] = "-c";
3560 vec[2] = buffer;
3561 vec[3] = NULL;
3562
3563 if ((out = fopen (ce->ce_file, "w")) == NULL)
3564 adios (ce->ce_file, "unable to open for writing");
3565
3566 for (i = 0; (child_id = vfork()) == NOTOK && i > 5; i++)
3567 sleep (5);
3568 switch (child_id) {
3569 case NOTOK:
3570 adios ("fork", "unable to fork");
3571 /* NOTREACHED */
3572
3573 case OK:
3574 if (!xstdout)
3575 dup2 (fileno (out), 1);
3576 close (fileno (out));
3577 execvp ("/bin/sh", vec);
3578 fprintf (stderr, "unable to exec ");
3579 perror ("/bin/sh");
3580 _exit (-1);
3581 /* NOTREACHED */
3582
3583 default:
3584 fclose (out);
3585 if (pidXwait(child_id, NULL))
3586 done (1);
3587 break;
3588 }
3589 }
3590
3591 /* Check size of file */
3592 if (listsw && ct->c_end == 0L) {
3593 struct stat st;
3594
3595 if (stat (ce->ce_file, &st) != NOTOK)
3596 ct->c_end = (long) st.st_size;
3597 }
3598 break;
3599 }
3600
3601 return OK;
3602 }
3603
3604
3605 /*
3606 * Scan the content.
3607 *
3608 * 1) choose a transfer encoding.
3609 * 2) check for clashes with multipart boundary string.
3610 * 3) for text content, figure out which character set is being used.
3611 *
3612 * If there is a clash with one of the contents and the multipart boundary,
3613 * this function will exit with NOTOK. This will cause the scanning process
3614 * to be repeated with a different multipart boundary. It is possible
3615 * (although highly unlikely) that this scan will be repeated multiple times.
3616 */
3617
3618 static int
3619 scan_content (CT ct)
3620 {
3621 int len;
3622 int check8bit, contains8bit = 0; /* check if contains 8bit data */
3623 int checklinelen, linelen = 0; /* check for long lines */
3624 int checkboundary, boundaryclash = 0; /* check if clashes with multipart boundary */
3625 int checklinespace, linespace = 0; /* check if any line ends with space */
3626 int checkebcdic, ebcdicunsafe = 0; /* check if contains ebcdic unsafe characters */
3627 char *cp, buffer[BUFSIZ];
3628 struct text *t;
3629 FILE *in;
3630 CE ce = ct->c_cefile;
3631
3632 /*
3633 * handle multipart by scanning all subparts
3634 * and then checking their encoding.
3635 */
3636 if (ct->c_type == CT_MULTIPART) {
3637 struct multipart *m = (struct multipart *) ct->c_ctparams;
3638 struct part *part;
3639
3640 /* initially mark the domain of enclosing multipart as 7bit */
3641 ct->c_encoding = CE_7BIT;
3642
3643 for (part = m->mp_parts; part; part = part->mp_next) {
3644 CT p = part->mp_part;
3645
3646 if (scan_content (p) == NOTOK) /* choose encoding for subpart */
3647 return NOTOK;
3648
3649 /* if necessary, enlarge encoding for enclosing multipart */
3650 if (p->c_encoding == CE_BINARY)
3651 ct->c_encoding = CE_BINARY;
3652 if (p->c_encoding == CE_8BIT && ct->c_encoding != CE_BINARY)
3653 ct->c_encoding = CE_8BIT;
3654 }
3655
3656 return OK;
3657 }
3658
3659 /*
3660 * Decide what to check while scanning this content.
3661 */
3662 switch (ct->c_type) {
3663 case CT_TEXT:
3664 check8bit = 1;
3665 checkboundary = 1;
3666 if (ct->c_subtype == TEXT_PLAIN) {
3667 checkebcdic = 0;
3668 checklinelen = 0;
3669 checklinespace = 0;
3670 } else {
3671 checkebcdic = ebcdicsw;
3672 checklinelen = 1;
3673 checklinespace = 1;
3674 }
3675 break;
3676
3677 case CT_APPLICATION:
3678 check8bit = 1;
3679 checkebcdic = ebcdicsw;
3680 checklinelen = 1;
3681 checklinespace = 1;
3682 checkboundary = 1;
3683 break;
3684
3685 case CT_MESSAGE:
3686 check8bit = 0;
3687 checkebcdic = 0;
3688 checklinelen = 0;
3689 checklinespace = 0;
3690
3691 /* don't check anything for message/external */
3692 if (ct->c_subtype == MESSAGE_EXTERNAL)
3693 checkboundary = 0;
3694 else
3695 checkboundary = 1;
3696 break;
3697
3698 case CT_AUDIO:
3699 case CT_IMAGE:
3700 case CT_VIDEO:
3701 /*
3702 * Don't check anything for these types,
3703 * since we are forcing use of base64.
3704 */
3705 check8bit = 0;
3706 checkebcdic = 0;
3707 checklinelen = 0;
3708 checklinespace = 0;
3709 checkboundary = 0;
3710 break;
3711 }
3712
3713 /*
3714 * Scan the unencoded content
3715 */
3716 if (check8bit || checklinelen || checklinespace || checkboundary) {
3717 if ((in = fopen (ce->ce_file, "r")) == NULL)
3718 adios (ce->ce_file, "unable to open for reading");
3719 len = strlen (prefix);
3720
3721 while (fgets (buffer, sizeof(buffer) - 1, in)) {
3722 /*
3723 * Check for 8bit data.
3724 */
3725 if (check8bit) {
3726 for (cp = buffer; *cp; cp++) {
3727 if (!isascii (*cp)) {
3728 contains8bit = 1;
3729 check8bit = 0; /* no need to keep checking */
3730 }
3731 /*
3732 * Check if character is ebcdic-safe. We only check
3733 * this if also checking for 8bit data.
3734 */
3735 if (checkebcdic && !ebcdicsafe[*cp & 0xff]) {
3736 ebcdicunsafe = 1;
3737 checkebcdic = 0; /* no need to keep checking */
3738 }
3739 }
3740 }
3741
3742 /*
3743 * Check line length.
3744 */
3745 if (checklinelen && (strlen (buffer) > CPERLIN + 1)) {
3746 linelen = 1;
3747 checklinelen = 0; /* no need to keep checking */
3748 }
3749
3750 /*
3751 * Check if line ends with a space.
3752 */
3753 if (checklinespace && (cp = buffer + strlen (buffer) - 2) > buffer && isspace (*cp)) {
3754 linespace = 1;
3755 checklinespace = 0; /* no need to keep checking */
3756 }
3757
3758 /*
3759 * Check if content contains a line that clashes
3760 * with our standard boundary for multipart messages.
3761 */
3762 if (checkboundary && buffer[0] == '-' && buffer[1] == '-') {
3763 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
3764 if (!isspace (*cp))
3765 break;
3766 *++cp = '\0';
3767 if (!strncmp(buffer + 2, prefix, len) && isdigit(buffer[2 + len])) {
3768 boundaryclash = 1;
3769 checkboundary = 0; /* no need to keep checking */
3770 }
3771 }
3772 }
3773 fclose (in);
3774 }
3775
3776 /*
3777 * Decide which transfer encoding to use.
3778 */
3779 switch (ct->c_type) {
3780 case CT_TEXT:
3781 /*
3782 * If the text content didn't specify a character
3783 * set, we need to figure out which one was used.
3784 */
3785 t = (struct text *) ct->c_ctparams;
3786 if (t->tx_charset == CHARSET_UNSPECIFIED) {
3787 CI ci = &ct->c_ctinfo;
3788 char **ap, **ep;
3789
3790 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++)
3791 continue;
3792
3793 if (contains8bit) {
3794 t->tx_charset = CHARSET_UNKNOWN;
3795 *ap = concat ("charset=", write_charset_8bit(), NULL);
3796 } else {
3797 t->tx_charset = CHARSET_USASCII;
3798 *ap = add ("charset=us-ascii", NULL);
3799 }
3800
3801 cp = strchr(*ap++, '=');
3802 *ap = NULL;
3803 *cp++ = '\0';
3804 *ep = cp;
3805 }
3806
3807 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3808 ct->c_encoding = CE_QUOTED;
3809 else
3810 ct->c_encoding = CE_7BIT;
3811 break;
3812
3813 case CT_APPLICATION:
3814 /* For application type, use base64, except when postscript */
3815 if (contains8bit || ebcdicunsafe || linelen || linespace || checksw)
3816 ct->c_encoding = (ct->c_subtype == APPLICATION_POSTSCRIPT)
3817 ? CE_QUOTED : CE_BASE64;
3818 else
3819 ct->c_encoding = CE_7BIT;
3820 break;
3821
3822 case CT_MESSAGE:
3823 ct->c_encoding = CE_7BIT;
3824 break;
3825
3826 case CT_AUDIO:
3827 case CT_IMAGE:
3828 case CT_VIDEO:
3829 /* For audio, image, and video contents, just use base64 */
3830 ct->c_encoding = CE_BASE64;
3831 break;
3832 }
3833
3834 return (boundaryclash ? NOTOK : OK);
3835 }
3836
3837
3838 /*
3839 * Scan the content structures, and build header
3840 * fields that will need to be output into the
3841 * message.
3842 */
3843
3844 static int
3845 build_headers (CT ct)
3846 {
3847 int cc, mailbody, len;
3848 char **ap, **ep;
3849 char *np, *vp, buffer[BUFSIZ];
3850 CI ci = &ct->c_ctinfo;
3851
3852 /*
3853 * If message is type multipart, then add the multipart
3854 * boundary to the list of attribute/value pairs.
3855 */
3856 if (ct->c_type == CT_MULTIPART) {
3857 char *cp;
3858 static int level = 0; /* store nesting level */
3859
3860 ap = ci->ci_attrs;
3861 ep = ci->ci_values;
3862 snprintf (buffer, sizeof(buffer), "boundary=%s%d", prefix, level++);
3863 cp = strchr(*ap++ = add (buffer, NULL), '=');
3864 *ap = NULL;
3865 *cp++ = '\0';
3866 *ep = cp;
3867 }
3868
3869 /*
3870 * Skip the output of Content-Type, parameters, content
3871 * description, and Content-ID if the content is of type
3872 * "message" and the rfc934 compatibility flag is set
3873 * (which means we are inside multipart/digest and the
3874 * switch -rfc934mode was given).
3875 */
3876 if (ct->c_type == CT_MESSAGE && ct->c_rfc934)
3877 goto skip_headers;
3878
3879 /*
3880 * output the content type and subtype
3881 */
3882 np = add (TYPE_FIELD, NULL);
3883 vp = concat (" ", ci->ci_type, "/", ci->ci_subtype, NULL);
3884
3885 /* keep track of length of line */
3886 len = strlen (TYPE_FIELD) + strlen (ci->ci_type)
3887 + strlen (ci->ci_subtype) + 3;
3888
3889 mailbody = ct->c_type == CT_MESSAGE
3890 && ct->c_subtype == MESSAGE_EXTERNAL
3891 && ((struct exbody *) ct->c_ctparams)->eb_body;
3892
3893 /*
3894 * Append the attribute/value pairs to
3895 * the end of the Content-Type line.
3896 */
3897 for (ap = ci->ci_attrs, ep = ci->ci_values; *ap; ap++, ep++) {
3898 if (mailbody && !strcasecmp (*ap, "body"))
3899 continue;
3900
3901 vp = add (";", vp);
3902 len++;
3903
3904 snprintf (buffer, sizeof(buffer), "%s=\"%s\"", *ap, *ep);
3905 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
3906 vp = add ("\n\t", vp);
3907 len = 8;
3908 } else {
3909 vp = add (" ", vp);
3910 len++;
3911 }
3912 vp = add (buffer, vp);
3913 len += cc;
3914 }
3915
3916 /*
3917 * Append any RFC-822 comment to the end of
3918 * the Content-Type line.
3919 */
3920 if (ci->ci_comment) {
3921 snprintf (buffer, sizeof(buffer), "(%s)", ci->ci_comment);
3922 if (len + 1 + (cc = 2 + strlen (ci->ci_comment)) >= CPERLIN) {
3923 vp = add ("\n\t", vp);
3924 len = 8;
3925 } else {
3926 vp = add (" ", vp);
3927 len++;
3928 }
3929 vp = add (buffer, vp);
3930 len += cc;
3931 }
3932 vp = add ("\n", vp);
3933 add_header (ct, np, vp);
3934
3935 /*
3936 * output the Content-ID
3937 */
3938 if (ct->c_id) {
3939 np = add (ID_FIELD, NULL);
3940 vp = concat (" ", ct->c_id, NULL);
3941 add_header (ct, np, vp);
3942 }
3943
3944 /*
3945 * output the Content-Description
3946 */
3947 if (ct->c_descr) {
3948 np = add (DESCR_FIELD, NULL);
3949 vp = concat (" ", ct->c_descr, NULL);
3950 add_header (ct, np, vp);
3951 }
3952
3953 skip_headers:
3954 /*
3955 * If this is the internal content structure for a
3956 * "message/external", then we are done with the
3957 * headers (since it has no body).
3958 */
3959 if (ct->c_ctexbody)
3960 return OK;
3961
3962 /*
3963 * output the Content-MD5
3964 */
3965 if (checksw) {
3966 np = add (MD5_FIELD, NULL);
3967 vp = calculate_digest (ct, (ct->c_encoding == CE_QUOTED) ? 1 : 0);
3968 add_header (ct, np, vp);
3969 }
3970
3971 /*
3972 * output the Content-Transfer-Encoding
3973 */
3974 switch (ct->c_encoding) {
3975 case CE_7BIT:
3976 /* Nothing to output */
3977 #if 0
3978 np = add (ENCODING_FIELD, NULL);
3979 vp = concat (" ", "7bit", "\n", NULL);
3980 add_header (ct, np, vp);
3981 #endif
3982 break;
3983
3984 case CE_8BIT:
3985 if (ct->c_type == CT_MESSAGE)
3986 adios (NULL, "internal error, invalid encoding");
3987
3988 np = add (ENCODING_FIELD, NULL);
3989 vp = concat (" ", "8bit", "\n", NULL);
3990 add_header (ct, np, vp);
3991 break;
3992
3993 case CE_QUOTED:
3994 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
3995 adios (NULL, "internal error, invalid encoding");
3996
3997 np = add (ENCODING_FIELD, NULL);
3998 vp = concat (" ", "quoted-printable", "\n", NULL);
3999 add_header (ct, np, vp);
4000 break;
4001
4002 case CE_BASE64:
4003 if (ct->c_type == CT_MESSAGE || ct->c_type == CT_MULTIPART)
4004 adios (NULL, "internal error, invalid encoding");
4005
4006 np = add (ENCODING_FIELD, NULL);
4007 vp = concat (" ", "base64", "\n", NULL);
4008 add_header (ct, np, vp);
4009 break;
4010
4011 case CE_BINARY:
4012 if (ct->c_type == CT_MESSAGE)
4013 adios (NULL, "internal error, invalid encoding");
4014
4015 np = add (ENCODING_FIELD, NULL);
4016 vp = concat (" ", "binary", "\n", NULL);
4017 add_header (ct, np, vp);
4018 break;
4019
4020 default:
4021 adios (NULL, "unknown transfer encoding in content");
4022 break;
4023 }
4024
4025 /*
4026 * Additional content specific header processing
4027 */
4028 switch (ct->c_type) {
4029 case CT_MULTIPART:
4030 {
4031 struct multipart *m;
4032 struct part *part;
4033
4034 m = (struct multipart *) ct->c_ctparams;
4035 for (part = m->mp_parts; part; part = part->mp_next) {
4036 CT p;
4037
4038 p = part->mp_part;
4039 build_headers (p);
4040 }
4041 }
4042 break;
4043
4044 case CT_MESSAGE:
4045 if (ct->c_subtype == MESSAGE_EXTERNAL) {
4046 struct exbody *e;
4047
4048 e = (struct exbody *) ct->c_ctparams;
4049 build_headers (e->eb_content);
4050 }
4051 break;
4052
4053 default:
4054 /* Nothing to do */
4055 break;
4056 }
4057
4058 return OK;
4059 }
4060
4061
4062 static char nib2b64[0x40+1] =
4063 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
4064
4065 static char *
4066 calculate_digest (CT ct, int asciiP)
4067 {
4068 int cc;
4069 char buffer[BUFSIZ], *vp, *op;
4070 unsigned char *dp;
4071 unsigned char digest[16];
4072 unsigned char outbuf[25];
4073 FILE *in;
4074 MD5_CTX mdContext;
4075 CE ce = ct->c_cefile;
4076
4077 /* open content */
4078 if ((in = fopen (ce->ce_file, "r")) == NULL)
4079 adios (ce->ce_file, "unable to open for reading");
4080
4081 /* Initialize md5 context */
4082 MD5Init (&mdContext);
4083
4084 /* calculate md5 message digest */
4085 if (asciiP) {
4086 while (fgets (buffer, sizeof(buffer) - 1, in)) {
4087 char c, *cp;
4088
4089 cp = buffer + strlen (buffer) - 1;
4090 if ((c = *cp) == '\n')
4091 *cp = '\0';
4092
4093 MD5Update (&mdContext, (unsigned char *) buffer,
4094 (unsigned int) strlen (buffer));
4095
4096 if (c == '\n')
4097 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
4098 }
4099 } else {
4100 while ((cc = fread (buffer, sizeof(*buffer), sizeof(buffer), in)) > 0)
4101 MD5Update (&mdContext, (unsigned char *) buffer, (unsigned int) cc);
4102 }
4103
4104 /* md5 finalization. Write digest and zero md5 context */
4105 MD5Final (digest, &mdContext);
4106
4107 /* close content */
4108 fclose (in);
4109
4110 /* print debugging info */
4111 if (debugsw) {
4112 unsigned char *ep;
4113
4114 fprintf (stderr, "MD5 digest=");
4115 for (ep = (dp = digest) + sizeof(digest) / sizeof(digest[0]);
4116 dp < ep; dp++)
4117 fprintf (stderr, "%02x", *dp & 0xff);
4118 fprintf (stderr, "\n");
4119 }
4120
4121 /* encode the digest using base64 */
4122 for (dp = digest, op = outbuf, cc = sizeof(digest) / sizeof(digest[0]);
4123 cc > 0; cc -= 3, op += 4) {
4124 unsigned long bits;
4125 char *bp;
4126
4127 bits = (*dp++ & 0xff) << 16;
4128 if (cc > 1) {
4129 bits |= (*dp++ & 0xff) << 8;
4130 if (cc > 2)
4131 bits |= *dp++ & 0xff;
4132 }
4133
4134 for (bp = op + 4; bp > op; bits >>= 6)
4135 *--bp = nib2b64[bits & 0x3f];
4136 if (cc < 3) {
4137 *(op + 3) = '=';
4138 if (cc < 2)
4139 *(op + 2) = '=';
4140 }
4141 }
4142
4143 /* null terminate string */
4144 outbuf[24] = '\0';
4145
4146 /* now make copy and return string */
4147 vp = concat (" ", outbuf, "\n", NULL);
4148 return vp;
4149 }
4150
4151
4152 static int
4153 readDigest (CT ct, char *cp)
4154 {
4155 int bitno, skip;
4156 unsigned long bits;
4157 char *bp = cp;
4158 unsigned char *dp, value, *ep;
4159 unsigned char *b, *b1, *b2, *b3;
4160
4161 b = (unsigned char *) &bits,
4162 b1 = &b[endian > 0 ? 1 : 2],
4163 b2 = &b[endian > 0 ? 2 : 1],
4164 b3 = &b[endian > 0 ? 3 : 0];
4165 bitno = 18;
4166 bits = 0L;
4167 skip = 0;
4168
4169 for (ep = (dp = ct->c_digest)
4170 + sizeof(ct->c_digest) / sizeof(ct->c_digest[0]); *cp; cp++)
4171 switch (*cp) {
4172 default:
4173 if (skip
4174 || (*cp & 0x80)
4175 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4176 if (debugsw)
4177 fprintf (stderr, "invalid BASE64 encoding\n");
4178 return NOTOK;
4179 }
4180
4181 bits |= value << bitno;
4182 test_end:
4183 if ((bitno -= 6) < 0) {
4184 if (dp + (3 - skip) > ep)
4185 goto invalid_digest;
4186 *dp++ = *b1;
4187 if (skip < 2) {
4188 *dp++ = *b2;
4189 if (skip < 1)
4190 *dp++ = *b3;
4191 }
4192 bitno = 18;
4193 bits = 0L;
4194 skip = 0;
4195 }
4196 break;
4197
4198 case '=':
4199 if (++skip > 3)
4200 goto self_delimiting;
4201 goto test_end;
4202 }
4203 if (bitno != 18) {
4204 if (debugsw)
4205 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4206
4207 return NOTOK;
4208 }
4209 self_delimiting:
4210 if (dp != ep) {
4211 invalid_digest:
4212 if (debugsw) {
4213 while (*cp)
4214 cp++;
4215 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
4216 cp - bp);
4217 }
4218
4219 return NOTOK;
4220 }
4221
4222 if (debugsw) {
4223 fprintf (stderr, "MD5 digest=");
4224 for (dp = ct->c_digest; dp < ep; dp++)
4225 fprintf (stderr, "%02x", *dp & 0xff);
4226 fprintf (stderr, "\n");
4227 }
4228
4229 return OK;
4230 }