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