Add the Exim version to process info (exiwhat)
[exim.git] / src / src / exim.c
index ded12fa34d62a5735d396b0ad122192d9152a3a9..ceec9cb2a98d551067fd76d0f499e80f15452722 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* 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);
 
 
@@ -81,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;
@@ -93,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)
@@ -124,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;
@@ -137,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--;
@@ -222,7 +230,7 @@ to disrupt whatever is going on outside the signal handler. */
 
 if (fd < 0) return;
 
-{int dummy = write(fd, process_info, process_info_len); dummy = dummy; }
+(void)write(fd, process_info, process_info_len);
 (void)close(fd);
 }
 
@@ -267,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
 */
@@ -276,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 */
@@ -398,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);
       }
     }
 
@@ -804,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
@@ -813,24 +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_OCSP
-  fprintf(f, " Experimental_OCSP");
+#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_TPDA
-  fprintf(f, " Experimental_TPDA");
+#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):");
@@ -906,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:");
@@ -994,12 +1031,13 @@ 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
@@ -1019,10 +1057,8 @@ DEBUG(D_any) do {
 
   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);
@@ -1479,6 +1515,7 @@ 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"/";
@@ -1605,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".
@@ -1715,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
@@ -2285,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)
@@ -2417,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;
@@ -2490,7 +2533,7 @@ for (i = 1; i < argc; i++)
 
     case 'f':
       {
-      int start, end;
+      int dummy_start, dummy_end;
       uschar *errmess;
       if (*argrest == 0)
         {
@@ -2498,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;
@@ -2508,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)
@@ -2653,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) */
 
@@ -3354,13 +3410,20 @@ for (i = 1; i < argc; i++)
 
     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 */
@@ -3661,6 +3724,10 @@ 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. */
@@ -3729,14 +3796,17 @@ else
 
 /* 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
@@ -3803,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.
@@ -3932,7 +4013,7 @@ 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;
@@ -3946,7 +4027,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
   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)
       {
@@ -3958,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; }
       }
@@ -3967,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);
@@ -4952,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,
@@ -5021,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
   }
 
 
@@ -5123,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())
     {
@@ -5299,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");
@@ -5311,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;
@@ -5415,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(