Add new errors mail_4xx, data_4xx, lost_connection, tls_required.
[exim.git] / src / src / exim.c
index 37f206d26bfb02a966d5076e1ce4719af175cc03..30ea05c41eb07e0ddb559b02491e7225a5d96b24 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.2 2004/10/14 11:21:02 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.37 2006/03/09 15:10:16 ph10 Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2004 */
+/* Copyright (c) University of Cambridge 1995 - 2006 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -196,9 +196,9 @@ os_non_restarting_signal(SIGALRM, sigalrm_handler);
 
 /* This function is called by millisleep() and exim_wait_tick() to wait for a
 period of time that may include a fraction of a second. The coding is somewhat
 
 /* This function is called by millisleep() and exim_wait_tick() to wait for a
 period of time that may include a fraction of a second. The coding is somewhat
-tedious. We do not expect setitimer() ever to fail, but if it does, the process 
-will wait for ever, so we panic in this instance. (There was a case of this 
-when a bug in a function that calls milliwait() caused it to pass invalid data. 
+tedious. We do not expect setitimer() ever to fail, but if it does, the process
+will wait for ever, so we panic in this instance. (There was a case of this
+when a bug in a function that calls milliwait() caused it to pass invalid data.
 That's when I added the check. :-)
 
 Argument:  an itimerval structure containing the interval
 That's when I added the check. :-)
 
 Argument:  an itimerval structure containing the interval
@@ -214,8 +214,8 @@ sigset_t old_sigmask;
 (void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
 (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask);  /* Block SIGALRM */
 if (setitimer(ITIMER_REAL, itval, NULL) < 0)           /* Start timer */
 (void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
 (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask);  /* Block SIGALRM */
 if (setitimer(ITIMER_REAL, itval, NULL) < 0)           /* Start timer */
-  log_write(0, LOG_MAIN|LOG_PANIC_DIE, 
-    "setitimer() failed: %s", strerror(errno)); 
+  log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+    "setitimer() failed: %s", strerror(errno));
 (void)sigfillset(&sigmask);                            /* All signals */
 (void)sigdelset(&sigmask, SIGALRM);                    /* Remove SIGALRM */
 (void)sigsuspend(&sigmask);                            /* Until SIGALRM */
 (void)sigfillset(&sigmask);                            /* All signals */
 (void)sigdelset(&sigmask, SIGALRM);                    /* Remove SIGALRM */
 (void)sigsuspend(&sigmask);                            /* Until SIGALRM */
@@ -375,6 +375,38 @@ va_end(ap);
 
 
 
 
 
 
+/*************************************************
+*   Call fopen() with umask 777 and adjust mode  *
+*************************************************/
+
+/* Exim runs with umask(0) so that files created with open() have the mode that
+is specified in the open() call. However, there are some files, typically in
+the spool directory, that are created with fopen(). They end up world-writeable
+if no precautions are taken. Although the spool directory is not accessible to
+the world, this is an untidiness. So this is a wrapper function for fopen()
+that sorts out the mode of the created file.
+
+Arguments:
+   filename       the file name
+   options        the fopen() options
+   mode           the required mode
+
+Returns:          the fopened FILE or NULL
+*/
+
+FILE *
+modefopen(uschar *filename, char *options, mode_t mode)
+{
+mode_t saved_umask = umask(0777);
+FILE *f = Ufopen(filename, options);
+(void)umask(saved_umask);
+if (f != NULL) (void)fchmod(fileno(f), mode);
+return f;
+}
+
+
+
+
 /*************************************************
 *   Ensure stdin, stdout, and stderr exist       *
 *************************************************/
 /*************************************************
 *   Ensure stdin, stdout, and stderr exist       *
 *************************************************/
@@ -406,10 +438,10 @@ for (i = 0; i <= 2; i++)
     if (devnull < 0) devnull = open("/dev/null", O_RDWR);
     if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
       string_open_failed(errno, "/dev/null"));
     if (devnull < 0) devnull = open("/dev/null", O_RDWR);
     if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
       string_open_failed(errno, "/dev/null"));
-    if (devnull != i) dup2(devnull, i);
+    if (devnull != i) (void)dup2(devnull, i);
     }
   }
     }
   }
-if (devnull > 2) close(devnull);
+if (devnull > 2) (void)close(devnull);
 }
 
 
 }
 
 
