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