7ee47fd92e0ce748b48f75bba9ec99716b43287d
[mmh] / uip / rcvdist.c
1
2 /*
3  * rcvdist.c -- asynchronously redistribute messages
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 <h/fmt_scan.h>
14 #include <h/rcvmail.h>
15 #include <h/tws.h>
16 #include <h/mts.h>
17 #include <h/utils.h>
18
19 static struct swit switches[] = {
20 #define FORMSW       0
21     { "form formfile",  4 },
22 #define VERSIONSW    1
23     { "version", 0 },
24 #define HELPSW       2
25     { "help", 0 },
26     { NULL, 0 }
27 };
28
29 static char backup[BUFSIZ] = "";
30 static char drft[BUFSIZ] = "";
31 static char tmpfil[BUFSIZ] = "";
32
33 /*
34  * prototypes
35  */
36 static void rcvdistout (FILE *, char *, char *);
37 static void unlink_done (int) NORETURN;
38
39
40 int
41 main (int argc, char **argv)
42 {
43     pid_t child_id;
44     int i, vecp = 1;
45     char *addrs = NULL, *cp, *form = NULL, buf[BUFSIZ];
46     char **argp, **arguments, *vec[MAXARGS];
47     register FILE *fp;
48
49     done=unlink_done;
50
51 #ifdef LOCALE
52     setlocale(LC_ALL, "");
53 #endif
54     invo_name = r1bindex (argv[0], '/');
55
56     /* read user profile/context */
57     context_read();
58
59     mts_init (invo_name);
60     arguments = getarguments (invo_name, argc, argv, 1);
61     argp = arguments;
62
63     while ((cp = *argp++)) {
64         if (*cp == '-') {
65             switch (smatch (++cp, switches)) {
66                 case AMBIGSW: 
67                     ambigsw (cp, switches);
68                     done (1);
69                 case UNKWNSW: 
70                     vec[vecp++] = --cp;
71                     continue;
72
73                 case HELPSW: 
74                     snprintf (buf, sizeof(buf),
75                         "%s [switches] [switches for postproc] address ...",
76                         invo_name);
77                     print_help (buf, switches, 1);
78                     done (1);
79                 case VERSIONSW:
80                     print_version(invo_name);
81                     done (1);
82
83                 case FORMSW: 
84                     if (!(form = *argp++) || *form == '-')
85                         adios (NULL, "missing argument to %s", argp[-2]);
86                     continue;
87             }
88         }
89         addrs = addrs ? add (cp, add (", ", addrs)) : getcpy (cp);
90     }
91
92     if (addrs == NULL)
93         adios (NULL, "usage: %s [switches] [switches for postproc] address ...",
94             invo_name);
95
96     umask (~m_gmprot ());
97     strncpy (tmpfil, m_tmpfil (invo_name), sizeof(tmpfil));
98     if ((fp = fopen (tmpfil, "w+")) == NULL)
99         adios (tmpfil, "unable to create");
100     cpydata (fileno (stdin), fileno (fp), "message", tmpfil);
101     fseek (fp, 0L, SEEK_SET);
102     strncpy (drft, m_tmpfil (invo_name), sizeof(drft));
103     rcvdistout (fp, form, addrs);
104     fclose (fp);
105
106     if (distout (drft, tmpfil, backup) == NOTOK)
107         done (1);
108
109     vec[0] = r1bindex (postproc, '/');
110     vec[vecp++] = "-dist";
111     vec[vecp++] = drft;
112     vec[vecp] = NULL;
113
114     for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
115         sleep (5);
116     switch (child_id) {
117         case NOTOK: 
118             admonish (NULL, "unable to fork");/* fall */
119         case OK: 
120             execvp (postproc, vec);
121             fprintf (stderr, "unable to exec ");
122             perror (postproc);
123             _exit (1);
124
125         default: 
126             done (pidXwait(child_id, postproc));
127     }
128
129     return 0;  /* dead code to satisfy the compiler */
130 }
131
132 /* very similar to routine in replsbr.c */
133
134 #define SBUFSIZ 256
135
136 static int outputlinelen = OUTPUTLINELEN;
137
138 static struct format *fmt;
139
140 static int ncomps = 0;
141 static char **compbuffers = 0;
142 static struct comp **used_buf = 0;
143
144 static int dat[5];
145
146 static char *addrcomps[] = {
147     "from",
148     "sender",
149     "reply-to",
150     "to",
151     "cc",
152     "bcc",
153     "resent-from",
154     "resent-sender",
155     "resent-reply-to",
156     "resent-to",
157     "resent-cc",
158     "resent-bcc",
159     NULL
160 };
161
162
163 static void
164 rcvdistout (FILE *inb, char *form, char *addrs)
165 {
166     register int char_read = 0, format_len, i, state;
167     register char *tmpbuf, **nxtbuf, **ap;
168     char *cp, *scanl, name[NAMESZ];
169     register struct comp *cptr, **savecomp;
170     FILE *out;
171
172     if (!(out = fopen (drft, "w")))
173         adios (drft, "unable to create");
174
175     /* get new format string */
176     cp = new_fs (form ? form : rcvdistcomps, NULL, NULL);
177     format_len = strlen (cp);
178     ncomps = fmt_compile (cp, &fmt) + 1;
179     if (!(nxtbuf = compbuffers = (char **) calloc ((size_t) ncomps, sizeof(char *))))
180         adios (NULL, "unable to allocate component buffers");
181     if (!(savecomp = used_buf = (struct comp **) calloc ((size_t) (ncomps + 1), sizeof(struct comp *))))
182         adios (NULL, "unable to allocate component buffer stack");
183     savecomp += ncomps + 1;
184     *--savecomp = 0;
185
186     for (i = ncomps; i--;)
187         *nxtbuf++ = mh_xmalloc (SBUFSIZ);
188     nxtbuf = compbuffers;
189     tmpbuf = *nxtbuf++;
190
191     for (ap = addrcomps; *ap; ap++) {
192         FINDCOMP (cptr, *ap);
193         if (cptr)
194             cptr->c_type |= CT_ADDR;
195     }
196
197     FINDCOMP (cptr, "addresses");
198     if (cptr)
199         cptr->c_text = addrs;
200
201     for (state = FLD;;) {
202         switch (state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb)) {
203             case FLD: 
204             case FLDPLUS: 
205                 if ((cptr = wantcomp[CHASH (name)]))
206                     do {
207                         if (!mh_strcasecmp (name, cptr->c_name)) {
208                             char_read += msg_count;
209                             if (!cptr->c_text) {
210                                 cptr->c_text = tmpbuf;
211                                 *--savecomp = cptr;
212                                 tmpbuf = *nxtbuf++;
213                             }
214                             else {
215                                 i = strlen (cp = cptr->c_text) - 1;
216                                 if (cp[i] == '\n') {
217                                     if (cptr->c_type & CT_ADDR) {
218                                         cp[i] = 0;
219                                         cp = add (",\n\t", cp);
220                                     }
221                                     else
222                                         cp = add ("\t", cp);
223                                 }
224                                 cptr->c_text = add (tmpbuf, cp);
225                             }
226                             while (state == FLDPLUS) {
227                                 state = m_getfld (state, name, tmpbuf,
228                                                   SBUFSIZ, inb);
229                                 cptr->c_text = add (tmpbuf, cptr->c_text);
230                                 char_read += msg_count;
231                             }
232                             break;
233                         }
234                     } while ((cptr = cptr->c_next));
235
236                 while (state == FLDPLUS)
237                     state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
238                 break;
239
240             case LENERR: 
241             case FMTERR: 
242             case BODY: 
243             case FILEEOF: 
244                 goto finished;
245
246             default: 
247                 adios (NULL, "m_getfld() returned %d", state);
248         }
249     }
250 finished: ;
251
252     i = format_len + char_read + 256;
253     scanl = mh_xmalloc ((size_t) i + 2);
254     dat[0] = dat[1] = dat[2] = dat[4] = 0;
255     dat[3] = outputlinelen;
256     fmt_scan (fmt, scanl, i, dat);
257     fputs (scanl, out);
258
259     if (ferror (out))
260         adios (drft, "error writing");
261     fclose (out);
262
263     free (scanl);
264     for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++); nxtbuf++, i--)
265         free (cptr->c_text);
266     while (i-- > 0)
267         free (*nxtbuf++);
268     free ((char *) compbuffers);
269     free ((char *) used_buf);
270 }
271
272
273 static void
274 unlink_done (int status)
275 {
276     if (backup[0])
277         unlink (backup);
278     if (drft[0])
279         unlink (drft);
280     if (tmpfil[0])
281         unlink (tmpfil);
282
283     exit (status ? RCV_MBX : RCV_MOK);
284 }