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