]> diplodocus.org Git - nmh/blob - uip/send.c
Use va_copy() to get a copy of va_list, instead of using original.
[nmh] / uip / send.c
1 /* send.c -- send a composed message
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 <fcntl.h>
10 #include "h/done.h"
11 #include <h/utils.h>
12 #ifdef OAUTH_SUPPORT
13 # include <h/oauth.h>
14 #endif
15 #include "sbr/m_maildir.h"
16 #include "sbr/m_mktemp.h"
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 #ifndef TLS_SUPPORT
25 # define TLSminc(a) (a)
26 #else /* TLS_SUPPORT */
27 # define TLSminc(a) 0
28 #endif /* TLS_SUPPORT */
29
30 #define SEND_SWITCHES \
31 X("alias aliasfile", 0, ALIASW) \
32 X("debug", -5, DEBUGSW) \
33 X("draft", 0, DRAFTSW) \
34 X("draftfolder +folder", 6, DFOLDSW) \
35 X("draftmessage msg", 6, DMSGSW) \
36 X("nodraftfolder", 0, NDFLDSW) \
37 X("filter filterfile", 0, FILTSW) \
38 X("nofilter", 0, NFILTSW) \
39 X("format", 0, FRMTSW) \
40 X("noformat", 0, NFRMTSW) \
41 X("forward", 0, FORWSW) \
42 X("noforward", 0, NFORWSW) \
43 X("mime", 0, MIMESW) \
44 X("nomime", 0, NMIMESW) \
45 X("msgid", 0, MSGDSW) \
46 X("nomsgid", 0, NMSGDSW) \
47 X("push", 0, PUSHSW) \
48 X("nopush", 0, NPUSHSW) \
49 X("split seconds", 0, SPLITSW) \
50 X("unique", -6, UNIQSW) \
51 X("nounique", -8, NUNIQSW) \
52 X("verbose", 0, VERBSW) \
53 X("noverbose", 0, NVERBSW) \
54 X("watch", 0, WATCSW) \
55 X("nowatch", 0, NWATCSW) \
56 X("width columns", 0, WIDTHSW) \
57 X("version", 0, VERSIONSW) \
58 X("help", 0, HELPSW) \
59 X("dashstuffing", -12, BITSTUFFSW) \
60 X("nodashstuffing", -14, NBITSTUFFSW) \
61 X("client host", -6, CLIESW) \
62 X("server host", 6, SERVSW) \
63 X("snoop", 5, SNOOPSW) \
64 X("sasl", SASLminc(4), SASLSW) \
65 X("nosasl", SASLminc(6), NOSASLSW) \
66 X("saslmech mechanism", SASLminc(6), SASLMECHSW) \
67 X("authservice", SASLminc(0), AUTHSERVICESW) \
68 X("user username", SASLminc(-4), USERSW) \
69 X("port server-port-name/number", 4, PORTSW) \
70 X("tls", TLSminc(-3), TLSSW) \
71 X("initialtls", TLSminc(-10), INITTLSSW) \
72 X("notls", TLSminc(-5), NTLSSW) \
73 X("certverify", TLSminc(-10), CERTVERSW) \
74 X("nocertverify", TLSminc(-12), NOCERTVERSW) \
75 X("sendmail program", 0, MTSSM) \
76 X("mts smtp|sendmail/smtp|sendmail/pipe", 2, MTSSW) \
77 X("messageid localname|random", 2, MESSAGEIDSW) \
78
79 #define X(sw, minchars, id) id,
80 DEFINE_SWITCH_ENUM(SEND);
81 #undef X
82
83 #define X(sw, minchars, id) { sw, minchars, id },
84 DEFINE_SWITCH_ARRAY(SEND, switches);
85 #undef X
86
87 #define USE_SWITCHES \
88 X("no", 0, NOSW) \
89 X("yes", 0, YESW) \
90 X("list", 0, LISTDSW) \
91
92 #define X(sw, minchars, id) id,
93 DEFINE_SWITCH_ENUM(USE);
94 #undef X
95
96 #define X(sw, minchars, id) { sw, minchars, id },
97 DEFINE_SWITCH_ARRAY(USE, anyl);
98 #undef X
99
100 extern int debugsw; /* from sendsbr.c */
101 extern bool forwsw;
102 extern int inplace;
103 extern bool pushsw;
104 extern int splitsw;
105 extern bool unique;
106 extern bool verbsw;
107
108 extern char *altmsg; /* .. */
109 extern char *annotext;
110 extern char *distfile;
111
112
113 int
114 main (int argc, char **argv)
115 {
116 int msgp = 0, vecp;
117 int isdf = 0;
118 int msgnum, status;
119 char *cp, *dfolder = NULL, *maildir = NULL;
120 char buf[BUFSIZ], **ap, **argp, **arguments, *program;
121 char *msgs[MAXARGS], **vec;
122 const char *user = NULL, *saslmech = NULL;
123 struct msgs *mp;
124 struct stat st;
125 char *auth_svc = NULL;
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 vec = argsplit(postproc, &program, &vecp);
133
134 vec[vecp++] = "-library";
135 vec[vecp++] = mh_xstrdup(m_maildir(""));
136
137 if ((cp = context_find ("fileproc"))) {
138 vec[vecp++] = "-fileproc";
139 vec[vecp++] = cp;
140 }
141
142 if ((cp = context_find ("mhlproc"))) {
143 vec[vecp++] = "-mhlproc";
144 vec[vecp++] = cp;
145 }
146
147 if ((cp = context_find ("credentials"))) {
148 /* post doesn't read context so need to pass credentials. */
149 vec[vecp++] = "-credentials";
150 vec[vecp++] = cp;
151 }
152
153 while ((cp = *argp++)) {
154 if (*cp == '-') {
155 switch (smatch (++cp, switches)) {
156 case AMBIGSW:
157 ambigsw (cp, switches);
158 done (1);
159 case UNKWNSW:
160 die("-%s unknown\n", cp);
161
162 case HELPSW:
163 snprintf (buf, sizeof(buf), "%s [file] [switches]", invo_name);
164 print_help (buf, switches, 1);
165 done (0);
166 case VERSIONSW:
167 print_version(invo_name);
168 done (0);
169
170 case DRAFTSW:
171 msgs[msgp++] = draft;
172 continue;
173
174 case DFOLDSW:
175 if (dfolder)
176 die("only one draft folder at a time!");
177 if (!(cp = *argp++) || *cp == '-')
178 die("missing argument to %s", argp[-2]);
179 dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
180 *cp != '@' ? TFOLDER : TSUBCWF);
181 continue;
182 case DMSGSW:
183 if (!(cp = *argp++) || *cp == '-')
184 die("missing argument to %s", argp[-2]);
185 msgs[msgp++] = cp;
186 continue;
187 case NDFLDSW:
188 dfolder = NULL;
189 isdf = NOTOK;
190 continue;
191
192 case PUSHSW:
193 pushsw = true;
194 continue;
195 case NPUSHSW:
196 pushsw = false;
197 continue;
198
199 case SPLITSW:
200 if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1)
201 die("missing argument to %s", argp[-2]);
202 continue;
203
204 case UNIQSW:
205 unique = true;
206 continue;
207 case NUNIQSW:
208 unique = false;
209 continue;
210
211 case FORWSW:
212 forwsw = true;
213 continue;
214 case NFORWSW:
215 forwsw = false;
216 continue;
217
218 case VERBSW:
219 verbsw = true;
220 vec[vecp++] = --cp;
221 continue;
222 case NVERBSW:
223 verbsw = false;
224 vec[vecp++] = --cp;
225 continue;
226
227 case MIMESW:
228 vec[vecp++] = --cp;
229 continue;
230 case NMIMESW:
231 vec[vecp++] = --cp;
232 continue;
233
234 case SNOOPSW:
235 vec[vecp++] = --cp;
236 continue;
237
238 case DEBUGSW:
239 debugsw++;
240 /* FALLTHRU */
241 case NFILTSW:
242 case FRMTSW:
243 case NFRMTSW:
244 case BITSTUFFSW:
245 case NBITSTUFFSW:
246 case MSGDSW:
247 case NMSGDSW:
248 case WATCSW:
249 case NWATCSW:
250 case SASLSW:
251 case NOSASLSW:
252 case TLSSW:
253 case INITTLSSW:
254 case NTLSSW:
255 case CERTVERSW:
256 case NOCERTVERSW:
257 vec[vecp++] = --cp;
258 continue;
259
260 case USERSW:
261 vec[vecp++] = --cp;
262 if (!(cp = *argp++) || *cp == '-')
263 die("missing argument to %s", argp[-2]);
264 vec[vecp++] = cp;
265 user = cp;
266 continue;
267
268 case AUTHSERVICESW:
269 #ifdef OAUTH_SUPPORT
270 if (!(auth_svc = *argp++) || *auth_svc == '-')
271 die("missing argument to %s", argp[-2]);
272 #else
273 die("not built with OAuth support");
274 #endif
275 continue;
276
277 case SASLMECHSW:
278 if (!(saslmech = *argp) || *saslmech == '-')
279 die("missing argument to %s", argp[-1]);
280 /* FALLTHRU */
281
282 case ALIASW:
283 case FILTSW:
284 case WIDTHSW:
285 case CLIESW:
286 case SERVSW:
287 case PORTSW:
288 case MTSSM:
289 case MTSSW:
290 case MESSAGEIDSW:
291 vec[vecp++] = --cp;
292 if (!(cp = *argp++) || *cp == '-')
293 die("missing argument to %s", argp[-2]);
294 vec[vecp++] = cp;
295 continue;
296 }
297 } else {
298 msgs[msgp++] = cp;
299 }
300 }
301
302 /*
303 * check for "Aliasfile:" profile entry
304 */
305 if ((cp = context_find ("Aliasfile"))) {
306 char *dp = NULL;
307
308 for (ap = brkstring(dp = mh_xstrdup(cp), " ", "\n"); ap && *ap; ap++) {
309 vec[vecp++] = "-alias";
310 vec[vecp++] = *ap;
311 }
312 }
313
314 if (dfolder == NULL) {
315 if (msgp == 0) {
316 msgs[msgp++] = mh_xstrdup(m_draft(NULL, NULL, 1, &isdf));
317 if (stat (msgs[0], &st) == NOTOK)
318 adios (msgs[0], "unable to stat draft file");
319 cp = concat ("Use \"", msgs[0], "\"? ", NULL);
320 for (status = LISTDSW; status != YESW;) {
321 if (!(argp = read_switch_multiword (cp, anyl)))
322 done (1);
323 switch (status = smatch (*argp, anyl)) {
324 case NOSW:
325 done (0);
326 case YESW:
327 break;
328 case LISTDSW:
329 showfile (++argp, msgs[0]);
330 break;
331 default:
332 inform("say what?");
333 break;
334 }
335 }
336 } else {
337 for (msgnum = 0; msgnum < msgp; msgnum++)
338 msgs[msgnum] = mh_xstrdup(m_maildir(msgs[msgnum]));
339 }
340 } else {
341 if (!context_find ("path"))
342 free (path ("./", TFOLDER));
343
344 if (!msgp)
345 msgs[msgp++] = "cur";
346 maildir = m_maildir (dfolder);
347
348 if (chdir (maildir) == NOTOK)
349 adios (maildir, "unable to change directory to");
350
351 /* read folder and create message structure */
352 if (!(mp = folder_read (dfolder, 1)))
353 die("unable to read folder %s", dfolder);
354
355 /* check for empty folder */
356 if (mp->nummsg == 0)
357 die("no messages in %s", dfolder);
358
359 /* parse all the message ranges/sequences and set SELECTED */
360 for (msgnum = 0; msgnum < msgp; msgnum++)
361 if (!m_convert (mp, msgs[msgnum]))
362 done (1);
363 seq_setprev (mp); /* set the previous-sequence */
364
365 for (msgp = 0, msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
366 if (is_selected (mp, msgnum)) {
367 msgs[msgp++] = mh_xstrdup(m_name (msgnum));
368 unset_exists (mp, msgnum);
369 }
370 }
371
372 mp->msgflags |= SEQMOD;
373 seq_save (mp);
374 }
375
376 #ifdef WHATNOW
377 go_to_it:
378 #endif /* WHATNOW */
379
380 if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
381 if ((cp = context_find ("signature")) && *cp)
382 setenv("SIGNATURE", cp, 1);
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 && atoi(cp)
398 && altmsg) {
399 vec[vecp++] = "-dist";
400 if ((cp = m_mktemp2(altmsg, invo_name, NULL, NULL)) == NULL) {
401 die("unable to create temporary file");
402 }
403 distfile = mh_xstrdup(cp);
404 (void) m_unlink(distfile);
405 if (link (altmsg, distfile) == NOTOK) {
406 /* Cygwin with FAT32 filesystem produces EPERM. */
407 if (errno != EXDEV && errno != EPERM
408 #ifdef EISREMOTE
409 && errno != EISREMOTE
410 #endif /* EISREMOTE */
411 )
412 adios (distfile, "unable to link %s to", altmsg);
413 free (distfile);
414 if ((cp = m_mktemp2(NULL, invo_name, NULL, NULL)) == NULL) {
415 die("unable to create temporary file in %s",
416 get_temp_dir());
417 }
418 distfile = mh_xstrdup(cp);
419 {
420 int in, out;
421 struct stat st;
422
423 if ((in = open (altmsg, O_RDONLY)) == NOTOK)
424 adios (altmsg, "unable to open");
425 fstat(in, &st);
426 if ((out = creat (distfile, (int) st.st_mode & 0777)) == NOTOK)
427 adios (distfile, "unable to write");
428 cpydata (in, out, altmsg, distfile);
429 close (in);
430 close (out);
431 }
432 }
433 } else {
434 distfile = NULL;
435 }
436
437 #ifdef OAUTH_SUPPORT
438 if (auth_svc == NULL) {
439 if (saslmech && ! strcasecmp(saslmech, "xoauth2")) {
440 die("must specify -authservice with -saslmech xoauth2");
441 }
442 } else {
443 if (user == NULL) {
444 die("must specify -user with -saslmech xoauth2");
445 }
446 }
447 #else
448 NMH_UNUSED(auth_svc);
449 NMH_UNUSED(user);
450 NMH_UNUSED(saslmech);
451 #endif /* OAUTH_SUPPORT */
452
453 if (altmsg == NULL || stat (altmsg, &st) == NOTOK) {
454 st.st_mtime = 0;
455 st.st_dev = 0;
456 st.st_ino = 0;
457 }
458 if (pushsw)
459 push ();
460
461 status = 0;
462 closefds (3);
463
464 for (msgnum = 0; msgnum < msgp; msgnum++) {
465 switch (sendsbr (vec, vecp, program, msgs[msgnum], &st, 1, auth_svc)) {
466 case DONE:
467 done (++status);
468 /* FALLTHRU */
469 case NOTOK:
470 status++;
471 /* FALLTHRU */
472 case OK:
473 break;
474 }
475 }
476
477 context_save (); /* save the context file */
478 done (status);
479 return 1;
480 }