e161674430e5bd2211e6998b2f366e205cbc414d
[mmh] / uip / post.c
1
2 /*
3  * post.c -- enter messages into the mail transport system
4  *
5  * $Id$
6  *
7  * This code is Copyright (c) 2002, by the authors of nmh.  See the
8  * COPYRIGHT file in the root directory of the nmh distribution for
9  * complete copyright information.
10  */
11
12 #include <h/mh.h>
13 #include <fcntl.h>
14 #include <h/signals.h>
15 #include <h/addrsbr.h>
16 #include <h/aliasbr.h>
17 #include <h/dropsbr.h>
18 #include <h/mime.h>
19 #include <h/utils.h>
20
21 #include <h/tws.h>
22 #include <h/mts.h>
23
24 #include <errno.h>
25 #include <setjmp.h>
26 #include <signal.h>
27
28 #ifdef TIME_WITH_SYS_TIME
29 # include <sys/time.h>
30 # include <time.h>
31 #else
32 # ifdef TM_IN_SYS_TIME
33 #  include <sys/time.h>
34 # else
35 #  include <time.h>
36 # endif
37 #endif
38
39 #ifdef SMTPMTS
40 # include <mts/smtp/smtp.h>
41 #endif
42
43 #ifndef CYRUS_SASL
44 # define SASLminc(a) (a)
45 #else /* CYRUS_SASL */
46 # define SASLminc(a)  0
47 #endif /* CYRUS_SASL */
48
49 #define FCCS            10      /* max number of fccs allowed */
50
51 /* In the following array of structures, the numeric second field of the
52    structures (minchars) is apparently used like this:
53
54    -# : Switch can be abbreviated to # characters; switch hidden in -help.
55    0  : Switch can't be abbreviated;               switch shown in -help.
56    #  : Switch can be abbreviated to # characters; switch shown in -help. */
57
58 static struct swit switches[] = {
59 #define ALIASW                    0
60     { "alias aliasfile", 0 },
61 #define CHKSW                     1
62     { "check", -5 },                    /* interface from whom */
63 #define NCHKSW                    2
64     { "nocheck", -7 },                  /* interface from whom */
65 #define DEBUGSW                   3
66     { "debug", -5 },
67 #define DISTSW                    4
68     { "dist", -4 },                     /* interface from dist */
69 #define FILTSW                    5
70     { "filter filterfile", 0 },
71 #define NFILTSW                   6
72     { "nofilter", 0 },
73 #define FRMTSW                    7
74     { "format", 0 },
75 #define NFRMTSW                   8
76     { "noformat", 0 },
77 #define LIBSW                     9
78     { "library directory", -7 },        /* interface from send, whom */
79 #define MIMESW                   10
80     { "mime", 0 },
81 #define NMIMESW                  11
82     { "nomime", 0 },
83 #define MSGDSW                   12
84     { "msgid", 0 },
85 #define NMSGDSW                  13
86     { "nomsgid", 0 },
87 #define VERBSW                   14
88     { "verbose", 0 },
89 #define NVERBSW                  15
90     { "noverbose", 0 },
91 #define WATCSW                   16
92     { "watch", 0 },
93 #define NWATCSW                  17
94     { "nowatch", 0 },
95 #define WHOMSW                   18
96     { "whom", -4 },                     /* interface from whom */
97 #define WIDTHSW                  19
98     { "width columns", 0 },
99 #define VERSIONSW                20
100     { "version", 0 },
101 #define HELPSW                   21
102     { "help", 0 },
103 #define BITSTUFFSW               22
104     { "dashstuffing", -12 },            /* should we dashstuff BCC messages? */
105 #define NBITSTUFFSW              23
106     { "nodashstuffing", -14 },
107 #define MAILSW                   24
108     { "mail", -4 },                     /* specify MAIL smtp mode */
109 #define SAMLSW                   25
110     { "saml", -4 },                     /* specify SAML smtp mode */
111 #define SENDSW                   26
112     { "send", -4 },                     /* specify SEND smtp mode */
113 #define SOMLSW                   27
114     { "soml", -4 },                     /* specify SOML smtp mode */
115 #define ANNOSW                   28
116     { "idanno number", -6 },            /* interface from send    */
117 #define DLVRSW                   29
118     { "deliver address-list", -7 },
119 #define CLIESW                   30
120     { "client host", -6 },
121 #define SERVSW                   31
122     { "server host", -6 },              /* specify alternate SMTP server */
123 #define SNOOPSW                  32
124     { "snoop", -5 },                    /* snoop the SMTP transaction */
125 #define FILLSW                   33
126     { "fill-in file", -7 },
127 #define FILLUSW                  34
128     { "fill-up", -7 },
129 #define PARTSW                   35
130     { "partno", -6 },
131 #define QUEUESW                  36
132     { "queued", -6 },
133 #define SASLSW                   37
134     { "sasl", SASLminc(-4) },
135 #define SASLMECHSW               38
136     { "saslmech", SASLminc(-5) },
137 #define USERSW                   39
138     { "user", SASLminc(-4) },
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     { "Sender",            HADR|HNGR,           0 },
206     { "Date",              HNOP,                MDAT },
207     { "To",                HADR|HNIL,           0 },
208     { "cc",                HADR|HNIL,           0 },
209     { "Bcc",               HADR|HTRY|HBCC|HNIL, 0 },
210     { "Fcc",               HIGN,                0 },
211     { NULL,                0,                   0 }
212 };
213
214 static short fccind = 0;        /* index into fccfold[] */
215 static short outputlinelen = OUTPUTLINELEN;
216
217 static int pfd = NOTOK;         /* fd to write annotation list to        */
218 static uid_t myuid= -1;         /* my user id                            */
219 static gid_t mygid= -1;         /* my group id                           */
220 static int recipients = 0;      /* how many people will get a copy       */
221 static int unkadr = 0;          /* how many of those were unknown        */
222 static int badadr = 0;          /* number of bad addrs                   */
223 static int badmsg = 0;          /* message has bad semantics             */
224 static int verbose = 0;         /* spell it out                          */
225 static int format = 1;          /* format addresses                      */
226 static int mime = 0;            /* use MIME-style encapsulations for Bcc */
227 static int msgid = 0;           /* add msgid                             */
228 static int debug = 0;           /* debugging post                        */
229 static int watch = 0;           /* watch the delivery process            */
230 static int whomsw = 0;          /* we are whom not post                  */
231 static int checksw = 0;         /* whom -check                           */
232 static int linepos=0;           /* putadr()'s position on the line       */
233 static int nameoutput=0;        /* putadr() has output header name       */
234 static int sasl=0;              /* Use SASL auth for SMTP                */
235 static char *saslmech=NULL;     /* Force use of particular SASL mech     */
236 static char *user=NULL;         /* Authenticate as this user             */
237
238 static unsigned msgflags = 0;   /* what we've seen */
239
240 #define NORMAL 0
241 #define RESENT 1
242 static int msgstate = NORMAL;
243
244 static time_t tclock = 0;       /* the time we started (more or less) */
245
246 static SIGNAL_HANDLER hstat, istat, qstat, tstat;
247
248 static char tmpfil[BUFSIZ];
249 static char bccfil[BUFSIZ];
250
251 static char from[BUFSIZ];       /* my network address            */
252 static char signature[BUFSIZ];  /* my signature                  */
253 static char *filter = NULL;     /* the filter for BCC'ing        */
254 static char *subject = NULL;    /* the subject field for BCC'ing */
255 static char *fccfold[FCCS];     /* foldernames for FCC'ing       */
256
257 static struct headers  *hdrtab; /* table for the message we're doing */
258
259 static struct mailname localaddrs={NULL};       /* local addrs     */
260 static struct mailname netaddrs={NULL};         /* network addrs   */
261 static struct mailname uuaddrs={NULL};          /* uucp addrs      */
262 static struct mailname tmpaddrs={NULL};         /* temporary queue */
263
264 #ifdef SMTPMTS
265 static int snoop      = 0;
266 static int smtpmode   = S_MAIL;
267 static char *clientsw = NULL;
268 static char *serversw = NULL;
269
270 extern struct smtp sm_reply;
271 #endif /* SMTPMTS */
272
273 static char prefix[] = "----- =_aaaaaaaaaa";
274
275 static int fill_up = 0;
276 static char *fill_in = NULL;
277 static char *partno = NULL;
278 static int queued = 0;
279
280 extern boolean  draft_from_masquerading;  /* defined in mts.c */
281
282 /*
283  * static prototypes
284  */
285 static void putfmt (char *, char *, FILE *);
286 static void start_headers (void);
287 static void finish_headers (FILE *);
288 static int get_header (char *, struct headers *);
289 static int putadr (char *, char *, struct mailname *, FILE *, unsigned int);
290 static void putgrp (char *, char *, FILE *, unsigned int);
291 static int insert (struct mailname *);
292 static void pl (void);
293 static void anno (void);
294 static int annoaux (struct mailname *);
295 static void insert_fcc (struct headers *, char *);
296 static void make_bcc_file (int);
297 static void verify_all_addresses (int);
298 static void chkadr (void);
299 static void sigon (void);
300 static void sigoff (void);
301 static void p_refile (char *);
302 static void fcc (char *, char *);
303 static void die (char *, char *, ...);
304 static void post (char *, int, int);
305 static void do_text (char *file, int fd);
306 static void do_an_address (struct mailname *, int);
307 static void do_addresses (int, int);
308 static int find_prefix (void);
309
310
311 int
312 main (int argc, char **argv)
313 {
314     int state, compnum, dashstuff = 0;
315     char *cp, *msg = NULL, **argp, **arguments;
316     char buf[BUFSIZ], name[NAMESZ];
317     FILE *in, *out;
318
319 #ifdef LOCALE
320     setlocale(LC_ALL, "");
321 #endif
322     invo_name = r1bindex (argv[0], '/');
323
324     /* foil search of user profile/context */
325     if (context_foil (NULL) == -1)
326         done (1);
327
328     mts_init (invo_name);
329     arguments = getarguments (invo_name, argc, argv, 0);
330     argp = arguments;
331
332     while ((cp = *argp++)) {
333         if (*cp == '-') {
334             switch (smatch (++cp, switches)) {
335                 case AMBIGSW: 
336                     ambigsw (cp, switches);
337                     done (1);
338                 case UNKWNSW: 
339                     adios (NULL, "-%s unknown", cp);
340
341                 case HELPSW: 
342                     snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
343                     print_help (buf, switches, 0);
344                     done (1);
345                 case VERSIONSW:
346                     print_version(invo_name);
347                     done (1);
348
349                 case LIBSW:
350                     if (!(cp = *argp++) || *cp == '-')
351                         adios (NULL, "missing argument to %s", argp[-2]);
352                     /* create a minimal context */
353                     if (context_foil (cp) == -1)
354                         done (1);
355                     continue;
356
357                 case ALIASW: 
358                     if (!(cp = *argp++) || *cp == '-')
359                         adios (NULL, "missing argument to %s", argp[-2]);
360                     if ((state = alias (cp)) != AK_OK)
361                         adios (NULL, "aliasing error in %s - %s",
362                                 cp, akerror (state));
363                     continue;
364
365                 case CHKSW: 
366                     checksw++;
367                     continue;
368                 case NCHKSW: 
369                     checksw = 0;
370                     continue;
371
372                 case DEBUGSW: 
373                     debug++;
374                     continue;
375
376                 case DISTSW:
377                     msgstate = RESENT;
378                     continue;
379
380                 case FILTSW:
381                     if (!(filter = *argp++) || *filter == '-')
382                         adios (NULL, "missing argument to %s", argp[-2]);
383                     mime = 0;
384                     continue;
385                 case NFILTSW:
386                     filter = NULL;
387                     continue;
388                 
389                 case FRMTSW: 
390                     format++;
391                     continue;
392                 case NFRMTSW: 
393                     format = 0;
394                     continue;
395
396                 case BITSTUFFSW:
397                     dashstuff = 1;
398                     continue;
399                 case NBITSTUFFSW:
400                     dashstuff = -1;
401                     continue;
402
403                 case MIMESW:
404                     mime++;
405                     filter = NULL;
406                     continue;
407                 case NMIMESW: 
408                     mime = 0;
409                     continue;
410
411                 case MSGDSW: 
412                     msgid++;
413                     continue;
414                 case NMSGDSW: 
415                     msgid = 0;
416                     continue;
417
418                 case VERBSW: 
419                     verbose++;
420                     continue;
421                 case NVERBSW: 
422                     verbose = 0;
423                     continue;
424
425                 case WATCSW: 
426                     watch++;
427                     continue;
428                 case NWATCSW: 
429                     watch = 0;
430                     continue;
431
432                 case WHOMSW: 
433                     whomsw++;
434                     continue;
435
436                 case WIDTHSW: 
437                     if (!(cp = *argp++) || *cp == '-')
438                         adios (NULL, "missing argument to %s", argp[-2]);
439                     if ((outputlinelen = atoi (cp)) < 10)
440                         adios (NULL, "impossible width %d", outputlinelen);
441                     continue;
442
443                 case ANNOSW: 
444                     if (!(cp = *argp++) || *cp == '-')
445                         adios (NULL, "missing argument to %s", argp[-2]);
446                     if ((pfd = atoi (cp)) <= 2)
447                         adios (NULL, "bad argument %s %s", argp[-2], cp);
448                     continue;
449
450                 case DLVRSW:
451                     if (!(cp = *argp++) || *cp == '-')
452                         adios (NULL, "missing argument to %s", argp[-2]);
453                     continue;
454
455 #ifndef SMTPMTS
456                 case CLIESW:
457                 case SERVSW:
458                     if (!(cp = *argp++) || *cp == '-')
459                         adios (NULL, "missing argument to %s", argp[-2]);
460                     continue;
461
462                 case SNOOPSW:
463                     continue;
464 #else /* SMTPMTS */
465                 case MAILSW:
466                     smtpmode = S_MAIL;
467                     continue;
468                 case SAMLSW:
469                     smtpmode = S_SAML;
470                     continue;
471                 case SOMLSW:
472                     smtpmode = S_SOML;
473                     continue;
474                 case SENDSW:
475                     smtpmode = S_SEND;
476                     continue;
477                 case CLIESW:
478                     if (!(clientsw = *argp++) || *clientsw == '-')
479                         adios (NULL, "missing argument to %s", argp[-2]);
480                     continue;
481                 case SERVSW:
482                     if (!(serversw = *argp++) || *serversw == '-')
483                         adios (NULL, "missing argument to %s", argp[-2]);
484                     continue;
485                 case SNOOPSW:
486                     snoop++;
487                     continue;
488 #endif /* SMTPMTS */
489
490                 case FILLSW:
491                     if (!(fill_in = *argp++) || *fill_in == '-')
492                         adios (NULL, "missing argument to %s", argp[-2]);
493                     continue;
494                 case FILLUSW:
495                     fill_up++;
496                     continue;
497                 case PARTSW:
498                     if (!(partno = *argp++) || *partno == '-')
499                         adios (NULL, "missing argument to %s", argp[-2]);
500                     continue;
501
502                 case QUEUESW:
503                     queued++;
504                     continue;
505                 
506                 case SASLSW:
507                     sasl++;
508                     continue;
509                 
510                 case SASLMECHSW:
511                     if (!(saslmech = *argp++) || *saslmech == '-')
512                         adios (NULL, "missing argument to %s", argp[-2]);
513                     continue;
514                 
515                 case USERSW:
516                     if (!(user = *argp++) || *user == '-')
517                         adios (NULL, "missing argument to %s", argp[-2]);
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     if (msgflags & MINV) {
623         make_bcc_file (dashstuff);
624         if (msgflags & MVIS) {
625             verify_all_addresses (verbose);
626             post (tmpfil, 0, verbose);
627         }
628         post (bccfil, 1, verbose);
629         unlink (bccfil);
630     } else {
631         post (tmpfil, 0, isatty (1));
632     }
633
634     p_refile (tmpfil);
635     unlink (tmpfil);
636
637     if (verbose)
638         printf (partno ? "Partial Message #%s Processed\n" : "Message Processed\n",
639                 partno);
640     return done (0);
641 }
642
643
644 /*
645  * DRAFT GENERATION
646  */
647
648 static void
649 putfmt (char *name, char *str, FILE *out)
650 {
651     int count, grp, i, keep;
652     char *cp, *pp, *qp;
653     char namep[BUFSIZ];
654     struct mailname *mp, *np;
655     struct headers *hdr;
656
657     while (*str == ' ' || *str == '\t')
658         str++;
659
660     if (msgstate == NORMAL && uprf (name, "resent")) {
661         advise (NULL, "illegal header line -- %s:", name);
662         badmsg++;
663         return;
664     }
665
666     if ((i = get_header (name, hdrtab)) == NOTOK) {
667         fprintf (out, "%s: %s", name, str);
668         return;
669     }
670
671     hdr = &hdrtab[i];
672     if (hdr->flags & HIGN) {
673         if (fill_in)
674             fprintf (out, "%s: %s", name, str);
675         return;
676     }
677     if (hdr->flags & HBAD) {
678         if (fill_in)
679             fprintf (out, "%s: %s", name, str);
680         else {
681             advise (NULL, "illegal header line -- %s:", name);
682             badmsg++;
683         }
684         return;
685     }
686     msgflags |= (hdr->set & ~(MVIS | MINV));
687
688     if (hdr->flags & HSUB)
689         subject = subject ? add (str, add ("\t", subject)) : getcpy (str);
690     if (hdr->flags & HFCC) {
691         if (fill_in) {
692             fprintf (out, "%s: %s", name, str);
693             return;
694         }
695
696         if ((cp = strrchr(str, '\n')))
697             *cp = 0;
698         for (cp = pp = str; (cp = strchr(pp, ',')); pp = cp) {
699             *cp++ = 0;
700             insert_fcc (hdr, pp);
701         }
702         insert_fcc (hdr, pp);
703         return;
704     }
705
706     if (!(hdr->flags & HADR)) {
707         fprintf (out, "%s: %s", name, str);
708         return;
709     }
710
711     tmpaddrs.m_next = NULL;
712     for (count = 0; (cp = getname (str)); count++)
713         if ((mp = getm (cp, NULL, 0, AD_HOST, NULL))) {
714             if (tmpaddrs.m_next)
715                 np->m_next = mp;
716             else
717                 tmpaddrs.m_next = mp;
718             np = mp;
719         }
720         else
721             if (hdr->flags & HTRY)
722                 badadr++;
723             else
724                 badmsg++;
725
726     if (count < 1) {
727         if (hdr->flags & HNIL)
728             fprintf (out, "%s: %s", name, str);
729         else {
730 #ifdef notdef
731             advise (NULL, "%s: field requires at least one address", name);
732             badmsg++;
733 #endif /* notdef */
734         }
735         return;
736     }
737
738     nameoutput = linepos = 0;
739     snprintf (namep, sizeof(namep), "%s%s",
740                 !fill_in && (hdr->flags & HMNG) ? "Original-" : "", name);
741
742     for (grp = 0, mp = tmpaddrs.m_next; mp; mp = np)
743         if (mp->m_nohost) {     /* also used to test (hdr->flags & HTRY) */
744             /* The address doesn't include a host, so it might be an alias. */
745             pp = akvalue (mp->m_mbox);  /* do mh alias substitution */
746             qp = akvisible () ? mp->m_mbox : "";
747             np = mp;
748             if (np->m_gname)
749                 putgrp (namep, np->m_gname, out, hdr->flags);
750             while ((cp = getname (pp))) {
751                 if (!(mp = getm (cp, NULL, 0, AD_HOST, NULL))) {
752                     badadr++;
753                     continue;
754                 }
755
756                 if (draft_from_masquerading && ((msgstate == RESENT)
757                                                 ? (hdr->set & MRFM)
758                                                 : (hdr->set & MFRM)))
759                     /* The user manually specified a [Resent-]From: address in
760                        their draft and the "masquerade:" line in mts.conf
761                        doesn't contain "draft_from", so we'll set things up to
762                        use the actual email address embedded in the draft
763                        [Resent-]From: (after alias substitution, and without the
764                        GECOS full name or angle brackets) as the envelope
765                        From:. */
766                     strncpy(from, auxformat(mp, 0), sizeof(from) - 1);
767
768                 if (hdr->flags & HBCC)
769                     mp->m_bcc++;
770                 if (np->m_ingrp)
771                     mp->m_ingrp = np->m_ingrp;
772                 else
773                     if (mp->m_gname)
774                         putgrp (namep, mp->m_gname, out, hdr->flags);
775                 if (mp->m_ingrp)
776                     grp++;
777                 if (putadr (namep, qp, mp, out, hdr->flags))
778                     msgflags |= (hdr->set & (MVIS | MINV));
779                 else
780                     mnfree (mp);
781             }
782             mp = np;
783             np = np->m_next;
784             mnfree (mp);
785         }
786         else {
787             /* Address includes a host, so no alias substitution is needed. */
788             if (draft_from_masquerading && ((msgstate == RESENT)
789                                             ? (hdr->set & MRFM)
790                                             : (hdr->set & MFRM)))
791                 /* The user manually specified a [Resent-]From: address in
792                    their draft and the "masquerade:" line in mts.conf
793                    doesn't contain "draft_from", so we'll set things up to
794                    use the actual email address embedded in the draft
795                    [Resent-]From: (after alias substitution, and without the
796                    GECOS full name or angle brackets) as the envelope
797                    From:. */
798                 strncpy(from, auxformat(mp, 0), sizeof(from) - 1);
799
800             if (hdr->flags & HBCC)
801                 mp->m_bcc++;
802             if (mp->m_gname)
803                 putgrp (namep, mp->m_gname, out, hdr->flags);
804             if (mp->m_ingrp)
805                 grp++;
806             keep = putadr (namep, "", mp, out, hdr->flags);
807             np = mp->m_next;
808             if (keep) {
809                 mp->m_next = NULL;
810                 msgflags |= (hdr->set & (MVIS | MINV));
811             }
812             else
813                 mnfree (mp);
814         }
815
816     if (grp > 0 && (hdr->flags & HNGR)) {
817         advise (NULL, "%s: field does not allow groups", name);
818         badmsg++;
819     }
820     if (linepos) {
821         if (fill_in && grp > 0)
822             putc (';', out);
823         putc ('\n', out);
824     }
825 }
826
827
828 static void
829 start_headers (void)
830 {
831     char  *cp;
832     char myhost[BUFSIZ], sigbuf[BUFSIZ];
833     struct mailname *mp;
834
835     myuid = getuid ();
836     mygid = getgid ();
837     time (&tclock);
838
839     strncpy (from, adrsprintf (NULL, NULL), sizeof(from));
840     strncpy (myhost, LocalName (), sizeof(myhost));
841
842     for (cp = myhost; *cp; cp++)
843         *cp = uptolow (*cp);
844
845     if ((cp = getfullname ()) && *cp) {
846         strncpy (sigbuf, cp, sizeof(sigbuf));
847         snprintf (signature, sizeof(signature), "%s <%s>",
848                 sigbuf, adrsprintf (NULL, NULL));
849         if ((cp = getname (signature)) == NULL)
850             adios (NULL, "getname () failed -- you lose extraordinarily big");
851         if ((mp = getm (cp, NULL, 0, AD_HOST, NULL)) == NULL)
852             adios (NULL, "bad signature '%s'", sigbuf);
853         mnfree (mp);
854         while (getname (""))
855             continue;
856     } else {
857         strncpy (signature, adrsprintf (NULL, NULL), sizeof(signature));
858     }
859 }
860
861
862 /*
863  * Now that we've outputted the header fields in the draft
864  * message, we will now output any remaining header fields
865  * that we need to add/create.
866  */
867
868 static void
869 finish_headers (FILE *out)
870 {
871     switch (msgstate) {
872         case NORMAL: 
873             if (whomsw && !fill_up)
874                 break;
875
876             fprintf (out, "Date: %s\n", dtime (&tclock, 0));
877             if (msgid)
878                 fprintf (out, "Message-ID: <%d.%ld@%s>\n",
879                         (int) getpid (), (long) tclock, LocalName ());
880             if (msgflags & MFRM) {
881                 /* There was already a From: in the draft.  Don't add one. */
882                 if (!draft_from_masquerading)
883                     /* mts.conf didn't contain "masquerade:[...]draft_from[...]"
884                        so we'll reveal the user's actual account@thismachine
885                        address in a Sender: header (and use it as the envelope
886                        From: later). */
887                     fprintf (out, "Sender: %s\n", from);
888             }
889             else
890                 /* Construct a From: header. */
891                 fprintf (out, "From: %s\n", signature);
892             if (whomsw)
893                 break;
894
895             if (!(msgflags & MVIS))
896                 fprintf (out, "Bcc: Blind Distribution List: ;\n");
897             break;
898
899         case RESENT: 
900             if (!(msgflags & MDAT)) {
901                 advise (NULL, "message has no Date: header");
902                 badmsg++;
903             }
904             if (!(msgflags & MFRM)) {
905                 advise (NULL, "message has no From: header");
906                 badmsg++;
907             }
908             if (whomsw && !fill_up)
909                 break;
910
911             fprintf (out, "Resent-Date: %s\n", dtime (&tclock, 0));
912             if (msgid)
913                 fprintf (out, "Resent-Message-ID: <%d.%ld@%s>\n",
914                         (int) getpid (), (long) tclock, LocalName ());
915             if (msgflags & MRFM) {
916                 /* There was already a Resent-From: in draft.  Don't add one. */
917                 if (!draft_from_masquerading)
918                     /* mts.conf didn't contain "masquerade:[...]draft_from[...]"
919                        so we'll reveal the user's actual account@thismachine
920                        address in a Sender: header (and use it as the envelope
921                        From: later). */
922                     fprintf (out, "Resent-Sender: %s\n", from);
923             }
924             else
925                 /* Construct a Resent-From: header. */
926                 fprintf (out, "Resent-From: %s\n", signature);
927             if (whomsw)
928                 break;
929             if (!(msgflags & MVIS))
930                 fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
931             break;
932     }
933
934     if (badmsg)
935         adios (NULL, "re-format message and try again");
936     if (!recipients)
937         adios (NULL, "no addressees");
938 }
939
940
941 static int
942 get_header (char *header, struct headers *table)
943 {
944     struct headers *h;
945
946     for (h = table; h->value; h++)
947         if (!strcasecmp (header, h->value))
948             return (h - table);
949
950     return NOTOK;
951 }
952
953
954 static int
955 putadr (char *name, char *aka, struct mailname *mp, FILE *out, unsigned int flags)
956 {
957     int len;
958     char *cp;
959     char buffer[BUFSIZ];
960
961     if (mp->m_mbox == NULL || ((flags & HTRY) && !insert (mp)))
962         return 0;
963     if ((!fill_in && (flags & (HBCC | HDCC))) || mp->m_ingrp)
964         return 1;
965
966     if (!nameoutput) {
967         fprintf (out, "%s: ", name);
968         linepos += (nameoutput = strlen (name) + 2);
969     }
970
971     if (*aka && mp->m_type != UUCPHOST && !mp->m_pers)
972         mp->m_pers = getcpy (aka);
973     if (format) {
974         if (mp->m_gname && !fill_in) {
975             snprintf (buffer, sizeof(buffer), "%s;", mp->m_gname);
976             cp = buffer;
977         } else {
978             cp = adrformat (mp);
979         }
980     } else {
981         cp = mp->m_text;
982     }
983     len = strlen (cp);
984
985     if (linepos != nameoutput) {
986         if (len + linepos + 2 > outputlinelen)
987             fprintf (out, ",\n%*s", linepos = nameoutput, "");
988         else {
989             fputs (", ", out);
990             linepos += 2;
991         }
992     }
993
994     fputs (cp, out);
995     linepos += len;
996
997     return (flags & HTRY);
998 }
999
1000
1001 static void
1002 putgrp (char *name, char *group, FILE *out, unsigned int flags)
1003 {
1004     int len;
1005     char *cp;
1006
1007     if (!fill_in && (flags & HBCC))
1008         return;
1009
1010     if (!nameoutput) {
1011         fprintf (out, "%s: ", name);
1012         linepos += (nameoutput = strlen (name) + 2);
1013         if (fill_in)
1014             linepos -= strlen (group);
1015     }
1016
1017     cp = fill_in ? group : concat (group, ";", NULL);
1018     len = strlen (cp);
1019
1020     if (linepos > nameoutput) {
1021         if (len + linepos + 2 > outputlinelen) {
1022             fprintf (out, ",\n%*s", nameoutput, "");
1023             linepos = nameoutput;
1024         }
1025         else {
1026             fputs (", ", out);
1027             linepos += 2;
1028         }
1029     }
1030
1031     fputs (cp, out);
1032     linepos += len;
1033 }
1034
1035
1036 static int
1037 insert (struct mailname *np)
1038 {
1039     struct mailname *mp;
1040
1041     if (np->m_mbox == NULL)
1042         return 0;
1043
1044     for (mp = np->m_type == LOCALHOST ? &localaddrs
1045             : np->m_type == UUCPHOST ? &uuaddrs
1046             : &netaddrs;
1047             mp->m_next;
1048             mp = mp->m_next)
1049         if (!strcasecmp (np->m_host, mp->m_next->m_host)
1050                 && !strcasecmp (np->m_mbox, mp->m_next->m_mbox)
1051                 && np->m_bcc == mp->m_next->m_bcc)
1052             return 0;
1053
1054     mp->m_next = np;
1055     recipients++;
1056     return 1;
1057 }
1058
1059
1060 static void
1061 pl (void)
1062 {
1063     int i;
1064     struct mailname *mp;
1065
1066     printf ("-------\n\t-- Addresses --\nlocal:\t");
1067     for (mp = localaddrs.m_next; mp; mp = mp->m_next)
1068         printf ("%s%s%s", mp->m_mbox,
1069                 mp->m_bcc ? "[BCC]" : "",
1070                 mp->m_next ? ",\n\t" : "");
1071
1072     printf ("\nnet:\t");
1073     for (mp = netaddrs.m_next; mp; mp = mp->m_next)
1074         printf ("%s%s@%s%s%s", mp->m_path ? mp->m_path : "",
1075                 mp->m_mbox, mp->m_host,
1076                 mp->m_bcc ? "[BCC]" : "",
1077                 mp->m_next ? ",\n\t" : "");
1078
1079     printf ("\nuucp:\t");
1080     for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
1081         printf ("%s!%s%s%s", mp->m_host, mp->m_mbox,
1082                 mp->m_bcc ? "[BCC]" : "",
1083                 mp->m_next ? ",\n\t" : "");
1084
1085     printf ("\n\t-- Folder Copies --\nfcc:\t");
1086     for (i = 0; i < fccind; i++)
1087         printf ("%s%s", fccfold[i], i + 1 < fccind ? ",\n\t" : "");
1088     printf ("\n");
1089 }
1090
1091
1092 static void
1093 anno (void)
1094 {
1095     struct mailname *mp;
1096
1097     for (mp = localaddrs.m_next; mp; mp = mp->m_next)
1098         if (annoaux (mp) == NOTOK)
1099             goto oops;
1100
1101     for (mp = netaddrs.m_next; mp; mp = mp->m_next)
1102         if (annoaux (mp) == NOTOK)
1103             goto oops;
1104
1105     for (mp = uuaddrs.m_next; mp; mp = mp->m_next)
1106         if (annoaux (mp) == NOTOK)
1107             break;
1108
1109 oops: ;
1110     close (pfd);
1111     pfd = NOTOK;
1112 }
1113
1114
1115 static int
1116 annoaux (struct mailname *mp)
1117 {
1118     int i;
1119     char buffer[BUFSIZ];
1120
1121     snprintf (buffer, sizeof(buffer), "%s\n", adrformat (mp));
1122     i = strlen (buffer);
1123
1124     return (write (pfd, buffer, i) == i ? OK : NOTOK);
1125 }
1126
1127
1128 static void
1129 insert_fcc (struct headers *hdr, char *pp)
1130 {
1131     char *cp;
1132
1133     for (cp = pp; isspace (*cp); cp++)
1134         continue;
1135     for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--)
1136         continue;
1137     if (pp >= cp)
1138         *++pp = 0;
1139     if (*cp == 0)
1140         return;
1141
1142     if (fccind >= FCCS)
1143         adios (NULL, "too many %ss", hdr->value);
1144     fccfold[fccind++] = getcpy (cp);
1145 }
1146
1147 /*
1148  * BCC GENERATION
1149  */
1150
1151 static void
1152 make_bcc_file (int dashstuff)
1153 {
1154     int fd, i;
1155     pid_t child_id;
1156     char *vec[6];
1157     FILE *out;
1158
1159     strncpy (bccfil, m_tmpfil ("bccs"), sizeof(bccfil));
1160     if ((out = fopen (bccfil, "w")) == NULL)
1161         adios (bccfil, "unable to create");
1162     chmod (bccfil, 0600);
1163
1164     fprintf (out, "Date: %s\n", dtime (&tclock, 0));
1165     if (msgid)
1166         fprintf (out, "Message-ID: <%d.%ld@%s>\n",
1167                 (int) getpid (), (long) tclock, LocalName ());
1168     fprintf (out, "From: %s\n", signature);
1169     if (subject)
1170         fprintf (out, "Subject: %s", subject);
1171     fprintf (out, "BCC:\n");
1172
1173     /*
1174      * Use MIME encapsulation for Bcc messages
1175      */
1176     if (mime) {
1177         char *cp;
1178
1179         /*
1180          * Check if any lines in the message clash with the
1181          * prefix for the MIME multipart separator.  If there
1182          * is a clash, increment one of the letters in the
1183          * prefix and check again.
1184          */
1185         if ((cp = strchr(prefix, 'a')) == NULL)
1186             adios (NULL, "lost prefix start");
1187         while (find_prefix () == NOTOK) {
1188             if (*cp < 'z')
1189                 (*cp)++;
1190             else
1191                 if (*++cp == 0)
1192                     adios (NULL, "can't find a unique delimiter string");
1193                 else
1194                     (*cp)++;
1195         }
1196
1197         fprintf (out, "%s: %s\n%s: multipart/digest; boundary=\"",
1198                  VRSN_FIELD, VRSN_VALUE, TYPE_FIELD);
1199         fprintf (out, "%s\"\n\n--%s\n\n", prefix, prefix);
1200     } else {
1201         fprintf (out, "\n------- Blind-Carbon-Copy\n\n");
1202     }
1203
1204     fflush (out);
1205
1206     /*
1207      * Do mhl filtering of Bcc messages instead
1208      * of MIME encapsulation.
1209      */
1210     if (filter != NULL) {
1211         vec[0] = r1bindex (mhlproc, '/');
1212
1213         for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
1214             sleep (5);
1215         switch (child_id) {
1216             case NOTOK: 
1217                 adios ("fork", "unable to");
1218
1219             case OK: 
1220                 dup2 (fileno (out), 1);
1221
1222                 i = 1;
1223                 vec[i++] = "-forward";
1224                 vec[i++] = "-form";
1225                 vec[i++] = filter;
1226                 vec[i++] = tmpfil;
1227
1228                 /* was the flag -[no]dashstuffing specified? */
1229                 if (dashstuff > 0)
1230                     vec[i++] = "-dashstuffing";
1231                 else if (dashstuff < 0)
1232                     vec[i++] = "-nodashstuffing";
1233                 vec[i] = NULL;
1234
1235                 execvp (mhlproc, vec);
1236                 fprintf (stderr, "unable to exec ");
1237                 perror (mhlproc);
1238                 _exit (-1);
1239
1240             default: 
1241                 pidXwait (child_id, mhlproc);
1242                 break;
1243         }
1244     } else {
1245         if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
1246             adios (tmpfil, "unable to re-open");
1247
1248         /*
1249          * If using MIME encapsulation, or if the -nodashstuffing
1250          * flag was given, then just copy message.  Else do
1251          * RFC934 quoting (dashstuffing).
1252          */
1253         if (mime || dashstuff < 0)
1254             cpydata (fd, fileno (out), tmpfil, bccfil);
1255         else
1256             cpydgst (fd, fileno (out), tmpfil, bccfil);
1257         close (fd);
1258     }
1259
1260     fseek (out, 0L, SEEK_END);
1261     if (mime)
1262         fprintf (out, "\n--%s--\n", prefix);
1263     else
1264         fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
1265     fclose (out);
1266 }
1267
1268
1269 /*
1270  * Scan message to check if any lines clash with
1271  * the prefix of the MIME multipart separator.
1272  */
1273
1274 static int
1275 find_prefix (void)
1276 {
1277     int len, result;
1278     char buffer[BUFSIZ];
1279     FILE *in;
1280
1281     if ((in = fopen (tmpfil, "r")) == NULL)
1282         adios (tmpfil, "unable to re-open");
1283
1284     len = strlen (prefix);
1285
1286     result = OK;
1287     while (fgets (buffer, sizeof(buffer) - 1, in))
1288         if (buffer[0] == '-' && buffer[1] == '-') {
1289             char *cp;
1290
1291             for (cp = buffer + strlen (buffer) - 1; cp >= buffer; cp--)
1292                 if (!isspace (*cp))
1293                     break;
1294             *++cp = '\0';
1295             if (strcmp (buffer + 2, prefix) == 0) {
1296                 result = NOTOK;
1297                 break;
1298             }
1299         }
1300
1301     fclose (in);
1302     return result;
1303 }
1304
1305
1306 #define plural(x) (x == 1 ? "" : "s")
1307
1308 static void
1309 chkadr (void)
1310 {
1311     if (badadr && unkadr)
1312         die (NULL, "%d address%s unparsable, %d addressee%s undeliverable",
1313                 badadr, plural (badadr), unkadr, plural (badadr));
1314     if (badadr)
1315         die (NULL, "%d address%s unparsable", badadr, plural (badadr));
1316     if (unkadr)
1317         die (NULL, "%d addressee%s undeliverable", unkadr, plural (unkadr));
1318 }
1319
1320
1321 static void
1322 do_addresses (int bccque, int talk)
1323 {
1324     int retval;
1325     int state;
1326     struct mailname *lp;
1327
1328     state = 0;
1329     for (lp = localaddrs.m_next; lp; lp = lp->m_next)
1330         if (lp->m_bcc ? bccque : !bccque) {
1331             if (talk && !state)
1332                 printf ("  -- Local Recipients --\n");
1333             do_an_address (lp, talk);
1334             state++;
1335         }
1336
1337     state = 0;
1338     for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
1339         if (lp->m_bcc ? bccque : !bccque) {
1340             if (talk && !state)
1341                 printf ("  -- UUCP Recipients --\n");
1342             do_an_address (lp, talk);
1343             state++;
1344         }
1345
1346     state = 0;
1347     for (lp = netaddrs.m_next; lp; lp = lp->m_next)
1348         if (lp->m_bcc ? bccque : !bccque) {
1349             if (talk && !state)
1350                 printf ("  -- Network Recipients --\n");
1351             do_an_address (lp, talk);
1352             state++;
1353         }
1354
1355     chkadr ();
1356
1357 #ifdef SMTPMTS
1358     if (rp_isbad (retval = sm_waend ()))
1359         die (NULL, "problem ending addresses; %s", rp_string (retval));
1360 #endif /* SMTPMTS */
1361 }
1362
1363
1364 /*
1365  * MTS-SPECIFIC INTERACTION
1366  */
1367
1368
1369 /*
1370  * SENDMAIL/SMTP routines
1371  */
1372
1373 #ifdef SMTPMTS
1374
1375 static void
1376 post (char *file, int bccque, int talk)
1377 {
1378     int fd, onex;
1379     int retval;
1380
1381     onex = !(msgflags & MINV) || bccque;
1382     if (verbose) {
1383         if (msgflags & MINV)
1384             printf (" -- Posting for %s Recipients --\n",
1385                     bccque ? "Blind" : "Sighted");
1386         else
1387             printf (" -- Posting for All Recipients --\n");
1388     }
1389
1390     sigon ();
1391
1392     if (rp_isbad (retval = sm_init (clientsw, serversw, watch, verbose,
1393                                     snoop, onex, queued, sasl, saslmech,
1394                                     user))
1395             || rp_isbad (retval = sm_winit (smtpmode, from)))
1396         die (NULL, "problem initializing server; %s", rp_string (retval));
1397
1398     do_addresses (bccque, talk && verbose);
1399     if ((fd = open (file, O_RDONLY)) == NOTOK)
1400         die (file, "unable to re-open");
1401     do_text (file, fd);
1402     close (fd);
1403     fflush (stdout);
1404
1405     sm_end (onex ? OK : DONE);
1406     sigoff ();
1407
1408     if (verbose) {
1409         if (msgflags & MINV)
1410             printf (" -- %s Recipient Copies Posted --\n",
1411                     bccque ? "Blind" : "Sighted");
1412         else
1413             printf (" -- Recipient Copies Posted --\n");
1414     }
1415
1416     fflush (stdout);
1417 }
1418
1419
1420 /* Address Verification */
1421
1422 static void
1423 verify_all_addresses (int talk)
1424 {
1425     int retval;
1426     struct mailname *lp;
1427
1428     sigon ();
1429
1430     if (!whomsw || checksw)
1431         if (rp_isbad (retval = sm_init (clientsw, serversw, watch, verbose, snoop, 0,
1432                                         queued, sasl, saslmech, user))
1433                 || rp_isbad (retval = sm_winit (smtpmode, from)))
1434             die (NULL, "problem initializing server; %s", rp_string (retval));
1435
1436     if (talk && !whomsw)
1437         printf (" -- Address Verification --\n");
1438     if (talk && localaddrs.m_next)
1439         printf ("  -- Local Recipients --\n");
1440     for (lp = localaddrs.m_next; lp; lp = lp->m_next)
1441         do_an_address (lp, talk);
1442
1443     if (talk && uuaddrs.m_next)
1444         printf ("  -- UUCP Recipients --\n");
1445     for (lp = uuaddrs.m_next; lp; lp = lp->m_next)
1446         do_an_address (lp, talk);
1447
1448     if (talk && netaddrs.m_next)
1449         printf ("  -- Network Recipients --\n");
1450     for (lp = netaddrs.m_next; lp; lp = lp->m_next)
1451         do_an_address (lp, talk);
1452
1453     chkadr ();
1454     if (talk && !whomsw)
1455         printf (" -- Address Verification Successful --\n");
1456
1457     if (!whomsw || checksw)
1458         sm_end (DONE);
1459
1460     fflush (stdout);
1461     sigoff ();
1462 }
1463
1464
1465 static void
1466 do_an_address (struct mailname *lp, int talk)
1467 {
1468     int retval;
1469     char *mbox, *host;
1470     char addr[BUFSIZ];
1471
1472     switch (lp->m_type) {
1473         case LOCALHOST: 
1474             mbox = lp->m_mbox;
1475             host = lp->m_host;
1476             strncpy (addr, mbox, sizeof(addr));
1477             break;
1478
1479         case UUCPHOST: 
1480             mbox = auxformat (lp, 0);
1481             host = NULL;
1482             snprintf (addr, sizeof(addr), "%s!%s", lp->m_host, lp->m_mbox);
1483             break;
1484
1485         default:                /* let SendMail decide if the host is bad  */
1486             mbox = lp->m_mbox;
1487             host = lp->m_host;
1488             snprintf (addr, sizeof(addr), "%s at %s", mbox, host);
1489             break;
1490     }
1491
1492     if (talk)
1493         printf ("  %s%s", addr, whomsw && lp->m_bcc ? "[BCC]" : "");
1494
1495     if (whomsw && !checksw) {
1496         putchar ('\n');
1497         return;
1498     }
1499     if (talk)
1500         printf (": ");
1501     fflush (stdout);
1502
1503     switch (retval = sm_wadr (mbox, host,
1504                          lp->m_type != UUCPHOST ? lp->m_path : NULL)) {
1505         case RP_OK: 
1506             if (talk)
1507                 printf ("address ok\n");
1508             break;
1509
1510         case RP_NO: 
1511         case RP_USER: 
1512             if (!talk)
1513                 fprintf (stderr, "  %s: ", addr);
1514             fprintf (talk ? stdout : stderr, "loses; %s\n",
1515                         rp_string (retval));
1516             unkadr++;
1517             break;
1518
1519         default: 
1520             if (!talk)
1521                 fprintf (stderr, "  %s: ", addr);
1522             die (NULL, "unexpected response; %s", rp_string (retval));
1523     }
1524
1525     fflush (stdout);
1526 }
1527
1528
1529 static void
1530 do_text (char *file, int fd)
1531 {
1532     int retval, state;
1533     char buf[BUFSIZ];
1534
1535     lseek (fd, (off_t) 0, SEEK_SET);
1536
1537     while ((state = read (fd, buf, sizeof(buf))) > 0) {
1538         if (rp_isbad (retval = sm_wtxt (buf, state)))
1539             die (NULL, "problem writing text; %s\n", rp_string (retval));
1540     }
1541
1542     if (state == NOTOK)
1543         die (file, "problem reading from");
1544
1545     switch (retval = sm_wtend ()) {
1546         case RP_OK: 
1547             break;
1548
1549         case RP_NO: 
1550         case RP_NDEL: 
1551             die (NULL, "posting failed; %s", rp_string (retval));
1552
1553         default: 
1554             die (NULL, "unexpected response; %s", rp_string (retval));
1555     }
1556 }
1557
1558 #endif /* SMTPMTS */
1559
1560
1561 /*
1562  * SIGNAL HANDLING
1563  */
1564
1565 static RETSIGTYPE
1566 sigser (int i)
1567 {
1568 #ifndef RELIABLE_SIGNALS
1569     SIGNAL (i, SIG_IGN);
1570 #endif
1571
1572     unlink (tmpfil);
1573     if (msgflags & MINV)
1574         unlink (bccfil);
1575
1576 #ifdef SMTPMTS
1577     if (!whomsw || checksw)
1578         sm_end (NOTOK);
1579 #endif /* SMTPMTS */
1580
1581     done (1);
1582 }
1583
1584
1585 static void
1586 sigon (void)
1587 {
1588     if (debug)
1589         return;
1590
1591     hstat = SIGNAL2 (SIGHUP, sigser);
1592     istat = SIGNAL2 (SIGINT, sigser);
1593     qstat = SIGNAL2 (SIGQUIT, sigser);
1594     tstat = SIGNAL2 (SIGTERM, sigser);
1595 }
1596
1597
1598 static void
1599 sigoff (void)
1600 {
1601     if (debug)
1602         return;
1603
1604     SIGNAL (SIGHUP, hstat);
1605     SIGNAL (SIGINT, istat);
1606     SIGNAL (SIGQUIT, qstat);
1607     SIGNAL (SIGTERM, tstat);
1608 }
1609
1610 /*
1611  * FCC INTERACTION
1612  */
1613
1614 static void
1615 p_refile (char *file)
1616 {
1617     int i;
1618
1619     if (fccind == 0)
1620         return;
1621
1622     if (verbose)
1623         printf (" -- Filing Folder Copies --\n");
1624     for (i = 0; i < fccind; i++)
1625         fcc (file, fccfold[i]);
1626     if (verbose)
1627         printf (" -- Folder Copies Filed --\n");
1628 }
1629
1630
1631 /*
1632  * Call the `fileproc' to add the file to the folder.
1633  */
1634
1635 static void
1636 fcc (char *file, char *folder)
1637 {
1638     pid_t child_id;
1639     int i, status;
1640     char fold[BUFSIZ];
1641
1642     if (verbose)
1643         printf ("  %sFcc %s: ", msgstate == RESENT ? "Resent-" : "", folder);
1644     fflush (stdout);
1645
1646     for (i = 0; (child_id = fork ()) == NOTOK && i < 5; i++)
1647         sleep (5);
1648
1649     switch (child_id) {
1650         case NOTOK: 
1651             if (!verbose)
1652                 fprintf (stderr, "  %sFcc %s: ",
1653                         msgstate == RESENT ? "Resent-" : "", folder);
1654             fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
1655             break;
1656
1657         case OK: 
1658             /* see if we need to add `+' */
1659             snprintf (fold, sizeof(fold), "%s%s",
1660                     *folder == '+' || *folder == '@' ? "" : "+", folder);
1661
1662             /* now exec the fileproc */
1663             execlp (fileproc, r1bindex (fileproc, '/'),
1664                     "-link", "-file", file, fold, NULL);
1665             _exit (-1);
1666
1667         default: 
1668             if ((status = pidwait (child_id, OK))) {
1669                 if (!verbose)
1670                     fprintf (stderr, "  %sFcc %s: ",
1671                             msgstate == RESENT ? "Resent-" : "", folder);
1672                 pidstatus (status, verbose ? stdout : stderr, NULL);
1673             } else {
1674                 if (verbose)
1675                     printf ("folder ok\n");
1676             }
1677     }
1678
1679     fflush (stdout);
1680 }
1681
1682 /*
1683  * TERMINATION
1684  */
1685
1686 static void
1687 die (char *what, char *fmt, ...)
1688 {
1689     va_list ap;
1690
1691     unlink (tmpfil);
1692     if (msgflags & MINV)
1693         unlink (bccfil);
1694
1695 #ifdef SMTPMTS
1696     if (!whomsw || checksw)
1697         sm_end (NOTOK);
1698 #endif /* SMTPMTS */
1699
1700     va_start(ap, fmt);
1701     advertise (what, NULL, fmt, ap);
1702     va_end(ap);
1703     done (1);
1704 }