Fix parsing of option type Kint (integer, stored in K). Bug 2348
[exim.git] / src / src / readconf.c
index 42bb5afe51599f71af847077592375e57ccaec62..44452baa6dec2382f76aa2283cc86a8eecfeb0df 100644 (file)
@@ -15,6 +15,9 @@ implementation of the conditional .ifdef etc. */
 # include "macro_predef.h"
 #endif
 
+#define READCONF_DEBUG if (FALSE)      /* Change to TRUE to enable */
+
+
 static uschar * syslog_facility_str;
 static void fn_smtp_receive_timeout(const uschar *, const uschar *);
 
@@ -123,6 +126,7 @@ static optionlist optionlist_config[] = {
 #endif
   { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
   { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
+  { "dns_cname_loops",         opt_int,         &dns_cname_loops },
   { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
   { "dns_csa_use_reverse",      opt_bool,        &dns_csa_use_reverse },
   { "dns_dnssec_ok",            opt_int,         &dns_dnssec_ok },
@@ -195,7 +199,9 @@ static optionlist optionlist_config[] = {
   { "local_from_prefix",        opt_stringptr,   &local_from_prefix },
   { "local_from_suffix",        opt_stringptr,   &local_from_suffix },
   { "local_interfaces",         opt_stringptr,   &local_interfaces },
+#ifdef HAVE_LOCAL_SCAN
   { "local_scan_timeout",       opt_time,        &local_scan_timeout },
+#endif
   { "local_sender_retain",      opt_bool,        &local_sender_retain },
   { "localhost_number",         opt_stringptr,   &host_number_string },
   { "log_file_path",            opt_stringptr,   &log_file_path },
@@ -234,6 +240,10 @@ static optionlist optionlist_config[] = {
 #endif
   { "pid_file_path",            opt_stringptr,   &pid_file_path },
   { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts },
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  { "pipelining_connect_advertise_hosts", opt_stringptr,
+                                                &pipe_connect_advertise_hosts },
+#endif
 #ifndef DISABLE_PRDR
   { "prdr_enable",              opt_bool,        &prdr_enable },
 #endif
@@ -345,6 +355,9 @@ static optionlist optionlist_config[] = {
   { "timezone",                 opt_stringptr,   &timezone_string },
   { "tls_advertise_hosts",      opt_stringptr,   &tls_advertise_hosts },
 #ifdef SUPPORT_TLS
+# ifdef EXPERIMENTAL_REQUIRETLS
+  { "tls_advertise_requiretls", opt_stringptr,   &tls_advertise_requiretls },
+# endif
   { "tls_certificate",          opt_stringptr,   &tls_certificate },
   { "tls_crl",                  opt_stringptr,   &tls_crl },
   { "tls_dh_max_bits",          opt_int,         &tls_dh_max_bits },
@@ -403,6 +416,19 @@ for (ai = auths_available; ai->driver_name[0]; ai++)
   }
 }
 
+void
+options_logging(void)
+{
+bit_table * bp;
+uschar buf[64];
+
+for (bp = log_options; bp < log_options + log_options_count; bp++)
+  {
+  spf(buf, sizeof(buf), US"_LOG_%T", bp->name);
+  builtin_macro_create(buf);
+  }
+}
+
 
 #else  /*!MACRO_PREDEF*/
 
@@ -592,6 +618,40 @@ return US"";
 
 
 
+/*************************************************
+*       Deal with an assignment to a macro       *
+*************************************************/
+
+/* We have a new definition; append to the list.
+
+Args:
+ name  Name of the macro; will be copied
+ val   Expansion result for the macro; will be copied
+*/
+
+macro_item *
+macro_create(const uschar * name, const uschar * val, BOOL command_line)
+{
+macro_item * m = store_get(sizeof(macro_item));
+
+READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val);
+m->next = NULL;
+m->command_line = command_line;
+m->namelen = Ustrlen(name);
+m->replen = Ustrlen(val);
+m->name = string_copy(name);
+m->replacement = string_copy(val);
+if (mlast)
+  mlast->next = m;
+else
+  macros = m;
+mlast = m;
+if (!macros_user)
+  macros_user = m;
+return m;
+}
+
+
 /* This function is called when a line that starts with an upper case letter is
 encountered. The argument "line" should contain a complete logical line, and
 start with the first letter of the macro name. The macro name and the
@@ -642,37 +702,53 @@ while (isspace(*s)) s++;
 just skip this definition. It's an error to attempt to redefine a macro without
 redef set to TRUE, or to redefine a macro when it hasn't been defined earlier.
 It is also an error to define a macro whose name begins with the name of a
-previously defined macro.
+previously defined macro.  This is the requirement that make using a tree
+for macros hard; we must check all macros for the substring.  Perhaps a
+sorted list, and a bsearch, would work?
 Note: it is documented that the other way round works. */
 
-if ((m = macro_search_prefix(name)))
+for (m = macros; m; m = m->next)
   {
-  if (m->namelen < namelen)    /* substring match */
+  if (Ustrcmp(m->name, name) == 0)
     {
-    log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
-      "a macro because previously defined macro \"%s\" is a substring",
-      name, m->tnode.name);
-    return FALSE;
+    if (!m->command_line && !redef)
+      {
+      log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
+       "defined (use \"==\" if you want to redefine it)", name);
+      return FALSE;
+      }
+    break;
     }
-                               /* exact match */
-  if (!m->command_line && !redef)
+
+  if (m->namelen < namelen && Ustrstr(name, m->name) != NULL)
     {
-    log_write(0, LOG_CONFIG|LOG_PANIC, "macro \"%s\" is already "
-       "defined (use \"==\" if you want to redefine it", name);
+    log_write(0, LOG_CONFIG|LOG_PANIC, "\"%s\" cannot be defined as "
+      "a macro because previously defined macro \"%s\" is a substring",
+      name, m->name);
     return FALSE;
     }
 
-  if (m->command_line)         /* overriding cmdline definition */
-    return TRUE;
+  /* We cannot have this test, because it is documented that a substring
+  macro is permitted (there is even an example).
+  *
+  * if (m->namelen > namelen && Ustrstr(m->name, name) != NULL)
+  *   log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
+  *     "a macro because it is a substring of previously defined macro \"%s\"",
+  *     name, m->name);
+  */
   }
 
+/* Check for an overriding command-line definition. */
+
+if (m && m->command_line) return TRUE;
+
 /* Redefinition must refer to an existing macro. */
 
 if (redef)
   if (m)
     {
     m->replen = Ustrlen(s);
-    m->tnode.data.ptr = string_copy(s);
+    m->replacement = string_copy(s);
     }
   else
     {
@@ -693,7 +769,7 @@ return TRUE;
 
 /* Process line for macros. The line is in big_buffer starting at offset len.
 Expand big_buffer if needed.  Handle definitions of new macros, and
-imacro expansions, rewriting the line in thw buffer.
+macro expansions, rewriting the line in the buffer.
 
 Arguments:
  len           Offset in buffer of start of line
@@ -708,6 +784,7 @@ macros_expand(int len, int * newlen, BOOL * macro_found)
 {
 uschar * ss = big_buffer + len;
 uschar * s;
+macro_item * m;
 
 /* Find the true start of the physical line - leading spaces are always
 ignored. */
@@ -728,52 +805,61 @@ if (len == 0 && isupper(*s))
   if (*s != '=') s = ss;          /* Not a macro definition */
   }
 
-/* Scan the line (from after XXX= if present), replacing any macros. Rescan
-after replacement for any later-defined macros. */
+/* Skip leading chars which cannot start a macro name, to avoid multiple
+pointless rescans in Ustrstr calls. */
+
+while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+
+/* For each defined macro, scan the line (from after XXX= if present),
+replacing all occurrences of the macro. */
 
 *macro_found = FALSE;
-while (*s)
+if (*s) for (m = *s == '_' ? macros : macros_user; m; m = m->next)
   {
-  if (isupper(*s) || *s == '_' && isupper(s[1]))
-    {
-    macro_item * m;
-    unsigned mnum = 0;
+  uschar * p, *pp;
+  uschar * t;
 
-    while ((m = macro_search_largest_prefix(s)) && m->m_number > mnum)
-      {
-      uschar * pp;
-      int moveby;
+  while (*s && !isupper(*s) && !(*s == '_' && isupper(s[1]))) s++;
+  if (!*s) break;
 
-      /* Expand the buffer if necessary */
+  t = s;
+  while ((p = Ustrstr(t, m->name)) != NULL)
+    {
+    int moveby;
 
-      while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
-       {
-       int newsize = big_buffer_size + BIG_BUFFER_SIZE;
-       uschar *newbuffer = store_malloc(newsize);
-       memcpy(newbuffer, big_buffer, *newlen + 1);
-       s = newbuffer  + (s - big_buffer);
-       ss = newbuffer + (ss - big_buffer);
-       big_buffer_size = newsize;
-       store_free(big_buffer);
-       big_buffer = newbuffer;
-       }
+    READCONF_DEBUG fprintf(stderr, "%s: matched '%s' in '%.*s'\n", __FUNCTION__,
+      m->name, (int) Ustrlen(ss)-1, ss);
+    /* Expand the buffer if necessary */
+
+    while (*newlen - m->namelen + m->replen + 1 > big_buffer_size)
+      {
+      int newsize = big_buffer_size + BIG_BUFFER_SIZE;
+      uschar *newbuffer = store_malloc(newsize);
+      memcpy(newbuffer, big_buffer, *newlen + 1);
+      p = newbuffer  + (p - big_buffer);
+      s = newbuffer  + (s - big_buffer);
+      ss = newbuffer + (ss - big_buffer);
+      t = newbuffer  + (t - big_buffer);
+      big_buffer_size = newsize;
+      store_free(big_buffer);
+      big_buffer = newbuffer;
+      }
 
-      /* Shuffle the remaining characters up or down in the buffer before
-      copying in the replacement text. Don't rescan the replacement for this
-      same macro. */
+    /* Shuffle the remaining characters up or down in the buffer before
+    copying in the replacement text. Don't rescan the replacement for this
+    same macro. */
 
-      pp = s + m->namelen;
-      if ((moveby = m->replen - m->namelen) != 0)
-       {
-       memmove(s + m->replen, pp, (big_buffer + *newlen) - pp + 1);
-       *newlen += moveby;
-       }
-      Ustrncpy(s, m->tnode.data.ptr, m->replen);
-      *macro_found = TRUE;
-      mnum = m->m_number;
+    pp = p + m->namelen;
+    if ((moveby = m->replen - m->namelen) != 0)
+      {
+      memmove(p + m->replen, pp, (big_buffer + *newlen) - pp + 1);
+      *newlen += moveby;
       }
+    Ustrncpy(p, m->replacement, m->replen);
+    t = p + m->replen;
+    while (*t && !isupper(*t) && !(*t == '_' && isupper(t[1]))) t++;
+    *macro_found = TRUE;
     }
-  s++;
   }
 
 /* An empty macro replacement at the start of a line could mean that ss no
@@ -2044,7 +2130,7 @@ switch (type)
   inttype = US"octal ";
 
   /*  Integer: a simple(ish) case; allow octal and hex formats, and
-  suffixes K, M and G. The different types affect output, not input. */
+  suffixes K, M, G, and T.  The different types affect output, not input. */
 
   case opt_mkint:
   case opt_int:
@@ -2059,80 +2145,75 @@ switch (type)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
         inttype, name);
 
-    if (errno != ERANGE)
-      if (tolower(*endptr) == 'k')
-        {
-        if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) errno = ERANGE;
-          else lvalue *= 1024;
-        endptr++;
-        }
-      else if (tolower(*endptr) == 'm')
-        {
-        if (lvalue > INT_MAX/(1024*1024) || lvalue < INT_MIN/(1024*1024))
-          errno = ERANGE;
-        else lvalue *= 1024*1024;
-        endptr++;
-        }
-      else if (tolower(*endptr) == 'g')
-        {
-        if (lvalue > INT_MAX/(1024*1024*1024) || lvalue < INT_MIN/(1024*1024*1024))
-          errno = ERANGE;
-        else lvalue *= 1024*1024*1024;
-        endptr++;
-        }
+    if (errno != ERANGE && *endptr)
+      {
+      uschar * mp = US"TtGgMmKk\0";    /* YyZzEePpTtGgMmKk */
+
+      if ((mp = Ustrchr(mp, *endptr)))
+       {
+       endptr++;
+       do
+         {
+         if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+           {
+           errno = ERANGE;
+           break;
+           }
+         lvalue *= 1024;
+         }
+       while (*(mp += 2));
+       }
+      }
 
     if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
         "absolute value of integer \"%s\" is too large (overflow)", s);
 
     while (isspace(*endptr)) endptr++;
-    if (*endptr != 0)
+    if (*endptr)
       extra_chars_error(endptr, inttype, US"integer value for ", name);
 
     value = (int)lvalue;
     }
 
