Removed the split feature of send.
[mmh] / uip / send.c
1 /*
2  * send.c -- send a composed message
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 <errno.h>
12 #include <signal.h>
13
14
15 static struct swit switches[] = {
16 #define ALIASW  0
17         { "alias aliasfile", 0 },
18 #define DEBUGSW  1
19         { "debug", -5 },
20 #define DRAFTSW  2
21         { "draft", 0 },
22 #define DFOLDSW  3
23         { "draftfolder +folder", 6 },
24 #define DMSGSW  4
25         { "draftmessage msg", 6 },
26 #define NDFLDSW  5
27         { "nodraftfolder", 0 },
28 #define FILTSW  6
29         { "filter filterfile", 0 },
30 #define NFILTSW  7
31         { "nofilter", 0 },
32 #define FRMTSW  8
33         { "format", 0 },
34 #define NFRMTSW  9
35         { "noformat", 0 },
36 #define FORWSW  10
37         { "forward", 0 },
38 #define NFORWSW  11
39         { "noforward", 0 },
40 #define MIMESW  12
41         { "mime", 0 },
42 #define NMIMESW  13
43         { "nomime", 0 },
44 #define MSGDSW  14
45         { "msgid", 0 },
46 #define NMSGDSW  15
47         { "nomsgid", 0 },
48 #define PUSHSW  16
49         { "push", 0 },
50 #define NPUSHSW  17
51         { "nopush", 0 },
52 #define UNIQSW  19
53         { "unique", -6 },
54 #define NUNIQSW  20
55         { "nounique", -8 },
56 #define VERBSW  21
57         { "verbose", 0 },
58 #define NVERBSW  22
59         { "noverbose", 0 },
60 #define WATCSW  23
61         { "watch", 0 },
62 #define NWATCSW  24
63         { "nowatch", 0 },
64 #define WIDTHSW  25
65         { "width columns", 0 },
66 #define VERSIONSW  26
67         { "version", 0 },
68 #define HELPSW  27
69         { "help", 0 },
70 #define BITSTUFFSW  28
71         { "dashstuffing", -12 },
72 #define NBITSTUFFSW  29
73         { "nodashstuffing", -14 },
74 #define MAILSW  30
75         { "mail", -4 },
76 #define SAMLSW  31
77         { "saml", -4 },
78 #define SENDSW  32
79         { "send", -4 },
80 #define SOMLSW  33
81         { "soml", -4 },
82 #define CLIESW  34
83         { "client host", -6 },
84 #define SERVSW  35
85         { "server host", 6 },
86 #define SNOOPSW  36
87         { "snoop", 5 },
88 #define ATTACHSW  40
89         { "attach", 6 },
90 #define ATTACHFORMATSW  41
91         { "attachformat", 7 },
92 #define PORTSW  42
93         { "port server-port-name/number" , 4 },
94         { NULL, 0 }
95 };
96
97 static struct swit anyl[] = {
98 #define NOSW  0
99         { "no", 0 },
100 #define YESW  1
101         { "yes", 0 },
102 #define LISTDSW  2
103         { "list", 0 },
104         { NULL, 0 }
105 };
106
107 extern int debugsw;  /* from sendsbr.c */
108 extern int forwsw;
109 extern int inplace;
110 extern int pushsw;
111 extern int unique;
112 extern int verbsw;
113
114 extern char *altmsg;  /*  .. */
115 extern char *annotext;
116 extern char *distfile;
117
118
119 int
120 main (int argc, char **argv)
121 {
122         int msgp = 0, distsw = 0, vecp = 1;
123         int isdf = 0, mime = 0;
124         int msgnum, status;
125         char *cp, *dfolder = NULL, *maildir = NULL;
126         char buf[BUFSIZ], **ap, **argp, **arguments;
127         char *msgs[MAXARGS], *vec[MAXARGS];
128         struct msgs *mp;
129         struct stat st;
130         char *attach = (char *)0;  /* header field name for attachments */
131         int attachformat = 0; /* mhbuild format specifier for attachments */
132 #ifdef UCI
133         FILE *fp;
134 #endif /* UCI */
135
136 #ifdef LOCALE
137         setlocale(LC_ALL, "");
138 #endif
139         invo_name = r1bindex (argv[0], '/');
140
141         /* read user profile/context */
142         context_read();
143
144         arguments = getarguments (invo_name, argc, argv, 1);
145         argp = arguments;
146
147         vec[vecp++] = "-library";
148         vec[vecp++] = getcpy (m_maildir (""));
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                                         adios (NULL, "-%s unknown\n", cp);
158
159                                 case HELPSW:
160                                         snprintf (buf, sizeof(buf), "%s [file] [switches]", invo_name);
161                                         print_help (buf, switches, 1);
162                                         done (1);
163                                 case VERSIONSW:
164                                         print_version(invo_name);
165                                         done (1);
166
167                                 case DRAFTSW:
168                                         msgs[msgp++] = draft;
169                                         continue;
170
171                                 case DFOLDSW:
172                                         if (dfolder)
173                                                 adios (NULL, "only one draft folder at a time!");
174                                         if (!(cp = *argp++) || *cp == '-')
175                                                 adios (NULL, "missing argument to %s", argp[-2]);
176                                         dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
177                                                         *cp != '@' ? TFOLDER : TSUBCWF);
178                                         continue;
179                                 case DMSGSW:
180                                         if (!(cp = *argp++) || *cp == '-')
181                                                 adios (NULL, "missing argument to %s", argp[-2]);
182                                         msgs[msgp++] = cp;
183                                         continue;
184                                 case NDFLDSW:
185                                         dfolder = NULL;
186                                         isdf = NOTOK;
187                                         continue;
188
189                                 case PUSHSW:
190                                         pushsw++;
191                                         continue;
192                                 case NPUSHSW:
193                                         pushsw = 0;
194                                         continue;
195
196                                 case UNIQSW:
197                                         unique++;
198                                         continue;
199                                 case NUNIQSW:
200                                         unique = 0;
201                                         continue;
202
203                                 case FORWSW:
204                                         forwsw++;
205                                         continue;
206                                 case NFORWSW:
207                                         forwsw = 0;
208                                         continue;
209
210                                 case VERBSW:
211                                         verbsw++;
212                                         vec[vecp++] = --cp;
213                                         continue;
214                                 case NVERBSW:
215                                         verbsw = 0;
216                                         vec[vecp++] = --cp;
217                                         continue;
218
219                                 case MIMESW:
220                                         mime++;
221                                         vec[vecp++] = --cp;
222                                         continue;
223                                 case NMIMESW:
224                                         mime = 0;
225                                         vec[vecp++] = --cp;
226                                         continue;
227
228                                 case DEBUGSW:
229                                         debugsw++;  /* fall */
230                                 case NFILTSW:
231                                 case FRMTSW:
232                                 case NFRMTSW:
233                                 case BITSTUFFSW:
234                                 case NBITSTUFFSW:
235                                 case MSGDSW:
236                                 case NMSGDSW:
237                                 case WATCSW:
238                                 case NWATCSW:
239                                 case MAILSW:
240                                 case SAMLSW:
241                                 case SENDSW:
242                                 case SOMLSW:
243                                 case SNOOPSW:
244                                         vec[vecp++] = --cp;
245                                         continue;
246
247                                 case ALIASW:
248                                 case FILTSW:
249                                 case WIDTHSW:
250                                 case CLIESW:
251                                 case SERVSW:
252                                 case PORTSW:
253                                         vec[vecp++] = --cp;
254                                         if (!(cp = *argp++) || *cp == '-')
255                                                 adios (NULL, "missing argument to %s", argp[-2]);
256                                         vec[vecp++] = cp;
257                                         continue;
258
259                                 case ATTACHSW:
260                                         if (!(attach = *argp++) || *attach == '-')
261                                                 adios (NULL, "missing argument to %s", argp[-2]);
262                                         continue;
263
264                                 case ATTACHFORMATSW:
265                                         if (! *argp || **argp == '-')
266                                                 adios (NULL, "missing argument to %s", argp[-1]);
267                                         else {
268                                                 attachformat = atoi (*argp);
269                                                 if (attachformat < 0 ||
270                                                         attachformat > ATTACHFORMATS - 1) {
271                                                         advise (NULL, "unsupported attachformat %d",
272                                                                         attachformat);
273                                                         continue;
274                                                 }
275                                         }
276                                         ++argp;
277                                         continue;
278                         }
279                 } else {
280                         msgs[msgp++] = cp;
281                 }
282         }
283
284         /*
285          * check for "Aliasfile:" profile entry
286          */
287         if ((cp = context_find ("Aliasfile"))) {
288                 char *dp = NULL;
289
290                 for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) {
291                         vec[vecp++] = "-alias";
292                         vec[vecp++] = *ap;
293                 }
294         }
295
296         if (dfolder == NULL) {
297                 if (msgp == 0) {
298 #ifdef WHATNOW
299                         if ((cp = getenv ("mhdraft")) && *cp) {
300                                 msgs[msgp++] = cp;
301                                 goto go_to_it;
302                         }
303 #endif /* WHATNOW */
304                         msgs[msgp++] = getcpy (m_draft (NULL, NULL, 1, &isdf));
305                         if (stat (msgs[0], &st) == NOTOK)
306                                 adios (msgs[0], "unable to stat draft file");
307                         cp = concat ("Use \"", msgs[0], "\"? ", NULL);
308                         for (status = LISTDSW; status != YESW;) {
309                                 if (!(argp = getans (cp, anyl)))
310                                         done (1);
311                                 switch (status = smatch (*argp, anyl)) {
312                                         case NOSW:
313                                                 done (0);
314                                         case YESW:
315                                                 break;
316                                         case LISTDSW:
317                                                 showfile (++argp, msgs[0]);
318                                                 break;
319                                         default:
320                                                 advise (NULL, "say what?");
321                                                 break;
322                                 }
323                         }
324                 } else {
325                         for (msgnum = 0; msgnum < msgp; msgnum++)
326                                 msgs[msgnum] = getcpy (m_maildir (msgs[msgnum]));
327                 }
328         } else {
329                 if (!context_find ("path"))
330                         free (path ("./", TFOLDER));
331
332                 if (!msgp)
333                         msgs[msgp++] = "cur";
334                 maildir = m_maildir (dfolder);
335
336                 if (chdir (maildir) == NOTOK)
337                         adios (maildir, "unable to change directory to");
338
339                 /* read folder and create message structure */
340                 if (!(mp = folder_read (dfolder)))
341                         adios (NULL, "unable to read folder %s", dfolder);
342
343                 /* check for empty folder */
344                 if (mp->nummsg == 0)
345                         adios (NULL, "no messages in %s", dfolder);
346
347                 /* parse all the message ranges/sequences and set SELECTED */
348                 for (msgnum = 0; msgnum < msgp; msgnum++)
349                         if (!m_convert (mp, msgs[msgnum]))
350                                 done (1);
351                 seq_setprev (mp);  /* set the previous-sequence */
352
353                 for (msgp = 0, msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
354                         if (is_selected (mp, msgnum)) {
355                                 msgs[msgp++] = getcpy (m_name (msgnum));
356                                 unset_exists (mp, msgnum);
357                         }
358                 }
359
360                 mp->msgflags |= SEQMOD;
361                 seq_save (mp);
362         }
363
364 #ifdef WHATNOW
365 go_to_it:
366 #endif /* WHATNOW */
367
368         if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
369                 if ((cp = context_find ("signature")) && *cp)
370                         m_putenv ("SIGNATURE", cp);
371 #ifdef UCI
372                 else {
373                         snprintf (buf, sizeof(buf), "%s/.signature", mypath);
374                         if ((fp = fopen (buf, "r")) != NULL
375                                 && fgets (buf, sizeof buf, fp) != NULL) {
376                                         fclose (fp);
377                                         if (cp = strchr (buf, '\n'))
378                                                 *cp = 0;
379                                         m_putenv ("SIGNATURE", buf);
380                         }
381                 }
382 #endif /* UCI */
383
384         for (msgnum = 0; msgnum < msgp; msgnum++)
385                 if (stat (msgs[msgnum], &st) == NOTOK)
386                         adios (msgs[msgnum], "unable to stat draft file");
387
388         if ((annotext = getenv ("mhannotate")) == NULL || *annotext == 0)
389                 annotext = NULL;
390         if (annotext && ((cp = getenv ("mhinplace")) != NULL && *cp != 0))
391                 inplace = atoi (cp);
392         if ((altmsg = getenv ("mhaltmsg")) == NULL || *altmsg == 0)
393                 altmsg = NULL;  /* used by dist interface - see below */
394
395         if ((cp = getenv ("mhdist"))
396                         && *cp
397                         && (distsw = atoi (cp))
398                         && altmsg) {
399                 vec[vecp++] = "-dist";
400                 distfile = getcpy (m_mktemp2 (altmsg, invo_name, NULL, NULL));
401                 if (link (altmsg, distfile) == NOTOK) {
402                         if (errno != EXDEV
403 #ifdef EISREMOTE
404                                         && errno != EISREMOTE
405 #endif /* EISREMOTE */
406                                 )
407                                 adios (distfile, "unable to link %s to", altmsg);
408                         free (distfile);
409                         distfile = getcpy (m_mktemp2(NULL, invo_name, NULL, NULL));
410                         {
411                                 int in, out;
412                                 struct stat st;
413
414                                 if ((in = open (altmsg, O_RDONLY)) == NOTOK)
415                                         adios (altmsg, "unable to open");
416                                 fstat(in, &st);
417                                 if ((out = creat (distfile, (int) st.st_mode & 0777)) == NOTOK)
418                                         adios (distfile, "unable to write");
419                                 cpydata (in, out, altmsg, distfile);
420                                 close (in);
421                                 close (out);
422                         }
423                 }
424         } else {
425                 distfile = NULL;
426         }
427
428         if (altmsg == NULL || stat (altmsg, &st) == NOTOK) {
429                 st.st_mtime = 0;
430                 st.st_dev = 0;
431                 st.st_ino = 0;
432         }
433         if (pushsw)
434                 push ();
435
436         status = 0;
437         vec[0] = r1bindex (postproc, '/');
438         closefds (3);
439
440         for (msgnum = 0; msgnum < msgp; msgnum++) {
441                 switch (sendsbr (vec, vecp, msgs[msgnum], &st, 1, attach,
442                                                  attachformat)) {
443                         case DONE:
444                                 done (++status);
445                         case NOTOK:
446                                 status++;  /* fall */
447                         case OK:
448                                 break;
449                 }
450         }
451
452         context_save ();  /* save the context file */
453         done (status);
454         return 1;
455 }