Add the Exim version to process info (exiwhat)
[exim.git] / src / src / exim.c
index b53d6ca9b98afb2c61f3639fd8ed9417f41ea361..ceec9cb2a98d551067fd76d0f499e80f15452722 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2015 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 
@@ -12,6 +12,13 @@ Also a few functions that don't naturally fit elsewhere. */
 
 #include "exim.h"
 
+#ifdef USE_GNUTLS
+# include <gnutls/gnutls.h>
+# if GNUTLS_VERSION_NUMBER < 0x030103 && !defined(DISABLE_OCSP)
+#  define DISABLE_OCSP
+# endif
+#endif
+
 extern void init_lookup_list(void);
 
 
@@ -52,6 +59,16 @@ store_free(block);
 
 
 
+/*************************************************
+*         Enums for cmdline interface            *
+*************************************************/
+
+enum commandline_info { CMDINFO_NONE=0,
+  CMDINFO_HELP, CMDINFO_SIEVE, CMDINFO_DSCP };
+
+
+
+
 /*************************************************
 *  Compile regular expression and panic on fail  *
 *************************************************/
@@ -71,7 +88,7 @@ Returns:      pointer to the compiled pattern
 */
 
 const pcre *
-regex_must_compile(uschar *pattern, BOOL caseless, BOOL use_malloc)
+regex_must_compile(const uschar *pattern, BOOL caseless, BOOL use_malloc)
 {
 int offset;
 int options = PCRE_COPT;
@@ -83,7 +100,7 @@ if (use_malloc)
   pcre_free = function_store_free;
   }
 if (caseless) options |= PCRE_CASELESS;
-yield = pcre_compile(CS pattern, options, (const char **)&error, &offset, NULL);
+yield = pcre_compile(CCS pattern, options, (const char **)&error, &offset, NULL);
 pcre_malloc = function_store_get;
 pcre_free = function_dummy_free;
 if (yield == NULL)
@@ -114,10 +131,11 @@ Returns:      TRUE or FALSE
 */
 
 BOOL
