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