]> diplodocus.org Git - nmh/blob - uip/post.c
Added oauth to mhparam examples.
[nmh] / uip / post.c
1 /*
2 * post.c -- enter messages into the mail transport system
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/signals.h>
12 #include <h/addrsbr.h>
13 #include <h/aliasbr.h>
14 #include <h/dropsbr.h>
15 #include <h/mime.h>
16 #include <h/utils.h>
17 #include <h/tws.h>
18 #include <h/mts.h>
19
20 #ifdef HAVE_SYS_TIME_H
21 # include <sys/time.h>
22 #endif
23 #include <time.h>
24
25 #include <mts/smtp/smtp.h>
26
27 #ifndef CYRUS_SASL
28 # define SASLminc(a) (a)
29 #else /* CYRUS_SASL */
30 # define SASLminc(a) 0
31 #endif /* CYRUS_SASL */
32
33 #ifndef TLS_SUPPORT
34 # define TLSminc(a) (a)
35 #else /* TLS_SUPPORT */
36 # define TLSminc(a) 0
37 #endif /* TLS_SUPPORT */
38
39 #ifndef OAUTH_SUPPORT
40 # define OAUTHminc(a) (a)
41 #else /* OAUTH_SUPPORT */
42 # define OAUTHminc(a) 0
43 #endif /* OAUTH_SUPPORT */
44
45 #define FCCS 10 /* max number of fccs allowed */
46
47 /* In the following array of structures, the numeric second field of the
48 structures (minchars) is apparently used like this:
49
50 -# : Switch can be abbreviated to # characters; switch hidden in -help.
51 0 : Switch can't be abbreviated; switch shown in -help.
52 # : Switch can be abbreviated to # characters; switch shown in -help. */
53
54 #define POST_SWITCHES \
55 X("alias aliasfile", 0, ALIASW) \
56 X("check", -5, CHKSW) /* interface from whom */ \
57 X("nocheck", -7, NCHKSW) /* interface from whom */ \
58 X("debug", -5, DEBUGSW) \
59 X("dist", -4, DISTSW) /* interface from dist */ \
60 X("filter filterfile", 0, FILTSW) \
61 X("nofilter", 0, NFILTSW) \
62 X("format", 0, FRMTSW) \
63 X("noformat", 0, NFRMTSW) \
64 X("library directory", -7, LIBSW) /* interface from send, whom */ \
65 X("mime", 0, MIMESW) \
66 X("nomime", 0, NMIMESW) \
67 X("msgid", 0, MSGDSW) \
68 X("nomsgid", 0, NMSGDSW) \
69 X("verbose", 0, VERBSW) \
70 X("noverbose", 0, NVERBSW) \
71 X("watch", 0, WATCSW) \
72 X("nowatch", 0, NWATCSW) \
73 X("whom", -4, WHOMSW) /* interface from whom */ \
74 X("width columns", 0, WIDTHSW) \
75 X("version", 0, VERSIONSW) \
76 X("help", 0, HELPSW) \
77 X("dashstuffing", -12, BITSTUFFSW) /* should we dashstuff BCC messages? */ \
78 X("nodashstuffing", -14, NBITSTUFFSW) \
79 X("idanno number", -6, ANNOSW) /* interface from send */ \
80 X("client host", -6, CLIESW) \
81 X("server host", 6, SERVSW) /* specify alternate SMTP server */ \
82 X("snoop", -5, SNOOPSW) /* snoop the SMTP transaction */ \
83 X("partno", -6, PARTSW) \
84 X("sasl", SASLminc(4), SASLSW) \
85 X("nosasl", SASLminc(6), NOSASLSW) \
86 X("saslmaxssf", SASLminc(10), SASLMXSSFSW) \
87 X("saslmech", SASLminc(5), SASLMECHSW) \
88 X("user", SASLminc(-4), USERSW) \
89 X("port server submission port name/number", 4, PORTSW) \
90 X("tls", TLSminc(-3), TLSSW) \
91 X("initialtls", TLSminc(-10), INITTLSSW) \
92 X("notls", TLSminc(-5), NTLSSW) \
93 X("fileproc", -4, FILEPROCSW) \
94 X("mhlproc", -3, MHLPROCSW) \
95 X("sendmail program", 0, MTSSM) \
96 X("mts smtp|sendmail/smtp|sendmail/pipe", 2, MTSSW) \
97 X("credentials legacy|file:filename", 0, CREDENTIALSSW) \
98 X("messageid localname|random", 2, MESSAGEIDSW) \
99 X("authservice auth-service-name", OAUTHminc(-11), AUTHSERVICESW) \
100 X("oauthcredfile credential-file", OAUTHminc(-7), OAUTHCREDFILESW) \
101 X("oauthclientid client-id", OAUTHminc(-12), OAUTHCLIDSW) \
102 X("oauthclientsecret client-secret", OAUTHminc(-12), OAUTHCLSECSW) \
103 X("oauthauthendpoint authentication-endpoint", OAUTHminc(-6), OAUTHAUTHENDSW) \
104 X("oauthredirect redirect-uri", OAUTHminc(-6), OAUTHREDIRSW) \
105 X("oauthtokenendpoint token-endpoint", OAUTHminc(-6), OAUTHTOKENDSW) \
106 X("oauthscope scope", OAUTHminc(-6), OAUTHSCOPESW) \
107
108 #define X(sw, minchars, id) id,
109 DEFINE_SWITCH_ENUM(POST);
110 #undef X
111
112 #define X(sw, minchars, id) { sw, minchars, id },
113 DEFINE_SWITCH_ARRAY(POST, switches);
114 #undef X
115
116
117 /*
118 * Mapping between command-line switches and profile entries, communicated
119 * from 'send'. We use a service name of 'post' internally.
120 */
121
122 static struct oauth_profile {
123 const char *profname;
124 int switchnum;
125 const char *value;
126 } oauthswitches[] = {
127 { "oauth-%s-credential-file", OAUTHCREDFILESW, NULL },
128 { "oauth-%s-client_id", OAUTHCLIDSW, NULL },
129 { "oauth-%s-client_secret", OAUTHCLSECSW, NULL },
130 { "oauth-%s-auth_endpoint", OAUTHAUTHENDSW, NULL },
131 { "oauth-%s-redirect_uri", OAUTHREDIRSW, NULL },
132 { "oauth-%s-token_endpoint", OAUTHTOKENDSW, NULL },
133 { "oauth-%s-scope", OAUTHSCOPESW, NULL },
134 { NULL, 0, NULL }
135 };
136
137 struct headers {
138 char *value;
139 unsigned int flags;
140 unsigned int set;
141 };
142
143 /*
144 * flags for headers->flags
145 */
146 #define HNOP 0x0000 /* just used to keep .set around */
147 #define HBAD 0x0001 /* bad header - don't let it through */
148 #define HADR 0x0002 /* header has an address field */
149 #define HSUB 0x0004 /* Subject: header */
150 #define HTRY 0x0008 /* try to send to addrs on header */
151 #define HBCC 0x0010 /* don't output this header, unless MTS_SENDMAIL_PIPE */
152 #define HMNG 0x0020 /* munge this header */
153 #define HNGR 0x0040 /* no groups allowed in this header */
154 #define HFCC 0x0080 /* FCC: type header */
155 #define HNIL 0x0100 /* okay for this header not to have addrs */
156 #define HIGN 0x0200 /* ignore this header */
157 #define HDCC 0x0400 /* another undocumented feature */
158 #define HONE 0x0800 /* Only (zero or) one address allowed */
159 #define HEFM 0x1000 /* Envelope-From: header */
160
161 /*
162 * flags for headers->set
163 */
164 #define MFRM 0x0001 /* we've seen a From: */
165 #define MDAT 0x0002 /* we've seen a Date: */
166 #define MRFM 0x0004 /* we've seen a Resent-From: */
167 #define MVIS 0x0008 /* we've seen sighted addrs */
168 #define MINV 0x0010 /* we've seen blind addrs */
169 #define MSND 0x0020 /* we've seen a Sender: */
170 #define MRSN 0x0040 /* We've seen a Resent-Sendr:*/
171 #define MEFM 0x0080 /* We've seen Envelope-From: */
172
173
174 static struct headers NHeaders[] = {
175 { "Return-Path", HBAD, 0 },
176 { "Received", HBAD, 0 },
177 { "Reply-To", HADR|HNGR, 0 },
178 { "From", HADR|HNGR, MFRM },
179 { "Sender", HADR|HNGR|HONE, MSND },
180 { "Date", HBAD, 0 },
181 { "Subject", HSUB, 0 },
182 { "To", HADR|HTRY, MVIS },
183 { "cc", HADR|HTRY, MVIS },
184 { "Bcc", HADR|HTRY|HBCC|HNIL, MINV },
185 { "Dcc", HADR|HTRY|HDCC|HNIL, MVIS }, /* sorta cc & bcc combined */
186 { "Message-ID", HBAD, 0 },
187 { "Fcc", HFCC, 0 },
188 { "Envelope-From", HADR|HONE|HEFM, MEFM },
189 { NULL, 0, 0 }
190 };
191
192 static struct headers RHeaders[] = {
193 { "Resent-Reply-To", HADR|HNGR, 0 },
194 { "Resent-From", HADR|HNGR, MRFM },
195 { "Resent-Sender", HADR|HNGR, MRSN },
196 { "Resent-Date", HBAD, 0 },
197 { "Resent-Subject", HSUB, 0 },
198 { "Resent-To", HADR|HTRY, MVIS },
199 { "Resent-cc", HADR|HTRY, MVIS },
200 { "Resent-Bcc", HADR|HTRY|HBCC, MINV },
201 { "Resent-Message-ID", HBAD, 0 },
202 { "Resent-Fcc", HFCC, 0 },
203 { "Reply-To", HADR, 0 },
204 { "From", HADR|HNGR, MFRM },
205 { "Sender", HADR|HNGR, MSND },
206 { "Date", HNOP, MDAT },
207 { "To", HADR|HNIL, 0 },
208 { "cc", HADR|HNIL, 0 },
209 { "Bcc", HADR|HTRY|HBCC|HNIL, 0 },
210 { "Fcc", HIGN, 0 },
211 { "Envelope-From", HADR|HONE|HEFM, MEFM },
212 { NULL, 0, 0 }
213 };
214
215 static short fccind = 0; /* index into fccfold[] */
216 static short outputlinelen = OUTPUTLINELEN;
217
218 static int pfd = NOTOK; /* fd to write annotation list to */
219 static int recipients = 0; /* how many people will get a copy */
220 static int unkadr = 0; /* how many of those were unknown */
221 static int badadr = 0; /* number of bad addrs */
222 static int badmsg = 0; /* message has bad semantics */
223 static int verbose = 0; /* spell it out */
224 static int format = 1; /* format addresses */
225 static int mime = 0; /* use MIME-style encapsulations for Bcc */
226 static int msgid = 0; /* add msgid */
227 static int debug = 0; /* debugging post */
228 static int watch = 0; /* watch the delivery process */
229 static int whomsw = 0; /* we are whom not post */
230 static int checksw = 0; /* whom -check */
231 static int linepos=0; /* putadr()'s position on the line */
232 static int nameoutput=0; /* putadr() has output header name */
233 static int sasl=0; /* Use SASL auth for SMTP */
234 static int saslssf=-1; /* Our maximum SSF for SASL */
235 static char *saslmech=NULL; /* Force use of particular SASL mech */
236 static char *user=NULL; /* Authenticate as this user */
237 static char *port="submission"; /* Name of server port for SMTP submission */
238 static int tls=-1; /* Use TLS for encryption */
239 static int fromcount=0; /* Count of addresses on From: header */
240 static int seensender=0; /* Have we seen a Sender: header? */
241
242 static unsigned msgflags = 0; /* what we've seen */
243
244 #define NORMAL 0
245 #define RESENT 1
246 static int msgstate = NORMAL;
247
248 static time_t tclock = 0; /* the time we started (more or less) */
249
250 static SIGNAL_HANDLER hstat, istat, qstat, tstat;
251
252 static char tmpfil[BUFSIZ];
253 static char bccfil[BUFSIZ];
254
255 static char from[BUFSIZ]; /* my network address */
256 static char sender[BUFSIZ]; /* my Sender: header */
257 static char efrom[BUFSIZ]; /* my Envelope-From: header */
258 static char fullfrom[BUFSIZ]; /* full contents of From header */
259 static char *filter = NULL; /* the filter for BCC'ing */
260 static char *subject = NULL; /* the subject field for BCC'ing */
261 static char *fccfold[FCCS]; /* foldernames for FCC'ing */
262
263 static struct headers *hdrtab; /* table for the message we're doing */
264
265 static struct mailname localaddrs; /* local addrs */
266 static struct mailname netaddrs; /* network addrs */
267 static struct mailname uuaddrs; /* uucp addrs */
268 static struct mailname tmpaddrs; /* temporary queue */
269
270 static int snoop = 0;
271 static char *clientsw = NULL;
272 static char *serversw = NULL;
273
274 static char prefix[] = "----- =_aaaaaaaaaa";
275
276 static char *partno = NULL;
277
278 /*
279 * static prototypes
280 */
281 static void putfmt (char *, char *, FILE *);
282 static void start_headers (void);
283 static void finish_headers (FILE *);
284 static int get_header (char *, struct headers *);
285 static int putadr (char *, char *, struct mailname *, FILE *, unsigned int);
286 static void putgrp (char *, char *, FILE *, unsigned int);
287 static int insert (struct mailname *);
288 static void pl (void);
289 static void anno (void);
290 static int annoaux (struct mailname *);
291 static void insert_fcc (struct headers *, char *);
292 static void make_bcc_file (int);
293 static void verify_all_addresses (int, char *, int, char *);
294 static void chkadr (void);
295 static void sigon (void);
296 static void sigoff (void);
297 static void p_refile (char *);
298 static void fcc (char *, char *);
299 static void die (char *, char *, ...);
300 static void post (char *, int, int, char *, int, char *);
301 static void do_text (char *file, int fd);
302 static void do_an_address (struct mailname *, int);
303 static void do_addresses (int, int);
304 static int find_prefix (void);
305
306
307 int
308 main (int argc, char **argv)
309 {
310 int state, compnum, dashstuff = 0, swnum, oauth_flag = 0;
311 char *cp, *msg = NULL, **argp, **arguments, *envelope;
312 char buf[BUFSIZ], name[NAMESZ], *auth_svc = NULL;
313 FILE *in, *out;
314 m_getfld_state_t gstate = 0;
315
316 if (nmh_init(argv[0], 0 /* use context_foil() */)) { return 1; }
317
318 mts_init ();
319 arguments = getarguments (invo_name, argc, argv, 0);
320 argp = arguments;
321
322 while ((cp = *argp++)) {
323 if (*cp == '-') {
324 switch ((swnum = smatch (++cp, switches))) {
325 case AMBIGSW:
326 ambigsw (cp, switches);
327 done (1);
328 case UNKWNSW:
329 adios (NULL, "-%s unknown", cp);
330
331 case HELPSW:
332 snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
333 print_help (buf, switches, 0);
334 done (0);
335 case VERSIONSW:
336 print_version(invo_name);
337 done (0);
338
339 case LIBSW:
340 if (!(cp = *argp++) || *cp == '-')
341 adios (NULL, "missing argument to %s", argp[-2]);
342 /* create a minimal context */
343 if (context_foil (cp) == -1)
344 done (1);
345 continue;
346
347 case ALIASW:
348 if (!(cp = *argp++) || *cp == '-')
349 adios (NULL, "missing argument to %s", argp[-2]);
350 if ((state = alias (cp)) != AK_OK)
351 adios (NULL, "aliasing error in %s - %s",
352 cp, akerror (state));
353 continue;
354
355 case CHKSW:
356 checksw++;
357 continue;
358 case NCHKSW:
359 checksw = 0;
360 continue;
361
362 case DEBUGSW:
363 debug++;
364 continue;
365
366 case DISTSW:
367 msgstate = RESENT;
368 continue;
369
370 case FILTSW:
371 if (!(filter = *argp++) || *filter == '-')
372 adios (NULL, "missing argument to %s", argp[-2]);
373 mime = 0;
374 continue;
375 case NFILTSW:
376 filter = NULL;
377 continue;
378
379 case FRMTSW:
380 format++;
381 continue;
382 case NFRMTSW:
383 format = 0;
384 continue;
385
386 case BITSTUFFSW:
387 dashstuff = 1;
388 continue;
389 case NBITSTUFFSW:
390 dashstuff = -1;
391 continue;
392
393 case MIMESW:
394 mime++;
395 filter = NULL;
396 continue;
397 case NMIMESW:
398 mime = 0;
399 continue;
400
401 case MSGDSW:
402 msgid++;
403 continue;
404 case NMSGDSW:
405 msgid = 0;
406 continue;
407
408 case VERBSW:
409 verbose++;
410 continue;
411 case NVERBSW:
412 verbose = 0;
413 continue;
414
415 case WATCSW:
416 watch++;
417 continue;
418 case NWATCSW:
419 watch = 0;
420 continue;
421
422 case WHOMSW:
423 whomsw++;
424 continue;
425
426 case WIDTHSW:
427 if (!(cp = *argp++) || *cp == '-')
428 adios (NULL, "missing argument to %s", argp[-2]);
429 if ((outputlinelen = atoi (cp)) < 10)
430 adios (NULL, "impossible width %d", outputlinelen);
431 continue;
432
433 case ANNOSW:
434 if (!(cp = *argp++) || *cp == '-')
435 adios (NULL, "missing argument to %s", argp[-2]);
436 if ((pfd = atoi (cp)) <= 2)
437 adios (NULL, "bad argument %s %s", argp[-2], cp);
438 continue;
439
440 case CLIESW:
441 if (!(clientsw = *argp++) || *clientsw == '-')
442 adios (NULL, "missing argument to %s", argp[-2]);
443 continue;
444 case SERVSW:
445 if (!(serversw = *argp++) || *serversw == '-')
446 adios (NULL, "missing argument to %s", argp[-2]);
447 continue;
448 case SNOOPSW:
449 snoop++;
450 continue;
451
452 case PARTSW:
453 if (!(partno = *argp++) || *partno == '-')
454 adios (NULL, "missing argument to %s", argp[-2]);
455 continue;
456
457 case SASLSW:
458 sasl++;
459 continue;
460
461 case NOSASLSW:
462 sasl = 0;
463 continue;
464
465 case SASLMXSSFSW:
466 if (!(cp = *argp++) || *cp == '-')
467 adios (NULL, "missing argument to %s", argp[-2]);
468 saslssf = atoi(cp);
469 continue;
470
471 case SASLMECHSW:
472 if (!(saslmech = *argp++) || *saslmech == '-')
473 adios (NULL, "missing argument to %s", argp[-2]);
474 continue;
475
476 case AUTHSERVICESW:
477 if (!(auth_svc = *argp++) || *auth_svc == '-')
478 adios (NULL, "missing argument to %s", argp[-2]);
479 oauth_flag++;
480 continue;
481
482 case OAUTHCREDFILESW:
483 case OAUTHCLIDSW:
484 case OAUTHCLSECSW:
485 case OAUTHAUTHENDSW:
486 case OAUTHREDIRSW:
487 case OAUTHTOKENDSW:
488 case OAUTHSCOPESW:
489 {
490 int i;
491
492 if (!(cp = *argp++) || *cp == '-')
493 adios (NULL, "missing argument to %s", argp[-2]);
494
495 for (i = 0; oauthswitches[i].profname != NULL; i++) {
496 if (oauthswitches[i].switchnum == swnum) {
497 oauthswitches[i].value = cp;
498 break;
499 }
500 }
501
502 if (oauthswitches[i].profname == NULL)
503 adios (NULL, "internal error: cannot map switch %s "
504 "to profile entry", argp[-2]);
505
506 oauth_flag++;
507 continue;
508 }
509
510 case USERSW:
511 if (!(user = *argp++) || *user == '-')
512 adios (NULL, "missing argument to %s", argp[-2]);
513 continue;
514
515 case PORTSW:
516 if (!(port = *argp++) || *port == '-')
517 adios (NULL, "missing argument to %s", argp[-2]);
518 continue;
519
520 case TLSSW:
521 tls = 1;
522 continue;
523
524 case INITTLSSW:
525 tls = 2;
526 continue;
527
528 case NTLSSW:
529 tls = 0;
530 continue;
531
532 case FILEPROCSW:
533 if (!(cp = *argp++) || *cp == '-')
534 adios (NULL, "missing argument to %s", argp[-2]);
535 fileproc = cp;
536 continue;
537
538 case MHLPROCSW:
539 if (!(cp = *argp++) || *cp == '-')
540 adios (NULL, "missing argument to %s", argp[-2]);
541 mhlproc = cp;
542 continue;
543
544 case MTSSM:
545 if (!(cp = *argp++) || *cp == '-')
546 adios (NULL, "missing argument to %s", argp[-2]);
547 sendmail = cp;
548 continue;
549
550 case MTSSW:
551 if (!(cp = *argp++) || *cp == '-')
552 adios (NULL, "missing argument to %s", argp[-2]);
553 save_mts_method (cp);
554 continue;
555
556 case CREDENTIALSSW: {
557 if (!(cp = *argp++) || *cp == '-')
558 adios (NULL, "missing argument to %s", argp[-2]);
559 add_profile_entry ("credentials", cp);
560 continue;
561 }
562
563 case MESSAGEIDSW:
564 if (!(cp = *argp++) || *cp == '-')
565 adios (NULL, "missing argument to %s", argp[-2]);
566 if (save_message_id_style (cp) != 0)
567 adios (NULL, "unsupported messageid \"%s\"", cp);
568 continue;
569 }
570 }
571 if (msg)
572 adios (NULL, "only one message at a time!");
573 else
574 msg = cp;
575 }
576
577 alias (AliasFile);
578
579 if (!msg)
580 adios (NULL, "usage: %s [switches] file", invo_name);
581
582 if (outputlinelen < 10)
583 adios (NULL, "impossible width %d", outputlinelen);
584
585 if ((in = fopen (msg, "r")) == NULL)
586 adios (msg, "unable to open");
587
588 start_headers ();
589 if (debug) {
590 verbose++;
591 out = stdout;
592 } else {
593 if (whomsw) {
594 if ((out = fopen ("/dev/null", "w")) == NULL)
595 adios ("/dev/null", "unable to open");
596 } else {
597 char *cp = m_mktemp2(NULL, invo_name, NULL, &out);
598 if (cp == NULL) {
599 adios(NULL, "unable to create temporary file in %s",
600 get_temp_dir());
601 }
602 strncpy(tmpfil, cp, sizeof(tmpfil));
603 }
604 }
605
606 hdrtab = msgstate == NORMAL ? NHeaders : RHeaders;
607
608 for (compnum = 1;;) {
609 int bufsz = sizeof buf;
610 switch (state = m_getfld (&gstate, name, buf, &bufsz, in)) {
611 case FLD:
612 case FLDPLUS:
613 compnum++;
614 cp = add (buf, NULL);
615 while (state == FLDPLUS) {
616 bufsz = sizeof buf;
617 state = m_getfld (&gstate, name, buf, &bufsz, in);
618 cp = add (buf, cp);
619 }
620 putfmt (name, cp, out);
621 free (cp);
622 continue;
623
624 case BODY:
625 finish_headers (out);
626 if (whomsw)
627 break;
628 fprintf (out, "\n%s", buf);
629 while (state == BODY) {
630 bufsz = sizeof buf;
631 state = m_getfld (&gstate, name, buf, &bufsz, in);
632 fputs (buf, out);
633 }
634 break;
635
636 case FILEEOF:
637 finish_headers (out);
638 break;
639
640 case LENERR:
641 case FMTERR:
642 adios (NULL, "message format error in component #%d", compnum);
643
644 default:
645 adios (NULL, "getfld() returned %d", state);
646 }
647 break;
648 }
649 m_getfld_state_destroy (&gstate);
650
651 if (pfd != NOTOK)
652 anno ();
653 fclose (in);
654
655 if (debug) {
656 pl ();
657 done (0);
658 } else {
659 fclose (out);
660 }
661
662 /*
663 * Here's how we decide which address to use as the envelope-from
664 * address for SMTP.
665 *
666 * - If we were given an Envelope-From header, use that.
667 * - If we were given a Sender: address, use that.
668 * - Otherwise, use the address on the From: line
669 */
670
671 if (msgflags & MEFM) {
672 envelope = efrom;
673 } else if (seensender) {
674 envelope = sender;
675 } else {
676 envelope = from;
677 }
678
679 if (tls == -1) {
680 #ifdef TLS_SUPPORT
681 /*
682 * The user didn't specify any of the tls switches. Try to
683 * help them by implying -initialtls if they're using port 465
684 * (smtps, until IANA revoked that registration in 1998).
685 */
686 tls = ! strcmp (port, "465") || ! strcasecmp (port, "smtps")
687 ? 2
688 : 0;
689 #else /* ! TLS_SUPPORT */
690 tls = 0;
691 #endif /* ! TLS_SUPPORT */
692 }
693
694 /*
695 * If we were given any oauth flags, store the appropriate profile
696 * entries and make sure an authservice was given (we have to do this
697 * here because we aren't guaranteed the authservice will be given on
698 * the command line before the other OAuth flags are given).
699 */
700
701 if (oauth_flag) {
702 int i;
703 char sbuf[128];
704
705 if (auth_svc == NULL) {
706 adios(NULL, "No authentication service given with -authservice");
707 }
708
709 for (i = 0; oauthswitches[i].profname != NULL; i++) {
710 if (oauthswitches[i].value != NULL) {
711 snprintf(sbuf, sizeof(sbuf),
712 oauthswitches[i].profname, auth_svc);
713 sbuf[sizeof(sbuf) - 1] = '\0';
714 add_profile_entry(sbuf, oauthswitches[i].value);
715 }
716 }
717 }
718
719 /* If we are doing a "whom" check */
720 if (whomsw) {
721 /* This won't work with MTS_SENDMAIL_PIPE. */
722 verify_all_addresses (1, envelope, oauth_flag, auth_svc);
723 done (0);
724 }
725
726 if (msgflags & MINV) {
727 make_bcc_file (dashstuff);
728 if (msgflags & MVIS) {
729 if (sm_mts != MTS_SENDMAIL_PIPE) {
730 /* It would be nice to have support to call
731 verify_all_addresses with MTS_SENDMAIL_PIPE, but
732 that might require running sendmail as root. Note
733 that spost didn't verify addresses. */
734 verify_all_addresses (verbose, envelope, oauth_flag, auth_svc);
735 }
736 post (tmpfil, 0, verbose, envelope, oauth_flag, auth_svc);
737 }
738 post (bccfil, 1, verbose, envelope, oauth_flag, auth_svc);
739 (void) m_unlink (bccfil);
740 } else {
741 post (tmpfil, 0, isatty (1), envelope, oauth_flag, auth_svc);
742 }
743
744 p_refile (tmpfil);
745 (void) m_unlink (tmpfil);
746
747 if (verbose) {
748 if (partno)
749 printf ("Partial Message #%s Processed\n", partno);
750 else
751 printf ("Message Processed\n");
752 }
753
754 done (0);
755 return 1;
756 }
757
758
759 /*
760 * DRAFT GENERATION
761 */
762
763 static void
764 putfmt (char *name, char *str, FILE *out)
765 {
766 int count, grp, i, keep;
767 char *cp, *pp, *qp;
768 char namep[BUFSIZ], error[BUFSIZ];
769 struct mailname *mp = NULL, *np = NULL;
770 struct headers *hdr;
771
772 while (*str == ' ' || *str == '\t')
773 str++;
774
775 if (msgstate == NORMAL && uprf (name, "resent")) {
776 advise (NULL, "illegal header line -- %s:", name);
777 badmsg++;
778 return;
779 }
780
781 if ((i = get_header (name, hdrtab)) == NOTOK) {
782 if (strncasecmp (name, "nmh-", 4)) {
783 fprintf (out, "%s: %s", name, str);
784 } else {
785 /* Filter out all Nmh-* headers, because Norm asked. They
786 should never have reached this point. Warn about any
787 that are non-empty. */
788 if (strcmp (str, "\n")) {
789 char *newline = strchr (str, '\n');
790 if (newline) *newline = '\0';
791 if (! whomsw) {
792 advise (NULL, "ignoring header line -- %s: %s", name, str);
793 }
794 }
795 }
796
797 return;
798 }
799
800 hdr = &hdrtab[i];
801 if (hdr->flags & HIGN) {
802 return;
803 }
804 if (hdr->flags & HBAD) {
805 advise (NULL, "illegal header line -- %s:", name);
806 badmsg++;
807 return;
808 }
809 msgflags |= (hdr->set & ~(MVIS | MINV));
810
811 if (hdr->flags & HSUB)
812 subject = subject ? add (str, add ("\t", subject)) : getcpy (str);
813 if (hdr->flags & HFCC) {
814 if ((cp = strrchr(str, '\n')))
815 *cp = 0;
816 for (pp = str; (cp = strchr(pp, ',')); pp = cp) {
817 *cp++ = 0;
818 insert_fcc (hdr, pp);
819 }
820 insert_fcc (hdr, pp);
821 return;
822 }
823
824 if (!(hdr->flags & HADR)) {
825 fprintf (out, "%s: %s", name, str);
826 return;
827 }
828
829 tmpaddrs.m_next = NULL;
830 for (count = 0; (cp = getname (str)); count++)
831 if ((mp = getm (cp, NULL, 0, error, sizeof(error)))) {
832 if (tmpaddrs.m_next)
833 np->m_next = mp;
834 else
835 tmpaddrs.m_next = mp;
836 np = mp;
837 }
838 else {
839 admonish(cp, "%s", error);
840 if (hdr->flags & HTRY)
841 badadr++;
842 else
843 badmsg++;
844 }
845
846 if (count < 1) {
847 if (hdr->flags & HNIL)
848 fprintf (out, "%s: %s", name, str);
849 else {
850 /*
851 * Sender (or Resent-Sender) can have only one address
852 */
853 if ((msgstate == RESENT) ? (hdr->set & MRSN)
854 : (hdr->set & MSND)) {
855 advise (NULL, "%s: field requires one address", name);
856 badmsg++;
857 }
858 #ifdef notdef
859 advise (NULL, "%s: field requires at least one address", name);
860 badmsg++;
861 #endif /* notdef */
862 }
863 return;
864 }
865
866 if (count > 1 && (hdr->flags & HONE)) {
867 advise (NULL, "%s: field only permits one address", name);
868 badmsg++;
869 return;
870 }
871
872 nameoutput = linepos = 0;
873 snprintf (namep, sizeof(namep), "%s%s",
874 (hdr->flags & HMNG) ? "Original-" : "", name);
875
876 for (grp = 0, mp = tmpaddrs.m_next; mp; mp = np)
877 if (mp->m_nohost) { /* also used to test (hdr->flags & HTRY) */
878 /* The address doesn't include a host, so it might be an alias. */
879 pp = akvalue (mp->m_mbox); /* do mh alias substitution */
880 qp = akvisible () ? mp->m_mbox : "";
881 np = mp;
882 if (np->m_gname)
883 putgrp (namep, np->m_gname, out, hdr->flags);
884 while ((cp = getname (pp))) {
885 if (!(mp = getm (cp, NULL, 0, error, sizeof(error)))) {
886 admonish(cp, "%s", error);
887 badadr++;
888 continue;
889 }
890
891 /*
892 * If it's a From: or Resent-From: header, save the address
893 * for later possible use (as the envelope address for SMTP)
894 */
895
896 if ((msgstate == RESENT) ? (hdr->set & MRFM)
897 : (hdr->set & MFRM)) {
898 strncpy(from, auxformat(mp, 0), sizeof(from) - 1);
899 from[sizeof(from) - 1] = '\0';
900 fromcount = count;
901 }
902
903 /*
904 * Also save the Sender: or Resent-Sender: header as well
905 */
906
907 if ((msgstate == RESENT) ? (hdr->set & MRSN)
908 : (hdr->set & MSND)) {
909 strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1);
910 sender[sizeof(sender) - 1] = '\0';
911 seensender++;
912 }
913
914 /*
915 * ALSO ... save Envelope-From
916 */
917
918 if (hdr->set & MEFM) {
919 strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1);
920 efrom[sizeof(efrom) - 1] = '\0';
921 }
922
923 if (hdr->flags & HBCC)
924 mp->m_bcc++;
925 if (np->m_ingrp)
926 mp->m_ingrp = np->m_ingrp;
927 else
928 if (mp->m_gname)
929 putgrp (namep, mp->m_gname, out, hdr->flags);
930 if (mp->m_ingrp) {
931 if (sm_mts == MTS_SENDMAIL_PIPE) {
932 /* Catch this before sendmail chokes with:
933 "553 List:; syntax illegal for recipient
934 addresses".
935 If we wanted to, we could expand out blind
936 aliases and put them in Bcc:, but then
937 they'd have the Blind-Carbon-Copy
938 indication. */
939 adios (NULL,
940 "blind lists not compatible with"
941 " sendmail/pipe");
942 }
943
944 grp++;
945 }
946 if (putadr (namep, qp, mp, out, hdr->flags))
947 msgflags |= (hdr->set & (MVIS | MINV));
948 else
949 mnfree (mp);
950 }
951 mp = np;
952 np = np->m_next;
953 mnfree (mp);
954 }
955 else {
956 /* Address includes a host, so no alias substitution is needed. */
957
958 /*
959 * If it's a From: or Resent-From header, save the address
960 * for later possible use (as the envelope address for SMTP)
961 */
962
963 if ((msgstate == RESENT) ? (hdr->set & MRFM)
964 : (hdr->set & MFRM)) {
965 strncpy(from, auxformat(mp, 0), sizeof(from) - 1);
966 fromcount = count;
967 }
968
969 /*
970 * Also save the Sender: header as well
971 */
972
973 if ((msgstate == RESENT) ? (hdr->set & MRSN)
974 : (hdr->set & MSND)) {
975 strncpy(sender, auxformat(mp, 0), sizeof(sender) - 1);
976 sender[sizeof(sender) - 1] = '\0';
977 seensender++;
978 }
979
980 /*
981 * ALSO ... save Envelope-From
982 */
983
984 if (hdr->set & MEFM) {
985 strncpy(efrom, auxformat(mp, 0), sizeof(efrom) - 1);
986 efrom[sizeof(efrom) - 1] = '\0';
987 }
988
989 if (hdr->flags & HBCC)
990 mp->m_bcc++;
991 if (mp->m_gname)
992 putgrp (namep, mp->m_gname, out, hdr->flags);
993 if (mp->m_ingrp)
994 grp++;
995 keep = putadr (namep, "", mp, out, hdr->flags);
996 np = mp->m_next;
997 if (keep) {
998 mp->m_next = NULL;
999 msgflags |= (hdr->set & (MVIS | MINV));
1000 }
1001 else
1002 mnfree (mp);
1003 }
1004
1005 /*
1006 * If this is a From:/Resent-From: header, save the full thing for
1007 * later in case we need it for use when constructing a Bcc draft message
1008 */
1009
1010 if ((msgstate == RESENT) ? (hdr->set & MRFM) : (hdr->set & MFRM)) {
1011 strncpy(fullfrom, str, sizeof(fullfrom));
1012 fullfrom[sizeof(fullfrom) - 1] = 0;
1013 /*
1014 * Strip off any trailing newlines
1015 */
1016
1017 while (strlen(fullfrom) > 0 && fullfrom[strlen(fullfrom) - 1] == '\n') {
1018 fullfrom[strlen(fullfrom) - 1] = '\0';
1019 }
1020 }
1021
1022 if (grp > 0 && (hdr->flags & HNGR)) {
1023 advise (NULL, "%s: field does not allow groups", name);
1024 badmsg++;
1025 }
1026 if (linepos) {
1027 putc ('\n', out);
1028 }
1029 }
1030
1031
1032 static void
1033 start_headers (void)
1034 {
1035 time (&tclock);
1036
1037 /*
1038 * Probably not necessary, but just in case ...
1039 */
1040
1041 from[0] = '\0';
1042 efrom[0] = '\0';
1043 sender[0] = '\0';
1044 fullfrom[0] = '\0';
1045 }
1046
1047
1048 /*
1049 * Now that we've outputted the header fields in the draft
1050 * message, we will now output any remaining header fields
1051 * that we need to add/create.
1052 */
1053
1054 static void
1055 finish_headers (FILE *out)
1056 {
1057 switch (msgstate) {
1058 case NORMAL:
1059 if (!(msgflags & MFRM)) {
1060 /*
1061 * A From: header is now required in the draft.
1062 */
1063 advise (NULL, "message has no From: header");
1064 advise (NULL, "See default components files for examples");
1065 badmsg++;
1066 break;
1067 }
1068
1069 if (fromcount > 1 && (seensender == 0 && !(msgflags & MEFM))) {
1070 advise (NULL, "A Sender: or Envelope-From: header is required "
1071 "with multiple\nFrom: addresses");
1072 badmsg++;
1073 break;
1074 }
1075
1076 if (whomsw)
1077 break;
1078
1079 fprintf (out, "Date: %s\n", dtime (&tclock, 0));
1080 if (msgid)
1081 fprintf (out, "Message-ID: %s\n", message_id (tclock, 0));
1082 /*
1083 * If we have multiple From: addresses, make sure we have an
1084 * Sender: header. If we don't have one, then generate one
1085 * from Envelope-From: (which in this case, cannot be blank)
1086 */
1087
1088 if (fromcount > 1 && seensender == 0) {
1089 if (efrom[0] == '\0') {
1090 advise (NULL, "Envelope-From cannot be blank when there "
1091 "is multiple From: addresses\nand no Sender: "
1092 "header");
1093 badmsg++;
1094 } else {
1095 fprintf (out, "Sender: %s\n", efrom);
1096 }
1097 }
1098
1099 if (!(msgflags & MVIS))
1100 fprintf (out, "Bcc: Blind Distribution List: ;\n");
1101 break;
1102
1103 case RESENT:
1104 if (!(msgflags & MDAT)) {
1105 advise (NULL, "message has no Date: header");
1106 badmsg++;
1107 }
1108 if (!(msgflags & MFRM)) {
1109 advise (NULL, "message has no From: header");
1110 badmsg++;
1111 }
1112 if (!(msgflags & MRFM)) {
1113 advise (NULL, "message has no Resent-From: header");
1114 advise (NULL, "See default components files for examples");
1115 badmsg++;
1116 break;
1117 }
1118 if (fromcount > 1 && (seensender == 0 && !(msgflags & MEFM))) {
1119 advise (NULL, "A Resent-Sender: or Envelope-From: header is "
1120 "required with multiple\nResent-From: addresses");
1121 badmsg++;
1122 break;
1123 }
1124
1125 if (whomsw)
1126 break;
1127
1128 fprintf (out, "Resent-Date: %s\n", dtime (&tclock, 0));
1129 if (msgid)
1130 fprintf (out, "Resent-Message-ID: %s\n",
1131 message_id (tclock, 0));
1132 /*
1133 * If we have multiple Resent-From: addresses, make sure we have an
1134 * Resent-Sender: header. If we don't have one, then generate one
1135 * from Envelope-From (which in this case, cannot be blank)
1136 */
1137
1138 if (fromcount > 1 && seensender == 0) {
1139 if (efrom[0] == '\0') {
1140 advise (NULL, "Envelope-From cannot be blank when there "
1141 "is multiple Resent-From: addresses and no "
1142 "Resent-Sender: header");
1143 badmsg++;
1144 } else {
1145 fprintf (out, "Resent-Sender: %s\n", efrom);
1146 }
1147 }
1148
1149 if (!(msgflags & MVIS))
1150 fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
1151 break;
1152 }
1153
1154 if (badmsg)
1155 adios (NULL, "re-format message and try again");
1156 if (!recipients)
1157 adios (NULL, "no addressees");
1158 }
1159
1160
1161 static int
1162 get_header (char *header, struct headers *table)
1163 {
1164 struct headers *h;
1165
1166 for (h = table; h->value; h++)
1167 if (!strcasecmp (header ? header : "", h->value ? h->value : ""))
1168 return (h - table);
1169
1170 return NOTOK;
1171 }
1172
1173
1174 static int
1175 putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flags)
1176 {
1177 int len;
1178 char *cp;
1179 char buffer[BUFSIZ];
1180
1181 if (mp->m_mbox == NULL || ((flags & HTRY) && !insert (mp)))
1182 return 0;
1183 if (sm_mts != MTS_SENDMAIL_PIPE &&
1184 ((flags & (HBCC | HDCC | HEFM)) || mp->m_ingrp))
1185 return 1;
1186
1187 if (!nameoutput) {
1188 fprintf (out, "%s: ", name);
1189 linepos += (nameoutput = strlen (name) + 2);
1190 }
1191
1192 if (*aka && mp->m_type != UUCPHOST && !mp->m_pers)
1193 mp->m_pers = getcpy (aka);
1194 if (format) {
1195 if (mp->m_gname) {
1196 snprintf (buffer, sizeof(buffer), "%s;", mp->m_gname);
1197 cp = buffer;
1198 } else {
1199 cp = adrformat (mp);
1200 }
1201 } else {
1202 cp = mp->m_text;
1203 }
1204 len = strlen (cp);
1205
1206 if (linepos != nameoutput) {
1207 if (len + linepos + 2 > outputlinelen)
1208 fprintf (out, ",\n%*s", linepos = nameoutput, "");
1209 else {
1210 fputs (", ", out);
1211 linepos += 2;
1212 }
1213 }
1214
1215 fputs (cp, out);
1216 linepos += len;
1217
1218 return (flags & HTRY);
1219 }
1220
1221
1222 static void
1223 putgrp (char *name, char *group, FILE *out, unsigned int flags)
1224 {
1225 int len;
1226 char *cp;
1227
1228 if (sm_mts != MTS_SENDMAIL_PIPE && (flags & HBCC))
1229 return;
1230
1231 if (!nameoutput) {
1232 fprintf (out, "%s: ", name);
1233 linepos += (nameoutput = strlen (name) + 2);
1234 }
1235
1236 cp = concat (group, ";", NULL);
1237 len = strlen (cp);
1238
1239 if (linepos > nameoutput) {
1240 if (len + linepos + 2 > outputlinelen) {
1241 fprintf (out, ",\n%*s", nameoutput, "");
1242 linepos = nameoutput;
1243 }
1244 else {
1245 fputs (", ", out);
1246 linepos += 2;
1247 }
1248 }
1249
1250 fputs (cp, out);
1251 linepos += len;
1252 }
1253
1254
1255 static int
1256 insert (struct mailname *np)
1257 {
1258 struct mailname *mp;
1259
1260 if (np->m_mbox == NULL)
1261 return 0;
1262
1263 for (mp = np->m_type == LOCALHOST ? &localaddrs
1264 : np->m_type == UUCPHOST ? &uuaddrs
1265 : &netaddrs;
1266 mp->m_next;
1267 mp = mp->m_next)
1268 if (!strcasecmp (np->m_host ? np->m_host : "",
1269 mp->m_next->m_host ? mp->m_next->m_host : "") &&
1270 !strcasecmp (np->m_mbox ? np->m_mbox : "",
1271 mp->m_next->m_mbox ? mp->m_next->m_mbox : "") &&
1272 np->m_bcc == mp->m_next->m_bcc)
1273 return 0;
1274
1275 mp->m_next = np;
1276 recipients++;
1277 return 1;
1278 }
1279
1280
1281 static void
1282 pl (void)
1283 {
1284 int i;
1285 struct mailname *mp;
1286
1287 printf ("-------\n\t-- Addresses --\nlocal:\t");
1288 for (mp = localaddrs.m_next; mp; mp = mp->m_next)
1289 printf ("%s%s%s", mp->m_mbox,
1290 mp->m_bcc ? "[BCC]" : "",
1291 mp->m_next ? ",\n\t" : "");
1292
1293 printf ("\nnet:\t");
1294 for (mp = netaddrs.m_next; mp; mp = mp->m_next)
1295 printf ("%s%s@%s%s%s", mp->m_path ? mp->m_path : "",
1296 mp->m_mbox, mp->m_host,
1297 mp->m_bcc ? "[BCC]" : "",
1298 mp->m_next ? ",\n\t" : "");
1299
1300 printf ("\nuucp:\t");
1301 for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
1302 printf ("%s!%s%s%s", mp->m_host, mp->m_mbox,
1303 mp->m_bcc ? "[BCC]" : "",
1304 mp->m_next ? ",\n\t" : "");
1305
1306 printf ("\n\t-- Folder Copies --\nfcc:\t");
1307 for (i = 0; i < fccind; i++)
1308 printf ("%s%s", fccfold[i], i + 1 < fccind ? ",\n\t" : "");
1309 printf ("\n");
1310 }
1311
1312
1313 static void
1314 anno (void)
1315 {
1316 struct mailname *mp;
1317
1318 for (mp = localaddrs.m_next; mp; mp = mp->m_next)
1319 if (annoaux (mp) == NOTOK)
1320 goto oops;
1321
1322 for (mp = netaddrs.m_next; mp; mp = mp->m_next)
1323 if (annoaux (mp) == NOTOK)
1324 goto oops;
1325
1326 for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
1327 if (annoaux (mp) == NOTOK)
1328 break;
1329
1330 oops: ;
1331 close (pfd);
1332 pfd = NOTOK;
1333 }
1334
1335
1336 static int
1337 annoaux (struct mailname *mp)
1338 {
1339 int i;
1340 char buffer[BUFSIZ];
1341
1342 snprintf (buffer, sizeof(buffer), "%s\n", adrformat (mp));
1343 i = strlen (buffer);
1344
1345 return (write (pfd, buffer, i) == i ? OK : NOTOK);
1346 }
1347
1348
1349 static void
1350 insert_fcc (struct headers *hdr, char *pp)
1351 {
1352 char *cp;
1353
1354 for (cp = pp; isspace ((unsigned char) *cp); cp++)
1355 continue;
1356 for (pp += strlen (pp) - 1; pp > cp && isspace ((unsigned char) *pp); pp--)
1357 continue;
1358 if (pp >= cp)
1359 *++pp = 0;
1360 if (*cp == 0)
1361 return;
1362
1363 if (fccind >= FCCS)
1364 adios (NULL, "too many %ss", hdr->value);
1365 fccfold[fccind++] = getcpy (cp);
1366 }
1367
1368 /*
1369 * BCC GENERATION
1370 */
1371
1372 static void
1373 make_bcc_file (int dashstuff)
1374 {
1375 int fd, i;
1376 pid_t child_id;
1377 char **vec;
1378 FILE *out;
1379 char *tfile = NULL, *program;
1380
1381 if ((tfile = m_mktemp2(NULL, "bccs", NULL, &out)) == NULL) {
1382 adios(NULL, "unable to create temporary file in %s", get_temp_dir());
1383 }
1384 strncpy (bccfil, tfile, sizeof(bccfil));
1385
1386 fprintf (out, "From: %s\n", fullfrom);
1387 fprintf (out, "Date: %s\n", dtime (&tclock, 0));
1388 if (msgid)
1389 fprintf (out, "Message-ID: %s\n", message_id (tclock, 0));
1390 if (subject)
1391 fprintf (out, "Subject: %s", subject);
1392 fprintf (out, "BCC:\n");
1393
1394 /*
1395 * Use MIME encapsulation for Bcc messages
1396 */
1397 if (mime) {
1398 char *cp;
1399
1400 /*
1401 * Check if any lines in the message clash with the
1402 * prefix for the MIME multipart separator. If there
1403 * is a clash, increment one of the letters in the
1404 * prefix and check again.
1405 */
1406 if ((cp = strchr(prefix, 'a')) == NULL)
1407 adios (NULL, "lost prefix start");
1408 while (find_prefix () == NOTOK) {
1409 if (*cp < 'z')
1410 (*cp)++;
1411 else
1412 if (*++cp == 0)
1413 adios (NULL, "can't find a unique delimiter string");
1414 else
1415 (*cp)++;
1416 }
1417
1418 fprintf (out, "%s: %s\n%s: multipart/digest; boundary=\"",
1419 VRSN_FIELD, VRSN_VALUE, TYPE_FIELD);
1420 fprintf (out, "%s\"\n\n--%s\n\n", prefix, prefix);
1421 } else {
1422 fprintf (out, "\n------- Blind-Carbon-Copy\n\n");
1423 }
1424
1425 fflush (out);
1426
1427 /*
1428 * Do mhl filtering of Bcc messages instead
1429 * of MIME encapsulation.
1430 */
1431 if (filter != NULL) {
1432 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
1433 sleep (5);
1434 switch (child_id) {
1435 case NOTOK:
1436 adios ("fork", "unable to");
1437
1438 case OK:
1439 dup2 (fileno (out), 1);
1440
1441 vec = argsplit(mhlproc, &program, &i);
1442 vec[i++] = "-forward";
1443 vec[i++] = "-form";
1444 vec[i++] = filter;
1445 vec[i++] = tmpfil;
1446
1447 /* was the flag -[no]dashstuffing specified? */
1448 if (dashstuff > 0)
1449 vec[i++] = "-dashstuffing";
1450 else if (dashstuff < 0)
1451 vec[i++] = "-nodashstuffing";
1452 vec[i] = NULL;
1453
1454 execvp (program, vec);
1455 fprintf (stderr, "unable to exec ");
1456 perror (mhlproc);
1457 _exit (-1);
1458
1459 default:
1460 pidXwait (child_id, mhlproc);
1461 break;
1462 }
1463 } else {
1464 if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
1465 adios (tmpfil, "unable to re-open");
1466
1467 /*
1468 * If using MIME encapsulation, or if the -nodashstuffing
1469 * flag was given, then just copy message. Else do
1470 * RFC934 quoting (dashstuffing).
1471 */
1472 if (mime || dashstuff < 0)
1473 cpydata (fd, fileno (out), tmpfil, bccfil);
1474 else
1475 cpydgst (fd, fileno (out), tmpfil, bccfil);
1476 close (fd);
1477 }
1478
1479 fseek (out, 0L, SEEK_END);
1480 if (mime)
1481 fprintf (out, "\n--%s--\n", prefix);
1482 else
1483 fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
1484 fclose (out);
1485 }
1486
1487
1488 /*
1489 * Scan message to check if any lines clash with
1490 * the prefix of the MIME multipart separator.
1491 */
1492
1493 static int
1494 find_prefix (void)
1495 {
1496 int result = OK;
1497 char buffer[BUFSIZ];
1498 FILE *in;
1499
1500 if ((in = fopen (tmpfil, "r")) == NULL)
1501 adios (tmpfil, "unable to re-open");
1502
1503 while (fgets (buffer, sizeof(buffer) - 1, in))
1504 if (buffer[0] == '-' && buffer[1] == '-') {
1505 char *cp;
1506
1507 for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1508 if (!isspace ((unsigned char) *cp))
1509 break;
1510 *++cp = '\0';
1511 if (strcmp (buffer + 2, prefix) == 0) {
1512 result = NOTOK;
1513 break;
1514 }
1515 }
1516
1517 fclose (in);
1518 return result;
1519 }
1520
1521
1522 #define plural(x) (x == 1 ? "" : "s")
1523
1524 static void
1525 chkadr (void)
1526 {
1527 if (badadr && unkadr)
1528 die (NULL, "%d address%s unparsable, %d addressee%s undeliverable",
1529 badadr, plural (badadr), unkadr, plural (badadr));
1530 if (badadr)
1531 die (NULL, "%d address%s unparsable", badadr, plural (badadr));
1532 if (unkadr)
1533 die (NULL, "%d addressee%s undeliverable", unkadr, plural (unkadr));
1534 }
1535
1536
1537 static void
1538 do_addresses (int bccque, int talk)
1539 {
1540 int retval;
1541 int state;
1542 struct mailname *lp;
1543
1544 state = 0;
1545 for (lp = localaddrs.m_next; lp; lp = lp->m_next)
1546 if (lp->m_bcc ? bccque : !bccque) {
1547 if (talk && !state)
1548 printf (" -- Local Recipients --\n");
1549 do_an_address (lp, talk);
1550 state++;
1551 }
1552
1553 state = 0;
1554 for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
1555 if (lp->m_bcc ? bccque : !bccque) {
1556 if (talk && !state)
1557 printf (" -- UUCP Recipients --\n");
1558 do_an_address (lp, talk);
1559 state++;
1560 }
1561
1562 state = 0;
1563 for (lp = netaddrs.m_next; lp; lp = lp->m_next)
1564 if (lp->m_bcc ? bccque : !bccque) {
1565 if (talk && !state)
1566 printf (" -- Network Recipients --\n");
1567 do_an_address (lp, talk);
1568 state++;
1569 }
1570
1571 chkadr ();
1572
1573 if (rp_isbad (retval = sm_waend ()))
1574 die (NULL, "problem ending addresses; %s", rp_string (retval));
1575 }
1576
1577
1578 /*
1579 * MTS-SPECIFIC INTERACTION
1580 */
1581
1582
1583 /*
1584 * SENDMAIL/SMTP routines
1585 */
1586
1587 static void
1588 post (char *file, int bccque, int talk, char *envelope, int oauth_flag,
1589 char *auth_svc)
1590 {
1591 int fd;
1592 int retval, i;
1593 pid_t child_id;
1594
1595 if (verbose) {
1596 if (msgflags & MINV)
1597 printf (" -- Posting for %s Recipients --\n",
1598 bccque ? "Blind" : "Sighted");
1599 else
1600 printf (" -- Posting for All Recipients --\n");
1601 }
1602
1603 sigon ();
1604
1605 if (sm_mts == MTS_SENDMAIL_PIPE) {
1606 char **argp, *program;
1607 int argc;
1608
1609 for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
1610 sleep (5);
1611 switch (child_id) {
1612 case NOTOK:
1613 adios ("fork", "unable to");
1614
1615 case OK:
1616 if (freopen( file, "r", stdin) == NULL) {
1617 adios (file, "can't reopen for sendmail");
1618 }
1619
1620 argp = argsplit(sendmail, &program, &argc);
1621 argp[argc++] = "-t"; /* read msg for recipients */
1622 argp[argc++] = "-i"; /* don't stop on "." */
1623 if (whomsw)
1624 argp[argc++] = "-bv";
1625 if (snoop)
1626 argp[argc++] = "-v";
1627 argp[argc] = NULL;
1628
1629 execv (program, argp);
1630 adios (sendmail, "can't exec");
1631
1632 default:
1633 pidXwait (child_id, NULL);
1634 break;
1635 }
1636 } else {
1637 if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch,
1638 verbose, snoop, sasl, saslssf,
1639 saslmech, user,
1640 oauth_flag ? auth_svc : NULL, tls))
1641 || rp_isbad (retval = sm_winit (envelope)))
1642 die (NULL, "problem initializing server; %s", rp_string (retval));
1643
1644 do_addresses (bccque, talk && verbose);
1645 if ((fd = open (file, O_RDONLY)) == NOTOK)
1646 die (file, "unable to re-open");
1647 do_text (file, fd);
1648 close (fd);
1649 fflush (stdout);
1650
1651 sm_end (!(msgflags & MINV) || bccque ? OK : DONE);
1652 sigoff ();
1653
1654 if (verbose) {
1655 if (msgflags & MINV)
1656 printf (" -- %s Recipient Copies Posted --\n",
1657 bccque ? "Blind" : "Sighted");
1658 else
1659 printf (" -- Recipient Copies Posted --\n");
1660 }
1661
1662 fflush (stdout);
1663 }
1664 }
1665
1666
1667 /* Address Verification */
1668
1669 static void
1670 verify_all_addresses (int talk, char *envelope, int oauth_flag, char *auth_svc)
1671 {
1672 int retval;
1673 struct mailname *lp;
1674
1675 sigon ();
1676
1677 if (!whomsw || checksw)
1678 if (rp_isbad (retval = sm_init (clientsw, serversw, port, watch,
1679 verbose, snoop, sasl, saslssf,
1680 saslmech, user,
1681 oauth_flag ? auth_svc : NULL, tls))
1682 || rp_isbad (retval = sm_winit (envelope)))
1683 die (NULL, "problem initializing server; %s", rp_string (retval));
1684
1685 if (talk && !whomsw)
1686 printf (" -- Address Verification --\n");
1687 if (talk && localaddrs.m_next)
1688 printf (" -- Local Recipients --\n");
1689 for (lp = localaddrs.m_next; lp; lp = lp->m_next)
1690 do_an_address (lp, talk);
1691
1692 if (talk && uuaddrs.m_next)
1693 printf (" -- UUCP Recipients --\n");
1694 for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
1695 do_an_address (lp, talk);
1696
1697 if (talk && netaddrs.m_next)
1698 printf (" -- Network Recipients --\n");
1699 for (lp = netaddrs.m_next; lp; lp = lp->m_next)
1700 do_an_address (lp, talk);
1701
1702 chkadr ();
1703 if (talk && !whomsw)
1704 printf (" -- Address Verification Successful --\n");
1705
1706 if (!whomsw || checksw)
1707 sm_end (DONE);
1708
1709 fflush (stdout);
1710 sigoff ();
1711 }
1712
1713
1714 static void
1715 do_an_address (struct mailname *lp, int talk)
1716 {
1717 int retval;
1718 char *mbox, *host;
1719 char addr[BUFSIZ];
1720
1721 switch (lp->m_type) {
1722 case LOCALHOST:
1723 mbox = lp->m_mbox;
1724 host = lp->m_host;
1725 strncpy (addr, mbox, sizeof(addr));
1726 break;
1727
1728 case UUCPHOST:
1729 mbox = auxformat (lp, 0);
1730 host = NULL;
1731 snprintf (addr, sizeof(addr), "%s!%s", lp->m_host, lp->m_mbox);
1732 break;
1733
1734 default: /* let SendMail decide if the host is bad */
1735 mbox = lp->m_mbox;
1736 host = lp->m_host;
1737 snprintf (addr, sizeof(addr), "%s at %s", mbox, host);
1738 break;
1739 }
1740
1741 if (talk)
1742 printf (" %s%s", addr, whomsw && lp->m_bcc ? "[BCC]" : "");
1743
1744 if (whomsw && !checksw) {
1745 putchar ('\n');
1746 return;
1747 }
1748 if (talk)
1749 printf (": ");
1750 fflush (stdout);
1751
1752 switch (retval = sm_wadr (mbox, host,
1753 lp->m_type != UUCPHOST ? lp->m_path : NULL)) {
1754 case RP_OK:
1755 if (talk)
1756 printf ("address ok\n");
1757 break;
1758
1759 case RP_NO:
1760 case RP_USER:
1761 if (!talk)
1762 fprintf (stderr, " %s: ", addr);
1763 fprintf (talk ? stdout : stderr, "loses; %s\n",
1764 rp_string (retval));
1765 unkadr++;
1766 break;
1767
1768 default:
1769 if (!talk)
1770 fprintf (stderr, " %s: ", addr);
1771 die (NULL, "unexpected response; %s", rp_string (retval));
1772 }
1773
1774 fflush (stdout);
1775 }
1776
1777
1778 static void
1779 do_text (char *file, int fd)
1780 {
1781 int retval, state;
1782 char buf[BUFSIZ];
1783
1784 lseek (fd, (off_t) 0, SEEK_SET);
1785
1786 while ((state = read (fd, buf, sizeof(buf))) > 0) {
1787 if (rp_isbad (retval = sm_wtxt (buf, state)))
1788 die (NULL, "problem writing text; %s\n", rp_string (retval));
1789 }
1790
1791 if (state == NOTOK)
1792 die (file, "problem reading from");
1793
1794 switch (retval = sm_wtend ()) {
1795 case RP_OK:
1796 break;
1797
1798 case RP_NO:
1799 case RP_NDEL:
1800 die (NULL, "posting failed; %s", rp_string (retval));
1801
1802 default:
1803 die (NULL, "unexpected response; %s", rp_string (retval));
1804 }
1805 }
1806
1807
1808 /*
1809 * SIGNAL HANDLING
1810 */
1811
1812 static void
1813 sigser (int i)
1814 {
1815 NMH_UNUSED (i);
1816
1817 (void) m_unlink (tmpfil);
1818 if (msgflags & MINV)
1819 (void) m_unlink (bccfil);
1820
1821 if (!whomsw || checksw)
1822 sm_end (NOTOK);
1823
1824 done (1);
1825 }
1826
1827
1828 static void
1829 sigon (void)
1830 {
1831 if (debug)
1832 return;
1833
1834 hstat = SIGNAL2 (SIGHUP, sigser);
1835 istat = SIGNAL2 (SIGINT, sigser);
1836 qstat = SIGNAL2 (SIGQUIT, sigser);
1837 tstat = SIGNAL2 (SIGTERM, sigser);
1838 }
1839
1840
1841 static void
1842 sigoff (void)
1843 {
1844 if (debug)
1845 return;
1846
1847 SIGNAL (SIGHUP, hstat);
1848 SIGNAL (SIGINT, istat);
1849 SIGNAL (SIGQUIT, qstat);
1850 SIGNAL (SIGTERM, tstat);
1851 }
1852
1853 /*
1854 * FCC INTERACTION
1855 */
1856
1857 static void
1858 p_refile (char *file)
1859 {
1860 int i;
1861
1862 if (fccind == 0)
1863 return;
1864
1865 if (verbose)
1866 printf (" -- Filing Folder Copies --\n");
1867 for (i = 0; i < fccind; i++)
1868 fcc (file, fccfold[i]);
1869 if (verbose)
1870 printf (" -- Folder Copies Filed --\n");
1871 }
1872
1873
1874 /*
1875 * Call the `fileproc' to add the file to the folder.
1876 */
1877
1878 static void
1879 fcc (char *file, char *folder)
1880 {
1881 pid_t child_id;
1882 int i, status, argp;
1883 char fold[BUFSIZ];
1884 char **arglist, *program;
1885
1886 if (verbose)
1887 printf (" %sFcc %s: ", msgstate == RESENT ? "Resent-" : "", folder);
1888 fflush (stdout);
1889
1890 for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
1891 sleep (5);
1892
1893 switch (child_id) {
1894 case NOTOK:
1895 if (!verbose)
1896 fprintf (stderr, " %sFcc %s: ",
1897 msgstate == RESENT ? "Resent-" : "", folder);
1898 fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
1899 break;
1900
1901 case OK:
1902 /* see if we need to add `+' */
1903 snprintf (fold, sizeof(fold), "%s%s",
1904 *folder == '+' || *folder == '@' ? "" : "+", folder);
1905
1906 /* now exec the fileproc */
1907
1908 arglist = argsplit(fileproc, &program, &argp);
1909 arglist[argp++] = "-link";
1910 arglist[argp++] = "-file";
1911 arglist[argp++] = file;
1912 arglist[argp++] = fold;
1913 arglist[argp] = NULL;
1914 execvp (program, arglist);
1915 _exit (-1);
1916
1917 default:
1918 if ((status = pidwait (child_id, OK))) {
1919 if (!verbose)
1920 fprintf (stderr, " %sFcc %s: ",
1921 msgstate == RESENT ? "Resent-" : "", folder);
1922 pidstatus (status, verbose ? stdout : stderr, NULL);
1923 } else {
1924 if (verbose)
1925 printf ("folder ok\n");
1926 }
1927 }
1928
1929 fflush (stdout);
1930 }
1931
1932 /*
1933 * TERMINATION
1934 */
1935
1936 static void
1937 die (char *what, char *fmt, ...)
1938 {
1939 va_list ap;
1940
1941 (void) m_unlink (tmpfil);
1942 if (msgflags & MINV)
1943 (void) m_unlink (bccfil);
1944
1945 if (!whomsw || checksw)
1946 sm_end (NOTOK);
1947
1948 va_start(ap, fmt);
1949 advertise (what, NULL, fmt, ap);
1950 va_end(ap);
1951 done (1);
1952 }