-regex_match_and_setup(const pcre *re, uschar *subject, int options, int setup)
+regex_match_and_setup(const pcre *re, const uschar *subject, int options, int setup)
 {
 int ovector[3*(EXPAND_MAXN+1)];
-int n = pcre_exec(re, NULL, CS subject, Ustrlen(subject), 0,
+uschar * s = string_copy(subject);     /* de-constifying */
+int n = pcre_exec(re, NULL, CS s, Ustrlen(s), 0,
   PCRE_EOPT | options, ovector, sizeof(ovector)/sizeof(int));
 BOOL yield = n >= 0;
 if (n == 0) n = EXPAND_MAXN + 1;
@@ -127,7 +145,7 @@ if (yield)
   expand_nmax = (setup < 0)? 0 : setup + 1;
   for (nn = (setup < 0)? 0 : 2; nn < n*2; nn += 2)
     {
-    expand_nstring[expand_nmax] = subject + ovector[nn];
+    expand_nstring[expand_nmax] = s + ovector[nn];
     expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
     }
   expand_nmax--;
@@ -257,6 +275,10 @@ 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. :-)
 
+We assume it to be not worth sleeping for under 100us; this value will
+require revisiting as hardware advances.  This avoids the issue of
+a zero-valued timer setting meaning "never fire".
+
 Argument:  an itimerval structure containing the interval
 Returns:   nothing
 */
@@ -266,6 +288,9 @@ milliwait(struct itimerval *itval)
 {
 sigset_t sigmask;
 sigset_t old_sigmask;
+
+if (itval->it_value.tv_usec < 100 && itval->it_value.tv_sec == 0)
+  return;
 (void)sigemptyset(&sigmask);                           /* Empty mask */
 (void)sigaddset(&sigmask, SIGALRM);                    /* Add SIGALRM */
 (void)sigprocmask(SIG_BLOCK, &sigmask, &old_sigmask);  /* Block SIGALRM */
@@ -388,10 +413,11 @@ if (exim_tvcmp(&now_tv, then_tv) <= 0)
     {
     if (!running_in_test_harness)
       {
-      debug_printf("tick check: %lu.%06lu %lu.%06lu\n",
-        then_tv->tv_sec, then_tv->tv_usec, now_tv.tv_sec, now_tv.tv_usec);
-      debug_printf("waiting %lu.%06lu\n", itval.it_value.tv_sec,
-        itval.it_value.tv_usec);
+      debug_printf("tick check: " TIME_T_FMT ".%06lu " TIME_T_FMT ".%06lu\n",
+        then_tv->tv_sec, (long) then_tv->tv_usec,
+               now_tv.tv_sec, (long) now_tv.tv_usec);
+      debug_printf("waiting " TIME_T_FMT ".%06lu\n",
+        itval.it_value.tv_sec, (long) itval.it_value.tv_usec);
       }
     }
 
@@ -516,7 +542,7 @@ close_unwanted(void)
 if (smtp_input)
   {
   #ifdef SUPPORT_TLS
-  tls_close(FALSE);      /* Shut down the TLS library */
+  tls_close(TRUE, FALSE);      /* Shut down the TLS library */
   #endif
   (void)close(fileno(smtp_in));
   (void)close(fileno(smtp_out));
@@ -794,6 +820,15 @@ fprintf(f, "Support for:");
 #ifdef WITH_OLD_DEMIME
   fprintf(f, " Old_Demime");
 #endif
+#ifndef DISABLE_DNSSEC
+  fprintf(f, " DNSSEC");
+#endif
+#ifndef DISABLE_PRDR
+  fprintf(f, " PRDR");
+#endif
+#ifndef DISABLE_OCSP
+  fprintf(f, " OCSP");
+#endif
 #ifdef EXPERIMENTAL_SPF
   fprintf(f, " Experimental_SPF");
 #endif
@@ -803,9 +838,33 @@ fprintf(f, "Support for:");
 #ifdef EXPERIMENTAL_BRIGHTMAIL
   fprintf(f, " Experimental_Brightmail");
 #endif
+#ifdef EXPERIMENTAL_DANE
+  fprintf(f, " Experimental_DANE");
+#endif
 #ifdef EXPERIMENTAL_DCC
   fprintf(f, " Experimental_DCC");
 #endif
+#ifdef EXPERIMENTAL_DMARC
+  fprintf(f, " Experimental_DMARC");
+#endif
+#ifdef EXPERIMENTAL_DSN_INFO
+  fprintf(f, " Experimental_DSN_info");
+#endif
+#ifdef EXPERIMENTAL_INTERNATIONAL
+  fprintf(f, " Experimental_International");
+#endif
+#ifdef EXPERIMENTAL_PROXY
+  fprintf(f, " Experimental_Proxy");
+#endif
+#ifdef EXPERIMENTAL_EVENT
+  fprintf(f, " Experimental_Event");
+#endif
+#ifdef EXPERIMENTAL_REDIS
+  fprintf(f, " Experimental_Redis");
+#endif
+#ifdef EXPERIMENTAL_SOCKS
+  fprintf(f, " Experimental_SOCKS");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Lookups (built-in):");
@@ -881,6 +940,9 @@ fprintf(f, "Authenticators:");
 #ifdef AUTH_SPA
   fprintf(f, " spa");
 #endif
+#ifdef AUTH_TLS
+  fprintf(f, " tls");
+#endif
 fprintf(f, "\n");
 
 fprintf(f, "Routers:");
@@ -969,34 +1031,34 @@ DEBUG(D_any) do {
 #ifdef SUPPORT_TLS
   tls_version_report(f);
 #endif
+#ifdef EXPERIMENTAL_INTERNATIONAL
+  utf8_version_report(f);
+#endif
 
-  for (authi = auths_available; *authi->driver_name != '\0'; ++authi) {
-    if (authi->version_report) {
+  for (authi = auths_available; *authi->driver_name != '\0'; ++authi)
+    if (authi->version_report)
       (*authi->version_report)(f);
-    }
-  }
 
+  /* PCRE_PRERELEASE is either defined and empty or a bare sequence of
+  characters; unless it's an ancient version of PCRE in which case it
+  is not defined. */
+#ifndef PCRE_PRERELEASE
+#define PCRE_PRERELEASE
+#endif
+#define QUOTE(X) #X
+#define EXPAND_AND_QUOTE(X) QUOTE(X)
   fprintf(f, "Library version: PCRE: Compile: %d.%d%s\n"
              "                       Runtime: %s\n",
           PCRE_MAJOR, PCRE_MINOR,
-          /* PRE_PRERELEASE is either defined and empty or a string.
-           * unless its an ancient version of PCRE in which case it
-           * is not defined */
-#ifdef PCRE_PRERELEASE
-# define STRINGIFY(x) #x
-          STRINGIFY(PCRE_PRERELEASE) "",
-# undef STRINGIFY
-#else
-          "",
-#endif
+          EXPAND_AND_QUOTE(PCRE_PRERELEASE) "",
           pcre_version());
+#undef QUOTE
+#undef EXPAND_AND_QUOTE
 
   init_lookup_list();
   for (i = 0; i < lookup_list_count; i++)
-    {
     if (lookup_list[i]->version_report)
       lookup_list[i]->version_report(f);
-    }
 
 #ifdef WHITELIST_D_MACROS
   fprintf(f, "WHITELIST_D_MACROS: \"%s\"\n", WHITELIST_D_MACROS);
@@ -1013,6 +1075,39 @@ DEBUG(D_any) do {
 }
 
 
+/*************************************************
+*     Show auxiliary information about Exim      *
+*************************************************/
+
+static void
+show_exim_information(enum commandline_info request, FILE *stream)
+{
+const uschar **pp;
+
+switch(request)
+  {
+  case CMDINFO_NONE:
+    fprintf(stream, "Oops, something went wrong.\n");
+    return;
+  case CMDINFO_HELP:
+    fprintf(stream,
+"The -bI: flag takes a string indicating which information to provide.\n"
+"If the string is not recognised, you'll get this help (on stderr).\n"
+"\n"
+"  exim -bI:help    this information\n"
+"  exim -bI:dscp    dscp value keywords known\n"
+"  exim -bI:sieve   list of supported sieve extensions, one per line.\n"
+);
+    return;
+  case CMDINFO_SIEVE:
+    for (pp = exim_sieve_extension_list; *pp; ++pp)
+      fprintf(stream, "%s\n", *pp);
+    return;
+  case CMDINFO_DSCP:
+    dscp_list_to_stream(stream);
+    return;
+  }
+}
 
 
 /*************************************************
@@ -1390,6 +1485,8 @@ BOOL checking = FALSE;
 BOOL count_queue = FALSE;
 BOOL expansion_test = FALSE;
 BOOL extract_recipients = FALSE;
+BOOL flag_G = FALSE;
+BOOL flag_n = FALSE;
 BOOL forced_delivery = FALSE;
 BOOL f_end_dot = FALSE;
 BOOL deliver_give_up = FALSE;
@@ -1410,6 +1507,7 @@ BOOL verify_as_sender = FALSE;
 BOOL version_printed = FALSE;
 uschar *alias_arg = NULL;
 uschar *called_as = US"";
+uschar *cmdline_syslog_name = NULL;
 uschar *start_queue_run_id = NULL;
 uschar *stop_queue_run_id = NULL;
 uschar *expansion_test_message = NULL;
@@ -1417,9 +1515,11 @@ uschar *ftest_domain = NULL;
 uschar *ftest_localpart = NULL;
 uschar *ftest_prefix = NULL;
 uschar *ftest_suffix = NULL;
+uschar *log_oneline = NULL;
 uschar *malware_test_file = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
+size_t sz;
 void *reset_point;
 
 struct passwd *pw;
@@ -1428,6 +1528,10 @@ pid_t passed_qr_pid = (pid_t)0;
 int passed_qr_pipe = -1;
 gid_t group_list[NGROUPS_MAX];
 
+/* For the -bI: flag */
+enum commandline_info info_flag = CMDINFO_NONE;
+BOOL info_stdout = FALSE;
+
 /* Possible options for -R and -S */
 
 static uschar *rsopts[] = { US"f", US"ff", US"r", US"rf", US"rff" };
@@ -1538,6 +1642,10 @@ if (log_buffer == NULL)
   exit(EXIT_FAILURE);
   }
 
+/* Initialize the default log options. */
+
+bits_set(log_selector, log_selector_size, log_default);
+
 /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
 NULL when the daemon is run and the file is closed. We have to use this
 indirection, because some systems don't allow writing to the variable "stderr".
@@ -1648,6 +1756,8 @@ regex_whitelisted_macro =
   regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
 #endif
 
+for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
+
 
 /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
 this seems to be a generally accepted convention, since one finds symbolic
@@ -1826,6 +1936,26 @@ for (i = 1; i < argc; i++)
 
   switch(switchchar)
     {
+
+    /* sendmail uses -Ac and -Am to control which .cf file is used;
+    we ignore them. */
+    case 'A':
+    if (*argrest == '\0') { badarg = TRUE; break; }
+    else
+      {
+      BOOL ignore = FALSE;
+      switch (*argrest)
+        {
+        case 'c':
+        case 'm':
+          if (*(argrest + 1) == '\0')
+            ignore = TRUE;
+          break;
+        }
+      if (!ignore) { badarg = TRUE; break; }
+      }
+    break;
+
     /* -Btype is a sendmail option for 7bit/8bit setting. Exim is 8-bit clean
     so has no need of it. */
 
@@ -1927,6 +2057,32 @@ for (i = 1; i < argc; i++)
 
     else if (Ustrcmp(argrest, "i") == 0) bi_option = TRUE;
 
+    /* -bI: provide information, of the type to follow after a colon.
+    This is an Exim flag. */
+
+    else if (argrest[0] == 'I' && Ustrlen(argrest) >= 2 && argrest[1] == ':')
+      {
+      uschar *p = &argrest[2];
+      info_flag = CMDINFO_HELP;
+      if (Ustrlen(p))
+        {
+        if (strcmpic(p, CUS"sieve") == 0)
+          {
+          info_flag = CMDINFO_SIEVE;
+          info_stdout = TRUE;
+          }
+        else if (strcmpic(p, CUS"dscp") == 0)
+          {
+          info_flag = CMDINFO_DSCP;
+          info_stdout = TRUE;
+          }
+        else if (strcmpic(p, CUS"help") == 0)
+          {
+          info_stdout = TRUE;
+          }
+        }
+      }
+
     /* -bm: Accept and deliver message - the default option. Reinstate
     receiving_message, which got turned off for all -b options. */
 
@@ -2172,7 +2328,7 @@ for (i = 1; i < argc; i++)
               if (nr_configs)
                 {
                 int sep = 0;
-                uschar *list = argrest;
+                const uschar *list = argrest;
                 uschar *filename;
                 while (trusted_config && (filename = string_nextinlist(&list,
                         &sep, big_buffer, big_buffer_size)) != NULL)
@@ -2304,8 +2460,8 @@ for (i = 1; i < argc; i++)
         argrest++;
         }
       if (*argrest != 0)
-        decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
-          debug_options_count, US"debug", 0);
+        decode_bits(&selector, 1, debug_notall, argrest,
+          debug_options, debug_options_count, US"debug", 0);
       debug_selector = selector;
       }
     break;
@@ -2377,7 +2533,7 @@ for (i = 1; i < argc; i++)
 
     case 'f':
       {
-      int start, end;
+      int dummy_start, dummy_end;
       uschar *errmess;
       if (*argrest == 0)
         {
@@ -2385,9 +2541,7 @@ for (i = 1; i < argc; i++)
           { badarg = TRUE; break; }
         }
       if (*argrest == 0)
-        {
         sender_address = string_sprintf("");  /* Ensure writeable memory */
-        }
       else
         {
         uschar *temp = argrest + Ustrlen(argrest) - 1;
@@ -2395,8 +2549,15 @@ for (i = 1; i < argc; i++)
         if (temp >= argrest && *temp == '.') f_end_dot = TRUE;
         allow_domain_literals = TRUE;
         strip_trailing_dot = TRUE;
-        sender_address = parse_extract_address(argrest, &errmess, &start, &end,
-          &sender_address_domain, TRUE);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+       allow_utf8_domains = TRUE;
+#endif
+        sender_address = parse_extract_address(argrest, &errmess,
+          &dummy_start, &dummy_end, &sender_address_domain, TRUE);
+#ifdef EXPERIMENTAL_INTERNATIONAL
+       message_smtputf8 =  string_is_utf8(sender_address);
+       allow_utf8_domains = FALSE;
+#endif
         allow_domain_literals = FALSE;
         strip_trailing_dot = FALSE;
         if (sender_address == NULL)
@@ -2409,9 +2570,13 @@ for (i = 1; i < argc; i++)
       }
     break;
 
-    /* This is some Sendmail thing which can be ignored */
+    /* -G: sendmail invocation to specify that it's a gateway submission and
+    sendmail may complain about problems instead of fixing them.
+    We make it equivalent to an ACL "control = suppress_local_fixups" and do
+    not at this time complain about problems. */
 
     case 'G':
+    flag_G = TRUE;
     break;
 
     /* -h: Set the hop count for an incoming message. Exim does not currently
@@ -2436,6 +2601,29 @@ for (i = 1; i < argc; i++)
     break;
 
 
+    /* -L: set the identifier used for syslog; equivalent to setting
+    syslog_processname in the config file, but needs to be an admin option. */
+
+    case 'L':
+    if (*argrest == '\0')
+      {
+      if(++i < argc) argrest = argv[i]; else
+        { badarg = TRUE; break; }
+      }
+    sz = Ustrlen(argrest);
+    if (sz > 32)
+      {
+      fprintf(stderr, "exim: the -L syslog name is too long: \"%s\"\n", argrest);
+      return EXIT_FAILURE;
+      }
+    if (sz < 1)
+      {
+      fprintf(stderr, "exim: the -L syslog name is too short\n");
+      return EXIT_FAILURE;
+      }
+    cmdline_syslog_name = argrest;
+    break;
+
     case 'M':
     receiving_message = FALSE;
 
@@ -2513,6 +2701,14 @@ for (i = 1; i < argc; i++)
       break;
       }
 
+    /* -MCD: set the smtp_use_dsn flag; this indicates that the host
+       that exim is connected to supports the esmtp extension DSN */
+    else if (Ustrcmp(argrest, "CD") == 0)
+      {
+      smtp_use_dsn = TRUE;
+      break;
+      }
+
     /* -MCP: set the smtp_use_pipelining flag; this is useful only when
     it preceded -MC (see above) */
 
@@ -2698,10 +2894,12 @@ for (i = 1; i < argc; i++)
     break;
 
 
-    /* -n: This means "don't alias" in sendmail, apparently. Just ignore
-    it. */
+    /* -n: This means "don't alias" in sendmail, apparently.
+    For normal invocations, it has no effect.
+    It may affect some other options. */
 
     case 'n':
+    flag_n = TRUE;
     break;
 
     /* -O: Just ignore it. In sendmail, apparently -O option=value means set
@@ -2844,6 +3042,23 @@ for (i = 1; i < argc; i++)
 
       else if (Ustrcmp(argrest, "Mi") == 0) interface_address = argv[++i];
 
+      /* -oMm: Message reference */
+
+      else if (Ustrcmp(argrest, "Mm") == 0)
+        {
+        if (!mac_ismsgid(argv[i+1]))
+          {
+            fprintf(stderr,"-oMm must be a valid message ID\n");
+            exit(EXIT_FAILURE);
+          }
+        if (!trusted_config)
+          {
+            fprintf(stderr,"-oMm must be called by a trusted user/config\n");
+            exit(EXIT_FAILURE);
+          }
+          message_reference = argv[++i];
+        }
+
       /* -oMr: Received protocol */
 
       else if (Ustrcmp(argrest, "Mr") == 0) received_protocol = argv[++i];
@@ -3149,7 +3364,7 @@ for (i = 1; i < argc; i++)
     /* -tls-on-connect: don't wait for STARTTLS (for old clients) */
 
     #ifdef SUPPORT_TLS
-    else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_on_connect = TRUE;
+    else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_in.on_connect = TRUE;
     #endif
 
     else badarg = TRUE;
@@ -3190,6 +3405,27 @@ for (i = 1; i < argc; i++)
     if (*argrest != 0) badarg = TRUE;
     break;
 
+    /* -X: in sendmail: takes one parameter, logfile, and sends debugging
+    logs to that file.  We swallow the parameter and otherwise ignore it. */
+
+    case 'X':
+    if (*argrest == '\0')
+      if (++i >= argc)
+        {
+        fprintf(stderr, "exim: string expected after -X\n");
+        exit(EXIT_FAILURE);
+        }
+    break;
+
+    case 'z':
+    if (*argrest == '\0')
+      if (++i < argc) log_oneline = argv[i]; else
+        {
+        fprintf(stderr, "exim: file name expected after %s\n", argv[i-1]);
+        exit(EXIT_FAILURE);
+        }
+    break;
+
     /* All other initial characters are errors */
 
     default:
@@ -3488,22 +3724,89 @@ is equivalent to the ability to modify a setuid binary!
 This needs to happen before we read the main configuration. */
 init_lookup_list();
 
+#ifdef EXPERIMENTAL_INTERNATIONAL
+if (running_in_test_harness) smtputf8_advertise_hosts = NULL;
+#endif
+
 /* Read the main runtime configuration data; this gives up if there
 is a failure. It leaves the configuration file open so that the subsequent
 configuration data for delivery can be read if needed. */
 
 readconf_main();
 
+/* If an action on specific messages is requested, or if a daemon or queue
+runner is being started, we need to know if Exim was called by an admin user.
+This is the case if the real user is root or exim, or if the real group is
+exim, or if one of the supplementary groups is exim or a group listed in
+admin_groups. We don't fail all message actions immediately if not admin_user,
+since some actions can be performed by non-admin users. Instead, set admin_user
+for later interrogation. */
+
+if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid)
+  admin_user = TRUE;
+else
+  {
+  int i, j;
+  for (i = 0; i < group_count; i++)
+    {
+    if (group_list[i] == exim_gid) admin_user = TRUE;
+    else if (admin_groups != NULL)
+      {
+      for (j = 1; j <= (int)(admin_groups[0]); j++)
+        if (admin_groups[j] == group_list[i])
+          { admin_user = TRUE; break; }
+      }
+    if (admin_user) break;
+    }
+  }
+
+/* Another group of privileged users are the trusted users. These are root,
+exim, and any caller matching trusted_users or trusted_groups. Trusted callers
+are permitted to specify sender_addresses with -f on the command line, and
+other message parameters as well. */
+
+if (real_uid == root_uid || real_uid == exim_uid)
+  trusted_caller = TRUE;
+else
+  {
+  int i, j;
+
+  if (trusted_users != NULL)
+    {
+    for (i = 1; i <= (int)(trusted_users[0]); i++)
+      if (trusted_users[i] == real_uid)
+        { trusted_caller = TRUE; break; }
+    }
+
+  if (!trusted_caller && trusted_groups != NULL)
+    {
+    for (i = 1; i <= (int)(trusted_groups[0]); i++)
+      {
+      if (trusted_groups[i] == real_gid)
+        trusted_caller = TRUE;
+      else for (j = 0; j < group_count; j++)
+        {
+        if (trusted_groups[i] == group_list[j])
+          { trusted_caller = TRUE; break; }
+        }
+      if (trusted_caller) break;
+      }
+    }
+  }
+
 /* Handle the decoding of logging options. */
 
-decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
+decode_bits(log_selector, log_selector_size, log_notall,
   log_selector_string, log_options, log_options_count, US"log", 0);
 
 DEBUG(D_any)
   {
+  int i;
   debug_printf("configuration file is %s\n", config_main_filename);
-  debug_printf("log selectors = %08x %08x\n", log_write_selector,
-    log_extra_selector);
+  debug_printf("log selectors =");
+  for (i = 0; i < log_selector_size; i++)
+    debug_printf(" %08x", log_selector[i]);
+  debug_printf("\n");
   }
 
 /* If domain literals are not allowed, check the sender address that was
@@ -3525,6 +3828,24 @@ if (sender_address != NULL)
     }
   }
 
+/* See if an admin user overrode our logging. */
+
+if (cmdline_syslog_name != NULL)
+  {
+  if (admin_user)
+    {
+    syslog_processname = cmdline_syslog_name;
+    log_file_path = string_copy(CUS"syslog");
+    }
+  else
+    {
+    /* not a panic, non-privileged users should not be able to spam paniclog */
+    fprintf(stderr,
+        "exim: you lack sufficient privilege to specify syslog process name\n");
+    return EXIT_FAILURE;
+    }
+  }
+
 /* Paranoia check of maximum lengths of certain strings. There is a check
 on the length of the log file path in log.c, which will come into effect
 if there are any calls to write the log earlier than this. However, if we
@@ -3552,6 +3873,17 @@ if (Ustrlen(syslog_processname) > 32)
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
     "syslog_processname is longer than 32 chars: aborting");
 
+if (log_oneline)
+  {
+  if (admin_user)
+    {
+    log_write(0, LOG_MAIN, "%s", log_oneline);
+    return EXIT_SUCCESS;
+    }
+  else
+    return EXIT_FAILURE;
+  }
+
 /* In some operating systems, the environment variable TMPDIR controls where
 temporary files are created; Exim doesn't use these (apart from when delivering
 to MBX mailboxes), but called libraries such as DBM libraries may require them.
@@ -3681,20 +4013,21 @@ 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 (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
+if (((debug_selector & D_any) != 0 || LOGGING(arguments))
       && really_exim && !list_options && !checking)
   {
   int i;
   uschar *p = big_buffer;
-  Ustrcpy(p, "cwd=");
-  (void)getcwd(CS p+4, big_buffer_size - 4);
+  char * dummy;
+  Ustrcpy(p, "cwd= (failed)");
+  dummy = /* quieten compiler */ getcwd(CS p+4, big_buffer_size - 4);
   while (*p) p++;
   (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
   while (*p) p++;
   for (i = 0; i < argc; i++)
     {
     int len = Ustrlen(argv[i]);
-    uschar *printing;
+    const uschar *printing;
     uschar *quote;
     if (p + len + 8 >= big_buffer + big_buffer_size)
       {
@@ -3706,7 +4039,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
     printing = string_printing(argv[i]);
     if (printing[0] == 0) quote = US"\""; else
       {
-      uschar *pp = printing;
+      const uschar *pp = printing;
       quote = US"";
       while (*pp != 0) if (isspace(*pp++)) { quote = US"\""; break; }
       }
@@ -3715,7 +4048,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
     while (*p) p++;
     }
 
-  if ((log_extra_selector & LX_arguments) != 0)
+  if (LOGGING(arguments))
     log_write(0, LOG_MAIN, "%s", big_buffer);
   else
     debug_printf("%s\n", big_buffer);
@@ -3730,8 +4063,9 @@ privilege by now. Before the chdir, we try to ensure that the directory exists.
 
 if (Uchdir(spool_directory) != 0)
   {
+  int dummy;
   (void)directory_make(spool_directory, US"", SPOOL_DIRECTORY_MODE, FALSE);
-  (void)Uchdir(spool_directory);
+  dummy = /* quieten compiler */ Uchdir(spool_directory);
   }
 
 /* Handle calls with the -bi option. This is a sendmail option to rebuild *the*
@@ -3768,65 +4102,9 @@ if (bi_option)
     }
   }
 
-/* If an action on specific messages is requested, or if a daemon or queue
-runner is being started, we need to know if Exim was called by an admin user.
-This is the case if the real user is root or exim, or if the real group is
-exim, or if one of the supplementary groups is exim or a group listed in
-admin_groups. We don't fail all message actions immediately if not admin_user,
-since some actions can be performed by non-admin users. Instead, set admin_user
-for later interrogation. */
-
-if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid)
-  admin_user = TRUE;
-else
-  {
-  int i, j;
-  for (i = 0; i < group_count; i++)
-    {
-    if (group_list[i] == exim_gid) admin_user = TRUE;
-    else if (admin_groups != NULL)
-      {
-      for (j = 1; j <= (int)(admin_groups[0]); j++)
-        if (admin_groups[j] == group_list[i])
-          { admin_user = TRUE; break; }
-      }
-    if (admin_user) break;
-    }
-  }
-
-/* Another group of privileged users are the trusted users. These are root,
-exim, and any caller matching trusted_users or trusted_groups. Trusted callers
-are permitted to specify sender_addresses with -f on the command line, and
-other message parameters as well. */
-
-if (real_uid == root_uid || real_uid == exim_uid)
-  trusted_caller = TRUE;
-else
-  {
-  int i, j;
-
-  if (trusted_users != NULL)
-    {
-    for (i = 1; i <= (int)(trusted_users[0]); i++)
-      if (trusted_users[i] == real_uid)
-        { trusted_caller = TRUE; break; }
-    }
-
-  if (!trusted_caller && trusted_groups != NULL)
-    {
-    for (i = 1; i <= (int)(trusted_groups[0]); i++)
-      {
-      if (trusted_groups[i] == real_gid)
-        trusted_caller = TRUE;
-      else for (j = 0; j < group_count; j++)
-        {
-        if (trusted_groups[i] == group_list[j])
-          { trusted_caller = TRUE; break; }
-        }
-      if (trusted_caller) break;
-      }
-    }
-  }
+/* We moved the admin/trusted check to be immediately after reading the
+configuration file.  We leave these prints here to ensure that syslog setup,
+logfile setup, and so on has already happened. */
 
 if (trusted_caller) DEBUG(D_any) debug_printf("trusted user\n");
 if (admin_user) DEBUG(D_any) debug_printf("admin user\n");
@@ -3894,6 +4172,21 @@ else
     interface_port = check_port(interface_address);
   }
 
+/* If the caller is trusted, then they can use -G to suppress_local_fixups. */
+if (flag_G)
+  {
+  if (trusted_caller)
+    {
+    suppress_local_fixups = suppress_local_fixups_default = TRUE;
+    DEBUG(D_acl) debug_printf("suppress_local_fixups forced on by -G\n");
+    }
+  else
+    {
+    fprintf(stderr, "exim: permission denied (-G requires a trusted user)\n");
+    return EXIT_FAILURE;
+    }
+  }
+
 /* If an SMTP message is being received check to see if the standard input is a
 TCP/IP socket. If it is, we assume that Exim was called from inetd if the
 caller is root or the Exim user, or if the port is a privileged one. Otherwise,
@@ -3915,7 +4208,7 @@ if (smtp_input)
         interface_address = host_ntoa(-1, &interface_sock, NULL,
           &interface_port);
 
-      if (host_is_tls_on_connect_port(interface_port)) tls_on_connect = TRUE;
+      if (host_is_tls_on_connect_port(interface_port)) tls_in.on_connect = TRUE;
 
       if (real_uid == root_uid || real_uid == exim_uid || interface_port < 1024)
         {
@@ -4081,13 +4374,12 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
   exit(yield);
   }
 
-/* All the modes below here require the remaining configuration sections
-to be read, except that we can skip over the ACL setting when delivering
-specific messages, or doing a queue run. (For various testing cases we could
-skip too, but as they are rare, it doesn't really matter.) The argument is TRUE
-for skipping. */
+/* We used to set up here to skip reading the ACL section, on
+ (msg_action_arg > 0 || (queue_interval == 0 && !daemon_listen)
+Now, since the intro of the ${acl } expansion, ACL definitions may be
+needed in transports so we lost the optimisation. */
 
-readconf_rest(msg_action_arg > 0 || (queue_interval == 0 && !daemon_listen));
+readconf_rest();
 
 /* The configuration data will have been read into POOL_PERM because we won't
 ever want to reset back past it. Change the current pool to POOL_MAIN. In fact,
@@ -4222,11 +4514,12 @@ if (test_retry_arg >= 0)
   }
 
 /* Handle a request to list one or more configuration options */
+/* If -n was set, we suppress some information */
 
 if (list_options)
   {
   set_process_info("listing variables");
-  if (recipients_arg >= argc) readconf_print(US"all", NULL);
+  if (recipients_arg >= argc) readconf_print(US"all", NULL, flag_n);
     else for (i = recipients_arg; i < argc; i++)
       {
       if (i < argc - 1 &&
@@ -4235,10 +4528,10 @@ if (list_options)
            Ustrcmp(argv[i], "authenticator") == 0 ||
            Ustrcmp(argv[i], "macro") == 0))
         {
-        readconf_print(argv[i+1], argv[i]);
+        readconf_print(argv[i+1], argv[i], flag_n);
         i++;
         }
-      else readconf_print(argv[i], NULL);
+      else readconf_print(argv[i], NULL, flag_n);
       }
   exim_exit(EXIT_SUCCESS);
   }
