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