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