91ce04a8a29f074191885e060bfc753fea883745
[mmh] / uip / forw.c
1 /*
2 ** forw.c -- forward a message, or group of messages.
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <fcntl.h>
11 #include <h/fmt_scan.h>
12 #include <h/tws.h>
13 #include <h/utils.h>
14 #include <unistd.h>
15 #include <locale.h>
16
17 #define IFORMAT  "digest-issue-%s"
18 #define VFORMAT  "digest-volume-%s"
19
20 static struct swit switches[] = {
21 #define ANNOSW  0
22         { "annotate", 0 },
23 #define NANNOSW  1
24         { "noannotate", 2 },
25 #define EDITRSW  2
26         { "editor editor", 0 },
27 #define FORMSW  3
28         { "form formfile", 0 },
29 #define DGSTSW  4
30         { "digest list", 0 },
31 #define ISSUESW  5
32         { "issue number", 0 },
33 #define VOLUMSW  6
34         { "volume number", 0 },
35 #define WHATSW  7
36         { "whatnowproc program", 0 },
37 #define VERSIONSW  8
38         { "Version", 0 },
39 #define HELPSW  9
40         { "help", 0 },
41 #define BILDSW  10
42         { "build", 5 },  /* interface from mhe */
43         { NULL, 0 }
44 };
45
46 static char drft[BUFSIZ];
47 static struct msgs *mp = NULL;
48
49
50 /*
51 ** static prototypes
52 */
53 static void add_forw_hdr(char *);
54 static int build_form(char *, char *, int, int);
55
56
57 int
58 main(int argc, char **argv)
59 {
60         int msgp = 0, anot = 0;
61         int issue = 0, volume = 0;
62         int in;
63         int out, msgnum;
64         char *cp, *cwd, *maildir;
65         char *digest = NULL, *ed = NULL;
66         char *folder = NULL;
67         char *form = NULL, buf[BUFSIZ], value[10];
68         char **argp, **arguments, *msgs[MAXARGS];
69         char *fmtstr;
70         int buildsw = 0;
71
72         setlocale(LC_ALL, "");
73         invo_name = mhbasename(argv[0]);
74
75         /* read user profile/context */
76         context_read();
77
78         arguments = getarguments(invo_name, argc, argv, 1);
79         argp = arguments;
80
81         while ((cp = *argp++)) {
82                 if (*cp == '-') {
83                         switch (smatch(++cp, switches)) {
84                         case AMBIGSW:
85                                 ambigsw(cp, switches);
86                                 /* sysexits.h EX_USAGE */
87                                 exit(1);
88                         case UNKWNSW:
89                                 adios(NULL, "-%s unknown", cp);
90
91                         case HELPSW:
92                                 snprintf(buf, sizeof(buf), "%s [+folder] [msgs] [switches]", invo_name);
93                                 print_help(buf, switches, 1);
94                                 exit(0);
95                         case VERSIONSW:
96                                 print_version(invo_name);
97                                 exit(0);
98
99                         case ANNOSW:
100                                 anot++;
101                                 continue;
102                         case NANNOSW:
103                                 anot = 0;
104                                 continue;
105
106                         case EDITRSW:
107                                 if (!(ed = *argp++) || *ed == '-')
108                                         adios(NULL, "missing argument to %s",
109                                                         argp[-2]);
110                                 continue;
111
112                         case WHATSW:
113                                 if (!(whatnowproc = *argp++) ||
114                                                 *whatnowproc == '-')
115                                         adios(NULL, "missing argument to %s",
116                                                         argp[-2]);
117                                 continue;
118
119                         case BILDSW:
120                                 buildsw++;
121                                 continue;
122
123                         case FORMSW:
124                                 if (!(form = *argp++) || *form == '-')
125                                         adios(NULL, "missing argument to %s",
126                                                         argp[-2]);
127                                 continue;
128
129                         case DGSTSW:
130                                 if (!(digest = *argp++) || *digest == '-')
131                                         adios(NULL, "missing argument to %s",
132                                                         argp[-2]);
133                                 //mime = 0;
134                                 continue;
135                         case ISSUESW:
136                                 if (!(cp = *argp++) || *cp == '-')
137                                         adios(NULL, "missing argument to %s",
138                                                         argp[-2]);
139                                 if ((issue = atoi(cp)) < 1)
140                                         adios(NULL, "bad argument %s %s",
141                                                         argp[-2], cp);
142                                 continue;
143                         case VOLUMSW:
144                                 if (!(cp = *argp++) || *cp == '-')
145                                         adios(NULL, "missing argument to %s",
146                                                         argp[-2]);
147                                 if ((volume = atoi(cp)) < 1)
148                                         adios(NULL, "bad argument %s %s",
149                                                         argp[-2], cp);
150                                 continue;
151                         }
152                 }
153                 if (*cp == '+' || *cp == '@') {
154                         if (folder)
155                                 adios(NULL, "only one folder at a time!");
156                         else
157                                 folder = getcpy(expandfol(cp));
158                 } else {
159                         msgs[msgp++] = cp;
160                 }
161         }
162
163         cwd = getcpy(pwd());
164         strncpy(drft, buildsw ? toabsdir("draft") : m_draft(seq_beyond),
165                         sizeof(drft));
166         /*
167         ** FIXME: (concerning MHE support (buildsw) only)
168         ** There's no check if the draft already exists. mmh has removed
169         ** this case by having the draft folder. I won't add code only to
170         ** handle this legacy issue for MHE. -- meillo@marmaro.de 2012-05
171         */
172
173         /*
174         ** Forwarding a message.
175         */
176         if (!msgp)
177                 msgs[msgp++] = seq_cur;
178         if (!folder)
179                 folder = getcurfol();
180         maildir = toabsdir(folder);
181
182         if (chdir(maildir) == NOTOK)
183                 adios(maildir, "unable to change directory to");
184
185         /* read folder and create message structure */
186         if (!(mp = folder_read(folder)))
187                 adios(NULL, "unable to read folder %s", folder);
188
189         /* check for empty folder */
190         if (mp->nummsg == 0)
191                 adios(NULL, "no messages in %s", folder);
192
193         /* parse all the message ranges/sequences and set SELECTED */
194         for (msgnum = 0; msgnum < msgp; msgnum++) {
195                 if (!m_convert(mp, msgs[msgnum])) {
196                         /* sysexits.h EX_USAGE */
197                         exit(1);
198                 }
199         }
200         seq_setprev(mp);  /* set the previous sequence */
201
202         if ((out = creat(drft, m_gmprot())) == NOTOK)
203                 adios(drft, "unable to create");
204
205         /* Open form (component) file. */
206         if (digest) {
207                 if (issue == 0) {
208                         snprintf(buf, sizeof(buf), IFORMAT, digest);
209                         if (volume == 0 && (cp = context_find(buf))
210                                         && ((issue = atoi(cp)) < 0))
211                                 issue = 0;
212                         issue++;
213                 }
214                 if (volume == 0) {
215                         snprintf(buf, sizeof(buf), VFORMAT, digest);
216                         if ((cp = context_find(buf)) == NULL ||
217                                         (volume = atoi(cp)) <= 0)
218                                 volume = 1;
219                 }
220                 if (!form)
221                         form = digestcomps;
222                 in = build_form(form, digest, volume, issue);
223                 cpydata(in, out, form, drft);
224                 close(in);
225         } else {
226                 fmtstr = new_fs(form, forwcomps);
227                 if (write(out, fmtstr, strlen(fmtstr)) != (int)strlen(fmtstr)) {
228                         adios(drft, "error writing");
229                 }
230         }
231         close(out);
232
233         add_forw_hdr(drft);
234
235         if (digest) {
236                 snprintf(buf, sizeof(buf), IFORMAT, digest);
237                 snprintf(value, sizeof(value), "%d", issue);
238                 context_replace(buf, getcpy(value));
239                 snprintf(buf, sizeof(buf), VFORMAT, digest);
240                 snprintf(value, sizeof(value), "%d", volume);
241                 context_replace(buf, getcpy(value));
242         }
243
244         context_replace(curfolder, folder); /* update current folder */
245         seq_setcur(mp, mp->lowsel);  /* update current message */
246         seq_save(mp);  /* synchronize sequences */
247         context_save();  /* save the context file */
248
249         if (buildsw)
250                 exit(0);
251         what_now(ed, NOUSE, drft, NULL, 0, mp,
252                 anot ? "Forwarded" : NULL, cwd);
253         /* sysexits.h EX_SOFTWARE */
254         return 1;
255 }
256
257
258 /*
259 ** Create an attachment header for the to be forward messages.
260 */
261 static void
262 add_forw_hdr(char *draft)
263 {
264         int msgnum;
265         char buf[BUFSIZ];
266         char *vec[MAXARGS];
267         int vecp = 0;
268
269         vec[vecp++] = "anno";
270         vec[vecp++] = "-append";
271         vec[vecp++] = "-nodate";
272         vec[vecp++] = draft;
273         vec[vecp++] = "-comp";
274         vec[vecp++] = attach_hdr;
275         vec[vecp++] = "-text";
276         snprintf(buf, sizeof buf, "+%s", mp->foldpath);
277         for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
278                 if (!is_selected(mp, msgnum)) {
279                         continue;
280                 }
281                 if (strlen(buf) + 1 + strlen(m_name(msgnum)) + 1
282                                 > sizeof buf) {
283                         adios(NULL, "Attachment header line too long. "
284                                         "Forward less messages.");
285                 }
286                 strcat(buf, " ");
287                 strcat(buf, m_name(msgnum));
288         }
289         vec[vecp++] = buf;
290         vec[vecp] = NULL;
291         if (execprog(*vec, vec) != 0) {
292                 advise(NULL, "unable to add attachment header");
293         }
294 }
295
296
297 static int
298 build_form(char *form, char *digest, int volume, int issue)
299 {
300         int in;
301         int fmtsize;
302         register char *fmtstr;
303         char *line, tmpfil[BUFSIZ];
304         FILE *tmp;
305         register struct comp *cptr;
306         struct format *fmt;
307         int dat[5];
308         char *cp = NULL;
309
310         /* Get new format string */
311         fmtstr = new_fs(form, NULL);
312         fmtsize = strlen(fmtstr) + 256;
313
314         /* Compile format string */
315         fmt_compile(fmtstr, &fmt);
316
317         FINDCOMP(cptr, "digest");
318         if (cptr)
319                 cptr->c_text = digest;
320         FINDCOMP(cptr, "date");
321         if (cptr)
322                 cptr->c_text = getcpy(dtimenow());
323
324         dat[0] = issue;
325         dat[1] = volume;
326         dat[2] = 0;
327         dat[3] = fmtsize;
328         dat[4] = 0;
329
330         cp = m_mktemp2(NULL, invo_name, NULL, &tmp);
331         if (cp == NULL) adios("forw", "unable to create temporary file");
332         strncpy(tmpfil, cp, sizeof(tmpfil));
333         unlink(tmpfil);
334         if ((in = dup(fileno(tmp))) == NOTOK)
335                 adios("dup", "unable to");
336
337         line = mh_xmalloc((unsigned) fmtsize);
338         fmt_scan(fmt, line, fmtsize, dat);
339         fputs(line, tmp);
340         free(line);
341         if (fclose(tmp))
342                 adios(tmpfil, "error writing");
343
344         lseek(in, (off_t) 0, SEEK_SET);
345         return in;
346 }