[exim-cvs] cvs commit: exim/exim-doc/doc-misc WishList exim…

Pàgina inicial
Delete this message
Reply to this message
Autor: Philip Hazel
Data:  
A: exim-cvs
Assumpte: [exim-cvs] cvs commit: exim/exim-doc/doc-misc WishList exim/exim-doc/doc-txt ChangeLog NewStuff exim/exim-src/src exim.c filter.c filtertest.c functions.h globals.c globals.h macros.h receive.c sie
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.