@@ -4740,7 +5033,7 @@ if (host_checking)
       sender_host_address);
 
   if (verify_check_host(&hosts_connection_nolog) == OK)
-    log_write_selector &= ~L_smtp_connection;
+    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
 
   /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
@@ -4765,7 +5058,8 @@ if (host_checking)
 
 /* Arrange for message reception if recipients or SMTP were specified;
 otherwise complain unless a version print (-bV) happened or this is a filter
-verification test. In the former case, show the configuration file name. */
+verification test or info dump.
+In the former case, show the configuration file name. */
 
 if (recipients_arg >= argc && !extract_recipients && !smtp_input)
   {
@@ -4775,6 +5069,12 @@ if (recipients_arg >= argc && !extract_recipients && !smtp_input)
     return EXIT_SUCCESS;
     }
 
+  if (info_flag != CMDINFO_NONE)
+    {
+    show_exim_information(info_flag, info_stdout ? stdout : stderr);
+    return info_stdout ? EXIT_SUCCESS : EXIT_FAILURE;
+    }
+
   if (filter_test == FTEST_NONE)
     exim_usage(called_as);
   }
@@ -4802,6 +5102,9 @@ if (mua_wrapper)
   deliver_drop_privilege = TRUE;
   queue_smtp = FALSE;
   queue_smtp_domains = NULL;
