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