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