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