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