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