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