Expand the documentation about the new submission mode behaviour.
[exim.git] / src / src / exim.c
index 6d227c33ece80e4fa8cd91d2533100dd045f53dd..32ddb96d24265ec1f45b5e1189281516656f11a4 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/exim.c,v 1.1 2004/10/07 10:39:01 ph10 Exp $ */
+/* $Cambridge: exim/src/src/exim.c,v 1.19 2005/05/03 14:20:01 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. */
 
 
@@ -196,7 +196,10 @@ 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
-tedious...
+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
 Returns:   nothing
@@ -210,7 +213,9 @@ sigset_t old_sigmask;
 (void)sigemptyset(&sigmask);                           /* Empty mask */
 (void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
 (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask);  /* Block SIGALRM */
-(void)setitimer(ITIMER_REAL, itval, NULL);             /* Start timer */
+if (setitimer(ITIMER_REAL, itval, NULL) < 0)           /* Start timer */
+  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 */
@@ -594,7 +599,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);
@@ -617,7 +622,7 @@ Arguments:
   flags        flag bits for verify_address()
   exit_value   to be set for failures
 
-Returns:       nothint
+Returns:       nothing
 */
 
 static void
@@ -635,7 +640,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;
   }
@@ -824,6 +829,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
@@ -834,6 +842,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:");
@@ -1164,7 +1190,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;
@@ -1210,7 +1237,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;
@@ -1229,7 +1255,7 @@ because some OS define it in /usr/include/unistd.h. */
 
 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. */
 
@@ -1264,6 +1290,15 @@ if (!route_finduser(US CONFIGURE_OWNERNAME, NULL, &config_uid))
   }
 #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. */
 
@@ -1567,20 +1602,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);
@@ -1849,7 +1896,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)
@@ -1865,6 +1913,11 @@ 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,
           debug_options_count, US"debug");
@@ -2741,7 +2794,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 &&
@@ -2759,19 +2812,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)
     ) ||
     (
@@ -2823,12 +2876,21 @@ else
       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)
-      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
 
@@ -2922,7 +2984,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,
@@ -2945,15 +3007,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;
     }
@@ -3164,8 +3237,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;
@@ -3197,7 +3270,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
@@ -3348,11 +3425,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;
@@ -3380,7 +3457,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;
@@ -3749,7 +3826,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, '&');
@@ -3883,7 +3960,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;
 
@@ -3914,7 +3991,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;
@@ -4065,11 +4142,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;
@@ -4112,7 +4201,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"
@@ -4371,6 +4460,11 @@ while (more)
     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);
@@ -4461,9 +4555,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);
@@ -4482,7 +4576,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;
@@ -4517,8 +4611,27 @@ while (more)
     if (ftest_suffix != NULL) printf("Suffix    = %s\n", ftest_suffix);
 
     chdir("/");   /* Get away from wherever the user is running this from */
-    exim_exit(filter_runtest(filter_fd, ftest_system, more)?
-      EXIT_SUCCESS : EXIT_FAILURE);
+
+    /* Now we run either a system filter test, or a user filter test, or both.
+    In the latter case, headers added by the system filter will persist and be
+    available to the user filter. We need to copy the filter variables
+    explicitly. */
+
+    if ((filter_test & FTEST_SYSTEM) != 0)
+      {
+      if (!filter_runtest(filter_sfd, filter_test_sfile, TRUE, more))
+        exim_exit(EXIT_FAILURE);
+      }
+
+    memcpy(filter_sn, filter_n, sizeof(filter_sn));
+
+    if ((filter_test & FTEST_USER) != 0)
+      {
+      if (!filter_runtest(filter_ufd, filter_test_ufile, FALSE, more))
+        exim_exit(EXIT_FAILURE);
+      }
+
+    exim_exit(EXIT_SUCCESS);
     }
 
   /* Else act on the result of message reception. We should not get here unless
@@ -4574,17 +4687,28 @@ 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
-  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;
+    search_tidyup();
+
     if ((pid = fork()) == 0)
       {
       int rc;
       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). */