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