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