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