Fixed broken swit numbering (there are more files to fix)
[mmh] / uip / spost.c
1 /*
2  * spost.c -- feed messages to sendmail
3  *
4  * This is a simpler, faster, replacement for "post" for use
5  * when "sendmail" is the transport system.
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 <signal.h>
14 #include <h/addrsbr.h>
15 #include <h/aliasbr.h>
16 #include <h/dropsbr.h>
17 #include <h/tws.h>
18 #include <h/mts.h>
19 #include <h/utils.h>
20
21 #define MAX_SM_FIELD 1476  /* < largest hdr field sendmail will accept */
22 #define FCCS 10  /* max number of fccs allowed */
23
24 struct swit switches[] = {
25 #define FILTSW  0
26         { "filter filterfile", 0 },
27 #define NFILTSW  1
28         { "nofilter", 0 },
29 #define FRMTSW  2
30         { "format", 0 },
31 #define NFRMTSW  3
32         { "noformat", 0 },
33 #define REMVSW  4
34         { "remove", 0 },
35 #define NREMVSW  5
36         { "noremove", 0 },
37 #define VERBSW  6
38         { "verbose", 0 },
39 #define NVERBSW  7
40         { "noverbose", 0 },
41 #define WATCSW  8
42         { "watch", 0 },
43 #define NWATCSW  9
44         { "nowatch", 0 },
45 #define BACKSW  10
46         { "backup", 0 },
47 #define NBACKSW  11
48         { "nobackup", 0 },
49 #define ALIASW  12
50         { "alias aliasfile", 0 },
51 #define NALIASW  13
52         { "noalias", 0 },
53 #define WIDTHSW  14
54         { "width columns", 0 },
55 #define VERSIONSW  15
56         { "version", 0 },
57 #define HELPSW  16
58         { "help", 0 },
59 #define DEBUGSW  17
60         { "debug", -5 },
61 #define DISTSW  18
62         { "dist", -4 },  /* interface from dist */
63 #define PUSHSW  19  /* fork to sendmail then exit */
64         { "push", -4 },
65 #define NPUSHSW  20  /* exec sendmail */
66         { "nopush", -6 },
67 #define LIBSW  21
68         { "library directory", -7 },
69 #define ANNOSW  22
70         { "idanno number", -6 },
71         { NULL, 0 }
72 };
73
74
75 /* flags for headers->flags */
76 #define HNOP  0x0000  /* just used to keep .set around */
77 #define HBAD  0x0001  /* bad header - don't let it through */
78 #define HADR  0x0002  /* header has an address field */
79 #define HSUB  0x0004  /* Subject: header */
80 #define HTRY  0x0008  /* try to send to addrs on header */
81 #define HBCC  0x0010  /* don't output this header */
82 #define HMNG  0x0020  /* mung this header */
83 #define HNGR  0x0040  /* no groups allowed in this header */
84 #define HFCC  0x0080  /* FCC: type header */
85 #define HNIL  0x0100  /* okay for this header not to have addrs */
86 #define HIGN  0x0200  /* ignore this header */
87
88 /* flags for headers->set */
89 #define MFRM  0x0001  /* we've seen a From: */
90 #define MDAT  0x0002  /* we've seen a Date: */
91 #define MRFM  0x0004  /* we've seen a Resent-From: */
92 #define MVIS  0x0008  /* we've seen sighted addrs */
93 #define MINV  0x0010  /* we've seen blind addrs */
94 #define MRDT  0x0020  /* we've seen a Resent-Date: */
95
96 struct headers {
97         char *value;
98         unsigned int flags;
99         unsigned int set;
100 };
101
102
103 static struct headers NHeaders[] = {
104         { "Return-Path", HBAD, 0 },
105         { "Received", HBAD, 0 },
106         { "Reply-To", HADR|HNGR, 0 },
107         { "From", HADR|HNGR, MFRM },
108         { "Sender", HADR|HBAD, 0 },
109         { "Date", HNOP, MDAT },
110         { "Subject", HSUB, 0 },
111         { "To", HADR|HTRY, MVIS },
112         { "cc", HADR|HTRY, MVIS },
113         { "Bcc", HADR|HTRY|HBCC|HNIL, MINV },
114         { "Message-Id", HBAD, 0 },
115         { "Fcc", HFCC, 0 },
116         { NULL, 0, 0 }
117 };
118
119 static struct headers RHeaders[] = {
120         { "Resent-Reply-To",   HADR|HNGR, 0 },
121         { "Resent-From", HADR|HNGR, MRFM },
122         { "Resent-Sender", HADR|HBAD, 0 },
123         { "Resent-Date", HNOP, MRDT },
124         { "Resent-Subject", HSUB, 0 },
125         { "Resent-To", HADR|HTRY, MVIS },
126         { "Resent-cc", HADR|HTRY, MVIS },
127         { "Resent-Bcc", HADR|HTRY|HBCC, MINV },
128         { "Resent-Message-Id", HBAD, 0 },
129         { "Resent-Fcc", HFCC, 0 },
130         { "Reply-To", HADR, 0 },
131         { "Fcc", HIGN, 0 },
132         { NULL, 0, 0 }
133 };
134
135
136 static short fccind = 0;  /* index into fccfold[] */
137
138 static int badmsg = 0;  /* message has bad semantics */
139 static int verbose = 0;  /* spell it out */
140 static int debug = 0;  /* debugging post */
141 static int rmflg = 1;  /* remove temporary file when done */
142 static int watch = 0;  /* watch the delivery process */
143 static int backflg = 0;  /* rename input file as *.bak when done */
144 static int pushflg = 0;  /* if going to fork to sendmail */
145 static int aliasflg = -1;  /* if going to process aliases */
146 static int outputlinelen=72;
147
148 static unsigned msgflags = 0;  /* what we've seen */
149
150 static enum {
151         normal, resent
152 } msgstate = normal;
153
154 static char tmpfil[] = "/tmp/pstXXXXXX";
155
156 static char from[BUFSIZ];  /* my network address */
157 static char signature[BUFSIZ];  /* my signature */
158 static char *filter = NULL;  /* the filter for BCC'ing */
159 static char *subject = NULL;  /* the subject field for BCC'ing */
160 static char *fccfold[FCCS];  /* foldernames for FCC'ing */
161
162 static struct headers *hdrtab;  /* table for the message we're doing */
163 static FILE *out;  /* output (temp) file */
164
165 extern char *sendmail;
166
167 /*
168  * external prototypes
169  */
170 extern char *getfullname (void);
171 extern char *getusername (void);
172
173 extern boolean  draft_from_masquerading;  /* defined in mts.c */
174
175 /*
176  * static prototypes
177  */
178 static void putfmt (char *, char *, FILE *);
179 static void start_headers (void);
180 static void finish_headers (FILE *);
181 static int get_header (char *, struct headers *);
182 static void putadr (char *, struct mailname *);
183 static int putone (char *, int, int);
184 static void insert_fcc (struct headers *, unsigned char *);
185 static void file (char *);
186 static void fcc (char *, char *);
187
188 #if 0
189 static void die (char *, char *, ...);
190 static void make_bcc_file (void);
191 #endif
192
193
194 int
195 main (int argc, char **argv)
196 {
197         int state, i, pid, compnum;
198         char *cp, *msg = NULL, **argp, **arguments;
199         char *sargv[16], buf[BUFSIZ], name[NAMESZ];
200         FILE *in;
201
202 #ifdef LOCALE
203         setlocale(LC_ALL, "");
204 #endif
205         invo_name = r1bindex (argv[0], '/');
206
207         /* foil search of user profile/context */
208         if (context_foil (NULL) == -1)
209                 done (1);
210
211         mts_init (invo_name);
212         arguments = getarguments (invo_name, argc, argv, 0);
213         argp = arguments;
214
215         while ((cp = *argp++)) {
216                 if (*cp == '-') {
217                         switch (smatch (++cp, switches)) {
218                                 case AMBIGSW:
219                                         ambigsw (cp, switches);
220                                         done (1);
221                                 case UNKWNSW:
222                                         adios (NULL, "-%s unknown", cp);
223
224                                 case HELPSW:
225                                         snprintf (buf, sizeof(buf), "%s [switches] file", invo_name);
226                                         print_help (buf, switches, 1);
227                                         done (1);
228                                 case VERSIONSW:
229                                         print_version(invo_name);
230                                         done (1);
231
232                                 case DEBUGSW:
233                                         debug++;
234                                         continue;
235
236                                 case DISTSW:
237                                         msgstate = resent;
238                                         continue;
239
240                                 case FILTSW:
241                                         if (!(filter = *argp++) || *filter == '-')
242                                                 adios (NULL, "missing argument to %s", argp[-2]);
243                                         continue;
244                                 case NFILTSW:
245                                         filter = NULL;
246                                         continue;
247
248                                 case REMVSW:
249                                         rmflg++;
250                                         continue;
251                                 case NREMVSW:
252                                         rmflg = 0;
253                                         continue;
254
255                                 case BACKSW:
256                                         backflg++;
257                                         continue;
258                                 case NBACKSW:
259                                         backflg = 0;
260                                         continue;
261
262                                 case VERBSW:
263                                         verbose++;
264                                         continue;
265                                 case NVERBSW:
266                                         verbose = 0;
267                                         continue;
268
269                                 case WATCSW:
270                                         watch++;
271                                         continue;
272                                 case NWATCSW:
273                                         watch = 0;
274                                         continue;
275
276                                 case PUSHSW:
277                                         pushflg++;
278                                         continue;
279                                 case NPUSHSW:
280                                         pushflg = 0;
281                                         continue;
282
283                                 case ALIASW:
284                                         if (!(cp = *argp++) || *cp == '-')
285                                                 adios (NULL, "missing argument to %s", argp[-2]);
286                                         if (aliasflg < 0)
287                                                 alias (AliasFile);/* load default aka's */
288                                         aliasflg = 1;
289                                         if ((state = alias(cp)) != AK_OK)
290                                                 adios (NULL, "aliasing error in file %s - %s",
291                                                            cp, akerror(state) );
292                                         continue;
293                                 case NALIASW:
294                                         aliasflg = 0;
295                                         continue;
296
297                                 case WIDTHSW:
298                                         if (!(cp = *argp++) || *cp == '-')
299                                                 adios (NULL, "missing argument to %s", argp[-2]);
300                                         outputlinelen = atoi (cp);
301                                         if (outputlinelen <= 10)
302                                                 outputlinelen = 72;
303                                         continue;
304
305                                 case LIBSW:
306                                         if (!(cp = *argp++) || *cp == '-')
307                                                 adios (NULL, "missing argument to %s", argp[-2]);
308                                         /* create a minimal context */
309                                         if (context_foil (cp) == -1)
310                                                 done(1);
311                                         continue;
312
313                                 case ANNOSW:
314                                         /* -idanno switch ignored */
315                                         if (!(cp = *argp++) || *cp == '-')
316                                                 adios (NULL, "missing argument to %s", argp[-2]);
317                                         continue;
318                         }
319                 }
320                 if (msg)
321                         adios (NULL, "only one message at a time!");
322                 else
323                         msg = cp;
324         }
325
326         if (aliasflg < 0)
327                 alias (AliasFile);  /* load default aka's */
328
329         if (!msg)
330                 adios (NULL, "usage: %s [switches] file", invo_name);
331
332         if ((in = fopen (msg, "r")) == NULL)
333                 adios (msg, "unable to open");
334
335         start_headers ();
336         if (debug) {
337                 verbose++;
338                 out = stdout;
339         }
340         else {
341 #ifdef HAVE_MKSTEMP
342                         if ((out = fdopen( mkstemp (tmpfil), "w" )) == NULL )
343                                 adios (tmpfil, "unable to create");
344 #else
345                         mktemp (tmpfil);
346                         if ((out = fopen (tmpfil, "w")) == NULL)
347                                 adios (tmpfil, "unable to create");
348                         chmod (tmpfil, 0600);
349 #endif
350                 }
351
352         hdrtab = (msgstate == normal) ? NHeaders : RHeaders;
353
354         for (compnum = 1, state = FLD;;) {
355                 switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
356                         case FLD:
357                                 compnum++;
358                                 putfmt (name, buf, out);
359                                 continue;
360
361                         case FLDPLUS:
362                                 compnum++;
363                                 cp = add (buf, cp);
364                                 while (state == FLDPLUS) {
365                                         state = m_getfld (state, name, buf, sizeof(buf), in);
366                                         cp = add (buf, cp);
367                                 }
368                                 putfmt (name, cp, out);
369                                 free (cp);
370                                 continue;
371
372                         case BODY:
373                                 finish_headers (out);
374                                 fprintf (out, "\n%s", buf);
375                                 while (state == BODY) {
376                                         state = m_getfld (state, name, buf, sizeof(buf), in);
377                                         fputs (buf, out);
378                                 }
379                                 break;
380
381                         case FILEEOF:
382                                 finish_headers (out);
383                                 break;
384
385                         case LENERR:
386                         case FMTERR:
387                                 adios (NULL, "message format error in component #%d",
388                                                 compnum);
389
390                         default:
391                                 adios (NULL, "getfld() returned %d", state);
392                 }
393                 break;
394         }
395
396         fclose (in);
397         if (backflg) {
398                 strncpy (buf, m_backup (msg), sizeof(buf));
399                 if (rename (msg, buf) == NOTOK)
400                         advise (buf, "unable to rename %s to", msg);
401         }
402
403         if (debug) {
404                 done (0);
405         }
406         else
407                 fclose (out);
408
409         file (tmpfil);
410
411         /*
412          * re-open the temp file, unlink it and exec sendmail, giving it
413          * the msg temp file as std in.
414          */
415         if ( freopen( tmpfil, "r", stdin) == NULL)
416                 adios (tmpfil, "can't reopen for sendmail");
417         if (rmflg)
418                 unlink (tmpfil);
419
420         argp = sargv;
421         *argp++ = "send-mail";
422         *argp++ = "-m";  /* send to me too */
423         *argp++ = "-t";  /* read msg for recipients */
424         *argp++ = "-i";  /* don't stop on "." */
425         if (watch || verbose)
426                 *argp++ = "-v";
427         *argp = NULL;
428
429         if (pushflg && !(watch || verbose)) {
430                 /* fork to a child to run sendmail */
431                 for (i=0; (pid = vfork()) == NOTOK && i < 5; i++)
432                         sleep(5);
433                 switch (pid) {
434                         case NOTOK:
435                                 fprintf (verbose ? stdout : stderr, "%s: can't fork to %s\n",
436                                                  invo_name, sendmail);
437                                 exit(-1);
438                         case OK:
439                                 /* we're the child .. */
440                                 break;
441                         default:
442                                 exit(0);
443                 }
444         }
445         execv ( sendmail, sargv);
446         adios ( sendmail, "can't exec");
447         return 0;  /* dead code to satisfy the compiler */
448 }
449
450 /* DRAFT GENERATION */
451
452 static void
453 putfmt (char *name, char *str, FILE *out)
454 {
455         int i;
456         char *cp, *pp;
457         struct headers *hdr;
458
459         while (*str == ' ' || *str == '\t')
460                 str++;
461
462         if ((i = get_header (name, hdrtab)) == NOTOK) {
463                 fprintf (out, "%s: %s", name, str);
464                 return;
465         }
466
467         hdr = &hdrtab[i];
468         if (hdr->flags & HIGN)
469                 return;
470         if (hdr->flags & HBAD) {
471                 advise (NULL, "illegal header line -- %s:", name);
472                 badmsg++;
473                 return;
474         }
475         msgflags |= hdr->set;
476
477         if (hdr->flags & HSUB)
478                 subject = subject ? add (str, add ("\t", subject)) : getcpy (str);
479
480         if (hdr->flags & HFCC) {
481                 if ((cp = strrchr(str, '\n')))
482                         *cp = 0;
483                 for (cp = pp = str; (cp = strchr(pp, ',')); pp = cp) {
484                         *cp++ = 0;
485                         insert_fcc (hdr, pp);
486                 }
487                 insert_fcc (hdr, pp);
488                 return;
489         }
490
491 #ifdef notdef
492         if (hdr->flags & HBCC) {
493                 insert_bcc(str);
494                 return;
495         }
496 #endif /* notdef */
497
498         if (*str != '\n' && *str != '\0') {
499                 if (aliasflg && hdr->flags & HTRY) {
500                         /* this header contains address(es) that we have to do
501                          * alias expansion on.  Because of the saved state in
502                          * getname we have to put all the addresses into a list.
503                          * We then let putadr munch on that list, possibly
504                          * expanding aliases.
505                          */
506                         register struct mailname *f = 0;
507                         register struct mailname *mp = 0;
508
509                         while ((cp = getname(str))) {
510                                 mp = getm( cp, NULL, 0, AD_HOST, NULL);
511                                 if (f == 0) {
512                                         f = mp;
513                                         mp->m_next = mp;
514                                 } else {
515                                         mp->m_next = f->m_next;
516                                         f->m_next = mp;
517                                         f = mp;
518                                 }
519                         }
520                         f = mp->m_next; mp->m_next = 0;
521                         putadr( name, f );
522                 } else {
523                         /*
524                          * The author(s) of spost decided that alias
525                          * substitution wasn't necessary for the non-HTRY
526                          * headers.  Unfortunately, one of those headers
527                          * is "From:", and having alias substitution
528                          * work on that is extremely useful for someone
529                          * with a lot of POP3 email accounts or aliases.
530                          * post supports aliasing of "From:"...
531                          *
532                          * Since "From:"-processing is incompletely
533                          * implemented in this unsupported and
534                          * undocumented spost backend, I'm not
535                          * going to take the time to implement my new
536                          * draft-From:-based email address masquerading.
537                          * If I do ever implement it here, I'd almost
538                          * certainly want to implement "From:" line
539                          * alias processing as well.
540                          * -- Dan Harkless <dan-nmh@dilvish.speed.net>
541                          */
542                         fprintf (out, "%s: %s", name, str );
543                 }
544         }
545 }
546
547
548 static void
549 start_headers (void)
550 {
551         char *cp;
552         char sigbuf[BUFSIZ];
553
554         strncpy(from, getusername(), sizeof(from));
555
556         if ((cp = getfullname ()) && *cp) {
557                 strncpy (sigbuf, cp, sizeof(sigbuf));
558                 snprintf (signature, sizeof(signature), "%s <%s>", sigbuf,  from);
559         } else
560                 snprintf (signature, sizeof(signature), "%s",  from);
561 }
562
563
564 static void
565 finish_headers (FILE *out)
566 {
567         switch (msgstate) {
568                 case normal:
569                         if (!(msgflags & MDAT))
570                                 fprintf (out, "Date: %s\n", dtimenow (0));
571
572                         if (msgflags & MFRM) {
573                                 /* There was already a From: in the draft.  Don't add one. */
574                                 if (!draft_from_masquerading)
575                                         /*
576                                          * mts.conf didn't contain
577                                          * "masquerade:[...]draft_from[...]"
578                                          * so we'll reveal the user's
579                                          * actual account@thismachine
580                                          * address in a Sender: header
581                                          * (and use it as the envelope
582                                          * From: later).
583                                          */
584                                         fprintf (out, "Sender: %s\n", from);
585                         } else
586                                 fprintf (out, "From: %s\n", signature);
587
588 #ifdef notdef
589                         if (!(msgflags & MVIS))
590                                 fprintf (out, "Bcc: Blind Distribution List: ;\n");
591 #endif /* notdef */
592                         break;
593
594                 case resent:
595                         if (!(msgflags & MRDT))
596                                 fprintf (out, "Resent-Date: %s\n", dtimenow(0));
597                         if (msgflags & MRFM) {
598                                 /* There was already a Resent-From: in draft.  Don't add one. */
599                                 if (!draft_from_masquerading)
600                                         /*
601                                          * mts.conf didn't contain
602                                          * "masquerade:[...]draft_from[...]"
603                                          * so we'll reveal the user's
604                                          * actual account@thismachine
605                                          * address in a Sender: header
606                                          * (and use it as the envelope
607                                          * From: later).
608                                          */
609                                         fprintf (out, "Resent-Sender: %s\n", from);
610                         } else
611                                 /* Construct a Resent-From: header. */
612                                 fprintf (out, "Resent-From: %s\n", signature);
613 #ifdef notdef
614                         if (!(msgflags & MVIS))
615                                 fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
616 #endif /* notdef */
617                         break;
618         }
619
620         if (badmsg)
621                 adios (NULL, "re-format message and try again");
622 }
623
624
625 static int
626 get_header (char *header, struct headers *table)
627 {
628         struct headers *h;
629
630         for (h = table; h->value; h++)
631                 if (!mh_strcasecmp (header, h->value))
632                         return (h - table);
633
634         return NOTOK;
635 }
636
637
638 /*
639  * output the address list for header "name".  The address list
640  * is a linked list of mailname structs.  "nl" points to the head
641  * of the list.  Alias substitution should be done on nl.
642  */
643 static void
644 putadr (char *name, struct mailname *nl)
645 {
646         register struct mailname *mp, *mp2;
647         register int linepos;
648         register char *cp;
649         int namelen;
650
651         fprintf (out, "%s: ", name);
652         namelen = strlen(name) + 2;
653         linepos = namelen;
654
655         for (mp = nl; mp; ) {
656                 if (linepos > MAX_SM_FIELD) {
657                                 fprintf (out, "\n%s: ", name);
658                                 linepos = namelen;
659                 }
660                 if (mp->m_nohost) {
661                         /* a local name - see if it's an alias */
662                         cp = akvalue(mp->m_mbox);
663                         if (cp == mp->m_mbox)
664                                 /* wasn't an alias - use what the user typed */
665                                 linepos = putone( mp->m_text, linepos, namelen );
666                         else
667                                 /* an alias - expand it */
668                                 while ((cp = getname(cp))) {
669                                         if (linepos > MAX_SM_FIELD) {
670                                                         fprintf (out, "\n%s: ", name);
671                                                         linepos = namelen;
672                                         }
673                                         mp2 = getm( cp, NULL, 0, AD_HOST, NULL);
674                                         if (akvisible()) {
675                                                 mp2->m_pers = getcpy(mp->m_mbox);
676                                                 linepos = putone( adrformat(mp2), linepos, namelen );
677                                         } else {
678                                                 linepos = putone( mp2->m_text, linepos, namelen );
679                                         }
680                                         mnfree( mp2 );
681                                 }
682                 } else {
683                         /* not a local name - use what the user typed */
684                         linepos = putone( mp->m_text, linepos, namelen );
685                 }
686                 mp2 = mp;
687                 mp = mp->m_next;
688                 mnfree( mp2 );
689         }
690         putc( '\n', out );
691 }
692
693 static int
694 putone (char *adr, int pos, int indent)
695 {
696         register int len;
697         static int linepos;
698
699         len = strlen( adr );
700         if (pos == indent)
701                 linepos = pos;
702         else if ( linepos+len > outputlinelen ) {
703                 fprintf ( out, ",\n%*s", indent, "");
704                 linepos = indent;
705                 pos += indent + 2;
706         }
707         else {
708                 fputs( ", ", out );
709                 linepos += 2;
710                 pos += 2;
711         }
712         fputs( adr, out );
713
714         linepos += len;
715         return (pos+len);
716 }
717
718
719 static void
720 insert_fcc (struct headers *hdr, unsigned char *pp)
721 {
722         unsigned char   *cp;
723
724         for (cp = pp; isspace (*cp); cp++)
725                 continue;
726         for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--)
727                 continue;
728         if (pp >= cp)
729                 *++pp = 0;
730         if (*cp == 0)
731                 return;
732
733         if (fccind >= FCCS)
734                 adios (NULL, "too many %ss", hdr->value);
735         fccfold[fccind++] = getcpy (cp);
736 }
737
738 #if 0
739 /* BCC GENERATION */
740
741 static void
742 make_bcc_file (void)
743 {
744         pid_t child_id;
745         int fd, i, status;
746         char *vec[6];
747         FILE * in, *out;
748
749 #ifdef HAVE_MKSTEMP
750         fd = mkstemp(bccfil);
751         if (fd == -1 || (out = fdopen(fd, "w")) == NULL)
752                 adios (bccfil, "unable to create");
753 #else
754         mktemp (bccfil);
755         if ((out = fopen (bccfil, "w")) == NULL)
756                 adios (bccfil, "unable to create");
757 #endif
758         chmod (bccfil, 0600);
759
760         fprintf (out, "Date: %s\n", dtimenow (0));
761         if (msgflags & MFRM) {
762           /* There was already a From: in the draft.  Don't add one. */
763           if (!draft_from_masquerading)
764                 /*
765                  * mts.conf didn't contain "masquerade:[...]draft_from[...]"
766                  * so we'll reveal the user's actual account@thismachine
767                  * address in a Sender: header (and use it as the envelope
768                  * From: later).
769                  */
770                 fprintf (out, "Sender: %s\n", from);
771         } else
772           /* Construct a From: header. */
773           fprintf (out, "From: %s\n", signature);
774         if (subject)
775                 fprintf (out, "Subject: %s", subject);
776         fprintf (out, "BCC:\n\n------- Blind-Carbon-Copy\n\n");
777         fflush (out);
778
779         if (filter == NULL) {
780                 if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
781                         adios (NULL, "unable to re-open");
782                 cpydgst (fd, fileno (out), tmpfil, bccfil);
783                 close (fd);
784         } else {
785                 vec[0] = r1bindex (mhlproc, '/');
786
787                 for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
788                         sleep (5);
789                 switch (child_id) {
790                         case NOTOK:
791                                 adios ("vfork", "unable to");
792
793                         case OK:
794                                 dup2 (fileno (out), 1);
795
796                                 i = 1;
797                                 vec[i++] = "-forward";
798                                 vec[i++] = "-form";
799                                 vec[i++] = filter;
800                                 vec[i++] = tmpfil;
801                                 vec[i] = NULL;
802
803                                 execvp (mhlproc, vec);
804                                 adios (mhlproc, "unable to exec");
805
806                         default:
807                                 if (status = pidwait(child_id, OK))
808                                         admonish (NULL, "%s lost (status=0%o)", vec[0], status);
809                                 break;
810                 }
811         }
812
813         fseek (out, 0L, SEEK_END);
814         fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
815         fclose (out);
816 }
817 #endif /* if 0 */
818
819 /* FCC INTERACTION */
820
821 static void
822 file (char *path)
823 {
824         int i;
825
826         if (fccind == 0)
827                 return;
828
829         for (i = 0; i < fccind; i++)
830                 fcc (path, fccfold[i]);
831 }
832
833
834 static void
835 fcc (char *file, char *folder)
836 {
837         pid_t child_id;
838         int i, status;
839         char fold[BUFSIZ];
840
841         if (verbose)
842                 printf ("%sFcc: %s\n", msgstate == resent ? "Resent-" : "", folder);
843         fflush (stdout);
844
845         for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
846                 sleep (5);
847         switch (child_id) {
848                 case NOTOK:
849                         if (!verbose)
850                                 fprintf (stderr, "  %sFcc %s: ",
851                                         msgstate == resent ? "Resent-" : "", folder);
852                         fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
853                         break;
854
855                 case OK:
856                         snprintf (fold, sizeof(fold), "%s%s",
857                                 *folder == '+' || *folder == '@' ? "" : "+", folder);
858                         execlp (fileproc, r1bindex (fileproc, '/'),
859                                 "-link", "-file", file, fold, NULL);
860                         _exit (-1);
861
862                 default:
863                         if ((status = pidwait(child_id, OK))) {
864                                 if (!verbose)
865                                         fprintf (stderr, "  %sFcc %s: ",
866                                                 msgstate == resent ? "Resent-" : "", folder);
867                                 fprintf (verbose ? stdout : stderr,
868                                         " errored (0%o)\n", status);
869                         }
870         }
871
872         fflush (stdout);
873 }
874
875
876 #if 0
877
878 /*
879  * TERMINATION
880  */
881
882 static void
883 die (char *what, char *fmt, ...)
884 {
885         va_list ap;
886
887         va_start(ap, fmt);
888         advertise (what, NULL, fmt, ap);
889         va_end(ap);
890
891         done (1);
892 }
893 #endif