Treat ECONNRESET the same as an end-of-file indication.
[exim.git] / src / src / filtertest.c
index 0cdee0304dc7902664626b5bea973fc3775e53ed..092febaa486352ff5eafba01468e40db05a5abcb 100644 (file)
@@ -1,10 +1,10 @@
-/* $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.7 2005/08/30 10:07:58 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2005 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 
 
 /*************************************************
-*            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)
+static void
+read_message_body(BOOL dot_ended)
 {
-int rc, body_len, body_end_len, filter_type, header_size;
 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);
+(void)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)
   {
@@ -248,7 +271,7 @@ if (is_system)
 else
   {
   yield = (filter_type == FILTER_SIEVE)?
-    sieve_interpret(filebuf, RDO_REWRITE, NULL, &generated, &error)
+    sieve_interpret(filebuf, RDO_REWRITE, NULL, NULL, NULL, &generated, &error)
     :
     filter_interpret(filebuf, RDO_REWRITE, &generated, &error);
   }