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 static const char rcsid[]="$Id: filter.C,v 1.8 2006/05/28 15:29:52 mrsam Exp $";
29
30 ///////////////////////////////////////////////////////////////////////////
31 //
32 // Filter message through an external command.
33 //
34 ///////////////////////////////////////////////////////////////////////////
35
36 int xfilter(const char *, int);
37
filter(const char * filtercmd)38 int filter(const char *filtercmd)
39 {
40 try
41 {
42 int rc=xfilter(filtercmd, 0);
43
44 if (rc == 0)
45 {
46 Message *ptr=maildrop.savemsgptr;
47
48 maildrop.savemsgptr=maildrop.msgptr;
49 maildrop.msgptr=ptr;
50 maildrop.msgptr->setmsgsize();
51 maildrop.msginfo.filtered();
52 }
53 maildrop.savemsgptr->Init();
54 return(rc);
55 }
56 catch (...)
57 {
58 maildrop.savemsgptr->Init();
59 throw;
60 }
61 }
62
xfilter(const char * filtercmd,int ignorewerr)63 int xfilter(const char *filtercmd, int ignorewerr)
64 {
65 FormatMbox format_mbox;
66 char buffer[1024];
67
68 maildrop.savemsgptr->Init();
69
70 // if (format_mbox.HasMsg()) return (0); // Empty
71 (void)format_mbox.HasMsg();
72
73 Buffer cmdbuf;
74
75 cmdbuf= filtercmd;
76 cmdbuf += '\0';
77
78 PipeFds pipe0, pipe1;
79
80 if (pipe0.Pipe() < 0 || pipe1.Pipe() < 0)
81 throw "Cannot create pipe.";
82
83 pid_t pid=fork();
84
85 if (pid < 0)
86 throw "Cannot fork.";
87
88 if (pid == 0)
89 {
90 try
91 {
92 pipe0.close1();
93 pipe1.close0();
94 dup2(pipe0.fds[0], 0);
95 pipe0.close0();
96 dup2(pipe1.fds[1], 1);
97 pipe1.close1();
98 subshell(filtercmd);
99 }
100 catch (const char *p)
101 {
102 if (write(2, p, strlen(p)) < 0 ||
103 write(2, "\n", 1) < 0)
104 ; /* ignore */
105 _exit(100);
106 }
107 #if NEED_NONCONST_EXCEPTIONS
108 catch (char *p)
109 {
110 if (write(2, p, strlen(p)) < 0 ||
111 write(2, "\n", 1) < 0)
112 ; /* ignore */
113 _exit(100);
114 }
115 #endif
116 catch (...)
117 {
118 _exit(101);
119 }
120 }
121
122 pipe0.close0();
123 pipe1.close1();
124
125 format_mbox.Init(0);
126
127 //////////////////////////////////////////////////////////////////////////
128 //
129 // Write message contents to the subprocess. Simultaneously read process's
130 // output, and save it. This is done simultaneously, via select() call.
131 //
132 //////////////////////////////////////////////////////////////////////////
133
134 const char *writebufptr=0;
135 int writebuflen=0;
136
137 fd_set readfd, writefd;
138
139 FD_ZERO(&readfd);
140 FD_ZERO(&writefd);
141
142 int errflag=0;
143 int maxfd=pipe1.fds[0];
144
145 if (pipe0.fds[1] > maxfd)
146 maxfd=pipe0.fds[1];
147 ++maxfd;
148
149 fcntl(pipe1.fds[0], F_SETFL, O_NDELAY);
150 fcntl(pipe0.fds[1], F_SETFL, O_NDELAY);
151
152 for (;;)
153 {
154 FD_SET(pipe1.fds[0], &readfd);
155 if (pipe0.fds[1] >= 0)
156 FD_SET(pipe0.fds[1], &writefd);
157
158 if (!writebuflen && pipe0.fds[1] >= 0)
159 { // Need more to write.
160 Buffer *p=format_mbox.NextLine();
161
162 if (!p)
163 {
164 FD_CLR(pipe0.fds[1], &writefd);
165 pipe0.close1(); // End of msg.
166 }
167 else
168 {
169 writebufptr= *p;
170 writebuflen= p->Length();
171 }
172 }
173
174 int n=select(maxfd, &readfd, &writefd, NULL, NULL);
175
176 if (n < 0)
177 {
178 if (errno != EINTR)
179 throw "maildrop: select() error.";
180 continue;
181 }
182
183 if (pipe0.fds[1] >= 0 && FD_ISSET(pipe0.fds[1], &writefd))
184 {
185 int n= ::write(pipe0.fds[1], writebufptr, writebuflen);
186
187 if (n < 0)
188 {
189 if (errno != EINTR) // Perfectly OK
190 {
191 FD_CLR(pipe0.fds[1], &writefd);
192 pipe0.close1();
193 writebuflen=0;
194 if (!ignorewerr)
195 {
196 merr << "maildrop: error writing to filter.\n";
197 errflag=1;
198 break;
199 }
200 }
201 }
202 else
203 {
204 writebufptr += n;
205 writebuflen -= n;
206 }
207 }
208
209 if (FD_ISSET(pipe1.fds[0], &readfd))
210 {
211 int readbuflen=::read(pipe1.fds[0], buffer, sizeof(buffer));
212
213 if (readbuflen < 0)
214 {
215 if (errno != EINTR)
216 {
217 merr << "maildrop: error reading from filter.\n";
218 errflag=1;
219 break;
220 }
221 continue;
222 }
223 if (readbuflen == 0) // FILTERED
224 {
225 if ( (pipe0.fds[1] >= 0) && (!ignorewerr) )
226 {
227 merr << "maildrop: filter terminated prematurely.\n";
228 errflag=1; // Not everything
229 break; // was written, though.
230 }
231 break;
232 }
233
234 maildrop.savemsgptr->Init(buffer, readbuflen);
235 }
236 }
237
238 pipe0.close1();
239 pipe1.close0();
240
241 int wait_stat;
242
243 while (wait(&wait_stat) != pid)
244 ;
245 wait_stat= WIFEXITED(wait_stat) ? WEXITSTATUS(wait_stat):-1;
246
247 if (!wait_stat && errflag)
248 wait_stat= -1;
249
250 {
251 Buffer name, val;
252
253 val.append( (unsigned long)wait_stat);
254 name="RETURNCODE";
255 SetVar(name, val);
256 }
257
258 if (wait_stat)
259 return (-1);
260 return (0);
261 }
262