Added "success_on_redirect" address verification option
[exim.git] / src / src / exim.c
index 8f51e66fa01da5acedf50d3243d0d1ef39bd942a..5cc0cb47c8ebbd3c9d4de4fb09ae1209955561d3 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.6 2004/10/19 11:40:52 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.22 2005/06/28 10:23:35 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. */
 
 
@@ -406,10 +406,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 != i) dup2(devnull, i);
+    if (devnull != i) (void)dup2(devnull, i);
     }
   }
-if (devnull > 2) close(devnull);
+if (devnull > 2) (void)close(devnull);
 }
 
 
@@ -459,19 +459,19 @@ if (smtp_input)
   #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
   {
-  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)
       {
-      close(2);
+      (void)close(2);
       log_stderr = NULL;
       }
     (void)setsid();
@@ -586,7 +586,8 @@ exit(rc);
 *************************************************/
 
 /* 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
@@ -599,7 +600,7 @@ static int
 check_port(uschar *address)
 {
 int port = host_extract_port(address);
-if (!string_is_ip_address(address, NULL))
+if (string_is_ip_address(address, NULL) == 0)
   {
   fprintf(stderr, "exim abandoned: \"%s\" is not an IP address\n", address);
   exit(EXIT_FAILURE);
@@ -622,7 +623,7 @@ Arguments:
   flags        flag bits for verify_address()
   exit_value   to be set for failures
 
-Returns:       nothint
+Returns:       nothing
 */
 
 static void
@@ -640,7 +641,7 @@ if (address == NULL)
 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;
   }
@@ -662,6 +663,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.
 
+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...
@@ -669,6 +674,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
+  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
@@ -678,8 +685,8 @@ Returns:         nothing on success - bomb out on failure
 */
 
 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;
@@ -732,14 +739,23 @@ else for(;;)
         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
-        second selector is never set. */
+        second selector is never set. When setting, some bits can be excluded.
+        */
 
         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
@@ -829,6 +845,9 @@ fprintf(f, "Support for:");
 #ifdef EXIM_PERL
   fprintf(f, " Perl");
 #endif
+#ifdef EXPAND_DLFUNC
+  fprintf(f, " Expand_dlfunc");
+#endif
 #ifdef USE_TCP_WRAPPERS
   fprintf(f, " TCPwrappers");
 #endif
@@ -839,6 +858,24 @@ fprintf(f, "Support for:");
   fprintf(f, " OpenSSL");
   #endif
 #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:");
@@ -1169,7 +1206,8 @@ uschar **argv = USS cargv;
 int  arg_receive_timeout = -1;
 int  arg_smtp_receive_timeout = -1;
 int  arg_error_handling = error_handling;
-int  filter_fd = -1;
+int  filter_sfd = -1;
+int  filter_ufd = -1;
 int  group_count;
 int  i;
 int  list_queue_option = 0;
@@ -1215,7 +1253,6 @@ uschar *ftest_prefix = NULL;
 uschar *ftest_suffix = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
-BOOL ftest_system = FALSE;
 void *reset_point;
 
 struct passwd *pw;
@@ -1581,20 +1618,32 @@ for (i = 1; i < argc; i++)
     else if (*argrest == 'e')
       expansion_test = checking = TRUE;
 
-    /* -bf:  Run in mail filter testing mode
-       -bF:  Ditto, but for system filters
+    /* -bF:  Run system filter test */
+
+    else if (*argrest == 'F')
+      {
+      filter_test |= FTEST_SYSTEM;
+      if (*(++argrest) != 0) { badarg = TRUE; break; }
+      if (++i < argc) filter_test_sfile = argv[i]; else
+        {
+        fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
+        exit(EXIT_FAILURE);
+        }
+      }
+
+    /* -bf:  Run user filter test
        -bfd: Set domain for filter testing
        -bfl: Set local part for filter testing
        -bfp: Set prefix for filter testing
        -bfs: Set suffix for filter testing
     */
 
