Allow both -bf and -bF in the same test run.
authorPhilip Hazel <ph10@hermes.cam.ac.uk>
Thu, 25 Nov 2004 13:54:30 +0000 (13:54 +0000)
committerPhilip Hazel <ph10@hermes.cam.ac.uk>
Thu, 25 Nov 2004 13:54:30 +0000 (13:54 +0000)
12 files changed:
doc/doc-misc/WishList
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
src/src/exim.c
src/src/filter.c
src/src/filtertest.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/macros.h
src/src/receive.c
src/src/sieve.c

index 2166acb..35c446d 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-misc/WishList,v 1.10 2004/11/25 11:03:37 ph10 Exp $
+$Cambridge: exim/doc/doc-misc/WishList,v 1.11 2004/11/25 13:54:30 ph10 Exp $
 
 EXIM 4 WISH LIST
 ----------------
@@ -1603,14 +1603,6 @@ A modifier that sets a delay between lines for multiline responses.
 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
 
 Various initiatives like SPF and DomainKeys require header lines to be added
index cd5fe98..a998053 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/ChangeLog,v 1.40 2004/11/25 10:26:04 ph10 Exp $
+$Cambridge: 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
 -------------------------------------------
@@ -176,6 +176,8 @@ Exim version 4.44
     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 0496b39..f8e3146 100644 (file)
@@ -1,4 +1,4 @@
-$Cambridge: exim/doc/doc-txt/NewStuff,v 1.17 2004/11/24 16:36:19 ph10 Exp $
+$Cambridge: exim/doc/doc-txt/NewStuff,v 1.18 2004/11/25 13:54:31 ph10 Exp $
 
 New Features in Exim
 --------------------
@@ -206,6 +206,14 @@ Version 4.44
     "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 e643cde..46dcd91 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.9 2004/11/18 11:17:33 ph10 Exp $ */
+/* $Cambridge: 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 @@ uschar **argv = USS cargv;
 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_prefix = NULL;
 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 @@ for (i = 1; i < argc; i++)
     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 @@ if ((
     (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 @@ if ((
     (
     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 @@ if ((                                            /* EITHER */
     ) ||                                         /*   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 @@ privileged user. */
 
 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 (real_uid != root_uid && real_uid != exim_uid &&
   }
 
 /* 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 @@ for (i = 0;;)
     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 @@ unless a trusted caller supplies a sender address with -f, or is passing in the
 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 @@ if ((!smtp_input && sender_address == NULL) ||
        ||                                /*         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 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     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 @@ while (more)
         }
       }
 
-    /* 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 @@ while (more)
   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 @@ while (more)
     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 0e8e790..c876a8a 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/filter.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: 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 @@ switch (c->type)
   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 @@ switch (c->type)
 
     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 @@ switch (c->type)
 
     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 @@ switch (c->type)
   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 @@ while (commands != NULL)
       }
 
     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 @@ while (commands != NULL)
 
     /* 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 @@ while (commands != NULL)
 
     /* Test case: report what would happen */
 
-    if (filter_test != NULL)
+    if (filter_test != FTEST_NONE)
       {
       indent();
       if (mode < 0)
@@ -1855,7 +1855,7 @@ while (commands != NULL)
 
     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 @@ while (commands != NULL)
       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 @@ while (commands != NULL)
     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 @@ while (commands != NULL)
       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 @@ while (commands != NULL)
     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 @@ while (commands != NULL)
     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 @@ while (commands != NULL)
     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 @@ while (commands != NULL)
 
     /* 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 @@ while (commands != NULL)
     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 @@ ptr = nextsigchar(ptr, TRUE);
 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 @@ if (filter_test != NULL || (debug_selector & D_filter) != 0)
     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 0cdee03..40da59b 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/filtertest.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/filtertest.c,v 1.2 2004/11/25 13:54:31 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 
 
 /*************************************************
-*            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 @@ while (body_end_len > 0)
       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 8b9f3bb..32a3c28 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/functions.h,v 1.5 2004/11/19 09:45:54 ph10 Exp $ */
+/* $Cambridge: 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     expand_string_integer(uschar *);
 
 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 53a9f0a..f51033d 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.c,v 1.7 2004/11/24 14:38:13 ph10 Exp $ */
+/* $Cambridge: 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 @@ uschar *extra_local_interfaces = NULL;
 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 5915440..44731b5 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/globals.h,v 1.6 2004/11/10 10:29:56 ph10 Exp $ */
+/* $Cambridge: 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 uschar *extra_local_interfaces; /* Local, non-listen interfaces */
 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 3b44078..996e2e0 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/macros.h,v 1.3 2004/11/24 14:38:13 ph10 Exp $ */
+/* $Cambridge: 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 @@ enum {
   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 @@ and some additional values are used only by non-driver functions.
 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 f9a2d3c..0ed05f1 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/receive.c,v 1.4 2004/11/17 14:32:25 ph10 Exp $ */
+/* $Cambridge: 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 @@ if (smtp_input)
   }
 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 @@ for (;;)
           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 (smtp_input && (receive_feof)())
 /* 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 @@ for (h = header_list->next; h != NULL; h = h->next)
     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 @@ DEBUG(D_receive)
 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 4301d1a..c684e34 100644 (file)
@@ -1,4 +1,4 @@
-/* $Cambridge: exim/src/src/sieve.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: 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 @@ static int compare(struct Sieve *filter, const struct String *needle, const stru
 {
 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 @@ switch (mt)
     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 @@ for (new_addr=*generated; new_addr; new_addr=new_addr->next)
   {
   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 @@ for (new_addr=*generated; new_addr; new_addr=new_addr->next)
     }
   }
 
-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 @@ else
   }
 
 #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