@@ -459,19 +491,19 @@ if (smtp_input)
   #ifdef SUPPORT_TLS
   tls_close(FALSE);      /* Shut down the TLS library */
   #endif
   #ifdef SUPPORT_TLS
   tls_close(FALSE);      /* Shut down the TLS library */
   #endif
-  close(fileno(smtp_in));
-  close(fileno(smtp_out));
+  (void)close(fileno(smtp_in));
+  (void)close(fileno(smtp_out));
   smtp_in = NULL;
   }
 else
   {
   smtp_in = NULL;
   }
 else
   {
-  close(0);                                           /* stdin */
-  if ((debug_selector & D_resolver) == 0)  close(1);  /* stdout */
-  if (debug_selector == 0)                            /* stderr */
+  (void)close(0);                                          /* stdin */
+  if ((debug_selector & D_resolver) == 0) (void)close(1);  /* stdout */
+  if (debug_selector == 0)                                 /* stderr */
     {
     if (!synchronous_delivery)
       {
     {
     if (!synchronous_delivery)
       {
-      close(2);
+      (void)close(2);
       log_stderr = NULL;
       }
     (void)setsid();
       log_stderr = NULL;
       }
     (void)setsid();
@@ -586,7 +618,8 @@ exit(rc);
 *************************************************/
 
 /* Called to extract the port from the values given to -oMa and -oMi.
 *************************************************/
 
 /* Called to extract the port from the values given to -oMa and -oMi.
-It also checks the syntax of the address.
+It also checks the syntax of the address, and terminates it before the
+port data when a port is extracted.
 
 Argument:
   address   the address, with possible port on the end
 
 Argument:
   address   the address, with possible port on the end
@@ -598,8 +631,8 @@ Returns:    the port, or zero if there isn't one
 static int
 check_port(uschar *address)
 {
 static int
 check_port(uschar *address)
 {
-int port = host_extract_port(address);
-if (!string_is_ip_address(address, NULL))
+int port = host_address_extract_port(address);
+if (string_is_ip_address(address, NULL) == 0)
   {
   fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
   exit(EXIT_FAILURE);
   {
   fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
   exit(EXIT_FAILURE);
@@ -622,7 +655,7 @@ Arguments:
   flags        flag bits for verify_address()
   exit_value   to be set for failures
 
   flags        flag bits for verify_address()
   exit_value   to be set for failures
 
-Returns:       nothint
+Returns:       nothing
 */
 
 static void
 */
 
 static void
@@ -640,7 +673,7 @@ if (address == NULL)
 else
   {
   int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1,
 else
   {
   int rc = verify_address(deliver_make_addr(address,TRUE), stdout, flags, -1,
-    -1, NULL, NULL, NULL);
+    -1, -1, NULL, NULL, NULL);
   if (rc == FAIL) *exit_value = 2;
     else if (rc == DEFER && *exit_value == 0) *exit_value = 1;
   }
   if (rc == FAIL) *exit_value = 2;
     else if (rc == DEFER && *exit_value == 0) *exit_value = 1;
   }
@@ -662,6 +695,10 @@ The log options are held in two unsigned ints (because there became too many
 for one). The top bit in the table means "put in 2nd selector". This does not
 yet apply to debug options, so the "=" facility sets only the first selector.
 
 for one). The top bit in the table means "put in 2nd selector". This does not
 yet apply to debug options, so the "=" facility sets only the first selector.
 
+The "all" selector, which must be equal to 0xffffffff, is recognized specially.
+It sets all the bits in both selectors. However, there is a facility for then
+unsetting certain bits, because we want to turn off "memory" in the debug case.
+
 A bad value for a debug setting is treated as an unknown option - error message
 to stderr and die. For log settings, which come from the configuration file,
 we write to the log on the way out...
 A bad value for a debug setting is treated as an unknown option - error message
 to stderr and die. For log settings, which come from the configuration file,
 we write to the log on the way out...
@@ -669,6 +706,8 @@ we write to the log on the way out...
 Arguments:
   selector1      address of the first bit string
   selector2      address of the second bit string, or NULL
 Arguments:
   selector1      address of the first bit string
   selector2      address of the second bit string, or NULL
+  notall1        bits to exclude from "all" for selector1
+  notall2        bits to exclude from "all" for selector2
   string         the configured string
   options        the table of option names
   count          size of table
   string         the configured string
   options        the table of option names
   count          size of table
@@ -678,8 +717,8 @@ Returns:         nothing on success - bomb out on failure
 */
 
 static void
 */
 
 static void
-decode_bits(unsigned int *selector1, unsigned int *selector2, uschar *string,
-  bit_table *options, int count, uschar *which)
+decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
+  int notall2, uschar *string, bit_table *options, int count, uschar *which)
 {
 uschar *errmsg;
 if (string == NULL) return;
 {
 uschar *errmsg;
 if (string == NULL) return;
@@ -732,14 +771,23 @@ else for(;;)
         unsigned int bit = middle->bit;
         unsigned int *selector;
 
         unsigned int bit = middle->bit;
         unsigned int *selector;
 
-        /* The value with all bits set means "set all bits in both selectors"
+        /* The value with all bits set means "force all bits in both selectors"
         in the case where two are being handled. However, the top bit in the
         in the case where two are being handled. However, the top bit in the
-        second selector is never set. */
+        second selector is never set. When setting, some bits can be excluded.
+        */
 
         if (bit == 0xffffffff)
           {
 
         if (bit == 0xffffffff)
           {
-          *selector1 = adding? bit : 0;
-          if (selector2 != NULL) *selector2 = adding? 0x7fffffff : 0;
+          if (adding)
+            {
+            *selector1 = 0xffffffff ^ notall1;
+            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
+            }
+          else
+            {
+            *selector1 = 0;
+            if (selector2 != NULL) *selector2 = 0;
+            }
           }
 
         /* Otherwise, the 0x80000000 bit means "this value, without the top
           }
 
         /* Otherwise, the 0x80000000 bit means "this value, without the top
@@ -817,18 +865,27 @@ fprintf(f, "Using tdb\n");
 #endif
 
 fprintf(f, "Support for:");
 #endif
 
 fprintf(f, "Support for:");
+#ifdef SUPPORT_CRYPTEQ
+  fprintf(f, " crypteq");
+#endif
 #if HAVE_ICONV
   fprintf(f, " iconv()");
 #endif
 #if HAVE_IPV6
   fprintf(f, " IPv6");
 #endif
 #if HAVE_ICONV
   fprintf(f, " iconv()");
 #endif
 #if HAVE_IPV6
   fprintf(f, " IPv6");
 #endif
+#ifdef HAVE_LOGIN_CAP
+  fprintf(f, " use_classresources");
+#endif
 #ifdef SUPPORT_PAM
   fprintf(f, " PAM");
 #endif
 #ifdef EXIM_PERL
   fprintf(f, " Perl");
 #endif
 #ifdef SUPPORT_PAM
   fprintf(f, " PAM");
 #endif
 #ifdef EXIM_PERL
   fprintf(f, " Perl");
 #endif
+#ifdef EXPAND_DLFUNC
+  fprintf(f, " Expand_dlfunc");
+#endif
 #ifdef USE_TCP_WRAPPERS
   fprintf(f, " TCPwrappers");
 #endif
 #ifdef USE_TCP_WRAPPERS
   fprintf(f, " TCPwrappers");
 #endif
@@ -839,6 +896,30 @@ fprintf(f, "Support for:");
   fprintf(f, " OpenSSL");
   #endif
 #endif
   fprintf(f, " OpenSSL");
   #endif
 #endif
+#ifdef SUPPORT_TRANSLATE_IP_ADDRESS
+  fprintf(f, " translate_ip_address");
+#endif
+#ifdef SUPPORT_MOVE_FROZEN_MESSAGES
+  fprintf(f, " move_frozen_messages");
+#endif
+#ifdef WITH_CONTENT_SCAN
+  fprintf(f, " Content_Scanning");
+#endif
+#ifdef WITH_OLD_DEMIME
+  fprintf(f, " Old_Demime");
+#endif
+#ifdef EXPERIMENTAL_SPF
+  fprintf(f, " Experimental_SPF");
+#endif
+#ifdef EXPERIMENTAL_SRS
+  fprintf(f, " Experimental_SRS");
+#endif
+#ifdef EXPERIMENTAL_BRIGHTMAIL
+  fprintf(f, " Experimental_Brightmail");
+#endif
+#ifdef EXPERIMENTAL_DOMAINKEYS
+  fprintf(f, " Experimental_DomainKeys");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups:");
 fprintf(f, "\n");
 
 fprintf(f, "Lookups:");
@@ -881,6 +962,9 @@ fprintf(f, "Lookups:");
 #ifdef LOOKUP_PGSQL
   fprintf(f, " pgsql");
 #endif
 #ifdef LOOKUP_PGSQL
   fprintf(f, " pgsql");
 #endif
+#ifdef LOOKUP_SQLITE
+  fprintf(f, " sqlite");
+#endif
 #ifdef LOOKUP_TESTDB
   fprintf(f, " testdb");
 #endif
 #ifdef LOOKUP_TESTDB
   fprintf(f, " testdb");
 #endif
@@ -963,6 +1047,8 @@ if (fixed_never_users[0] > 0)
     fprintf(f, "%d:", (unsigned int)fixed_never_users[i]);
   fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]);
   }
     fprintf(f, "%d:", (unsigned int)fixed_never_users[i]);
   fprintf(f, "%d\n", (unsigned int)fixed_never_users[i]);
   }
+
+fprintf(f, "Size of off_t: %d\n", sizeof(off_t));
 }
 
 
 }
 
 
@@ -1169,7 +1255,8 @@ uschar **argv = USS cargv;
 int  arg_receive_timeout = -1;
 int  arg_smtp_receive_timeout = -1;
 int  arg_error_handling = error_handling;
 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;
 int  group_count;
 int  i;
 int  list_queue_option = 0;
@@ -1215,7 +1302,6 @@ uschar *ftest_prefix = NULL;
 uschar *ftest_suffix = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
 uschar *ftest_suffix = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
-BOOL ftest_system = FALSE;
 void *reset_point;
 
 struct passwd *pw;
 void *reset_point;
 
 struct passwd *pw;
@@ -1234,7 +1320,7 @@ because some OS define it in /usr/include/unistd.h. */
 
 extern char **environ;
 
 
 extern char **environ;
 
-/* If the Exim user and/or group and/or the configuration file owner were
+/* If the Exim user and/or group and/or the configuration file owner/group were
 defined by ref:name at build time, we must now find the actual uid/gid values.
 This is a feature to make the lives of binary distributors easier. */
 
 defined by ref:name at build time, we must now find the actual uid/gid values.
 This is a feature to make the lives of binary distributors easier. */
 
@@ -1269,6 +1355,15 @@ if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid))
   }
 #endif
 
   }
 #endif
 
