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 2166acb47ec7e0feea8e0ad85cb7891834738ec1..35c446d33efbdb199cb640d266cc9ddb9a2906dd 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 cd5fe982178cad826314487866b88b6f5b686e85..a9980534c3c6726c3f4529d0234728d9289cbe7d 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 0496b39c529a3d7014ab427c8137a99a582c7392..f8e31469df8de8db129be37b9f5d0dccb77fc284 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 e643cdeec1aa301aa22193972ec8a3ec5236b850..46dcd9100e46e3522e6164d3271bb1dc814d0088 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 0e8e790a4799638d6a5630cfb8b4f400fa20b64c..c876a8a686a1c418a27220d32f47f16f7ecc9637 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 0cdee0304dc7902664626b5bea973fc3775e53ed..40da59b1770d0383240a83af04ccd4a8874a098c 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 8b9f3bbe96343759bced2376258fc55911d6f787..32a3c288a97dfc9cb41e5fb2a2702a09ebd60569 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 53a9f0a00a183d0718e6c74af7a071a973e85276..f51033dcbec7d7241f619eeae9900243fd9ba322 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 59154403e7460841dafd29e5f3a5319da6108b94..44731b5854952c7beddacd09fd95a0b007f5df01 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 3b440785156c3d0ce44819a42c515e47e17ab935..996e2e089e89ea58b4a3c1bc6611f921fdaa0ca3 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 f9a2d3c69e4c82ac515cfa9c43f7ce671f8119e3..0ed05f1db213f8cacc59c62d4c7ea48991f05646 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 4301d1ab7d6520e6dab7b6b085af619c3bd2b37e..c684e34a9de4f086e9ae7177b156368d618a9c85 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