1 /*
2 ** Copyright 1998 - 2006 Double Precision, Inc.
3 ** See COPYING for distribution information.
4 */
5
6 #include "funcs.h"
7 #include "message.h"
8 #include "messageinfo.h"
9 #include "mio.h"
10 #include "pipefds.h"
11 #include "formatmbox.h"
12 #include "xconfig.h"
13 #include "varlist.h"
14 #include "maildrop.h"
15 #include "config.h"
16 #include <sys/types.h>
17 #if HAVE_SYS_STAT_H
18 #include <sys/stat.h>
19 #endif
20 #include "mywait.h"
21 #include "mytime.h"
22 #include <signal.h>
23 #include <errno.h>
24 #if HAVE_STRINGS_H
25 #include <strings.h>
26 #endif
27
28
29 ///////////////////////////////////////////////////////////////////////////
30 //
31 // Filter message through an external command.
32 //
33 ///////////////////////////////////////////////////////////////////////////
34
35 int xfilter(const char *, int);
36
filter(const char * filtercmd)37 int filter(const char *filtercmd)
38 {
39 try
40 {
41 int rc=xfilter(filtercmd, 0);
42
43 if (rc == 0)
44 {
45 Message *ptr=maildrop.savemsgptr;
46
47 maildrop.savemsgptr=maildrop.msgptr;
48 maildrop.msgptr=ptr;
49 maildrop.msgptr->setmsgsize();
50 maildrop.msginfo.filtered();
51 }
52 maildrop.savemsgptr->Init();
53 return(rc);
54 }
55 catch (...)
56 {
57 maildrop.savemsgptr->Init();
58 throw;
59 }
60 }
61
xfilter(const char * filtercmd,int ignorewerr)62 int xfilter(const char *filtercmd, int ignorewerr)
63 {
64 FormatMbox format_mbox;
65 char buffer[1024];
66
67 maildrop.savemsgptr->Init();
68
69 // if (format_mbox.HasMsg()) return (0); // Empty
70 (void)format_mbox.HasMsg();
71
72 Buffer cmdbuf;
73
74 cmdbuf= filtercmd;
75 cmdbuf += '\0';
76
77 PipeFds pipe0, pipe1;
78
79 if (pipe0.Pipe() < 0 || pipe1.Pipe() < 0)
80 throw "Cannot create pipe.";
81
82 pid_t pid=fork();
83
84 if (pid < 0)
85 throw "Cannot fork.";
86
87 if (pid == 0)
88 {
89 try
90 {
91 pipe0.close1();
92 pipe1.close0();
93 dup2(pipe0.fds[0], 0);
94 pipe0.close0();
95 dup2(pipe1.fds[1], 1);
96 pipe1.close1();
97 subshell(filtercmd);
98 }
99 catch (const char *p)
100 {
101 if (write(2, p, strlen(p)) < 0 ||
102 write(2, "\n", 1) < 0)
103 ; /* ignore */
104 _exit(100);
105 }
106 #if NEED_NONCONST_EXCEPTIONS
107 catch (char *p)
108 {
109 if (write(2, p, strlen(p)) < 0 ||
110 write(2, "\n", 1) < 0)
111 ; /* ignore */
112 _exit(100);
113 }
114 #endif
115 catch (...)
116 {
117 _exit(101);
118 }
119 }
120
121 pipe0.close0();
122 pipe1.close1();
123
124 format_mbox.Init(0);
125
126 //////////////////////////////////////////////////////////////////////////
127 //
128 // Write message contents to the subprocess. Simultaneously read process's
129 // output, and save it. This is done simultaneously, via select() call.
130 //
131 //////////////////////////////////////////////////////////////////////////
132
133 const char *writebufptr=0;
134 int writebuflen=0;
135
136 fd_set readfd, writefd;
137
138 FD_ZERO(&readfd);
139 FD_ZERO(&writefd);
140
141 int errflag=0;
142 int maxfd=pipe1.fds[0];
143
144 if (pipe0.fds[1] > maxfd)
145 maxfd=pipe0.fds[1];
146 ++maxfd;
147
148 fcntl(pipe1.fds[0], F_SETFL, O_NDELAY);
149 fcntl(pipe0.fds[1], F_SETFL, O_NDELAY);
150
151 for (;;)
152 {
153 FD_SET(pipe1.fds[0], &readfd);
154 if (pipe0.fds[1] >= 0)
155 FD_SET(pipe0.fds[1], &writefd);
156
157 if (!writebuflen && pipe0.fds[1] >= 0)
158 { // Need more to write.
159 Buffer *p=format_mbox.NextLine();
160
161 if (!p)
162 {
163 FD_CLR(pipe0.fds[1], &writefd);
164 pipe0.close1(); // End of msg.
165 }
166 else
167 {
168 writebufptr= *p;
169 writebuflen= p->Length();
170 }
171 }
172
173 int n=select(maxfd, &readfd, &writefd, NULL, NULL);
174
175 if (n < 0)
176 {
177 if (errno != EINTR)
178 throw "maildrop: select() error.";
179 continue;
180 }
181
182 if (pipe0.fds[1] >= 0 && FD_ISSET(pipe0.fds[1], &writefd))
183 {
184 int n= ::write(pipe0.fds[1], writebufptr, writebuflen);
185
186 if (n < 0)
187 {
188 if (errno != EINTR
189 #ifdef EAGAIN
190 && errno != EAGAIN
191 #endif
192 #ifdef EWOULDBLOCK
193 && errno != EWOULDBLOCK
194 #endif
195
196 ) // Perfectly OK
197 {
198 FD_CLR(pipe0.fds[1], &writefd);
199 pipe0.close1();
200 writebuflen=0;
201 if (!ignorewerr)
202 {
203 merr << "maildrop: error writing to filter.\n";
204 errflag=1;
205 break;
206 }
207 }
208 }
209 else
210 {
211 writebufptr += n;
212 writebuflen -= n;
213 }
214 }
215
216 if (FD_ISSET(pipe1.fds[0], &readfd))
217 {
218 int readbuflen=::read(pipe1.fds[0], buffer, sizeof(buffer));
219
220 if (readbuflen < 0)
221 {
222 if (errno != EINTR
223 #ifdef EAGAIN
224 && errno != EAGAIN
225 #endif
226 #ifdef EWOULDBLOCK
227 && errno != EWOULDBLOCK
228 #endif
229 )
230 {
231 merr << "maildrop: error reading from filter.\n";
232 errflag=1;
233 break;
234 }
235 continue;
236 }
237 if (readbuflen == 0) // FILTERED
238 {
239 if ( (pipe0.fds[1] >= 0) && (!ignorewerr) )
240 {
241 merr << "maildrop: filter terminated prematurely.\n";
242 errflag=1; // Not everything
243 break; // was written, though.
244 }
245 break;
246 }
247
248 maildrop.savemsgptr->Init(buffer, readbuflen);
249 }
250 }
251
252 pipe0.close1();
253 pipe1.close0();
254
255 int wait_stat;
256
257 while (wait(&wait_stat) != pid)
258 ;
259 wait_stat= WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat):-1;
260
261 if (!wait_stat && errflag)
262 wait_stat= -1;
263
264 {
265 Buffer name, val;
266
267 val.append( (unsigned long)wait_stat);
268 name="RETURNCODE";
269 SetVar(name, val);
270 }
271
272 if (wait_stat)
273 return (-1);
274 return (0);
275 }
276
executesystem(const char * cmd)277 void executesystem(const char *cmd)
278 {
279 int devnull=open("/dev/null", O_RDONLY);
280 pid_t pid;
281
282 if (devnull < 0)
283 throw "Cannot open /dev/null";
284
285 pid=fork();
286 if (pid < 0)
287 {
288 close(devnull);
289 throw "Cannot fork.";
290 }
291
292 if (pid == 0)
293 {
294 try
295 {
296 dup2(devnull, 0);
297 close(devnull);
298 subshell(cmd);
299 }
300 catch (const char *p)
301 {
302 if (write(2, p, strlen(p)) < 0 ||
303 write(2, "\n", 1) < 0)
304 ; /* ignore */
305 _exit(100);
306 }
307 #if NEED_NONCONST_EXCEPTIONS
308 catch (char *p)
309 {
310 if (write(2, p, strlen(p)) < 0 ||
311 write(2, "\n", 1) < 0)
312 ; /* ignore */
313 _exit(100);
314 }
315 #endif
316 catch (...)
317 {
318 _exit(101);
319 }
320 }
321 close(devnull);
322
323 int wait_stat;
324
325 while (wait(&wait_stat) != pid)
326 ;
327 wait_stat= WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat):-1;
328
329 {
330 Buffer name, val;
331
332 val.append( (unsigned long)wait_stat);
333 name="RETURNCODE";
334 SetVar(name, val);
335 }
336 }
337