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