+#ifdef EXPERIMENTAL_INTERNATIONAL
+  message_utf8_downconvert = -1;       /* convert-if-needed */
+#endif
   }
 
 
@@ -4904,7 +5207,7 @@ if (smtp_input)
   smtp_in = stdin;
   smtp_out = stdout;
   if (verify_check_host(&hosts_connection_nolog) == OK)
-    log_write_selector &= ~L_smtp_connection;
+    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
   if (!smtp_start_session())
     {
@@ -5080,7 +5383,6 @@ while (more)
 
         if (recipients_max > 0 && ++rcount > recipients_max &&
             !extract_recipients)
-          {
           if (error_handling == ERRORS_STDERR)
             {
             fprintf(stderr, "exim: too many recipients\n");
@@ -5092,11 +5394,22 @@ while (more)
               moan_to_sender(ERRMESS_TOOMANYRECIP, NULL, NULL, stdin, TRUE)?
                 errors_sender_rc : EXIT_FAILURE;
             }
-          }
 
+#ifdef EXPERIMENTAL_INTERNATIONAL
+       {
+       BOOL b = allow_utf8_domains;
+       allow_utf8_domains = TRUE;
+#endif
         recipient =
           parse_extract_address(s, &errmess, &start, &end, &domain, FALSE);
 
+#ifdef EXPERIMENTAL_INTERNATIONAL
+       if (string_is_utf8(recipient))
+         message_smtputf8 = TRUE;
+       else
+         allow_utf8_domains = b;
+       }
+#endif
         if (domain == 0 && !allow_unqualified_recipient)
           {
           recipient = NULL;
@@ -5196,9 +5509,7 @@ while (more)
       return_path = string_copy(sender_address);
       }
     else
-      {
       printf("Return-path = %s\n", (return_path[0] == 0)? US"<>" : return_path);
-      }
     printf("Sender      = %s\n", (sender_address[0] == 0)? US"<>" : sender_address);
 
     receive_add_recipient(
@@ -5212,7 +5523,11 @@ while (more)
     if (ftest_prefix != NULL) printf("Prefix    = %s\n", ftest_prefix);
     if (ftest_suffix != NULL) printf("Suffix    = %s\n", ftest_suffix);
 
-    (void)chdir("/");   /* Get away from wherever the user is running this from */
+    if (chdir("/"))   /* Get away from wherever the user is running this from */
+      {
+      DEBUG(D_receive) debug_printf("chdir(\"/\") failed\n");
+      exim_exit(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