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