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