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