+#ifdef CONFIGURE_GROUPNAME
+if (!route_findgroup(US CONFIGURE_GROUPNAME, &config_gid))
+  {
+  fprintf(stderr, "exim: failed to find gid for group name \"%s\"\n",
+    CONFIGURE_GROUPNAME);
+  exit(EXIT_FAILURE);
+  }
+#endif
+
 /* In the Cygwin environment, some initialization needs doing. It is fudged
 in by means of this macro. */
 
 /* In the Cygwin environment, some initialization needs doing. It is fudged
 in by means of this macro. */
 
@@ -1377,10 +1472,17 @@ message_id_external[0] = 'E';
 message_id = message_id_external + 1;
 message_id[0] = 0;
 
 message_id = message_id_external + 1;
 message_id[0] = 0;
 
-/* Set the umask to zero so that any files that Exim creates are created
-with the modes that it specifies. */
+/* Set the umask to zero so that any files Exim creates using open() are
+created with the modes that it specifies. NOTE: Files created with fopen() have
+a problem, which was not recognized till rather late (February 2006). With this
+umask, such files will be world writeable. (They are all content scanning files
+in the spool directory, which isn't world-accessible, so this is not a
+disaster, but it's untidy.) I don't want to change this overall setting,
+however, because it will interact badly with the open() calls. Instead, there's
+now a function called modefopen() that fiddles with the umask while calling
+fopen(). */
 
 
-umask(0);
+(void)umask(0);
 
 /* Precompile the regular expression for matching a message id. Keep this in
 step with the code that generates ids in the accept.c module. We need to do
 
 /* Precompile the regular expression for matching a message id. Keep this in
 step with the code that generates ids in the accept.c module. We need to do
@@ -1572,20 +1674,32 @@ for (i = 1; i < argc; i++)
     else if (*argrest == 'e')
       expansion_test = checking = TRUE;
 
     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
     */
 
        -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);
           {
           fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
           exit(EXIT_FAILURE);
@@ -1854,7 +1968,8 @@ for (i = 1; i < argc; i++)
     break;
 
     /* -d: Set debug level (see also -v below) or set the drop_cr option.
     break;
 
     /* -d: Set debug level (see also -v below) or set the drop_cr option.
-    The latter is now a no-opt, retained for compatibility only. */
+    The latter is now a no-op, retained for compatibility only. If -dd is used,
+    debugging subprocesses of the daemon is disabled. */
 
     case 'd':
     if (Ustrcmp(argrest, "ropcr") == 0)
 
     case 'd':
     if (Ustrcmp(argrest, "ropcr") == 0)
@@ -1870,8 +1985,13 @@ for (i = 1; i < argc; i++)
       unsigned int selector = D_default;
       debug_selector = 0;
       debug_file = NULL;
       unsigned int selector = D_default;
       debug_selector = 0;
       debug_file = NULL;
+      if (*argrest == 'd')
+        {
+        debug_daemon = TRUE;
+        argrest++;
+        }
       if (*argrest != 0)
       if (*argrest != 0)
-        decode_bits(&selector, NULL, argrest, debug_options,
+        decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
           debug_options_count, US"debug");
       debug_selector = selector;
       }
           debug_options_count, US"debug");
       debug_selector = selector;
       }
