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