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