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