ph10 2004/11/25 13:54:31 GMT
Modified files:
exim-doc/doc-misc WishList
exim-doc/doc-txt ChangeLog NewStuff
exim-src/src exim.c filter.c filtertest.c functions.h
globals.c globals.h macros.h receive.c
sieve.c
Added files:
exim-test-orig/AutoTest/aux 597.sfilter 597.ufilter
exim-test-orig/AutoTest/confs 597
exim-test-orig/AutoTest/scripts 597
exim-test-orig/AutoTest/stdout 597
Log:
Allow both -bf and -bF in the same test run.
Revision Changes Path
1.11 +0 -8 exim/exim-doc/doc-misc/WishList
1.41 +2 -0 exim/exim-doc/doc-txt/ChangeLog
1.18 +8 -0 exim/exim-doc/doc-txt/NewStuff
1.10 +74 -32 exim/exim-src/src/exim.c
1.2 +19 -19 exim/exim-src/src/filter.c
1.2 +133 -110 exim/exim-src/src/filtertest.c
1.6 +1 -1 exim/exim-src/src/functions.h
1.8 +3 -1 exim/exim-src/src/globals.c
1.7 +3 -1 exim/exim-src/src/globals.h
1.4 +10 -4 exim/exim-src/src/macros.h
1.5 +7 -7 exim/exim-src/src/receive.c
1.2 +5 -5 exim/exim-src/src/sieve.c
1.1 +10 -0 exim/exim-test-orig/AutoTest/aux/597.sfilter (new)
1.1 +9 -0 exim/exim-test-orig/AutoTest/aux/597.ufilter (new)
1.1 +17 -0 exim/exim-test-orig/AutoTest/confs/597 (new)
1.1 +7 -0 exim/exim-test-orig/AutoTest/scripts/597 (new)
1.1 +24 -0 exim/exim-test-orig/AutoTest/stdout/597 (new)
Index: WishList
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-misc/WishList,v
retrieving revision 1.10
retrieving revision 1.11
diff -u -r1.10 -r1.11
--- WishList 25 Nov 2004 11:03:37 -0000 1.10
+++ WishList 25 Nov 2004 13:54:30 -0000 1.11
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-misc/WishList,v 1.10 2004/11/25 11:03:37 ph10 Exp $
+$Cambridge: exim/exim-doc/doc-misc/WishList,v 1.11 2004/11/25 13:54:30 ph10 Exp $
EXIM 4 WISH LIST
----------------
@@ -1601,14 +1601,6 @@
(268) 25-May-04 S? Add a PID to every log line
Given that pids are reused non-cyclically these days, is this actually useful?
-------------------------------------------------------------------------------
-
-(269) 26-May-04 U Run both a system and a user filter in test mode
-
- exim -bF systemfilter -bf userfilter -f sender@dom < message
-
-This would allow testing the way the userfilter handles the system
-variables set by the systemfilter.
------------------------------------------------------------------------------
(270) 01-Jun-04 M Add headers at top and middle
Index: ChangeLog
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-txt/ChangeLog,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -r1.40 -r1.41
--- ChangeLog 25 Nov 2004 10:26:04 -0000 1.40
+++ ChangeLog 25 Nov 2004 13:54:31 -0000 1.41
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.40 2004/11/25 10:26:04 ph10 Exp $
+$Cambridge: exim/exim-doc/doc-txt/ChangeLog,v 1.41 2004/11/25 13:54:31 ph10 Exp $
Change log file for Exim from version 4.21
-------------------------------------------
@@ -175,6 +175,8 @@
41. Include certificate and key file names in error message when GnuTLS fails
to set them up, because the GnuTLS error message doesn't include the name
of the failing file when there is a problem reading it.
+
+42. Allow both -bf and -bF in the same test run.
Exim version 4.43
Index: NewStuff
===================================================================
RCS file: /home/cvs/exim/exim-doc/doc-txt/NewStuff,v
retrieving revision 1.17
retrieving revision 1.18
diff -u -r1.17 -r1.18
--- NewStuff 24 Nov 2004 16:36:19 -0000 1.17
+++ NewStuff 25 Nov 2004 13:54:31 -0000 1.18
@@ -1,4 +1,4 @@
-$Cambridge: exim/exim-doc/doc-txt/NewStuff,v 1.17 2004/11/24 16:36:19 ph10 Exp $
+$Cambridge: exim/exim-doc/doc-txt/NewStuff,v 1.18 2004/11/25 13:54:31 ph10 Exp $
New Features in Exim
--------------------
@@ -205,6 +205,14 @@
puts the queue time on individual delivery lines), the time is tagged with
"QT=", and it is measured from the time that the message starts to be
received, so it includes the reception time.
+
+18. It is now possible to use both -bF and -bf on the same command, in order to
+ test a system filter and a user filter in the same run. For example:
+
+ exim -bF /system/filter -bf /user/filter </test/message
+
+ This is helpful when the system filter adds header lines or sets filter
+ variables that are used by the user filter.
Version 4.43
Index: exim.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/exim.c,v
retrieving revision 1.9
retrieving revision 1.10
diff -u -r1.9 -r1.10
--- exim.c 18 Nov 2004 11:17:33 -0000 1.9
+++ exim.c 25 Nov 2004 13:54:31 -0000 1.10
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/exim.c,v 1.9 2004/11/18 11:17:33 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/exim.c,v 1.10 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -1169,7 +1169,8 @@
int arg_receive_timeout = -1;
int arg_smtp_receive_timeout = -1;
int arg_error_handling = error_handling;
-int filter_fd = -1;
+int filter_sfd = -1;
+int filter_ufd = -1;
int group_count;
int i;
int list_queue_option = 0;
@@ -1215,7 +1216,6 @@
uschar *ftest_suffix = NULL;
uschar *real_sender_address;
uschar *originator_home = US"/";
-BOOL ftest_system = FALSE;
void *reset_point;
struct passwd *pw;
@@ -1581,20 +1581,32 @@
else if (*argrest == 'e')
expansion_test = checking = TRUE;
- /* -bf: Run in mail filter testing mode
- -bF: Ditto, but for system filters
+ /* -bF: Run system filter test */
+
+ else if (*argrest == 'F')
+ {
+ filter_test |= FTEST_SYSTEM;
+ if (*(++argrest) != 0) { badarg = TRUE; break; }
+ if (++i < argc) filter_test_sfile = argv[i]; else
+ {
+ fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ /* -bf: Run user filter test
-bfd: Set domain for filter testing
-bfl: Set local part for filter testing
-bfp: Set prefix for filter testing
-bfs: Set suffix for filter testing
*/
- else if (*argrest == 'f' || *argrest == 'F')
+ else if (*argrest == 'f')
{
- ftest_system = *argrest++ == 'F';
- if (*argrest == 0)
+ if (*(++argrest) == 0)
{
- if(++i < argc) filter_test = argv[i]; else
+ filter_test |= FTEST_USER;
+ if (++i < argc) filter_test_ufile = argv[i]; else
{
fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
exit(EXIT_FAILURE);
@@ -2761,7 +2773,7 @@
(smtp_input || extract_recipients || recipients_arg < argc) &&
(daemon_listen || queue_interval >= 0 || bi_option ||
test_retry_arg >= 0 || test_rewrite_arg >= 0 ||
- filter_test != NULL || (msg_action_arg > 0 && !one_msg_action))
+ filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action))
) ||
(
msg_action_arg > 0 &&
@@ -2779,19 +2791,19 @@
(
list_options &&
(checking || smtp_input || extract_recipients ||
- filter_test != NULL || bi_option)
+ filter_test != FTEST_NONE || bi_option)
) ||
(
verify_address_mode &&
(address_test_mode || smtp_input || extract_recipients ||
- filter_test != NULL || bi_option)
+ filter_test != FTEST_NONE || bi_option)
) ||
(
address_test_mode && (smtp_input || extract_recipients ||
- filter_test != NULL || bi_option)
+ filter_test != FTEST_NONE || bi_option)
) ||
(
- smtp_input && (sender_address != NULL || filter_test != NULL ||
+ smtp_input && (sender_address != NULL || filter_test != FTEST_NONE ||
extract_recipients)
) ||
(
@@ -2951,7 +2963,7 @@
) || /* OR */
expansion_test /* expansion testing */
|| /* OR */
- filter_test != NULL) /* Filter testing */
+ filter_test != FTEST_NONE) /* Filter testing */
{
setgroups(group_count, group_list);
exim_setugid(real_uid, real_gid, FALSE,
@@ -2974,15 +2986,26 @@
else exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective");
-/* If testing a filter, open the file now, before wasting time doing other
+/* If testing a filter, open the file(s) now, before wasting time doing other
setups and reading the message. */
-if (filter_test != NULL)
+if ((filter_test & FTEST_SYSTEM) != 0)
+ {
+ filter_sfd = Uopen(filter_test_sfile, O_RDONLY, 0);
+ if (filter_sfd < 0)
+ {
+ fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_sfile,
+ strerror(errno));
+ return EXIT_FAILURE;
+ }
+ }
+
+if ((filter_test & FTEST_USER) != 0)
{
- filter_fd = Uopen(filter_test, O_RDONLY,0);
- if (filter_fd < 0)
+ filter_ufd = Uopen(filter_test_ufile, O_RDONLY, 0);
+ if (filter_ufd < 0)
{
- fprintf(stderr, "exim: failed to open %s: %s\n", filter_test,
+ fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_ufile,
strerror(errno));
return EXIT_FAILURE;
}
@@ -3377,11 +3400,11 @@
}
/* If the caller is not trusted, certain arguments are ignored when running for
-real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf). Note
-that authority for performing certain actions on messages is tested in the
+real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF).
+Note that authority for performing certain actions on messages is tested in the
queue_action() function. */
-if (!trusted_caller && !checking && filter_test == NULL)
+if (!trusted_caller && !checking && filter_test == FTEST_NONE)
{
sender_host_name = sender_host_address = interface_address =
sender_ident = received_protocol = NULL;
@@ -3778,7 +3801,7 @@
if (originator_name == NULL)
{
if (sender_address == NULL ||
- (!trusted_caller && filter_test == NULL))
+ (!trusted_caller && filter_test == FTEST_NONE))
{
uschar *name = US pw->pw_gecos;
uschar *amp = Ustrchr(name, '&');
@@ -3912,7 +3935,7 @@
message via SMTP (inetd invocation or otherwise). */
if ((sender_address == NULL && !smtp_input) ||
- (!trusted_caller && filter_test == NULL))
+ (!trusted_caller && filter_test == FTEST_NONE))
{
sender_local = TRUE;
@@ -3943,7 +3966,7 @@
|| /* OR */
(sender_address[0] != 0 && /* Non-empty sender address, AND */
!checking && /* Not running tests, AND */
- filter_test == NULL)) /* Not testing a filter */
+ filter_test == FTEST_NONE)) /* Not testing a filter */
{
sender_address = originator_login;
sender_address_forced = FALSE;
@@ -4153,7 +4176,7 @@
printf("Configuration file is %s\n", config_main_filename);
return EXIT_SUCCESS;
}
- if (filter_test == NULL)
+ if (filter_test == FTEST_NONE)
{
fprintf(stderr,
"Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
@@ -4507,9 +4530,9 @@
}
}
- /* Read the data for the message. If filter_test is true, this will
- just read the headers for the message, and not write anything onto
- the spool. */
+ /* Read the data for the message. If filter_test is not FTEST_NONE, this
+ will just read the headers for the message, and not write anything onto the
+ spool. */
message_ended = END_NOTENDED;
more = receive_msg(extract_recipients);
@@ -4528,7 +4551,7 @@
unless specified. The the return path is set to to the sender unless it has
already been set from a return-path header in the message. */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
deliver_domain = (ftest_domain != NULL)?
ftest_domain : qualify_domain_recipient;
@@ -4563,8 +4586,27 @@
if (ftest_suffix != NULL) printf("Suffix = %s\n", ftest_suffix);
chdir("/"); /* Get away from wherever the user is running this from */
- exim_exit(filter_runtest(filter_fd, ftest_system, more)?
- EXIT_SUCCESS : EXIT_FAILURE);
+
+ /* Now we run either a system filter test, or a user filter test, or both.
+ In the latter case, headers added by the system filter will persist and be
+ available to the user filter. We need to copy the filter variables
+ explicitly. */
+
+ if ((filter_test & FTEST_SYSTEM) != 0)
+ {
+ if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more))
+ exim_exit(EXIT_FAILURE);
+ }
+
+ memcpy(filter_sn, filter_n, sizeof(filter_sn));
+
+ if ((filter_test & FTEST_USER) != 0)
+ {
+ if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more))
+ exim_exit(EXIT_FAILURE);
+ }
+
+ exim_exit(EXIT_SUCCESS);
}
/* Else act on the result of message reception. We should not get here unless
Index: filter.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/filter.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- filter.c 7 Oct 2004 10:39:01 -0000 1.1
+++ filter.c 25 Nov 2004 13:54:31 -0000 1.2
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/filter.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/filter.c,v 1.2 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -1462,7 +1462,7 @@
and filter testing and verification. */
case cond_firsttime:
- yield = filter_test != NULL || message_id[0] == 0 || deliver_firsttime;
+ yield = filter_test != FTEST_NONE || message_id[0] == 0 || deliver_firsttime;
break;
/* Only TRUE if a message is actually being processed; FALSE for address
@@ -1503,7 +1503,7 @@
if (filter_thisaddress != NULL)
{
- if ((filter_test != NULL && debug_selector != 0) ||
+ if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
indent();
@@ -1578,7 +1578,7 @@
case cond_matches:
case cond_MATCHES:
- if ((filter_test != NULL && debug_selector != 0) ||
+ if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
debug_printf("Match expanded arguments:\n");
@@ -1621,7 +1621,7 @@
break;
}
-if ((filter_test != NULL && debug_selector != 0) ||
+if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
indent();
@@ -1729,7 +1729,7 @@
}
filter_n[n[1]] += n[0];
- if (filter_test != NULL) printf("Add %d to n%d\n", n[0], n[1]);
+ if (filter_test != FTEST_NONE) printf("Add %d to n%d\n", n[0], n[1]);
break;
/* A deliver command's argument must be a valid address. Its optional
@@ -1777,7 +1777,7 @@
/* Test case: report what would happen */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%seliver message to: %s%s%s%s\n",
@@ -1818,7 +1818,7 @@
/* Test case: report what would happen */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
if (mode < 0)
@@ -1855,7 +1855,7 @@
case pipe_command:
s = string_copy(commands->args[0].u);
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%sipe message to: %s%s\n", (commands->seen)?
@@ -1911,7 +1911,7 @@
log_fd = -1;
}
log_filename = expargs[0];
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%sogfile %s\n", (commands->seen)? "Seen l" : "L", log_filename);
@@ -1921,7 +1921,7 @@
case logwrite_command:
s = expargs[0];
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%sogwrite \"%s\"\n", (commands->seen)? "Seen l" : "L",
@@ -1983,7 +1983,7 @@
int subtype = commands->args[1].i;
s = expargs[0];
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
printf("Headers %s \"%s\"\n", (subtype == TRUE)? "add" :
(subtype == FALSE)? "remove" : "charset", string_printing(s));
@@ -2042,7 +2042,7 @@
fmsg = string_printing(fmsg);
*error_pointer = fmsg;
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%c%s text \"%s\"\n", toupper(ff_name[0]), ff_name+1, fmsg);
@@ -2051,7 +2051,7 @@
return ff_ret;
case finish_command:
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
indent();
printf("%sinish\n", (commands->seen)? "Seen f" : "F");
@@ -2091,7 +2091,7 @@
case vacation_command:
if (return_path == NULL || return_path[0] == 0)
{
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
printf("%s command ignored because return_path is empty\n",
command_list[commands->command]);
else DEBUG(D_filter) debug_printf("%s command ignored because return_path "
@@ -2180,7 +2180,7 @@
/* Proceed with mail or vacation command */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
uschar *to = commands->args[mailarg_index_to].u;
indent();
@@ -2278,10 +2278,10 @@
break;
case testprint_command:
- if (filter_test != NULL || (debug_selector & D_filter) != 0)
+ if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
{
uschar *s = string_printing(expargs[0]);
- if (filter_test == NULL)
+ if (filter_test == FTEST_NONE)
debug_printf("Filter: testprint: %s\n", s);
else
printf("Testprint: %s\n", s);
@@ -2460,7 +2460,7 @@
if (read_command_list(&ptr, &lastcmdptr, FALSE))
yield = interpret_commands(commands, generated);
-if (filter_test != NULL || (debug_selector & D_filter) != 0)
+if (filter_test != FTEST_NONE || (debug_selector & D_filter) != 0)
{
uschar *s = US"";
switch(yield)
@@ -2493,7 +2493,7 @@
break;
}
- if (filter_test != NULL) printf("%s\n", CS s);
+ if (filter_test != FTEST_NONE) printf("%s\n", CS s);
else debug_printf("%s\n", s);
}
Index: filtertest.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/filtertest.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- filtertest.c 7 Oct 2004 10:39:01 -0000 1.1
+++ filtertest.c 25 Nov 2004 13:54:31 -0000 1.2
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/filtertest.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/filtertest.c,v 1.2 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -15,118 +15,29 @@
/*************************************************
-* Test a mail filter *
+* Read message and set body/size variables *
*************************************************/
-/* This is called when exim is run with the -bf option. The name
-of the filter file is in filter_test, and we are running under an
-unprivileged uid/gid. A test message's headers have been read into
-store, and the body of the message is still accessible on the
-standard input.
+/* We have to read the remainder of the message in order to find its size, so
+we can set up the message_body variables at the same time (in normal use, the
+message_body variables are not set up unless needed). The reading code is
+written out here rather than having options in read_message_data, in order to
+keep that function as efficient as possible. Handling message_body_end is
+somewhat more tedious. Pile it all into a circular buffer and sort out at the
+end.
-Argument:
- fd the standard input fd, containing the message body
- is_system TRUE if testing is to be as a system filter
+Arguments:
dot_ended TRUE if message already terminated by '.'
-Returns: TRUE if no errors
+Returns: nothing
*/
-
-BOOL
-filter_runtest(int fd, BOOL is_system, BOOL dot_ended)
-{
-int rc, body_len, body_end_len, filter_type, header_size;
+
+static void
+read_message_body(dot_ended)
+{
register int ch;
-BOOL yield;
-struct stat statbuf;
-address_item *generated = NULL;
-uschar *error, *filebuf, *s;
-
-/* Read the filter file into store as will be done by the router in a real
-case. */
-
-if (fstat(fd, &statbuf) != 0)
- {
- printf("exim: failed to get size of %s: %s\n", filter_test, strerror(errno));
- return FALSE;
- }
-
-filebuf = store_get(statbuf.st_size + 1);
-rc = read(fd, filebuf, statbuf.st_size);
-close(fd);
-
-if (rc != statbuf.st_size)
- {
- printf("exim: error while reading %s: %s\n", filter_test, strerror(errno));
- return FALSE;
- }
-
-filebuf[statbuf.st_size] = 0;
-
-/* Check the filter type. User filters start with "# Exim filter" or "# Sieve
-filter". If the filter type is not recognized, the file is treated as an
-ordinary .forward file. System filters do not need the "# Exim filter" in order
-to be recognized as Exim filters. */
-
-filter_type = rda_is_filter(filebuf);
-if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
-
-printf("Testing %s file \"%s\"\n\n",
- (filter_type == FILTER_EXIM)? "Exim filter" :
- (filter_type == FILTER_SIEVE)? "Sieve filter" :
- "forward file",
- filter_test);
-
-/* Handle a plain .forward file */
-
-if (filter_type == FILTER_FORWARD)
- {
- yield = parse_forward_list(filebuf,
- RDO_REWRITE,
- &generated, /* for generated addresses */
- &error, /* for errors */
- deliver_domain, /* incoming domain for \name */
- NULL, /* no check on includes */
- NULL); /* fail on syntax errors */
-
- switch(yield)
- {
- case FF_FAIL:
- printf("exim: forward file contains \":fail:\"\n");
- break;
-
- case FF_BLACKHOLE:
- printf("exim: forwardfile contains \":blackhole:\"\n");
- break;
-
- case FF_ERROR:
- printf("exim: error in forward file: %s\n", error);
- return FALSE;
- }
-
- if (generated == NULL)
- printf("exim: no addresses generated from forward file\n");
-
- else
- {
- printf("exim: forward file generated:\n");
- while (generated != NULL)
- {
- printf(" %s\n", generated->address);
- generated = generated->next;
- }
- }
-
- return TRUE;
- }
-
-/* For a filter, we have to read the remainder of the message in order to find
-its size, so we might as well set up the message_body variable at the same time
-(when *really* filtering this is not read unless needed). The reading code is
-written out here rather than having options in read_message_data, in order to
-keep that function as efficient as possible. Handling message_body_end is
-somewhat more tedious. Pile it all into a circular buffer and sort out at the
-end. */
+int body_len, body_end_len, header_size;
+uschar *s;
message_body = store_malloc(message_body_visible + 1);
message_body_end = store_malloc(message_body_visible + 1);
@@ -227,13 +138,125 @@
message_body_end[body_end_len] == 0)
message_body_end[body_end_len] = ' ';
}
+}
+
+
+
+/*************************************************
+* Test a mail filter *
+*************************************************/
+
+/* This is called when exim is run with the -bf option. At this point it is
+running under an unprivileged uid/gid. A test message's headers have been read
+into store, and the body of the message is still accessible on the standard
+input if this is the first time this function has been called. It may be called
+twice if both system and user filters are being tested.
+
+Argument:
+ fd an fd containing the filter file
+ filename the name of the filter file
+ is_system TRUE if testing is to be as a system filter
+ dot_ended TRUE if message already terminated by '.'
+
+Returns: TRUE if no errors
+*/
+
+BOOL
+filter_runtest(int fd, uschar *filename, BOOL is_system, BOOL dot_ended)
+{
+int rc, filter_type;
+BOOL yield;
+struct stat statbuf;
+address_item *generated = NULL;
+uschar *error, *filebuf;
+
+/* Read the filter file into store as will be done by the router in a real
+case. */
+
+if (fstat(fd, &statbuf) != 0)
+ {
+ printf("exim: failed to get size of %s: %s\n", filename, strerror(errno));
+ return FALSE;
+ }
+
+filebuf = store_get(statbuf.st_size + 1);
+rc = read(fd, filebuf, statbuf.st_size);
+close(fd);
+
+if (rc != statbuf.st_size)
+ {
+ printf("exim: error while reading %s: %s\n", filename, strerror(errno));
+ return FALSE;
+ }
+
+filebuf[statbuf.st_size] = 0;
+
+/* Check the filter type. User filters start with "# Exim filter" or "# Sieve
+filter". If the filter type is not recognized, the file is treated as an
+ordinary .forward file. System filters do not need the "# Exim filter" in order
+to be recognized as Exim filters. */
+
+filter_type = rda_is_filter(filebuf);
+if (is_system && filter_type == FILTER_FORWARD) filter_type = FILTER_EXIM;
+
+printf("Testing %s file \"%s\"\n\n",
+ (filter_type == FILTER_EXIM)? "Exim filter" :
+ (filter_type == FILTER_SIEVE)? "Sieve filter" :
+ "forward file",
+ filename);
+
+/* Handle a plain .forward file */
+
+if (filter_type == FILTER_FORWARD)
+ {
+ yield = parse_forward_list(filebuf,
+ RDO_REWRITE,
+ &generated, /* for generated addresses */
+ &error, /* for errors */
+ deliver_domain, /* incoming domain for \name */
+ NULL, /* no check on includes */
+ NULL); /* fail on syntax errors */
+
+ switch(yield)
+ {
+ case FF_FAIL:
+ printf("exim: forward file contains \":fail:\"\n");
+ break;
+
+ case FF_BLACKHOLE:
+ printf("exim: forwardfile contains \":blackhole:\"\n");
+ break;
+
+ case FF_ERROR:
+ printf("exim: error in forward file: %s\n", error);
+ return FALSE;
+ }
+
+ if (generated == NULL)
+ printf("exim: no addresses generated from forward file\n");
+
+ else
+ {
+ printf("exim: forward file generated:\n");
+ while (generated != NULL)
+ {
+ printf(" %s\n", generated->address);
+ generated = generated->next;
+ }
+ }
+
+ return TRUE;
+ }
+
+/* For a filter, set up the message_body variables and the message size if this
+is the first time this function has been called. */
+
+if (message_body == NULL) read_message_body(dot_ended);
/* Now pass the filter file to the function that interprets it. Because
-filter_test is not NULL, the interpreter will output comments about what
-it is doing, but an error message will have to be output here. No need to
-clean up store. The last argument is 0 because Exim has given up root privilege
-when running a filter test, and in any case, as it is a test, is isn't going to
-try writing any files. */
+filter_test is not FILTER_NONE, the interpreter will output comments about what
+it is doing. No need to clean up store. Indeed, we must not, because we may be
+testing a system filter that is going to be followed by a user filter test. */
if (is_system)
{
Index: functions.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/functions.h,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- functions.h 19 Nov 2004 09:45:54 -0000 1.5
+++ functions.h 25 Nov 2004 13:54:31 -0000 1.6
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/functions.h,v 1.5 2004/11/19 09:45:54 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/functions.h,v 1.6 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -92,7 +92,7 @@
extern int filter_interpret(uschar *, int, address_item **, uschar **);
extern BOOL filter_personal(string_item *, BOOL);
-extern BOOL filter_runtest(int, BOOL, BOOL);
+extern BOOL filter_runtest(int, uschar *, BOOL, BOOL);
extern BOOL filter_system_interpret(address_item **, uschar **);
extern void header_add(int, char *, ...);
Index: globals.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/globals.c,v
retrieving revision 1.7
retrieving revision 1.8
diff -u -r1.7 -r1.8
--- globals.c 24 Nov 2004 14:38:13 -0000 1.7
+++ globals.c 25 Nov 2004 13:54:31 -0000 1.8
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/globals.c,v 1.7 2004/11/24 14:38:13 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/globals.c,v 1.8 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -476,7 +476,9 @@
int filter_n[FILTER_VARIABLE_COUNT];
BOOL filter_running = FALSE;
int filter_sn[FILTER_VARIABLE_COUNT];
-uschar *filter_test = NULL;
+int filter_test = FTEST_NONE;
+uschar *filter_test_sfile = NULL;
+uschar *filter_test_ufile = NULL;
uschar *filter_thisaddress = NULL;
int finduser_retries = 0;
uid_t fixed_never_users[] = { FIXED_NEVER_USERS };
Index: globals.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/globals.h,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- globals.h 10 Nov 2004 10:29:56 -0000 1.6
+++ globals.h 25 Nov 2004 13:54:31 -0000 1.7
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/globals.h,v 1.6 2004/11/10 10:29:56 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/globals.h,v 1.7 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -275,7 +275,9 @@
extern int filter_n[FILTER_VARIABLE_COUNT]; /* filter variables */
extern BOOL filter_running; /* TRUE while running a filter */
extern int filter_sn[FILTER_VARIABLE_COUNT]; /* variables set by system filter */
-extern uschar *filter_test; /* Run as a filter tester on this file */
+extern int filter_test; /* Filter test type */
+extern uschar *filter_test_sfile; /* System filter test file */
+extern uschar *filter_test_ufile; /* User filter test file */
extern uschar *filter_thisaddress; /* For address looping */
extern int finduser_retries; /* Retry count for getpwnam() */
extern uid_t fixed_never_users[]; /* Can't be overridden */
Index: macros.h
===================================================================
RCS file: /home/cvs/exim/exim-src/src/macros.h,v
retrieving revision 1.3
retrieving revision 1.4
diff -u -r1.3 -r1.4
--- macros.h 24 Nov 2004 14:38:13 -0000 1.3
+++ macros.h 25 Nov 2004 13:54:31 -0000 1.4
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/macros.h,v 1.3 2004/11/24 14:38:13 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/macros.h,v 1.4 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -227,6 +227,12 @@
CEE_EXEC_PANIC /* Panic-die if exec fails */
};
+/* Bit values for filter_test */
+
+#define FTEST_NONE 0 /* Not filter testing */
+#define FTEST_USER 1 /* Testing user filter */
+#define FTEST_SYSTEM 2 /* Testing system filter */
+
/* Returns from the routing, transport and authentication functions (not all
apply to all of them). Some other functions also use these convenient values,
and some additional values are used only by non-driver functions.
@@ -234,10 +240,10 @@
OK, FAIL, DEFER, and ERROR are also declared in local_scan.h for use in the
local_scan() function. Do not change them unilaterally. */
-#define OK 0 /* Successful match */
-#define DEFER 1 /* Defer - some problem */
-#define FAIL 2 /* Matching failed */
-#define ERROR 3 /* Internal or config error */
+#define OK 0 /* Successful match */
+#define DEFER 1 /* Defer - some problem */
+#define FAIL 2 /* Matching failed */
+#define ERROR 3 /* Internal or config error */
/***********/
#define DECLINE 4 /* Declined to handle the address, pass to next
router unless no_more is set */
Index: receive.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/receive.c,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- receive.c 17 Nov 2004 14:32:25 -0000 1.4
+++ receive.c 25 Nov 2004 13:54:31 -0000 1.5
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/receive.c,v 1.4 2004/11/17 14:32:25 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/receive.c,v 1.5 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -427,7 +427,7 @@
}
else
{
- if (filter_test == NULL)
+ if (filter_test == FTEST_NONE)
{
fprintf(stderr, "\nexim: %s received - message abandoned\n",
(sig == SIGTERM)? "SIGTERM" : "SIGINT");
@@ -1508,18 +1508,18 @@
if (domain == 0 && newsender[0] != 0)
newsender = rewrite_address_qualify(newsender, FALSE);
- if (filter_test != NULL || receive_check_set_sender(newsender))
+ if (filter_test != FTEST_NONE || receive_check_set_sender(newsender))
{
sender_address = newsender;
- if (trusted_caller || filter_test != NULL)
+ if (trusted_caller || filter_test != FTEST_NONE)
{
authenticated_sender = NULL;
originator_name = US"";
sender_local = FALSE;
}
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
printf("Sender taken from \"From \" line\n");
}
}
@@ -1659,7 +1659,7 @@
/* If this is a filter test run and no headers were read, output a warning
in case there is a mistake in the test message. */
-if (filter_test != NULL && header_list->next == NULL)
+if (filter_test != FTEST_NONE && header_list->next == NULL)
printf("Warning: no message headers read\n");
@@ -1781,7 +1781,7 @@
otherwise set. However, remove any <> that surround the address
because the variable doesn't have these. */
- if (filter_test != NULL)
+ if (filter_test != FTEST_NONE)
{
uschar *start = h->text + 12;
uschar *end = start + Ustrlen(start);
@@ -2378,7 +2378,7 @@
testing mode, that is all this function does. Return TRUE if the message
ended with a dot. */
-if (filter_test != NULL)
+if (filter_test != FTEST_NONE)
{
process_info[process_info_len] = 0;
return message_ended == END_DOT;
Index: sieve.c
===================================================================
RCS file: /home/cvs/exim/exim-src/src/sieve.c,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- sieve.c 7 Oct 2004 10:39:01 -0000 1.1
+++ sieve.c 25 Nov 2004 13:54:31 -0000 1.2
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/exim-src/src/sieve.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/exim-src/src/sieve.c,v 1.2 2004/11/25 13:54:31 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
@@ -631,7 +631,7 @@
{
int r=0;
-if ((filter_test != NULL && debug_selector != 0) ||
+if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
{
debug_printf("String comparison (match ");
@@ -728,7 +728,7 @@
break;
}
}
-if ((filter_test != NULL && debug_selector != 0) ||
+if ((filter_test != FTEST_NONE && debug_selector != 0) ||
(debug_selector & D_filter) != 0)
debug_printf(" Result %s\n",r?"true":"false");
return r;
@@ -852,7 +852,7 @@
{
if (Ustrcmp(new_addr->address,addr)==0 && (file ? testflag(new_addr, af_pfr|af_file) : 1))
{
- if ((filter_test != NULL && debug_selector != 0) || (debug_selector & D_filter) != 0)
+ if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
{
debug_printf("Repeated %s `%s' ignored.\n",file ? "fileinto" : "redirect", addr);
}
@@ -860,7 +860,7 @@
}
}
-if ((filter_test != NULL && debug_selector != 0) || (debug_selector & D_filter) != 0)
+if ((filter_test != FTEST_NONE && debug_selector != 0) || (debug_selector & D_filter) != 0)
{
debug_printf("%s `%s'\n",file ? "fileinto" : "redirect", addr);
}
@@ -2771,7 +2771,7 @@
}
#ifndef COMPILE_SYNTAX_CHECKER
-if (filter_test != NULL) printf("%s\n", (const char*) msg);
+if (filter_test != FTEST_NONE) printf("%s\n", (const char*) msg);
else debug_printf("%s\n", msg);
#endif
Index: 597.sfilter
====================================================================
# Exim filter - testing system filter
testprint "--------System filter---------"
testprint "Sender: $sender_address"
testprint "Subject: $h_Subject:"
testprint "Body: $message_body"
headers add "X-Header: Added in a system filter"
add 3 to n1
testprint "n1: $n1"
Index: 597.ufilter
====================================================================
# Exim filter - testing user filter
testprint "--------User filter---------"
testprint "Sender: $sender_address"
testprint "Subject: $header_Subject:"
testprint "Body: $message_body"
testprint "X-Header: $h_X-Header:"
testprint "sn1: $sn1"
Index: 597
====================================================================
# Exim test configuration 597
# Macros are set externally in order to get the path
# of the Exim that is being tested, and the directory
# in which the test data lives.
exim_path = EXIM_PATH
primary_hostname = myhost.test.ex
spool_directory = DIR/spool
# ----- Main settings -----
# End
Index: 597
====================================================================
0 -bf and -bF combined
exim -bf DIR/aux/597.ufilter -bF DIR/aux/597.sfilter -f sen@???
From: a.user@???
Subject: test 1
Body content.
****
Index: 597
====================================================================
Return-path copied from sender
Sender = sen@???
Recipient = ph10@???
Testing Exim filter file "/source/exim4/AutoTest/aux/597.sfilter"
Testprint: --------System filter---------
Testprint: Sender: sen@???
Testprint: Subject: test 1
Testprint: Body: Body content.
Headers add "X-Header: Added in a system filter"
Add 3 to n1
Testprint: n1: 3
Filtering did not set up a significant delivery.
Normal delivery will occur.
Testing Exim filter file "/source/exim4/AutoTest/aux/597.ufilter"
Testprint: --------User filter---------
Testprint: Sender: sen@???
Testprint: Subject: test 1
Testprint: Body: Body content.
Testprint: X-Header: Added in a system filter
Testprint: sn1: 3
Filtering did not set up a significant delivery.
Normal delivery will occur.