-  if (data_block == NULL)
-    *((int *)(ol->value)) = value;
+  if (data_block)
+    *(int *)(US data_block + (long int)ol->value) = value;
   else
-    *((int *)(US data_block + (long int)(ol->value))) = value;
+    *(int *)ol->value = value;
   break;
 
-  /*  Integer held in K: again, allow octal and hex formats, and suffixes K, M
-  and G. */
-  /*XXX consider moving to int_eximarith_t (but mind the overflow test 0415) */
+  /*  Integer held in K: again, allow formats and suffixes as above. */
 
   case opt_Kint:
     {
     uschar *endptr;
     errno = 0;
-    value = strtol(CS s, CSS &endptr, intbase);
+    int_eximarith_t lvalue = strtol(CS s, CSS &endptr, intbase);
 
     if (endptr == s)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
         inttype, name);
 
-    if (errno != ERANGE)
-      if (tolower(*endptr) == 'g')
-        {
-        if (value > INT_MAX/(1024*1024) || value < INT_MIN/(1024*1024))
-         errno = ERANGE;
-       else
-         value *= 1024*1024;
-        endptr++;
-        }
-      else if (tolower(*endptr) == 'm')
-        {
-        if (value > INT_MAX/1024 || value < INT_MIN/1024)
-         errno = ERANGE;
-       else
-         value *= 1024;
-        endptr++;
-        }
-      else if (tolower(*endptr) == 'k')
-        endptr++;
+    if (errno != ERANGE && *endptr)
+      {
+      uschar * mp = US"ZzEePpTtGgMmKk\0";      /* YyZzEePpTtGgMmKk */
+
+      if ((mp = Ustrchr(mp, *endptr)))
+       {
+       endptr++;
+       while (*(mp += 2))
+         {
+         if (lvalue > EXIM_ARITH_MAX/1024 || lvalue < EXIM_ARITH_MIN/1024)
+           {
+           errno = ERANGE;
+           break;
+           }
+         lvalue *= 1024;
+         }
+       }
       else
-        value = (value + 512)/1024;
+       lvalue = (lvalue + 512)/1024;
+      }
 
     if (errno == ERANGE) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "absolute value of integer \"%s\" is too large (overflow)", s);