@@ -1923,6 +2043,7 @@ for (i = 1; i < argc; i++)
         { badarg = TRUE; break; }
       }
     originator_name = argrest;
         { badarg = TRUE; break; }
       }
     originator_name = argrest;
+    sender_name_forced = TRUE;
     break;
 
 
     break;
 
 
@@ -2746,7 +2867,7 @@ if ((
     (smtp_input || extract_recipients || recipients_arg < argc) &&
     (daemon_listen || queue_interval >= 0 || bi_option ||
       test_retry_arg >= 0 || test_rewrite_arg >= 0 ||
     (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 &&
     ) ||
     (
     msg_action_arg > 0 &&
@@ -2764,19 +2885,19 @@ if ((
     (
     list_options &&
     (checking || smtp_input || extract_recipients ||
     (
     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 ||
     ) ||
     (
     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 ||
     ) ||
     (
     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)
     ) ||
     (
       extract_recipients)
     ) ||
     (
@@ -2828,12 +2949,21 @@ else
       strerror(errno));
     rlp.rlim_cur = rlp.rlim_max = 0;
     }
       strerror(errno));
     rlp.rlim_cur = rlp.rlim_max = 0;
     }
+
+  /* I originally chose 1000 as a nice big number that was unlikely to
+  be exceeded. It turns out that some older OS have a fixed upper limit of
+  256. */
+
   if (rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = 1000;
     if (setrlimit(RLIMIT_NOFILE, &rlp) < 0)
   if (rlp.rlim_cur < 1000)
     {
     rlp.rlim_cur = rlp.rlim_max = 1000;
     if (setrlimit(RLIMIT_NOFILE, &rlp) < 0)
-      log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NOFILE) failed: %s",
-        strerror(errno));
+      {
+      rlp.rlim_cur = rlp.rlim_max = 256;
+      if (setrlimit(RLIMIT_NOFILE, &rlp) < 0)
+        log_write(0, LOG_MAIN|LOG_PANIC, "setrlimit(RLIMIT_NOFILE) failed: %s",
+          strerror(errno));
+      }
     }
   #endif
 
     }
   #endif
 
@@ -2927,7 +3057,7 @@ if ((                                            /* EITHER */
     ) ||                                         /*   OR   */
     expansion_test                               /* expansion testing */
     ||                                           /*   OR   */
     ) ||                                         /*   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,
   {
   setgroups(group_count, group_list);
   exim_setugid(real_uid, real_gid, FALSE,
@@ -2950,15 +3080,26 @@ privileged user. */
 
 else exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective");
 
 
 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. */
 
 setups and reading the message. */
 
-if (filter_test != NULL)
+if ((filter_test & FTEST_SYSTEM) != 0)
   {
   {
-  filter_fd = Uopen(filter_test, O_RDONLY,0);
-  if (filter_fd < 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,
+    fprintf(stderr, "exim: failed to open %s: %s\n", filter_test_sfile,
+      strerror(errno));
+    return EXIT_FAILURE;
+    }
+  }
+
+if ((filter_test & FTEST_USER) != 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_ufile,
       strerror(errno));
     return EXIT_FAILURE;
     }
       strerror(errno));
     return EXIT_FAILURE;
     }
