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