c6d5c777ae98242e5911859809f98d90b436a0fc
[mmh] / uip / mhn.c
1
2 /*
3  * mhn.c -- display, list, cache, or store the contents of MIME messages
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <fcntl.h>
14 #include <h/signals.h>
15 #include <h/md5.h>
16 #include <errno.h>
17 #include <signal.h>
18 #include <h/mts.h>
19 #include <h/tws.h>
20 #include <h/mime.h>
21 #include <h/mhparse.h>
22 #include <h/mhcachesbr.h>
23
24 #ifdef HAVE_SYS_WAIT_H
25 # include <sys/wait.h>
26 #endif
27
28 /*
29  * We allocate space for message names (msgs array)
30  * this number of elements at a time.
31  */
32 #define MAXMSGS  256
33
34
35 static struct swit switches[] = {
36 #define AUTOSW                  0
37     { "auto", 0 },
38 #define NAUTOSW                 1
39     { "noauto", 0 },
40 #define CACHESW                 2
41     { "cache", 0 },
42 #define NCACHESW                3
43     { "nocache", 0 },
44 #define CHECKSW                 4
45     { "check", 0 },
46 #define NCHECKSW                5
47     { "nocheck", 0 },
48 #define HEADSW                  6
49     { "headers", 0 },
50 #define NHEADSW                 7
51     { "noheaders", 0 },
52 #define LISTSW                  8
53     { "list", 0 },
54 #define NLISTSW                 9
55     { "nolist", 0 },
56 #define PAUSESW                10
57     { "pause", 0 },
58 #define NPAUSESW               11
59     { "nopause", 0 },
60 #define SIZESW                 12
61     { "realsize", 0 },
62 #define NSIZESW                13
63     { "norealsize", 0 },
64 #define SERIALSW               14
65     { "serialonly", 0 },
66 #define NSERIALSW              15
67     { "noserialonly", 0 },
68 #define SHOWSW                 16
69     { "show", 0 },
70 #define NSHOWSW                17
71     { "noshow", 0 },
72 #define STORESW                18
73     { "store", 0 },
74 #define NSTORESW               19
75     { "nostore", 0 },
76 #define VERBSW                 20
77     { "verbose", 0 },
78 #define NVERBSW                21
79     { "noverbose", 0 },
80 #define FILESW                 22       /* interface from show */
81     { "file file", 0 },
82 #define FORMSW                 23
83     { "form formfile", 0 },
84 #define PARTSW                 24
85     { "part number", 0 },
86 #define TYPESW                 25
87     { "type content", 0 },
88 #define RCACHESW               26
89     { "rcache policy", 0 },
90 #define WCACHESW               27
91     { "wcache policy", 0 },
92 #define VERSIONSW              28
93     { "version", 0 },
94 #define HELPSW                 29
95     { "help", 0 },
96
97 /*
98  * switches for debugging
99  */
100 #define DEBUGSW                30
101     { "debug", -5 },
102
103 /*
104  * switches for moreproc/mhlproc
105  */
106 #define PROGSW                 31
107     { "moreproc program", -4 },
108 #define NPROGSW                32
109     { "nomoreproc", -3 },
110 #define LENSW                  33
111     { "length lines", -4 },
112 #define WIDTHSW                34
113     { "width columns", -4 },
114
115 /*
116  * switches for mhbuild
117  */
118 #define BUILDSW                35
119     { "build", -5 },
120 #define NBUILDSW               36
121     { "nobuild", -7 },
122 #define EBCDICSW               37
123     { "ebcdicsafe", -10 },
124 #define NEBCDICSW              38
125     { "noebcdicsafe", -12 },
126 #define RFC934SW               39
127     { "rfc934mode", -10 },
128 #define NRFC934SW              40
129     { "norfc934mode", -12 },
130     { NULL, 0 }
131 };
132
133
134 extern int errno;
135
136 /* mhparse.c */
137 extern int checksw;
138 extern char *tmp;       /* directory to place temp files */
139
140 /* mhcachesbr.c */
141 extern int rcachesw;
142 extern int wcachesw;
143 extern char *cache_public;
144 extern char *cache_private;
145
146 /* mhshowsbr.c */
147 extern int pausesw;
148 extern int serialsw;
149 extern char *progsw;
150 extern int nolist;
151 extern int nomore;      /* flags for moreproc/header display */
152 extern char *formsw;
153
154 /* mhstoresbr.c */
155 extern int autosw;
156 extern char *cwd;       /* cache current working directory */
157
158 /* mhmisc.c */
159 extern int npart;
160 extern int ntype;
161 extern char *parts[NPARTS + 1];
162 extern char *types[NTYPES + 1];
163 extern int userrs;
164
165 int debugsw = 0;
166 int verbosw = 0;
167
168 /* The list of top-level contents to display */
169 CT *cts = NULL;
170
171 /*
172  * variables for mhbuild (mhn -build)
173  */
174 static int buildsw  = 0;
175 static int ebcdicsw = 0;
176 static int rfc934sw = 0;
177
178 /*
179  * what action to take?
180  */
181 static int cachesw = 0;
182 static int listsw  = 0;
183 static int showsw  = 0;
184 static int storesw = 0;
185
186 #define quitser pipeser
187
188 /* mhparse.c */
189 CT parse_mime (char *);
190
191 /* mhmisc.c */
192 int part_ok (CT, int);
193 int type_ok (CT, int);
194 void set_endian (void);
195 void flush_errors (void);
196
197 /* mhshowsbr.c */
198 void show_all_messages (CT *);
199
200 /* mhlistsbr.c */
201 void list_all_messages (CT *, int, int, int, int);
202
203 /* mhstoresbr.c */
204 void store_all_messages (CT *);
205
206 /* mhcachesbr.c */
207 void cache_all_messages (CT *);
208
209 /* mhfree.c */
210 void free_content (CT);
211
212 /*
213  * static prototypes
214  */
215 static RETSIGTYPE pipeser (int);
216
217
218 int
219 main (int argc, char **argv)
220 {
221     int sizesw = 1, headsw = 1;
222     int nummsgs, maxmsgs, msgnum, *icachesw;
223     char *cp, *file = NULL, *folder = NULL;
224     char *maildir, buf[100], **argp;
225     char **arguments, **msgs;
226     struct msgs *mp = NULL;
227     CT ct, *ctp;
228     FILE *fp;
229
230 #ifdef LOCALE
231     setlocale(LC_ALL, "");
232 #endif
233     invo_name = r1bindex (argv[0], '/');
234
235     /* read user profile/context */
236     context_read();
237
238     arguments = getarguments (invo_name, argc, argv, 1);
239     argp = arguments;
240
241     /*
242      * Allocate the initial space to record message
243      * names, ranges, and sequences.
244      */
245     nummsgs = 0;
246     maxmsgs = MAXMSGS;
247     if (!(msgs = (char **) malloc ((size_t) (maxmsgs * sizeof(*msgs)))))
248         adios (NULL, "unable to allocate storage");
249
250     /*
251      * Parse arguments
252      */
253     while ((cp = *argp++)) {
254         if (*cp == '-') {
255             switch (smatch (++cp, switches)) {
256             case AMBIGSW: 
257                 ambigsw (cp, switches);
258                 done (1);
259             case UNKWNSW: 
260                 adios (NULL, "-%s unknown", cp);
261
262             case HELPSW: 
263                 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
264                         invo_name);
265                 print_help (buf, switches, 1);
266                 done (1);
267             case VERSIONSW:
268                 print_version(invo_name);
269                 done (1);
270
271             case AUTOSW:
272                 autosw++;
273                 continue;
274             case NAUTOSW:
275                 autosw = 0;
276                 continue;
277
278             case CACHESW:
279                 cachesw++;
280                 continue;
281             case NCACHESW:
282                 cachesw = 0;
283                 continue;
284
285             case RCACHESW:
286                 icachesw = &rcachesw;
287                 goto do_cache;
288             case WCACHESW:
289                 icachesw = &wcachesw;
290 do_cache:
291                 if (!(cp = *argp++) || *cp == '-')
292                     adios (NULL, "missing argument to %s", argp[-2]);
293                 switch (*icachesw = smatch (cp, caches)) {
294                 case AMBIGSW:
295                     ambigsw (cp, caches);
296                     done (1);
297                 case UNKWNSW:
298                     adios (NULL, "%s unknown", cp);
299                 default:
300                     break;
301                 }
302                 continue;
303
304             case CHECKSW:
305                 checksw++;
306                 continue;
307             case NCHECKSW:
308                 checksw = 0;
309                 continue;
310
311             case HEADSW:
312                 headsw = 1;
313                 continue;
314             case NHEADSW:
315                 headsw = 0;
316                 continue;
317
318             case LISTSW:
319                 listsw = 1;
320                 continue;
321             case NLISTSW:
322                 listsw = 0;
323                 continue;
324
325             case PAUSESW:
326                 pausesw = 1;
327                 continue;
328             case NPAUSESW:
329                 pausesw = 0;
330                 continue;
331
332             case SERIALSW:
333                 serialsw = 1;
334                 continue;
335             case NSERIALSW:
336                 serialsw = 0;
337                 continue;
338
339             case SHOWSW:
340                 showsw = 1;
341                 continue;
342             case NSHOWSW:
343                 showsw = 0;
344                 continue;
345
346             case SIZESW:
347                 sizesw = 1;
348                 continue;
349             case NSIZESW:
350                 sizesw = 0;
351                 continue;
352
353             case STORESW:
354                 storesw = 1;
355                 continue;
356             case NSTORESW:
357                 storesw = 0;
358                 continue;
359
360             case PARTSW:
361                 if (!(cp = *argp++) || *cp == '-')
362                     adios (NULL, "missing argument to %s", argp[-2]);
363                 if (npart >= NPARTS)
364                     adios (NULL, "too many parts (starting with %s), %d max",
365                            cp, NPARTS);
366                 parts[npart++] = cp;
367                 continue;
368
369             case TYPESW:
370                 if (!(cp = *argp++) || *cp == '-')
371                     adios (NULL, "missing argument to %s", argp[-2]);
372                 if (ntype >= NTYPES)
373                     adios (NULL, "too many types (starting with %s), %d max",
374                            cp, NTYPES);
375                 types[ntype++] = cp;
376                 continue;
377
378             case FILESW:
379                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
380                     adios (NULL, "missing argument to %s", argp[-2]);
381                 file = *cp == '-' ? cp : path (cp, TFILE);
382                 continue;
383
384             case FORMSW:
385                 if (!(cp = *argp++) || *cp == '-')
386                     adios (NULL, "missing argument to %s", argp[-2]);
387                 if (formsw)
388                     free (formsw);
389                 formsw = getcpy (etcpath (cp));
390                 continue;
391
392             /*
393              * Switches for moreproc/mhlproc
394              */
395             case PROGSW:
396                 if (!(progsw = *argp++) || *progsw == '-')
397                     adios (NULL, "missing argument to %s", argp[-2]);
398                 continue;
399             case NPROGSW:
400                 nomore++;
401                 continue;
402
403             case LENSW:
404             case WIDTHSW:
405                 if (!(cp = *argp++) || *cp == '-')
406                     adios (NULL, "missing argument to %s", argp[-2]);
407                 continue;
408
409             /*
410              * Switches for mhbuild
411              */
412             case BUILDSW:
413                 buildsw = 1;
414                 continue;
415             case NBUILDSW:
416                 buildsw = 0;
417                 continue;
418             case RFC934SW:
419                 rfc934sw = 1;
420                 continue;
421             case NRFC934SW:
422                 rfc934sw = -1;
423                 continue;
424             case EBCDICSW:
425                 ebcdicsw = 1;
426                 continue;
427             case NEBCDICSW:
428                 ebcdicsw = -1;
429                 continue;
430
431             case VERBSW: 
432                 verbosw = 1;
433                 continue;
434             case NVERBSW: 
435                 verbosw = 0;
436                 continue;
437             case DEBUGSW:
438                 debugsw = 1;
439                 continue;
440             }
441         }
442         if (*cp == '+' || *cp == '@') {
443             if (folder)
444                 adios (NULL, "only one folder at a time!");
445             else
446                 folder = path (cp + 1, *cp == '+' ? TFOLDER : TSUBCWF);
447         } else {
448             /*
449              * Check if we need to allocate more space
450              * for message names/ranges/sequences.
451              */
452             if (nummsgs >= maxmsgs) {
453                 maxmsgs += MAXMSGS;
454                 if (!(msgs = (char **) realloc (msgs,
455                         (size_t) (maxmsgs * sizeof(*msgs)))))
456                     adios (NULL, "unable to reallocate msgs storage");
457             }
458             msgs[nummsgs++] = cp;
459         }
460     }
461
462     /* null terminate the list of acceptable parts/types */
463     parts[npart] = NULL;
464     types[ntype] = NULL;
465
466     set_endian ();
467
468     if ((cp = getenv ("MM_NOASK")) && !strcmp (cp, "1")) {
469         nolist  = 1;
470         listsw  = 0;
471         pausesw = 0;
472     }
473
474     /*
475      * Check if we've specified an additional profile
476      */
477     if ((cp = getenv ("MHN"))) {
478         if ((fp = fopen (cp, "r"))) {
479             readconfig ((struct node **) 0, fp, cp, 0);
480             fclose (fp);
481         } else {
482             admonish ("", "unable to read $MHN profile (%s)", cp);
483         }
484     }
485
486     /*
487      * Read the standard profile setup
488      */
489     if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
490         readconfig ((struct node **) 0, fp, cp, 0);
491         fclose (fp);
492     }
493
494     /* Check for public cache location */
495     if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
496         cache_public = NULL;
497
498     /* Check for private cache location */
499     if (!(cache_private = context_find (nmhprivcache)))
500         cache_private = ".cache";
501     cache_private = getcpy (m_maildir (cache_private));
502
503     /*
504      * Cache the current directory before we do any chdirs()'s.
505      */
506     cwd = getcpy (pwd());
507
508     /*
509      * Check for storage directory.  If specified,
510      * then store temporary files there.  Else we
511      * store them in standard nmh directory.
512      */
513     if ((cp = context_find (nmhstorage)) && *cp)
514         tmp = concat (cp, "/", invo_name, NULL);
515     else
516         tmp = add (m_maildir (invo_name), NULL);
517
518     if (!context_find ("path"))
519         free (path ("./", TFOLDER));
520
521     /*
522      * Process a mhn composition file (mhn -build)
523      */
524     if (buildsw) {
525         char *vec[MAXARGS];
526         int vecp;
527
528         if (showsw || storesw || cachesw)
529             adios (NULL, "cannot use -build with -show, -store, -cache");
530         if (nummsgs < 1)
531             adios (NULL, "need to specify a %s composition file", invo_name);
532         if (nummsgs > 1)
533             adios (NULL, "only one %s composition file at a time", invo_name);
534
535         vecp = 0;
536         vec[vecp++] = "mhbuild";
537
538         if (ebcdicsw == 1)
539             vec[vecp++] = "-ebcdicsafe";
540         else if (ebcdicsw == -1)
541             vec[vecp++] = "-noebcdicsafe";
542
543         if (rfc934sw == 1)
544             vec[vecp++] = "-rfc934mode";
545         else if (rfc934sw == -1)
546             vec[vecp++] = "-norfc934mode";
547
548         vec[vecp++] = msgs[0];
549         vec[vecp] = NULL;
550
551         execvp ("mhbuild", vec);
552         fprintf (stderr, "unable to exec ");
553         _exit (-1);
554     }
555
556     /*
557      * Process a mhn composition file (old MH style)
558      */
559     if (nummsgs == 1 && !folder && !npart && !cachesw
560         && !showsw && !storesw && !ntype && !file
561         && (cp = getenv ("mhdraft"))
562         && strcmp (cp, msgs[0]) == 0) {
563
564         char *vec[MAXARGS];
565         int vecp;
566
567         vecp = 0;
568         vec[vecp++] = "mhbuild";
569
570         if (ebcdicsw == 1)
571             vec[vecp++] = "-ebcdicsafe";
572         else if (ebcdicsw == -1)
573             vec[vecp++] = "-noebcdicsafe";
574
575         if (rfc934sw == 1)
576             vec[vecp++] = "-rfc934mode";
577         else if (rfc934sw == -1)
578             vec[vecp++] = "-norfc934mode";
579
580         vec[vecp++] = cp;
581         vec[vecp] = NULL;
582
583         execvp ("mhbuild", vec);
584         fprintf (stderr, "unable to exec ");
585         _exit (-1);
586     }
587
588     if (file && nummsgs)
589         adios (NULL, "cannot specify msg and file at same time!");
590
591     /*
592      * check if message is coming from file
593      */
594     if (file) {
595         if (!(cts = (CT *) calloc ((size_t) 2, sizeof(*cts))))
596             adios (NULL, "out of memory");
597         ctp = cts;
598
599         if ((ct = parse_mime (file)));
600             *ctp++ = ct;
601     } else {
602         /*
603          * message(s) are coming from a folder
604          */
605         if (!nummsgs)
606             msgs[nummsgs++] = "cur";
607         if (!folder)
608             folder = getfolder (1);
609         maildir = m_maildir (folder);
610
611         if (chdir (maildir) == NOTOK)
612             adios (maildir, "unable to change directory to");
613
614         /* read folder and create message structure */
615         if (!(mp = folder_read (folder)))
616             adios (NULL, "unable to read folder %s", folder);
617
618         /* check for empty folder */
619         if (mp->nummsg == 0)
620             adios (NULL, "no messages in %s", folder);
621
622         /* parse all the message ranges/sequences and set SELECTED */
623         for (msgnum = 0; msgnum < nummsgs; msgnum++)
624             if (!m_convert (mp, msgs[msgnum]))
625                 done (1);
626         seq_setprev (mp);       /* set the previous-sequence */
627
628         if (!(cts = (CT *) calloc ((size_t) (mp->numsel + 1), sizeof(*cts))))
629             adios (NULL, "out of memory");
630         ctp = cts;
631
632         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
633             if (is_selected(mp, msgnum)) {
634                 char *msgnam;
635
636                 msgnam = m_name (msgnum);
637                 if ((ct = parse_mime (msgnam)))
638                     *ctp++ = ct;
639             }
640         }
641     }
642
643     if (!*cts)
644         done (1);
645
646     /*
647      * You can't give more than one of these flags
648      * at a time.
649      */
650     if (showsw + listsw + storesw + cachesw > 1)
651         adios (NULL, "can only use one of -show, -list, -store, -cache at same time");
652
653     /* If no action is specified, assume -show */
654     if (!listsw && !showsw && !storesw && !cachesw)
655         showsw = 1;
656
657     userrs = 1;
658     SIGNAL (SIGQUIT, quitser);
659     SIGNAL (SIGPIPE, pipeser);
660
661     /*
662      * Get the associated umask for the relevant contents.
663      */
664     for (ctp = cts; *ctp; ctp++) {
665         struct stat st;
666
667         ct = *ctp;
668         if (type_ok (ct, 1) && !ct->c_umask) {
669             if (stat (ct->c_file, &st) != NOTOK)
670                 ct->c_umask = ~(st.st_mode & 0777);
671             else
672                 ct->c_umask = ~m_gmprot();
673         }
674     }
675
676     /*
677      * List the message content
678      */
679     if (listsw)
680         list_all_messages (cts, headsw, sizesw, verbosw, debugsw);
681
682     /*
683      * Store the message content
684      */
685     if (storesw)
686         store_all_messages (cts);
687
688     /*
689      * Cache the message content
690      */
691     if (cachesw)
692         cache_all_messages (cts);
693
694     /*
695      * Show the message content
696      */
697     if (showsw)
698         show_all_messages (cts);
699
700     /* Now free all the structures for the content */
701     for (ctp = cts; *ctp; ctp++)
702         free_content (*ctp);
703
704     free ((char *) cts);
705     cts = NULL;
706
707     /* If reading from a folder, do some updating */
708     if (mp) {
709         context_replace (pfolder, folder);/* update current folder  */
710         seq_setcur (mp, mp->hghsel);      /* update current message */
711         seq_save (mp);                    /* synchronize sequences  */
712         context_save ();                  /* save the context file  */
713     }
714
715     return done (0);
716 }
717
718
719 static RETSIGTYPE
720 pipeser (int i)
721 {
722     if (i == SIGQUIT) {
723         unlink ("core");
724         fflush (stdout);
725         fprintf (stderr, "\n");
726         fflush (stderr);
727     }
728
729     done (1);
730     /* NOTREACHED */
731 }
732
733
734 int
735 done (int status)
736 {
737     CT *ctp;
738
739     if ((ctp = cts))
740         for (; *ctp; ctp++)
741             free_content (*ctp);
742
743     exit (status);
744     return 1;  /* dead code to satisfy the compiler */
745 }