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