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