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