@@ -2140,13 +2221,13 @@ switch (type)
     while (isspace(*endptr)) endptr++;
     if (*endptr != 0)
       extra_chars_error(endptr, inttype, US"integer value for ", name);
-    }
 
-  if (data_block == NULL)
-    *((int *)(ol->value)) = value;
-  else
-    *((int *)(US data_block + (long int)(ol->value))) = value;
-  break;
+    if (data_block)
+      *(int_eximarith_t *)(US data_block + (long int)ol->value) = lvalue;
+    else
+      *(int_eximarith_t *)ol->value = lvalue;
+    break;
+    }
 
   /*  Fixed-point number: held to 3 decimal places. */
 
@@ -2345,7 +2426,7 @@ if (!ol)
 /* Non-admin callers cannot see options that have been flagged secure by the
 "hide" prefix. */
 
-if (!admin_user && ol->type & opt_secure)
+if (!f.admin_user && ol->type & opt_secure)
   {
   if (no_labels)
     printf("%s\n", hidden);
@@ -2403,11 +2484,13 @@ switch(ol->type & opt_mask)
 
   case opt_Kint:
     {
-    int x = *((int *)value);
+    int_eximarith_t x = *((int_eximarith_t *)value);
     if (!no_labels) printf("%s = ", name);
     if (x == 0) printf("0\n");
-    else if ((x & 1023) == 0) printf("%dM\n", x >> 10);
-    else printf("%dK\n", x);
+    else if ((x & ((1<<30)-1)) == 0) printf(PR_EXIM_ARITH "T\n", x >> 30);
+    else if ((x & ((1<<20)-1)) == 0) printf(PR_EXIM_ARITH "G\n", x >> 20);
+    else if ((x & ((1<<10)-1)) == 0) printf(PR_EXIM_ARITH "M\n", x >> 10);
+    else printf(PR_EXIM_ARITH "K\n", x);
     }
     break;
 
@@ -2704,7 +2787,7 @@ if (!type)
 
   if (Ustrcmp(name, "config") == 0)
     {
-    print_config(admin_user, no_labels);
+    print_config(f.admin_user, no_labels);
     return TRUE;
     }
 
@@ -2805,30 +2888,33 @@ else if (Ustrcmp(type, "macro") == 0)
   {
   /* People store passwords in macros and they were previously not available
   for printing.  So we have an admin_users restriction. */
-  if (!admin_user)
+  if (!f.admin_user)
     {
     fprintf(stderr, "exim: permission denied\n");
     return FALSE;
     }
-
-  if (name)
-    if ((m = macro_search(name)))
-      macro_print(m->tnode.name, m->tnode.data.ptr, (void *)(long)names_only);
-    else
+  for (m = macros; m; m = m->next)
+    if (!name || Ustrcmp(name, m->name) == 0)
       {
-      printf("%s %s not found\n", type, name);
-      return FALSE;
+      if (names_only)
+        printf("%s\n", CS m->name);
+      else if (no_labels)
+        printf("%s\n", CS m->replacement);
+      else
+        printf("%s=%s\n", CS m->name, CS m->replacement);
+      if (name)
+        return TRUE;
       }
-  else
-    tree_walk(tree_macros, macro_print, (void *)(long)names_only);
+  if (!name) return TRUE;
 
-  return TRUE;
+  printf("%s %s not found\n", type, name);
+  return FALSE;
   }
 
 if (names_only)
   {
   for (; d; d = d->next) printf("%s\n", CS d->name);
-  return TRUE;;
+  return TRUE;
   }
 
 /* Either search for a given driver, or print all of them */
@@ -2838,8 +2924,7 @@ for (; d; d = d->next)
   BOOL rc = FALSE;
   if (!name)
     printf("\n%s %s:\n", d->name, type);
-  else if (Ustrcmp(d->name, name) != 0)
-    continue;
+  else if (Ustrcmp(d->name, name) != 0) continue;
 
   for (ol = ol2; ol < ol2 + size; ol++)
     if (!(ol->type & opt_hidden))
@@ -3210,7 +3295,7 @@ if (Uchdir("/") < 0)
 /* Check the status of the file we have opened, if we have retained root
 privileges and the file isn't /dev/null (which *should* be 0666). */
 
-if (trusted_config && Ustrcmp(filename, US"/dev/null"))
+if (f.trusted_config && Ustrcmp(filename, US"/dev/null"))
   {
   if (fstat(fileno(config_file), &statbuf) != 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to stat configuration file %s",
@@ -4126,6 +4211,9 @@ static void
 auths_init(void)
 {
 auth_instance *au, *bu;
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+int nauths = 0;
+#endif
 
 readconf_driver_init(US"authenticator",
   (driver_instance **)(&auths),      /* chain anchor */
@@ -4149,7 +4237,13 @@ for (au = auths; au; au = au->next)
           "(%s and %s) have the same public name (%s)",
           au->client ? US"client" : US"server", au->name, bu->name,
           au->public_name);
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+  nauths++;
+#endif
   }
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+f.smtp_in_early_pipe_no_auth = nauths > 16;
+#endif
 }
 
 
@@ -4336,7 +4430,7 @@ void
 readconf_save_config(const uschar *s)
 {
 save_config_line(string_sprintf("# Exim Configuration (%s)",
-  running_in_test_harness ? US"X" : s));
+  f.running_in_test_harness ? US"X" : s));
 }
 
 static void