-/* $Cambridge: exim/src/src/readconf.c,v 1.6 2005/04/04 10:33:49 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.25 2006/10/18 08:55:38 ph10 Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2006 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for reading the configuration file, and for displaying
#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 },
{ "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 },
{ "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 },
+ { "disable_ipv6", opt_bool, &disable_ipv6 },
{ "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 },
+ { "dns_csa_use_reverse", opt_bool, &dns_csa_use_reverse },
{ "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup },
{ "dns_retrans", opt_time, &dns_retrans },
{ "dns_retry", opt_int, &dns_retry },
#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 },
+ { "srs_hashmin", opt_int, &srs_hashmin },
+ { "srs_maxage", opt_int, &srs_maxage },
+ { "srs_secrets", opt_stringptr, &srs_secrets },
+ { "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 },
+/*************************************************
+* Deal with an assignment to a macro *
+*************************************************/
+
+/* 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
+replacement text are extracted and stored. Redefinition of existing,
+non-command line, macros is permitted using '==' instead of '='.
+
+Arguments:
+ s points to the start of the logical line
+
+Returns: nothing
+*/
+
+static void
+read_macro_assignment(uschar *s)
+{
+uschar name[64];
+int namelen = 0;
+BOOL redef = FALSE;
+macro_item *m;
+macro_item *mlast = NULL;
+
+while (isalnum(*s) || *s == '_')
+ {
+ if (namelen >= sizeof(name) - 1)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "macro name too long (maximum is %d characters)", sizeof(name) - 1);
+ name[namelen++] = *s++;
+ }
+name[namelen] = 0;
+
+while (isspace(*s)) s++;
+if (*s++ != '=')
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "malformed macro definition");
+
+if (*s == '=')
+ {
+ redef = TRUE;
+ s++;
+ }
+while (isspace(*s)) s++;
+
+/* If an existing macro of the same name was defined on the command line, we
+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. Note: it is documented that the other way round
+works. */
+
+for (m = macros; m != NULL; m = m->next)
+ {
+ int len = Ustrlen(m->name);
+
+ if (Ustrcmp(m->name, name) == 0)
+ {
+ if (!m->command_line && !redef)
+ log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already "
+ "defined (use \"==\" if you want to redefine it", name);
+ break;
+ }
+
+ if (len < namelen && Ustrstr(name, m->name) != NULL)
+ log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
+ "a macro because previously defined macro \"%s\" is a substring",
+ name, m->name);
+
+ /* We cannot have this test, because it is documented that a substring
+ macro is permitted (there is even an example).
+ *
+ * if (len > 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);
+ */
+
+ mlast = m;
+ }
+
+/* Check for an overriding command-line definition. */
+
+if (m != NULL && m->command_line) return;
+
+/* Redefinition must refer to an existing macro. */
+
+if (redef)
+ {
+ if (m == NULL)
+ log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "can't redefine an undefined macro "
+ "\"%s\"", name);
+ }
+
+/* We have a new definition. The macro_item structure includes a final vector
+called "name" which is one byte long. Thus, adding "namelen" gives us enough
+room to store the "name" string. */
+
+else
+ {
+ m = store_get(sizeof(macro_item) + namelen);
+ if (macros == NULL) macros = m; else mlast->next = m;
+ Ustrncpy(m->name, name, namelen);
+ m->name[namelen] = 0;
+ m->next = NULL;
+ m->command_line = FALSE;
+ }
+
+/* Set the value of the new or redefined macro */
+
+m->replacement = string_copy(s);
+}
+
+
+
+
/*************************************************
* Read configuration line *
{
if (config_file_stack != NULL) /* EOF inside .include */
{
- fclose(config_file);
+ (void)fclose(config_file);
config_file = config_file_stack->file;
config_filename = config_file_stack->filename;
config_lineno = config_file_stack->lineno;
}
*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));
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);
else
*((uid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
- p = sptr;
+ p = op;
while (count-- > 1)
{
int sep = 0;
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);
else
*((gid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
- p = sptr;
+ p = op;
while (count-- > 1)
{
int sep = 0;
int s, m, h, d, w;
uschar *p = time_buffer;
+if (t < 0)
+ {
+ *p++ = '-';
+ t = -t;
+ }
+
s = t % 60;
t /= 60;
m = t % 60;
while ((s = get_config_line()) != NULL)
{
- if (isupper(s[0]))
- {
- macro_item *m;
- macro_item *mlast = NULL;
- uschar name[64];
- int namelen = 0;
-
- while (isalnum(*s) || *s == '_')
- {
- if (namelen >= sizeof(name) - 1)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "macro name too long (maximum is %d characters)", sizeof(name) - 1);
- name[namelen++] = *s++;
- }
- name[namelen] = 0;
- while (isspace(*s)) s++;
- if (*s++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "malformed macro definition");
- while (isspace(*s)) s++;
-
- /* If an existing macro of the same name was defined on the command line,
- we just skip this definition. Otherwise it's an error to attempt to
- redefine a macro. It is also an error to define a macro whose name begins
- with the name of a previously-defined macro. */
-
- for (m = macros; m != NULL; m = m->next)
- {
- int len = Ustrlen(m->name);
-
- if (Ustrcmp(m->name, name) == 0)
- {
- if (m->command_line) break;
- log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "macro \"%s\" is already "
- "defined", name);
- }
-
- if (len < namelen && Ustrstr(name, m->name) != NULL)
- log_write(0, LOG_CONFIG|LOG_PANIC_DIE, "\"%s\" cannot be defined as "
- "a macro because previously defined macro \"%s\" is a substring",
- name, m->name);
-
- mlast = m;
- }
- if (m != NULL) continue; /* Found an overriding command-line definition */
-
- m = store_get(sizeof(macro_item) + namelen);
- m->next = NULL;
- m->command_line = FALSE;
- if (mlast == NULL) macros = m; else mlast->next = m;
-
- /* This use of strcpy() is OK because we have obtained a block of store
- whose size is based on the length of "name". The definition of the
- macro_item structure includes a final vector called "name" which is one
- byte long. Thus, adding "namelen" gives us enough room to store the "name"
- string. */
-
- Ustrcpy(m->name, name);
- m->replacement = string_copy(s);
- }
+ if (isupper(s[0])) read_macro_assignment(s);
else if (Ustrncmp(s, "domainlist", 10) == 0)
read_named_list(&domainlist_anchor, &domainlist_count,
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
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;
driver_instance *d = NULL;
uschar *buffer;
-/* Now process the configuration lines */
-
while ((buffer = get_config_line()) != NULL)
{
uschar name[64];
+ uschar *s;
+
+ /* Read the first name on the line and test for the start of a new driver. A
+ macro definition indicates the end of the previous driver. If this isn't the
+ start of a new driver, the line will be re-read. */
- /* Read the first name on the line and test for the start of a new driver.
- If this isn't the start of a new driver, the line will be re-read. */
+ s = readconf_readname(name, sizeof(name), buffer);
- uschar *s = readconf_readname(name, sizeof(name), buffer);
+ /* Handle macro definition, first finishing off the initialization of the
+ previous driver, if any. */
+
+ if (isupper(*name) && *s == '=')
+ {
+ if (d != NULL)
+ {
+ if (d->driver_name == NULL)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+ "no driver defined for %s \"%s\"", class, d->name);
+ (d->info->init)(d);
+ d = NULL;
+ }
+ read_macro_assignment(buffer);
+ continue;
+ }
/* If the line starts with a name terminated by a colon, we are at the
start of the definition of a new driver. The rest of the line must be
continue;
}
- /* Give an error if we have not set up a current driver yet. */
+ /* Not the start of a new driver. Give an error if we have not set up a
+ current driver yet. */
if (d == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
"%s name missing", class);
}
}
-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" */
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);
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;
}
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");
return;
}
-/* Read each ACL and add it into the tree */
+/* Read each ACL and add it into the tree. Macro (re)definitions are allowed
+between ACLs. */
acl_line = get_config_line();
uschar *error;
p = readconf_readname(name, sizeof(name), acl_line);
- if (*p != ':')
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing ACL name");
+ if (isupper(*name) && *p == '=')
+ {
+ read_macro_assignment(acl_line);
+ acl_line = get_config_line();
+ continue;
+ }
+
+ if (*p != ':' || name[0] == 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
node = store_get(sizeof(tree_node) + Ustrlen(name));
Ustrcpy(node->name, name);
}
}
-fclose(config_file);
+(void)fclose(config_file);
}
/* End of readconf.c */