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