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