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