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