@@ -2972,7 +3113,7 @@ readconf_main();
 
 /* Handle the decoding of logging options. */
 
 
 /* Handle the decoding of logging options. */
 
-decode_bits(&log_write_selector, &log_extra_selector, log_selector_string,
+decode_bits(&log_write_selector, &log_extra_selector, 0, 0, log_selector_string,
   log_options, log_options_count, US"log");
 
 DEBUG(D_any)
   log_options, log_options_count, US"log");
 
 DEBUG(D_any)
@@ -3169,8 +3310,8 @@ a debugging feature for finding out what arguments certain MUAs actually use.
 Don't attempt it if logging is disabled, or if listing variables or if
 verifying/testing addresses or expansions. */
 
 Don't attempt it if logging is disabled, or if listing variables or if
 verifying/testing addresses or expansions. */
 
-if ((log_extra_selector & LX_arguments) != 0 && really_exim
-     && !list_options && !checking)
+if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
+      && really_exim && !list_options && !checking)
   {
   int i;
   uschar *p = big_buffer;
   {
   int i;
   uschar *p = big_buffer;
@@ -3202,18 +3343,23 @@ if ((log_extra_selector & LX_arguments) != 0 && really_exim
       (p - big_buffer) - 4), printing, quote);
     while (*p) p++;
     }
       (p - big_buffer) - 4), printing, quote);
     while (*p) p++;
     }
