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