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