]> diplodocus.org Git - nmh/blob - docs/historical/mh-6.8.5/uip/mhn.c
sbr/mts.c: Delete mmdlm2; use same-valued mmdlm1 instead.
[nmh] / docs / historical / mh-6.8.5 / uip / mhn.c
1 /* mhn.c - multi-media MH */
2 #ifndef lint
3 static char ident[] = "@(#)$Id: mhn.c,v 2.42 1995/12/06 22:32:29 jromine Exp $";
4 #endif /* lint */
5
6 #include "../h/mh.h"
7 #include <ctype.h>
8 #include <errno.h>
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include "../zotnet/mts.h"
13 #include <sys/types.h>
14 #include <sys/stat.h>
15 #ifdef BSD42
16 #include <sys/wait.h>
17 #endif
18 #ifdef LOCALE
19 #include <locale.h>
20 #endif
21
22 /* \f */
23
24 static struct swit switches[] = {
25 #define AUTOSW 0
26 "auto", 0,
27 #define NAUTOSW 1
28 "noauto", 0,
29
30 #define CACHESW 2
31 "cache", 0,
32 #define NCACHESW 3
33 "nocache", 0,
34
35 #define CHECKSW 4
36 "check", 0,
37 #define NCHECKSW 5
38 "nocheck", 0,
39
40 #define DEBUGSW 6
41 "debug", -5,
42
43 #define EBCDICSW 7
44 "ebcdicsafe", 0,
45 #define NEBCDICSW 8
46 "noebcdicsafe", 0,
47
48 #define FILESW 9 /* interface from show */
49 "file file", 0,
50
51 #define FORMSW 10
52 "form formfile", 4,
53
54 #define HEADSW 11
55 "headers", 0,
56 #define NHEADSW 12
57 "noheaders", 0,
58
59 #define LISTSW 13
60 "list", 0,
61 #define NLISTSW 14
62 "nolist", 0,
63
64 #define PARTSW 15
65 "part number", 0,
66
67 #define PAUSESW 16
68 "pause", 0,
69 #define NPAUSESW 17
70 "nopause", 0,
71
72 #define RCACHESW 18
73 "rcache policy", 0,
74
75 #define SIZESW 19
76 "realsize", 0,
77 #define NSIZESW 20
78 "norealsize", 0,
79
80 #define RFC934SW 21
81 "rfc934mode", 0,
82 #define NRFC934SW 22
83 "norfc934mode", 0,
84
85 #define SERIALSW 23
86 "serialonly", 0,
87 #define NSERIALSW 24
88 "noserialonly", 0,
89
90 #define SHOWSW 25
91 "show", 0,
92 #define NSHOWSW 26
93 "noshow", 0,
94
95 #define STORESW 27
96 "store", 0,
97 #define NSTORESW 28
98 "nostore", 0,
99
100 #define TYPESW 29
101 "type content", 0,
102
103 #define VERBSW 30
104 "verbose", 0,
105 #define NVERBSW 31
106 "noverbose", 0,
107
108 #define WCACHESW 32
109 "wcache policy", 0,
110
111 #define HELPSW 33
112 "help", 4,
113
114 #define PROGSW 34
115 "moreproc program", -4,
116 #define NPROGSW 35
117 "nomoreproc", -3,
118
119 #define LENSW 36
120 "length lines", -4,
121 #define WIDSW 37
122 "width columns", -4,
123
124 #define VIAMSW 38
125 "viamail mailpath", -7,
126 #define VIASSW 39
127 "viasubj subject", -7,
128 #define VIAPSW 40
129 "viaparm arguments", -7,
130 #define VIADSW 41
131 "viadesc text", -7,
132 #define VIACSW 42
133 "viacmnt text", -7,
134 #define VIAZSW 43
135 "viadelay seconds", -8,
136 #define VIAFSW 44
137 "viafrom mailpath", -7,
138
139 NULL, 0
140 };
141
142 /* \f */
143
144 #define NPARTS 50
145 #define NTYPES 20
146
147 static struct swit caches[] = {
148 #define CACHE_NEVER 0
149 "never", 0,
150 #define CACHE_PRIVATE 1
151 "private", 0,
152 #define CACHE_PUBLIC 2
153 "public", 0,
154 #define CACHE_ASK 3
155 "ask", 0,
156
157 NULL, 0
158 };
159
160 static int autosw = 0;
161 static int cachesw = 0;
162 static int checksw = 0;
163 int debugsw = 0;
164 static int ebcdicsw = 0;
165 static char *formsw = NULLCP;
166 static int headsw = 1;
167 static int listsw = 0;
168 static int nolist = 0;
169 static int nomore = 0;
170 static int npart = 0;
171 static char *parts[NPARTS + 1];
172 static int pausesw = 1;
173 static char *progsw = NULLCP;
174 static int rcachesw = CACHE_ASK;
175 static int rfc934sw = 1;
176 static int serialsw = 0;
177 static int showsw = 0;
178 static int sizesw = 1;
179 static int storesw = 0;
180 static int ntype = 0;
181 static char *types[NTYPES + 1];
182 int verbosw = 0;
183 static int wcachesw = CACHE_ASK;
184
185 static int endian = 0;
186 static char *mm_charset = NULL;
187
188 static int xpid = 0;
189 static int userrs = 0;
190
191 static char *cache_public;
192 static char *cache_private;
193 static int cwdlen;
194 static char *cwd;
195 static char *dir;
196 static char *errs = NULL;
197 static char *tmp;
198
199
200 extern int errno;
201 #ifndef BSD44
202 extern int sys_nerr;
203 extern char *sys_errlist[];
204 #endif
205
206 off_t lseek ();
207 time_t time ();
208
209 /* \f */
210
211 #define LSTFMT1 "%4s %-5s %-24s %5s %-36s\n"
212
213 #define LSTFMT2a "%4d "
214 #define LSTFMT2b "%-5s %-24.24s "
215 #define LSTFMT2c1 "%5lu"
216 #define LSTFMT2c2 "%4lu%c"
217 #define LSTFMT2c3 "huge "
218 #define LSTFMT2c4 " "
219 #define LSTFMT2d1 " %-36.36s"
220 #define LSTFMT2d2 "\t %-65.65s\n"
221
222 static void build_comp ();
223
224 typedef struct CTinfo {
225 char *ci_type;
226 char *ci_subtype;
227
228 #define NPARMS 10
229 char *ci_attrs[NPARMS + 2];
230 char *ci_values[NPARMS];
231
232 char *ci_comment;
233
234 char *ci_magic;
235 } CTInfo, *CI;
236 #define NULLCI ((CI) 0)
237
238 static int get_ctinfo ();
239 static int get_comment ();
240
241
242 typedef struct Content {
243 char *c_partno; /* within multipart content */
244
245 char *c_vrsn; /* Body-Version: */
246
247 char *c_ctline; /* Content-Type: */
248 CTInfo c_ctinfo;
249
250 int c_type; /* internal form */
251 #define CT_UNKNOWN 0x00
252 #define CT_APPLICATION 0x01
253 #define CT_AUDIO 0x02
254 #define CT_IMAGE 0x03
255 #define CT_MESSAGE 0x04
256 #define CT_MULTIPART 0x05
257 #define CT_TEXT 0x06
258 #define CT_VIDEO 0x07
259 #define CT_EXTENSION 0x08
260
261 int c_subtype; /* filled-in by c_ctinitfnx */
262 caddr_t c_ctparams; /* .. */
263 caddr_t c_ctextern; /* .. */
264 char *c_showproc; /* default, if not in profile */
265 char *c_termproc; /* for charset madness... */
266 char *c_storeproc; /* overrides profile entry, if any */
267
268 int (*c_ctinitfnx) (); /* parse content */
269 int (*c_ctlistfnx) (); /* list content */
270 int (*c_ctshowfnx) (); /* show content */
271 int (*c_ctstorefnx) (); /* store content */
272 int (*c_ctfreefnx) (); /* free content-specific structures */
273
274
275 char *c_celine; /* Content-Transfer-Encoding: */
276
277 int c_encoding; /* internal form */
278 #define CE_UNKNOWN 0x00
279 #define CE_BASE64 0x01
280 #define CE_QUOTED 0x02
281 #define CE_8BIT 0x03
282 #define CE_7BIT 0x04
283 #define CE_BINARY 0x05
284 #define CE_EXTENSION 0x06
285 #define CE_EXTERNAL 0x07 /* for external-body */
286
287 caddr_t c_ceparams; /* filled-in by encoding initfnx */
288
289 int (*c_ceopenfnx) (); /* get a stream to decoded contents */
290 int (*c_ceclosefnx) (); /* release stream */
291 int (*c_celistfnx) (); /* list decoding info */
292 unsigned long
293 (*c_cesizefnx) (); /* size of decoded contents */
294 int (*c_cefreefnx) (); /* free encoding-specific structures */
295
296
297 char *c_id; /* Content-ID: */
298 char *c_descr; /* Content-Description: */
299
300 int c_digested; /* Content-MD5: */
301 unsigned char c_digest[16]; /* .. */
302
303 FILE *c_fp; /* read contents (stream) */
304 char *c_file; /* read contents (file) */
305 int c_unlink; /* remove file when done? */
306 int c_umask; /* associated umask */
307 long c_begin; /* where content starts in file */
308 long c_end; /* .. ends */
309
310 int c_pid; /* process doing display */
311 char *c_storage; /* write contents (file) */
312
313 int c_rfc934; /* rfc934 compatibility */
314 } Content, *CT;
315 #define NULLCT ((CT) 0)
316
317 static CT get_content ();
318 static int list_content (), show_content (), store_content ();
319 static int cache_content ();
320 static int user_content (), compose_content (), output_content ();
321 static void free_content (), flush_errors (), set_id ();
322
323 #if defined(__STDC__) && defined(VSPRINTF)
324 static void content_error (char *, register CT, char *, ...);
325 #else
326 static void content_error ();
327 #endif
328
329 static int init_encoding (), type_ok (), copy_some_headers (), set_endian ();
330 static int make_intermediates ();
331 static int find_cache (), find_cache_aux (), find_cache_aux2 ();
332 static int write7Bit (), writeQuoted (), writeBase64 (), writeBase64aux ();
333 static int writeDigest (), readDigest ();
334 static int via_mail (), via_post (), pidcheck ();
335
336 static CT *cts = NULL;
337
338
339 #define quitser pipeser
340 static TYPESIG pipeser ();
341 static char *fgetstr ();
342
343 /* \f */
344
345 /* ARGSUSED */
346
347 main (argc, argv)
348 int argc;
349 char **argv;
350 {
351 int f6 = 0,
352 msgp = 0,
353 msgnum,
354 *icachesw;
355 char *cp,
356 *f1 = NULL,
357 *f2 = NULL,
358 *f3 = NULL,
359 *f4 = NULL,
360 *f5 = NULL,
361 *f7 = NULL,
362 *file = NULL,
363 *folder = NULL,
364 *maildir,
365 buf[100],
366 **ap,
367 **argp,
368 *arguments[MAXARGS],
369 *msgs[MAXARGS];
370 struct msgs *mp;
371 register CT ct,
372 *ctp;
373 FILE *fp;
374
375 #ifdef LOCALE
376 setlocale(LC_ALL, "");
377 #endif
378 invo_name = r1bindex (argv[0], '/');
379 if (argv[1] && uprf (argv[1], "-via"))
380 m_foil (NULLCP);
381 if ((cp = m_find (invo_name)) != NULL) {
382 ap = brkstring (cp = getcpy (cp), " ", "\n");
383 ap = copyip (ap, arguments);
384 }
385 else
386 ap = arguments;
387 (void) copyip (argv + 1, ap);
388 argp = arguments;
389
390 /* \f */
391
392 while (cp = *argp++) {
393 if (*cp == '-')
394 switch (smatch (++cp, switches)) {
395 case AMBIGSW:
396 ambigsw (cp, switches);
397 done (1);
398 case UNKWNSW:
399 adios (NULLCP, "-%s unknown", cp);
400 case HELPSW:
401 (void) sprintf (buf, "%s [+folder] [msgs] [switches]",
402 invo_name);
403 help (buf, switches);
404 done (1);
405
406 case AUTOSW:
407 autosw++;
408 continue;
409 case NAUTOSW:
410 autosw = 0;
411 continue;
412
413 case CACHESW:
414 cachesw++;
415 continue;
416 case NCACHESW:
417 cachesw = 0;
418 continue;
419
420 case RCACHESW:
421 icachesw = &rcachesw;
422 goto do_cache;
423 case WCACHESW:
424 icachesw = &wcachesw;
425 do_cache: ;
426 if (!(cp = *argp++) || *cp == '-')
427 adios (NULLCP, "missing argument to %s", argp[-2]);
428 switch (*icachesw = smatch (cp, caches)) {
429 case AMBIGSW:
430 ambigsw (cp, caches);
431 done (1);
432 case UNKWNSW:
433 adios (NULLCP, "%s unknown", cp);
434 default:
435 break;
436 }
437 continue;
438
439 case CHECKSW:
440 checksw++;
441 continue;
442 case NCHECKSW:
443 checksw = 0;
444 continue;
445
446 case DEBUGSW:
447 debugsw++;
448 continue;
449
450 case EBCDICSW:
451 ebcdicsw++;
452 continue;
453 case NEBCDICSW:
454 ebcdicsw = 0;
455 continue;
456
457 case FORMSW:
458 if (!(cp = *argp++) || *cp == '-')
459 adios (NULLCP, "missing argument to %s", argp[-2]);
460 if (formsw)
461 free (formsw);
462 formsw = getcpy (libpath (cp));
463 continue;
464
465 case HEADSW:
466 headsw++;
467 continue;
468 case NHEADSW:
469 headsw = 0;
470 continue;
471
472 case LISTSW:
473 listsw++;
474 continue;
475 case NLISTSW:
476 listsw = 0;
477 continue;
478
479 case PARTSW:
480 if (!(cp = *argp++) || *cp == '-')
481 adios (NULLCP, "missing argument to %s", argp[-2]);
482 if (npart >= NPARTS)
483 adios (NULLCP,
484 "too many parts (starting with %s), %d max",
485 cp, NPARTS);
486 parts[npart++] = cp;
487 continue;
488
489 case PAUSESW:
490 pausesw++;
491 continue;
492 case NPAUSESW:
493 pausesw = 0;
494 continue;
495
496 case RFC934SW:
497 rfc934sw++;
498 continue;
499 case NRFC934SW:
500 rfc934sw = 0;
501 continue;
502
503 case SERIALSW:
504 serialsw++;
505 continue;
506 case NSERIALSW:
507 serialsw = 0;
508 continue;
509
510 case SHOWSW:
511 showsw++;
512 continue;
513 case NSHOWSW:
514 showsw = 0;
515 continue;
516
517 case SIZESW:
518 sizesw++;
519 continue;
520 case NSIZESW:
521 sizesw = 0;
522 continue;
523
524 case STORESW:
525 storesw++;
526 continue;
527 case NSTORESW:
528 storesw = 0;
529 continue;
530
531 case TYPESW:
532 if (!(cp = *argp++) || *cp == '-')
533 adios (NULLCP, "missing argument to %s", argp[-2]);
534 if (ntype >= NTYPES)
535 adios (NULLCP,
536 "too many types (starting with %s), %d max",
537 cp, NTYPES);
538 types[ntype++] = cp;
539 continue;
540
541 case VERBSW:
542 verbosw++;
543 continue;
544 case NVERBSW:
545 verbosw = 0;
546 continue;
547
548 case PROGSW:
549 if (!(progsw = *argp++) || *progsw == '-')
550 adios (NULLCP, "missing argument to %s", argp[-2]);
551 continue;
552 case NPROGSW:
553 nomore++;
554 continue;
555
556 case LENSW:
557 case WIDSW:
558 if (!(cp = *argp++) || *cp == '-')
559 adios (NULLCP, "missing argument to %s", argp[-2]);
560 continue;
561
562 case FILESW:
563 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
564 adios (NULLCP, "missing argument to %s", argp[-2]);
565 file = *cp == '-' ? cp : path (cp, TFILE);
566 continue;
567
568 case VIAMSW:
569 if (!(f1 = *argp++))
570 adios (NULLCP, "missing argument to %s", argp[-2]);
571 continue;
572 case VIASSW:
573 if (!(f2 = *argp++))
574 adios (NULLCP, "missing argument to %s", argp[-2]);
575 continue;
576 case VIAPSW:
577 if (!(f3 = *argp++))
578 adios (NULLCP, "missing argument to %s", argp[-2]);
579 continue;
580 case VIADSW:
581 if (!(f4 = *argp++))
582 adios (NULLCP, "missing argument to %s", argp[-2]);
583 continue;
584 case VIACSW:
585 if (!(f5 = *argp++))
586 adios (NULLCP, "missing argument to %s", argp[-2]);
587 continue;
588 case VIAZSW:
589 if (!(cp = *argp++) || *cp == '-')
590 adios (NULLCP, "missing argument to %s", argp[-2]);
591 if (sscanf (cp, "%d", &f6) != 1 || f6 < 0)
592 f6 = 300;
593 continue;
594 case VIAFSW:
595 if (!(f7 = *argp++))
596 adios (NULLCP, "missing argument to %s", argp[-2]);
597 continue;
598 }
599 if (*cp == '+' || *cp == '@') {
600 if (folder)
601 adios (NULLCP, "only one folder at a time!");
602 else
603 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
604 }
605 else
606 msgs[msgp++] = cp;
607 }
608 parts[npart] = NULL, types[ntype] = NULL;
609 if (!formsw)
610 formsw = getcpy (libpath ("mhl.headers"));
611
612 set_endian ();
613
614 /* \f */
615
616 if (f1) {
617 via_mail (f1, f2, f3, f4, f5, f6, f7);
618 /* NOTREACHED */
619 }
620 else
621 if (f2 || f3 || f4 || f5 || f6 || f7)
622 adios (NULLCP, "missing -viamail \"mailpath\" switch");
623
624 if (cp = getenv ("MHN")) {
625 if (fp = fopen (cp, "r")) {
626 m_readefs ((struct node **) 0, fp, cp, 0);
627
628 (void) fclose (fp);
629 }
630 else
631 admonish ("", "unable to read $MHN profile (%s)", cp);
632 }
633 if (fp = fopen (cp = libpath ("mhn_defaults"), "r")) {
634 m_readefs ((struct node **) 0, fp, cp, 0);
635
636 (void) fclose (fp);
637 }
638
639 (void) sprintf (buf, "%s-cache", invo_name);
640 if ((cache_public = m_find (buf)) && *cache_public != '/')
641 cache_public = NULL;
642 (void) sprintf (buf, "%s-private-cache", invo_name);
643 if (!(cache_private = m_find (buf)))
644 cache_private = ".cache";
645 cache_private = getcpy (m_maildir (cache_private));
646
647 cwdlen = strlen (cwd = getcpy (pwd ()));
648 (void) sprintf (buf, "%s-storage", invo_name);
649 dir = getcpy ((cp = m_find (buf)) && *cp ? cp : cwd);
650 tmp = cp && *cp ? concat (cp, "/", invo_name, NULLCP)
651 : add (m_maildir (invo_name), NULLCP);
652
653 if (!m_find ("path"))
654 free (path ("./", TFOLDER));
655
656 if (msgp == 1
657 && !folder
658 && !npart
659 && !cachesw
660 && !showsw
661 && !storesw
662 && !ntype
663 && !file
664 && (cp = getenv ("mhdraft"))
665 && strcmp (cp, msgs[0]) == 0) {
666 build_comp (cp);
667 /* NOTREACHED */
668 }
669
670 if (file) {
671 int stdinP;
672
673 if (msgp)
674 adios (NULLCP, "only one file at a time!");
675
676 if ((cts = (CT *) calloc ((unsigned) 2, sizeof *cts)) == NULL)
677 adios (NULLCP, "out of memory");
678
679 ctp = cts;
680 if (stdinP = (strcmp (file, "-") == 0)) {
681 char buffer[BUFSIZ];
682
683 file = add (m_tmpfil (invo_name), NULLCP);
684
685 if ((fp = fopen (file, "w+")) == NULL)
686 adios (file, "unable to fopen for writing and reading");
687 (void) chmod (file, 0600);
688 while (fgets (buffer, sizeof buffer, stdin))
689 (void) fputs (buffer, fp);
690 (void) fflush (fp);
691
692 if (ferror (stdin)) {
693 (void) unlink (file);
694 adios ("stdin", "error reading");
695 }
696
697 if (ferror (fp)) {
698 (void) unlink (file);
699 adios (file, "error writing");
700 }
701
702 (void) fseek (fp, 0L, 0);
703 }
704 else
705 if ((fp = fopen (file, "r")) == NULL)
706 adios (file, "unable to read");
707
708 if (ct = get_content (fp, file, 1)) {
709 if (stdinP)
710 ct -> c_unlink = 1;
711
712 ct -> c_fp = NULL;
713 if (ct -> c_end == 0L) {
714 (void) fseek (fp, 0L, 2);
715 ct -> c_end = ftell (fp);
716 }
717 if (ct -> c_ctinitfnx && (*ct -> c_ctinitfnx) (ct) == NOTOK)
718 free_content (ct);
719 else
720 *ctp++ = ct;
721 }
722 else
723 advise (NULLCP, "unable to decode %s", file);
724
725 (void) fclose (fp);
726
727 mp = NULL;
728 goto go_to_it;
729 }
730
731 if (!msgp)
732 msgs[msgp++] = "cur";
733 if (!folder)
734 folder = m_getfolder ();
735 maildir = m_maildir (folder);
736
737 if (chdir (maildir) == NOTOK)
738 adios (maildir, "unable to change directory to");
739 if (!(mp = m_gmsg (folder)))
740 adios (NULLCP, "unable to read folder %s", folder);
741 if (mp -> hghmsg == 0)
742 adios (NULLCP, "no messages in %s", folder);
743
744 for (msgnum = 0; msgnum < msgp; msgnum++)
745 if (!m_convert (mp, msgs[msgnum]))
746 done (1);
747 m_setseq (mp);
748
749 if ((cts = (CT *) calloc ((unsigned) (mp -> numsel + 1), sizeof *cts))
750 == NULL)
751 adios (NULLCP, "out of memory");
752
753 ctp = cts;
754 for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
755 if (mp -> msgstats[msgnum] & SELECTED) {
756 char *msgnam;
757
758 if ((fp = fopen (msgnam = m_name (msgnum), "r")) == NULL)
759 adios (msgnam, "unable to read message");
760
761 if (ct = get_content (fp, msgnam, 1)) {
762 ct -> c_fp = NULL;
763 if (ct -> c_end == 0L) {
764 (void) fseek (fp, 0L, 2);
765 ct -> c_end = ftell (fp);
766 }
767 if (ct -> c_ctinitfnx && (*ct -> c_ctinitfnx) (ct) == NOTOK)
768 free_content (ct);
769 else
770 *ctp++ = ct;
771 }
772 else
773 advise (NULLCP, "unable to decode message %s", msgnam);
774
775 (void) fclose (fp);
776 }
777
778 go_to_it: ;
779 if (!*cts)
780 done (1);
781
782 if (!listsw && !showsw && !storesw && !cachesw)
783 showsw++;
784
785 /* listsw && showsw -> user wants per-message listing,
786 so delay until showsw processing
787
788 && storesw && sizesw -> evaluating size will cause more work,
789 so delay until after storesw processing
790 */
791 userrs = 1;
792 (void) signal (SIGQUIT, quitser);
793 (void) signal (SIGPIPE, pipeser);
794
795 for (ctp = cts; ct = *ctp; ctp++)
796 if (type_ok (ct, 1)
797 && (ct -> c_ctlistfnx
798 || ct -> c_ctstorefnx
799 || ct -> c_ctshowfnx)) {
800 struct stat st;
801
802 if (!ct -> c_umask)
803 ct -> c_umask = ~(stat (ct -> c_file, &st) != NOTOK
804 ? (st.st_mode & 0777) : m_gmprot ());
805 }
806
807 if (listsw && !showsw && (!storesw || !sizesw)) {
808 if (headsw)
809 printf (LSTFMT1, "msg", "part", "type/subtype", "size",
810 "description");
811
812 for (ctp = cts; ct = *ctp; ctp++)
813 if (type_ok (ct, 1) && ct -> c_ctlistfnx) {
814 (void) umask (ct -> c_umask);
815 (void) (*ct -> c_ctlistfnx) (ct, 1);
816 if (ct -> c_fp)
817 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
818 if (ct -> c_ceclosefnx)
819 (*ct -> c_ceclosefnx) (ct);
820 }
821
822 flush_errors ();
823 }
824
825 if (storesw) {
826 for (ctp = cts; ct = *ctp; ctp++)
827 if (type_ok (ct, 1) && ct -> c_ctstorefnx) {
828 (void) umask (ct -> c_umask);
829 (void) (*ct -> c_ctstorefnx) (ct, NULLCP);
830 if (ct -> c_fp)
831 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
832 if (ct -> c_ceclosefnx)
833 (*ct -> c_ceclosefnx) (ct);
834 }
835
836 flush_errors ();
837 }
838
839 if (cachesw) {
840 for (ctp = cts; ct = *ctp; ctp++)
841 if (type_ok (ct, 1)) {
842 cache_content (ct);
843 if (ct -> c_fp)
844 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
845 if (ct -> c_ceclosefnx)
846 (*ct -> c_ceclosefnx) (ct);
847 }
848
849 flush_errors ();
850 }
851
852 if (listsw && !showsw && storesw && sizesw) {
853 if (headsw)
854 printf (LSTFMT1, "msg", "part", "type/subtype", "size",
855 "description");
856
857 for (ctp = cts; ct = *ctp; ctp++)
858 if (type_ok (ct, 1) && ct -> c_ctlistfnx) {
859 (void) umask (ct -> c_umask);
860 (void) (*ct -> c_ctlistfnx) (ct, 1);
861 if (ct -> c_fp)
862 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
863 if (ct -> c_ceclosefnx)
864 (*ct -> c_ceclosefnx) (ct);
865 }
866
867 flush_errors ();
868 listsw = 0;
869 }
870
871 if (showsw)
872 for (ctp = cts; ct = *ctp; ctp++) {
873 #if defined(BSD42) && !defined(WAITINT)
874 union wait status;
875 #else
876 int status;
877 #endif
878 TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) ();
879
880 if (!type_ok (ct, 0))
881 continue;
882
883 (void) umask (ct -> c_umask);
884
885 if (listsw) {
886 if (headsw)
887 printf (LSTFMT1, "msg", "part", "type/subtype", "size",
888 "description");
889
890 if (ct -> c_ctlistfnx)
891 (void) (*ct -> c_ctlistfnx) (ct, 1);
892 }
893
894 if (!ct -> c_ctshowfnx) {
895 if (ct -> c_fp)
896 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
897 if (ct -> c_ceclosefnx)
898 (*ct -> c_ceclosefnx) (ct);
899 continue;
900 }
901
902 if (strcmp (formsw, "mhl.null")) {
903 int child_id,
904 i,
905 vecp;
906 char *vec[8];
907
908 vecp = 0;
909 vec[vecp++] = r1bindex (mhlproc, '/');
910 vec[vecp++] = "-form";
911 vec[vecp++] = formsw;
912 vec[vecp++] = "-nobody";
913 vec[vecp++] = ct -> c_file;
914 if (nomore)
915 vec[vecp++] = "-nomoreproc";
916 else
917 if (progsw) {
918 vec[vecp++] = "-moreproc";
919 vec[vecp++] = progsw;
920 }
921 vec[vecp] = NULL;
922
923 (void) fflush (stdout);
924
925 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
926 sleep (5);
927 switch (child_id) {
928 case NOTOK:
929 adios ("fork", "unable to");
930 /* NOTREACHED */
931
932 case OK:
933 (void) execvp (mhlproc, vec);
934 fprintf (stderr, "unable to exec ");
935 perror (mhlproc);
936 _exit (-1);
937 /* NOTREACHED */
938
939 default:
940 xpid = -child_id;
941 break;
942 }
943 }
944 else
945 xpid = 0;
946
947 (void) (*ct -> c_ctshowfnx) (ct, 1, 0);
948 if (ct -> c_fp)
949 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
950 if (ct -> c_ceclosefnx)
951 (*ct -> c_ceclosefnx) (ct);
952
953 hstat = signal (SIGHUP, SIG_IGN);
954 istat = signal (SIGINT, SIG_IGN);
955 qstat = signal (SIGQUIT, SIG_IGN);
956 tstat = signal (SIGTERM, SIG_IGN);
957
958 while (wait (&status) != NOTOK) {
959 #if defined(BSD42) && !defined(WAITINT)
960 (void) pidcheck (status.w_status);
961 #else
962 (void) pidcheck (status);
963 #endif
964 continue;
965 }
966
967 (void) signal (SIGHUP, hstat);
968 (void) signal (SIGINT, istat);
969 (void) signal (SIGQUIT, qstat);
970 (void) signal (SIGTERM, tstat);
971
972 xpid = 0;
973
974 flush_errors ();
975 }
976
977 for (ctp = cts; *ctp; ctp++)
978 free_content (*ctp);
979 free ((char *) cts);
980 cts = NULL;
981
982 if (mp) {
983 m_replace (pfolder, folder);
984 if (mp -> hghsel != mp -> curmsg)
985 m_setcur (mp, mp -> hghsel);
986 m_sync (mp);
987 m_update ();
988 }
989
990 done (0);
991 /* NOTREACHED */
992 }
993
994 /* \f */
995
996 static TYPESIG pipeser (i)
997 int i;
998 {
999 if (i == SIGQUIT) {
1000 (void) unlink ("core");
1001
1002 (void) fflush (stdout);
1003
1004 fprintf (stderr, "\n");
1005 (void) fflush (stderr);
1006 }
1007
1008 done (1);
1009 /* NOTREACHED */
1010 }
1011
1012 /* \f */
1013
1014 #include "../h/mhn.h"
1015
1016
1017 struct str2init {
1018 char *si_key;
1019 int si_val;
1020 int (*si_init) ();
1021 };
1022
1023
1024 static int InitApplication (), InitMessage (), InitMultiPart (), InitText (),
1025 InitGeneric ();
1026
1027
1028 static struct str2init str2cts[] = {
1029 "application", CT_APPLICATION, InitApplication,
1030 "audio", CT_AUDIO, InitGeneric,
1031 "image", CT_IMAGE, InitGeneric,
1032 "message", CT_MESSAGE, InitMessage,
1033 "multipart", CT_MULTIPART, InitMultiPart,
1034 "text", CT_TEXT, InitText,
1035 "video", CT_VIDEO, InitGeneric,
1036
1037 NULL, CT_EXTENSION, NULL, /* these two must be last! */
1038 NULL, CT_UNKNOWN, NULL,
1039 };
1040
1041
1042 static int InitBase64 (), InitQuoted (), Init7Bit ();
1043
1044 static struct str2init str2ces[] = {
1045 "base64", CE_BASE64, InitBase64,
1046 "quoted-printable", CE_QUOTED, InitQuoted,
1047 "8bit", CE_8BIT, Init7Bit,
1048 "7bit", CE_7BIT, Init7Bit,
1049 "binary", CE_BINARY, NULL,
1050
1051 NULL, CE_EXTENSION, NULL, /* these two must be last! */
1052 NULL, CE_UNKNOWN, NULL,
1053 };
1054
1055 /* \f */
1056
1057 static CT get_content (in, file, toplevel)
1058 FILE *in;
1059 char *file;
1060 int toplevel;
1061 {
1062 int compnum,
1063 state;
1064 char buf[BUFSIZ],
1065 name[NAMESZ];
1066 register CT ct;
1067
1068 if ((ct = (CT) calloc (1, sizeof *ct)) == NULL)
1069 adios (NULLCP, "out of memory");
1070
1071 ct -> c_begin = ftell (ct -> c_fp = in) + 1;
1072 ct -> c_file = add (file, NULLCP);
1073 for (compnum = 1, state = FLD;;) {
1074 switch (state = m_getfld (state, name, buf, sizeof buf, in)) {
1075 case FLD:
1076 case FLDPLUS:
1077 case FLDEOF:
1078 compnum++;
1079
1080 if (uleq (name, VRSN_FIELD)) {
1081 int ucmp;
1082 char c,
1083 *cp,
1084 *dp;
1085
1086 cp = add (buf, NULLCP);
1087 while (state == FLDPLUS) {
1088 state = m_getfld (state, name, buf, sizeof buf, in);
1089 cp = add (buf, cp);
1090 }
1091
1092 if (ct -> c_vrsn) {
1093 /*
1094 advise (NULLCP,
1095 "message %s has multiple %s: fields (%s)",
1096 ct -> c_file, VRSN_FIELD, dp = trimcpy (cp));
1097 free (dp);
1098 */
1099 free (cp);
1100 /*
1101 goto out;
1102 */
1103 goto got_header;
1104 }
1105
1106 ct -> c_vrsn = cp;
1107 while (isspace (*cp))
1108 cp++;
1109 for (dp = index (cp, '\n'); dp; dp = index (dp, '\n'))
1110 *dp++ = ' ';
1111 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1112 if (!isspace (*dp))
1113 break;
1114 *++dp = '\0';
1115 if (debugsw)
1116 fprintf (stderr, "%s: %s\n", VRSN_FIELD, cp);
1117
1118 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK)
1119 goto out;
1120
1121 for (dp = cp; istoken (*dp); dp++)
1122 continue;
1123 c = *dp, *dp = '\0';
1124 ucmp = uleq (cp, VRSN_VALUE);
1125 *dp = c;
1126 if (!ucmp)
1127 admonish (NULLCP,
1128 "message %s has unknown value for %s: field (%s)",
1129 ct -> c_file, VRSN_FIELD, cp);
1130 goto got_header;
1131 }
1132
1133 if (uleq (name, TYPE_FIELD)) {
1134 register char *cp;
1135 register struct str2init *s2i;
1136 register CI ci = &ct -> c_ctinfo;
1137
1138 cp = add (buf, NULLCP);
1139 while (state == FLDPLUS) {
1140 state = m_getfld (state, name, buf, sizeof buf, in);
1141 cp = add (buf, cp);
1142 }
1143
1144 if (ct -> c_ctline) {
1145 char *dp = trimcpy (cp);
1146
1147 advise (NULLCP,
1148 "message %s has multiple %s: fields (%s)",
1149 ct -> c_file, TYPE_FIELD, dp);
1150 free (dp);
1151 free (cp);
1152 goto out;
1153 }
1154
1155 if (get_ctinfo (cp, ct, 0) == NOTOK)
1156 goto out;
1157 for (s2i = str2cts; s2i -> si_key; s2i++)
1158 if (uleq (ci -> ci_type, s2i -> si_key))
1159 break;
1160 if (!s2i -> si_key && !uprf (ci -> ci_type, "X-"))
1161 s2i++;
1162 ct -> c_type = s2i -> si_val;
1163 ct -> c_ctinitfnx = s2i -> si_init;
1164 goto got_header;
1165 }
1166
1167 if (uleq (name, ENCODING_FIELD)) {
1168 register char *cp,
1169 *dp;
1170 char c;
1171 register struct str2init *s2i;
1172
1173 cp = add (buf, NULLCP);
1174 while (state == FLDPLUS) {
1175 state = m_getfld (state, name, buf, sizeof buf, in);
1176 cp = add (buf, cp);
1177 }
1178
1179 if (ct -> c_celine) {
1180 advise (NULLCP,
1181 "message %s has multiple %s: fields (%s)",
1182 ct -> c_file, ENCODING_FIELD,
1183 dp = trimcpy (cp));
1184 free (dp);
1185 free (cp);
1186 goto out;
1187 }
1188
1189 ct -> c_celine = cp;
1190 while (isspace (*cp))
1191 cp++;
1192 for (dp = cp; istoken (*dp); dp++)
1193 continue;
1194 c = *dp, *dp = '\0';
1195 for (s2i = str2ces; s2i -> si_key; s2i++)
1196 if (uleq (cp, s2i -> si_key))
1197 break;
1198 if (!s2i -> si_key && !uprf (cp, "X-"))
1199 s2i++;
1200 *dp = c;
1201 ct -> c_encoding = s2i -> si_val;
1202 if (s2i -> si_init && (*s2i -> si_init) (ct) == NOTOK)
1203 goto out;
1204 goto got_header;
1205 }
1206
1207 if (uleq (name, ID_FIELD)) {
1208 ct -> c_id = add (buf, ct -> c_id);
1209 while (state == FLDPLUS) {
1210 state = m_getfld (state, name, buf, sizeof buf, in);
1211 ct -> c_id = add (buf, ct -> c_id);
1212 }
1213 goto got_header;
1214 }
1215
1216 if (uleq (name, DESCR_FIELD)) {
1217 ct -> c_descr = add (buf, ct -> c_descr);
1218 while (state == FLDPLUS) {
1219 state = m_getfld (state, name, buf, sizeof buf, in);
1220 ct -> c_descr = add (buf, ct -> c_descr);
1221 }
1222 goto got_header;
1223 }
1224
1225 if (uleq (name, MD5_FIELD)) {
1226 char *cp,
1227 *dp,
1228 *ep;
1229
1230 cp = add (buf, NULLCP);
1231 while (state == FLDPLUS) {
1232 state = m_getfld (state, name, buf, sizeof buf, in);
1233 cp = add (buf, cp);
1234 }
1235
1236 if (!checksw) {
1237 free (cp);
1238 goto got_header;
1239 }
1240
1241 if (ct -> c_digested) {
1242 advise (NULLCP,
1243 "message %s has multiple %s: fields (%s)",
1244 ct -> c_file, MD5_FIELD,
1245 dp = trimcpy (cp));
1246 free (dp);
1247 free (cp);
1248 goto out;
1249 }
1250
1251 ep = cp;
1252 while (isspace (*cp))
1253 cp++;
1254 for (dp = index (cp, '\n'); dp; dp = index (dp, '\n'))
1255 *dp++ = ' ';
1256 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1257 if (!isspace (*dp))
1258 break;
1259 *++dp = '\0';
1260 if (debugsw)
1261 fprintf (stderr, "%s: %s\n", MD5_FIELD, cp);
1262
1263 if (*cp == '(' && get_comment (ct, &cp, 0) == NOTOK) {
1264 free (ep);
1265 goto out;
1266 }
1267
1268 for (dp = cp; *dp && !isspace (*dp); dp++)
1269 continue;
1270 *dp = '\0';
1271
1272 (void) readDigest (ct, cp);
1273 free (ep);
1274 ct -> c_digested++;
1275 goto got_header;
1276 }
1277
1278 #ifdef notdef
1279 if (uprf (name, XXX_FIELD_PRF))
1280 advise (NULLCP, "unknown field (%s) in message %s",
1281 name, ct -> c_file);
1282 /* and fall... */
1283 #endif
1284
1285 while (state == FLDPLUS)
1286 state = m_getfld (state, name, buf, sizeof buf, in);
1287 got_header: ;
1288 if (state != FLDEOF) {
1289 ct -> c_begin = ftell (in) + 1;
1290 continue;
1291 }
1292 /* else fall... */
1293
1294 case BODY:
1295 case BODYEOF:
1296 ct -> c_begin = ftell (in) - strlen (buf);
1297 break;
1298
1299 case FILEEOF:
1300 ct -> c_begin = ftell (in);
1301 break;
1302
1303 case LENERR:
1304 case FMTERR:
1305 adios (NULLCP, "message format error in component #%d",
1306 compnum);
1307
1308 default:
1309 adios (NULLCP, "getfld() returned %d", state);
1310 }
1311 break;
1312 }
1313
1314 if (!ct -> c_ctline) {
1315 if (toplevel < 0) {
1316 if (get_ctinfo ("message/rfc822", ct, 0) == NOTOK)
1317 goto out;
1318 ct -> c_type = CT_MESSAGE;
1319 ct -> c_ctinitfnx = InitMessage;
1320 }
1321 else {
1322 if (get_ctinfo ("text/plain", ct, 0) == NOTOK)
1323 goto out;
1324 ct -> c_type = CT_TEXT;
1325 ct -> c_ctinitfnx = InitText;
1326 }
1327 }
1328 if (!ct -> c_ctlistfnx)
1329 ct -> c_ctlistfnx = list_content;
1330 if (!ct -> c_ctshowfnx)
1331 ct -> c_ctshowfnx = show_content;
1332 if (!ct -> c_ctstorefnx)
1333 ct -> c_ctstorefnx = store_content;
1334
1335 if (!ct -> c_celine) {
1336 ct -> c_encoding = CE_7BIT;
1337 (void) Init7Bit (ct);
1338 }
1339
1340 return ct;
1341
1342 out:
1343 free_content (ct);
1344 return NULLCT;
1345 }
1346
1347 /* \f */
1348
1349 static int get_ctinfo (cp, ct, magic)
1350 char *cp;
1351 register CT ct;
1352 int magic;
1353 {
1354 int i = strlen (invo_name) + 2;
1355 register char *dp,
1356 **ap,
1357 **ep;
1358 char c;
1359 register CI ci = &ct -> c_ctinfo;
1360
1361 cp = ct -> c_ctline = add (cp, NULLCP);
1362 while (isspace (*cp))
1363 cp++;
1364 for (dp = index (cp, '\n'); dp; dp = index (dp, '\n'))
1365 *dp++ = ' ';
1366 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1367 if (!isspace (*dp))
1368 break;
1369 *++dp = '\0';
1370 if (debugsw)
1371 fprintf (stderr, "%s: %s\n", TYPE_FIELD, cp);
1372
1373 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1374 return NOTOK;
1375
1376 for (dp = cp; istoken (*dp); dp++)
1377 continue;
1378 c = *dp, *dp = '\0';
1379 ci -> ci_type = add (cp, NULLCP);
1380 *dp = c, cp = dp;
1381
1382 if (!*ci -> ci_type) {
1383 advise (NULLCP, "invalid %s: field in message %s (empty type)",
1384 TYPE_FIELD, ct -> c_file);
1385 return NOTOK;
1386 }
1387
1388 for (dp = ci -> ci_type; *dp; dp++)
1389 if (isalpha(*dp) && isupper (*dp))
1390 *dp = tolower (*dp);
1391
1392 while (isspace (*cp))
1393 cp++;
1394
1395 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1396 return NOTOK;
1397
1398 if (*cp != '/') {
1399 if (!magic)
1400 ci -> ci_subtype = add ("", NULLCP);
1401 goto magic_skip;
1402 }
1403
1404 cp++;
1405 while (isspace (*cp))
1406 cp++;
1407
1408 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1409 return NOTOK;
1410
1411 for (dp = cp; istoken (*dp); dp++)
1412 continue;
1413 c = *dp, *dp = '\0';
1414 ci -> ci_subtype = add (cp, NULLCP);
1415 *dp = c, cp = dp;
1416
1417 if (!*ci -> ci_subtype) {
1418 advise (NULLCP,
1419 "invalid %s: field in message %s (empty subtype for \"%s\")",
1420 TYPE_FIELD, ct -> c_file, ci -> ci_type);
1421 return NOTOK;
1422 }
1423
1424 for (dp = ci -> ci_subtype; *dp; dp++)
1425 if (isalpha(*dp) && isupper (*dp))
1426 *dp = tolower (*dp);
1427
1428 magic_skip: ;
1429 while (isspace (*cp))
1430 cp++;
1431
1432 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1433 return NOTOK;
1434
1435 ep = (ap = ci -> ci_attrs) + NPARMS;
1436 while (*cp == ';') {
1437 char *vp,
1438 *up;
1439
1440 if (ap >= ep) {
1441 advise (NULLCP,
1442 "too many parameters in message %s's %s: field (%d max)",
1443 ct -> c_file, TYPE_FIELD, NPARMS);
1444 return NOTOK;
1445 }
1446
1447 cp++;
1448 while (isspace (*cp))
1449 cp++;
1450
1451 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1452 return NOTOK;
1453
1454 if (*cp == 0) {
1455 advise (NULLCP,
1456 "extraneous trailing ';' in message %s's %s: parameter list",
1457 ct -> c_file, TYPE_FIELD);
1458 return OK;
1459 }
1460
1461 for (dp = cp; istoken (*dp); dp++)
1462 if (isalpha(*dp) && isupper (*dp))
1463 *dp = tolower (*dp);
1464 for (up = dp; isspace (*dp); )
1465 dp++;
1466 if (dp == cp || *dp != '=') {
1467 advise (NULLCP,
1468 "invalid parameter in message %s's %s: field\n%*.*sparameter %s (error detected at offset %d)",
1469 ct -> c_file, TYPE_FIELD, i, i, "", cp, dp - cp);
1470 return NOTOK;
1471 }
1472
1473 vp = (*ap = add (cp, NULLCP)) + (up - cp);
1474 *vp = '\0';
1475 for (dp++; isspace (*dp); )
1476 dp++;
1477 ci -> ci_values[ap - ci -> ci_attrs] = vp = *ap + (dp - cp);
1478 if (*dp == '"') {
1479 for (cp = ++dp, dp = vp;;) {
1480 switch (c = *cp++) {
1481 case '\0':
1482 bad_quote: ;
1483 advise (NULLCP,
1484 "invalid quoted-string in message %s's %s: field\n%*.*s(parameter %s)",
1485 ct -> c_file, TYPE_FIELD, i, i, "", *ap);
1486 return NOTOK;
1487
1488 case '\\':
1489 *dp++ = c;
1490 if ((c = *cp++) == '\0')
1491 goto bad_quote;
1492 /* else fall... */
1493
1494 default:
1495 *dp++ = c;
1496 continue;
1497
1498 case '"':
1499 *dp = '\0';
1500 break;
1501 }
1502 break;
1503 }
1504 }
1505 else {
1506 for (cp = dp, dp = vp; istoken (*cp); cp++, dp++)
1507 continue;
1508 *dp = '\0';
1509 }
1510 if (!*vp) {
1511 advise (NULLCP,
1512 "invalid parameter in message %s's %s: field\n%*.*s(parameter %s)",
1513 ct -> c_file, TYPE_FIELD, i, i, "", *ap);
1514 return NOTOK;
1515 }
1516 ap++;
1517
1518 while (isspace (*cp))
1519 cp++;
1520
1521 if (*cp == '(' && get_comment (ct, &cp, 1) == NOTOK)
1522 return NOTOK;
1523 }
1524
1525 if (magic && *cp == '<') {
1526 if (ct -> c_id)
1527 free (ct -> c_id), ct -> c_id = NULL;
1528
1529 if (!(dp = index (ct -> c_id = ++cp, '>'))) {
1530 advise (NULLCP, "invalid ID in message %s", ct -> c_file);
1531 return NOTOK;
1532 }
1533
1534 c = *dp, *dp = '\0';
1535 if (*ct -> c_id)
1536 ct -> c_id = concat ("<", ct -> c_id, ">\n", NULLCP);
1537 else
1538 ct -> c_id = NULL;
1539 *dp++ = c, cp = dp;
1540
1541 while (isspace (*cp))
1542 cp++;
1543 }
1544
1545 if (magic && *cp == '[') {
1546 ct -> c_descr = ++cp;
1547 for (dp = cp + strlen (cp) - 1; dp >= cp; dp--)
1548 if (*dp == ']')
1549 break;
1550 if (dp < cp) {
1551 advise (NULLCP, "invalid description in message %s", ct -> c_file);
1552 ct -> c_descr = NULL;
1553 return NOTOK;
1554 }
1555
1556 c = *dp, *dp = '\0';
1557 if (*ct -> c_descr)
1558 ct -> c_descr = concat (ct -> c_descr, "\n", NULLCP);
1559 else
1560 ct -> c_descr = NULL;
1561 *dp++ = c, cp = dp;
1562
1563 while (isspace (*cp))
1564 cp++;
1565 }
1566
1567 if (*cp) {
1568 if (magic)
1569 ci -> ci_magic = add (cp, NULLCP);
1570 else
1571 advise (NULLCP,
1572 "extraneous information in message %s's %s: field\n%*.*s(%s)",
1573 ct -> c_file, TYPE_FIELD, i, i, "", cp);
1574 }
1575
1576 return OK;
1577 }
1578
1579 /* \f */
1580
1581 static int get_comment (ct, ap, istype)
1582 CT ct;
1583 char **ap;
1584 int istype;
1585 {
1586 register int i;
1587 register char *bp,
1588 *cp;
1589 char c,
1590 buffer[BUFSIZ],
1591 *dp;
1592 register CI ci = &ct -> c_ctinfo;
1593
1594 cp = *ap;
1595
1596 bp = buffer;
1597 cp++;
1598 for (i = 0;;) {
1599 switch (c = *cp++) {
1600 case '\0':
1601 invalid: ;
1602 advise (NULLCP, "invalid comment in message %s's %s: field",
1603 ct -> c_file, istype ? TYPE_FIELD : VRSN_FIELD);
1604 return NOTOK;
1605
1606 case '\\':
1607 *bp++ = c;
1608 if ((c = *cp++) == '\0')
1609 goto invalid;
1610 *bp++ = c;
1611 continue;
1612
1613 case '(':
1614 i++;
1615 /* and fall... */
1616 default:
1617 *bp++ = c;
1618 continue;
1619
1620 case ')':
1621 if (--i < 0)
1622 break;
1623 *bp++ = c;
1624 continue;
1625 }
1626 break;
1627 }
1628 *bp = '\0';
1629
1630 if (istype) {
1631 if (dp = ci -> ci_comment) {
1632 ci -> ci_comment = concat (dp, " ", buffer, NULLCP);
1633 free (dp);
1634 }
1635 else
1636 ci -> ci_comment = add (buffer, NULLCP);
1637 }
1638
1639 while (isspace (*cp))
1640 cp++;
1641
1642 *ap = cp;
1643
1644 return OK;
1645 }
1646
1647 /* \f */
1648
1649 #define empty(s) ((s) ? (s) : "")
1650
1651
1652 static int list_content (ct, toplevel)
1653 register CT ct;
1654 int toplevel;
1655 {
1656 unsigned long size;
1657 register char **ap,
1658 **ep;
1659 char *cp,
1660 buffer[BUFSIZ];
1661 register CI ci = &ct -> c_ctinfo;
1662
1663 printf (toplevel > 0 ? LSTFMT2a : toplevel < 0 ? "part " : " ",
1664 atoi (r1bindex (empty (ct -> c_file), '/')));
1665 (void) sprintf (buffer, "%s/%s", empty (ci -> ci_type),
1666 empty (ci -> ci_subtype));
1667 printf (LSTFMT2b, empty (ct -> c_partno), buffer);
1668
1669 size = ct -> c_cesizefnx && sizesw ? (*ct -> c_cesizefnx) (ct)
1670 : ct -> c_end - ct -> c_begin;
1671
1672 for (cp = " KMGT"; size > 9999; size >>= 10)
1673 if (!*++cp)
1674 break;
1675 switch (*cp) {
1676 case ' ':
1677 if (size > 0 || ct -> c_encoding != CE_EXTERNAL)
1678 printf (LSTFMT2c1, size);
1679 else
1680 printf (LSTFMT2c4);
1681 break;
1682
1683 default:
1684 printf (LSTFMT2c2, size, *cp);
1685 break;
1686
1687 case '\0':
1688 printf (LSTFMT2c3);
1689 }
1690
1691 if (ct -> c_descr) {
1692 char *dp;
1693
1694 dp = trimcpy (cp = add (ct -> c_descr, NULLCP));
1695 free (cp);
1696 printf (LSTFMT2d1, dp);
1697 free (dp);
1698 }
1699
1700 printf ("\n");
1701
1702 if (verbosw && ci -> ci_comment) {
1703 char *dp;
1704
1705 dp = trimcpy (cp = add (ci -> ci_comment, NULLCP));
1706 free (cp);
1707 (void) sprintf (buffer, "(%s)", dp);
1708 free (dp);
1709 printf (LSTFMT2d2, buffer);
1710 }
1711
1712 if (!debugsw)
1713 return OK;
1714
1715 (void) fflush (stdout);
1716
1717 fprintf (stderr, " partno \"%s\"\n", empty (ct -> c_partno));
1718
1719 if (ct -> c_vrsn)
1720 fprintf (stderr, " %s:%s\n", VRSN_FIELD, ct -> c_vrsn);
1721
1722 if (ct -> c_ctline)
1723 fprintf (stderr, " %s:%s", TYPE_FIELD, ct -> c_ctline);
1724 fprintf (stderr,
1725 " type \"%s\" subtype \"%s\" comment \"%s\" magic \"%s\"\n",
1726 empty (ci -> ci_type), empty (ci -> ci_subtype),
1727 empty (ci -> ci_comment), empty (ci -> ci_magic));
1728 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
1729 fprintf (stderr, " parameter %s=\"%s\"\n", *ap, *ep);
1730 fprintf (stderr,
1731 " type 0x%x subtype 0x%x params 0x%x\n",
1732 ct -> c_type, ct -> c_subtype, ct -> c_ctparams);
1733
1734 fprintf (stderr, " showproc \"%s\"\n", empty (ct -> c_showproc));
1735 fprintf (stderr, " termproc \"%s\"\n", empty (ct -> c_termproc));
1736 fprintf (stderr, " storeproc \"%s\"\n", empty (ct -> c_storeproc));
1737
1738 if (ct -> c_celine)
1739 fprintf (stderr, " %s:%s", ENCODING_FIELD, ct -> c_celine);
1740 fprintf (stderr, " encoding 0x%x params 0x%x\n",
1741 ct -> c_encoding, ct -> c_ceparams);
1742
1743 if (ct -> c_id)
1744 fprintf (stderr, " %s:%s", ID_FIELD, ct -> c_id);
1745 if (ct -> c_descr)
1746 fprintf (stderr, " %s:%s", DESCR_FIELD, ct -> c_descr);
1747
1748 fprintf (stderr, " fp 0x%x file \"%s\" begin %d end %d\n",
1749 ct -> c_fp, empty (ct -> c_file), ct -> c_begin, ct -> c_end);
1750
1751 if (ct -> c_celistfnx)
1752 (void) (*ct -> c_celistfnx) (ct);
1753
1754 return OK;
1755 }
1756 #undef empty
1757
1758 /* \f */
1759
1760 #ifdef VSPRINTF
1761 #ifdef __STDC__
1762 #include <stdarg.h>
1763 #else
1764 #include <varargs.h>
1765 #endif
1766 #endif
1767
1768 #ifdef VSPRINTF
1769 #ifdef __STDC__
1770 static void content_error (char *what, register CT ct, char *fmt, ...)
1771 #else
1772 static void content_error (va_alist)
1773 va_dcl
1774 #endif
1775 #else /* !VSPRINTF */
1776 /* VARARGS3 */
1777 static void content_error (what, ct, fmt, a, b, c, d, e, f)
1778 char *what,
1779 *fmt,
1780 *a,
1781 *b,
1782 *c,
1783 *d,
1784 *e,
1785 *f;
1786 register CT ct;
1787 #endif
1788 {
1789 #ifdef VSPRINTF
1790 va_list arglist;
1791 #endif
1792 #if defined(VSPRINTF) && !defined(__STDC__)
1793 char *what, *fmt;
1794 register CT ct;
1795 #endif
1796 int i;
1797 register char *bp;
1798 char buffer[BUFSIZ];
1799 register CI ci;
1800
1801 bp = buffer;
1802
1803 if (userrs && invo_name && *invo_name) {
1804 (void) sprintf (bp, "%s: ", invo_name);
1805 bp += strlen (bp);
1806 }
1807
1808 #ifdef VSPRINTF
1809 #ifdef __STDC__
1810 va_start (arglist, fmt);
1811 #else
1812 va_start (arglist);
1813 what = va_arg(arglist, char *);
1814 ct = va_arg(arglist, CT);
1815 fmt = va_arg(arglist, char *);
1816 #endif
1817 (void) vsprintf (bp, fmt, arglist);
1818 bp += strlen (bp);
1819 #else
1820 (void) sprintf (bp, fmt, a, b, c, d, e, f);
1821 bp += strlen (bp);
1822 #endif
1823 ci = &ct -> c_ctinfo;
1824
1825 if (what) {
1826 if (*what) {
1827 (void) sprintf (bp, " %s: ", what);
1828 bp += strlen (bp);
1829 }
1830
1831 if (errno > 0 && errno < sys_nerr)
1832 (void) sprintf (bp, "%s", sys_errlist[errno]);
1833 else
1834 (void) sprintf (bp, "Error %d", errno);
1835 bp += strlen (bp);
1836 }
1837
1838 i = strlen (invo_name) + 2;
1839 (void) sprintf (bp, "\n%*.*s(content %s/%s", i, i, "", ci -> ci_type,
1840 ci -> ci_subtype);
1841 bp += strlen (bp);
1842 if (ct -> c_file) {
1843 (void) sprintf (bp, " in message %s", ct -> c_file);
1844 bp += strlen (bp);
1845 if (ct -> c_partno) {
1846 (void) sprintf (bp, ", part %s", ct -> c_partno);
1847 bp += strlen (bp);
1848 }
1849 }
1850 (void) sprintf (bp, ")");
1851 bp += strlen (bp);
1852
1853 if (userrs) {
1854 *bp++ = '\n';
1855 *bp = '\0';
1856
1857 errs = add (buffer, errs);
1858 }
1859 else
1860 advise (NULLCP, "%s", buffer);
1861 }
1862
1863
1864 static void flush_errors ()
1865 {
1866 if (errs) {
1867 (void) fflush (stdout);
1868 fprintf (stderr, "%s", errs);
1869 free (errs);
1870 errs = NULL;
1871 }
1872 }
1873
1874 /* \f */
1875
1876 static jmp_buf intrenv;
1877
1878
1879 /* ARGSUSED */
1880
1881 static TYPESIG intrser (i)
1882 int i;
1883 {
1884 #ifdef BSD42
1885 (void) signal (SIGINT, intrser);
1886 #endif
1887
1888 (void) putchar ('\n');
1889
1890 longjmp (intrenv, DONE);
1891 }
1892
1893 /* \f */
1894
1895 static int show_content_aux (), show_content_aux2 ();
1896
1897
1898 static int show_content (ct, serial, alternate)
1899 register CT ct;
1900 int serial,
1901 alternate;
1902 {
1903 register char *cp;
1904 char buffer[BUFSIZ];
1905 register CI ci = &ct -> c_ctinfo;
1906
1907 (void) sprintf (buffer, "%s-show-%s/%s", invo_name, ci -> ci_type,
1908 ci -> ci_subtype);
1909 if ((cp = m_find (buffer)) == NULL || *cp == 0) {
1910 (void) sprintf (buffer, "%s-show-%s", invo_name, ci -> ci_type);
1911 if (((cp = m_find (buffer)) == NULL || *cp == 0)
1912 && (cp = ct -> c_showproc) == NULL) {
1913 if (!alternate)
1914 content_error (NULLCP, ct,
1915 "don't know how to display content");
1916
1917 return NOTOK;
1918 }
1919 }
1920
1921 return show_content_aux (ct, serial, alternate, cp, NULLCP);
1922 }
1923
1924
1925 static int show_content_aux (ct, serial, alternate, cp, cracked)
1926 register CT ct;
1927 int serial,
1928 alternate;
1929 register char *cp;
1930 char *cracked;
1931 {
1932 int fd,
1933 xlist,
1934 xpause,
1935 xstdin,
1936 xtty;
1937 register char *bp;
1938 char *file,
1939 buffer[BUFSIZ];
1940 register CI ci = &ct -> c_ctinfo;
1941
1942 if (!ct -> c_ceopenfnx) {
1943 if (!alternate)
1944 content_error (NULLCP, ct, "don't know how to decode content");
1945
1946 return NOTOK;
1947 }
1948
1949 file = NULL;
1950 if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK)
1951 return NOTOK;
1952 if (ct -> c_showproc && strcmp (ct -> c_showproc, "true") == 0)
1953 return (alternate ? DONE : OK);
1954
1955 xlist = xpause = xstdin = xtty = 0;
1956 if (cracked) {
1957 (void) strcpy (buffer, cp);
1958 goto got_command;
1959 }
1960 buffer[0] = '\0';
1961 for (bp = buffer; *cp; cp++)
1962 if (*cp == '%') {
1963 switch (*++cp) {
1964 case 'a': /* additional arguments */
1965 {
1966 register char **ap,
1967 **ep;
1968 char *s = "";
1969
1970 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
1971 *ap;
1972 ap++, ep++) {
1973 (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep);
1974 bp += strlen (bp);
1975 s = " ";
1976 }
1977 }
1978 break;
1979
1980 case 'd': /* content description */
1981 if (ct -> c_descr) {
1982 char *s;
1983
1984 (void) strcpy (bp, s = trimcpy (ct -> c_descr));
1985 free (s);
1986 }
1987 break;
1988
1989 case 'e': /* exclusive execution */
1990 xtty = 1;
1991 break;
1992
1993 case 'F': /* %e, %f, and stdin is terminal not content */
1994 xstdin = xtty = 1;
1995 /* and fall... */
1996 case 'f': /* filename */
1997 (void) sprintf (bp, "%s", file);
1998 break;
1999
2000 case 'p': /* pause prior to displaying content */
2001 xpause = pausesw;
2002 /* and fall... */
2003 case 'l': /* display listing prior to displaying
2004 content */
2005 xlist = !nolist;
2006 break;
2007
2008 case 's': /* subtype */
2009 (void) strcpy (bp, ci -> ci_subtype);
2010 break;
2011
2012 case '%':
2013 goto raw;
2014
2015 default:
2016 *bp++ = *--cp;
2017 *bp = '\0';
2018 continue;
2019 }
2020 bp += strlen (bp);
2021 }
2022 else {
2023 raw: ;
2024 *bp++ = *cp;
2025 *bp = '\0';
2026 }
2027 if (ct -> c_termproc) {
2028 char term[BUFSIZ];
2029
2030 (void) strcpy (term, buffer);
2031 (void) sprintf (buffer, ct -> c_termproc, term);
2032 }
2033
2034 got_command: ;
2035 return show_content_aux2 (ct, serial, alternate, cracked, buffer,
2036 fd, xlist, xpause, xstdin, xtty);
2037 }
2038
2039
2040 static int show_content_aux2 (ct, serial, alternate, cracked, buffer,
2041 fd, xlist, xpause, xstdin, xtty)
2042 register CT ct;
2043 int serial,
2044 alternate;
2045 char *cracked,
2046 *buffer;
2047 int fd,
2048 xlist,
2049 xpause,
2050 xstdin,
2051 xtty;
2052 {
2053 int child_id,
2054 i;
2055 char *vec[4],
2056 exec[BUFSIZ + sizeof "exec "];
2057 register CI ci = &ct -> c_ctinfo;
2058
2059 if (debugsw || cracked) {
2060 (void) fflush (stdout);
2061
2062 fprintf (stderr, "%s msg %s", cracked ? "storing" : "show",
2063 ct -> c_file);
2064 if (ct -> c_partno)
2065 fprintf (stderr, " part %s", ct -> c_partno);
2066 if (cracked)
2067 fprintf (stderr, " using command (cd %s; %s)\n", cracked, buffer);
2068 else
2069 fprintf (stderr, " using command %s\n", buffer);
2070 }
2071
2072 if (xpid < 0 || (xtty && xpid)) {
2073 if (xpid < 0)
2074 xpid = -xpid;
2075 (void) pidcheck (pidwait (xpid, NOTOK));
2076 xpid = 0;
2077 }
2078
2079 if (xlist) {
2080 char prompt[BUFSIZ];
2081
2082 if (ct -> c_ctlistfnx) {
2083 if (ct -> c_type == CT_MULTIPART)
2084 (void) list_content (ct, -1);
2085 else
2086 (*ct -> c_ctlistfnx) (ct, -1);
2087
2088 if (xpause && SOprintf ("Press <return> to show content..."))
2089 printf ("Press <return> to show content...");
2090 }
2091 else {
2092 register char *pp;
2093
2094 pp = prompt;
2095 if (ct -> c_descr) {
2096 (void) sprintf (pp, "%s (", ct -> c_descr);
2097 pp += strlen (pp);
2098 }
2099
2100 (void) sprintf (pp, "content %s/%s", ci -> ci_type,
2101 ci -> ci_subtype);
2102 pp += strlen (pp);
2103 if (ct -> c_file) {
2104 (void) sprintf (pp, " in message %s", ct -> c_file);
2105 pp += strlen (pp);
2106 if (ct -> c_partno) {
2107 (void) sprintf (pp, ", part %s", ct -> c_partno);
2108 pp += strlen (pp);
2109 }
2110 }
2111
2112 if (ct -> c_descr) {
2113 (void) sprintf (pp, ")");
2114 pp += strlen (pp);
2115 }
2116
2117 if (!xpause)
2118 printf ("%s\n", prompt);
2119 else
2120 if (SOprintf ("Press <return> to show %s...", prompt))
2121 printf ("Press <return> to show %s...", prompt);
2122 }
2123
2124 if (xpause) {
2125 int intr;
2126 TYPESIG (*istat) ();
2127
2128 istat = signal (SIGINT, intrser);
2129 if ((intr = setjmp (intrenv)) == OK) {
2130 (void) fflush (stdout);
2131 prompt[0] = 0;
2132 (void) read (fileno (stdout), prompt, sizeof prompt);
2133 }
2134 (void) signal (SIGINT, istat);
2135 if (intr != OK || prompt[0] == 'q') {
2136 (void) (*ct -> c_ceclosefnx) (ct);
2137 return (alternate ? DONE : NOTOK);
2138 }
2139 }
2140 }
2141
2142 (void) sprintf (exec, "exec %s", buffer);
2143
2144 vec[0] = "/bin/sh";
2145 vec[1] = "-c";
2146 vec[2] = exec;
2147 vec[3] = NULL;
2148
2149 (void) fflush (stdout);
2150
2151 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
2152 sleep (5);
2153 switch (child_id) {
2154 case NOTOK:
2155 advise ("fork", "unable to");
2156 (void) (*ct -> c_ceclosefnx) (ct);
2157 return NOTOK;
2158
2159 case OK:
2160 if (cracked)
2161 (void) chdir (cracked);
2162 if (!xstdin)
2163 (void) dup2 (fd, 0);
2164 (void) close (fd);
2165 (void) execvp ("/bin/sh", vec);
2166 fprintf (stderr, "unable to exec ");
2167 perror ("/bin/sh");
2168 _exit (-1);
2169 /* NOTREACHED */
2170
2171 default:
2172 if (!serial) {
2173 ct -> c_pid = child_id;
2174 if (xtty)
2175 xpid = child_id;
2176 }
2177 else
2178 (void) pidcheck (pidXwait (child_id, NULLCP));
2179
2180 if (fd != NOTOK)
2181 (void) (*ct -> c_ceclosefnx) (ct);
2182 return (alternate ? DONE : OK);
2183 }
2184 }
2185
2186 /* \f */
2187
2188 static int store_content (ct, append)
2189 register CT ct;
2190 char *append;
2191 {
2192 int appending = append && *append;
2193 long last,
2194 pos;
2195 register char *bp,
2196 *cp;
2197 char *file,
2198 buffer[BUFSIZ];
2199 register CI ci = &ct -> c_ctinfo;
2200 FILE *fp;
2201
2202 if (appending) {
2203 (void) strcpy (buffer, append);
2204 goto got_filename;
2205 }
2206
2207 if ((cp = ct -> c_storeproc) == NULL || *cp == 0) {
2208 (void) sprintf (buffer, "%s-store-%s/%s", invo_name, ci -> ci_type,
2209 ci -> ci_subtype);
2210 if ((cp = m_find (buffer)) == NULL || *cp == 0) {
2211 (void) sprintf (buffer, "%s-store-%s", invo_name, ci -> ci_type);
2212 if ((cp = m_find (buffer)) == NULL || *cp == 0)
2213 cp = ct -> c_type == CT_MESSAGE ? "+" : "%m%P.%s";
2214 }
2215 }
2216
2217 switch (*cp) {
2218 case '+':
2219 case '@':
2220 {
2221 char *folder = cp[1] ? path (cp + 1, *cp == '+' ? TFOLDER
2222 : TSUBCWF)
2223 : m_getfolder ();
2224 struct msgs *mp = NULL;
2225 struct stat st;
2226
2227 if (stat (bp = m_mailpath (folder), &st) == NOTOK) {
2228 int answer;
2229 char *ep;
2230
2231 if (errno != ENOENT) {
2232 advise (bp, "error on folder");
2233 goto losing_folder;
2234 }
2235
2236 ep = concat ("Create folder \"", bp, "\"? ", NULLCP);
2237 answer = getanswer (ep);
2238 free (ep);
2239
2240 if (!answer)
2241 goto losing_folder;
2242 if (!makedir (bp)) {
2243 advise (NULLCP, "unable to create folder %s", bp);
2244 goto losing_folder;
2245 }
2246 }
2247
2248 if (mp = m_gmsg (folder))
2249 (void) sprintf (buffer, "%s/%d", mp -> foldpath,
2250 mp -> hghmsg + 1);
2251 else
2252 advise (NULLCP, "unable to read folder %s", folder);
2253 losing_folder: ;
2254 if (cp[1])
2255 free (folder);
2256 if (mp)
2257 m_fmsg (mp);
2258 else
2259 return NOTOK;
2260 }
2261 goto got_filename;
2262
2263 case '/':
2264 case '|':
2265 case '!':
2266 bp = buffer;
2267 buffer[0] = '\0';
2268 break;
2269
2270 default:
2271 bp = autosw ? cwd : dir;
2272 (void) sprintf (buffer, "%s/", bp[1] ? bp : "");
2273 bp = buffer + strlen (buffer);
2274 break;
2275 }
2276 for (; *cp; cp++)
2277 if (*cp == '%') {
2278 switch (*++cp) {
2279 case 'a': /* additional arguments */
2280 if (buffer[0] != '|' && buffer[0] != '!') {
2281 *bp++ = *--cp;
2282 *bp = '\0';
2283 continue;
2284 }
2285 else {
2286 register char **ap,
2287 **ep;
2288 char *s = "";
2289
2290 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
2291 *ap;
2292 ap++, ep++) {
2293 (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep);
2294 bp += strlen (bp);
2295 s = " ";
2296 }
2297 }
2298 break;
2299
2300 case 'm': /* message */
2301 (void) sprintf (bp, "%s", r1bindex (ct -> c_file, '/'));
2302 break;
2303
2304 case 'P': /* .part */
2305 if (ct -> c_partno)
2306 (void) sprintf (bp, ".%s", ct -> c_partno);
2307 break;
2308
2309 case 'p': /* part */
2310 if (ct -> c_partno)
2311 (void) strcpy (bp, ct -> c_partno);
2312 break;
2313
2314 case 't': /* type */
2315 (void) strcpy (bp, ci -> ci_type);
2316 break;
2317
2318 case 's': /* subtype */
2319 (void) strcpy (bp, ci -> ci_subtype);
2320 break;
2321
2322 case '%':
2323 goto raw;
2324
2325 default:
2326 *bp++ = *--cp;
2327 *bp = '\0';
2328 continue;
2329 }
2330 bp += strlen (bp);
2331 }
2332 else {
2333 raw: ;
2334 *bp++ = *cp;
2335 *bp = '\0';
2336 }
2337 if (buffer[0] == '|' || buffer[0] == '!')
2338 return show_content_aux (ct, 1, 0, buffer + 1, autosw ? cwd : dir);
2339 got_filename: ;
2340
2341 ct -> c_storage = add (buffer, NULLCP);
2342 (void) fflush (stdout);
2343 fprintf (stderr, "storing message %s", ct -> c_file);
2344 if (ct -> c_partno)
2345 fprintf (stderr, " part %s", ct -> c_partno);
2346 fprintf (stderr, " as file %s\n",
2347 strncmp (ct -> c_storage, cwd, cwdlen)
2348 || ct -> c_storage[cwdlen] != '/'
2349 ? ct -> c_storage : ct -> c_storage + cwdlen + 1);
2350 if (index (ct -> c_storage, '/')
2351 && make_intermediates (ct -> c_storage) == NOTOK)
2352 return NOTOK;
2353
2354 if (ct -> c_encoding != CE_7BIT) {
2355 int cc,
2356 fd;
2357
2358 if (!ct -> c_ceopenfnx) {
2359 advise (NULLCP, "don't know how to decode part %s of message %s",
2360 ct -> c_partno, ct -> c_file);
2361 return NOTOK;
2362 }
2363
2364 file = appending || !strcmp (ct -> c_storage, "-") ? NULLCP
2365 : ct -> c_storage;
2366 if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK)
2367 return NOTOK;
2368 if (strcmp (file, ct -> c_storage) == 0) {
2369 (void) (*ct -> c_ceclosefnx) (ct);
2370 return OK;
2371 }
2372
2373 if (!strcmp (ct -> c_storage, "-")) {
2374 int gd;
2375
2376 if ((gd = dup (fileno (stdout))) == NOTOK) {
2377 advise ("stdout", "unable to dup");
2378 losing: ;
2379 (void) (*ct -> c_ceclosefnx) (ct);
2380 return NOTOK;
2381 }
2382 if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
2383 advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
2384 appending ? "a" : "w");
2385 (void) close (gd);
2386 goto losing;
2387 }
2388 }
2389 else
2390 if ((fp = fopen (ct -> c_storage, appending ? "a" : "w"))
2391 == NULL) {
2392 advise (ct -> c_storage, "unable to fopen for %s",
2393 appending ? "appending" : "writing");
2394 goto losing;
2395 }
2396
2397 if (append && !*append)
2398 (void) copy_some_headers (fp, ct);
2399
2400 for (;;) {
2401 switch (cc = read (fd, buffer, sizeof buffer)) {
2402 case NOTOK:
2403 advise (file, "error reading content from");
2404 break;
2405
2406 case OK:
2407 break;
2408
2409 default:
2410 (void) fwrite (buffer, sizeof *buffer, cc, fp);
2411 continue;
2412 }
2413 break;
2414 }
2415
2416 (void) (*ct -> c_ceclosefnx) (ct);
2417
2418 if (cc != NOTOK && fflush (fp))
2419 advise (ct -> c_storage, "error writing to");
2420
2421 (void) fclose (fp);
2422
2423 return (cc != NOTOK ? OK : NOTOK);
2424 }
2425
2426 if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
2427 advise (ct -> c_file, "unable to open for reading");
2428 return NOTOK;
2429 }
2430
2431 (void) fseek (ct -> c_fp, pos = ct -> c_begin, 0);
2432 last = ct -> c_end;
2433
2434 if (!strcmp (ct -> c_storage, "-")) {
2435 int gd;
2436
2437 if ((gd = dup (fileno (stdout))) == NOTOK) {
2438 advise ("stdout", "unable to dup");
2439 return NOTOK;
2440 }
2441 if ((fp = fdopen (gd, appending ? "a" : "w")) == NULL) {
2442 advise ("stdout", "unable to fdopen (%d, \"%s\") from", gd,
2443 appending ? "a" : "w");
2444 (void) close (gd);
2445 return NOTOK;
2446 }
2447 }
2448 else
2449 if ((fp = fopen (ct -> c_storage, appending ? "a" : "w")) == NULL) {
2450 advise (ct -> c_storage, "unable to fopen for %s",
2451 appending ? "appending" : "writing");
2452 return NOTOK;
2453 }
2454
2455 if (append && !*append) {
2456 (void) copy_some_headers (fp, ct);
2457 appending = 1;
2458 }
2459 else
2460 appending = 0;
2461
2462 while (fgets (buffer, sizeof buffer - 1, ct -> c_fp)) {
2463 if ((pos += strlen (buffer)) > last) {
2464 int diff = strlen (buffer) - (pos - last);
2465
2466 if (diff >= 0)
2467 buffer[diff] = '\0';
2468 }
2469
2470 if (appending)
2471 switch (buffer[0]) {
2472 case ' ':
2473 case '\t':
2474 if (appending < 0)
2475 buffer[0] = 0;
2476 break;
2477
2478 case '\n':
2479 appending = 0;
2480 break;
2481
2482 default:
2483 if (!uprf (buffer, XXX_FIELD_PRF)
2484 && !uprf (buffer, "Encrypted:")
2485 && !uprf (buffer, "Message-ID:")) {
2486 appending = -1;
2487 buffer[0] = 0;
2488 break;
2489 }
2490 appending = 1;
2491 break;
2492 }
2493
2494 (void) fputs (buffer, fp);
2495 if (pos >= last)
2496 break;
2497 }
2498
2499 if (fflush (fp))
2500 advise (ct -> c_storage, "error writing to");
2501
2502 (void) fclose (fp);
2503
2504 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
2505
2506 return OK;
2507 }
2508
2509
2510 static int copy_some_headers (out, ct)
2511 FILE *out;
2512 register CT ct;
2513 {
2514 int state;
2515 char buf[BUFSIZ],
2516 name[NAMESZ];
2517 FILE *in;
2518
2519 if ((in = fopen (ct -> c_file, "r")) == NULL) {
2520 advise (ct -> c_file, "unable to open for reading");
2521 return NOTOK;
2522 }
2523
2524 for (state = FLD;;) {
2525 switch (state = m_getfld (state, name, buf, sizeof buf, in)) {
2526 case FLD:
2527 case FLDPLUS:
2528 case FLDEOF:
2529 if (uprf (name, XXX_FIELD_PRF)
2530 || uleq (name, "Encrypted")
2531 || uleq (name, "Message-ID")) {
2532 while (state == FLDPLUS)
2533 state = m_getfld (state, name, buf, sizeof buf, in);
2534 continue;
2535 }
2536
2537 fprintf (out, "%s:%s", name, buf);
2538 while (state == FLDPLUS) {
2539 state = m_getfld (state, name, buf, sizeof buf, in);
2540 (void) fputs (buf, out);
2541 }
2542 if (state != FLDEOF)
2543 continue;
2544 /* else fall... */
2545 case BODY:
2546 case BODYEOF:
2547 case FILEEOF:
2548 break;
2549
2550 case LENERR:
2551 case FMTERR:
2552 default:
2553 break;
2554 }
2555
2556 break;
2557 }
2558
2559 (void) fclose (in);
2560
2561 return OK;
2562 }
2563
2564
2565 static int make_intermediates (file)
2566 char *file;
2567 {
2568 register char *cp;
2569
2570 for (cp = file + 1; cp = index (cp, '/'); cp++) {
2571 struct stat st;
2572
2573 *cp = NULL;
2574
2575 if (stat (file, &st) == NOTOK) {
2576 int answer;
2577 char *ep;
2578
2579 if (errno != ENOENT) {
2580 advise (file, "error on directory");
2581 losing_directory: ;
2582 *cp = '/';
2583 return NOTOK;
2584 }
2585
2586 ep = concat ("Create directory \"", file, "\"? ", NULLCP);
2587 answer = getanswer (ep);
2588 free (ep);
2589
2590 if (!answer)
2591 goto losing_directory;
2592 if (!makedir (file)) {
2593 advise (NULLCP, "unable to create directory %s", file);
2594 goto losing_directory;
2595 }
2596 }
2597
2598 *cp = '/';
2599 }
2600
2601 return OK;
2602 }
2603
2604 /* \f */
2605
2606 static void free_ctinfo (ct)
2607 register CT ct;
2608 {
2609 register char **ap;
2610 register CI ci = &ct -> c_ctinfo;
2611
2612 if (ci -> ci_type)
2613 free (ci -> ci_type);
2614 ci -> ci_type = NULL;
2615 if (ci -> ci_subtype)
2616 free (ci -> ci_subtype);
2617 ci -> ci_subtype = NULL;
2618 for (ap = ci -> ci_attrs; *ap; ap++)
2619 free (*ap), *ap = NULL;
2620 if (ci -> ci_comment)
2621 free (ci -> ci_comment);
2622 ci -> ci_comment = NULL;
2623 if (ci -> ci_magic)
2624 free (ci -> ci_magic);
2625 ci -> ci_magic = NULL;
2626 }
2627
2628
2629 static void free_content (ct)
2630 register CT ct;
2631 {
2632 if (!ct)
2633 return;
2634
2635 if (ct -> c_partno)
2636 free (ct -> c_partno);
2637 ct -> c_partno = NULL;
2638
2639 if (ct -> c_vrsn)
2640 free (ct -> c_vrsn);
2641 ct -> c_vrsn = NULL;
2642
2643 if (ct -> c_ctline)
2644 free (ct -> c_ctline);
2645 ct -> c_ctline = NULL;
2646
2647 free_ctinfo (ct);
2648
2649 if (ct -> c_ctfreefnx)
2650 (void) (*ct -> c_ctfreefnx) (ct);
2651 ct -> c_ctfreefnx = NULL;
2652
2653 if (ct -> c_showproc)
2654 free (ct -> c_showproc);
2655 ct -> c_showproc = NULL;
2656 if (ct -> c_termproc)
2657 free (ct -> c_termproc);
2658 ct -> c_termproc = NULL;
2659 if (ct -> c_storeproc)
2660 free (ct -> c_storeproc);
2661 ct -> c_storeproc = NULL;
2662
2663 if (ct -> c_celine)
2664 free (ct -> c_celine);
2665 ct -> c_celine = NULL;
2666 if (ct -> c_cefreefnx)
2667 (void) (*ct -> c_cefreefnx) (ct, 1);
2668 ct -> c_cefreefnx = NULL;
2669
2670 if (ct -> c_id)
2671 free (ct -> c_id);
2672 ct -> c_id = NULL;
2673 if (ct -> c_descr)
2674 free (ct -> c_descr);
2675 ct -> c_descr = NULL;
2676
2677 if (ct -> c_file) {
2678 if (ct -> c_unlink)
2679 (void) unlink (ct -> c_file);
2680 free (ct -> c_file);
2681 ct -> c_file = NULL;
2682 }
2683 if (ct -> c_fp)
2684 (void) fclose (ct -> c_fp);
2685 ct -> c_fp = NULL;
2686
2687 if (ct -> c_storage)
2688 (void) free (ct -> c_storage);
2689 ct -> c_storage = NULL;
2690
2691 free ((char *) ct);
2692 }
2693
2694 /* \f */
2695
2696 static int part_ok (ct, sP)
2697 register CT ct;
2698 int sP;
2699 {
2700 register char **ap;
2701
2702 if ((ct -> c_type == CT_MULTIPART && (sP || ct -> c_subtype))
2703 || npart == 0)
2704 return 1;
2705
2706 for (ap = parts; *ap; ap++)
2707 if (strcmp (*ap, ct -> c_partno) == 0)
2708 return 1;
2709
2710 return 0;
2711 }
2712
2713
2714
2715 static int type_ok (ct, sP)
2716 register CT ct;
2717 int sP;
2718 {
2719 register char **ap;
2720 char buffer[BUFSIZ];
2721 register CI ci = &ct -> c_ctinfo;
2722
2723 if ((ct -> c_type == CT_MULTIPART && (sP || ct -> c_subtype))
2724 || ntype == 0)
2725 return 1;
2726
2727 (void) sprintf (buffer, "%s/%s", ci -> ci_type, ci -> ci_subtype);
2728 for (ap = types; *ap; ap++)
2729 if (uleq (*ap, ci -> ci_type) || uleq (*ap, buffer))
2730 return 1;
2731
2732 return 0;
2733 }
2734
2735 /* \f CONTENTS */
2736
2737 struct k2v {
2738 char *kv_key;
2739 int kv_value;
2740 };
2741
2742
2743 static int InitGeneric (ct)
2744 register CT ct;
2745 {
2746 register char **ap,
2747 **ep;
2748 register CI ci = &ct -> c_ctinfo;
2749
2750 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
2751 if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) {
2752 register char *cp;
2753
2754 if (*(cp = *ep) != '/'
2755 && *cp != '.'
2756 && *cp != '|'
2757 && *cp != '!'
2758 && !index (cp, '%'))
2759 ct -> c_storeproc = add (cp, NULLCP);
2760 }
2761
2762 return OK;
2763 }
2764
2765 /* \f TEXT */
2766
2767 #define TEXT_UNKNOWN 0x00
2768 #define TEXT_PLAIN 0x01
2769 #define TEXT_RICHTEXT 0x02
2770
2771 struct text {
2772 int tx_charset;
2773 #define CHARSET_UNKNOWN 0x00
2774 #define CHARSET_USASCII 0x01
2775 #define CHARSET_LATIN 0x02
2776 };
2777
2778 static struct k2v Charset[] = {
2779 "us-ascii", CHARSET_USASCII,
2780 "iso-8859-1", CHARSET_LATIN,
2781
2782 NULL, CHARSET_UNKNOWN /* this one must be last! */
2783 };
2784
2785 static int free_text (ct)
2786 register CT ct;
2787 {
2788 register struct text *t = (struct text *) ct -> c_ctparams;
2789
2790 if (!t)
2791 return;
2792
2793 free ((char *) t);
2794 ct -> c_ctparams = NULL;
2795 }
2796
2797
2798 static struct k2v SubText[] = {
2799 "plain", TEXT_PLAIN,
2800 "richtext", TEXT_RICHTEXT,
2801
2802 NULL, TEXT_UNKNOWN /* this one must be last! */
2803 };
2804
2805 static int InitText (ct)
2806 register CT ct;
2807 {
2808 char buffer[BUFSIZ];
2809 register char **ap,
2810 **ep;
2811 register struct k2v *kv;
2812 register CI ci = &ct -> c_ctinfo;
2813
2814 if (!*ci -> ci_subtype) /* XXX: attmail bogosity! */
2815 ci -> ci_subtype = add ("plain", ci -> ci_subtype);
2816 for (kv = SubText; kv -> kv_key; kv++)
2817 if (uleq (ci -> ci_subtype, kv -> kv_key))
2818 break;
2819 if ((ct -> c_subtype = kv -> kv_value) == TEXT_PLAIN) {
2820 (void) sprintf (buffer, "%%p%s '%%F'",
2821 progsw ? progsw
2822 : moreproc && *moreproc ? moreproc : "more");
2823 ct -> c_showproc = add (buffer, NULLCP);
2824 }
2825
2826 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
2827 if (!ct -> c_ctparams && uleq (*ap, "charset")) {
2828 char *cp;
2829 register struct text *t;
2830
2831 if ((t = (struct text *) calloc (1, sizeof *t)) == NULL)
2832 adios (NULLCP, "out of memory");
2833 ct -> c_ctparams = (caddr_t) t;
2834 ct -> c_ctfreefnx = free_text;
2835
2836 for (kv = Charset; kv -> kv_key; kv++)
2837 if (uleq (*ep, kv -> kv_key)) {
2838 (void) sprintf (buffer, "%s-charset-%s", invo_name,
2839 kv -> kv_key);
2840 break;
2841 }
2842 t -> tx_charset = kv -> kv_value;
2843 if (!kv -> kv_key)
2844 (void) sprintf (buffer, "%s-charset-%s", invo_name, *ep);
2845 if ((!mm_charset || !uleq (mm_charset, *ep))
2846 && (cp = m_find (buffer)))
2847 ct -> c_termproc = getcpy (cp);
2848 }
2849 else
2850 if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) {
2851 register char *cp;
2852
2853 if (*(cp = *ep) != '/'
2854 && *cp != '.'
2855 && *cp != '|'
2856 && *cp != '!'
2857 && !index (cp, '%'))
2858 ct -> c_storeproc = add (cp, NULLCP);
2859 }
2860
2861 return OK;
2862 }
2863
2864 /* \f MULTIPART */
2865
2866 #define MULTI_UNKNOWN 0x00
2867 #define MULTI_MIXED 0x01
2868 #define MULTI_ALTERNATE 0x02
2869 #define MULTI_DIGEST 0x03
2870 #define MULTI_PARALLEL 0x04
2871
2872
2873 struct multipart {
2874 char *mp_start;
2875 char *mp_stop;
2876
2877 struct part {
2878 CT mp_part;
2879
2880 struct part *mp_next;
2881 } *mp_parts;
2882 };
2883
2884
2885 static int list_multi (ct, toplevel)
2886 register CT ct;
2887 int toplevel;
2888 {
2889 register struct multipart *m = (struct multipart *) ct -> c_ctparams;
2890 register struct part *part;
2891
2892 (void) list_content (ct, toplevel);
2893
2894 for (part = m -> mp_parts; part; part = part -> mp_next) {
2895 register CT p = part -> mp_part;
2896
2897 if (part_ok (p, 1) && type_ok (p, 1) && p -> c_ctlistfnx)
2898 (void) (*p -> c_ctlistfnx) (p, 0);
2899 }
2900
2901 return OK;
2902 }
2903
2904
2905 static int show_multi (ct, serial, alternate)
2906 register CT ct;
2907 int serial,
2908 alternate;
2909 {
2910 int alternating,
2911 nowalternate,
2912 nowserial,
2913 result;
2914 register struct multipart *m = (struct multipart *) ct -> c_ctparams;
2915 register struct part *part;
2916 register CT p;
2917 TYPESIG (*hstat) (), (*istat) (), (*qstat) (), (*tstat) ();
2918
2919 alternating = 0;
2920 nowalternate = alternate;
2921 switch (ct -> c_subtype) {
2922 case MULTI_PARALLEL:
2923 if (!(nowserial = serialsw)) {
2924 set_signals: ;
2925 hstat = signal (SIGHUP, SIG_IGN);
2926 istat = signal (SIGINT, SIG_IGN);
2927 qstat = signal (SIGQUIT, SIG_IGN);
2928 tstat = signal (SIGTERM, SIG_IGN);
2929 }
2930 break;
2931
2932 case MULTI_ALTERNATE:
2933 nowalternate = alternating = 1;
2934 /* and fall... */
2935 default:
2936 if (!(nowserial = serial))
2937 goto set_signals;
2938 break;
2939 }
2940
2941 /* alternate -> we're inside an alternative
2942 alternating -> we are an alternative
2943 */
2944
2945 result = alternate ? NOTOK : OK;
2946 for (part = m -> mp_parts; part; part = part -> mp_next) {
2947 p = part -> mp_part;
2948
2949 if (part_ok (p, 0) && type_ok (p, 0) && p -> c_ctshowfnx) {
2950 int inneresult;
2951
2952 switch (inneresult = (*p -> c_ctshowfnx) (p, nowserial,
2953 nowalternate)) {
2954 case NOTOK:
2955 if (alternate && !alternating) {
2956 result = NOTOK;
2957 goto out;
2958 }
2959 continue;
2960
2961 case OK:
2962 case DONE:
2963 if (alternating) {
2964 result = DONE;
2965 break;
2966 }
2967 if (alternate) {
2968 alternate = nowalternate = 0;
2969 if (result == NOTOK)
2970 result = inneresult;
2971 }
2972 continue;
2973 }
2974 break;
2975 }
2976 }
2977 if (alternating && !part) {
2978 if (!alternate)
2979 content_error (NULLCP, ct,
2980 "don't know how to display any of the contents");
2981
2982 result = NOTOK;
2983 goto out;
2984 }
2985
2986 if (serial && !nowserial) {
2987 int pid,
2988 kids;
2989 #if defined(BSD42) && !defined(WAITINT)
2990 union wait status;
2991 #else
2992 int status;
2993 #endif
2994
2995 kids = 0;
2996 for (part = m -> mp_parts; part; part = part -> mp_next) {
2997 p = part -> mp_part;
2998
2999 if (p -> c_pid > OK)
3000 if (kill (p -> c_pid, 0) == NOTOK)
3001 p -> c_pid = 0;
3002 else
3003 kids++;
3004 }
3005
3006 while (kids > 0 && (pid = wait (&status)) != NOTOK) {
3007 #if defined(BSD42) && !defined(WAITINT)
3008 (void) pidcheck (status.w_status);
3009 #else
3010 (void) pidcheck (status);
3011 #endif
3012
3013 for (part = m -> mp_parts; part; part = part -> mp_next) {
3014 p = part -> mp_part;
3015
3016 if (xpid == pid)
3017 xpid = 0;
3018 if (p -> c_pid == pid) {
3019 p -> c_pid = 0;
3020 kids--;
3021 break;
3022 }
3023 }
3024 }
3025 }
3026
3027 out: ;
3028 if (!nowserial) {
3029 (void) signal (SIGHUP, hstat);
3030 (void) signal (SIGINT, istat);
3031 (void) signal (SIGQUIT, qstat);
3032 (void) signal (SIGTERM, tstat);
3033 }
3034
3035 return result;
3036 }
3037
3038
3039 static int show_unknown_multi (ct, serial, alternate)
3040 register CT ct;
3041 int serial,
3042 alternate;
3043 {
3044 int xlist,
3045 xpause,
3046 xtty;
3047 register char *bp,
3048 *cp;
3049 char buffer[BUFSIZ];
3050 register struct multipart *m = (struct multipart *) ct -> c_ctparams;
3051 register struct part *part;
3052 register CI ci = &ct -> c_ctinfo;
3053 register CT p;
3054
3055 (void) sprintf (buffer, "%s-show-%s/%s", invo_name, ci -> ci_type,
3056 ci -> ci_subtype);
3057 if ((cp = m_find (buffer)) == NULL || *cp == 0) {
3058 (void) sprintf (buffer, "%s-show-%s", invo_name, ci -> ci_type);
3059 if (((cp = m_find (buffer)) == NULL || *cp == 0)
3060 && (cp = ct -> c_showproc) == NULL) {
3061 if (!alternate)
3062 content_error (NULLCP, ct,
3063 "don't know how to display content");
3064
3065 return NOTOK;
3066 }
3067 }
3068
3069 for (part = m -> mp_parts; part; part = part -> mp_next) {
3070 p = part -> mp_part;
3071
3072 if (!p -> c_ceopenfnx) {
3073 if (!alternate)
3074 content_error (NULLCP, p, "don't know how to decode content");
3075
3076 return NOTOK;
3077 }
3078
3079 if (p -> c_storage == NULL) {
3080 if ((*p -> c_ceopenfnx) (p, &p -> c_storage) == NOTOK)
3081 return NOTOK;
3082
3083 if (p -> c_showproc && strcmp (p -> c_showproc, "true") == 0)
3084 return (alternate ? DONE : OK);
3085 (*p -> c_ceclosefnx) (p);
3086 }
3087 }
3088
3089 xlist = xpause = xtty = 0;
3090 buffer[0] = '\0';
3091 for (bp = buffer; *cp; cp++)
3092 if (*cp == '%') {
3093 switch (*++cp) {
3094 case 'a': /* additional arguments */
3095 {
3096 register char **ap,
3097 **ep;
3098 char *s = "";
3099
3100 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
3101 *ap;
3102 ap++, ep++) {
3103 (void) sprintf (bp, "%s%s=\"%s\"", s, *ap, *ep);
3104 bp += strlen (bp);
3105 s = " ";
3106 }
3107 }
3108 break;
3109
3110 case 'd': /* content description */
3111 if (ct -> c_descr) {
3112 char *s;
3113
3114 (void) strcpy (bp, s = trimcpy (ct -> c_descr));
3115 free (s);
3116 }
3117 break;
3118
3119 case 'e': /* exclusive execution */
3120 xtty = 1;
3121 break;
3122
3123 case 'F': /* %e and %f */
3124 xtty = 1;
3125 /* and fall... */
3126 case 'f': /* filename(s) */
3127 {
3128 char *s = "";
3129
3130 for (part = m -> mp_parts;
3131 part;
3132 part = part -> mp_next) {
3133 p = part -> mp_part;
3134
3135 (void) sprintf (bp, "%s'%s'", s, p -> c_storage);
3136 bp += strlen (bp);
3137 s = " ";
3138 }
3139 }
3140 break;
3141
3142 case 'p': /* pause prior to displaying content */
3143 xpause = pausesw;
3144 /* and fall... */
3145 case 'l': /* display listing prior to displaying
3146 content */
3147 xlist = !nolist;
3148 break;
3149
3150 case 's': /* subtype */
3151 (void) strcpy (bp, ci -> ci_subtype);
3152 break;
3153
3154 case '%':
3155 goto raw;
3156
3157 default:
3158 *bp++ = *--cp;
3159 *bp = '\0';
3160 continue;
3161 }
3162 bp += strlen (bp);
3163 }
3164 else {
3165 raw: ;
3166 *bp++ = *cp;
3167 *bp = '\0';
3168 }
3169 if (ct -> c_termproc) {
3170 char term[BUFSIZ];
3171
3172 (void) strcpy (term, buffer);
3173 (void) sprintf (buffer, ct -> c_termproc, term);
3174 }
3175
3176 return show_content_aux2 (ct, serial, alternate, NULLCP, buffer,
3177 NOTOK, xlist, xpause, 0, xtty);
3178 }
3179
3180
3181 /* ARGSUSED */
3182
3183 static int store_multi (ct, unused)
3184 register CT ct;
3185 char *unused;
3186 {
3187 int result;
3188 register struct multipart *m = (struct multipart *) ct -> c_ctparams;
3189 register struct part *part;
3190
3191 result = NOTOK;
3192 for (part = m -> mp_parts; part; part = part -> mp_next) {
3193 register CT p = part -> mp_part;
3194
3195 if (part_ok (p, 1)
3196 && type_ok (p, 1)
3197 && p -> c_ctstorefnx
3198 && (result = (*p -> c_ctstorefnx) (p, NULLCP)) == OK
3199 && ct -> c_subtype == MULTI_ALTERNATE)
3200 break;
3201 }
3202
3203 return result;
3204 }
3205
3206
3207 static int free_multi (ct)
3208 register CT ct;
3209 {
3210 register struct multipart *m = (struct multipart *) ct -> c_ctparams;
3211 register struct part *part,
3212 *next;
3213
3214 if (!m)
3215 return;
3216
3217 if (m -> mp_start)
3218 free (m -> mp_start);
3219 if (m -> mp_stop)
3220 free (m -> mp_stop);
3221
3222 for (part = m -> mp_parts; part; part = next) {
3223 next = part -> mp_next;
3224
3225 free_content (part -> mp_part);
3226
3227 free ((char *) part);
3228 }
3229 m -> mp_parts = NULL;
3230
3231 free ((char *) m);
3232 ct -> c_ctparams = NULL;
3233 }
3234
3235
3236 static struct k2v SubMultiPart[] = {
3237 "mixed", MULTI_MIXED,
3238 "alternative", MULTI_ALTERNATE,
3239 "digest", MULTI_DIGEST,
3240 "parallel", MULTI_PARALLEL,
3241
3242 NULL, MULTI_UNKNOWN /* this one must be last! */
3243 };
3244
3245 static int InitMultiPart (ct)
3246 register CT ct;
3247 {
3248 int inout;
3249 long last,
3250 pos;
3251 register char *cp,
3252 *dp,
3253 **ap,
3254 **ep;
3255 char *bp,
3256 buffer[BUFSIZ];
3257 register struct multipart *m;
3258 register struct k2v *kv;
3259 register struct part *part,
3260 **next;
3261 register CI ci = &ct -> c_ctinfo;
3262 register CT p;
3263 FILE *fp;
3264
3265 ct -> c_ctshowfnx = NULL;
3266 ct -> c_ctstorefnx = NULL;
3267
3268 if (ct -> c_encoding != CE_7BIT) {
3269 admonish (NULLCP,
3270 "\"%s/%s\" type in message %s should be encoded in 7bit",
3271 ci -> ci_type, ci -> ci_subtype, ct -> c_file);
3272 return NOTOK;
3273 }
3274
3275 for (kv = SubMultiPart; kv -> kv_key; kv++)
3276 if (uleq (ci -> ci_subtype, kv -> kv_key))
3277 break;
3278 ct -> c_subtype = kv -> kv_value;
3279
3280 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
3281 if (uleq (*ap, "boundary")) {
3282 bp = *ep;
3283 break;
3284 }
3285 if (!*ap) {
3286 advise (NULLCP,
3287 "a \"boundary\" parameter is mandatory for \"%s/%s\" type in message %s's %s: field",
3288 ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD);
3289 return NOTOK;
3290 }
3291
3292 if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL)
3293 adios (NULLCP, "out of memory");
3294 ct -> c_ctparams = (caddr_t) m;
3295 ct -> c_ctlistfnx = list_multi;
3296 ct -> c_ctshowfnx = ct -> c_subtype != MULTI_UNKNOWN ? show_multi
3297 : show_unknown_multi;
3298 ct -> c_ctstorefnx = store_multi;
3299 ct -> c_ctfreefnx = free_multi;
3300
3301 for (cp = bp; isspace (*cp); cp++)
3302 continue;
3303 if (!*cp) {
3304 advise (NULLCP, "invalid \"boundary\" parameter for \"%s/%s\" type in message %s's %s: field",
3305 ci -> ci_type, ci -> ci_subtype, ct -> c_file, TYPE_FIELD);
3306 return NOTOK;
3307 }
3308 for (cp = bp, dp = cp + strlen (cp) - 1; dp > cp; dp--)
3309 if (!isspace (*dp))
3310 break;
3311 *++dp = '\0';
3312 m -> mp_start = concat (bp, "\n", NULLCP);
3313 m -> mp_stop = concat (bp, "--\n", NULLCP);
3314
3315 if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
3316 advise (ct -> c_file, "unable to open for reading");
3317 return NOTOK;
3318 }
3319
3320 (void) fseek (fp = ct -> c_fp, pos = ct -> c_begin, 0);
3321 last = ct -> c_end;
3322
3323 next = &m -> mp_parts, part = NULL, inout = 1;
3324 while (fgets (buffer, sizeof buffer - 1, fp)) {
3325 if (pos > last)
3326 break;
3327
3328 pos += strlen (buffer);
3329
3330 if (buffer[0] != '-' || buffer[1] != '-')
3331 continue;
3332
3333 if (inout) {
3334 if (strcmp (buffer + 2, m -> mp_start))
3335 continue;
3336
3337 next_part: ;
3338 if ((part = (struct part *) calloc (1, sizeof *part)) == NULL)
3339 adios (NULLCP, "out of memory");
3340 *next = part, next = &part -> mp_next;
3341
3342 if ((p = get_content (fp, ct -> c_file,
3343 rfc934sw && ct -> c_subtype == MULTI_DIGEST
3344 ? -1 : 0)) == NULLCT) {
3345 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
3346 return NOTOK;
3347 }
3348 p -> c_fp = NULL;
3349
3350 part -> mp_part = p;
3351 (void) fseek (fp, pos = p -> c_begin, 0);
3352 inout = 0;
3353 }
3354 else
3355 if (strcmp (buffer + 2, m -> mp_start) == 0) {
3356 inout = 1;
3357
3358 end_part: ;
3359 p = part -> mp_part;
3360 p -> c_end = ftell (fp) - (strlen (buffer) + 1);
3361 if (p -> c_end < p -> c_begin)
3362 p -> c_begin = p -> c_end;
3363 if (inout)
3364 goto next_part;
3365 goto last_part;
3366 }
3367 else
3368 if (strcmp (buffer + 2, m -> mp_stop) == 0)
3369 goto end_part;
3370 }
3371 advise (NULLCP, "bogus multipart content in message %s", ct -> c_file);
3372 if (!inout && part) {
3373 p = part -> mp_part;
3374 p -> c_end = ct -> c_end;
3375
3376 if (p -> c_begin >= p -> c_end) {
3377 for (next = &m -> mp_parts;
3378 *next != part;
3379 next = &((*next) -> mp_next))
3380 continue;
3381 *next = NULL;
3382 free_content (p);
3383 free ((char *) part);
3384 }
3385 }
3386
3387 last_part: ;
3388 if (ct -> c_subtype == MULTI_ALTERNATE
3389 && m -> mp_parts
3390 && m -> mp_parts -> mp_next) {
3391 register int i;
3392 register struct part **base,
3393 **bmp;
3394
3395 i = 0;
3396 for (part = m -> mp_parts; part; part = part -> mp_next)
3397 i++;
3398 if ((base = (struct part **) calloc ((unsigned) (i + 1), sizeof *base))
3399 == NULL)
3400 adios (NULLCP, "out of memory");
3401 bmp = base;
3402 for (part = m -> mp_parts; part; part = part -> mp_next)
3403 *bmp++ = part;
3404 *bmp = NULL;
3405
3406 next = &m -> mp_parts;
3407 for (bmp--; bmp >= base; bmp--) {
3408 part = *bmp;
3409 *next = part, next = &part -> mp_next;
3410 }
3411 *next = NULL;
3412
3413 free ((char *) base);
3414 }
3415
3416 {
3417 int partnum;
3418 register char *pp;
3419 char partnam[BUFSIZ];
3420
3421 if (ct -> c_partno) {
3422 (void) sprintf (partnam, "%s.", ct -> c_partno);
3423 pp = partnam + strlen (partnam);
3424 }
3425 else
3426 pp = partnam;
3427
3428 for (part = m -> mp_parts, partnum = 1;
3429 part;
3430 part = part -> mp_next, partnum++) {
3431 p = part -> mp_part;
3432
3433 (void) sprintf (pp, "%d", partnum);
3434 p -> c_partno = add (partnam, NULLCP);
3435
3436 if (p -> c_ctinitfnx && (*p -> c_ctinitfnx) (p) == NOTOK) {
3437 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
3438 return NOTOK;
3439 }
3440 }
3441 }
3442
3443 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
3444
3445 return OK;
3446 }
3447
3448 /* \f MESSAGE */
3449
3450 #define MESSAGE_UNKNOWN 0x00
3451 #define MESSAGE_RFC822 0x01
3452 #define MESSAGE_PARTIAL 0x02
3453 #define MESSAGE_EXTERNAL 0x03
3454
3455
3456 struct partial {
3457 char *pm_partid;
3458
3459 int pm_partno;
3460 int pm_maxno;
3461
3462 int pm_marked;
3463 int pm_stored;
3464 };
3465
3466
3467 static int list_partial (ct, toplevel)
3468 register CT ct;
3469 int toplevel;
3470 {
3471 register struct partial *p = (struct partial *) ct -> c_ctparams;
3472
3473 (void) list_content (ct, toplevel);
3474 if (verbosw) {
3475 printf ("\t [message %s, part %d", p -> pm_partid, p -> pm_partno);
3476 if (p -> pm_maxno)
3477 printf (" of %d", p -> pm_maxno);
3478 printf ("]\n");
3479 }
3480
3481 return OK;
3482 }
3483
3484
3485 static int ct_compar (a, b)
3486 CT *a,
3487 *b;
3488 {
3489 register struct partial *am = (struct partial *) ((*a) -> c_ctparams);
3490 register struct partial *bm = (struct partial *) ((*b) -> c_ctparams);
3491
3492 return (am -> pm_marked - bm -> pm_marked);
3493 }
3494
3495
3496 /* ARGSUSED */
3497
3498 static int store_partial (ct, unused)
3499 register CT ct;
3500 char *unused;
3501 {
3502 int cur,
3503 hi,
3504 i;
3505 register CT p,
3506 *ctp,
3507 *ctq;
3508 CT *base;
3509 struct partial *qm = (struct partial *) ct -> c_ctparams;
3510
3511 if (qm -> pm_stored)
3512 return OK;
3513
3514 hi = i = 0;
3515 for (ctp = cts; p = *ctp; ctp++)
3516 if (p -> c_type == CT_MESSAGE && p -> c_subtype == ct -> c_subtype) {
3517 register struct partial *pm = (struct partial *) p -> c_ctparams;
3518
3519 if (!pm -> pm_stored
3520 && strcmp (qm -> pm_partid, pm -> pm_partid) == 0) {
3521 pm -> pm_marked = pm -> pm_partno;
3522 if (pm -> pm_maxno)
3523 hi = pm -> pm_maxno;
3524 pm -> pm_stored = 1;
3525 i++;
3526 }
3527 else
3528 pm -> pm_marked = 0;
3529 }
3530 if (hi == 0) {
3531 advise (NULLCP, "missing (at least) last part of multipart message");
3532 return NOTOK;
3533 }
3534
3535 if ((base = (CT *) calloc ((unsigned) (i + 1), sizeof *base)) == NULL)
3536 adios (NULLCP, "out of memory");
3537
3538 ctq = base;
3539 for (ctp = cts; p = *ctp; ctp++)
3540 if (p -> c_type == CT_MESSAGE && p -> c_subtype == ct -> c_subtype) {
3541 register struct partial *pm = (struct partial *) p -> c_ctparams;
3542
3543 if (pm -> pm_marked)
3544 *ctq++ = p;
3545 }
3546 *ctq = NULL;
3547
3548 if (i > 1)
3549 qsort ((char *) base, i, sizeof *base, ct_compar);
3550
3551 cur = 1;
3552 for (ctq = base; p = *ctq; ctq++) {
3553 register struct partial *pm = (struct partial *) p -> c_ctparams;
3554
3555 if (pm -> pm_marked != cur) {
3556 if (pm -> pm_marked == cur - 1) {
3557 admonish (NULLCP,
3558 "duplicate part %d of %d part multipart message",
3559 pm -> pm_marked, hi);
3560 continue;
3561 }
3562
3563 missing_part: ;
3564 advise (NULLCP,
3565 "missing %spart %d of %d part multipart message",
3566 cur != hi ? "(at least) " : "", cur, hi);
3567 goto losing;
3568 }
3569 else
3570 cur++;
3571 }
3572 if (hi != --cur) {
3573 cur = hi;
3574 goto missing_part;
3575 }
3576
3577 ctq = base;
3578 ct = *ctq++;
3579 if (store_content (ct, "") == NOTOK) {
3580 losing: ;
3581 free ((char *) base);
3582 return NOTOK;
3583 }
3584
3585 for (; p = *ctq; ctq++)
3586 if (store_content (p, ct -> c_storage) == NOTOK)
3587 goto losing;
3588
3589 free ((char *) base);
3590 return OK;
3591 }
3592
3593
3594 static int free_partial (ct)
3595 register CT ct;
3596 {
3597 register struct partial *p = (struct partial *) ct -> c_ctparams;
3598
3599 if (!p)
3600 return;
3601
3602 if (p -> pm_partid)
3603 free (p -> pm_partid);
3604
3605 free ((char *) p);
3606 ct -> c_ctparams = NULL;
3607 }
3608
3609
3610 struct exbody {
3611 CT eb_parent;
3612 CT eb_content;
3613 char *eb_partno;
3614
3615 char *eb_access;
3616 int eb_flags;
3617
3618 char *eb_name;
3619 char *eb_permission;
3620
3621 char *eb_site;
3622 char *eb_dir;
3623 char *eb_mode;
3624 unsigned long
3625 eb_size;
3626
3627 char *eb_server;
3628 char *eb_subject;
3629 char *eb_body;
3630 };
3631
3632
3633 static int openFile ();
3634 static int openFTP ();
3635 static int openMail ();
3636
3637 /* NOTE WELL: si_key MUST NOT have value of NOTOK */
3638
3639 static struct str2init str2methods[] = {
3640 "afs", 1, openFile,
3641 "anon-ftp", 1, openFTP,
3642 "ftp", 0, openFTP,
3643 "local-file", 0, openFile,
3644 "mail-server", 0, openMail,
3645
3646 NULL
3647 };
3648
3649
3650 static int params_external (ct, composing)
3651 register CT ct;
3652 int composing;
3653 {
3654 register char **ap,
3655 **ep;
3656 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
3657 register CI ci = &ct -> c_ctinfo;
3658
3659 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) {
3660 if (uleq (*ap, "access-type")) {
3661 register struct str2init *s2i;
3662 register CT p = e -> eb_content;
3663
3664 for (s2i = str2methods; s2i -> si_key; s2i++)
3665 if (uleq (*ep, s2i -> si_key))
3666 break;
3667 if (!s2i -> si_key) {
3668 e -> eb_access = *ep;
3669 e -> eb_flags = NOTOK;
3670 p -> c_encoding = CE_EXTERNAL;
3671 continue;
3672 }
3673 e -> eb_access = s2i -> si_key;
3674 e -> eb_flags = s2i -> si_val;
3675 p -> c_encoding = CE_EXTERNAL;
3676 if (init_encoding (p, s2i -> si_init) == NOTOK)
3677 return NOTOK;
3678 continue;
3679 }
3680 if (uleq (*ap, "name")) {
3681 e -> eb_name = *ep;
3682 continue;
3683 }
3684 if (uleq (*ap, "permission")) {
3685 e -> eb_permission = *ep;
3686 continue;
3687 }
3688 if (uleq (*ap, "site")) {
3689 e -> eb_site = *ep;
3690 continue;
3691 }
3692 if (uleq (*ap, "directory")) {
3693 e -> eb_dir = *ep;
3694 continue;
3695 }
3696 if (uleq (*ap, "mode")) {
3697 e -> eb_mode = *ep;
3698 continue;
3699 }
3700 if (uleq (*ap, "size")) {
3701 (void) sscanf (*ep, "%lu", &e -> eb_size);
3702 continue;
3703 }
3704 if (uleq (*ap, "server")) {
3705 e -> eb_server = *ep;
3706 continue;
3707 }
3708 if (uleq (*ap, "subject")) {
3709 e -> eb_subject = *ep;
3710 continue;
3711 }
3712 if (composing && uleq (*ap, "body")) {
3713 e -> eb_body = getcpy (*ep);
3714 continue;
3715 }
3716 }
3717
3718 if (!e -> eb_access) {
3719 advise (NULLCP,
3720 "invalid parameters for \"%s/%s\" type in message %s's %s field",
3721 ci -> ci_type, ci -> ci_subtype,
3722 ct -> c_file, TYPE_FIELD);
3723 return NOTOK;
3724 }
3725
3726 return OK;
3727 }
3728
3729 static int list_external (ct, toplevel)
3730 register CT ct;
3731 int toplevel;
3732 {
3733 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
3734
3735 (void) list_content (ct, toplevel);
3736 if (verbosw) {
3737 if (e -> eb_name)
3738 printf ("\t retrieve %s\n", e -> eb_name);
3739 if (e -> eb_dir)
3740 printf ("\t in directory %s\n", e -> eb_dir);
3741 if (e -> eb_site)
3742 printf ("\t from %s\n", e -> eb_site);
3743 if (e -> eb_server)
3744 printf ("\t from mailbox %s\n", e -> eb_server);
3745 if (e -> eb_subject)
3746 printf ("\t with subject %s\n", e -> eb_subject);
3747 printf ("\t using %s", e -> eb_access);
3748 if (e -> eb_mode)
3749 printf (" (in %s mode)", e -> eb_mode);
3750 if (e -> eb_permission)
3751 printf (" (permission %s)", e -> eb_permission);
3752 if (e -> eb_flags == NOTOK)
3753 printf (" [service unavailable]");
3754 printf ("\n");
3755 }
3756 (void) list_content (e -> eb_content, 0);
3757
3758 return OK;
3759 }
3760
3761
3762 static int show_external (ct, serial, alternate)
3763 register CT ct;
3764 int serial,
3765 alternate;
3766 {
3767 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
3768 register CT p = e -> eb_content;
3769
3770 if (!type_ok (p, 0))
3771 return OK;
3772
3773 if (p -> c_ctshowfnx)
3774 return (*p -> c_ctshowfnx) (p, serial, alternate);
3775
3776 content_error (NULLCP, p, "don't know how to display content");
3777 return NOTOK;
3778 }
3779
3780
3781 static int store_external (ct)
3782 register CT ct;
3783 {
3784 int result = NOTOK;
3785 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
3786 register CT p = e -> eb_content;
3787
3788 if (!type_ok (p, 1))
3789 return OK;
3790
3791 p -> c_partno = ct -> c_partno;
3792 if (p -> c_ctstorefnx)
3793 result = (*p -> c_ctstorefnx) (p, NULLCP);
3794 p -> c_partno = NULL;
3795
3796 return result;
3797 }
3798
3799
3800 static int free_external (ct)
3801 register CT ct;
3802 {
3803 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
3804
3805 if (!e)
3806 return;
3807
3808 free_content (e -> eb_content);
3809 if (e -> eb_body)
3810 free (e -> eb_body);
3811
3812 free ((char *) e);
3813 ct -> c_ctparams = NULL;
3814 }
3815
3816
3817 static struct k2v SubMessage[] = {
3818 "rfc822", MESSAGE_RFC822,
3819 "partial", MESSAGE_PARTIAL,
3820 "external-body", MESSAGE_EXTERNAL,
3821
3822 NULL, MESSAGE_UNKNOWN /* this one must be last! */
3823 };
3824
3825 static int InitMessage (ct)
3826 register CT ct;
3827 {
3828 register struct k2v *kv;
3829 register CI ci = &ct -> c_ctinfo;
3830
3831 if (ct -> c_encoding != CE_7BIT) {
3832 admonish (NULLCP,
3833 "\"%s/%s\" type in message %s should be encoded in 7bit",
3834 ci -> ci_type, ci -> ci_subtype, ct -> c_file);
3835 return NOTOK;
3836 }
3837
3838 if (!*ci -> ci_subtype) /* XXX: attmail bogosity! */
3839 ci -> ci_subtype = add ("rfc822", ci -> ci_subtype);
3840 for (kv = SubMessage; kv -> kv_key; kv++)
3841 if (uleq (ci -> ci_subtype, kv -> kv_key))
3842 break;
3843
3844 switch (ct -> c_subtype = kv -> kv_value) {
3845 case MESSAGE_RFC822:
3846 ct -> c_showproc = add ("%pshow -file '%F'", NULLCP);
3847 break;
3848
3849 case MESSAGE_PARTIAL:
3850 {
3851 register char **ap,
3852 **ep;
3853 register struct partial *p;
3854
3855 ct -> c_ctshowfnx = NULL;
3856 ct -> c_ctstorefnx = NULL;
3857
3858 if ((p = (struct partial *) calloc (1, sizeof *p)) == NULL)
3859 adios (NULLCP, "out of memory");
3860 ct -> c_ctparams = (caddr_t) p;
3861 ct -> c_ctfreefnx = free_partial;
3862
3863 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
3864 *ap;
3865 ap++, ep++) {
3866 if (uleq (*ap, "id")) {
3867 p -> pm_partid = add (*ep, NULLCP);
3868
3869 continue;
3870 }
3871
3872 if (uleq (*ap, "number")) {
3873 if (sscanf (*ep, "%d", &p -> pm_partno) != 1
3874 || p -> pm_partno < 1) {
3875 invalid_param: ;
3876 advise (NULLCP,
3877 "invalid %s parameter for \"%s/%s\" type in message %s's %s field",
3878 *ap, ci -> ci_type, ci -> ci_subtype,
3879 ct -> c_file, TYPE_FIELD);
3880 return NOTOK;
3881 }
3882
3883 continue;
3884 }
3885
3886 if (uleq (*ap, "total")) {
3887 if (sscanf (*ep, "%d", &p -> pm_maxno) != 1
3888 || p -> pm_maxno < 1)
3889 goto invalid_param;
3890
3891 continue;
3892 }
3893 }
3894
3895 if (!p -> pm_partid
3896 || !p -> pm_partno
3897 || (p -> pm_maxno && p -> pm_partno > p -> pm_maxno)) {
3898 advise (NULLCP,
3899 "invalid parameters for \"%s/%s\" type in message %s's %s field",
3900 ci -> ci_type, ci -> ci_subtype,
3901 ct -> c_file, TYPE_FIELD);
3902 return NOTOK;
3903 }
3904
3905 ct -> c_ctlistfnx = list_partial;
3906 ct -> c_ctstorefnx = store_partial;
3907 }
3908 break;
3909
3910 case MESSAGE_EXTERNAL:
3911 {
3912 int exresult;
3913 register struct exbody *e;
3914 CT p;
3915 FILE *fp;
3916
3917 ct -> c_ctshowfnx = NULL;
3918 ct -> c_ctstorefnx = NULL;
3919
3920 if ((e = (struct exbody *) calloc (1, sizeof *e)) == NULL)
3921 adios (NULLCP, "out of memory");
3922 ct -> c_ctparams = (caddr_t) e;
3923 ct -> c_ctfreefnx = free_external;
3924
3925 if (!ct -> c_fp
3926 && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
3927 advise (ct -> c_file, "unable to open for reading");
3928 return NOTOK;
3929 }
3930
3931 (void) fseek (fp = ct -> c_fp, ct -> c_begin, 0);
3932
3933 if ((p = get_content (fp, ct -> c_file, 0)) == NULLCT) {
3934 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
3935 return NOTOK;
3936 }
3937
3938 e -> eb_parent = ct;
3939 e -> eb_content = p;
3940 p -> c_ctextern = (caddr_t) e;
3941 if ((exresult = params_external (ct, 0)) != NOTOK
3942 && p -> c_ceopenfnx == openMail) {
3943 int cc,
3944 size;
3945 char *bp;
3946
3947 if ((size = ct -> c_end - p -> c_begin) <= 0) {
3948 if (!e -> eb_subject)
3949 content_error (NULLCP, ct,
3950 "empty body for access-type=mail-server");
3951 goto no_body;
3952 }
3953
3954 if ((e -> eb_body = bp = malloc ((unsigned) size)) == NULL)
3955 adios (NULLCP, "out of memory");
3956 (void) fseek (p -> c_fp, p -> c_begin, 0);
3957 while (size > 0)
3958 switch (cc = fread (bp, sizeof *bp, size, p -> c_fp)) {
3959 case NOTOK:
3960 adios ("failed", "fread");
3961
3962 case OK:
3963 adios (NULLCP, "unexpected EOF from fread");
3964
3965 default:
3966 bp += cc, size -= cc;
3967 break;
3968 }
3969 *bp = 0;
3970 no_body: ;
3971 }
3972 p -> c_fp = NULL;
3973 p -> c_end = p -> c_begin;
3974
3975 (void) fclose (ct -> c_fp), ct -> c_fp = NULL;
3976
3977 ct -> c_ctlistfnx = list_external;
3978
3979 if (exresult == NOTOK)
3980 return NOTOK;
3981 if (e -> eb_flags == NOTOK)
3982 return OK;
3983
3984 if (e -> eb_name && autosw) {
3985 char *cp = e -> eb_name;
3986
3987 if (*cp != '/'
3988 && *cp != '.'
3989 && *cp != '|'
3990 && *cp != '!'
3991 && !index (cp, '%')) {
3992 if (!ct -> c_storeproc)
3993 ct -> c_storeproc = add (cp, NULLCP);
3994 if (!p -> c_storeproc)
3995 p -> c_storeproc = add (cp, NULLCP);
3996 }
3997 }
3998
3999 ct -> c_ctshowfnx = show_external;
4000 ct -> c_ctstorefnx = store_external;
4001 switch (p -> c_type) {
4002 case CT_MULTIPART:
4003 break;
4004
4005 case CT_MESSAGE:
4006 if (p -> c_subtype != MESSAGE_RFC822)
4007 break;
4008 /* else fall... */
4009 default:
4010 e -> eb_partno = ct -> c_partno;
4011 if (p -> c_ctinitfnx)
4012 (void) (*p -> c_ctinitfnx) (p);
4013 break;
4014 }
4015 }
4016 break;
4017
4018 default:
4019 break;
4020 }
4021
4022 return OK;
4023 }
4024
4025 /* \f APPLICATION */
4026
4027 #define APPLICATION_UNKNOWN 0x00
4028 #define APPLICATION_OCTETS 0x01
4029 #define APPLICATION_POSTSCRIPT 0x02
4030
4031
4032 static int list_application (ct, toplevel)
4033 register CT ct;
4034 int toplevel;
4035 {
4036 (void) list_content (ct, toplevel);
4037 if (verbosw) {
4038 register char **ap,
4039 **ep;
4040 register CI ci = &ct -> c_ctinfo;
4041
4042 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
4043 printf ("\t %s=\"%s\"\n", *ap, *ep);
4044 }
4045
4046 return OK;
4047 }
4048
4049
4050 static struct k2v SubApplication[] = {
4051 "octet-stream", APPLICATION_OCTETS,
4052 "postscript", APPLICATION_POSTSCRIPT,
4053 NULL, APPLICATION_UNKNOWN /* this one must be last! */
4054 };
4055
4056 static int InitApplication (ct)
4057 register CT ct;
4058 {
4059 register char **ap,
4060 **ep;
4061 register struct k2v *kv;
4062 register CI ci = &ct -> c_ctinfo;
4063
4064 ct -> c_ctlistfnx = list_application;
4065
4066 for (kv = SubApplication; kv -> kv_key; kv++)
4067 if (uleq (ci -> ci_subtype, kv -> kv_key))
4068 break;
4069
4070 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++)
4071 if (autosw && !ct -> c_storeproc && uleq (*ap, "name")) {
4072 register char *cp;
4073
4074 if (*(cp = *ep) != '/'
4075 && *cp != '.'
4076 && *cp != '|'
4077 && *cp != '!'
4078 && !index (cp, '%'))
4079 ct -> c_storeproc = add (cp, NULLCP);
4080 }
4081
4082 if ((ct -> c_subtype = kv -> kv_value) == APPLICATION_OCTETS) {
4083 int tarP,
4084 zP;
4085
4086 tarP = zP = 0;
4087 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) {
4088 if (uleq (*ap, "type")) {
4089 if (!uleq (*ep, "tar"))
4090 break;
4091
4092 tarP = 1;
4093 continue;
4094 }
4095
4096 if ((uleq (*ap, "conversions") || uleq (*ap, "x-conversions"))
4097 && (uleq (*ep, "compress") || uleq (*ep, "x-compress"))) {
4098 zP = 1;
4099 continue;
4100 }
4101 }
4102
4103 if (tarP) {
4104 ct -> c_showproc = add (zP ? "%euncompress | tar tvf -"
4105 : "%etar tvf -", NULLCP);
4106 if (!ct -> c_storeproc)
4107 if (autosw) {
4108 ct -> c_storeproc = add (zP ? "| uncompress | tar xvpf -"
4109 : "| tar xvpf -", NULLCP);
4110 ct -> c_umask = 0022;
4111 }
4112 else
4113 ct -> c_storeproc = add (zP ? "%m%P.tar.Z" : "%m%P.tar",
4114 NULLCP);
4115 }
4116 }
4117
4118 return OK;
4119 }
4120
4121 /* \f ENCODINGS */
4122
4123 #include "md5.c"
4124
4125
4126 struct cefile {
4127 char *ce_file;
4128 int ce_unlink;
4129
4130 FILE *ce_fp;
4131 };
4132
4133
4134 static int list_encoding (ct)
4135 register CT ct;
4136 {
4137 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4138
4139 if (ce)
4140 fprintf (stderr, " decoded fp 0x%x file \"%s\"\n", ce -> ce_fp,
4141 ce -> ce_file ? ce -> ce_file : "");
4142
4143 return OK;
4144 }
4145
4146
4147 static int close_encoding (ct)
4148 register CT ct;
4149 {
4150 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4151
4152 if (!ce)
4153 return;
4154
4155 if (ce -> ce_fp) {
4156 (void) fclose (ce -> ce_fp);
4157 ce -> ce_fp = NULL;
4158 }
4159 }
4160
4161
4162 static unsigned long size_encoding (ct)
4163 register CT ct;
4164 {
4165 int fd;
4166 unsigned long size;
4167 char *file;
4168 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4169 struct stat st;
4170
4171 if (!ce) {
4172 estimate: ;
4173
4174 return (ct -> c_end - ct -> c_begin);
4175 }
4176
4177 if (ce -> ce_fp && fstat (fileno (ce -> ce_fp), &st) != NOTOK)
4178 return (long) st.st_size;
4179
4180 if (ce -> ce_file)
4181 return stat (ce -> ce_file, &st) != NOTOK ? (long) st.st_size : 0L;
4182
4183 if (ct -> c_encoding == CE_EXTERNAL)
4184 goto estimate;
4185
4186 file = NULL;
4187 if ((fd = (*ct -> c_ceopenfnx) (ct, &file)) == NOTOK)
4188 goto estimate;
4189
4190 size = fstat (fd, &st) != NOTOK ? (long) st.st_size : 0L;
4191
4192 (*ct -> c_ceclosefnx) (ct);
4193
4194 return size;
4195 }
4196
4197
4198 static int free_encoding (ct, toplevel)
4199 register CT ct;
4200 int toplevel;
4201 {
4202 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4203
4204 if (!ce)
4205 return;
4206
4207 if (ce -> ce_fp) {
4208 (void) fclose (ce -> ce_fp);
4209 ce -> ce_fp = NULL;
4210 }
4211
4212 if (ce -> ce_file) {
4213 if (ce -> ce_unlink)
4214 (void) unlink (ce -> ce_file);
4215 free (ce -> ce_file);
4216 }
4217
4218 if (toplevel) {
4219 free ((char *) ce);
4220 ct -> c_ceparams = NULL;
4221 }
4222 else
4223 ct -> c_ceopenfnx = NULL;
4224 }
4225
4226
4227 static init_encoding (ct, openfnx)
4228 register CT ct;
4229 int (*openfnx) ();
4230 {
4231 register struct cefile *ce;
4232
4233 if ((ce = (struct cefile *) calloc (1, sizeof *ce)) == NULL)
4234 adios (NULLCP, "out of memory");
4235
4236 ct -> c_ceparams = (caddr_t) ce;
4237 ct -> c_ceopenfnx = openfnx;
4238 ct -> c_ceclosefnx = close_encoding;
4239 ct -> c_cesizefnx = size_encoding;
4240 ct -> c_celistfnx = list_encoding;
4241 ct -> c_cefreefnx = free_encoding;
4242
4243 return OK;
4244 }
4245
4246 /* \f BASE64 */
4247
4248 static unsigned char b642nib[0x80] = {
4249 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4250 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4251 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4252 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4253 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4254 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
4255 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
4256 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
4257 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
4258 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
4259 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
4260 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
4261 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
4262 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
4263 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
4264 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
4265 };
4266
4267
4268 static int openBase64 (ct, file)
4269 register CT ct;
4270 char **file;
4271 {
4272 int bitno,
4273 cc,
4274 digested,
4275 fd,
4276 len,
4277 skip;
4278 unsigned long bits;
4279 register char *cp,
4280 *ep;
4281 unsigned char value,
4282 *b = (unsigned char *) &bits,
4283 *b1 = &b[endian > 0 ? 1 : 2],
4284 *b2 = &b[endian > 0 ? 2 : 1],
4285 *b3 = &b[endian > 0 ? 3 : 0];
4286 char buffer[BUFSIZ];
4287 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4288 MD5_CTX mdContext;
4289
4290 if (ce -> ce_fp)
4291 goto ready_to_go;
4292 if (ce -> ce_file) {
4293 if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) {
4294 content_error (ce -> ce_file, ct, "unable to fopen for reading");
4295 return NOTOK;
4296 }
4297
4298 *file = ce -> ce_file;
4299 return fileno (ce -> ce_fp);
4300 }
4301
4302 ce -> ce_unlink = *file == NULL;
4303 if ((ce -> ce_fp = fopen (ce -> ce_file =
4304 add (*file ? *file : m_scratch ("", tmp),
4305 NULLCP),
4306 "w+")) == NULL) {
4307 content_error (ce -> ce_file, ct,
4308 "unable to fopen for writing and reading");
4309 return NOTOK;
4310 }
4311
4312 if ((len = ct -> c_end - ct -> c_begin) < 0)
4313 adios (NULLCP, "internal error(1)");
4314
4315 if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
4316 content_error (ct -> c_file, ct, "unable to open for reading");
4317 return NOTOK;
4318 }
4319
4320 if (digested = ct -> c_digested)
4321 MD5Init (&mdContext);
4322
4323 (void) lseek (fd = fileno (ct -> c_fp), (off_t)ct -> c_begin, 0);
4324 bitno = 18, bits = 0L, skip = 0;
4325 while (len > 0)
4326 switch (cc = read (fd, buffer, sizeof buffer - 1)) {
4327 case NOTOK:
4328 content_error (ct -> c_file, ct, "error reading from");
4329 goto clean_up;
4330
4331 case OK:
4332 content_error (NULLCP, ct, "premature eof");
4333 goto clean_up;
4334
4335 default:
4336 if (cc > len)
4337 cc = len;
4338 len -= cc;
4339
4340 for (ep = (cp = buffer) + cc; cp < ep; cp++)
4341 switch (*cp) {
4342 default:
4343 if (isspace (*cp))
4344 break;
4345 if (skip
4346 || (*cp & 0x80)
4347 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
4348 if (debugsw)
4349 fprintf (stderr,
4350 "*cp=0x%x pos=%ld skip=%d\n", *cp,
4351 (long) lseek (fd, (off_t)0, 1) - (ep - cp),
4352 skip);
4353 content_error (NULLCP, ct,
4354 "invalid BASE64 encoding -- continuing");
4355 continue;
4356 }
4357
4358 bits |= value << bitno;
4359 test_end: ;
4360 if ((bitno -= 6) < 0) {
4361 (void) putc ((char) *b1, ce -> ce_fp);
4362 if (digested)
4363 MD5Update (&mdContext, b1, 1);
4364 if (skip < 2) {
4365 (void) putc ((char) *b2, ce -> ce_fp);
4366 if (digested)
4367 MD5Update (&mdContext, b2, 1);
4368 if (skip < 1) {
4369 (void) putc ((char) *b3, ce -> ce_fp);
4370 if (digested)
4371 MD5Update (&mdContext, b3, 1);
4372 }
4373 }
4374
4375 if (ferror (ce -> ce_fp)) {
4376 content_error (ce -> ce_file, ct,
4377 "error writing to");
4378 goto clean_up;
4379 }
4380 bitno = 18, bits = 0L, skip = 0;
4381 }
4382 break;
4383
4384 case '=':
4385 if (++skip > 3)
4386 goto self_delimiting;
4387 goto test_end;
4388 }
4389 }
4390 if (bitno != 18) {
4391 if (debugsw)
4392 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
4393
4394 content_error (NULLCP, ct, "invalid BASE64 encoding");
4395 goto clean_up;
4396 }
4397 self_delimiting: ;
4398 (void) fseek (ct -> c_fp, 0L, 0);
4399
4400 if (fflush (ce -> ce_fp)) {
4401 content_error (ce -> ce_file, ct, "error writing to");
4402 goto clean_up;
4403 }
4404
4405 if (digested) {
4406 unsigned char digest[16];
4407
4408 MD5Final (digest, &mdContext);
4409 if (bcmp ((char *) digest, (char *) ct -> c_digest,
4410 sizeof digest / sizeof digest[0]))
4411 content_error (NULLCP, ct,
4412 "content integrity suspect (digest mismatch) -- continuing");
4413 else
4414 if (debugsw)
4415 fprintf (stderr, "content integrity confirmed\n");
4416 }
4417
4418 ready_to_go: ;
4419 (void) fseek (ce -> ce_fp, 0L, 0);
4420 *file = ce -> ce_file;
4421 return fileno (ce -> ce_fp);
4422
4423 clean_up: ;
4424 free_encoding (ct, 0);
4425
4426 return NOTOK;
4427 }
4428
4429
4430 static int InitBase64 (ct)
4431 register CT ct;
4432 {
4433 return init_encoding (ct, openBase64);
4434 }
4435
4436
4437 static int set_endian ()
4438 {
4439 char *cp;
4440 union {
4441 long l;
4442 char c[sizeof (long)];
4443 } un;
4444
4445 un.l = 1;
4446 endian = un.c[0] ? -1 : 1;
4447 if (debugsw)
4448 fprintf (stderr, "%s endian architecture\n",
4449 endian > 0 ? "big" : "little");
4450
4451 mm_charset = getenv ("MM_CHARSET");
4452
4453 if ((cp = getenv ("MM_NOASK")) && strcmp (cp, "1") == 0) {
4454 nolist = 1, pausesw = 0;
4455 if (showsw)
4456 listsw = 0;
4457 }
4458 }
4459
4460 /* \f QUOTED */
4461
4462 static char hex2nib[0x80] = {
4463 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4464 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4465 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4466 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4467 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4468 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4469 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
4470 0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4471 0x00, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x00,
4472 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4473 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4474 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4475 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00,
4476 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4477 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
4478 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
4479 };
4480
4481
4482 static int openQuoted (ct, file)
4483 register CT ct;
4484 char **file;
4485 {
4486 int cc,
4487 digested,
4488 len,
4489 quoted;
4490 register char *cp,
4491 *ep;
4492 char buffer[BUFSIZ];
4493 unsigned char mask;
4494 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4495 MD5_CTX mdContext;
4496
4497 if (ce -> ce_fp)
4498 goto ready_to_go;
4499 if (ce -> ce_file) {
4500 if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) {
4501 content_error (ce -> ce_file, ct, "unable to fopen for reading");
4502 return NOTOK;
4503 }
4504
4505 *file = ce -> ce_file;
4506 return fileno (ce -> ce_fp);
4507 }
4508
4509 ce -> ce_unlink = *file == NULL;
4510 if ((ce -> ce_fp = fopen (ce -> ce_file =
4511 add (*file ? *file : m_scratch ("", tmp),
4512 NULLCP),
4513 "w+")) == NULL) {
4514 content_error (ce -> ce_file, ct,
4515 "unable to fopen for writing and reading");
4516 return NOTOK;
4517 }
4518
4519 if ((len = ct -> c_end - ct -> c_begin) < 0)
4520 adios (NULLCP, "internal error(2)");
4521
4522 if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
4523 content_error (ct -> c_file, ct, "unable to open for reading");
4524 return NOTOK;
4525 }
4526
4527 if (digested = ct -> c_digested)
4528 MD5Init (&mdContext);
4529
4530 (void) fseek (ct -> c_fp, ct -> c_begin, 0);
4531 quoted = 0;
4532 #ifdef lint
4533 mask = 0;
4534 #endif
4535 while (len > 0) {
4536 char *dp;
4537
4538 if (fgets (buffer, sizeof buffer - 1, ct -> c_fp) == NULL) {
4539 content_error (NULLCP, ct, "premature eof");
4540 goto clean_up;
4541 }
4542
4543 if ((cc = strlen (buffer)) > len)
4544 cc = len;
4545 len -= cc;
4546
4547 for (ep = (cp = buffer) + cc - 1; cp <= ep; ep--)
4548 if (!isspace (*ep))
4549 break;
4550 *++ep = '\n', ep++;
4551
4552 for (; cp < ep; cp++) {
4553 if (quoted) {
4554 if (quoted > 1) {
4555 if (!isxdigit (*cp)) {
4556 invalid_hex: ;
4557 dp = "expecting hexidecimal-digit";
4558 goto invalid_encoding;
4559 }
4560 mask <<= 4;
4561 mask |= hex2nib[*cp & 0x7f];
4562 (void) putc (mask, ce -> ce_fp);
4563 if (digested)
4564 MD5Update (&mdContext, &mask, 1);
4565 }
4566 else
4567 switch (*cp) {
4568 case ':':
4569 (void) putc (*cp, ce -> ce_fp);
4570 if (digested)
4571 MD5Update (&mdContext, (unsigned char *) ":",
4572 1);
4573 break;
4574
4575 default:
4576 if (!isxdigit (*cp))
4577 goto invalid_hex;
4578 mask = hex2nib[*cp & 0x7f];
4579 quoted = 2;
4580 continue;
4581 }
4582
4583 if (ferror (ce -> ce_fp)) {
4584 content_error (ce -> ce_file, ct, "error writing to");
4585 goto clean_up;
4586 }
4587 quoted = 0;
4588 continue;
4589 }
4590
4591 switch (*cp) {
4592 default:
4593 if (*cp < '!' || *cp > '~') {
4594 int i;
4595 dp = "expecting character in range [!..~]";
4596
4597 invalid_encoding: ;
4598 i = strlen (invo_name) + 2;
4599 content_error (NULLCP, ct,
4600 "invalid QUOTED-PRINTABLE encoding -- %s,\n%*.*sbut got char 0x%x",
4601 dp, i, i, "", *cp);
4602 goto clean_up;
4603 }
4604 /* and fall...*/
4605 case ' ':
4606 case '\t':
4607 case '\n':
4608 (void) putc (*cp, ce -> ce_fp);
4609 if (digested) {
4610 if (*cp == '\n')
4611 MD5Update (&mdContext, (unsigned char *) "\r\n",2);
4612 else
4613 MD5Update (&mdContext, (unsigned char *) cp, 1);
4614 }
4615 if (ferror (ce -> ce_fp)) {
4616 content_error (ce -> ce_file, ct, "error writing to");
4617 goto clean_up;
4618 }
4619 break;
4620
4621 case '=':
4622 if (*++cp != '\n') {
4623 quoted = 1;
4624 cp--;
4625 }
4626 break;
4627 }
4628 }
4629 }
4630 if (quoted) {
4631 content_error (NULLCP, ct,
4632 "invalid QUOTED-PRINTABLE encoding -- end-of-content while still quoting");
4633 goto clean_up;
4634 }
4635 (void) fseek (ct -> c_fp, 0L, 0);
4636
4637 if (fflush (ce -> ce_fp)) {
4638 content_error (ce -> ce_file, ct, "error writing to");
4639 goto clean_up;
4640 }
4641
4642 if (digested) {
4643 unsigned char digest[16];
4644
4645 MD5Final (digest, &mdContext);
4646 if (bcmp ((char *) digest, (char *) ct -> c_digest,
4647 sizeof digest / sizeof digest[0]))
4648 content_error (NULLCP, ct,
4649 "content integrity suspect (digest mismatch) -- continuing");
4650 else
4651 if (debugsw)
4652 fprintf (stderr, "content integrity confirmed\n");
4653 }
4654
4655 ready_to_go: ;
4656 (void) fseek (ce -> ce_fp, 0L, 0);
4657 *file = ce -> ce_file;
4658 return fileno (ce -> ce_fp);
4659
4660 clean_up: ;
4661 free_encoding (ct, 0);
4662
4663 return NOTOK;
4664 }
4665
4666
4667 static int InitQuoted (ct)
4668 register CT ct;
4669 {
4670 return init_encoding (ct, openQuoted);
4671 }
4672
4673 /* \f 7BIT */
4674
4675 static int open7Bit (ct, file)
4676 register CT ct;
4677 char **file;
4678 {
4679 int cc,
4680 fd,
4681 len;
4682 char buffer[BUFSIZ];
4683 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4684
4685 if (ce -> ce_fp)
4686 goto ready_to_go;
4687 if (ce -> ce_file) {
4688 if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) {
4689 content_error (ce -> ce_file, ct, "unable to fopen for reading");
4690 return NOTOK;
4691 }
4692
4693 *file = ce -> ce_file;
4694 return fileno (ce -> ce_fp);
4695 }
4696
4697 ce -> ce_unlink = *file == NULL;
4698 if ((ce -> ce_fp = fopen (ce -> ce_file =
4699 add (*file ? *file : m_scratch ("", tmp),
4700 NULLCP),
4701 "w+")) == NULL) {
4702 content_error (ce -> ce_file, ct,
4703 "unable to fopen for writing and reading");
4704 return NOTOK;
4705 }
4706
4707 if (ct -> c_type == CT_MULTIPART) {
4708 register char **ap,
4709 **ep;
4710 register CI ci = &ct -> c_ctinfo;
4711
4712 len = 0;
4713
4714 fprintf (ce -> ce_fp, "%s: %s/%s", TYPE_FIELD, ci -> ci_type,
4715 ci -> ci_subtype);
4716 len += strlen (TYPE_FIELD) + 2 + strlen (ci -> ci_type)
4717 + 1 + strlen (ci -> ci_subtype);
4718 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) {
4719 (void) putc (';', ce -> ce_fp);
4720 len++;
4721
4722 (void) sprintf (buffer, "%s=\"%s\"", *ap, *ep);
4723
4724 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
4725 (void) fputs ("\n\t", ce -> ce_fp);
4726 len = 8;
4727 }
4728 else {
4729 (void) putc (' ', ce -> ce_fp);
4730 len++;
4731 }
4732 fprintf (ce -> ce_fp, "%s", buffer);
4733 len += cc;
4734 }
4735 if (ci -> ci_comment) {
4736 if (len + 1 + (cc = 2 + strlen (ci -> ci_comment)) >= CPERLIN) {
4737 (void) fputs ("\n\t", ce -> ce_fp);
4738 len = 8;
4739 }
4740 else {
4741 (void) putc (' ', ce -> ce_fp);
4742 len++;
4743 }
4744 fprintf (ce -> ce_fp, "(%s)", ci -> ci_comment);
4745 len += cc;
4746 }
4747 fprintf (ce -> ce_fp, "\n");
4748 if (ct -> c_id)
4749 fprintf (ce -> ce_fp, "%s:%s", ID_FIELD, ct -> c_id);
4750 if (ct -> c_descr)
4751 fprintf (ce -> ce_fp, "%s:%s", DESCR_FIELD, ct -> c_descr);
4752 fprintf (ce -> ce_fp, "\n");
4753 }
4754
4755 if ((len = ct -> c_end - ct -> c_begin) < 0)
4756 adios (NULLCP, "internal error(3)");
4757
4758 if (!ct -> c_fp && (ct -> c_fp = fopen (ct -> c_file, "r")) == NULL) {
4759 content_error (ct -> c_file, ct, "unable to open for reading");
4760 return NOTOK;
4761 }
4762
4763 (void) lseek (fd = fileno (ct -> c_fp), (off_t) ct -> c_begin, 0);
4764 while (len > 0)
4765 switch (cc = read (fd, buffer, sizeof buffer - 1)) {
4766 case NOTOK:
4767 content_error (ct -> c_file, ct, "error reading from");
4768 goto clean_up;
4769
4770 case OK:
4771 content_error (NULLCP, ct, "premature eof");
4772 goto clean_up;
4773
4774 default:
4775 if (cc > len)
4776 cc = len;
4777 len -= cc;
4778
4779 (void) fwrite (buffer, sizeof *buffer, cc, ce -> ce_fp);
4780 if (ferror (ce -> ce_fp)) {
4781 content_error (ce -> ce_file, ct, "error writing to");
4782 goto clean_up;
4783 }
4784 }
4785 (void) fseek (ct -> c_fp, 0L, 0);
4786
4787 if (fflush (ce -> ce_fp)) {
4788 content_error (ce -> ce_file, ct, "error writing to");
4789 goto clean_up;
4790 }
4791
4792 ready_to_go: ;
4793 (void) fseek (ce -> ce_fp, 0L, 0);
4794 *file = ce -> ce_file;
4795 return fileno (ce -> ce_fp);
4796
4797 clean_up: ;
4798 free_encoding (ct, 0);
4799
4800 return NOTOK;
4801 }
4802
4803
4804 static int Init7Bit (ct)
4805 register CT ct;
4806 {
4807 if (init_encoding (ct, open7Bit) == NOTOK)
4808 return NOTOK;
4809 ct -> c_cesizefnx = NULL;
4810
4811 return OK;
4812 }
4813
4814 /* \f External */
4815
4816 static int openExternal (ct, cb, ce, file, fd)
4817 register CT ct;
4818 CT cb;
4819 struct cefile *ce;
4820 char **file;
4821 int *fd;
4822 {
4823 char cachefile[BUFSIZ];
4824
4825 if (ce -> ce_fp) {
4826 (void) fseek (ce -> ce_fp, 0L, 0);
4827
4828 ready_already: ;
4829 *file = ce -> ce_file, *fd = fileno (ce -> ce_fp);
4830 return DONE;
4831 }
4832
4833 if (ce -> ce_file) {
4834 if ((ce -> ce_fp = fopen (ce -> ce_file, "r")) == NULL) {
4835 content_error (ce -> ce_file, ct, "unable to fopen for reading");
4836 return NOTOK;
4837 }
4838
4839 goto ready_already;
4840 }
4841
4842 if (find_cache (ct, rcachesw, (int *) 0, cb -> c_id, cachefile) != NOTOK) {
4843 if (ce -> ce_fp = fopen (cachefile, "r")) {
4844 ce -> ce_unlink = 0;
4845 ce -> ce_file = getcpy (cachefile);
4846 goto ready_already;
4847 }
4848 else
4849 admonish (cachefile, "unable to fopen for reading");
4850 }
4851
4852 return OK;
4853 }
4854
4855 /* \f File */
4856
4857 static int openFile (ct, file)
4858 register CT ct;
4859 char **file;
4860 {
4861 int fd,
4862 cachetype;
4863 char cachefile[BUFSIZ];
4864 register struct exbody *e = (struct exbody *) ct -> c_ctextern;
4865 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4866
4867 switch (openExternal (e -> eb_parent, e -> eb_content, ce, file, &fd)) {
4868 case NOTOK:
4869 return NOTOK;
4870
4871 case OK:
4872 break;
4873
4874 case DONE:
4875 return fd;
4876 }
4877
4878 if (!e -> eb_name) {
4879 content_error (NULLCP, ct, "missing name parameter");
4880 return NOTOK;
4881 }
4882
4883 ce -> ce_unlink = 0;
4884 if ((ce -> ce_fp = fopen (ce -> ce_file = getcpy (e -> eb_name), "r"))
4885 == NULL) {
4886 content_error (ce -> ce_file, ct, "unable to fopen for reading");
4887 return NOTOK;
4888 }
4889
4890 if ((!e -> eb_permission || !uleq (e -> eb_permission, "read-write"))
4891 && find_cache (NULLCT, wcachesw, &cachetype,
4892 e -> eb_content -> c_id, cachefile) != NOTOK) {
4893 int mask;
4894 FILE *fp;
4895
4896 mask = umask (cachetype ? ~m_gmprot () : 0222);
4897 if (fp = fopen (cachefile, "w")) {
4898 int cc;
4899 char buffer[BUFSIZ];
4900 FILE *gp = ce -> ce_fp;
4901
4902 (void) fseek (gp, 0L, 0);
4903
4904 while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp))
4905 > 0)
4906 (void) fwrite (buffer, sizeof *buffer, cc, fp);
4907 (void) fflush (fp);
4908
4909 if (ferror (gp)) {
4910 admonish (ce -> ce_file, "error reading");
4911 (void) unlink (cachefile);
4912 }
4913 else
4914 if (ferror (fp)) {
4915 admonish (cachefile, "error writing");
4916 (void) unlink (cachefile);
4917 }
4918 (void) fclose (fp);
4919 }
4920 (void) umask (mask);
4921 }
4922
4923 (void) fseek (ce -> ce_fp, 0L, 0);
4924 *file = ce -> ce_file;
4925 return fileno (ce -> ce_fp);
4926 }
4927
4928 /* \f FTP */
4929
4930 static int openFTP (ct, file)
4931 register CT ct;
4932 char **file;
4933 {
4934 int cachetype,
4935 caching,
4936 fd;
4937 char *bp,
4938 *ftp,
4939 *user,
4940 *pass,
4941 buffer[BUFSIZ],
4942 cachefile[BUFSIZ];
4943 register struct exbody *e = (struct exbody *) ct -> c_ctextern;
4944 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
4945 static char *username = NULL;
4946 static char *password = NULL;
4947
4948 (void) sprintf (buffer, "%s-access-ftp", invo_name);
4949 if ((ftp = m_find (buffer)) && !*ftp)
4950 ftp = NULLCP;
4951 #ifndef FTP
4952 if (!ftp)
4953 return NOTOK;
4954 #endif
4955 switch (openExternal (e -> eb_parent, e -> eb_content, ce, file, &fd)) {
4956 case NOTOK:
4957 return NOTOK;
4958
4959 case OK:
4960 break;
4961
4962 case DONE:
4963 return fd;
4964 }
4965
4966 if (!e -> eb_name || !e -> eb_site) {
4967 content_error (NULLCP, ct, "missing %s parameter",
4968 e -> eb_name ? "site": "name");
4969 return NOTOK;
4970 }
4971
4972 if (xpid) {
4973 if (xpid < 0)
4974 xpid = -xpid;
4975 (void) pidcheck (pidwait (xpid, NOTOK));
4976 xpid = 0;
4977 }
4978
4979 bp = buffer;
4980 (void) sprintf (bp, "Retrieve %s", e -> eb_name);
4981 bp += strlen (bp);
4982 if (e -> eb_partno) {
4983 (void) sprintf (bp, " (content %s)", e -> eb_partno);
4984 bp += strlen (bp);
4985 }
4986 (void) sprintf (bp, "\n using %sFTP from site %s",
4987 e -> eb_flags ? "anonymous " : "", e -> eb_site);
4988 bp += strlen (bp);
4989 if (e -> eb_size > 0) {
4990 (void) sprintf (bp, " (%lu octets)", e -> eb_size);
4991 bp += strlen (bp);
4992 }
4993 (void) sprintf (bp, "? ");
4994 if (!getanswer (buffer))
4995 return NOTOK;
4996
4997 if (e -> eb_flags) {
4998 user = "anonymous";
4999 (void) sprintf (pass = buffer, "%s@%s", getusr (), LocalName ());
5000 }
5001 else {
5002 ruserpass (e -> eb_site, &username, &password);
5003 user = username, pass = password;
5004 }
5005
5006 ce -> ce_unlink = *file == NULL, caching = 0, cachefile[0] = 0;
5007 if ((!e -> eb_permission || !uleq (e -> eb_permission, "read-write"))
5008 && find_cache (NULLCT, wcachesw, &cachetype,
5009 e -> eb_content -> c_id, cachefile) != NOTOK) {
5010 if (*file == NULL) {
5011 ce -> ce_unlink = 0;
5012 caching = 1;
5013 }
5014 }
5015
5016 if ((ce -> ce_fp = fopen (ce -> ce_file =
5017 add (*file ? *file
5018 : caching ? cachefile
5019 : m_scratch ("", tmp),
5020 NULLCP),
5021 "w+")) == NULL) {
5022 content_error (ce -> ce_file, ct,
5023 "unable to fopen for writing and reading");
5024 return NOTOK;
5025 }
5026
5027 #ifdef FTP
5028 if (ftp)
5029 #endif
5030 {
5031 int child_id,
5032 i,
5033 vecp;
5034 char *vec[9];
5035
5036 vecp = 0;
5037 vec[vecp++] = r1bindex (ftp, '/');
5038 vec[vecp++] = e -> eb_site;
5039 vec[vecp++] = user;
5040 vec[vecp++] = pass;
5041 vec[vecp++] = e -> eb_dir;
5042 vec[vecp++] = e -> eb_name;
5043 vec[vecp++] = ce -> ce_file,
5044 vec[vecp++] = e -> eb_mode && uleq (e -> eb_mode, "ascii")
5045 ? "ascii" : "binary";
5046 vec[vecp] = NULL;
5047
5048 (void) fflush (stdout);
5049
5050 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
5051 sleep (5);
5052 switch (child_id) {
5053 case NOTOK:
5054 adios ("fork", "unable to");
5055 /* NOTREACHED */
5056
5057 case OK:
5058 (void) close (fileno (ce -> ce_fp));
5059 (void) execvp (ftp, vec);
5060 fprintf (stderr, "unable to exec ");
5061 perror (ftp);
5062 _exit (-1);
5063 /* NOTREACHED */
5064
5065 default:
5066 if (pidXwait (child_id, NULLCP)) {
5067 #ifdef FTP
5068 losing_ftp: ;
5069 #endif
5070 username = password = NULL;
5071 ce -> ce_unlink = 1;
5072 return NOTOK;
5073 }
5074 break;
5075 }
5076 }
5077 #ifdef FTP
5078 else
5079 if (ftp_get (e -> eb_site, user, pass, e -> eb_dir, e -> eb_name,
5080 ce -> ce_file,
5081 !e -> eb_mode || uleq (e -> eb_mode, "ascii"), 0)
5082 == NOTOK)
5083 goto losing_ftp;
5084 #endif
5085
5086 if (cachefile[0])
5087 if (caching)
5088 (void) chmod (cachefile, cachetype ? m_gmprot () : 0444);
5089 else {
5090 int mask;
5091 FILE *fp;
5092
5093 mask = umask (cachetype ? ~m_gmprot () : 0222);
5094 if (fp = fopen (cachefile, "w")) {
5095 int cc;
5096 FILE *gp = ce -> ce_fp;
5097
5098 (void) fseek (gp, 0L, 0);
5099
5100 while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp))
5101 > 0)
5102 (void) fwrite (buffer, sizeof *buffer, cc, fp);
5103 (void) fflush (fp);
5104
5105 if (ferror (gp)) {
5106 admonish (ce -> ce_file, "error reading");
5107 (void) unlink (cachefile);
5108 }
5109 else
5110 if (ferror (fp)) {
5111 admonish (cachefile, "error writing");
5112 (void) unlink (cachefile);
5113 }
5114 (void) fclose (fp);
5115 }
5116 (void) umask (mask);
5117 }
5118
5119 (void) fseek (ce -> ce_fp, 0L, 0);
5120 *file = ce -> ce_file;
5121 return fileno (ce -> ce_fp);
5122 }
5123
5124 /* \f Mail */
5125
5126 static int openMail (ct, file)
5127 register CT ct;
5128 char **file;
5129 {
5130 int child_id,
5131 fd,
5132 i,
5133 vecp;
5134 char *bp,
5135 buffer[BUFSIZ],
5136 *vec[7];
5137 register struct exbody *e = (struct exbody *) ct -> c_ctextern;
5138 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
5139
5140 switch (openExternal (e -> eb_parent, e -> eb_content, ce, file, &fd)) {
5141 case NOTOK:
5142 return NOTOK;
5143
5144 case OK:
5145 break;
5146
5147 case DONE:
5148 return fd;
5149 }
5150
5151 if (!e -> eb_server) {
5152 content_error (NULLCP, ct, "missing server parameter");
5153 return NOTOK;
5154 }
5155
5156 if (xpid) {
5157 if (xpid < 0)
5158 xpid = -xpid;
5159 (void) pidcheck (pidwait (xpid, NOTOK));
5160 xpid = 0;
5161 }
5162
5163 bp = buffer;
5164 (void) sprintf (bp, "Retrieve content");
5165 bp += strlen (bp);
5166 if (e -> eb_partno) {
5167 (void) sprintf (bp, " %s", e -> eb_partno);
5168 bp += strlen (bp);
5169 }
5170 (void) sprintf (bp, " by asking %s\n\n%s\n? ",
5171 e -> eb_server,
5172 e -> eb_subject ? e -> eb_subject : e -> eb_body);
5173 if (!getanswer (buffer))
5174 return NOTOK;
5175
5176 vecp = 0;
5177 vec[vecp++] = r1bindex (mailproc, '/');
5178 vec[vecp++] = e -> eb_server;
5179 vec[vecp++] = "-subject";
5180 vec[vecp++] = e -> eb_subject ? e -> eb_subject : "mail-server request";
5181 vec[vecp++] = "-body";
5182 vec[vecp++] = e -> eb_body;
5183 vec[vecp] = NULL;
5184
5185 for (i = 0; (child_id = vfork ()) == NOTOK && i < 5; i++)
5186 sleep (5);
5187 switch (child_id) {
5188 case NOTOK:
5189 advise ("fork", "unable to");
5190 return NOTOK;
5191
5192 case OK:
5193 (void) execvp (mailproc, vec);
5194 fprintf (stderr, "unable to exec ");
5195 perror (mailproc);
5196 _exit (-1);
5197 /* NOTREACHED */
5198
5199 default:
5200 if (pidXwait (child_id, NULLCP) == OK)
5201 advise (NULLCP, "request sent");
5202 break;
5203 }
5204
5205 ce -> ce_unlink = *file == NULL;
5206 if ((ce -> ce_fp = fopen (ce -> ce_file =
5207 add (*file ? *file : m_scratch ("", tmp),
5208 NULLCP),
5209 "w+")) == NULL) {
5210 content_error (ce -> ce_file, ct,
5211 "unable to fopen for writing and reading");
5212 return NOTOK;
5213 }
5214 if (ct -> c_showproc)
5215 free (ct -> c_showproc);
5216 ct -> c_showproc = add ("true", NULLCP);
5217
5218 (void) fseek (ce -> ce_fp, 0L, 0);
5219 *file = ce -> ce_file;
5220 return fileno (ce -> ce_fp);
5221 }
5222
5223 /* \f CACHE */
5224
5225 static int find_cache (ct, policy, writing, id, buffer)
5226 CT ct;
5227 int policy,
5228 *writing;
5229 char *id,
5230 *buffer;
5231 {
5232 int status = NOTOK;
5233
5234 if (id == NULL)
5235 return NOTOK;
5236 id = trimcpy (id);
5237
5238 if (debugsw)
5239 fprintf (stderr, "find_cache %s(%d) %s %s\n",
5240 caches[policy].sw, policy, writing ? "writing" : "reading",
5241 id);
5242
5243 switch (policy) {
5244 case CACHE_NEVER:
5245 default:
5246 break;
5247
5248 case CACHE_ASK:
5249 case CACHE_PUBLIC:
5250 if (cache_private
5251 && !writing
5252 && find_cache_aux (writing ? 2 : 0, cache_private, id,
5253 buffer) == OK) {
5254 if (access (buffer, 04) != NOTOK) {
5255 got_private: ;
5256 if (writing)
5257 *writing = 1;
5258 got_it: ;
5259 status = OK;
5260 break;
5261 }
5262 }
5263 if (cache_public
5264 && find_cache_aux (writing ? 1 : 0, cache_public, id,
5265 buffer) == OK) {
5266 if (writing || access (buffer, 04) != NOTOK) {
5267 if (writing)
5268 *writing = 0;
5269 goto got_it;
5270 }
5271 }
5272 break;
5273
5274 case CACHE_PRIVATE:
5275 if (cache_private
5276 && find_cache_aux (writing ? 2 : 0, cache_private, id,
5277 buffer) == OK) {
5278 if (writing || access (buffer, 04) != NOTOK)
5279 goto got_private;
5280 }
5281 break;
5282
5283 }
5284
5285 if (status == OK && policy == CACHE_ASK) {
5286 char *bp,
5287 query[BUFSIZ];
5288
5289 if (xpid) {
5290 if (xpid < 0)
5291 xpid = -xpid;
5292 (void) pidcheck (pidwait (xpid, NOTOK));
5293 xpid = 0;
5294 }
5295
5296 bp = query;
5297 if (writing)
5298 (void) sprintf (bp, "Make cached, publically-accessible copy");
5299 else {
5300 struct stat st;
5301
5302 (void) sprintf (bp, "Use cached copy");
5303 bp += strlen (bp);
5304 if (ct -> c_partno) {
5305 (void) sprintf (bp, " of content %s", ct -> c_partno);
5306 bp += strlen (bp);
5307 }
5308 (void) stat (buffer, &st);
5309 (void) sprintf (bp, " (size %lu octets)",
5310 (unsigned long) st.st_size);
5311 }
5312 bp += strlen (bp);
5313 (void) sprintf (bp, "\n in file %s? ", buffer);
5314 if (!getanswer (query))
5315 status = NOTOK;
5316 }
5317 if (status == OK && writing) {
5318 if (*writing && index (buffer, '/'))
5319 (void) make_intermediates (buffer);
5320 (void) unlink (buffer);
5321 }
5322
5323 free (id);
5324 return status;
5325 }
5326
5327 /* \f */
5328
5329 static int find_cache_aux (writing, directory, id, buffer)
5330 int writing;
5331 char *directory,
5332 *id,
5333 *buffer;
5334 {
5335 int mask;
5336 #ifdef BSD42
5337 int usemap = index (id, '/') ? 1 : 0;
5338 #else
5339 int usemap = 1;
5340 #endif
5341 char mapfile[BUFSIZ],
5342 mapname[BUFSIZ];
5343 FILE *fp;
5344 static int partno,
5345 pid;
5346 static long clock = 0L;
5347
5348 if (debugsw)
5349 fprintf (stderr, "find_cache_aux %s usemap=%d\n", directory, usemap);
5350
5351 (void) sprintf (mapfile, "%s/cache.map", directory);
5352 if (find_cache_aux2 (mapfile, id, mapname) == OK)
5353 goto done_map;
5354
5355 if (!writing) {
5356 if (usemap)
5357 return NOTOK;
5358
5359 use_raw: ;
5360 (void) sprintf (buffer, "%s/%s", directory, id);
5361 return OK;
5362 }
5363
5364 if (!usemap && access (mapfile, 02) == NOTOK)
5365 goto use_raw;
5366
5367 if (clock != 0L) {
5368 long now;
5369
5370 (void) time (&now);
5371 if (now > clock)
5372 clock = 0L;
5373 }
5374 else
5375 pid = getpid ();
5376 if (clock == 0L) {
5377 (void) time (&clock);
5378 partno = 0;
5379 }
5380 else
5381 if (partno > 0xff)
5382 clock++, partno = 0;
5383
5384 (void) sprintf (mapname, "%08x%04x%02x", clock & 0xffffffff,
5385 pid & 0xffff, partno++ & 0xff);
5386 if (debugsw)
5387 fprintf (stderr, "creating mapping %s -> %s\n", mapname, id);
5388
5389 (void) make_intermediates (mapfile);
5390 mask = umask (writing == 2 ? 0077 : 0);
5391 if (!(fp = lkfopen (mapfile, "a")) && errno == ENOENT) {
5392 int fd = creat (mapfile, 0666);
5393
5394 if (fd != NOTOK) {
5395 (void) close (fd);
5396 fp = lkfopen (mapfile, "a");
5397 }
5398 }
5399 (void) umask (mask);
5400 if (!fp)
5401 return NOTOK;
5402 fprintf (fp, "%s: %s\n", mapname, id);
5403 (void) lkfclose (fp, mapfile);
5404
5405 done_map: ;
5406 if (*mapname == '/')
5407 (void) strcpy (buffer, mapname);
5408 else
5409 (void) sprintf (buffer, "%s/%s", directory, mapname);
5410 if (debugsw)
5411 fprintf (stderr, "use %s\n", buffer);
5412
5413 return OK;
5414 }
5415
5416 /* \f */
5417
5418 static int find_cache_aux2 (mapfile, id, mapname)
5419 char *mapfile,
5420 *id,
5421 *mapname;
5422 {
5423 int state;
5424 char buf[BUFSIZ],
5425 name[NAMESZ];
5426 FILE *fp;
5427
5428 if (!(fp = lkfopen (mapfile, "r")))
5429 return NOTOK;
5430
5431 for (state = FLD;;) {
5432 int result;
5433 register char *cp,
5434 *dp;
5435
5436 switch (state = m_getfld (state, name, buf, sizeof buf, fp)) {
5437 case FLD:
5438 case FLDPLUS:
5439 case FLDEOF:
5440 (void) strcpy (mapname, name);
5441 if (state != FLDPLUS)
5442 cp = buf;
5443 else {
5444 cp = add (buf, NULLCP);
5445 while (state == FLDPLUS) {
5446 state = m_getfld (state, name, buf, sizeof buf, fp);
5447 cp = add (buf, cp);
5448 }
5449 }
5450 dp = trimcpy (cp);
5451 if (cp != buf)
5452 free (cp);
5453 if (debugsw)
5454 fprintf (stderr, "compare %s to %s <- %s\n", id, dp,
5455 mapname);
5456 result = strcmp (id, dp);
5457 free (dp);
5458 if (result == 0) {
5459 (void) lkfclose (fp, mapfile);
5460 return OK;
5461 }
5462 if (state != FLDEOF)
5463 continue;
5464 /* else fall... */
5465
5466 case BODY:
5467 case BODYEOF:
5468 case FILEEOF:
5469 default:
5470 break;
5471 }
5472 break;
5473 }
5474
5475 (void) lkfclose (fp, mapfile);
5476 return NOTOK;
5477 }
5478
5479 /* \f */
5480
5481 static int cache_content (ct)
5482 register CT ct;
5483 {
5484 int cachetype;
5485 char *file,
5486 cachefile[BUFSIZ];
5487 register struct cefile *ce = (struct cefile *) ct -> c_ceparams;
5488
5489 if (!ct -> c_id) {
5490 advise (NULLCP, "no %s: field in %s", ID_FIELD, ct -> c_file);
5491 return;
5492 }
5493
5494 if (!ce) {
5495 advise (NULLCP, "unable to decode %s", ct -> c_file);
5496 return;
5497 }
5498
5499 if (ct -> c_ceopenfnx == openMail) {
5500 advise (NULLCP, "a radish may no know Greek, but I do...");
5501 return;
5502 }
5503
5504 if (find_cache (NULLCT, wcachesw != CACHE_NEVER ? wcachesw : CACHE_ASK,
5505 &cachetype, ct -> c_id, cachefile)
5506 == NOTOK) {
5507 advise (NULLCP, "unable to cache %s's contents", ct -> c_file);
5508 return;
5509 }
5510 if (wcachesw != CACHE_NEVER && wcachesw != CACHE_ASK) {
5511 (void) fflush (stdout);
5512 fprintf (stderr, "caching message %s as file %s\n", ct -> c_file,
5513 cachefile);
5514 }
5515
5516 if (ce -> ce_file) {
5517 int mask = umask (cachetype ? ~m_gmprot () : 0222);
5518 FILE *fp;
5519
5520 if (debugsw)
5521 fprintf (stderr, "caching by copying %s...\n", ce -> ce_file);
5522
5523 file = NULL;
5524 if ((*ct -> c_ceopenfnx) (ct, &file) == NOTOK)
5525 goto reset_umask;
5526
5527 if (fp = fopen (cachefile, "w")) {
5528 int cc;
5529 char buffer[BUFSIZ];
5530 FILE *gp = ce -> ce_fp;
5531
5532 (void) fseek (gp, 0L, 0);
5533
5534 while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, gp))
5535 > 0)
5536 (void) fwrite (buffer, sizeof *buffer, cc, fp);
5537 (void) fflush (fp);
5538
5539 if (ferror (gp)) {
5540 admonish (ce -> ce_file, "error reading");
5541 (void) unlink (cachefile);
5542 }
5543 else
5544 if (ferror (fp)) {
5545 admonish (cachefile, "error writing");
5546 (void) unlink (cachefile);
5547 }
5548 (void) fclose (fp);
5549 }
5550 else
5551 content_error (cachefile, ct, "unable to fopen for writing");
5552 reset_umask: ;
5553 (void) umask (mask);
5554 }
5555 else {
5556 if (debugsw)
5557 fprintf (stderr, "in place caching...\n");
5558
5559 file = cachefile;
5560 if ((*ct -> c_ceopenfnx) (ct, &file) != NOTOK)
5561 (void) chmod (cachefile, cachetype ? m_gmprot () : 0444);
5562 }
5563 }
5564
5565 /* \f COMPOSITION */
5566
5567 static char prefix[] = "----- =_aaaaaaaaaa";
5568
5569 static char *free_file = NULL;
5570 static CT free_ct = NULL;
5571
5572
5573 static void build_comp (file)
5574 char *file;
5575 {
5576 int compnum,
5577 state;
5578 char *cp,
5579 buf[BUFSIZ],
5580 name[NAMESZ],
5581 tmpfil[BUFSIZ];
5582 struct multipart *m;
5583 register struct part **pp;
5584 CT ct;
5585 FILE *in,
5586 *out;
5587
5588 if ((in = fopen (file, "r")) == NULL)
5589 adios (file, "unable to open for reading");
5590
5591 (void) umask (~m_gmprot ());
5592
5593 (void) strcpy (tmpfil, m_scratch (file, invo_name));
5594 if ((out = fopen (tmpfil, "w")) == NULL)
5595 adios (tmpfil, "unable to open for writing");
5596 free_file = tmpfil;
5597
5598 for (compnum = 1, state = FLD;;) {
5599 switch (state = m_getfld (state, name, buf, sizeof buf, in)) {
5600 case FLD:
5601 case FLDPLUS:
5602 case FLDEOF:
5603 compnum++;
5604
5605 if (uleq (name, VRSN_FIELD))
5606 adios (NULLCP, "draft shouldn't contain %s: field",
5607 VRSN_FIELD);
5608
5609 if (uleq (name, TYPE_FIELD)) {
5610 while (state == FLDPLUS)
5611 state = m_getfld (state, name, buf, sizeof buf, in);
5612 goto finish_field;
5613 }
5614
5615 if (uleq (name, ENCODING_FIELD))
5616 adios (NULLCP, "draft shouldn't contain %s: field",
5617 ENCODING_FIELD);
5618
5619 fprintf (out, "%s:%s", name, buf);
5620 while (state == FLDPLUS) {
5621 state = m_getfld (state, name, buf, sizeof buf, in);
5622 (void) fputs (buf, out);
5623 }
5624 finish_field: ;
5625 if (state != FLDEOF)
5626 continue;
5627 /* else fall... */
5628
5629 case FILEEOF:
5630 adios (NULLCP, "draft has empty body -- no directives!");
5631 /* NOTREACHED */
5632
5633 case BODY:
5634 case BODYEOF:
5635 (void) fseek (in, (long) (-strlen (buf)), 1);
5636 break;
5637
5638 case LENERR:
5639 case FMTERR:
5640 adios (NULLCP, "message format error in component #%d",
5641 compnum);
5642
5643 default:
5644 adios (NULLCP, "getfld() returned %d", state);
5645 }
5646 break;
5647 }
5648
5649 if ((free_ct = ct = (CT) calloc (1, sizeof *ct)) == NULL)
5650 adios (NULLCP, "out of memory");
5651 if (get_ctinfo ("multipart/mixed", ct, 0) == NOTOK)
5652 done (1);
5653 ct -> c_type = CT_MULTIPART;
5654 ct -> c_subtype = MULTI_MIXED;
5655 ct -> c_ctlistfnx = list_multi;
5656 ct -> c_ctfreefnx = free_multi;
5657 ct -> c_file = add (file, NULLCP);
5658
5659 if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL)
5660 adios (NULLCP, "out of memory");
5661 ct -> c_ctparams = (caddr_t) m;
5662
5663 pp = &m -> mp_parts;
5664 while (fgetstr (buf, sizeof buf - 1, in)) {
5665 register struct part *part;
5666 CT p;
5667
5668 if (user_content (in, file, buf, &p) == DONE) {
5669 admonish (NULLCP, "ignoring spurious #end");
5670 continue;
5671 }
5672 if (!p)
5673 continue;
5674
5675 if ((part = (struct part *) calloc (1, sizeof *part)) == NULL)
5676 adios (NULLCP, "out of memory");
5677 *pp = part, pp = &part -> mp_next;
5678 part -> mp_part = p;
5679 }
5680
5681 (void) fclose (in);
5682
5683 if (!m -> mp_parts)
5684 adios (NULLCP, "no content directives found");
5685 if (!m -> mp_parts -> mp_next) {
5686 CT p = m -> mp_parts -> mp_part;
5687
5688 m -> mp_parts -> mp_part = NULL;
5689 free_content (ct);
5690 free_ct = ct = p;
5691 }
5692 else
5693 set_id (ct, 1);
5694
5695 if ((cp = index (prefix, 'a')) == NULL)
5696 adios (NULLCP, "internal error(4)");
5697
5698 while (compose_content (ct) == NOTOK)
5699 if (*cp < 'z')
5700 (*cp)++;
5701 else
5702 if (*++cp == 0)
5703 adios (NULLCP,
5704 "giving up trying to find a unique delimiter string");
5705 else
5706 (*cp)++;
5707
5708 fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
5709 (void) output_content (ct, out);
5710
5711 if (fflush (out))
5712 adios (tmpfil, "error writing to");
5713
5714 (void) fclose (out);
5715
5716 if (listsw && ct -> c_ctlistfnx) {
5717 char *savfile;
5718
5719 if (headsw)
5720 printf (LSTFMT1, "msg", "part", "type/subtype", "size",
5721 "description");
5722
5723 /* to get msgno */
5724 savfile = ct -> c_file, ct -> c_file = file;
5725 (*ct -> c_ctlistfnx) (ct, 1);
5726 ct -> c_file = savfile;
5727 }
5728
5729 free_content (ct);
5730 free_ct = NULL;
5731
5732 (void) sprintf (buf, "%s.orig", m_backup (file));
5733 if (rename (file, buf) == NOTOK)
5734 adios (buf, "unable to rename %s to", file);
5735 if (rename (tmpfil, file) == NOTOK) {
5736 advise (file, "unable to rename %s to", tmpfil);
5737 (void) rename (buf, file);
5738 done (1);
5739 }
5740 free_file = NULL;
5741
5742 done (0);
5743 }
5744
5745 /* \f */
5746
5747 static char *fgetstr (s, n, stream)
5748 char *s;
5749 int n;
5750 FILE *stream;
5751 {
5752 register char *cp,
5753 *ep;
5754
5755 for (ep = (cp = s) + n; cp < ep; ) {
5756 register int i;
5757
5758 if (!fgets (cp, n, stream))
5759 return (cp != s ? s : NULL);
5760 if (cp == s && *cp != '#')
5761 return s;
5762
5763 cp += (i = strlen (cp)) - 1;
5764 if (i <= 1 || *cp-- != '\n' || *cp != '\\')
5765 break;
5766 *cp = 0, n -= (i - 2);
5767 }
5768
5769 return s;
5770 }
5771
5772 /* \f */
5773
5774 static int user_content (in, file, buf, ctp)
5775 FILE *in;
5776 char *file,
5777 *buf;
5778 CT *ctp;
5779 {
5780 int extrnal,
5781 vrsn;
5782 register char *cp,
5783 **ap;
5784 char buffer[BUFSIZ];
5785 struct multipart *m;
5786 register struct part **pp;
5787 struct stat st;
5788 register struct str2init *s2i;
5789 register CI ci;
5790 register CT ct;
5791
5792 if (buf[0] == '\n' || strcmp (buf, "#\n") == 0) {
5793 *ctp = NULL;
5794 return OK;
5795 }
5796
5797 if ((ct = (CT) calloc (1, sizeof *ct)) == NULL)
5798 adios (NULLCP, "out of memory");
5799 *ctp = ct;
5800 ci = &ct -> c_ctinfo;
5801 ct -> c_ctlistfnx = list_content;
5802 set_id (ct, 0);
5803
5804 if (buf[0] != '#' || buf[1] == '#' || buf[1] == '<') {
5805 int headers,
5806 inlineD;
5807 long pos;
5808 char content[BUFSIZ];
5809 FILE *out;
5810
5811 ct -> c_file = add (m_tmpfil (invo_name), NULLCP);
5812 ct -> c_unlink = 1;
5813
5814 if ((out = fopen (ct -> c_file, "w")) == NULL)
5815 adios (ct -> c_file, "unable to open for writing");
5816
5817 if (buf[0] == '#' && buf[1] == '<') {
5818 (void) strcpy (content, buf + 2);
5819 inlineD = 1;
5820 goto rock_and_roll;
5821 }
5822 else
5823 inlineD = 0;
5824
5825 (void) strcpy (content, "text/plain");
5826 headers = 0;
5827 (void) strcpy (buffer, buf[0] != '#' ? buf : buf + 1);
5828 for (;;) {
5829 int i;
5830
5831 if (headers >= 0
5832 && uprf (buffer, DESCR_FIELD)
5833 && buffer[i = strlen (DESCR_FIELD)] == ':') {
5834 headers = 1;
5835
5836 again_descr: ;
5837 ct -> c_descr = add (buffer + i + 1, ct -> c_descr);
5838 if (!fgetstr (buffer, sizeof buffer - 1, in))
5839 adios (NULLCP,
5840 "end-of-file after %s: field in plaintext",
5841 DESCR_FIELD);
5842 switch (buffer[0]) {
5843 case ' ':
5844 case '\t':
5845 i = -1;
5846 goto again_descr;
5847
5848 case '#':
5849 adios (NULLCP,
5850 "#-directive after %s: field in plaintext",
5851 DESCR_FIELD);
5852 /* NOTREACHED */
5853
5854 default:
5855 break;
5856 }
5857 }
5858
5859 if (headers != 1 || buffer[0] != '\n')
5860 (void) fputs (buffer, out);
5861 rock_and_roll: ;
5862 headers = -1;
5863
5864 pos = ftell (in);
5865 if ((cp = fgetstr (buffer, sizeof buffer - 1, in)) == NULL)
5866 break;
5867 if (buffer[0] == '#') {
5868 register char *bp;
5869
5870 if (buffer[1] != '#')
5871 break;
5872 for (cp = (bp = buffer) + 1; *cp; cp++)
5873 *bp++ = *cp;
5874 *bp = '\0';
5875 }
5876 }
5877
5878 if (listsw)
5879 ct -> c_end = ftell (out);
5880 (void) fclose (out);
5881
5882 if (get_ctinfo (content, ct, inlineD) == NOTOK)
5883 done (1);
5884 for (s2i = str2cts; s2i -> si_key; s2i++)
5885 if (uleq (ci -> ci_type, s2i -> si_key))
5886 break;
5887 if (!s2i -> si_key && !uprf (ci -> ci_type, "X-"))
5888 s2i++;
5889 switch (ct -> c_type = s2i -> si_val) {
5890 case CT_MESSAGE:
5891 if (uleq (ci -> ci_subtype, "rfc822")) {
5892 ct -> c_encoding = CE_7BIT; /* XXX */
5893 goto call_init;
5894 }
5895 /* else fall... */
5896 case CT_MULTIPART:
5897 adios (NULLCP,
5898 "it makes sense to define a in-line %s content... NOT!",
5899 ct -> c_type == CT_MESSAGE ? "message" : "multipart");
5900 /* NOTREACHED */
5901
5902 default:
5903 call_init: ;
5904 if (ct -> c_ctinitfnx = s2i -> si_init)
5905 (void) (*ct -> c_ctinitfnx) (ct);
5906 break;
5907 }
5908
5909 if (cp)
5910 (void) fseek (in, pos, 0);
5911 return OK;
5912 }
5913
5914 extrnal = buf[1] == '@';
5915 if (get_ctinfo (buf + (extrnal ? 2 : 1), ct, 1) == NOTOK)
5916 done (1);
5917
5918 for (s2i = str2cts; s2i -> si_key; s2i++)
5919 if (uleq (ci -> ci_type, s2i -> si_key))
5920 break;
5921 if (s2i -> si_key) { /* type/subtype [file] */
5922 if (!ci -> ci_subtype)
5923 adios (NULLCP, "missing subtype in \"#%s\"", ci -> ci_type);
5924
5925 switch (ct -> c_type = s2i -> si_val) {
5926 case CT_MULTIPART:
5927 adios (NULLCP, "use \"#begin ... #end\" instead of \"#%s/%s\"",
5928 ci -> ci_type, ci -> ci_subtype);
5929 /* NOTREACHED */
5930
5931 case CT_MESSAGE:
5932 if (uleq (ci -> ci_subtype, "partial"))
5933 adios (NULLCP, "sorry, \"#%s/%s\" isn't supported",
5934 ci -> ci_type, ci -> ci_subtype);
5935 if (uleq (ci -> ci_subtype, "external-body"))
5936 adios (NULLCP, "use \"#@type/subtype ... [] ...\" instead of \"#%s/%s\"",
5937 ci -> ci_type, ci -> ci_subtype);
5938 use_forw: ;
5939 adios (NULLCP,
5940 "use \"#forw [+folder] [msgs]\" instead of \"#%s/%s\"",
5941 ci -> ci_type, ci -> ci_subtype);
5942 /* NOTREACHED */
5943
5944 default:
5945 if (ct -> c_ctinitfnx = s2i -> si_init)
5946 (void) (*ct -> c_ctinitfnx) (ct);
5947 break;
5948 }
5949
5950 if (extrnal) {
5951 register struct exbody *e;
5952 CT p;
5953
5954 if (!ci -> ci_magic)
5955 adios (NULLCP, "need external information for \"#@%s/%s\"",
5956 ci -> ci_type, ci -> ci_subtype);
5957 p = ct;
5958
5959 (void) sprintf (buffer, "message/external-body; %s",
5960 ci -> ci_magic);
5961 free (ci -> ci_magic), ci -> ci_magic = NULL;
5962
5963 if ((ct = (CT) calloc (1, sizeof *ct)) == NULL)
5964 adios (NULLCP, "out of memory");
5965 *ctp = ct;
5966 ci = &ct -> c_ctinfo;
5967 ct -> c_ctlistfnx = list_content;
5968 if (get_ctinfo (buffer, ct, 0) == NOTOK)
5969 done (1);
5970 ct -> c_type = CT_MESSAGE;
5971 ct -> c_subtype = MESSAGE_EXTERNAL;
5972
5973 if ((e = (struct exbody *) calloc (1, sizeof *e)) == NULL)
5974 adios (NULLCP, "out of memory");
5975 ct -> c_ctparams = (caddr_t) e;
5976 ct -> c_ctfreefnx = free_external;
5977
5978 e -> eb_parent = ct;
5979 e -> eb_content = p;
5980 p -> c_ctextern = (caddr_t) e;
5981
5982 ct -> c_ctlistfnx = list_external;
5983
5984 if (params_external (ct, 1) == NOTOK)
5985 done (1);
5986
5987 return OK;
5988 }
5989
5990 if (ci -> ci_magic) {
5991 if (*ci -> ci_magic == '|' || *ci -> ci_magic == '!') {
5992 for (cp = ci -> ci_magic + 1; isspace (*cp); cp++)
5993 continue;
5994 if (!*cp)
5995 adios (NULLCP, "empty pipe command for #%s directive",
5996 ci -> ci_type);
5997 cp = add (cp, NULLCP);
5998 free (ci -> ci_magic);
5999 ci -> ci_magic = cp;
6000 }
6001 else {
6002 if (access (ct -> c_file = ci -> ci_magic, 04) == NOTOK)
6003 adios ("reading", "unable to access %s for", ct -> c_file);
6004 if (listsw && stat (ct -> c_file, &st) != NOTOK)
6005 ct -> c_end = (long) st.st_size;
6006 ci -> ci_magic = NULL;
6007 }
6008 return OK;
6009 }
6010
6011 (void) sprintf (buffer, "%s-compose-%s/%s", invo_name, ci -> ci_type,
6012 ci -> ci_subtype);
6013 if ((cp = m_find (buffer)) == NULL || *cp == 0) {
6014 (void) sprintf (buffer, "%s-compose-%s", invo_name, ci -> ci_type);
6015 if ((cp = m_find (buffer)) == NULL || *cp == 0) {
6016 content_error (NULLCP, ct,
6017 "don't know how to compose content");
6018 done (1);
6019 }
6020 }
6021 ci -> ci_magic = add (cp, NULLCP);
6022 return OK;
6023 }
6024
6025 if (extrnal)
6026 adios (NULLCP, "externally definition not allowed for \"#%s\"",
6027 ci -> ci_type);
6028
6029 if (uleq (ci -> ci_type, "forw")) { /* #forw [+folder] [msgs] */
6030 int msgnum;
6031 char *folder,
6032 *arguments[MAXARGS];
6033 struct msgs *mp;
6034
6035 if (ci -> ci_magic) {
6036 ap = brkstring (ci -> ci_magic, " ", "\n");
6037 ap = copyip (ap, arguments);
6038 }
6039 else
6040 arguments[0] = "cur", arguments[1] = NULL;
6041
6042 folder = NULL;
6043 for (ap = arguments; cp = *ap; ap++)
6044 if (*cp == '+' || *cp == '@')
6045 if (folder)
6046 adios (NULLCP, "only one folder per #forw directive");
6047 else
6048 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
6049 if (!folder)
6050 folder = add (m_getfolder (), NULLCP);
6051
6052 if ((mp = m_gmsg (folder)) == NULL)
6053 adios (NULLCP, "unable to read folder %s", folder);
6054 for (ap = arguments; cp = *ap; ap++)
6055 if (*cp != '+' && *cp != '@')
6056 if (!m_convert (mp, cp))
6057 done (1);
6058 free (folder);
6059
6060 free_ctinfo (ct);
6061 if (mp -> numsel > 1) {
6062 if (get_ctinfo ("multipart/digest", ct, 0) == NOTOK)
6063 done (1);
6064 ct -> c_type = CT_MULTIPART;
6065 ct -> c_subtype = MULTI_DIGEST;
6066 ct -> c_ctlistfnx = list_multi;
6067 ct -> c_ctfreefnx = free_multi;
6068
6069 if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL)
6070 adios (NULLCP, "out of memory");
6071 ct -> c_ctparams = (caddr_t) m;
6072
6073 pp = &m -> mp_parts;
6074 }
6075 else
6076 free_content (ct);
6077 for (msgnum = mp -> lowsel; msgnum <= mp -> hghsel; msgnum++)
6078 if (mp -> msgstats[msgnum] & SELECTED) {
6079 register struct part *part;
6080 register CT p;
6081
6082 if ((p = (CT) calloc (1, sizeof *p)) == NULL)
6083 adios (NULLCP, "out of memory");
6084 if (get_ctinfo ("message/rfc822", p, 0) == NOTOK)
6085 done (1);
6086 p -> c_type = CT_MESSAGE;
6087 p -> c_subtype = MESSAGE_RFC822;
6088 p -> c_ctlistfnx = list_content;
6089
6090 (void) sprintf (buffer, "%s/%d", mp -> foldpath, msgnum);
6091 p -> c_file = add (buffer, NULLCP);
6092 if (listsw && stat (p -> c_file, &st) != NOTOK)
6093 p -> c_end = (long) st.st_size;
6094
6095 if (mp -> numsel > 1) {
6096 if ((part = (struct part *) calloc (1, sizeof *part))
6097 == NULL)
6098 adios (NULLCP, "out of memory");
6099 *pp = part, pp = &part -> mp_next;
6100 part -> mp_part = p;
6101 }
6102 else
6103 *ctp = ct = p;
6104 }
6105
6106 m_fmsg (mp);
6107
6108 return OK;
6109 }
6110
6111 if (uleq (ci -> ci_type, "end")) {
6112 free_content (ct);
6113 *ctp = NULL;
6114 return DONE;
6115 }
6116
6117 if (!uleq (ci -> ci_type, "begin"))
6118 adios (NULLCP, "unknown directive \"#%s\"", ci -> ci_type);
6119
6120 /* #begin [ alternative | parallel ] */
6121 if (!ci -> ci_magic)
6122 cp = SubMultiPart[(vrsn = MULTI_MIXED) - 1].kv_key;
6123 else
6124 if (uleq (ci -> ci_magic, "alternative"))
6125 cp = SubMultiPart[(vrsn = MULTI_ALTERNATE) - 1].kv_key;
6126 else
6127 if (uleq (ci -> ci_magic, "parallel"))
6128 cp = SubMultiPart[(vrsn = MULTI_PARALLEL) - 1].kv_key;
6129 else
6130 if (uprf (ci -> ci_magic, "digest"))
6131 goto use_forw;
6132 else
6133 cp = ci -> ci_magic, vrsn = MULTI_UNKNOWN;
6134 free_ctinfo (ct);
6135 (void) sprintf (buffer, "multipart/%s", cp);
6136 if (get_ctinfo (buffer, ct, 0) == NOTOK)
6137 done (1);
6138 ct -> c_type = CT_MULTIPART;
6139 ct -> c_subtype = vrsn;
6140 ct -> c_ctlistfnx = list_multi;
6141 ct -> c_ctfreefnx = free_multi;
6142
6143 if ((m = (struct multipart *) calloc (1, sizeof *m)) == NULL)
6144 adios (NULLCP, "out of memory");
6145 ct -> c_ctparams = (caddr_t) m;
6146
6147 pp = &m -> mp_parts;
6148 while (fgetstr (buffer, sizeof buffer - 1, in)) {
6149 register struct part *part;
6150 CT p;
6151
6152 if (user_content (in, file, buffer, &p) == DONE) {
6153 if (!m -> mp_parts)
6154 adios (NULLCP, "empty \"#begin ... #end\" sequence");
6155 return OK;
6156 }
6157 if (!p)
6158 continue;
6159
6160 if ((part = (struct part *) calloc (1, sizeof *part)) == NULL)
6161 adios (NULLCP, "out of memory");
6162 *pp = part, pp = &part -> mp_next;
6163 part -> mp_part = p;
6164 }
6165 admonish (NULLCP, "premature end-of-file, missing #end");
6166 return OK;
6167 }
6168
6169 /* \f */
6170
6171 static void set_id (ct, top)
6172 CT ct;
6173 int top;
6174 {
6175 char msgid[BUFSIZ];
6176 static int partno;
6177 static long clock = 0L;
6178 static char *msgfmt;
6179
6180 if (clock == 0L) {
6181 (void) time (&clock);
6182 (void) sprintf (msgid, "<%d.%ld.%%d@%s>\n", getpid (), clock,
6183 LocalName ());
6184 partno = 0;
6185 msgfmt = getcpy (msgid);
6186 }
6187 (void) sprintf (msgid, msgfmt, top ? 0 : ++partno);
6188 ct -> c_id = getcpy (msgid);
6189 }
6190
6191 /* \f */
6192
6193 static char ebcdicsafe[0x100] = {
6194 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6195 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
6196 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6197 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6198 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01,
6199 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6200 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6201 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6202 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6203 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6204 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6205 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01,
6206 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6207 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6208 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
6209 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
6210 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6211 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6212 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6214 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6218 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6219 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6221 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6222 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6223 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6225 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
6226 };
6227
6228 static int compose_content (ct)
6229 register CT ct;
6230 {
6231 register char *cp;
6232 char buffer[BUFSIZ];
6233 register CI ci = &ct -> c_ctinfo;
6234
6235 if (ct -> c_type == CT_MESSAGE && ct -> c_subtype == MESSAGE_EXTERNAL)
6236 return OK;
6237
6238 switch (ct -> c_type) {
6239 case CT_MULTIPART:
6240 {
6241 int partnum;
6242 register char *pp;
6243 char partnam[BUFSIZ];
6244 struct multipart *m = (struct multipart *) ct -> c_ctparams;
6245 register struct part *part;
6246
6247 if (ct -> c_partno) {
6248 (void) sprintf (partnam, "%s.", ct -> c_partno);
6249 pp = partnam + strlen (partnam);
6250 }
6251 else
6252 pp = partnam;
6253
6254 for (part = m -> mp_parts, partnum = 1;
6255 part;
6256 part = part -> mp_next, partnum++) {
6257 register CT p = part -> mp_part;
6258
6259 (void) sprintf (pp, "%d", partnum);
6260 p -> c_partno = add (partnam, NULLCP);
6261
6262 if (compose_content (p) == NOTOK)
6263 return NOTOK;
6264 }
6265
6266 if (rfc934sw && ct -> c_subtype == MULTI_DIGEST) {
6267 int is934 = 1;
6268
6269 for (part = m -> mp_parts; part; part = part -> mp_next) {
6270 register CT p = part -> mp_part;
6271
6272 if (p -> c_subtype != MESSAGE_RFC822) {
6273 is934 = 0;
6274 break;
6275 }
6276 }
6277
6278 ct -> c_rfc934 = is934;
6279 for (part = m -> mp_parts; part; part = part -> mp_next) {
6280 register CT p = part -> mp_part;
6281
6282 if (p -> c_rfc934 = is934)
6283 p -> c_end++;
6284 }
6285 }
6286
6287 if (listsw) {
6288 ct -> c_end = (partnum = strlen (prefix) + 2) + 2;
6289 if (ct -> c_rfc934)
6290 ct -> c_end += 1;
6291
6292 for (part = m -> mp_parts; part; part = part -> mp_next)
6293 ct -> c_end += part -> mp_part -> c_end + partnum;
6294 }
6295 }
6296 break;
6297
6298
6299 default:
6300 if (!ct -> c_file) {
6301 int child_id,
6302 i,
6303 xstdout;
6304 register char *bp,
6305 **ap;
6306 char *vec[4];
6307 FILE *out;
6308
6309 if (!(cp = ci -> ci_magic))
6310 adios (NULLCP, "internal error(5)");
6311
6312 ct -> c_file = add (m_tmpfil (invo_name), NULLCP);
6313 ct -> c_unlink = 1;
6314
6315 xstdout = 0;
6316 buffer[0] = '\0';
6317 for (bp = buffer; *cp; cp++)
6318 if (*cp == '%') {
6319 switch (*++cp) {
6320 case 'a': /* additional arguments */
6321 {
6322 register char **ep;
6323 char *s = "";
6324
6325 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
6326 *ap;
6327 ap++, ep++) {
6328 (void) sprintf (bp, "%s%s=\"%s\"", s,
6329 *ap, *ep);
6330 bp += strlen (bp);
6331 s = " ";
6332 }
6333 }
6334 break;
6335
6336 case 'F': /* %f, and stdout is not-redirected */
6337 xstdout = 1;
6338 /* and fall... */
6339 case 'f': /* filename */
6340 (void) sprintf (bp, "%s", ct -> c_file);
6341 break;
6342
6343 case 's': /* subtype */
6344 (void) strcpy (bp, ci -> ci_subtype);
6345 break;
6346
6347 case '%':
6348 goto raw;
6349
6350 default:
6351 *bp++ = *--cp;
6352 *bp = '\0';
6353 continue;
6354 }
6355 bp += strlen (bp);
6356 }
6357 else {
6358 raw: ;
6359 *bp++ = *cp;
6360 *bp = '\0';
6361 }
6362
6363 printf ("composing content %s/%s from command\n\t%s\n",
6364 ci -> ci_type, ci -> ci_subtype, buffer);
6365 (void) fflush (stdout);
6366
6367 vec[0] = "/bin/sh";
6368 vec[1] = "-c";
6369 vec[2] = buffer;
6370 vec[3] = NULL;
6371
6372 if ((out = fopen (ct -> c_file, "w")) == NULL)
6373 adios (ct -> c_file, "unable to open for writing");
6374
6375 for (i = 0; (child_id = vfork ()) == NOTOK && i > 5; i++)
6376 sleep (5);
6377 switch (child_id) {
6378 case NOTOK:
6379 adios ("fork", "unable to fork");
6380 /* NOTREACHED */
6381
6382 case OK:
6383 if (!xstdout)
6384 (void) dup2 (fileno (out), 1);
6385 (void) close (fileno (out));
6386 (void) execvp ("/bin/sh", vec);
6387 fprintf (stderr, "unable to exec ");
6388 perror ("/bin/sh");
6389 _exit (-1);
6390 /* NOTREACHED */
6391
6392 default:
6393 (void) fclose (out);
6394 if (pidXwait (child_id, NULLCP))
6395 done (1);
6396 break;
6397 }
6398 }
6399 if (listsw && ct -> c_end == 0L) {
6400 struct stat st;
6401
6402 if (stat (ct -> c_file, &st) != NOTOK)
6403 ct -> c_end = (long) st.st_size;
6404 }
6405 if (ct -> c_type != CT_TEXT && ct -> c_type != CT_APPLICATION)
6406 break;
6407 /* else fall... */
6408
6409 case CT_MESSAGE:
6410 {
6411 int charset,
6412 len,
6413 linelen,
6414 result;
6415 FILE *in;
6416
6417 if ((in = fopen (ct -> c_file, "r")) == NULL)
6418 adios (ct -> c_file, "unable to open for reading");
6419
6420 len = strlen (prefix);
6421 result = OK;
6422 switch (ct -> c_type) {
6423 case CT_TEXT:
6424 charset = ct -> c_ctparams ? 0 : -1;
6425 linelen = ct -> c_subtype == TEXT_PLAIN ? 0 : -1;
6426 break;
6427
6428 case CT_APPLICATION:
6429 charset = linelen = ct -> c_encoding ? 0 : -1;
6430 break;
6431
6432 default:
6433 charset = linelen = 0;
6434 break;
6435 }
6436 while (fgets (buffer, sizeof buffer - 1, in)) {
6437 if (charset == -1) {
6438 for (cp = buffer; *cp; cp++) {
6439 if (!isascii (*cp)) {
6440 charset = CHARSET_UNKNOWN;
6441 break;
6442 }
6443 if (linelen == -1
6444 && ebcdicsw
6445 && !ebcdicsafe[*cp & 0xff])
6446 linelen = 1;
6447 }
6448 if ((linelen == -1) && (cp - buffer > CPERLIN + 1))
6449 linelen = 1;
6450 if (result == NOTOK)
6451 break;
6452 }
6453 else
6454 if ((linelen == -1) && (strlen (buffer) > CPERLIN + 1))
6455 linelen = 1;
6456 if (result == NOTOK)
6457 continue;
6458
6459 if (linelen == -1) {
6460 if ((cp = buffer + strlen (buffer) - 2) > buffer
6461 && isspace (*cp))
6462 linelen = 1;
6463 else
6464 if ((*(cp = buffer) == '.')
6465 || (strncmp (cp, "From ",
6466 sizeof "From " -1) == 0))
6467 linelen = 1;
6468 }
6469
6470 if (buffer[0] == '-' && buffer[1] == '-') {
6471 for (cp = buffer + strlen (buffer) - 1;
6472 cp >= buffer;
6473 cp--)
6474 if (!isspace (*cp))
6475 break;
6476 *++cp = '\0';
6477 if (strncmp (buffer + 2, prefix, len) == 0
6478 && isdigit (buffer[2 + len])) {
6479 result = NOTOK;
6480 if (charset != -1 && linelen != -1)
6481 break;
6482 }
6483 }
6484 }
6485 if (ct -> c_type == CT_APPLICATION && !ct -> c_encoding)
6486 ct -> c_encoding = linelen == -1
6487 && charset != CHARSET_UNKNOWN
6488 ? CE_7BIT
6489 : ct -> c_subtype
6490 != APPLICATION_POSTSCRIPT
6491 ? CE_BASE64 : CE_QUOTED;
6492 if (ct -> c_type == CT_TEXT && !ct -> c_ctparams) {
6493 register char **ap,
6494 **ep;
6495 register struct text *t;
6496
6497 if (charset == CHARSET_UNKNOWN && mm_charset)
6498 charset = -2;
6499 else
6500 if (charset == -1)
6501 charset = CHARSET_USASCII;
6502
6503 if ((t = (struct text *) calloc (1, sizeof *t)) == NULL)
6504 adios (NULLCP, "out of memory");
6505 ct -> c_ctparams = (caddr_t) t;
6506 for (ap = ci -> ci_attrs, ep = ci -> ci_values;
6507 *ap;
6508 ap++, ep++)
6509 continue;
6510 switch (t -> tx_charset = charset) {
6511 case CHARSET_USASCII:
6512 *ap = add ("charset=us-ascii", NULLCP);
6513 break;
6514
6515 case CHARSET_UNKNOWN:
6516 default:
6517 *ap = add ("charset=x-unknown", NULLCP);
6518 break;
6519
6520 case -2:
6521 *ap = concat ("charset=", mm_charset, NULLCP);
6522 break;
6523 }
6524 cp = index (*ap++, '=');
6525 *ap = NULL;
6526 *cp++ = '\0';
6527 *ep = cp;
6528 }
6529 if (ct -> c_type == CT_TEXT && ct -> c_subtype != TEXT_PLAIN)
6530 ct -> c_encoding = linelen == -1 ? CE_7BIT : CE_QUOTED;
6531
6532 (void) fclose (in);
6533
6534 return result;
6535 }
6536 }
6537
6538 return OK;
6539 }
6540
6541 /* \f */
6542
6543 static int output_content (ct, out)
6544 register CT ct;
6545 FILE *out;
6546 {
6547 int cc,
6548 mailbody,
6549 len;
6550 register char **ap,
6551 **ep;
6552 char buffer[BUFSIZ];
6553 register CI ci = &ct -> c_ctinfo;
6554
6555 if (ct -> c_type == CT_MULTIPART) {
6556 register char *cp;
6557 static int encl = 0;
6558
6559 ap = ci -> ci_attrs, ep = ci -> ci_values;
6560
6561 (void) sprintf (buffer, "boundary=%s%d", prefix, encl++);
6562 cp = index (*ap++ = add (buffer, NULLCP), '=');
6563 *ap = NULL;
6564 *cp++ = '\0';
6565 *ep = cp;
6566 }
6567 else
6568 if (ct -> c_type == CT_MESSAGE && ct -> c_rfc934)
6569 goto rfc934_mode;
6570
6571 len = 0;
6572 fprintf (out, "%s: %s/%s", TYPE_FIELD, ci -> ci_type, ci -> ci_subtype);
6573 len += strlen (TYPE_FIELD) + 2 + strlen (ci -> ci_type)
6574 + 1 + strlen (ci -> ci_subtype);
6575 mailbody = ct -> c_type == CT_MESSAGE
6576 && ct -> c_subtype == MESSAGE_EXTERNAL
6577 && ((struct exbody *) ct -> c_ctparams) -> eb_body;
6578 for (ap = ci -> ci_attrs, ep = ci -> ci_values; *ap; ap++, ep++) {
6579 if (mailbody && uleq (*ap, "body"))
6580 continue;
6581
6582 (void) putc (';', out);
6583 len++;
6584
6585 (void) sprintf (buffer, "%s=\"%s\"", *ap, *ep);
6586
6587 if (len + 1 + (cc = strlen (buffer)) >= CPERLIN) {
6588 (void) fputs ("\n\t", out);
6589 len = 8;
6590 }
6591 else {
6592 (void) putc (' ', out);
6593 len++;
6594 }
6595 fputs (buffer, out);
6596 len += cc;
6597 }
6598 if (ci -> ci_comment) {
6599 if (len + 1 + (cc = 2 + strlen (ci -> ci_comment)) >= CPERLIN) {
6600 (void) fputs ("\n\t", out);
6601 len = 8;
6602 }
6603 else {
6604 (void) putc (' ', out);
6605 len++;
6606 }
6607 fprintf (out, "(%s)", ci -> ci_comment);
6608 len += cc;
6609 }
6610 (void) putc ('\n', out);
6611 if (ct -> c_id)
6612 fprintf (out, "%s: %s", ID_FIELD, ct -> c_id);
6613 if (ct -> c_descr)
6614 fprintf (out, "%s: %s", DESCR_FIELD, ct -> c_descr);
6615
6616 rfc934_mode: ;
6617 if (ct -> c_ctextern)
6618 return OK;
6619 switch (ct -> c_type) {
6620 case CT_MULTIPART:
6621 {
6622 struct multipart *m = (struct multipart *) ct -> c_ctparams;
6623 register struct part *part;
6624
6625 if (ct -> c_rfc934)
6626 (void) putc ('\n', out);
6627
6628 for (part = m -> mp_parts; part; part = part -> mp_next) {
6629 register CT p = part -> mp_part;
6630
6631 fprintf (out, "\n--%s\n", ci -> ci_values[0]);
6632 (void) output_content (p, out);
6633 }
6634
6635 fprintf (out, "\n--%s--\n", ci -> ci_values[0]);
6636 }
6637 break;
6638
6639 case CT_TEXT:
6640 if (ct -> c_ctparams
6641 && ((struct text *) ct -> c_ctparams) -> tx_charset
6642 != CHARSET_USASCII) {
6643 quoted_printable: ;
6644 if (checksw)
6645 writeDigest (ct, out, 1);
6646 fprintf (out, "%s: %s\n\n", ENCODING_FIELD,
6647 "quoted-printable");
6648 (void) writeQuoted (ct, out);
6649 break;
6650 }
6651 if ((ct -> c_subtype != TEXT_PLAIN && ct -> c_encoding != CE_7BIT)
6652 || checksw)
6653 goto quoted_printable;
6654 /* else fall... */
6655
6656 case CT_MESSAGE:
6657 seven_bit: ;
6658 (void) putc ('\n', out);
6659 if (ct -> c_type == CT_MESSAGE
6660 && ct -> c_subtype == MESSAGE_EXTERNAL) {
6661 register struct exbody *e = (struct exbody *) ct -> c_ctparams;
6662
6663 (void) output_content (e -> eb_content, out);
6664 if (e -> eb_body) {
6665 register char *cp;
6666
6667 putc ('\n', out);
6668 for (cp = e -> eb_body; *cp; cp++) {
6669 CT ct2 = e -> eb_content;
6670 CI ci2 = &ct2 -> c_ctinfo;
6671
6672 if (*cp == '\\')
6673 switch (*++cp) {
6674 case 'I':
6675 if (ct2 -> c_id) {
6676 char *dp = trimcpy (ct2 -> c_id);
6677
6678 (void) fputs (dp, out);
6679 free (dp);
6680 }
6681 continue;
6682
6683 case 'N':
6684 for (ap = ci2 -> ci_attrs,
6685 ep = ci2 -> ci_values;
6686 *ap;
6687 ap++, ep++)
6688 if (uleq (*ap, "name")) {
6689 fprintf (out, "%s", *ep);
6690 break;
6691 }
6692 continue;
6693
6694 case 'T':
6695 fprintf (out, "%s/%s", ci2 -> ci_type,
6696 ci2 -> ci_subtype);
6697 for (ap = ci2 -> ci_attrs,
6698 ep = ci2 -> ci_values;
6699 *ap;
6700 ap++, ep++)
6701 fprintf (out, "; %s=\"%s\"", *ap, *ep);
6702 continue;
6703
6704 case 'n':
6705 (void) putc ('\n', out);
6706 continue;
6707
6708 case 't':
6709 (void) putc ('\t', out);
6710 continue;
6711
6712 case '\0':
6713 cp--;
6714 break;
6715
6716 case '\\':
6717 case '"':
6718 break;
6719
6720 default:
6721 (void) putc ('\\', out);
6722 break;
6723 }
6724
6725 (void) putc (*cp, out);
6726 }
6727 putc ('\n', out);
6728 }
6729 }
6730 else
6731 (void) write7Bit (ct, out);
6732 break;
6733
6734 case CT_APPLICATION:
6735 switch (ct -> c_encoding) {
6736 case CE_7BIT:
6737 goto seven_bit;
6738
6739 case CE_QUOTED:
6740 goto quoted_printable;
6741
6742 default:
6743 break;
6744 }
6745 /* else fall... */
6746
6747 default:
6748 if (checksw)
6749 writeDigest (ct, out, 0);
6750 fprintf (out, "%s: %s\n\n", ENCODING_FIELD, "base64");
6751 (void) writeBase64 (ct, out);
6752 break;
6753 }
6754
6755 return OK;
6756 }
6757
6758 /* \f */
6759
6760 static int write7Bit (ct, out)
6761 register CT ct;
6762 FILE *out;
6763 {
6764 char c,
6765 buffer[BUFSIZ];
6766 FILE *in;
6767
6768 if ((in = fopen (ct -> c_file, "r")) == NULL)
6769 adios (ct -> c_file, "unable to open for reading");
6770
6771 c = '\n';
6772 while (fgets (buffer, sizeof buffer - 1, in)) {
6773 c = buffer[strlen (buffer) - 1];
6774 (void) fputs (buffer, out);
6775 }
6776 if (c != '\n')
6777 (void) putc ('\n', out);
6778
6779 (void) fclose (in);
6780
6781 return OK;
6782 }
6783
6784 /* \f */
6785
6786 static int writeQuoted (ct, out)
6787 register CT ct;
6788 FILE *out;
6789 {
6790 register char *cp;
6791 char c,
6792 buffer[BUFSIZ];
6793 FILE *in;
6794
6795 if ((in = fopen (ct -> c_file, "r")) == NULL)
6796 adios (ct -> c_file, "unable to open for reading");
6797
6798 while (fgets (buffer, sizeof buffer - 1, in)) {
6799 register int n;
6800
6801 cp = buffer + strlen (buffer) - 1;
6802 if ((c = *cp) == '\n')
6803 *cp = '\0';
6804
6805 if ((*(cp = buffer) == '.')
6806 || (strncmp (cp, "From ", sizeof "From " - 1) == 0)) {
6807 (void) fprintf (out, "=%02X", *cp++ & 0xff);
6808 n = 3;
6809 }
6810 else
6811 n = 0;
6812 for (; *cp; cp++) {
6813 if (n > CPERLIN - 3) {
6814 (void) fputs ("=\n", out);
6815 n = 0;
6816 }
6817
6818 switch (*cp) {
6819 case ' ':
6820 case '\t':
6821 (void) putc (*cp, out);
6822 n++;
6823 break;
6824
6825 default:
6826 if (*cp < '!'
6827 || *cp > '~'
6828 || (ebcdicsw && !ebcdicsafe[*cp & 0xff]))
6829 goto three_print;
6830 (void) putc (*cp, out);
6831 n++;
6832 break;
6833
6834 case '=':
6835 three_print: ;
6836 (void) fprintf (out, "=%02X", *cp & 0xff);
6837 n += 3;
6838 break;
6839 }
6840 }
6841
6842 if (c == '\n') {
6843 if (cp > buffer && (*--cp == ' ' || *cp == '\t'))
6844 (void) fputs ("=\n", out);
6845
6846 (void) putc ('\n', out);
6847 }
6848 else
6849 (void) fputs ("=\n", out);
6850 }
6851
6852 (void) fclose (in);
6853
6854 return OK;
6855 }
6856
6857 /* \f */
6858
6859 static char nib2b64[0x40+1] =
6860 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6861
6862
6863 static int writeBase64 (ct, out)
6864 register CT ct;
6865 FILE *out;
6866 {
6867 int result;
6868 FILE *in;
6869
6870 if ((in = fopen (ct -> c_file, "r")) == NULL)
6871 adios (ct -> c_file, "unable to open for reading");
6872
6873 result = writeBase64aux (in, out);
6874
6875 (void) fclose (in);
6876
6877 return result;
6878 }
6879
6880
6881 static int writeBase64aux (in, out)
6882 FILE *in,
6883 *out;
6884 {
6885 int cc,
6886 n;
6887 char inbuf[3];
6888
6889 n = BPERLIN;
6890 while ((cc = fread (inbuf, sizeof *inbuf, sizeof inbuf, in)) > 0) {
6891 unsigned long bits;
6892 register char *bp;
6893 char outbuf[4];
6894
6895 if (cc < sizeof inbuf) {
6896 inbuf[2] = 0;
6897 if (cc < sizeof inbuf - 1)
6898 inbuf[1] = 0;
6899 }
6900 bits = (inbuf[0] & 0xff) << 16;
6901 bits |= (inbuf[1] & 0xff) << 8;
6902 bits |= inbuf[2] & 0xff;
6903
6904 for (bp = outbuf + sizeof outbuf; bp > outbuf; bits >>= 6)
6905 *--bp = nib2b64[bits & 0x3f];
6906 if (cc < sizeof inbuf) {
6907 outbuf[3] = '=';
6908 if (cc < sizeof inbuf - 1)
6909 outbuf[2] = '=';
6910 }
6911
6912 (void) fwrite (outbuf, sizeof *outbuf, sizeof outbuf, out);
6913
6914 if (cc < sizeof inbuf) {
6915 (void) putc ('\n', out);
6916 return OK;
6917 }
6918
6919 if (--n <= 0) {
6920 n = BPERLIN;
6921 (void) putc ('\n', out);
6922 }
6923 }
6924 if (n != BPERLIN)
6925 (void) putc ('\n', out);
6926
6927 return OK;
6928 }
6929
6930 /* \f */
6931
6932 static int writeDigest (ct, out, asciiP)
6933 register CT ct;
6934 FILE *out;
6935 int asciiP;
6936 {
6937 int cc;
6938 char buffer[BUFSIZ];
6939 register unsigned char *dp;
6940 unsigned char digest[16];
6941 FILE *in;
6942 MD5_CTX mdContext;
6943
6944 if ((in = fopen (ct -> c_file, "r")) == NULL)
6945 adios (ct -> c_file, "unable to open for reading");
6946
6947 MD5Init (&mdContext);
6948 if (asciiP) {
6949 while (fgets (buffer, sizeof buffer - 1, in)) {
6950 register char *cp;
6951 char c;
6952
6953 cp = buffer + strlen (buffer) - 1;
6954 if ((c = *cp) == '\n')
6955 *cp = '\0';
6956
6957 MD5Update (&mdContext, (unsigned char *) buffer,
6958 (unsigned int) strlen (buffer));
6959
6960 if (c == '\n')
6961 MD5Update (&mdContext, (unsigned char *) "\r\n", 2);
6962 }
6963 }
6964 else
6965 while ((cc = fread (buffer, sizeof *buffer, sizeof buffer, in)) > 0)
6966 MD5Update (&mdContext, (unsigned char *) buffer,
6967 (unsigned int) cc);
6968 MD5Final (digest, &mdContext);
6969 if (debugsw) {
6970 unsigned char *ep;
6971
6972 fprintf (stderr, "MD5 digest=");
6973 for (ep = (dp = digest) + sizeof digest / sizeof digest[0];
6974 dp < ep;
6975 dp++)
6976 fprintf (stderr, "%02x", *dp & 0xff);
6977 fprintf (stderr, "\n");
6978 }
6979
6980 (void) fclose (in);
6981
6982 fprintf (out, "%s: ", MD5_FIELD);
6983 for (dp = digest, cc = sizeof digest / sizeof digest[0]; cc > 0; cc -= 3) {
6984 unsigned long bits;
6985 register char *bp;
6986 char outbuf[4];
6987
6988 bits = (*dp++ & 0xff) << 16;
6989 if (cc > 1) {
6990 bits |= (*dp++ & 0xff) << 8;
6991 if (cc > 2)
6992 bits |= *dp++ & 0xff;
6993 }
6994
6995 for (bp = outbuf + sizeof outbuf; bp > outbuf; bits >>= 6)
6996 *--bp = nib2b64[bits & 0x3f];
6997 if (cc < 3) {
6998 outbuf[3] = '=';
6999 if (cc < 2)
7000 outbuf[2] = '=';
7001 }
7002
7003 (void) fwrite (outbuf, sizeof *outbuf, sizeof outbuf, out);
7004 }
7005 fprintf (out, "\n");
7006 }
7007
7008 /* \f */
7009
7010 static int readDigest (ct, cp)
7011 register CT ct;
7012 register char *cp;
7013 {
7014 int bitno,
7015 skip;
7016 unsigned long bits;
7017 char *bp = cp;
7018 register unsigned char *dp;
7019 unsigned char value,
7020 *ep,
7021 *b = (unsigned char *) &bits,
7022 *b1 = &b[endian > 0 ? 1 : 2],
7023 *b2 = &b[endian > 0 ? 2 : 1],
7024 *b3 = &b[endian > 0 ? 3 : 0];
7025
7026 bitno = 18, bits = 0L, skip = 0;
7027 for (ep = (dp = ct -> c_digest)
7028 + sizeof ct -> c_digest / sizeof ct -> c_digest[0];
7029 *cp;
7030 cp++)
7031 switch (*cp) {
7032 default:
7033 if (skip
7034 || (*cp & 0x80)
7035 || (value = b642nib[*cp & 0x7f]) > 0x3f) {
7036 if (debugsw)
7037 fprintf (stderr, "invalid BASE64 encoding\n");
7038 return NOTOK;
7039 }
7040
7041 bits |= value << bitno;
7042 test_end: ;
7043 if ((bitno -= 6) < 0) {
7044 if (dp + (3 - skip) > ep)
7045 goto invalid_digest;
7046 *dp++ = *b1;
7047 if (skip < 2) {
7048 *dp++ = *b2;
7049 if (skip < 1)
7050 *dp++ = *b3;
7051 }
7052 bitno = 18, bits = 0L, skip = 0;
7053 }
7054 break;
7055
7056 case '=':
7057 if (++skip > 3)
7058 goto self_delimiting;
7059 goto test_end;
7060 }
7061 if (bitno != 18) {
7062 if (debugsw)
7063 fprintf (stderr, "premature ending (bitno %d)\n", bitno);
7064
7065 return NOTOK;
7066 }
7067 self_delimiting: ;
7068 if (dp != ep) {
7069 invalid_digest: ;
7070 if (debugsw) {
7071 while (*cp)
7072 cp++;
7073 fprintf (stderr, "invalid MD5 digest (got %d octets)\n",
7074 cp - bp);
7075 }
7076
7077 return NOTOK;
7078 }
7079
7080 if (debugsw) {
7081 fprintf (stderr, "MD5 digest=");
7082 for (dp = ct -> c_digest; dp < ep; dp++)
7083 fprintf (stderr, "%02x", *dp & 0xff);
7084 fprintf (stderr, "\n");
7085 }
7086
7087 return OK;
7088 }
7089
7090 /* \f VIAMAIL */
7091
7092 #include "../zotnet/tws.h"
7093
7094
7095 static int via_mail (mailsw, subjsw, parmsw, descsw, cmntsw, slowsw, fromsw)
7096 char *mailsw,
7097 *subjsw,
7098 *parmsw,
7099 *descsw,
7100 *cmntsw,
7101 *fromsw;
7102 int slowsw;
7103 {
7104 int nlines,
7105 nparts,
7106 status;
7107 long pos;
7108 long offset;
7109 char tmpfil[BUFSIZ];
7110 struct stat st;
7111 FILE *fp;
7112
7113 (void) umask (~m_gmprot ());
7114
7115 (void) strcpy (tmpfil, m_tmpfil (invo_name));
7116 if ((fp = fopen (tmpfil, "w+")) == NULL)
7117 adios (tmpfil, "unable to open for writing");
7118 (void) chmod (tmpfil, 0600);
7119
7120 if (!index (mailsw, '@'))
7121 mailsw = concat (mailsw, "@", LocalName (), NULLCP);
7122 fprintf (fp, "To: %s\n", mailsw);
7123 nlines = 1;
7124 if (subjsw)
7125 fprintf (fp, "Subject: %s\n", subjsw), nlines++;
7126 if (fromsw) {
7127 if (!index (fromsw, '@'))
7128 fromsw = concat (fromsw, "@", LocalName (), NULLCP);
7129 fprintf (fp, "From: %s\n", fromsw), nlines++;
7130 }
7131 fprintf (fp, "%s: %s\n", VRSN_FIELD, VRSN_VALUE), nlines++;
7132 offset = ftell (fp);
7133 fprintf (fp, "%s: application/octet-stream", TYPE_FIELD);
7134 if (parmsw)
7135 fprintf (fp, "; %s", parmsw);
7136 if (cmntsw)
7137 fprintf (fp, "\n\t(%s)", cmntsw), nlines++;
7138 if (descsw)
7139 fprintf (fp, "\n%s: %s", DESCR_FIELD, descsw), nlines++;
7140 fprintf (fp, "\n%s: %s\n\n", ENCODING_FIELD, "base64"), nlines += 2;
7141 if (fflush (fp))
7142 adios (tmpfil, "error writing to");
7143
7144 pos = ftell (fp);
7145 (void) writeBase64aux (stdin, fp);
7146 if (fflush (fp))
7147 adios (tmpfil, "error writing to");
7148
7149 if (fstat (fileno (fp), &st) == NOTOK)
7150 adios ("failed", "fstat of %s", tmpfil);
7151 nlines += (((long) st.st_size - pos) + CPERLIN) / (CPERLIN + 1);
7152 nparts = (nlines + (LPERMSG - 1)) / LPERMSG;
7153
7154 if (nparts <= 1)
7155 status = via_post (tmpfil, 0);
7156 else {
7157 int partno;
7158 long clock;
7159 char buffer[BUFSIZ],
7160 msgid[BUFSIZ];
7161
7162 if (verbosw) {
7163 printf ("sending binary image as %d partial messages\n", nparts);
7164 (void) fflush (stdout);
7165 }
7166
7167 (void) time (&clock);
7168 (void) sprintf (msgid, "<%d.%ld@%s>", getpid (), clock, LocalName ());
7169
7170 (void) fseek (fp, offset, 0);
7171 for (partno = 1; partno <= nparts; partno++) {
7172 int lineno;
7173 char tmpdrf[BUFSIZ];
7174 FILE *out;
7175
7176 (void) strcpy (tmpdrf, m_tmpfil (invo_name));
7177 if ((out = fopen (tmpdrf, "w")) == NULL)
7178 adios (tmpdrf, "unable to open for writing");
7179 (void) chmod (tmpdrf, 0600);
7180
7181 fprintf (out, "To: %s\n", mailsw);
7182 if (subjsw)
7183 fprintf (out, "Subject: %s\n", subjsw);
7184 fprintf (out, "%s: %s\n", VRSN_FIELD, VRSN_VALUE);
7185 fprintf (out,
7186 "%s: message/partial; id=\"%s\";\n\tnumber=%d; total=%d\n",
7187 TYPE_FIELD, msgid, partno, nparts);
7188 fprintf (out, "%s: part %d of %d\n\n", DESCR_FIELD, partno,
7189 nparts);
7190
7191 if (partno == 1)
7192 fprintf (out, "Message-ID: %s\n", msgid);
7193
7194 for (lineno = LPERMSG; lineno > 0; lineno--) {
7195 if (!fgets (buffer, sizeof buffer, fp)) {
7196 if (partno == nparts)
7197 break;
7198 adios (NULLCP, "premature eof");
7199 }
7200
7201 (void) fputs (buffer, out);
7202 }
7203 offset = ftell (fp);
7204
7205 if (fflush (out))
7206 adios (tmpdrf, "error writing to");
7207
7208 (void) fclose (out);
7209
7210 status = via_post (tmpdrf, slowsw == 0);
7211 (void) unlink (tmpdrf);
7212 if (status)
7213 break;
7214
7215 if (slowsw > 0 && partno < nparts) {
7216 if (verbosw) {
7217 printf ("pausing %d seconds before sending part %d...\n",
7218 slowsw, partno + 1);
7219 (void) fflush (stdout);
7220 }
7221
7222 sleep ((unsigned) slowsw);
7223 }
7224 }
7225 }
7226
7227 (void) fclose (fp);
7228 (void) unlink (tmpfil);
7229
7230 done (status ? 1 : 0);
7231 }
7232
7233 /* \f */
7234
7235 static int via_post (file, queued)
7236 char *file;
7237 int queued;
7238 {
7239 int child_id,
7240 i;
7241
7242 for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
7243 sleep (5);
7244 switch (child_id) {
7245 case NOTOK:
7246 adios ("fork", "unable to");
7247 /* NOTREACHED */
7248
7249 case OK:
7250 (void) execlp (postproc, r1bindex (postproc, '/'), file,
7251 queued ? "-queued" : NULLCP, NULLCP);
7252 fprintf (stderr, "unable to exec ");
7253 perror (postproc);
7254 _exit (-1);
7255 /* NOTREACHED */
7256
7257 default:
7258 return pidXwait (child_id, postproc);
7259 }
7260 }
7261
7262 /* \f */
7263
7264 void done (status)
7265 int status;
7266 {
7267 register CT *ctp;
7268
7269 if (ctp = cts)
7270 for (; *ctp; ctp++)
7271 free_content (*ctp);
7272 if (free_ct)
7273 free_content (free_ct);
7274 if (free_file)
7275 (void) unlink (free_file);
7276
7277 exit (status);
7278 }
7279
7280
7281 static int pidcheck (status)
7282 int status;
7283 {
7284 if ((status & 0xff00) == 0xff00 || (status & 0x007f) != SIGQUIT)
7285 return status;
7286
7287 (void) unlink ("core");
7288
7289 (void) fflush (stdout);
7290
7291 (void) fflush (stderr);
7292
7293 done (1);
7294 /* NOTREACHED */
7295 }