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