moreproc is now Pager/defaultpager; removed -moreproc flags; added env vars.
[mmh] / uip / mhshow.c
1 /*
2 ** mhshow.c -- display the contents of MIME messages
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/signals.h>
12 #include <h/md5.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <h/tws.h>
16 #include <h/mime.h>
17 #include <h/mhparse.h>
18 #include <h/mhcachesbr.h>
19 #include <h/utils.h>
20
21 #ifdef HAVE_SYS_WAIT_H
22 # include <sys/wait.h>
23 #endif
24
25 static struct swit switches[] = {
26 #define CHECKSW  0
27         { "check", 0 },
28 #define NCHECKSW  1
29         { "nocheck", 0 },
30 #define SERIALSW  2
31         { "serialonly", 0 },
32 #define NSERIALSW  3
33         { "noserialonly", 0 },
34 #define VERBSW  4
35         { "verbose", 0 },
36 #define NVERBSW  5
37         { "noverbose", 0 },
38 #define FILESW  6  /* interface from show */
39         { "file file", 0 },
40 #define FORMSW  7
41         { "form formfile", 0 },
42 #define PARTSW  8
43         { "part number", 0 },
44 #define TYPESW  9
45         { "type content", 0 },
46 #define RCACHESW  10
47         { "rcache policy", 0 },
48 #define WCACHESW  11
49         { "wcache policy", 0 },
50 #define VERSIONSW  12
51         { "version", 0 },
52 #define HELPSW  13
53         { "help", 0 },
54 /*
55 ** switches for debugging
56 */
57 #define DEBUGSW  14
58         { "debug", -5 },
59         { NULL, 0 }
60 };
61
62
63 /* mhparse.c */
64 extern char *tmp;  /* directory to place temp files */
65
66 /* mhcachesbr.c */
67 extern int rcachesw;
68 extern int wcachesw;
69 extern char *cache_public;
70 extern char *cache_private;
71
72 /* mhshowsbr.c */
73 extern int serialsw;
74 extern int nolist;
75 extern char *formsw;
76
77 /* mhmisc.c */
78 extern int npart;
79 extern int ntype;
80 extern char *parts[NPARTS + 1];
81 extern char *types[NTYPES + 1];
82 extern int userrs;
83
84 int debugsw = 0;
85 int verbosw = 0;
86
87 #define quitser pipeser
88
89 /* mhparse.c */
90 CT parse_mime(char *);
91
92 /* mhmisc.c */
93 int part_ok(CT, int);
94 int type_ok(CT, int);
95 void set_endian(void);
96 void flush_errors(void);
97
98 /* mhshowsbr.c */
99 void show_all_messages(CT *);
100
101 /* mhfree.c */
102 void free_content(CT);
103 extern CT *cts;
104 void freects_done(int) NORETURN;
105
106 /*
107 ** static prototypes
108 */
109 static RETSIGTYPE pipeser(int);
110
111
112 int
113 main(int argc, char **argv)
114 {
115         int msgnum, *icachesw;
116         char *cp, *file = NULL, *folder = NULL;
117         char *maildir, buf[100], **argp;
118         char **arguments;
119         struct msgs_array msgs = { 0, 0, NULL };
120         struct msgs *mp = NULL;
121         CT ct, *ctp;
122         FILE *fp;
123
124         done=freects_done;
125
126 #ifdef LOCALE
127         setlocale(LC_ALL, "");
128 #endif
129         invo_name = mhbasename(argv[0]);
130
131         /* read user profile/context */
132         context_read();
133
134         arguments = getarguments(invo_name, argc, argv, 1);
135         argp = arguments;
136
137         /*
138         ** Parse arguments
139         */
140         while ((cp = *argp++)) {
141                 if (*cp == '-') {
142                         switch (smatch(++cp, switches)) {
143                         case AMBIGSW:
144                                 ambigsw(cp, switches);
145                                 done(1);
146                         case UNKWNSW:
147                                 adios(NULL, "-%s unknown", cp);
148
149                         case HELPSW:
150                                 snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name);
151                                 print_help(buf, switches, 1);
152                                 done(1);
153                         case VERSIONSW:
154                                 print_version(invo_name);
155                                 done(1);
156
157                         case RCACHESW:
158                                 icachesw = &rcachesw;
159                                 goto do_cache;
160                         case WCACHESW:
161                                 icachesw = &wcachesw;
162 do_cache:
163                                 if (!(cp = *argp++) || *cp == '-')
164                                         adios(NULL, "missing argument to %s",
165                                                         argp[-2]);
166                                 switch (*icachesw = smatch(cp, caches)) {
167                                 case AMBIGSW:
168                                         ambigsw(cp, caches);
169                                         done(1);
170                                 case UNKWNSW:
171                                         adios(NULL, "%s unknown", cp);
172                                 default:
173                                         break;
174                                 }
175                                 continue;
176
177                         case CHECKSW:
178                                 checksw++;
179                                 continue;
180                         case NCHECKSW:
181                                 checksw = 0;
182                                 continue;
183
184                         case SERIALSW:
185                                 serialsw = 1;
186                                 continue;
187                         case NSERIALSW:
188                                 serialsw = 0;
189                                 continue;
190
191                         case PARTSW:
192                                 if (!(cp = *argp++) || *cp == '-')
193                                         adios(NULL, "missing argument to %s",
194                                                         argp[-2]);
195                                 if (npart >= NPARTS)
196                                         adios(NULL, "too many parts (starting with %s), %d max", cp, NPARTS);
197                                 parts[npart++] = cp;
198                                 continue;
199
200                         case TYPESW:
201                                 if (!(cp = *argp++) || *cp == '-')
202                                         adios(NULL, "missing argument to %s",
203                                                         argp[-2]);
204                                 if (ntype >= NTYPES)
205                                         adios(NULL, "too many types (starting with %s), %d max", cp, NTYPES);
206                                 types[ntype++] = cp;
207                                 continue;
208
209                         case FILESW:
210                                 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
211                                         adios(NULL, "missing argument to %s",
212                                                         argp[-2]);
213                                 file = *cp == '-' ? cp : getcpy(expanddir(cp));
214                                 continue;
215
216                         case FORMSW:
217                                 if (!(cp = *argp++) || *cp == '-')
218                                         adios(NULL, "missing argument to %s",
219                                                         argp[-2]);
220                                 if (formsw)
221                                         free(formsw);
222                                 formsw = getcpy(etcpath(cp));
223                                 continue;
224
225                         case VERBSW:
226                                 verbosw = 1;
227                                 continue;
228                         case NVERBSW:
229                                 verbosw = 0;
230                                 continue;
231                         case DEBUGSW:
232                                 debugsw = 1;
233                                 continue;
234                         }
235                 }
236                 if (*cp == '+' || *cp == '@') {
237                         if (folder)
238                                 adios(NULL, "only one folder at a time!");
239                         else
240                                 folder = getcpy(expandfol(cp));
241                 } else
242                         app_msgarg(&msgs, cp);
243         }
244
245         /* null terminate the list of acceptable parts/types */
246         parts[npart] = NULL;
247         types[ntype] = NULL;
248
249         set_endian();
250
251         if ((cp = getenv("MM_NOASK")) && strcmp(cp, "1")==0) {
252                 nolist  = 1;
253         }
254
255         /*
256         ** Check if we've specified an additional profile
257         */
258         if ((cp = getenv("MHSHOW"))) {
259                 if ((fp = fopen(cp, "r"))) {
260                         readconfig((struct node **) 0, fp, cp, 0);
261                         fclose(fp);
262                 } else {
263                         admonish("", "unable to read $MHSHOW profile (%s)",
264                                         cp);
265                 }
266         }
267
268         /*
269         ** Read the standard profile setup
270         */
271         if ((fp = fopen(cp = etcpath("mhn.defaults"), "r"))) {
272                 readconfig((struct node **) 0, fp, cp, 0);
273                 fclose(fp);
274         }
275
276         /* Check for public cache location */
277         if ((cache_public = context_find(nmhcache)) && *cache_public != '/')
278                 cache_public = NULL;
279
280         /* Check for private cache location */
281         if (!(cache_private = context_find(nmhprivcache)))
282                 cache_private = ".cache";
283         cache_private = getcpy(toabsdir(cache_private));
284
285         /*
286         ** Check for storage directory.  If specified,
287         ** then store temporary files there.  Else we
288         ** store them in standard nmh directory.
289         */
290         if ((cp = context_find(nmhstorage)) && *cp)
291                 tmp = concat(cp, "/", invo_name, NULL);
292         else
293                 tmp = getcpy(toabsdir(invo_name));
294
295         if (file && msgs.size)
296                 adios(NULL, "cannot specify msg and file at same time!");
297
298         /*
299         ** check if message is coming from file
300         */
301         if (file) {
302                 if (!(cts = (CT *) calloc((size_t) 2, sizeof(*cts))))
303                         adios(NULL, "out of memory");
304                 ctp = cts;
305
306                 if ((ct = parse_mime(file)));
307                         *ctp++ = ct;
308         } else {
309                 /*
310                 ** message(s) are coming from a folder
311                 */
312                 if (!msgs.size)
313                         app_msgarg(&msgs, seq_cur);
314                 if (!folder)
315                         folder = getcurfol();
316                 maildir = toabsdir(folder);
317
318                 if (chdir(maildir) == NOTOK)
319                         adios(maildir, "unable to change directory to");
320
321                 /* read folder and create message structure */
322                 if (!(mp = folder_read(folder)))
323                         adios(NULL, "unable to read folder %s", folder);
324
325                 /* check for empty folder */
326                 if (mp->nummsg == 0)
327                         adios(NULL, "no messages in %s", folder);
328
329                 /* parse all the message ranges/sequences and set SELECTED */
330                 for (msgnum = 0; msgnum < msgs.size; msgnum++)
331                         if (!m_convert(mp, msgs.msgs[msgnum]))
332                                 done(1);
333
334                 /*
335                 ** Set the SELECT_UNSEEN bit for all the SELECTED messages,
336                 ** since we will use that as a tag to know which messages
337                 ** to remove from the "unseen" sequence.
338                 */
339                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
340                         if (is_selected(mp, msgnum))
341                                 set_unseen(mp, msgnum);
342
343                 seq_setprev(mp);  /* set the Previous-Sequence */
344                 seq_setunseen(mp, 0);  /* unset unseen seqs for shown msgs */
345
346                 if (!(cts = (CT *) calloc((size_t) (mp->numsel + 1),
347                                 sizeof(*cts))))
348                         adios(NULL, "out of memory");
349                 ctp = cts;
350
351                 /*
352                 ** Parse all the SELECTED messages.
353                 */
354                 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
355                         if (is_selected(mp, msgnum)) {
356                                 char *msgnam;
357
358                                 msgnam = m_name(msgnum);
359                                 if ((ct = parse_mime(msgnam)))
360                                         *ctp++ = ct;
361                         }
362                 }
363         }
364
365         if (!*cts)
366                 done(1);
367
368         userrs = 1;
369         SIGNAL(SIGQUIT, quitser);
370         SIGNAL(SIGPIPE, pipeser);
371
372         /*
373         ** Get the associated umask for the relevant contents.
374         */
375         for (ctp = cts; *ctp; ctp++) {
376                 struct stat st;
377
378                 ct = *ctp;
379                 if (type_ok(ct, 1) && !ct->c_umask) {
380                         if (stat(ct->c_file, &st) != NOTOK)
381                                 ct->c_umask = ~(st.st_mode & 0777);
382                         else
383                                 ct->c_umask = ~m_gmprot();
384                 }
385         }
386
387         /*
388         ** Show the message content
389         */
390         show_all_messages(cts);
391
392         /* Now free all the structures for the content */
393         for (ctp = cts; *ctp; ctp++)
394                 free_content(*ctp);
395
396         free((char *) cts);
397         cts = NULL;
398
399         /* If reading from a folder, do some updating */
400         if (mp) {
401                 context_replace(curfolder, folder); /* update current folder */
402                 seq_setcur(mp, mp->hghsel);        /* update current message */
403                 seq_save(mp);                      /* synchronize sequences */
404                 context_save();                    /* save the context file */
405         }
406
407         done(0);
408         return 1;
409 }
410
411
412 static RETSIGTYPE
413 pipeser(int i)
414 {
415         if (i == SIGQUIT) {
416                 unlink("core");
417                 fflush(stdout);
418                 fprintf(stderr, "\n");
419                 fflush(stderr);
420         }
421
422         done(1);
423         /* NOTREACHED */
424 }