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