Fix previous attempt to remove "memory" from debug "all".
[exim.git] / src / src / filtertest.c
CommitLineData
e4a89c47 1/* $Cambridge: exim/src/src/filtertest.c,v 1.5 2005/04/06 14:40:24 ph10 Exp $ */
059ec3d9
PH
2
3/*************************************************
4* Exim - an Internet mail transport agent *
5*************************************************/
6
c988f1f4 7/* Copyright (c) University of Cambridge 1995 - 2005 */
059ec3d9
PH
8/* See the file NOTICE for conditions of use and distribution. */
9
10
11/* Code for the filter test function. */
12
13#include "exim.h"
14
15
16
17/*************************************************
f05da2e8 18* Read message and set body/size variables *
059ec3d9
PH
19*************************************************/
20
f05da2e8
PH
21/* We have to read the remainder of the message in order to find its size, so
22we can set up the message_body variables at the same time (in normal use, the
23message_body variables are not set up unless needed). The reading code is
24written out here rather than having options in read_message_data, in order to
25keep that function as efficient as possible. Handling message_body_end is
26somewhat more tedious. Pile it all into a circular buffer and sort out at the
27end.
059ec3d9 28
8e669ac1 29Arguments:
059ec3d9
PH
30 dot_ended TRUE if message already terminated by '.'
31
f05da2e8 32Returns: nothing
059ec3d9 33*/
8e669ac1 34
f05da2e8
PH
35static void
36read_message_body(dot_ended)
8e669ac1 37{
059ec3d9 38register int ch;
f05da2e8
PH
39int body_len, body_end_len, header_size;
40uschar *s;
059ec3d9
PH
41
42message_body = store_malloc(message_body_visible + 1);
43message_body_end = store_malloc(message_body_visible + 1);
44s = message_body_end;
45body_len = 0;
46body_linecount = 0;
47header_size = message_size;
48
49if (!dot_ended && !feof(stdin))
50 {
51 if (!dot_ends)
52 {
53 while ((ch = getc(stdin)) != EOF)
54 {
55 if (ch == 0) body_zerocount++;
56 if (ch == '\n') body_linecount++;
57 if (body_len < message_body_visible) message_body[body_len++] = ch;
58 *s++ = ch;
59 if (s > message_body_end + message_body_visible) s = message_body_end;
60 message_size++;
61 }
62 }
63 else
64 {
65 int ch_state = 1;
66 while ((ch = getc(stdin)) != EOF)
67 {
68 if (ch == 0) body_zerocount++;
69 switch (ch_state)
70 {
71 case 0: /* Normal state */
72 if (ch == '\n') { body_linecount++; ch_state = 1; }
73 break;
74
75 case 1: /* After "\n" */
76 if (ch == '.')
77 {
78 ch_state = 2;
79 continue;
80 }
81 if (ch != '\n') ch_state = 0;
82 break;
83
84 case 2: /* After "\n." */
85 if (ch == '\n') goto READ_END;
86 if (body_len < message_body_visible) message_body[body_len++] = '.';
87 *s++ = '.';
88 if (s > message_body_end + message_body_visible)
89 s = message_body_end;
90 message_size++;
91 ch_state = 0;
92 break;
93 }
94 if (body_len < message_body_visible) message_body[body_len++] = ch;
95 *s++ = ch;
96 if (s > message_body_end + message_body_visible) s = message_body_end;
97 message_size++;
98 }
99 READ_END: ch = ch; /* Some compilers don't like null statements */
100 }
101 if (s == message_body_end || s[-1] != '\n') body_linecount++;
102 }
103
104message_body[body_len] = 0;
105message_body_size = message_size - header_size;
106
107/* body_len stops at message_body_visible; it if got there, we may have
108wrapped round in message_body_end. */
109
110if (body_len >= message_body_visible)
111 {
112 int below = s - message_body_end;
113 int above = message_body_visible - below;
114 if (above > 0)
115 {
116 uschar *temp = store_get(below);
117 memcpy(temp, message_body_end, below);
118 memmove(message_body_end, s+1, above);
119 memcpy(message_body_end + above, temp, below);
120 s = message_body_end + message_body_visible;
121 }
122 }
123
124*s = 0;
125body_end_len = s - message_body_end;
126
127/* Convert newlines and nulls in the body variables to spaces */
128
129while (body_len > 0)
130 {
131 if (message_body[--body_len] == '\n' || message_body[body_len] == 0)
132 message_body[body_len] = ' ';
133 }
134
135while (body_end_len > 0)
136 {
137 if (message_body_end[--body_end_len] == '\n' ||
138 message_body_end[body_end_len] == 0)
139 message_body_end[body_end_len] = ' ';
140 }
f05da2e8
PH
141}
142
143
144
145/*************************************************
146* Test a mail filter *
147*************************************************/
148
149/* This is called when exim is run with the -bf option. At this point it is
150running under an unprivileged uid/gid. A test message's headers have been read
151into store, and the body of the message is still accessible on the standard
152input if this is the first time this function has been called. It may be called
153twice if both system and user filters are being tested.
154
155Argument:
156 fd an fd containing the filter file
8e669ac1 157 filename the name of the filter file
f05da2e8
PH
158 is_system TRUE if testing is to be as a system filter
159 dot_ended TRUE if message already terminated by '.'
160
161Returns: TRUE if no errors
162*/
163
164BOOL
165filter_runtest(int fd, uschar *filename, BOOL is_system, BOOL dot_ended)
166{
167int rc, filter_type;
168BOOL yield;
169struct stat statbuf;
170address_item *generated = NULL;
171uschar *error, *filebuf;
172
173/* Read the filter file into store as will be done by the router in a real
174case. */
175
176if (fstat(fd, &statbuf) != 0)
177 {
178 printf("exim: failed to get size of %s: %s\n", filename, strerror(errno));
179 return FALSE;
180 }
181
182filebuf = store_get(statbuf.st_size + 1);
183rc = read(fd, filebuf, statbuf.st_size);
184close(fd);
185
186if (rc != statbuf.st_size)
187 {
188 printf("exim: error while reading %s: %s\n", filename, strerror(errno));
189 return FALSE;
190 }
191
192filebuf[statbuf.st_size] = 0;
193
194/* Check the filter type. User filters start with "# Exim filter" or "# Sieve
195filter". If the filter type is not recognized, the file is treated as an
196ordinary .forward file. System filters do not need the "# Exim filter" in order
197to be recognized as Exim filters. */
198
199filter_type = rda_is_filter(filebuf);
200if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
201
202printf("Testing %s file \"%s\"\n\n",
203 (filter_type == FILTER_EXIM)? "Exim filter" :
204 (filter_type == FILTER_SIEVE)? "Sieve filter" :
205 "forward file",
206 filename);
207
208/* Handle a plain .forward file */
209
210if (filter_type == FILTER_FORWARD)
211 {
212 yield = parse_forward_list(filebuf,
213 RDO_REWRITE,
214 &generated, /* for generated addresses */
215 &error, /* for errors */
216 deliver_domain, /* incoming domain for \name */
217 NULL, /* no check on includes */
218 NULL); /* fail on syntax errors */
219
220 switch(yield)
221 {
222 case FF_FAIL:
223 printf("exim: forward file contains \":fail:\"\n");
224 break;
225
226 case FF_BLACKHOLE:
227 printf("exim: forwardfile contains \":blackhole:\"\n");
228 break;
229
230 case FF_ERROR:
231 printf("exim: error in forward file: %s\n", error);
232 return FALSE;
233 }
234
235 if (generated == NULL)
236 printf("exim: no addresses generated from forward file\n");
237
238 else
239 {
240 printf("exim: forward file generated:\n");
241 while (generated != NULL)
242 {
243 printf(" %s\n", generated->address);
244 generated = generated->next;
245 }
246 }
247
248 return TRUE;
249 }
250
8e669ac1 251/* For a filter, set up the message_body variables and the message size if this
f05da2e8
PH
252is the first time this function has been called. */
253
254if (message_body == NULL) read_message_body(dot_ended);
059ec3d9
PH
255
256/* Now pass the filter file to the function that interprets it. Because
f05da2e8
PH
257filter_test is not FILTER_NONE, the interpreter will output comments about what
258it is doing. No need to clean up store. Indeed, we must not, because we may be
259testing a system filter that is going to be followed by a user filter test. */
059ec3d9
PH
260
261if (is_system)
262 {
263 system_filtering = TRUE;
264 enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
265 yield = filter_interpret
266 (filebuf,
267 RDO_DEFER|RDO_FAIL|RDO_FILTER|RDO_FREEZE|RDO_REWRITE, &generated, &error);
268 enable_dollar_recipients = FALSE;
269 system_filtering = FALSE;
270 }
271else
272 {
273 yield = (filter_type == FILTER_SIEVE)?
e4a89c47 274 sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, &generated, &error)
059ec3d9
PH
275 :
276 filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
277 }
278
279return yield != FF_ERROR;
280}
281
282/* End of filtertest.c */