Merge native DKIM support (from DEVEL_PDKIM)
[exim.git] / src / src / readconf.c
index 1cc95b90d6c6d61988d7181fda7ac4d3bb93e739..2b02ddf640b30924ab83f17a32ecb16d3941c42f 100644 (file)
@@ -1,10 +1,10 @@
-/* $Cambridge: exim/src/src/readconf.c,v 1.11 2005/06/27 14:29:43 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.36 2009/06/10 07:34:04 tom Exp $ */
 
 /*************************************************
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2007 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading the configuration file, and for displaying
@@ -138,9 +138,13 @@ static optionlist optionlist_config[] = {
 #ifdef WITH_CONTENT_SCAN
   { "acl_not_smtp_mime",        opt_stringptr,   &acl_not_smtp_mime },
 #endif
+  { "acl_not_smtp_start",       opt_stringptr,   &acl_not_smtp_start },
   { "acl_smtp_auth",            opt_stringptr,   &acl_smtp_auth },
   { "acl_smtp_connect",         opt_stringptr,   &acl_smtp_connect },
   { "acl_smtp_data",            opt_stringptr,   &acl_smtp_data },
+#ifndef DISABLE_DKIM
+  { "acl_smtp_dkim",            opt_stringptr,   &acl_smtp_dkim },
+#endif
   { "acl_smtp_etrn",            opt_stringptr,   &acl_smtp_etrn },
   { "acl_smtp_expn",            opt_stringptr,   &acl_smtp_expn },
   { "acl_smtp_helo",            opt_stringptr,   &acl_smtp_helo },
@@ -149,6 +153,7 @@ static optionlist optionlist_config[] = {
 #ifdef WITH_CONTENT_SCAN
   { "acl_smtp_mime",            opt_stringptr,   &acl_smtp_mime },
 #endif
+  { "acl_smtp_notquit",         opt_stringptr,   &acl_smtp_notquit },
   { "acl_smtp_predata",         opt_stringptr,   &acl_smtp_predata },
   { "acl_smtp_quit",            opt_stringptr,   &acl_smtp_quit },
   { "acl_smtp_rcpt",            opt_stringptr,   &acl_smtp_rcpt },
@@ -182,17 +187,30 @@ static optionlist optionlist_config[] = {
   { "callout_random_local_part",opt_stringptr,   &callout_random_local_part },
   { "check_log_inodes",         opt_int,         &check_log_inodes },
   { "check_log_space",          opt_Kint,        &check_log_space },
+  { "check_rfc2047_length",     opt_bool,        &check_rfc2047_length },
   { "check_spool_inodes",       opt_int,         &check_spool_inodes },
   { "check_spool_space",        opt_Kint,        &check_spool_space },
   { "daemon_smtp_port",         opt_stringptr|opt_hidden, &daemon_smtp_port },
   { "daemon_smtp_ports",        opt_stringptr,   &daemon_smtp_port },
   { "daemon_startup_retries",   opt_int,         &daemon_startup_retries },
   { "daemon_startup_sleep",     opt_time,        &daemon_startup_sleep },
+#ifdef EXPERIMENTAL_DCC
+  { "dcc_direct_add_header",    opt_bool,        &dcc_direct_add_header },
+  { "dccifd_address",           opt_stringptr,   &dccifd_address },
+  { "dccifd_options",           opt_stringptr,   &dccifd_options },
+#endif
   { "delay_warning",            opt_timelist,    &delay_warning },
   { "delay_warning_condition",  opt_stringptr,   &delay_warning_condition },
   { "deliver_drop_privilege",   opt_bool,        &deliver_drop_privilege },
   { "deliver_queue_load_max",   opt_fixed,       &deliver_queue_load_max },
   { "delivery_date_remove",     opt_bool,        &delivery_date_remove },
+#ifdef ENABLE_DISABLE_FSYNC
+  { "disable_fsync",            opt_bool,        &disable_fsync },
+#endif
+  { "disable_ipv6",             opt_bool,        &disable_ipv6 },
+#ifndef DISABLE_DKIM
+  { "dkim_verify_signers",      opt_stringptr,   &dkim_verify_signers },
+#endif
   { "dns_again_means_nonexist", opt_stringptr,   &dns_again_means_nonexist },
   { "dns_check_names_pattern",  opt_stringptr,   &check_dns_names_pattern },
   { "dns_csa_search_limit",     opt_int,         &dns_csa_search_limit },
@@ -203,6 +221,7 @@ static optionlist optionlist_config[] = {
  /* This option is now a no-op, retained for compability */
   { "drop_cr",                  opt_bool,        &drop_cr },
 /*********************************************************/
