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