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