+  { "dsn_from",                 opt_stringptr,   &dsn_from },
   { "envelope_to_remove",       opt_bool,        &envelope_to_remove },
   { "errors_copy",              opt_stringptr,   &errors_copy },
   { "errors_reply_to",          opt_stringptr,   &errors_reply_to },
@@ -215,6 +234,11 @@ static optionlist optionlist_config[] = {
   { "freeze_tell",              opt_stringptr,   &freeze_tell },
   { "gecos_name",               opt_stringptr,   &gecos_name },
   { "gecos_pattern",            opt_stringptr,   &gecos_pattern },
+#ifdef SUPPORT_TLS
+  { "gnutls_require_kx",        opt_stringptr,   &gnutls_require_kx },
+  { "gnutls_require_mac",       opt_stringptr,   &gnutls_require_mac },
+  { "gnutls_require_protocols", opt_stringptr,   &gnutls_require_proto },
+#endif
   { "header_line_maxsize",      opt_int,         &header_line_maxsize },
   { "header_maxsize",           opt_int,         &header_maxsize },
   { "headers_charset",          opt_stringptr,   &headers_charset },
@@ -252,6 +276,7 @@ static optionlist optionlist_config[] = {
   { "log_timezone",             opt_bool,        &log_timezone },
   { "lookup_open_max",          opt_int,         &lookup_open_max },
   { "max_username_length",      opt_int,         &max_username_length },
+  { "message_body_newlines",    opt_bool,        &message_body_newlines },
   { "message_body_visible",     opt_mkint,       &message_body_visible },
   { "message_id_header_domain", opt_stringptr,   &message_id_domain },
   { "message_id_header_text",   opt_stringptr,   &message_id_text },
@@ -290,6 +315,7 @@ static optionlist optionlist_config[] = {
   { "queue_only",               opt_bool,        &queue_only },
   { "queue_only_file",          opt_stringptr,   &queue_only_file },
   { "queue_only_load",          opt_fixed,       &queue_only_load },
+  { "queue_only_load_latch",    opt_bool,        &queue_only_load_latch },
   { "queue_only_override",      opt_bool,        &queue_only_override },
   { "queue_run_in_order",       opt_bool,        &queue_run_in_order },
   { "queue_run_max",            opt_int,         &queue_run_max },
@@ -336,9 +362,15 @@ static optionlist optionlist_config[] = {
   { "smtp_return_error_details",opt_bool,        &smtp_return_error_details },
 #ifdef WITH_CONTENT_SCAN
   { "spamd_address",            opt_stringptr,   &spamd_address },
+#endif
+#ifdef EXPERIMENTAL_SPF
+  { "spf_guess",                opt_stringptr,   &spf_guess },
 #endif
   { "split_spool_directory",    opt_bool,        &split_spool_directory },
   { "spool_directory",          opt_stringptr,   &spool_directory },
+#ifdef LOOKUP_SQLITE
+  { "sqlite_lock_timeout",      opt_int,         &sqlite_lock_timeout },
+#endif
 #ifdef EXPERIMENTAL_SRS
   { "srs_config",               opt_stringptr,   &srs_config },
   { "srs_hashlength",           opt_int,         &srs_hashlength },
@@ -348,6 +380,7 @@ static optionlist optionlist_config[] = {
   { "srs_usehash",              opt_bool,        &srs_usehash },
   { "srs_usetimestamp",         opt_bool,        &srs_usetimestamp },
 #endif
+  { "strict_acl_vars",          opt_bool,        &strict_acl_vars },
   { "strip_excess_angle_brackets", opt_bool,     &strip_excess_angle_brackets },
   { "strip_trailing_dot",       opt_bool,        &strip_trailing_dot },
   { "syslog_duplication",       opt_bool,        &syslog_duplication },
@@ -807,6 +840,10 @@ for (;;)
       }
     *t = 0;
 
+    if (*ss != '/')
+      log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, ".include specifies a non-"
+        "absolute path \"%s\"", ss);
+
     if (include_if_exists != 0 && (Ustat(ss, &statbuf) != 0)) continue;
 
     save = store_get(sizeof(config_file_item));
@@ -1622,10 +1659,16 @@ switch (type)
       int count = 1;
       uid_t *list;
       int ptr = 0;
-      uschar *p = sptr;
+      uschar *p;
+      uschar *op = expand_string (sptr);
 
+      if (op == NULL)
+        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+          name, expand_string_message);
+
+      p = op;
       if (*p != 0) count++;
-      while (*p != 0) if (*p++ == ':') count++;
+      while (*p != 0) if (*p++ == ':' && *p != 0) count++;
       list = store_malloc(count*sizeof(uid_t));
       list[ptr++] = (uid_t)(count - 1);
 
@@ -1634,7 +1677,7 @@ switch (type)
       else
         *((uid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
 
-      p = sptr;
+      p = op;
       while (count-- > 1)
         {
         int sep = 0;
@@ -1657,10 +1700,16 @@ switch (type)
       int count = 1;
       gid_t *list;
       int ptr = 0;
-      uschar *p = sptr;
+      uschar *p;
+      uschar *op = expand_string (sptr);
 
+      if (op == NULL)
+        log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+          name, expand_string_message);
+
+      p = op;
       if (*p != 0) count++;
-      while (*p != 0) if (*p++ == ':') count++;
+      while (*p != 0) if (*p++ == ':' && *p != 0) count++;
       list = store_malloc(count*sizeof(gid_t));
       list[ptr++] = (gid_t)(count - 1);
 
@@ -1669,7 +1718,7 @@ switch (type)
       else
         *((gid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
 
-      p = sptr;
+      p = op;
       while (count-- > 1)
         {
         int sep = 0;
@@ -1797,8 +1846,10 @@ switch (type)
   case opt_int:
     {
     uschar *endptr;
+    long int lvalue;
+
     errno = 0;
-    value = strtol(CS s, CSS &endptr, intbase);
+    lvalue = strtol(CS s, CSS &endptr, intbase);
 
     if (endptr == s)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
@@ -1808,25 +1859,28 @@ switch (type)
       {
       if (tolower(*endptr) == 'k')
         {
-        if (value > INT_MAX/1024 || value < INT_MIN/1024) errno = ERANGE;
-          else value *= 1024;
+        if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) errno = ERANGE;
+          else lvalue *= 1024;
         endptr++;
         }
       else if (tolower(*endptr) == 'm')
         {
-        if (value > INT_MAX/(1024*1024) || value < INT_MIN/(1024*1024))
+        if (lvalue > INT_MAX/(1024*1024) || lvalue < INT_MIN/(1024*1024))
           errno = ERANGE;
-        else value *= 1024*1024;
+        else lvalue *= 1024*1024;
         endptr++;
         }
       }
 
-    if (errno == ERANGE) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-      "absolute value of integer \"%s\" is too large (overflow)", s);
+    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)
       extra_chars_error(endptr, inttype, US"integer value for ", name);
+
+    value = (int)lvalue;
     }
 
   if (data_block == NULL)
@@ -1994,6 +2048,12 @@ readconf_printtime(int t)
 int s, m, h, d, w;
 uschar *p = time_buffer;
 
+if (t < 0)
+  {
+  *p++ = '-';
+  t = -t;
+  }
+
 s = t % 60;
 t /= 60;
 m = t % 60;
@@ -2792,10 +2852,19 @@ wanted. */
 
 if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL;
 
+/* The max retry interval must not be greater than 24 hours. */
+
+if (retry_interval_max > 24*60*60) retry_interval_max = 24*60*60;
+
 /* remote_max_parallel must be > 0 */
 
 if (remote_max_parallel <= 0) remote_max_parallel = 1;
 
+/* Save the configured setting of freeze_tell, so we can re-instate it at the
+start of a new SMTP message. */
+
+freeze_tell_config = freeze_tell;
+
 /* The primary host name may be required for expansion of spool_directory
 and log_file_path, so make sure it is set asap. It is obtained from uname(),
 but if that yields an unqualified value, make a FQDN by using gethostbyname to
@@ -2816,9 +2885,9 @@ if (primary_hostname == NULL)
     struct hostent *hostdata;
 
     #if HAVE_IPV6
-    if (dns_ipv4_lookup == NULL ||
+    if (!disable_ipv6 && (dns_ipv4_lookup == NULL ||
          match_isinlist(hostname, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN,
-           TRUE, NULL) != OK)
+           TRUE, NULL) != OK))
       af = AF_INET6;
     #else
     af = AF_INET;
@@ -3385,7 +3454,9 @@ else if (len == 7 && strncmpic(pp, US"timeout", len) == 0)
     }
   }
 
-else if (strncmpic(pp, US"rcpt_4", 6) == 0)
+else if (strncmpic(pp, US"mail_4", 6) == 0 ||
+         strncmpic(pp, US"rcpt_4", 6) == 0 ||
+         strncmpic(pp, US"data_4", 6) == 0)
   {
   BOOL bad = FALSE;
   int x = 255;                           /* means "any 4xx code" */
@@ -3402,18 +3473,24 @@ else if (strncmpic(pp, US"rcpt_4", 6) == 0)
     else if (a != 'x' || b != 'x') bad = TRUE;
     }
 
-  if (bad) return US"rcpt_4 must be followed by xx, dx, or dd, where "
-    "x is literal and d is any digit";
+  if (bad)
+    return string_sprintf("%.4s_4 must be followed by xx, dx, or dd, where "
+      "x is literal and d is any digit", pp);
 
-  *basic_errno = ERRNO_RCPT4XX;
+  *basic_errno = (*pp == 'm')? ERRNO_MAIL4XX :
+                 (*pp == 'r')? ERRNO_RCPT4XX : ERRNO_DATA4XX;
   *more_errno = x << 8;
   }
 
 else if (len == 4 && strncmpic(pp, US"auth", len) == 0 &&
          strncmpic(q+1, US"failed", p-q-1) == 0)
-  {
   *basic_errno = ERRNO_AUTHFAIL;
-  }
+
+else if (strncmpic(pp, US"lost_connection", p - pp) == 0)
+  *basic_errno = ERRNO_SMTPCLOSED;
+
+else if (strncmpic(pp, US"tls_required", p - pp) == 0)
+  *basic_errno = ERRNO_TLSREQUIRED;
 
 else if (len != 1 || Ustrncmp(pp, "*", 1) != 0)
   return string_sprintf("unknown or malformed retry error \"%.*s\"", p-pp, pp);
@@ -3551,6 +3628,7 @@ while ((p = get_config_line()) != NULL)
       break;
 
       case 'G':   /* Geometrically increasing intervals */
+      case 'H':   /* Ditto, but with randomness */
       rule->p1 = retry_arg(&p, 0);
       rule->p2 = retry_arg(&p, 1);
       break;
@@ -3561,7 +3639,7 @@ while ((p = get_config_line()) != NULL)
       }
 
     if (rule->timeout <= 0 || rule->p1 <= 0 ||
-          (rule->rule == 'G' && rule->p2 < 1000))
+          (rule->rule != 'F' && rule->p2 < 1000))
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
         "bad parameters for retry rule");