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