Clean up install documentation a bit.
[mmh] / uip / spost.c
1
2 /*
3  * spost.c -- feed messages to sendmail
4  *
5  * This is a simpler, faster, replacement for "post" for use
6  * when "sendmail" is the transport system.
7  *
8  * This code is Copyright (c) 2002, by the authors of nmh.  See the
9  * COPYRIGHT file in the root directory of the nmh distribution for
10  * complete copyright information.
11  */
12
13 #include <h/mh.h>
14 #include <signal.h>
15 #include <h/addrsbr.h>
16 #include <h/aliasbr.h>
17 #include <h/dropsbr.h>
18 #include <h/tws.h>
19 #include <h/mts.h>
20 #include <h/utils.h>
21
22 #define MAX_SM_FIELD    1476    /* < largest hdr field sendmail will accept */
23 #define FCCS            10      /* max number of fccs allowed */
24
25 struct swit switches[] = {
26 #define FILTSW               0
27     { "filter filterfile", 0 },
28 #define NFILTSW              1
29     { "nofilter", 0 },
30 #define REMVSW               2
31     { "remove", 0 },
32 #define NREMVSW              3
33     { "noremove", 0 },
34 #define VERBSW               4
35     { "verbose", 0 },
36 #define NVERBSW              5
37     { "noverbose", 0 },
38 #define WATCSW               6
39     { "watch", 0 },
40 #define NWATCSW              7
41     { "nowatch", 0 },
42 #define BACKSW               8
43     { "backup", 0 },
44 #define NBACKSW              9
45     { "nobackup", 0 },
46 #define ALIASW              10
47     { "alias aliasfile", 0 },
48 #define NALIASW             11
49     { "noalias", 0 },
50 #define WIDTHSW             12
51     { "width columns", 0 },
52 #define VERSIONSW           13
53     { "version", 0 },
54 #define HELPSW              14
55     { "help", 0 },
56 #define DEBUGSW             15
57     { "debug", -5 },
58 #define DISTSW              16
59     { "dist", -4 },             /* interface from dist */
60 #define WHOMSW              17
61     { "whom", -4 },             /* interface from whom */
62 #define PUSHSW              18  /* fork to sendmail then exit */
63     { "push", -4 },
64 #define NPUSHSW             19  /* exec sendmail */
65     { "nopush", -6 },
66 #define LIBSW               20
67     { "library directory", -7 },
68 #define ANNOSW              21
69     { "idanno number", -6 },
70     { NULL, 0 }
71 };
72
73
74 /* flags for headers->flags */
75 #define HNOP    0x0000          /* just used to keep .set around */
76 #define HBAD    0x0001          /* bad header - don't let it through */
77 #define HADR    0x0002          /* header has an address field */
78 #define HSUB    0x0004          /* Subject: header */
79 #define HTRY    0x0008          /* try to send to addrs on header */
80 #define HBCC    0x0010          /* don't output this header */
81 /* #define      HMNG    0x0020  */      /* munge this header */
82 #define HNGR    0x0040          /* no groups allowed in this header */
83 #define HFCC    0x0080          /* FCC: type header */
84 #define HNIL    0x0100          /* okay for this header not to have addrs */
85 #define HIGN    0x0200          /* ignore this header */
86
87 /* flags for headers->set */
88 #define MFRM    0x0001          /* we've seen a From: */
89 #define MDAT    0x0002          /* we've seen a Date: */
90 #define MRFM    0x0004          /* we've seen a Resent-From: */
91 #define MVIS    0x0008          /* we've seen sighted addrs */
92 #define MINV    0x0010          /* we've seen blind addrs */
93 #define MRDT    0x0020          /* we've seen a Resent-Date: */
94
95 struct headers {
96     char *value;
97     unsigned int flags;
98     unsigned int set;
99 };
100
101
102 static struct headers NHeaders[] = {
103     { "Return-Path", HBAD,                0 },
104     { "Received",    HBAD,                0 },
105     { "Reply-To",    HADR|HNGR,           0 },
106     { "From",        HADR|HNGR,           MFRM },
107     { "Sender",      HADR|HBAD,           0 },
108     { "Date",        HNOP,                MDAT },
109     { "Subject",     HSUB,                0 },
110     { "To",          HADR|HTRY,           MVIS },
111     { "cc",          HADR|HTRY,           MVIS },
112     { "Bcc",         HADR|HTRY|HBCC|HNIL, MINV },
113     { "Message-Id",  HBAD,                0 },
114     { "Fcc",         HFCC,                0 },
115     { "Envelope-From",HIGN,               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     { "Envelope-From",     HIGN,           0 },
133     { NULL,                0,              0 }
134 };
135
136
137 static short fccind = 0;        /* index into fccfold[] */
138
139 static int badmsg = 0;          /* message has bad semantics            */
140 static int verbose = 0;         /* spell it out                         */
141 static int debug = 0;           /* debugging post                       */
142 static int rmflg = 1;           /* remove temporary file when done      */
143 static int watch = 0;           /* watch the delivery process           */
144 static int backflg = 0;         /* rename input file as *.bak when done */
145 static int whomflg = 0;         /* if just checking addresses           */
146 static int pushflg = 0;         /* if going to fork to sendmail         */
147 static int aliasflg = -1;       /* if going to process aliases          */
148 static int outputlinelen=72;
149
150 static unsigned msgflags = 0;   /* what we've seen */
151
152 static enum {
153     normal, resent
154 } msgstate = normal;
155
156 static char tmpfil[] = "/tmp/pstXXXXXX";
157
158 static char from[BUFSIZ];       /* my network address */
159 static char signature[BUFSIZ];  /* my signature */
160 static char *filter = NULL;     /* the filter for BCC'ing */
161 static char *subject = NULL;    /* the subject field for BCC'ing */
162 static char *fccfold[FCCS];     /* foldernames for FCC'ing */
163
164 static struct headers *hdrtab;  /* table for the message we're doing */
165 static FILE *out;               /* output (temp) file */
166
167 extern char *sendmail;
168
169 /*
170  * external prototypes
171  */
172 extern char *getfullname (void);
173 extern char *getusername (void);
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 WHOMSW:
241                     whomflg++;
242                     continue;
243
244                 case FILTSW:
245                     if (!(filter = *argp++) || *filter == '-')
246                         adios (NULL, "missing argument to %s", argp[-2]);
247                     continue;
248                 case NFILTSW:
249                     filter = NULL;
250                     continue;
251                 
252                 case REMVSW: 
253                     rmflg++;
254                     continue;
255                 case NREMVSW: 
256                     rmflg = 0;
257                     continue;
258
259                 case BACKSW: 
260                     backflg++;
261                     continue;
262                 case NBACKSW: 
263                     backflg = 0;
264                     continue;
265
266                 case VERBSW: 
267                     verbose++;
268                     continue;
269                 case NVERBSW: 
270                     verbose = 0;
271                     continue;
272
273                 case WATCSW: 
274                     watch++;
275                     continue;
276                 case NWATCSW: 
277                     watch = 0;
278                     continue;
279                 
280                 case PUSHSW:
281                     pushflg++;
282                     continue;
283                 case NPUSHSW:
284                     pushflg = 0;
285                     continue;
286
287                 case ALIASW:
288                     if (!(cp = *argp++) || *cp == '-')
289                         adios (NULL, "missing argument to %s", argp[-2]);
290                     if (aliasflg < 0)
291                         alias (AliasFile);/* load default aka's */
292                     aliasflg = 1;
293                     if ((state = alias(cp)) != AK_OK)
294                         adios (NULL, "aliasing error in file %s - %s",
295                                cp, akerror(state) );
296                     continue;
297                 case NALIASW:
298                     aliasflg = 0;
299                     continue;
300
301                 case WIDTHSW:
302                     if (!(cp = *argp++) || *cp == '-')
303                         adios (NULL, "missing argument to %s", argp[-2]);
304                     outputlinelen = atoi (cp);
305                     if (outputlinelen <= 10)
306                         outputlinelen = 72;
307                     continue;
308
309                 case LIBSW:
310                     if (!(cp = *argp++) || *cp == '-')
311                         adios (NULL, "missing argument to %s", argp[-2]);
312                     /* create a minimal context */
313                     if (context_foil (cp) == -1)
314                         done(1);
315                     continue;
316
317                 case ANNOSW:
318                     /* -idanno switch ignored */
319                     if (!(cp = *argp++) || *cp == '-')
320                         adios (NULL, "missing argument to %s", argp[-2]);
321                     continue;
322             }
323         }
324         if (msg)
325             adios (NULL, "only one message at a time!");
326         else
327             msg = cp;
328     }
329
330     if (aliasflg < 0)
331         alias (AliasFile);      /* load default aka's */
332
333     if (!msg)
334         adios (NULL, "usage: %s [switches] file", invo_name);
335
336     if ((in = fopen (msg, "r")) == NULL)
337         adios (msg, "unable to open");
338
339     start_headers ();
340     if (debug) {
341         verbose++;
342         out = stdout;
343     }
344     else {
345             if ((out = fdopen( mkstemp (tmpfil), "w" )) == NULL )
346                 adios (tmpfil, "unable to create");
347         }
348
349     hdrtab = (msgstate == normal) ? NHeaders : RHeaders;
350
351     for (compnum = 1, state = FLD;;) {
352         switch (state = m_getfld (state, name, buf, sizeof(buf), in)) {
353             case FLD: 
354                 compnum++;
355                 putfmt (name, buf, out);
356                 continue;
357
358             case FLDPLUS: 
359                 compnum++;
360                 cp = add (buf, cp);
361                 while (state == FLDPLUS) {
362                     state = m_getfld (state, name, buf, sizeof(buf), in);
363                     cp = add (buf, cp);
364                 }
365                 putfmt (name, cp, out);
366                 free (cp);
367                 continue;
368
369             case BODY: 
370                 finish_headers (out);
371                 fprintf (out, "\n%s", buf);
372                 if(whomflg == 0)
373                     while (state == BODY) {
374                         state = m_getfld (state, name, buf, sizeof(buf), in);
375                         fputs (buf, out);
376                     }
377                 break;
378
379             case FILEEOF: 
380                 finish_headers (out);
381                 break;
382
383             case LENERR: 
384             case FMTERR: 
385                 adios (NULL, "message format error in component #%d",
386                         compnum);
387
388             default: 
389                 adios (NULL, "getfld() returned %d", state);
390         }
391         break;
392     }
393
394     fclose (in);
395     if (backflg && !whomflg) {
396         strncpy (buf, m_backup (msg), sizeof(buf));
397         if (rename (msg, buf) == NOTOK)
398             advise (buf, "unable to rename %s to", msg);
399     }
400
401     if (debug) {
402         done (0);
403     }
404     else
405         fclose (out);
406
407     file (tmpfil);
408
409     /*
410      * re-open the temp file, unlink it and exec sendmail, giving it
411      * the msg temp file as std in.
412      */
413     if ( freopen( tmpfil, "r", stdin) == NULL)
414         adios (tmpfil, "can't reopen for sendmail");
415     if (rmflg)
416         unlink (tmpfil);
417
418     argp = sargv;
419     *argp++ = "send-mail";
420     *argp++ = "-m";     /* send to me too */
421     *argp++ = "-t";     /* read msg for recipients */
422     *argp++ = "-i";     /* don't stop on "." */
423     if (whomflg)
424         *argp++ = "-bv";
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             /* The author(s) of spost decided that alias substitution wasn't
524                necessary for the non-HTRY headers.  Unfortunately, one of those
525                headers is "From:", and having alias substitution work on that is
526                extremely useful for someone with a lot of POP3 email accounts or
527                aliases.  post supports aliasing of "From:"...
528
529                Since "From:"-processing is incompletely implemented in this
530                unsupported and undocumented spost backend, I'm not going to take
531                the time to implement my new draft-From:-based email address
532                masquerading.  If I do ever implement it here, I'd almost
533                certainly want to implement "From:" line alias processing as
534                well.  -- Dan Harkless <dan-nmh@dilvish.speed.net> */
535             fprintf (out, "%s: %s", name, str );
536         }
537     }
538 }
539
540
541 static void
542 start_headers (void)
543 {
544     char *cp;
545     char sigbuf[BUFSIZ];
546
547     strncpy(from, getusername(), sizeof(from));
548
549     if ((cp = getfullname ()) && *cp) {
550         strncpy (sigbuf, cp, sizeof(sigbuf));
551         snprintf (signature, sizeof(signature), "%s <%s>", sigbuf,  from);
552     }
553     else
554         snprintf (signature, sizeof(signature), "%s",  from);
555 }
556
557
558 static void
559 finish_headers (FILE *out)
560 {
561     switch (msgstate) {
562         case normal: 
563             if (!(msgflags & MDAT))
564                 fprintf (out, "Date: %s\n", dtimenow (0));
565             
566             if (!(msgflags & MFRM))
567                 fprintf (out, "From: %s\n", signature);
568            
569 #ifdef notdef
570             if (!(msgflags & MVIS))
571                 fprintf (out, "Bcc: Blind Distribution List: ;\n");
572 #endif  /* notdef */
573             break;
574
575         case resent: 
576             if (!(msgflags & MRDT))
577                 fprintf (out, "Resent-Date: %s\n", dtimenow(0));
578             if (!(msgflags & MRFM))
579                 /* Construct a Resent-From: header. */
580                 fprintf (out, "Resent-From: %s\n", signature);
581 #ifdef notdef
582             if (!(msgflags & MVIS))
583                 fprintf (out, "Resent-Bcc: Blind Re-Distribution List: ;\n");
584 #endif  /* notdef */
585             break;
586     }
587
588     if (badmsg)
589         adios (NULL, "re-format message and try again");
590 }
591
592
593 static int
594 get_header (char *header, struct headers *table)
595 {
596     struct headers *h;
597
598     for (h = table; h->value; h++)
599         if (!mh_strcasecmp (header, h->value))
600             return (h - table);
601
602     return NOTOK;
603 }
604
605
606 /*
607  * output the address list for header "name".  The address list
608  * is a linked list of mailname structs.  "nl" points to the head
609  * of the list.  Alias substitution should be done on nl.
610  */
611 static void
612 putadr (char *name, struct mailname *nl)
613 {
614     register struct mailname *mp, *mp2;
615     register int linepos;
616     register char *cp;
617     int namelen;
618
619     fprintf (out, "%s: ", name);
620     namelen = strlen(name) + 2;
621     linepos = namelen;
622
623     for (mp = nl; mp; ) {
624         if (linepos > MAX_SM_FIELD) {
625                 fprintf (out, "\n%s: ", name);
626                 linepos = namelen;
627         }
628         if (mp->m_nohost) {
629             /* a local name - see if it's an alias */
630             cp = akvalue(mp->m_mbox);
631             if (cp == mp->m_mbox)
632                 /* wasn't an alias - use what the user typed */
633                 linepos = putone( mp->m_text, linepos, namelen );
634             else
635                 /* an alias - expand it */
636                 while ((cp = getname(cp))) {
637                     if (linepos > MAX_SM_FIELD) {
638                             fprintf (out, "\n%s: ", name);
639                             linepos = namelen;
640                     }
641                     mp2 = getm( cp, NULL, 0, AD_HOST, NULL);
642                     if (akvisible()) {
643                         mp2->m_pers = getcpy(mp->m_mbox);
644                         linepos = putone( adrformat(mp2), linepos, namelen );
645                     } else {
646                         linepos = putone( mp2->m_text, linepos, namelen );
647                     }
648                     mnfree( mp2 );
649                 }
650         } else {
651             /* not a local name - use what the user typed */
652             linepos = putone( mp->m_text, linepos, namelen );
653         }
654         mp2 = mp;
655         mp = mp->m_next;
656         mnfree( mp2 );
657     }
658     putc( '\n', out );
659 }
660
661 static int
662 putone (char *adr, int pos, int indent)
663 {
664     register int len;
665     static int linepos;
666
667     len = strlen( adr );
668     if (pos == indent)
669         linepos = pos;
670     else if ( linepos+len > outputlinelen ) {
671         fprintf ( out, ",\n%*s", indent, "");
672         linepos = indent;
673         pos += indent + 2;
674     }
675     else {
676         fputs( ", ", out );
677         linepos += 2;
678         pos += 2;
679     }
680     fputs( adr, out );
681
682     linepos += len;
683     return (pos+len);
684 }
685
686
687 static void
688 insert_fcc (struct headers *hdr, unsigned char *pp)
689 {
690     unsigned char   *cp;
691
692     for (cp = pp; isspace (*cp); cp++)
693         continue;
694     for (pp += strlen (pp) - 1; pp > cp && isspace (*pp); pp--)
695         continue;
696     if (pp >= cp)
697         *++pp = 0;
698     if (*cp == 0)
699         return;
700
701     if (fccind >= FCCS)
702         adios (NULL, "too many %ss", hdr->value);
703     fccfold[fccind++] = getcpy (cp);
704 }
705
706 #if 0
707 /* BCC GENERATION */
708
709 static void
710 make_bcc_file (void)
711 {
712     pid_t child_id;
713     int fd, i, status;
714     char *vec[6];
715     FILE * in, *out;
716
717     fd = mkstemp(bccfil);
718     if (fd == -1 || (out = fdopen(fd, "w")) == NULL)
719         adios (bccfil, "unable to create");
720     chmod (bccfil, 0600);
721
722     fprintf (out, "Date: %s\n", dtimenow (0));
723     if (!(msgflags & MFRM))
724       /* Construct a From: header. */
725       fprintf (out, "From: %s\n", signature);
726     if (subject)
727         fprintf (out, "Subject: %s", subject);
728     fprintf (out, "BCC:\n\n------- Blind-Carbon-Copy\n\n");
729     fflush (out);
730
731     if (filter == NULL) {
732         if ((fd = open (tmpfil, O_RDONLY)) == NOTOK)
733             adios (NULL, "unable to re-open");
734         cpydgst (fd, fileno (out), tmpfil, bccfil);
735         close (fd);
736     }
737     else {
738         vec[0] = r1bindex (mhlproc, '/');
739
740         for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
741             sleep (5);
742         switch (child_id) {
743             case NOTOK: 
744                 adios ("vfork", "unable to");
745
746             case OK: 
747                 dup2 (fileno (out), 1);
748
749                 i = 1;
750                 vec[i++] = "-forward";
751                 vec[i++] = "-form";
752                 vec[i++] = filter;
753                 vec[i++] = tmpfil;
754                 vec[i] = NULL;
755
756                 execvp (mhlproc, vec);
757                 adios (mhlproc, "unable to exec");
758
759             default: 
760                 if (status = pidwait(child_id, OK))
761                     admonish (NULL, "%s lost (status=0%o)", vec[0], status);
762                 break;
763         }
764     }
765
766     fseek (out, 0L, SEEK_END);
767     fprintf (out, "\n------- End of Blind-Carbon-Copy\n");
768     fclose (out);
769 }
770 #endif  /* if 0 */
771
772 /* FCC INTERACTION */
773
774 static void
775 file (char *path)
776 {
777     int i;
778
779     if (fccind == 0)
780         return;
781
782     for (i = 0; i < fccind; i++)
783         if (whomflg)
784             printf ("Fcc: %s\n", fccfold[i]);
785         else
786             fcc (path, fccfold[i]);
787 }
788
789
790 static void
791 fcc (char *file, char *folder)
792 {
793     pid_t child_id;
794     int i, status;
795     char fold[BUFSIZ];
796
797     if (verbose)
798         printf ("%sFcc: %s\n", msgstate == resent ? "Resent-" : "", folder);
799     fflush (stdout);
800
801     for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
802         sleep (5);
803     switch (child_id) {
804         case NOTOK: 
805             if (!verbose)
806                 fprintf (stderr, "  %sFcc %s: ",
807                         msgstate == resent ? "Resent-" : "", folder);
808             fprintf (verbose ? stdout : stderr, "no forks, so not ok\n");
809             break;
810
811         case OK: 
812             snprintf (fold, sizeof(fold), "%s%s",
813                     *folder == '+' || *folder == '@' ? "" : "+", folder);
814             execlp (fileproc, r1bindex (fileproc, '/'),
815                     "-link", "-file", file, fold, NULL);
816             _exit (-1);
817
818         default: 
819             if ((status = pidwait(child_id, OK))) {
820                 if (!verbose)
821                     fprintf (stderr, "  %sFcc %s: ",
822                             msgstate == resent ? "Resent-" : "", folder);
823                 fprintf (verbose ? stdout : stderr,
824                         " errored (0%o)\n", status);
825             }
826     }
827
828     fflush (stdout);
829 }
830
831
832 #if 0
833
834 /*
835  * TERMINATION
836  */
837
838 static void
839 die (char *what, char *fmt, ...)
840 {
841     va_list ap;
842
843     va_start(ap, fmt);
844     advertise (what, NULL, fmt, ap);
845     va_end(ap);
846
847     done (1);
848 }
849 #endif