-  log_write(0, LOG_MAIN, "%s", big_buffer);
+
+  if ((log_extra_selector & LX_arguments) != 0)
+    log_write(0, LOG_MAIN, "%s", big_buffer);
+  else
+    debug_printf("%s\n", big_buffer);
   }
 
 /* Set the working directory to be the top-level spool directory. We don't rely
 on this in the code, which always uses fully qualified names, but it's useful
 for core dumps etc. Don't complain if it fails - the spool directory might not
 be generally accessible and calls with the -C option (and others) have lost
   }
 
 /* Set the working directory to be the top-level spool directory. We don't rely
 on this in the code, which always uses fully qualified names, but it's useful
 for core dumps etc. Don't complain if it fails - the spool directory might not
 be generally accessible and calls with the -C option (and others) have lost
-privilege by now. */
+privilege by now. Before the chdir, we try to ensure that the directory exists.
+*/
 
 if (Uchdir(spool_directory) != 0)
   {
 
 if (Uchdir(spool_directory) != 0)
   {
-  (void)directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, TRUE);
+  (void)directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, FALSE);
   (void)Uchdir(spool_directory);
   }
 
   (void)Uchdir(spool_directory);
   }
 
@@ -3225,7 +3371,7 @@ script. */
 
 if (bi_option)
   {
 
 if (bi_option)
   {
-  fclose(config_file);
+  (void)fclose(config_file);
   if (bi_command != NULL)
     {
     int i = 0;
   if (bi_command != NULL)
     {
     int i = 0;
@@ -3353,11 +3499,11 @@ if (real_uid != root_uid && real_uid != exim_uid &&
   }
 
 /* If the caller is not trusted, certain arguments are ignored when running for
   }
 
 /* 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. */
 
 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;
   {
   sender_host_name = sender_host_address = interface_address =
     sender_ident = received_protocol = NULL;
@@ -3385,7 +3531,7 @@ barf. */
 if (smtp_input)
   {
   union sockaddr_46 inetd_sock;
 if (smtp_input)
   {
   union sockaddr_46 inetd_sock;
-  SOCKLEN_T size = sizeof(inetd_sock);
+  EXIM_SOCKLEN_T size = sizeof(inetd_sock);
   if (getpeername(0, (struct sockaddr *)(&inetd_sock), &size) == 0)
     {
     int family = ((struct sockaddr *)(&inetd_sock))->sa_family;
   if (getpeername(0, (struct sockaddr *)(&inetd_sock), &size) == 0)
     {
     int family = ((struct sockaddr *)(&inetd_sock))->sa_family;
@@ -3579,11 +3725,13 @@ if (test_retry_arg >= 0)
       return EXIT_FAILURE;
       }
 
       return EXIT_FAILURE;
       }
 
-    /* For the rcpt_4xx errors, a value of 255 means "any", and a code > 100 as
-    an error is for matching codes to the decade. Turn them into a real error
-    code, off the decade. */
+    /* For the {MAIL,RCPT,DATA}_4xx errors, a value of 255 means "any", and a
+    code > 100 as an error is for matching codes to the decade. Turn them into
+    a real error code, off the decade. */
 
 
-    if (basic_errno == ERRNO_RCPT4XX)
+    if (basic_errno == ERRNO_MAIL4XX ||
+        basic_errno == ERRNO_RCPT4XX ||
+        basic_errno == ERRNO_DATA4XX)
       {
       int code = (more_errno >> 8) & 255;
       if (code == 255)
       {
       int code = (more_errno >> 8) & 255;
       if (code == 255)
@@ -3754,7 +3902,7 @@ for (i = 0;;)
     if (originator_name == NULL)
       {
       if (sender_address == NULL ||
     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, '&');
         {
         uschar *name = US pw->pw_gecos;
         uschar *amp = Ustrchr(name, '&');
@@ -3817,7 +3965,7 @@ for (i = 0;;)
 
 /* If we cannot get a user login, log the incident and give up, unless the
 configuration specifies something to use. When running in the test harness,
 
 /* If we cannot get a user login, log the incident and give up, unless the
 configuration specifies something to use. When running in the test harness,
-any setting of unknown_login overrides the actual login name. */
+any setting of unknown_login overrides the actual name. */
 
 if (originator_login == NULL || running_in_test_harness)
   {
 
 if (originator_login == NULL || running_in_test_harness)
   {
@@ -3851,12 +3999,17 @@ DEBUG(D_receive) debug_printf("originator: uid=%d gid=%d login=%s name=%s\n",
 
 /* Run in daemon and/or queue-running mode. The function daemon_go() never
 returns. We leave this till here so that the originator_ fields are available
 
 /* Run in daemon and/or queue-running mode. The function daemon_go() never
 returns. We leave this till here so that the originator_ fields are available
-for incoming messages via the daemon. */
+for incoming messages via the daemon. The daemon cannot be run in mua_wrapper
+mode. */
 
 if (daemon_listen || queue_interval > 0)
   {
 
 if (daemon_listen || queue_interval > 0)
   {
-  if (mua_wrapper) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be "
-    "run when mua_wrapper is set");
+  if (mua_wrapper)
+    {
+    fprintf(stderr, "Daemon cannot be run when mua_wrapper is set\n");
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Daemon cannot be run when "
+      "mua_wrapper is set");
+    }
   daemon_go();
   }
 
   daemon_go();
   }
 
@@ -3888,7 +4041,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) ||
 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;
 
   {
   sender_local = TRUE;
 
@@ -3919,7 +4072,7 @@ if ((!smtp_input && sender_address == NULL) ||
        ||                                /*         OR            */
        (sender_address[0] != 0 &&        /* Non-empty sender address, AND */
        !checking &&                      /* Not running tests, AND */
        ||                                /*         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;
     {
     sender_address = originator_login;
     sender_address_forced = FALSE;
@@ -4070,11 +4223,23 @@ call to find the ident for. */
 
 if (host_checking)
   {
 
 if (host_checking)
   {
+  int x[4];
+  int size;
+
   sender_ident = NULL;
   if (running_in_test_harness && sender_host_port != 0 &&
       interface_address != NULL && interface_port != 0)
     verify_get_ident(1413);
 
   sender_ident = NULL;
   if (running_in_test_harness && sender_host_port != 0 &&
       interface_address != NULL && interface_port != 0)
     verify_get_ident(1413);
 
+  /* In case the given address is a non-canonical IPv6 address, canonicize
+  it. The code works for both IPv4 and IPv6, as it happens. */
+
+  size = host_aton(sender_host_address, x);
+  sender_host_address = store_get(48);  /* large enough for full IPv6 */
+  (void)host_nmtoa(size, x, -1, sender_host_address, ':');
+
+  /* Now set up for testing */
+
   host_build_sender_fullhost();
   smtp_input = TRUE;
   smtp_in = stdin;
   host_build_sender_fullhost();
   smtp_input = TRUE;
   smtp_in = stdin;
@@ -4117,7 +4282,7 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     printf("Configuration file is %s\n", config_main_filename);
     return EXIT_SUCCESS;
     }
     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"
     {
     fprintf(stderr,
 "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
@@ -4170,7 +4335,7 @@ sender_ident. */
 
 else if (is_inetd)
   {
 
 else if (is_inetd)
   {
-  fclose(stderr);
+  (void)fclose(stderr);
   exim_nullstd();                       /* Re-open to /dev/null */
   verify_get_ident(IDENT_PORT);
   host_build_sender_fullhost();
   exim_nullstd();                       /* Re-open to /dev/null */
   verify_get_ident(IDENT_PORT);
   host_build_sender_fullhost();
@@ -4200,7 +4365,7 @@ else if (!is_inetd) sender_host_unknown = TRUE;
 if exim is started from inetd. In this case fd 0 will be set to the socket,
 but fd 1 will not be set. This also happens for passed SMTP channels. */
 
 if exim is started from inetd. In this case fd 0 will be set to the socket,
 but fd 1 will not be set. This also happens for passed SMTP channels. */
 
-if (fstat(1, &statbuf) < 0) dup2(0, 1);
+if (fstat(1, &statbuf) < 0) (void)dup2(0, 1);
 
 /* Set up the incoming protocol name and the state of the program. Root
 is allowed to force received protocol via the -oMr option above, and if we are
 
 /* Set up the incoming protocol name and the state of the program. Root
 is allowed to force received protocol via the -oMr option above, and if we are
@@ -4376,6 +4541,11 @@ while (more)
     int count = argc - recipients_arg;
     uschar **list = argv + recipients_arg;
 
     int count = argc - recipients_arg;
     uschar **list = argv + recipients_arg;
 
+    /* These options cannot be changed dynamically for non-SMTP messages */
+
+    active_local_sender_retain = local_sender_retain;
+    active_local_from_check = local_from_check;
+
     /* Save before any rewriting */
 
     raw_sender = string_copy(sender_address);
     /* Save before any rewriting */
 
     raw_sender = string_copy(sender_address);
@@ -4466,9 +4636,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);
 
     message_ended = END_NOTENDED;
     more = receive_msg(extract_recipients);
@@ -4487,7 +4657,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. */
 
   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;
     {
     deliver_domain = (ftest_domain != NULL)?
       ftest_domain : qualify_domain_recipient;
@@ -4521,9 +4691,28 @@ while (more)
     if (ftest_prefix != NULL) printf("Prefix    = %s\n", ftest_prefix);
     if (ftest_suffix != NULL) printf("Suffix    = %s\n", ftest_suffix);
 
     if (ftest_prefix != NULL) printf("Prefix    = %s\n", ftest_prefix);
     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);
+    (void)chdir("/");   /* Get away from wherever the user is running this from */
+
+    /* 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
     }
 
   /* Else act on the result of message reception. We should not get here unless
@@ -4579,11 +4768,16 @@ while (more)
   /* Else do the delivery unless the ACL or local_scan() called for queue only
   or froze the message. Always deliver in a separate process. A fork failure is
   not a disaster, as the delivery will eventually happen on a subsequent queue
   /* Else do the delivery unless the ACL or local_scan() called for queue only
   or froze the message. Always deliver in a separate process. A fork failure is
   not a disaster, as the delivery will eventually happen on a subsequent queue
-  run. */
+  run. The search cache must be tidied before the fork, as the parent will
+  do it before exiting. The child will trigger a lookup failure and
+  thereby defer the delivery if it tries to use (for example) a cached ldap
+  connection that the parent has called unbind on. */
 
   else if (!queue_only_policy && !deliver_freeze)
     {
     pid_t pid;
 
   else if (!queue_only_policy && !deliver_freeze)
     {
     pid_t pid;
+    search_tidyup();
+
     if ((pid = fork()) == 0)
       {
       int rc;
     if ((pid = fork()) == 0)
       {
       int rc;