]> diplodocus.org Git - nmh/blob - uip/mhn.c
fdcompare.c: Move interface to own file.
[nmh] / uip / mhn.c
1 /* mhn.c -- display, list, cache, or store the contents of MIME messages
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/folder_read.h"
10 #include "sbr/context_save.h"
11 #include "sbr/context_replace.h"
12 #include "sbr/context_find.h"
13 #include "sbr/readconfig.h"
14 #include "sbr/ambigsw.h"
15 #include "sbr/path.h"
16 #include "sbr/print_version.h"
17 #include "sbr/print_help.h"
18 #include "sbr/error.h"
19 #include <fcntl.h>
20 #include "h/signals.h"
21 #include "h/md5.h"
22 #include "h/mts.h"
23 #include "h/tws.h"
24 #include "h/fmt_scan.h"
25 #include "h/mime.h"
26 #include "h/mhparse.h"
27 #include "h/mhcachesbr.h"
28 #include "h/done.h"
29 #include "h/utils.h"
30 #include "mhmisc.h"
31 #include "sbr/m_maildir.h"
32 #include "mhfree.h"
33 #include "mhshowsbr.h"
34
35 #define MHN_SWITCHES \
36 X("auto", 0, AUTOSW) \
37 X("noauto", 0, NAUTOSW) \
38 X("cache", 0, CACHESW) \
39 X("nocache", 0, NCACHESW) \
40 X("check", 0, CHECKSW) \
41 X("nocheck", 0, NCHECKSW) \
42 X("headers", 0, HEADSW) \
43 X("noheaders", 0, NHEADSW) \
44 X("list", 0, LISTSW) \
45 X("nolist", 0, NLISTSW) \
46 X("realsize", 0, SIZESW) \
47 X("norealsize", 0, NSIZESW) \
48 X("show", 0, SHOWSW) \
49 X("noshow", 0, NSHOWSW) \
50 X("store", 0, STORESW) \
51 X("nostore", 0, NSTORESW) \
52 X("verbose", 0, VERBSW) \
53 X("noverbose", 0, NVERBSW) \
54 X("file file", 0, FILESW) \
55 X("form formfile", 0, FORMSW) \
56 X("part number", 0, PARTSW) \
57 X("type content", 0, TYPESW) \
58 X("rcache policy", 0, RCACHESW) \
59 X("wcache policy", 0, WCACHESW) \
60 X("version", 0, VERSIONSW) \
61 X("help", 0, HELPSW) \
62 /* \
63 * for debugging \
64 */ \
65 X("debug", -5, DEBUGSW) \
66 /* \
67 * switches for moreproc/mhlproc \
68 */ \
69 X("moreproc program", -4, PROGSW) \
70 X("nomoreproc", -3, NPROGSW) \
71 X("length lines", -4, LENSW) \
72 X("width columns", -4, WIDTHSW) \
73 /* \
74 * switches for mhbuild \
75 */ \
76 X("build", -5, BUILDSW) \
77 X("nobuild", -7, NBUILDSW) \
78 X("rfc934mode", 0, RFC934SW) \
79 X("norfc934mode", 0, NRFC934SW) \
80
81 #define X(sw, minchars, id) id,
82 DEFINE_SWITCH_ENUM(MHN);
83 #undef X
84
85 #define X(sw, minchars, id) { sw, minchars, id },
86 DEFINE_SWITCH_ARRAY(MHN, switches);
87 #undef X
88
89
90 int debugsw = 0;
91 int verbosw = 0;
92
93 /*
94 * variables for mhbuild (mhn -build)
95 */
96 static bool buildsw;
97 static int rfc934sw = 0;
98
99 /*
100 * what action to take?
101 */
102 static bool cachesw;
103 static bool listsw;
104 static bool showsw;
105 static bool storesw;
106
107 #define quitser pipeser
108
109 /*
110 * static prototypes
111 */
112 static void pipeser (int);
113
114
115 int
116 main (int argc, char **argv)
117 {
118 bool sizesw = true;
119 bool headsw = true;
120 bool autosw = false;
121 int msgnum, *icachesw;
122 char *cp, *file = NULL, *folder = NULL;
123 char *maildir, buf[100], **argp;
124 char **arguments;
125 char *cwd;
126 struct msgs_array msgs = { 0, 0, NULL };
127 struct msgs *mp = NULL;
128 CT ct, *ctp;
129 FILE *fp;
130 mhstoreinfo_t info;
131
132 if (nmh_init(argv[0], true, true)) { return 1; }
133
134 set_done(freects_done);
135
136 arguments = getarguments (invo_name, argc, argv, 1);
137 argp = arguments;
138
139 /*
140 * Parse arguments
141 */
142 while ((cp = *argp++)) {
143 if (*cp == '-') {
144 switch (smatch (++cp, switches)) {
145 case AMBIGSW:
146 ambigsw (cp, switches);
147 done (1);
148 case UNKWNSW:
149 die("-%s unknown", cp);
150
151 case HELPSW:
152 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
153 invo_name);
154 print_help (buf, switches, 1);
155 done (0);
156 case VERSIONSW:
157 print_version(invo_name);
158 done (0);
159
160 case AUTOSW:
161 autosw = true;
162 continue;
163 case NAUTOSW:
164 autosw = false;
165 continue;
166
167 case CACHESW:
168 cachesw = true;
169 continue;
170 case NCACHESW:
171 cachesw = false;
172 continue;
173
174 case RCACHESW:
175 icachesw = &rcachesw;
176 goto do_cache;
177 case WCACHESW:
178 icachesw = &wcachesw;
179 do_cache:
180 if (!(cp = *argp++) || *cp == '-')
181 die("missing argument to %s", argp[-2]);
182 switch (*icachesw = smatch (cp, cache_policy)) {
183 case AMBIGSW:
184 ambigsw (cp, cache_policy);
185 done (1);
186 case UNKWNSW:
187 die("%s unknown", cp);
188 default:
189 break;
190 }
191 continue;
192
193 case CHECKSW:
194 checksw++;
195 continue;
196 case NCHECKSW:
197 checksw = 0;
198 continue;
199
200 case HEADSW:
201 headsw = true;
202 continue;
203 case NHEADSW:
204 headsw = false;
205 continue;
206
207 case LISTSW:
208 listsw = true;
209 continue;
210 case NLISTSW:
211 listsw = false;
212 continue;
213
214 case SHOWSW:
215 showsw = true;
216 continue;
217 case NSHOWSW:
218 showsw = false;
219 continue;
220
221 case SIZESW:
222 sizesw = true;
223 continue;
224 case NSIZESW:
225 sizesw = false;
226 continue;
227
228 case STORESW:
229 storesw = true;
230 continue;
231 case NSTORESW:
232 storesw = false;
233 continue;
234
235 case PARTSW:
236 if (!(cp = *argp++) || *cp == '-')
237 die("missing argument to %s", argp[-2]);
238 if (npart >= NPARTS)
239 die("too many parts (starting with %s), %d max",
240 cp, NPARTS);
241 parts[npart++] = cp;
242 continue;
243
244 case TYPESW:
245 if (!(cp = *argp++) || *cp == '-')
246 die("missing argument to %s", argp[-2]);
247 if (ntype >= NTYPES)
248 die("too many types (starting with %s), %d max",
249 cp, NTYPES);
250 types[ntype++] = cp;
251 continue;
252
253 case FILESW:
254 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
255 die("missing argument to %s", argp[-2]);
256 file = *cp == '-' ? cp : path (cp, TFILE);
257 continue;
258
259 case FORMSW:
260 if (!(cp = *argp++) || *cp == '-')
261 die("missing argument to %s", argp[-2]);
262 free(formsw);
263 formsw = mh_xstrdup(etcpath(cp));
264 continue;
265
266 /*
267 * Switches for moreproc/mhlproc
268 */
269 case PROGSW:
270 if (!(progsw = *argp++) || *progsw == '-')
271 die("missing argument to %s", argp[-2]);
272 continue;
273 case NPROGSW:
274 nomore++;
275 continue;
276
277 case LENSW:
278 case WIDTHSW:
279 if (!(cp = *argp++) || *cp == '-')
280 die("missing argument to %s", argp[-2]);
281 continue;
282
283 /*
284 * Switches for mhbuild
285 */
286 case BUILDSW:
287 buildsw = true;
288 continue;
289 case NBUILDSW:
290 buildsw = false;
291 continue;
292 case RFC934SW:
293 rfc934sw = 1;
294 continue;
295 case NRFC934SW:
296 rfc934sw = -1;
297 continue;
298
299 case VERBSW:
300 verbosw = 1;
301 continue;
302 case NVERBSW:
303 verbosw = 0;
304 continue;
305 case DEBUGSW:
306 debugsw = 1;
307 continue;
308 }
309 }
310 if (*cp == '+' || *cp == '@') {
311 if (folder)
312 die("only one folder at a time!");
313 folder = pluspath (cp);
314 } else
315 app_msgarg(&msgs, cp);
316 }
317
318 /* null terminate the list of acceptable parts/types */
319 parts[npart] = NULL;
320 types[ntype] = NULL;
321
322 /*
323 * Check if we've specified an additional profile
324 */
325 if ((cp = getenv ("MHN"))) {
326 if ((fp = fopen (cp, "r"))) {
327 readconfig(NULL, fp, cp, 0);
328 fclose (fp);
329 } else {
330 admonish ("", "unable to read $MHN profile (%s)", cp);
331 }
332 }
333
334 /*
335 * Read the standard profile setup
336 */
337 if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
338 readconfig(NULL, fp, cp, 0);
339 fclose (fp);
340 }
341
342 /* Check for public cache location */
343 if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
344 cache_public = NULL;
345
346 /* Check for private cache location */
347 if (!(cache_private = context_find (nmhprivcache)))
348 cache_private = ".cache";
349 cache_private = mh_xstrdup(m_maildir(cache_private));
350
351 /*
352 * Cache the current directory before we do any chdirs()'s.
353 */
354 cwd = mh_xstrdup(pwd());
355
356 if (!context_find ("path"))
357 free (path ("./", TFOLDER));
358
359 /*
360 * Process a mhn composition file (mhn -build)
361 */
362 if (buildsw) {
363 char *vec[MAXARGS];
364 int vecp;
365
366 if (showsw || storesw || cachesw)
367 die("cannot use -build with -show, -store, -cache");
368 if (msgs.size < 1)
369 die("need to specify a %s composition file", invo_name);
370 if (msgs.size > 1)
371 die("only one %s composition file at a time", invo_name);
372
373 vecp = 0;
374 vec[vecp++] = "mhbuild";
375
376 if (rfc934sw == 1)
377 vec[vecp++] = "-rfc934mode";
378 else if (rfc934sw == -1)
379 vec[vecp++] = "-norfc934mode";
380
381 vec[vecp++] = msgs.msgs[0];
382 vec[vecp] = NULL;
383
384 execvp ("mhbuild", vec);
385 fprintf (stderr, "unable to exec ");
386 _exit(1);
387 }
388
389 /*
390 * Process a mhn composition file (old MH style)
391 */
392 if (msgs.size == 1 && !folder && !npart && !cachesw
393 && !showsw && !storesw && !ntype && !file
394 && (cp = getenv ("mhdraft"))
395 && strcmp (cp, msgs.msgs[0]) == 0) {
396
397 char *vec[MAXARGS];
398 int vecp;
399
400 vecp = 0;
401 vec[vecp++] = "mhbuild";
402
403 if (rfc934sw == 1)
404 vec[vecp++] = "-rfc934mode";
405 else if (rfc934sw == -1)
406 vec[vecp++] = "-norfc934mode";
407
408 vec[vecp++] = cp;
409 vec[vecp] = NULL;
410
411 execvp ("mhbuild", vec);
412 fprintf (stderr, "unable to exec ");
413 _exit(1);
414 }
415
416 if (file && msgs.size)
417 die("cannot specify msg and file at same time!");
418
419 /*
420 * check if message is coming from file
421 */
422 if (file) {
423 cts = mh_xcalloc(2, sizeof *cts);
424 ctp = cts;
425
426 if ((ct = parse_mime (file)))
427 *ctp++ = ct;
428 } else {
429 /*
430 * message(s) are coming from a folder
431 */
432 if (!msgs.size)
433 app_msgarg(&msgs, "cur");
434 if (!folder)
435 folder = getfolder (1);
436 maildir = m_maildir (folder);
437
438 if (chdir (maildir) == NOTOK)
439 adios (maildir, "unable to change directory to");
440
441 /* read folder and create message structure */
442 if (!(mp = folder_read (folder, 1)))
443 die("unable to read folder %s", folder);
444
445 /* check for empty folder */
446 if (mp->nummsg == 0)
447 die("no messages in %s", folder);
448
449 /* parse all the message ranges/sequences and set SELECTED */
450 for (msgnum = 0; msgnum < msgs.size; msgnum++)
451 if (!m_convert (mp, msgs.msgs[msgnum]))
452 done (1);
453 seq_setprev (mp); /* set the previous-sequence */
454
455 cts = mh_xcalloc(mp->numsel + 1, sizeof *cts);
456 ctp = cts;
457
458 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
459 if (is_selected(mp, msgnum)) {
460 char *msgnam;
461
462 msgnam = m_name (msgnum);
463 if ((ct = parse_mime (msgnam)))
464 *ctp++ = ct;
465 }
466 }
467 }
468
469 if (!*cts)
470 done (1);
471
472 /*
473 * You can't give more than one of these flags
474 * at a time.
475 */
476 if (showsw + listsw + storesw + cachesw > 1)
477 die("can only use one of -show, -list, -store, -cache at same time");
478
479 /* If no action is specified, assume -show */
480 if (!listsw && !showsw && !storesw && !cachesw)
481 showsw = true;
482
483 userrs = true;
484 SIGNAL (SIGQUIT, quitser);
485 SIGNAL (SIGPIPE, pipeser);
486
487 /*
488 * Get the associated umask for the relevant contents.
489 */
490 for (ctp = cts; *ctp; ctp++) {
491 struct stat st;
492
493 ct = *ctp;
494 if (type_ok (ct, 1) && !ct->c_umask) {
495 if (stat (ct->c_file, &st) != NOTOK)
496 ct->c_umask = ~(st.st_mode & 0777);
497 else
498 ct->c_umask = ~m_gmprot();
499 }
500 }
501
502 /*
503 * List the message content
504 */
505 if (listsw)
506 list_all_messages (cts, headsw, sizesw, verbosw, debugsw, 0);
507
508 /*
509 * Store the message content
510 */
511 if (storesw) {
512 info = mhstoreinfo_create (cts, cwd, "always", autosw, verbosw);;
513 store_all_messages (info);
514 mhstoreinfo_free (info);
515 }
516
517 /* If reading from a folder, do some updating */
518 if (mp) {
519 context_replace (pfolder, folder);/* update current folder */
520 seq_setcur (mp, mp->hghsel); /* update current message */
521 seq_save (mp); /* synchronize sequences */
522 context_save (); /* save the context file */
523 }
524
525 /*
526 * Cache the message content
527 */
528 if (cachesw)
529 cache_all_messages (cts);
530
531 /*
532 * Show the message content
533 */
534 if (showsw)
535 show_all_messages (cts, 0, 0, 0);
536
537 /* Now free all the structures for the content */
538 for (ctp = cts; *ctp; ctp++)
539 free_content (*ctp);
540
541 free (cts);
542 cts = NULL;
543
544 done (0);
545 return 1;
546 }
547
548
549 static void
550 pipeser (int i)
551 {
552 if (i == SIGQUIT) {
553 fflush (stdout);
554 fprintf (stderr, "\n");
555 fflush (stderr);
556 }
557
558 done (1);
559 /* NOTREACHED */
560 }