1 /* $Cambridge: exim/src/src/filtertest.c,v 1.7 2005/08/30 10:07:58 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2005 */
8 /* See the file NOTICE for conditions of use and distribution. */
11 /* Code for the filter test function. */
17 /*************************************************
18 * Read message and set body/size variables *
19 *************************************************/
21 /* We have to read the remainder of the message in order to find its size, so
22 we can set up the message_body variables at the same time (in normal use, the
23 message_body variables are not set up unless needed). The reading code is
24 written out here rather than having options in read_message_data, in order to
25 keep that function as efficient as possible. Handling message_body_end is
26 somewhat more tedious. Pile it all into a circular buffer and sort out at the
30 dot_ended TRUE if message already terminated by '.'
36 read_message_body(BOOL dot_ended)
39 int body_len, body_end_len, header_size;
42 message_body = store_malloc(message_body_visible + 1);
43 message_body_end = store_malloc(message_body_visible + 1);
47 header_size = message_size;
49 if (!dot_ended && !feof(stdin))
53 while ((ch = getc(stdin)) != EOF)
55 if (ch == 0) body_zerocount++;
56 if (ch == '\n') body_linecount++;
57 if (body_len < message_body_visible) message_body[body_len++] = ch;
59 if (s > message_body_end + message_body_visible) s = message_body_end;
66 while ((ch = getc(stdin)) != EOF)
68 if (ch == 0) body_zerocount++;
71 case 0: /* Normal state */
72 if (ch == '\n') { body_linecount++; ch_state = 1; }
75 case 1: /* After "\n" */
81 if (ch != '\n') ch_state = 0;
84 case 2: /* After "\n." */
85 if (ch == '\n') goto READ_END;
86 if (body_len < message_body_visible) message_body[body_len++] = '.';
88 if (s > message_body_end + message_body_visible)
94 if (body_len < message_body_visible) message_body[body_len++] = ch;
96 if (s > message_body_end + message_body_visible) s = message_body_end;
99 READ_END: ch = ch; /* Some compilers don't like null statements */
101 if (s == message_body_end || s[-1] != '\n') body_linecount++;
104 message_body[body_len] = 0;
105 message_body_size = message_size - header_size;
107 /* body_len stops at message_body_visible; it if got there, we may have
108 wrapped round in message_body_end. */
110 if (body_len >= message_body_visible)
112 int below = s - message_body_end;
113 int above = message_body_visible - below;
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;
125 body_end_len = s - message_body_end;
127 /* Convert newlines and nulls in the body variables to spaces */
131 if (message_body[--body_len] == '\n' || message_body[body_len] == 0)
132 message_body[body_len] = ' ';
135 while (body_end_len > 0)
137 if (message_body_end[--body_end_len] == '\n' ||
138 message_body_end[body_end_len] == 0)
139 message_body_end[body_end_len] = ' ';
145 /*************************************************
146 * Test a mail filter *
147 *************************************************/
149 /* This is called when exim is run with the -bf option. At this point it is
150 running under an unprivileged uid/gid. A test message's headers have been read
151 into store, and the body of the message is still accessible on the standard
152 input if this is the first time this function has been called. It may be called
153 twice if both system and user filters are being tested.
156 fd an fd containing the filter file
157 filename the name of the filter file
158 is_system TRUE if testing is to be as a system filter
159 dot_ended TRUE if message already terminated by '.'
161 Returns: TRUE if no errors
165 filter_runtest(int fd, uschar *filename, BOOL is_system, BOOL dot_ended)
170 address_item *generated = NULL;
171 uschar *error, *filebuf;
173 /* Read the filter file into store as will be done by the router in a real
176 if (fstat(fd, &statbuf) != 0)
178 printf("exim: failed to get size of %s: %s\n", filename, strerror(errno));
182 filebuf = store_get(statbuf.st_size + 1);
183 rc = read(fd, filebuf, statbuf.st_size);
186 if (rc != statbuf.st_size)
188 printf("exim: error while reading %s: %s\n", filename, strerror(errno));
192 filebuf[statbuf.st_size] = 0;
194 /* Check the filter type. User filters start with "# Exim filter" or "# Sieve
195 filter". If the filter type is not recognized, the file is treated as an
196 ordinary .forward file. System filters do not need the "# Exim filter" in order
197 to be recognized as Exim filters. */
199 filter_type = rda_is_filter(filebuf);
200 if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
202 printf("Testing %s file \"%s\"\n\n",
203 (filter_type == FILTER_EXIM)? "Exim filter" :
204 (filter_type == FILTER_SIEVE)? "Sieve filter" :
208 /* Handle a plain .forward file */
210 if (filter_type == FILTER_FORWARD)
212 yield = parse_forward_list(filebuf,
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 */
223 printf("exim: forward file contains \":fail:\"\n");
227 printf("exim: forwardfile contains \":blackhole:\"\n");
231 printf("exim: error in forward file: %s\n", error);
235 if (generated == NULL)
236 printf("exim: no addresses generated from forward file\n");
240 printf("exim: forward file generated:\n");
241 while (generated != NULL)
243 printf(" %s\n", generated->address);
244 generated = generated->next;
251 /* For a filter, set up the message_body variables and the message size if this
252 is the first time this function has been called. */
254 if (message_body == NULL) read_message_body(dot_ended);
256 /* Now pass the filter file to the function that interprets it. Because
257 filter_test is not FILTER_NONE, the interpreter will output comments about what
258 it is doing. No need to clean up store. Indeed, we must not, because we may be
259 testing a system filter that is going to be followed by a user filter test. */
263 system_filtering = TRUE;
264 enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
265 yield = filter_interpret
267 RDO_DEFER|RDO_FAIL|RDO_FILTER|RDO_FREEZE|RDO_REWRITE, &generated, &error);
268 enable_dollar_recipients = FALSE;
269 system_filtering = FALSE;
273 yield = (filter_type == FILTER_SIEVE)?
274 sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, &generated, &error)
276 filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
279 return yield != FF_ERROR;
282 /* End of filtertest.c */