Added all of the MH sources, including RCS files, in
[mmh] / 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 }