-    else if (*argrest == 'f' || *argrest == 'F')
+    else if (*argrest == 'f')
       {
-      ftest_system = *argrest++ == 'F';
-      if (*argrest == 0)
+      if (*(++argrest) == 0)
         {
-        if(++i < argc) filter_test = argv[i]; else
+        filter_test |= FTEST_USER;
+        if (++i < argc) filter_test_ufile = argv[i]; else
           {
           fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
           exit(EXIT_FAILURE);
@@ -1863,7 +1912,8 @@ for (i = 1; i < argc; i++)
     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)
@@ -1879,8 +1929,13 @@ for (i = 1; i < argc; i++)
       unsigned int selector = D_default;
       debug_selector = 0;
       debug_file = NULL;
+      if (*argrest == 'd')
+        {
+        debug_daemon = TRUE;
+        argrest++;
+        }
       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;
       }
@@ -2755,7 +2810,7 @@ if ((
     (smtp_input || extract_recipients || recipients_arg < argc) &&
     (daemon_listen || queue_interval >= 0 || bi_option ||
       test_retry_arg >= 0 || test_rewrite_arg >= 0 ||
-      filter_test != NULL || (msg_action_arg > 0 && !one_msg_action))
+      filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action))
     ) ||
     (
     msg_action_arg > 0 &&
@@ -2773,19 +2828,19 @@ if ((
     (
     list_options &&
     (checking || smtp_input || extract_recipients ||
-      filter_test != NULL || bi_option)
+      filter_test != FTEST_NONE || bi_option)
     ) ||
     (
     verify_address_mode &&
     (address_test_mode || smtp_input || extract_recipients ||
-      filter_test != NULL || bi_option)
+      filter_test != FTEST_NONE || bi_option)
     ) ||
     (
     address_test_mode && (smtp_input || extract_recipients ||
-      filter_test != NULL || bi_option)
+      filter_test != FTEST_NONE || bi_option)
     ) ||
     (
-    smtp_input && (sender_address != NULL || filter_test != NULL ||
+    smtp_input && (sender_address != NULL || filter_test != FTEST_NONE ||
       extract_recipients)
     ) ||
     (
@@ -2945,7 +3000,7 @@ if ((                                            /* EITHER */
     ) ||                                         /*   OR   */
     expansion_test                               /* expansion testing */
     ||                                           /*   OR   */
-    filter_test != NULL)                         /* Filter testing */
+    filter_test != FTEST_NONE)                   /* Filter testing */
   {
   setgroups(group_count, group_list);
   exim_setugid(real_uid, real_gid, FALSE,
@@ -2968,15 +3023,26 @@ privileged user. */
 
 else exim_setugid(geteuid(), getegid(), FALSE, US"forcing real = effective");
 
-/* If testing a filter, open the file now, before wasting time doing other
+/* If testing a filter, open the file(s) now, before wasting time doing other
 setups and reading the message. */
 
-if (filter_test != NULL)
+if ((filter_test & FTEST_SYSTEM) != 0)
   {
-  filter_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;
     }
@@ -2990,7 +3056,7 @@ readconf_main();
 
 /* 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)
@@ -3187,8 +3253,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. */
 
-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;
@@ -3220,7 +3286,11 @@ if ((log_extra_selector & LX_arguments) != 0 && really_exim
       (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
@@ -3243,7 +3313,7 @@ script. */
 
 if (bi_option)
   {
-  fclose(config_file);
+  (void)fclose(config_file);
   if (bi_command != NULL)
     {
     int i = 0;
@@ -3371,11 +3441,11 @@ if (real_uid != root_uid && real_uid != exim_uid &&
   }
 
 /* If the caller is not trusted, certain arguments are ignored when running for
-real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf). Note
-that authority for performing certain actions on messages is tested in the
+real, but are permitted when checking things (-be, -bv, -bt, -bh, -bf, -bF).
+Note that authority for performing certain actions on messages is tested in the
 queue_action() function. */
 
-if (!trusted_caller && !checking && filter_test == NULL)
+if (!trusted_caller && !checking && filter_test == FTEST_NONE)
   {
   sender_host_name = sender_host_address = interface_address =
     sender_ident = received_protocol = NULL;
@@ -3403,7 +3473,7 @@ barf. */
 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;
@@ -3772,7 +3842,7 @@ for (i = 0;;)
     if (originator_name == NULL)
       {
       if (sender_address == NULL ||
-           (!trusted_caller && filter_test == NULL))
+           (!trusted_caller && filter_test == FTEST_NONE))
         {
         uschar *name = US pw->pw_gecos;
         uschar *amp = Ustrchr(name, '&');
@@ -3869,12 +3939,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
-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 (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();
   }
 
@@ -3906,7 +3981,7 @@ unless a trusted caller supplies a sender address with -f, or is passing in the
 message via SMTP (inetd invocation or otherwise). */
 
 if ((sender_address == NULL && !smtp_input) ||
-    (!trusted_caller && filter_test == NULL))
+    (!trusted_caller && filter_test == FTEST_NONE))
   {
   sender_local = TRUE;
 
@@ -3937,7 +4012,7 @@ if ((!smtp_input && sender_address == NULL) ||
        ||                                /*         OR            */
        (sender_address[0] != 0 &&        /* Non-empty sender address, AND */
        !checking &&                      /* Not running tests, AND */
-       filter_test == NULL))             /* Not testing a filter */
+       filter_test == FTEST_NONE))       /* Not testing a filter */
     {
     sender_address = originator_login;
     sender_address_forced = FALSE;
@@ -4088,11 +4163,23 @@ call to find the ident for. */
 
 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);
 
+  /* 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;
@@ -4135,7 +4222,7 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     printf("Configuration file is %s\n", config_main_filename);
     return EXIT_SUCCESS;
     }
-  if (filter_test == NULL)
+  if (filter_test == FTEST_NONE)
     {
     fprintf(stderr,
 "Exim is a Mail Transfer Agent. It is normally called by Mail User Agents,\n"
@@ -4188,7 +4275,7 @@ sender_ident. */
 
 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();
@@ -4218,7 +4305,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 (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
@@ -4489,9 +4576,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);
@@ -4510,7 +4597,7 @@ while (more)
   unless specified. The the return path is set to to the sender unless it has
   already been set from a return-path header in the message. */
 
-  if (filter_test != NULL)
+  if (filter_test != FTEST_NONE)
     {
     deliver_domain = (ftest_domain != NULL)?
       ftest_domain : qualify_domain_recipient;
@@ -4544,9 +4631,28 @@ while (more)
     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
@@ -4618,6 +4724,12 @@ while (more)
       close_unwanted();      /* Close unwanted file descriptors and TLS */
       exim_nullstd();        /* Ensure std{in,out,err} exist */
 
+      /* Occasionally in the test harness we don't have synchronous delivery
+      set (can happen with bounces). In that case, let the old process finish
+      before continuing, to keep the debug output the same. */
+
+      if (running_in_test_harness && !synchronous_delivery) millisleep(100);
+
       /* Re-exec Exim if we need to regain privilege (note: in mua_wrapper
       mode, deliver_drop_privilege is forced TRUE). */