]> diplodocus.org Git - nmh/blob - uip/forw.c
84d10ade39c5890fc0f27cbf656799ba9e9a7059
[nmh] / uip / forw.c
1 /* forw.c -- forward a message, or group of 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/error.h"
10 #include <fcntl.h>
11 #include <h/tws.h>
12 #include "h/done.h"
13 #include <h/utils.h>
14 #include "sbr/m_maildir.h"
15 #include "forwsbr.h"
16
17
18 #define IFORMAT "digest-issue-%s"
19 #define VFORMAT "digest-volume-%s"
20
21 #define FORW_SWITCHES \
22 X("annotate", 0, ANNOSW) \
23 X("noannotate", 0, NANNOSW) \
24 X("draftfolder +folder", 0, DFOLDSW) \
25 X("draftmessage msg", 0, DMSGSW) \
26 X("nodraftfolder", 0, NDFLDSW) \
27 X("editor editor", 0, EDITRSW) \
28 X("noedit", 0, NEDITSW) \
29 X("filter filterfile", 0, FILTSW) \
30 X("form formfile", 0, FORMSW) \
31 X("format", 5, FRMTSW) \
32 X("noformat", 7, NFRMTSW) \
33 X("inplace", 0, INPLSW) \
34 X("noinplace", 0, NINPLSW) \
35 X("mime", 0, MIMESW) \
36 X("nomime", 0, NMIMESW) \
37 X("digest list", 0, DGSTSW) \
38 X("issue number", 0, ISSUESW) \
39 X("volume number", 0, VOLUMSW) \
40 X("whatnowproc program", 0, WHATSW) \
41 X("nowhatnowproc", 0, NWHATSW) \
42 X("dashstuffing", 0, BITSTUFFSW) /* interface to mhl */ \
43 X("nodashstuffing", 0, NBITSTUFFSW) \
44 X("version", 0, VERSIONSW) \
45 X("help", 0, HELPSW) \
46 X("file file", 4, FILESW) \
47 X("build", 5, BILDSW) /* interface from mhe */ \
48 X("from address", 0, FROMSW) \
49 X("to address", 0, TOSW) \
50 X("cc address", 0, CCSW) \
51 X("subject text", 0, SUBJECTSW) \
52 X("fcc mailbox", 0, FCCSW) \
53 X("width columns", 0, WIDTHSW) \
54
55 #define X(sw, minchars, id) id,
56 DEFINE_SWITCH_ENUM(FORW);
57 #undef X
58
59 #define X(sw, minchars, id) { sw, minchars, id },
60 DEFINE_SWITCH_ARRAY(FORW, switches);
61 #undef X
62
63 #define DISPO_SWITCHES \
64 X("quit", 0, NOSW) \
65 X("replace", 0, YESW) \
66 X("list", 0, LISTDSW) \
67 X("refile +folder", 0, REFILSW) \
68 X("new", 0, NEWSW) \
69
70 #define X(sw, minchars, id) id,
71 DEFINE_SWITCH_ENUM(DISPO);
72 #undef X
73
74 #define X(sw, minchars, id) { sw, minchars, id },
75 DEFINE_SWITCH_ARRAY(DISPO, aqrnl);
76 #undef X
77
78 static struct swit aqrl[] = {
79 { "quit", 0, NOSW },
80 { "replace", 0, YESW },
81 { "list", 0, LISTDSW },
82 { "refile +folder", 0, REFILSW },
83 { NULL, 0, 0 }
84 };
85
86 static char drft[BUFSIZ];
87
88 static char delim3[] =
89 "\n------------------------------------------------------------\n\n";
90 static char delim4[] = "\n------------------------------\n\n";
91
92
93 static struct msgs *mp = NULL; /* used a lot */
94
95
96 /*
97 * static prototypes
98 */
99 static void mhl_draft (int, char *, int, int, char *, char *, int);
100 static void copy_draft (int, char *, char *, int, int, int);
101 static void copy_mime_draft (int);
102
103
104 int
105 main (int argc, char **argv)
106 {
107 bool anot = false;
108 bool inplace = true;
109 bool mime = false;
110 int issue = 0, volume = 0, dashstuff = 0;
111 bool nedit = false;
112 bool nwhat = false;
113 int i, in;
114 int out, isdf = 0, msgnum = 0;
115 int outputlinelen = OUTPUTLINELEN;
116 int dat[5];
117 char *cp, *cwd, *maildir, *dfolder = NULL;
118 char *dmsg = NULL, *digest = NULL, *ed = NULL;
119 char *file = NULL, *filter = NULL, *folder = NULL, *fwdmsg = NULL;
120 char *from = NULL, *to = NULL, *cc = NULL, *subject = NULL, *fcc = NULL;
121 char *form = NULL, buf[BUFSIZ];
122 char **argp, **arguments;
123 struct stat st;
124 struct msgs_array msgs = { 0, 0, NULL };
125 bool buildsw = false;
126
127 if (nmh_init(argv[0], true, true)) { return 1; }
128
129 arguments = getarguments (invo_name, argc, argv, 1);
130 argp = arguments;
131
132 while ((cp = *argp++)) {
133 if (*cp == '-') {
134 switch (smatch (++cp, switches)) {
135 case AMBIGSW:
136 ambigsw (cp, switches);
137 done (1);
138 case UNKWNSW:
139 die("-%s unknown", cp);
140
141 case HELPSW:
142 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
143 invo_name);
144 print_help (buf, switches, 1);
145 done (0);
146 case VERSIONSW:
147 print_version(invo_name);
148 done (0);
149
150 case ANNOSW:
151 anot = true;
152 continue;
153 case NANNOSW:
154 anot = false;
155 continue;
156
157 case EDITRSW:
158 if (!(ed = *argp++) || *ed == '-')
159 die("missing argument to %s", argp[-2]);
160 nedit = false;
161 continue;
162 case NEDITSW:
163 nedit = true;
164 continue;
165
166 case WHATSW:
167 if (!(whatnowproc = *argp++) || *whatnowproc == '-')
168 die("missing argument to %s", argp[-2]);
169 nwhat = false;
170 continue;
171 case BILDSW:
172 buildsw = true;
173 /* FALLTHRU */
174 case NWHATSW:
175 nwhat = true;
176 continue;
177
178 case FILESW:
179 if (file)
180 die("only one file at a time!");
181 if (!(cp = *argp++) || *cp == '-')
182 die("missing argument to %s", argp[-2]);
183 file = path (cp, TFILE);
184 continue;
185 case FILTSW:
186 if (!(cp = *argp++) || *cp == '-')
187 die("missing argument to %s", argp[-2]);
188 filter = mh_xstrdup(etcpath(cp));
189 mime = false;
190 continue;
191 case FORMSW:
192 if (!(form = *argp++) || *form == '-')
193 die("missing argument to %s", argp[-2]);
194 continue;
195
196 case FRMTSW:
197 filter = mh_xstrdup(etcpath(mhlforward));
198 continue;
199 case NFRMTSW:
200 filter = NULL;
201 continue;
202
203 case INPLSW:
204 inplace = true;
205 continue;
206 case NINPLSW:
207 inplace = false;
208 continue;
209
210 case MIMESW:
211 mime = true;
212 filter = NULL;
213 continue;
214 case NMIMESW:
215 mime = false;
216 continue;
217
218 case DGSTSW:
219 if (!(cp = *argp++) || *cp == '-')
220 die("missing argument to %s", argp[-2]);
221 digest = mh_xstrdup(cp);
222 mime = false;
223 continue;
224 case ISSUESW:
225 if (!(cp = *argp++) || *cp == '-')
226 die("missing argument to %s", argp[-2]);
227 if ((issue = atoi (cp)) < 1)
228 die("bad argument %s %s", argp[-2], cp);
229 continue;
230 case VOLUMSW:
231 if (!(cp = *argp++) || *cp == '-')
232 die("missing argument to %s", argp[-2]);
233 if ((volume = atoi (cp)) < 1)
234 die("bad argument %s %s", argp[-2], cp);
235 continue;
236
237 case DFOLDSW:
238 if (dfolder)
239 die("only one draft folder at a time!");
240 if (!(cp = *argp++) || *cp == '-')
241 die("missing argument to %s", argp[-2]);
242 dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
243 *cp != '@' ? TFOLDER : TSUBCWF);
244 continue;
245 case DMSGSW:
246 if (dmsg)
247 die("only one draft message at a time!");
248 if (!(dmsg = *argp++) || *dmsg == '-')
249 die("missing argument to %s", argp[-2]);
250 continue;
251 case NDFLDSW:
252 dfolder = NULL;
253 isdf = NOTOK;
254 continue;
255
256 case BITSTUFFSW:
257 dashstuff = 1; /* ternary logic */
258 continue;
259 case NBITSTUFFSW:
260 dashstuff = -1; /* ternary logic */
261 continue;
262
263 case FROMSW:
264 if (!(cp = *argp++) || *cp == '-')
265 die("missing argument to %s", argp[-2]);
266 from = addlist(from, cp);
267 continue;
268 case TOSW:
269 if (!(cp = *argp++) || *cp == '-')
270 die("missing argument to %s", argp[-2]);
271 to = addlist(to, cp);
272 continue;
273 case CCSW:
274 if (!(cp = *argp++) || *cp == '-')
275 die("missing argument to %s", argp[-2]);
276 cc = addlist(cc, cp);
277 continue;
278 case FCCSW:
279 if (!(cp = *argp++) || *cp == '-')
280 die("missing argument to %s", argp[-2]);
281 fcc = addlist(fcc, cp);
282 continue;
283 case SUBJECTSW:
284 if (!(cp = *argp++) || *cp == '-')
285 die("missing argument to %s", argp[-2]);
286 subject = mh_xstrdup(cp);
287 continue;
288
289 case WIDTHSW:
290 if (!(cp = *argp++) || *cp == '-')
291 die("missing argument to %s", argp[-2]);
292 if ((outputlinelen = atoi(cp)) < 10)
293 die("impossible width %d", outputlinelen);
294 continue;
295 }
296 }
297 if (*cp == '+' || *cp == '@') {
298 if (folder)
299 die("only one folder at a time!");
300 folder = pluspath (cp);
301 } else {
302 app_msgarg(&msgs, cp);
303 }
304 }
305
306 cwd = mh_xstrdup(pwd ());
307
308 if (!context_find ("path"))
309 free (path ("./", TFOLDER));
310 if (file && (msgs.size || folder))
311 die("can't mix files and folders/msgs");
312
313 try_it_again:
314
315 strncpy (drft, buildsw ? m_maildir ("draft")
316 : m_draft (dfolder, NULL, NOUSE, &isdf), sizeof(drft));
317
318 /* Check if a draft already exists */
319 if (!buildsw && stat (drft, &st) != NOTOK) {
320 printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
321 for (i = LISTDSW; i != YESW;) {
322 if (!(argp = read_switch_multiword ("\nDisposition? ",
323 isdf ? aqrnl : aqrl)))
324 done (1);
325 switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
326 case NOSW:
327 done (0);
328 case NEWSW:
329 dmsg = NULL;
330 goto try_it_again;
331 case YESW:
332 break;
333 case LISTDSW:
334 showfile (++argp, drft);
335 break;
336 case REFILSW:
337 if (refile (++argp, drft) == 0)
338 i = YESW;
339 break;
340 default:
341 inform("say what?");
342 break;
343 }
344 }
345 }
346
347 if (file) {
348 /*
349 * Forwarding a file.
350 */
351 anot = false; /* don't want to annotate a file */
352 } else {
353 /*
354 * Forwarding a message.
355 */
356 if (!msgs.size)
357 app_msgarg(&msgs, "cur");
358 if (!folder)
359 folder = getfolder (1);
360 maildir = m_maildir (folder);
361
362 if (chdir (maildir) == NOTOK)
363 adios (maildir, "unable to change directory to");
364
365 /* read folder and create message structure */
366 if (!(mp = folder_read (folder, 1)))
367 die("unable to read folder %s", folder);
368
369 /* check for empty folder */
370 if (mp->nummsg == 0)
371 die("no messages in %s", folder);
372
373 /* parse all the message ranges/sequences and set SELECTED */
374 for (msgnum = 0; msgnum < msgs.size; msgnum++)
375 if (!m_convert (mp, msgs.msgs[msgnum]))
376 done (1);
377
378 seq_setprev (mp); /* set the previous sequence */
379
380 /*
381 * Find the first message in our set and use it as the input
382 * for the component scanner
383 */
384
385 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
386 if (is_selected (mp, msgnum)) {
387 fwdmsg = mh_xstrdup(m_name(msgnum));
388 break;
389 }
390
391 if (! fwdmsg)
392 die("Unable to find input message");
393 }
394
395 if (filter && access (filter, R_OK) == NOTOK)
396 adios (filter, "unable to read");
397
398 /*
399 * Open form (component) file.
400 */
401 if (digest) {
402 if (issue == 0) {
403 snprintf (buf, sizeof(buf), IFORMAT, digest);
404 if (volume == 0
405 && (cp = context_find (buf))
406 && ((issue = atoi (cp)) < 0))
407 issue = 0;
408 issue++;
409 }
410 if (volume == 0) {
411 snprintf (buf, sizeof(buf), VFORMAT, digest);
412 if ((cp = context_find (buf)) == NULL || (volume = atoi (cp)) <= 0)
413 volume = 1;
414 }
415 if (!form)
416 form = digestcomps;
417 } else {
418 if (!form)
419 form = forwcomps;
420 }
421
422 dat[0] = digest ? issue : msgnum;
423 dat[1] = volume;
424 dat[2] = 0;
425 dat[3] = outputlinelen;
426 dat[4] = 0;
427
428
429 in = build_form (form, digest, dat, from, to, cc, fcc, subject,
430 file ? file : fwdmsg);
431
432 if ((out = creat (drft, m_gmprot ())) == NOTOK)
433 adios (drft, "unable to create");
434
435 /*
436 * copy the components into the draft
437 */
438 cpydata (in, out, form, drft);
439 close (in);
440
441 if (file) {
442 /* just copy the file into the draft */
443 if ((in = open (file, O_RDONLY)) == NOTOK)
444 adios (file, "unable to open");
445 cpydata (in, out, file, drft);
446 close (in);
447 close (out);
448 } else {
449 /*
450 * If filter file is defined, then format the
451 * messages into the draft using mhlproc.
452 */
453 if (filter)
454 mhl_draft (out, digest, volume, issue, drft, filter, dashstuff);
455 else if (mime)
456 copy_mime_draft (out);
457 else
458 copy_draft (out, digest, drft, volume, issue, dashstuff);
459 close (out);
460
461 if (digest) {
462 snprintf (buf, sizeof(buf), IFORMAT, digest);
463 context_replace (buf, mh_xstrdup(m_str(issue)));
464 snprintf (buf, sizeof(buf), VFORMAT, digest);
465 context_replace (buf, mh_xstrdup(m_str(volume)));
466 }
467
468 context_replace (pfolder, folder); /* update current folder */
469 seq_setcur (mp, mp->lowsel); /* update current message */
470 seq_save (mp); /* synchronize sequences */
471 context_save (); /* save the context file */
472 }
473
474 if (nwhat)
475 done (0);
476 what_now (ed, nedit, NOUSE, drft, NULL, 0, mp,
477 anot ? "Forwarded" : NULL, inplace, cwd, 0);
478 done (1);
479 return 1;
480 }
481
482
483 /*
484 * Filter the messages you are forwarding, into the
485 * draft calling the mhlproc, and reading its output
486 * from a pipe.
487 */
488
489 static void
490 mhl_draft (int out, char *digest, int volume, int issue,
491 char *file, char *filter, int dashstuff)
492 {
493 pid_t child_id;
494 int msgnum, pd[2];
495 char buf1[BUFSIZ];
496 char buf2[BUFSIZ];
497 char *program;
498 struct msgs_array vec = { 0, 0, NULL };
499
500 if (pipe (pd) == NOTOK)
501 adios ("pipe", "unable to create");
502
503 argsplit_msgarg(&vec, mhlproc, &program);
504
505 child_id = fork();
506 switch (child_id) {
507 case NOTOK:
508 adios ("fork", "unable to");
509
510 case OK:
511 close (pd[0]);
512 dup2 (pd[1], 1);
513 close (pd[1]);
514
515 app_msgarg(&vec, "-forwall");
516 app_msgarg(&vec, "-form");
517 app_msgarg(&vec, filter);
518
519 if (digest) {
520 app_msgarg(&vec, "-digest");
521 app_msgarg(&vec, digest);
522 app_msgarg(&vec, "-issue");
523 snprintf (buf1, sizeof(buf1), "%d", issue);
524 app_msgarg(&vec, buf1);
525 app_msgarg(&vec, "-volume");
526 snprintf (buf2, sizeof(buf2), "%d", volume);
527 app_msgarg(&vec, buf2);
528 }
529
530 /*
531 * Are we dashstuffing (quoting) the lines that begin
532 * with `-'. We use the mhl default (don't add any flag)
533 * unless the user has specified a specific flag.
534 */
535 if (dashstuff > 0)
536 app_msgarg(&vec, "-dashstuffing");
537 else if (dashstuff < 0)
538 app_msgarg(&vec, "-nodashstuffing");
539
540 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
541 if (is_selected (mp, msgnum))
542 app_msgarg(&vec, mh_xstrdup(m_name (msgnum)));
543 }
544
545 app_msgarg(&vec, NULL);
546
547 execvp (program, vec.msgs);
548 fprintf (stderr, "unable to exec ");
549 perror (mhlproc);
550 _exit(1);
551
552 default:
553 close (pd[1]);
554 cpydata (pd[0], out, vec.msgs[0], file);
555 close (pd[0]);
556 pidXwait(child_id, mhlproc);
557 break;
558 }
559 }
560
561
562 /*
563 * Copy the messages into the draft. The messages are
564 * not filtered through the mhlproc. Do dashstuffing if
565 * necessary.
566 */
567
568 static void
569 copy_draft (int out, char *digest, char *file, int volume, int issue, int dashstuff)
570 {
571 int fd,i, msgcnt, msgnum;
572 int len, buflen;
573 char *bp, *msgnam;
574 char buffer[BUFSIZ];
575
576 msgcnt = 1;
577 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
578 if (is_selected (mp, msgnum)) {
579 if (digest) {
580 strncpy (buffer, msgnum == mp->lowsel ? delim3 : delim4,
581 sizeof(buffer));
582 } else {
583 /* Get buffer ready to go */
584 bp = buffer;
585 buflen = sizeof(buffer);
586
587 strncpy (bp, "\n-------", buflen);
588 len = strlen (bp);
589 bp += len;
590 buflen -= len;
591
592 if (msgnum == mp->lowsel) {
593 snprintf (bp, buflen, " Forwarded Message%s",
594 PLURALS(mp->numsel));
595 } else {
596 snprintf (bp, buflen, " Message %d", msgcnt);
597 }
598 len = strlen (bp);
599 bp += len;
600 buflen -= len;
601
602 strncpy (bp, "\n\n", buflen);
603 }
604 if (write (out, buffer, strlen (buffer)) < 0) {
605 advise (drft, "write");
606 }
607
608 if ((fd = open (msgnam = m_name (msgnum), O_RDONLY)) == NOTOK) {
609 admonish (msgnam, "unable to read message");
610 continue;
611 }
612
613 /*
614 * Copy the message. Add RFC934 quoting (dashstuffing)
615 * unless given the -nodashstuffing flag.
616 */
617 if (dashstuff >= 0)
618 cpydgst (fd, out, msgnam, file);
619 else
620 cpydata (fd, out, msgnam, file);
621
622 close (fd);
623 msgcnt++;
624 }
625 }
626
627 if (digest) {
628 strncpy (buffer, delim4, sizeof(buffer));
629 } else {
630 snprintf (buffer, sizeof(buffer), "\n------- End of Forwarded Message%s\n",
631 PLURALS(mp->numsel));
632 }
633 if (write (out, buffer, strlen (buffer)) < 0) {
634 advise (drft, "write");
635 }
636
637 if (digest) {
638 snprintf (buffer, sizeof(buffer), "End of %s Digest [Volume %d Issue %d]\n",
639 digest, volume, issue);
640 i = strlen (buffer);
641 for (bp = buffer + i; i > 1; i--)
642 *bp++ = '*';
643 *bp++ = '\n';
644 *bp = 0;
645 if (write (out, buffer, strlen (buffer)) < 0) {
646 advise (drft, "write");
647 }
648 }
649 }
650
651
652 /*
653 * Create a mhn composition file for forwarding message.
654 */
655
656 static void
657 copy_mime_draft (int out)
658 {
659 int msgnum;
660 char buffer[BUFSIZ];
661
662 snprintf (buffer, sizeof(buffer), "#forw [forwarded message%s] +%s",
663 PLURALS(mp->numsel), mp->foldpath);
664 if (write (out, buffer, strlen (buffer)) < 0) {
665 advise (drft, "write");
666 }
667 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
668 if (is_selected (mp, msgnum)) {
669 snprintf (buffer, sizeof(buffer), " %s", m_name (msgnum));
670 if (write (out, buffer, strlen (buffer)) < 0) {
671 advise (drft, "write");
672 }
673 }
674 if (write (out, "\n", 1) < 0) {
675 advise (drft, "write newline");
676 }
677 }