1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2009 */
6 /* See the file NOTICE for conditions of use and distribution. */
9 /* Code for the filter test function. */
15 /*************************************************
16 * Read message and set body/size variables *
17 *************************************************/
19 /* We have to read the remainder of the message in order to find its size, so
20 we can set up the message_body variables at the same time (in normal use, the
21 message_body variables are not set up unless needed). The reading code is
22 written out here rather than having options in read_message_data, in order to
23 keep that function as efficient as possible. (Later: this function is now
24 global because it is also used by the -bem testing option.) Handling
25 message_body_end is somewhat more tedious. Pile it all into a circular buffer
26 and sort out at the end.
29 dot_ended TRUE if message already terminated by '.'
35 read_message_body(BOOL dot_ended)
38 int body_len, body_end_len, header_size;
41 message_body = store_malloc(message_body_visible + 1);
42 message_body_end = store_malloc(message_body_visible + 1);
46 header_size = message_size;
48 if (!dot_ended && !feof(stdin))
52 while ((ch = getc(stdin)) != EOF)
54 if (ch == 0) body_zerocount++;
55 if (ch == '\n') body_linecount++;
56 if (body_len < message_body_visible) message_body[body_len++] = ch;
58 if (s > message_body_end + message_body_visible) s = message_body_end;
65 while ((ch = getc(stdin)) != EOF)
67 if (ch == 0) body_zerocount++;
70 case 0: /* Normal state */
71 if (ch == '\n') { body_linecount++; ch_state = 1; }
74 case 1: /* After "\n" */
80 if (ch != '\n') ch_state = 0;
83 case 2: /* After "\n." */
84 if (ch == '\n') goto READ_END;
85 if (body_len < message_body_visible) message_body[body_len++] = '.';
87 if (s > message_body_end + message_body_visible)
93 if (body_len < message_body_visible) message_body[body_len++] = ch;
95 if (s > message_body_end + message_body_visible) s = message_body_end;
98 READ_END: ch = ch; /* Some compilers don't like null statements */
100 if (s == message_body_end || s[-1] != '\n') body_linecount++;
103 message_body[body_len] = 0;
104 message_body_size = message_size - header_size;
106 /* body_len stops at message_body_visible; it if got there, we may have
107 wrapped round in message_body_end. */
109 if (body_len >= message_body_visible)
111 int below = s - message_body_end;
112 int above = message_body_visible - below;
115 uschar *temp = store_get(below);
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;
124 body_end_len = s - message_body_end;
126 /* Convert newlines and nulls in the body variables to spaces */
130 if (message_body[--body_len] == '\n' || message_body[body_len] == 0)
131 message_body[body_len] = ' ';
134 while (body_end_len > 0)
136 if (message_body_end[--body_end_len] == '\n' ||
137 message_body_end[body_end_len] == 0)
138 message_body_end[body_end_len] = ' ';
144 /*************************************************
145 * Test a mail filter *
146 *************************************************/
148 /* This is called when exim is run with the -bf option. At this point it is
149 running under an unprivileged uid/gid. A test message's headers have been read
150 into store, and the body of the message is still accessible on the standard
151 input if this is the first time this function has been called. It may be called
152 twice if both system and user filters are being tested.
155 fd an fd containing the filter file
156 filename the name of the filter file
157 is_system TRUE if testing is to be as a system filter
158 dot_ended TRUE if message already terminated by '.'
160 Returns: TRUE if no errors
164 filter_runtest(int fd, uschar *filename, BOOL is_system, BOOL dot_ended)
169 address_item *generated = NULL;
170 uschar *error, *filebuf;
172 /* Read the filter file into store as will be done by the router in a real
175 if (fstat(fd, &statbuf) != 0)
177 printf("exim: failed to get size of %s: %s\n", filename, strerror(errno));
181 filebuf = store_get(statbuf.st_size + 1);
182 rc = read(fd, filebuf, statbuf.st_size);
185 if (rc != statbuf.st_size)
187 printf("exim: error while reading %s: %s\n", filename, strerror(errno));
191 filebuf[statbuf.st_size] = 0;
193 /* Check the filter type. User filters start with "# Exim filter" or "# Sieve
194 filter". If the filter type is not recognized, the file is treated as an
195 ordinary .forward file. System filters do not need the "# Exim filter" in order
196 to be recognized as Exim filters. */
198 filter_type = rda_is_filter(filebuf);
199 if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
201 printf("Testing %s file \"%s\"\n\n",
202 (filter_type == FILTER_EXIM)? "Exim filter" :
203 (filter_type == FILTER_SIEVE)? "Sieve filter" :
207 /* Handle a plain .forward file */
209 if (filter_type == FILTER_FORWARD)
211 yield = parse_forward_list(filebuf,
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 */
222 printf("exim: forward file contains \":fail:\"\n");
226 printf("exim: forwardfile contains \":blackhole:\"\n");
230 printf("exim: error in forward file: %s\n", error);
234 if (generated == NULL)
235 printf("exim: no addresses generated from forward file\n");
239 printf("exim: forward file generated:\n");
240 while (generated != NULL)
242 printf(" %s\n", generated->address);
243 generated = generated->next;
250 /* For a filter, set up the message_body variables and the message size if this
251 is the first time this function has been called. */
253 if (message_body == NULL) read_message_body(dot_ended);
255 /* Now pass the filter file to the function that interprets it. Because
256 filter_test is not FILTER_NONE, the interpreter will output comments about what
257 it is doing. No need to clean up store. Indeed, we must not, because we may be
258 testing a system filter that is going to be followed by a user filter test. */
262 system_filtering = TRUE;
263 enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
264 yield = filter_interpret
266 RDO_DEFER|RDO_FAIL|RDO_FILTER|RDO_FREEZE|RDO_REWRITE, &generated, &error);
267 enable_dollar_recipients = FALSE;
268 system_filtering = FALSE;
272 yield = (filter_type == FILTER_SIEVE)?
273 sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, NULL, &generated, &error)
275 filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
278 return yield != FF_ERROR;
281 /* End of filtertest.c */