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