1 /* $Cambridge: exim/src/src/filtertest.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
3 /*************************************************
4 * Exim - an Internet mail transport agent *
5 *************************************************/
7 /* Copyright (c) University of Cambridge 1995 - 2004 */
8 /* See the file NOTICE for conditions of use and distribution. */
11 /* Code for the filter test function. */
17 /*************************************************
18 * Test a mail filter *
19 *************************************************/
21 /* This is called when exim is run with the -bf option. The name
22 of the filter file is in filter_test, and we are running under an
23 unprivileged uid/gid. A test message's headers have been read into
24 store, and the body of the message is still accessible on the
28 fd the standard input fd, containing the message body
29 is_system TRUE if testing is to be as a system filter
30 dot_ended TRUE if message already terminated by '.'
32 Returns: TRUE if no errors
36 filter_runtest(int fd, BOOL is_system, BOOL dot_ended)
38 int rc, body_len, body_end_len, filter_type, header_size;
42 address_item *generated = NULL;
43 uschar *error, *filebuf, *s;
45 /* Read the filter file into store as will be done by the router in a real
48 if (fstat(fd, &statbuf) != 0)
50 printf("exim: failed to get size of %s: %s\n", filter_test, strerror(errno));
54 filebuf = store_get(statbuf.st_size + 1);
55 rc = read(fd, filebuf, statbuf.st_size);
58 if (rc != statbuf.st_size)
60 printf("exim: error while reading %s: %s\n", filter_test, strerror(errno));
64 filebuf[statbuf.st_size] = 0;
66 /* Check the filter type. User filters start with "# Exim filter" or "# Sieve
67 filter". If the filter type is not recognized, the file is treated as an
68 ordinary .forward file. System filters do not need the "# Exim filter" in order
69 to be recognized as Exim filters. */
71 filter_type = rda_is_filter(filebuf);
72 if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
74 printf("Testing %s file \"%s\"\n\n",
75 (filter_type == FILTER_EXIM)? "Exim filter" :
76 (filter_type == FILTER_SIEVE)? "Sieve filter" :
80 /* Handle a plain .forward file */
82 if (filter_type == FILTER_FORWARD)
84 yield = parse_forward_list(filebuf,
86 &generated, /* for generated addresses */
87 &error, /* for errors */
88 deliver_domain, /* incoming domain for \name */
89 NULL, /* no check on includes */
90 NULL); /* fail on syntax errors */
95 printf("exim: forward file contains \":fail:\"\n");
99 printf("exim: forwardfile contains \":blackhole:\"\n");
103 printf("exim: error in forward file: %s\n", error);
107 if (generated == NULL)
108 printf("exim: no addresses generated from forward file\n");
112 printf("exim: forward file generated:\n");
113 while (generated != NULL)
115 printf(" %s\n", generated->address);
116 generated = generated->next;
123 /* For a filter, we have to read the remainder of the message in order to find
124 its size, so we might as well set up the message_body variable at the same time
125 (when *really* filtering this is not read unless needed). The reading code is
126 written out here rather than having options in read_message_data, in order to
127 keep that function as efficient as possible. Handling message_body_end is
128 somewhat more tedious. Pile it all into a circular buffer and sort out at the
131 message_body = store_malloc(message_body_visible + 1);
132 message_body_end = store_malloc(message_body_visible + 1);
133 s = message_body_end;
136 header_size = message_size;
138 if (!dot_ended && !feof(stdin))
142 while ((ch = getc(stdin)) != EOF)
144 if (ch == 0) body_zerocount++;
145 if (ch == '\n') body_linecount++;
146 if (body_len < message_body_visible) message_body[body_len++] = ch;
148 if (s > message_body_end + message_body_visible) s = message_body_end;
155 while ((ch = getc(stdin)) != EOF)
157 if (ch == 0) body_zerocount++;
160 case 0: /* Normal state */
161 if (ch == '\n') { body_linecount++; ch_state = 1; }
164 case 1: /* After "\n" */
170 if (ch != '\n') ch_state = 0;
173 case 2: /* After "\n." */
174 if (ch == '\n') goto READ_END;
175 if (body_len < message_body_visible) message_body[body_len++] = '.';
177 if (s > message_body_end + message_body_visible)
178 s = message_body_end;
183 if (body_len < message_body_visible) message_body[body_len++] = ch;
185 if (s > message_body_end + message_body_visible) s = message_body_end;
188 READ_END: ch = ch; /* Some compilers don't like null statements */
190 if (s == message_body_end || s[-1] != '\n') body_linecount++;
193 message_body[body_len] = 0;
194 message_body_size = message_size - header_size;
196 /* body_len stops at message_body_visible; it if got there, we may have
197 wrapped round in message_body_end. */
199 if (body_len >= message_body_visible)
201 int below = s - message_body_end;
202 int above = message_body_visible - below;
205 uschar *temp = store_get(below);
206 memcpy(temp, message_body_end, below);
207 memmove(message_body_end, s+1, above);
208 memcpy(message_body_end + above, temp, below);
209 s = message_body_end + message_body_visible;
214 body_end_len = s - message_body_end;
216 /* Convert newlines and nulls in the body variables to spaces */
220 if (message_body[--body_len] == '\n' || message_body[body_len] == 0)
221 message_body[body_len] = ' ';
224 while (body_end_len > 0)
226 if (message_body_end[--body_end_len] == '\n' ||
227 message_body_end[body_end_len] == 0)
228 message_body_end[body_end_len] = ' ';
231 /* Now pass the filter file to the function that interprets it. Because
232 filter_test is not NULL, the interpreter will output comments about what
233 it is doing, but an error message will have to be output here. No need to
234 clean up store. The last argument is 0 because Exim has given up root privilege
235 when running a filter test, and in any case, as it is a test, is isn't going to
236 try writing any files. */
240 system_filtering = TRUE;
241 enable_dollar_recipients = TRUE; /* Permit $recipients in system filter */
242 yield = filter_interpret
244 RDO_DEFER|RDO_FAIL|RDO_FILTER|RDO_FREEZE|RDO_REWRITE, &generated, &error);
245 enable_dollar_recipients = FALSE;
246 system_filtering = FALSE;
250 yield = (filter_type == FILTER_SIEVE)?
251 sieve_interpret(filebuf, RDO_REWRITE, NULL, &generated, &error)
253 filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
256 return yield != FF_ERROR;
259 /* End of filtertest.c */