* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2024 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions for reading the configuration file, and for displaying
overall configuration values. Thanks to Brian Candler for the original
{ "acl_smtp_data_prdr", opt_stringptr, {&acl_smtp_data_prdr} },
#endif
#ifndef DISABLE_DKIM
- { "acl_smtp_dkim", opt_stringptr, {&acl_smtp_dkim} },
+ { "acl_smtp_dkim", opt_module, {US"dkim"} },
#endif
{ "acl_smtp_etrn", opt_stringptr, {&acl_smtp_etrn} },
{ "acl_smtp_expn", opt_stringptr, {&acl_smtp_expn} },
{ "acl_smtp_starttls", opt_stringptr, {&acl_smtp_starttls} },
#endif
{ "acl_smtp_vrfy", opt_stringptr, {&acl_smtp_vrfy} },
+#ifndef DISABLE_WELLKNOWN
+ { "acl_smtp_wellknown", opt_stringptr, {&acl_smtp_wellknown} },
+#endif
{ "add_environment", opt_stringptr, {&add_environment} },
{ "admin_groups", opt_gidlist, {&admin_groups} },
{ "allow_domain_literals", opt_bool, {&allow_domain_literals} },
-#ifdef ALLOW_INSECURE_TAINTED_DATA
- { "allow_insecure_tainted_data", opt_bool, {&allow_insecure_tainted_data} },
-#endif
{ "allow_mx_to_ip", opt_bool, {&allow_mx_to_ip} },
{ "allow_utf8_domains", opt_bool, {&allow_utf8_domains} },
{ "auth_advertise_hosts", opt_stringptr, {&auth_advertise_hosts} },
#endif
{ "disable_ipv6", opt_bool, {&disable_ipv6} },
#ifndef DISABLE_DKIM
- { "dkim_verify_hashes", opt_stringptr, {&dkim_verify_hashes} },
- { "dkim_verify_keytypes", opt_stringptr, {&dkim_verify_keytypes} },
- { "dkim_verify_min_keysizes", opt_stringptr, {&dkim_verify_min_keysizes} },
- { "dkim_verify_minimal", opt_bool, {&dkim_verify_minimal} },
- { "dkim_verify_signers", opt_stringptr, {&dkim_verify_signers} },
+ { "dkim_verify_hashes", opt_module, {US"dkim"} },
+ { "dkim_verify_keytypes", opt_module, {US"dkim"} },
+ { "dkim_verify_min_keysizes", opt_module, {US"dkim"} },
+ { "dkim_verify_minimal", opt_module, {US"dkim"} },
+ { "dkim_verify_signers", opt_module, {US"dkim"} },
#endif
#ifdef SUPPORT_DMARC
- { "dmarc_forensic_sender", opt_stringptr, {&dmarc_forensic_sender} },
- { "dmarc_history_file", opt_stringptr, {&dmarc_history_file} },
- { "dmarc_tld_file", opt_stringptr, {&dmarc_tld_file} },
+ { "dmarc_forensic_sender", opt_module, {US"dmarc"} },
+ { "dmarc_history_file", opt_module, {US"dmarc"} },
+ { "dmarc_tld_file", opt_module, {US"dmarc"} },
#endif
{ "dns_again_means_nonexist", opt_stringptr, {&dns_again_means_nonexist} },
{ "dns_check_names_pattern", opt_stringptr, {&check_dns_names_pattern} },
{ "hosts_connection_nolog", opt_stringptr, {&hosts_connection_nolog} },
#ifdef SUPPORT_PROXY
{ "hosts_proxy", opt_stringptr, {&hosts_proxy} },
+#endif
+#ifndef DISABLE_TLS
+ { "hosts_require_alpn", opt_stringptr, {&hosts_require_alpn} },
#endif
{ "hosts_require_helo", opt_stringptr, {&hosts_require_helo} },
{ "hosts_treat_as_local", opt_stringptr, {&hosts_treat_as_local} },
+#ifdef EXPERIMENTAL_XCLIENT
+ { "hosts_xclient", opt_stringptr, {&hosts_xclient} },
+#endif
#ifdef LOOKUP_IBASE
{ "ibase_servers", opt_stringptr, {&ibase_servers} },
#endif
{ "ldap_start_tls", opt_bool, {&eldap_start_tls} },
{ "ldap_version", opt_int, {&eldap_version} },
#endif
-#ifdef EXPERIMENTAL_ESMTP_LIMITS
+#ifndef DISABLE_ESMTP_LIMITS
{ "limits_advertise_hosts", opt_stringptr, {&limits_advertise_hosts} },
#endif
{ "local_from_check", opt_bool, {&local_from_check} },
#ifdef LOOKUP_ORACLE
{ "oracle_servers", opt_stringptr, {&oracle_servers} },
#endif
+ { "panic_coredump", opt_bool, {&panic_coredump} },
{ "percent_hack_domains", opt_stringptr, {&percent_hack_domains} },
#ifdef EXIM_PERL
{ "perl_at_start", opt_bool, {&opt_perl_at_start} },
{ "received_header_text", opt_stringptr, {&received_header_text} },
{ "received_headers_max", opt_int, {&received_headers_max} },
{ "recipient_unqualified_hosts", opt_stringptr, {&recipient_unqualified_hosts} },
- { "recipients_max", opt_int, {&recipients_max} },
+ { "recipients_max", opt_stringptr, {&recipients_max} },
{ "recipients_max_reject", opt_bool, {&recipients_max_reject} },
#ifdef LOOKUP_REDIS
{ "redis_servers", opt_stringptr, {&redis_servers} },
{ "spamd_address", opt_stringptr, {&spamd_address} },
#endif
#ifdef SUPPORT_SPF
- { "spf_guess", opt_stringptr, {&spf_guess} },
- { "spf_smtp_comment_template",opt_stringptr, {&spf_smtp_comment_template} },
+ { "spf_guess", opt_module, {US"spf"} },
+ { "spf_smtp_comment_template",opt_module, {US"spf"} },
#endif
{ "split_spool_directory", opt_bool, {&split_spool_directory} },
{ "spool_directory", opt_stringptr, {&spool_directory} },
#ifdef LOOKUP_SQLITE
{ "sqlite_dbfile", opt_stringptr, {&sqlite_dbfile} },
{ "sqlite_lock_timeout", opt_int, {&sqlite_lock_timeout} },
-#endif
-#ifdef EXPERIMENTAL_SRS_ALT
- { "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} },
{ "timezone", opt_stringptr, {&timezone_string} },
{ "tls_advertise_hosts", opt_stringptr, {&tls_advertise_hosts} },
#ifndef DISABLE_TLS
+ { "tls_alpn", opt_stringptr, {&tls_alpn} },
{ "tls_certificate", opt_stringptr, {&tls_certificate} },
{ "tls_crl", opt_stringptr, {&tls_crl} },
{ "tls_dh_max_bits", opt_int, {&tls_dh_max_bits} },
{ "uucp_from_pattern", opt_stringptr, {&uucp_from_pattern} },
{ "uucp_from_sender", opt_stringptr, {&uucp_from_sender} },
{ "warn_message_file", opt_stringptr, {&warn_message_file} },
- { "write_rejectlog", opt_bool, {&write_rejectlog} }
+#ifndef DISABLE_WELLKNOWN
+ { "wellknown_advertise_hosts",opt_stringptr, {&wellknown_advertise_hosts} },
+#endif
+ { "write_rejectlog", opt_bool, {&write_rejectlog} },
};
#ifndef MACRO_PREDEF
{
uschar buf[EXIM_DRIVERNAME_MAX];
-options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
+options_from_list(optionlist_auths, optionlist_auths_size,
+ US"AUTHENTICATORS", NULL);
-for (struct auth_info * ai = auths_available; ai->driver_name[0]; ai++)
+for (driver_info * di = (driver_info *)auths_available; di; di = di->next)
{
- spf(buf, sizeof(buf), US"_DRIVER_AUTHENTICATOR_%T", ai->driver_name);
+ auth_info * ai = (auth_info *)di;
+
+ spf(buf, sizeof(buf), US"_DRIVER_AUTHENTICATOR_%T", di->driver_name);
builtin_macro_create(buf);
- options_from_list(ai->options, (unsigned)*ai->options_count, US"AUTHENTICATOR", ai->driver_name);
+ options_from_list(di->options, (unsigned)*di->options_count,
+ US"AUTHENTICATOR", di->driver_name);
if (ai->macros_create) (ai->macros_create)();
}
pointer variables in the options table or in option tables for various drivers.
For debugging output, it is useful to be able to find the name of the option
which is currently being processed. This function finds it, if it exists, by
-searching the table(s).
+searching the table(s) for a value with the given content.
Arguments: a value that is presumed to be in the table above
Returns: the option name, or an empty string
*/
-uschar *
-readconf_find_option(void *p)
+const uschar *
+readconf_find_option(void * listptr)
{
-for (int i = 0; i < nelem(optionlist_config); i++)
- if (p == optionlist_config[i].v.value) return US optionlist_config[i].name;
+uschar * list = * USS listptr;
+const uschar * name = NULL, * drname = NULL;
-for (router_instance * r = routers; r; r = r->next)
- {
- router_info *ri = r->info;
- for (int i = 0; i < *ri->options_count; i++)
- {
- if ((ri->options[i].type & opt_mask) != opt_stringptr) continue;
- if (p == CS (r->options_block) + ri->options[i].v.offset)
- return US ri->options[i].name;
- }
- }
+for (optionlist * o = optionlist_config; /* main-config options */
+ o < optionlist_config + optionlist_config_size; o++)
+ if (listptr == o->v.value)
+ return US o->name;
-for (transport_instance * t = transports; t; t = t->next)
- {
- transport_info *ti = t->info;
- for (int i = 0; i < *ti->options_count; i++)
+if (router_name)
+ for (const driver_instance * rd = (driver_instance *)routers;
+ rd; rd = rd->next) if (Ustrcmp(rd->name, router_name) == 0)
{
- optionlist * op = &ti->options[i];
- if ((op->type & opt_mask) != opt_stringptr) continue;
- if (p == ( op->type & opt_public
- ? CS t
- : CS t->options_block
- )
- + op->v.offset)
- return US op->name;
+ const router_instance * r = (router_instance *)rd;
+ const router_info * ri = (router_info *)rd->info;
+
+ /* Check for a listptr match first */
+
+ for (optionlist * o = optionlist_routers; /* generic options */
+ o < optionlist_routers + optionlist_routers_size; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && listptr == CS r + o->v.offset)
+ return US o->name;
+
+ for (optionlist * o = ri->drinfo.options; /* private options */
+ o < ri->drinfo.options + *ri->drinfo.options_count; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && listptr == CS rd->options_block + o->v.offset)
+ return US o->name;
+
+ /* Check for a list addr match, unless null */
+
+ if (!list) continue;
+
+ for (optionlist * o = optionlist_routers; /* generic options */
+ o < optionlist_routers + optionlist_routers_size; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && list == * USS(CS r + o->v.offset))
+ if (name)
+ return string_sprintf("DUP: %s %s vs. %s %s",
+ drname, name, rd->name, o->name);
+ else
+ { name = US o->name; drname = rd->name; }
+
+ for (optionlist * o = ri->drinfo.options; /* private options */
+ o < ri->drinfo.options + *ri->drinfo.options_count; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && list == * USS(CS rd->options_block + o->v.offset))
+ if (name)
+ return string_sprintf("DUP: %s %s vs. %s %s",
+ drname, name, rd->name, o->name);
+ else
+ { name = US o->name; drname = rd->name; }
}
- }
-return US"";
+if (transport_name)
+ for (transport_instance * t = transports; t; t = t->drinst.next)
+ if (Ustrcmp(t->drinst.name, transport_name) == 0)
+ {
+ const transport_info * ti = t->drinst.info;
+
+ /* Check for a listptr match first */
+
+ for (optionlist * o = optionlist_transports; /* generic options */
+ o < optionlist_transports + optionlist_transports_size; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && listptr == CS t + o->v.offset)
+ return US o->name;
+
+ for (optionlist * o = ti->drinfo.options; /* private options */
+ o < ti->drinfo.options + *ti->drinfo.options_count; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && listptr == CS t->drinst.options_block + o->v.offset)
+ return US o->name;
+
+ /* Check for a list addr match, unless null */
+
+ if (!list) continue;
+
+ for (optionlist * o = optionlist_transports; /* generic options */
+ o < optionlist_transports + optionlist_transports_size; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && list == * USS(CS t + o->v.offset))
+ if (name)
+ return string_sprintf("DUP: %s %s vs. %s %s",
+ drname, name, t->drinst.name, o->name);
+ else
+ { name = US o->name; drname = t->drinst.name; }
+
+ for (optionlist * o = ti->drinfo.options; /* private options */
+ o < ti->drinfo.options + *ti->drinfo.options_count; o++)
+ if ( (o->type & opt_mask) == opt_stringptr
+ && list == * USS(CS t->drinst.options_block + o->v.offset))
+ if (name)
+ return string_sprintf("DUP: %s %s vs. %s %s",
+ drname, name, t->drinst.name, o->name);
+ else
+ { name = US o->name; drname = t->drinst.name; }
+ }
+
+return name ? name : US"";
}
macro_item *
macro_create(const uschar * name, const uschar * val, BOOL command_line)
{
-macro_item * m = store_get(sizeof(macro_item), FALSE);
+macro_item * m = store_get(sizeof(macro_item), GET_UNTAINTED);
READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val);
m->next = NULL;
(Ustrncmp(ss+8, "_if_exists", 10) == 0 && isspace(ss[18]))))
{
uschar *t;
- int include_if_exists = isspace(ss[8])? 0 : 10;
+ int include_if_exists = isspace(ss[8]) ? 0 : 10;
config_file_item *save;
struct stat statbuf;
if (config_lines)
save_config_position(config_filename, config_lineno);
- save = store_get(sizeof(config_file_item), FALSE);
+ save = store_get(sizeof(config_file_item), GET_UNTAINTED);
save->next = config_file_stack;
config_file_stack = save;
save->file = config_file;
return NULL;
}
+#ifdef LOOKUP_MODULE_DIR
+/* Check for any required module load operations */
+
+//mod_load_check(s);
+#endif
+
/* Return the first non-blank character. */
return s;
Returns: new input pointer
*/
-uschar *
-readconf_readname(uschar *name, int len, uschar *s)
+const uschar *
+readconf_readname(uschar * name, int len, const uschar * s)
{
int p = 0;
BOOL broken = FALSE;
static rewrite_rule *
readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal)
{
-rewrite_rule *next = store_get(sizeof(rewrite_rule), FALSE);
+rewrite_rule * next = store_get(sizeof(rewrite_rule), GET_UNTAINTED);
next->next = NULL;
next->key = string_dequote(&p);
readconf_handle_option(uschar *buffer, optionlist *oltop, int last,
void *data_block, uschar *unknown_txt)
{
-int ptr = 0;
+int ptr;
int offset = 0;
int count, type, value;
int issecure = 0;
int intbase = 0;
uschar *inttype = US"";
uschar *sptr;
-uschar *s = buffer;
+const uschar * s;
uschar **str_target;
uschar name[EXIM_DRIVERNAME_MAX];
uschar name2[EXIM_DRIVERNAME_MAX];
+sublist:
+
+s = buffer;
+ptr = 0;
+
/* There may be leading spaces; thereafter, we expect an option name starting
with a letter. */
-while (isspace(*s)) s++;
-if (!isalpha(*s))
+if (!isalpha( Uskip_whitespace(&s) ))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "option setting expected: %s", s);
/* Read the name of the option, and skip any subsequent white space. If
s++;
}
name[ptr] = 0;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
if (Ustrcmp(name, "hide") != 0) break;
issecure = opt_secure;
ptr = 0;
/* Skip white space after = */
-if (*s == '=') while (isspace((*(++s))));
+if (*s == '=') while (isspace(*++s));
/* If there is a data block and the opt_public flag is not set, change
the data block pointer to the private options block. */
case opt_gidlist:
case opt_rewrite:
- reset_point = store_mark();
- sptr = read_string(s, name);
+ reset_point = store_mark();
+ sptr = read_string(s, name);
- /* Having read a string, we now have several different ways of using it,
- depending on the data type, so do another switch. If keeping the actual
- string is not required (because it is interpreted), freesptr is set TRUE,
- and at the end we reset the pool. */
+ /* Having read a string, we now have several different ways of using it,
+ depending on the data type, so do another switch. If keeping the actual
+ string is not required (because it is interpreted), freesptr is set TRUE,
+ and at the end we reset the pool. */
- switch (type)
- {
- /* If this was a string, set the variable to point to the new string,
- and set the flag so its store isn't reclaimed. If it was a list of rewrite
- rules, we still keep the string (for printing), and parse the rules into a
- control block and flags word. */
-
- case opt_stringptr:
- str_target = data_block ? USS (US data_block + ol->v.offset)
- : USS ol->v.value;
- if (ol->type & opt_rep_con)
+ switch (type)
{
- uschar * saved_condition;
- /* We already have a condition, we're conducting a crude hack to let
- multiple condition rules be chained together, despite storing them in
- text form. */
- *str_target = string_copy_perm( (saved_condition = *str_target)
- ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
- saved_condition, sptr)
- : sptr,
- FALSE);
- /* TODO(pdp): there is a memory leak here and just below
- when we set 3 or more conditions; I still don't
- understand the store mechanism enough to know
- what's the safe way to free content from an earlier store.
- AFAICT, stores stack, so freeing an early stored item also stores
- all data alloc'd after it. If we knew conditions were adjacent,
- we could survive that, but we don't. So I *think* we need to take
- another bit from opt_type to indicate "malloced"; this seems like
- quite a hack, especially for this one case. It also means that
- we can't ever reclaim the store from the *first* condition.
-
- Because we only do this once, near process start-up, I'm prepared to
- let this slide for the time being, even though it rankles. */
- }
- else if (ol->type & opt_rep_str)
- {
- uschar sep_o =
- Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
- : Ustrncmp(name, "set", 3) == 0 ? ';'
- : ':';
- int sep_i = -(int)sep_o;
- const uschar * list = sptr;
- uschar * s;
- gstring * list_o = NULL;
-
- if (*str_target)
- {
- list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
- list_o = string_cat(list_o, *str_target);
- }
-
- while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
- list_o = string_append_listele(list_o, sep_o, s);
+ /* If this was a string, set the variable to point to the new string,
+ and set the flag so its store isn't reclaimed. If it was a list of rewrite
+ rules, we still keep the string (for printing), and parse the rules into a
+ control block and flags word. */
+
+ case opt_stringptr:
+ str_target = data_block ? USS (US data_block + ol->v.offset)
+ : USS ol->v.value;
+ if (ol->type & opt_rep_con)
+ {
+ uschar * saved_condition;
+ /* We already have a condition, we're conducting a crude hack to let
+ multiple condition rules be chained together, despite storing them in
+ text form. */
+ *str_target = string_copy_perm( (saved_condition = *str_target)
+ ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
+ saved_condition, sptr)
+ : sptr,
+ FALSE);
+ /* TODO(pdp): there is a memory leak here and just below
+ when we set 3 or more conditions; I still don't
+ understand the store mechanism enough to know
+ what's the safe way to free content from an earlier store.
+ AFAICT, stores stack, so freeing an early stored item also stores
+ all data alloc'd after it. If we knew conditions were adjacent,
+ we could survive that, but we don't. So I *think* we need to take
+ another bit from opt_type to indicate "malloced"; this seems like
+ quite a hack, especially for this one case. It also means that
+ we can't ever reclaim the store from the *first* condition.
+
+ Because we only do this once, near process start-up, I'm prepared to
+ let this slide for the time being, even though it rankles. */
+ }
+ else if (ol->type & opt_rep_str)
+ {
+ uschar sep_o =
+ Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
+ : Ustrncmp(name, "set", 3) == 0 ? ';'
+ : ':';
+ int sep_i = -(int)sep_o;
+ const uschar * list = sptr;
+ uschar * s;
+ gstring * list_o = NULL;
+
+ if (*str_target)
+ {
+ list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
+ list_o = string_cat(list_o, *str_target);
+ }
- if (list_o)
- *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
- }
- else
- {
- *str_target = sptr;
- freesptr = FALSE;
- }
- break;
+ while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
+ list_o = string_append_listele(list_o, sep_o, s);
- case opt_rewrite:
- if (data_block)
- *USS (US data_block + ol->v.offset) = sptr;
- else
- *USS ol->v.value = sptr;
- freesptr = FALSE;
- if (type == opt_rewrite)
- {
- int sep = 0;
- int *flagptr;
- uschar *p = sptr;
- rewrite_rule **chain;
- optionlist *ol3;
-
- sprintf(CS name2, "*%.50s_rules", name);
- ol2 = find_option(name2, oltop, last);
- sprintf(CS name2, "*%.50s_flags", name);
- ol3 = find_option(name2, oltop, last);
-
- if (!ol2 || !ol3)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "rewrite rules not available for driver");
+ if (list_o)
+ *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
+ }
+ else
+ {
+ *str_target = sptr;
+ freesptr = FALSE;
+ }
+ break;
- if (data_block)
- {
- chain = (rewrite_rule **)(US data_block + ol2->v.offset);
- flagptr = (int *)(US data_block + ol3->v.offset);
- }
- else
- {
- chain = (rewrite_rule **)ol2->v.value;
- flagptr = (int *)ol3->v.value;
- }
+ case opt_rewrite:
+ if (data_block)
+ *USS (US data_block + ol->v.offset) = sptr;
+ else
+ *USS ol->v.value = sptr;
+ freesptr = FALSE;
+ if (type == opt_rewrite)
+ {
+ int sep = 0;
+ int *flagptr;
+ uschar *p = sptr;
+ rewrite_rule **chain;
+ optionlist *ol3;
+
+ sprintf(CS name2, "*%.50s_rules", name);
+ ol2 = find_option(name2, oltop, last);
+ sprintf(CS name2, "*%.50s_flags", name);
+ ol3 = find_option(name2, oltop, last);
+
+ if (!ol2 || !ol3)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "rewrite rules not available for driver");
+
+ if (data_block)
+ {
+ chain = (rewrite_rule **)(US data_block + ol2->v.offset);
+ flagptr = (int *)(US data_block + ol3->v.offset);
+ }
+ else
+ {
+ chain = (rewrite_rule **)ol2->v.value;
+ flagptr = (int *)ol3->v.value;
+ }
- /* This will trap if sptr is tainted. Not sure if that can happen */
- while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
- {
- rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
- *chain = next;
- chain = &(next->next);
- }
+ /* This will trap if sptr is tainted. Not sure if that can happen */
+ while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
+ {
+ rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
+ *chain = next;
+ chain = &(next->next);
+ }
- if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
- "non-header rewrite - not allowed at transport time -");
- }
- break;
+ if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
+ "non-header rewrite - not allowed at transport time -");
+ }
+ break;
- /* If it was an expanded uid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
- of data. */
+ /* If it was an expanded uid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
+ of data. */
- case opt_expand_uid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ case opt_expand_uid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Look up a fixed uid, and also make use of the corresponding gid
- if a passwd entry is returned and the gid has not been set. */
+ /* Look up a fixed uid, and also make use of the corresponding gid
+ if a passwd entry is returned and the gid has not been set. */
- case opt_uid:
- if (!route_finduser(sptr, &pw, &uid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
- if (data_block)
- *(uid_t *)(US data_block + ol->v.offset) = uid;
- else
- *(uid_t *)ol->v.value = uid;
+ case opt_uid:
+ if (!route_finduser(sptr, &pw, &uid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
+ if (data_block)
+ *(uid_t *)(US data_block + ol->v.offset) = uid;
+ else
+ *(uid_t *)ol->v.value = uid;
- /* Set the flag indicating a fixed value is set */
+ /* Set the flag indicating a fixed value is set */
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- /* Handle matching gid if we have a passwd entry: done by finding the
- same name with terminating "user" changed to "group"; if not found,
- ignore. Also ignore if the value is already set. */
+ /* Handle matching gid if we have a passwd entry: done by finding the
+ same name with terminating "user" changed to "group"; if not found,
+ ignore. Also ignore if the value is already set. */
- if (pw == NULL) break;
- Ustrcpy(name+Ustrlen(name)-4, US"group");
- ol2 = find_option(name, oltop, last);
- if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
- (ol2->type & opt_mask) == opt_expand_gid))
- {
- BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
- if (!*set_flag)
- {
- if (data_block)
- *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
- else
- *((gid_t *)ol2->v.value) = pw->pw_gid;
- *set_flag = TRUE;
- }
- }
- break;
+ if (pw == NULL) break;
+ Ustrcpy(name+Ustrlen(name)-4, US"group");
+ ol2 = find_option(name, oltop, last);
+ if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
+ (ol2->type & opt_mask) == opt_expand_gid))
+ {
+ BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
+ if (!*set_flag)
+ {
+ if (data_block)
+ *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
+ else
+ *((gid_t *)ol2->v.value) = pw->pw_gid;
+ *set_flag = TRUE;
+ }
+ }
+ break;
- /* If it was an expanded gid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
- of data. */
+ /* If it was an expanded gid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
+ of data. */
- case opt_expand_gid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ case opt_expand_gid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Handle freestanding gid */
+ /* Handle freestanding gid */
- case opt_gid:
- if (!route_findgroup(sptr, &gid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
- if (data_block)
- *((gid_t *)(US data_block + ol->v.offset)) = gid;
- else
- *((gid_t *)ol->v.value) = gid;
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- break;
+ case opt_gid:
+ if (!route_findgroup(sptr, &gid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
+ if (data_block)
+ *((gid_t *)(US data_block + ol->v.offset)) = gid;
+ else
+ *((gid_t *)ol->v.value) = gid;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ break;
- /* If it was a uid list, look up each individual entry, and build
- a vector of uids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into
- permanent store already.) */
+ /* If it was a uid list, look up each individual entry, and build
+ a vector of uids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into
+ permanent store already.) */
- case opt_uidlist:
- {
- int count = 1;
- uid_t *list;
- int ptr = 0;
- const uschar *p;
- const 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++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(uid_t));
- list[ptr++] = (uid_t)(count - 1);
-
- if (data_block)
- *((uid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((uid_t **)ol->v.value) = list;
+ case opt_uidlist:
+ {
+ int count = 1;
+ uid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const 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++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(uid_t));
+ list[ptr++] = (uid_t)(count - 1);
+
+ if (data_block)
+ *((uid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((uid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- /* If p is tainted we trap. Not sure that can happen */
- (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
- if (!route_finduser(big_buffer, NULL, &uid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
- big_buffer);
- list[ptr++] = uid;
- }
- }
- break;
+ p = op;
+ while (count-- > 1)
+ {
+ int sep = 0;
+ /* If p is tainted we trap. Not sure that can happen */
+ (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+ if (!route_finduser(big_buffer, NULL, &uid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
+ big_buffer);
+ list[ptr++] = uid;
+ }
+ break;
+ }
- /* If it was a gid list, look up each individual entry, and build
- a vector of gids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into permanent
- store already.) */
+ /* If it was a gid list, look up each individual entry, and build
+ a vector of gids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into permanent
+ store already.) */
- case opt_gidlist:
- {
- int count = 1;
- gid_t *list;
- int ptr = 0;
- const uschar *p;
- const uschar *op = expand_string (sptr);
-
- if (!op)
- 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++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(gid_t));
- list[ptr++] = (gid_t)(count - 1);
-
- if (data_block)
- *((gid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((gid_t **)ol->v.value) = list;
+ case opt_gidlist:
+ {
+ int count = 1;
+ gid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const uschar *op = expand_string (sptr);
+
+ if (!op)
+ 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++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(gid_t));
+ list[ptr++] = (gid_t)(count - 1);
+
+ if (data_block)
+ *((gid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((gid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- /* If p is tainted we trap. Not sure that can happen */
- (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
- if (!route_findgroup(big_buffer, &gid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
- big_buffer);
- list[ptr++] = gid;
- }
+ p = op;
+ while (count-- > 1)
+ {
+ int sep = 0;
+ /* If p is tainted we trap. Not sure that can happen */
+ (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
+ if (!route_findgroup(big_buffer, &gid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
+ big_buffer);
+ list[ptr++] = gid;
+ }
+ break;
+ }
}
- break;
- }
- /* Release store if the value of the string doesn't need to be kept. */
+ /* Release store if the value of the string doesn't need to be kept. */
- if (freesptr) reset_point = store_reset(reset_point);
- break;
+ if (freesptr) reset_point = store_reset(reset_point);
+ break;
/* Expanded boolean: if no characters follow, or if there are no dollar
characters, this is a fixed-valued boolean, and we fall through. Otherwise,
save the string for later expansion in the alternate place. */
case opt_expand_bool:
- if (*s && Ustrchr(s, '$') != 0)
- {
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
+ if (*s && Ustrchr(s, '$') != 0)
{
- reset_point = store_mark();
- sptr = read_string(s, name);
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = sptr;
- else
- *(USS ol2->v.value) = sptr;
- freesptr = FALSE;
- break;
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ reset_point = store_mark();
+ sptr = read_string(s, name);
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = sptr;
+ else
+ *(USS ol2->v.value) = sptr;
+ freesptr = FALSE;
+ break;
+ }
}
- }
- /* Fall through */
+ /* Fall through */
/* Boolean: if no characters follow, the value is boolvalue. Otherwise
look for yes/not/true/false. Some booleans are stored in a single bit in
case opt_bit:
case opt_bool_verify:
case opt_bool_set:
- if (*s != 0)
- {
- s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
- if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
- boolvalue = TRUE;
- else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
- boolvalue = FALSE;
- else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "\"%s\" is not a valid value for the \"%s\" option", name2, name);
- if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
- US"for boolean option ", name);
- }
+ if (*s)
+ {
+ s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
+ if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
+ boolvalue = TRUE;
+ else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
+ boolvalue = FALSE;
+ else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "\"%s\" is not a valid value for the \"%s\" option", name2, name);
+ if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
+ US"for boolean option ", name);
+ }
- /* Handle single-bit type. */
+ /* Handle single-bit type. */
- if (type == opt_bit)
- {
- int bit = 1 << ((ol->type >> 16) & 31);
- int * ptr = data_block
- ? (int *)(US data_block + ol->v.offset)
- : (int *)ol->v.value;
- if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
- break;
- }
+ if (type == opt_bit)
+ {
+ int bit = 1 << ((ol->type >> 16) & 31);
+ int * ptr = data_block
+ ? (int *)(US data_block + ol->v.offset)
+ : (int *)ol->v.value;
+ if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
+ break;
+ }
- /* Handle full BOOL types */
+ /* Handle full BOOL types */
- if (data_block)
- *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
- else
- *((BOOL *)ol->v.value) = boolvalue;
+ if (data_block)
+ *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol->v.value) = boolvalue;
- /* Verify fudge */
+ /* Verify fudge */
- if (type == opt_bool_verify)
- {
- sprintf(CS name2, "%.50s_recipient", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
- else
- *((BOOL *)ol2->v.value) = boolvalue;
- }
+ if (type == opt_bool_verify)
+ {
+ sprintf(CS name2, "%.50s_recipient", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol2->v.value) = boolvalue;
+ }
- /* Note that opt_bool_set type is set, if there is somewhere to do so */
+ /* Note that opt_bool_set type is set, if there is somewhere to do so */
- else if (type == opt_bool_set)
- {
- sprintf(CS name2, "*set_%.50s", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
- else
- *((BOOL *)ol2->v.value) = TRUE;
- }
- break;
+ else if (type == opt_bool_set)
+ {
+ sprintf(CS name2, "*set_%.50s", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
+ else
+ *((BOOL *)ol2->v.value) = TRUE;
+ }
+ break;
/* Octal integer */
case opt_octint:
- intbase = 8;
- inttype = US"octal ";
+ intbase = 8;
+ inttype = US"octal ";
/* Integer: a simple(ish) case; allow octal and hex formats, and
suffixes K, M, G, and T. The different types affect output, not input. */
case opt_mkint:
case opt_int:
- {
- uschar *endptr;
- long int lvalue;
+ {
+ uschar *endptr;
+ long int lvalue;
- errno = 0;
- lvalue = strtol(CS s, CSS &endptr, intbase);
+ errno = 0;
+ 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 (endptr == s)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
+ inttype, name);
- if (errno != ERANGE && *endptr)
- {
- uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
-
- if ((mp = Ustrchr(mp, *endptr)))
+ if (errno != ERANGE && *endptr)
{
- endptr++;
- do
+ uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
+
+ if ((mp = Ustrchr(mp, *endptr)))
{
- if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ endptr++;
+ do
{
- errno = ERANGE;
- break;
+ if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ {
+ errno = ERANGE;
+ break;
+ }
+ lvalue *= 1024;
}
- lvalue *= 1024;
+ while (*(mp += 2));
}
- 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);
+ 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)
- extra_chars_error(endptr, inttype, US"integer value for ", name);
+ if (Uskip_whitespace(&endptr))
+ extra_chars_error(endptr, inttype, US"integer value for ", name);
- value = (int)lvalue;
- }
+ value = (int)lvalue;
+ }
- if (data_block)
- *(int *)(US data_block + ol->v.offset) = value;
- else
- *(int *)ol->v.value = value;
- break;
+ if (data_block)
+ *(int *)(US data_block + ol->v.offset) = value;
+ else
+ *(int *)ol->v.value = value;
+ break;
/* Integer held in K: again, allow formats and suffixes as above. */
if (errno == ERANGE) 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 (Uskip_whitespace(&endptr))
extra_chars_error(endptr, inttype, US"integer value for ", name);
if (data_block)
/* Fixed-point number: held to 3 decimal places. */
case opt_fixed:
- if (sscanf(CS s, "%d%n", &value, &count) != 1)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "fixed-point number expected for %s", name);
+ if (sscanf(CS s, "%d%n", &value, &count) != 1)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "fixed-point number expected for %s", name);
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- value *= 1000;
+ value *= 1000;
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- /* We get a coverity error here for using count, as it derived
- from the tainted buffer pointed to by s, as parsed by sscanf().
- By the definition of sscanf we must be accessing between start
- and end of s (assuming it is nul-terminated...) so ignore the error. */
- /* coverity[tainted_data] */
- if (s[count] == '.')
- {
- int d = 100;
- while (isdigit(s[++count]))
+ /* We get a coverity error here for using count, as it derived
+ from the tainted buffer pointed to by s, as parsed by sscanf().
+ By the definition of sscanf we must be accessing between start
+ and end of s (assuming it is nul-terminated...) so ignore the error. */
+ /* coverity[tainted_data] */
+ if (s[count] == '.')
{
- value += (s[count] - '0') * d;
- d /= 10;
+ int d = 100;
+ while (isdigit(s[++count]))
+ {
+ value += (s[count] - '0') * d;
+ d /= 10;
+ }
}
- }
- while (isspace(s[count])) count++;
+ while (isspace(s[count])) count++;
- if (s[count] != 0)
- extra_chars_error(s+count, US"fixed-point value for ", name, US"");
+ if (s[count] != 0)
+ extra_chars_error(s+count, US"fixed-point value for ", name, US"");
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* There's a special routine to read time values. */
case opt_time:
- value = readconf_readtime(s, 0, FALSE);
- if (value < 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
- name);
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ value = readconf_readtime(s, 0, FALSE);
+ if (value < 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
+ name);
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* A time list is a list of colon-separated times, with the first
element holding the size of the list and the second the number of
list[count+1] = value;
if (snext == NULL) break;
s = snext + 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
}
if (count > list[0] - 2)
}
case opt_func:
- {
- void (*fn)() = ol->v.fn;
- fn(name, s, 0);
+ ol->v.fn(name, s, 0);
break;
+
+ case opt_module:
+ {
+ uschar * errstr;
+ misc_module_info * mi = misc_mod_find(US ol->v.value, &errstr);
+ if (!mi)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "failed to find %s module for %s: %s", US ol->v.value, name, errstr);
+
+ oltop = mi->options;
+ last = mi->options_count;
+ goto sublist;
}
}
int s, m, h, d, w;
uschar *p = time_buffer;
-if (t < 0)
- {
- *p++ = '-';
- t = -t;
- }
+if (t < 0) *p++ = '-', t = -t;
s = t % 60;
t /= 60;
break;
case opt_bit:
- printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))?
- "" : "no_", name);
+ printf("%s%s\n", (*((int *)value)) & (1 << ((ol->type >> 16) & 31))
+ ? "" : "no_", name);
break;
case opt_expand_bool:
case opt_bool:
case opt_bool_verify:
case opt_bool_set:
- printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name);
+ printf("%s%s\n", *((BOOL *)value) ? "" : "no_", name);
break;
case opt_func:
*/
BOOL
-readconf_print(const uschar *name, uschar *type, BOOL no_labels)
+readconf_print(const uschar * name, const uschar * type, BOOL no_labels)
{
BOOL names_only = FALSE;
optionlist *ol2 = NULL;
for printing. So we have an admin_users restriction. */
if (!f.admin_user)
{
- fprintf(stderr, "exim: permission denied\n");
+ fprintf(stderr, "exim: permission denied; not admin\n");
return FALSE;
}
for (macro_item * m = macros; m; m = m->next)
for (; d; d = d->next)
{
BOOL rc = FALSE;
+ driver_info * di = d->info;
+
if (!name)
printf("\n%s %s:\n", d->name, type);
else if (Ustrcmp(d->name, name) != 0) continue;
if (!(ol->type & opt_hidden))
rc |= print_ol(ol, US ol->name, d, ol2, size, no_labels);
- for (optionlist * ol = d->info->options;
- ol < d->info->options + *(d->info->options_count); ol++)
+ for (optionlist * ol = di->options;
+ ol < di->options + *di->options_count; ol++)
if (!(ol->type & opt_hidden))
- rc |= print_ol(ol, US ol->name, d, d->info->options,
- *d->info->options_count, no_labels);
+ rc |= print_ol(ol, US ol->name, d, di->options,
+ *di->options_count, no_labels);
if (name) return rc;
}
BOOL forcecache = FALSE;
uschar *ss;
tree_node *t;
-namedlist_block * nb = store_get_perm(sizeof(namedlist_block), FALSE);
+namedlist_block * nb = store_get_perm(sizeof(namedlist_block), GET_UNTAINTED);
if (Ustrncmp(s, "_cache", 6) == 0)
{
Uskip_whitespace(&s);
ss = s;
while (isalnum(*s) || *s == '_') s++;
-t = store_get(sizeof(tree_node) + s-ss, is_tainted(ss));
+t = store_get(sizeof(tree_node) + s-ss, ss);
Ustrncpy(t->name, ss, s-ss);
t->name[s-ss] = 0;
Uskip_whitespace(&s);
{
int sep = 0;
struct stat statbuf;
-uschar *s, *filename;
-const uschar *list = config_main_filelist;
+uschar * s, * filename;
+const uschar * list = config_main_filelist;
/* Loop through the possible file names */
if (os_getcwd(buf, PATH_MAX) == NULL)
{
perror("exim: getcwd");
- exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE);
}
g = string_cat(NULL, buf);
/* If the dir does not end with a "/", append one */
- if (g->s[g->ptr-1] != '/')
+ if (gstring_last_char(g) != '/')
g = string_catn(g, US"/", 1);
/* If the config file contains a "/", extract the directory part */
if (Uchdir("/") < 0)
{
perror("exim: chdir `/': ");
- exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE);
}
/* Check the status of the file we have opened, if we have retained root
if (statbuf.st_size > 8192)
{
rmark r = store_mark();
- void * dummy = store_get((int)statbuf.st_size, FALSE);
+ void * dummy = store_get((int)statbuf.st_size, GET_UNTAINTED);
store_reset(r);
}
}
/* Expand the spool directory name; it may, for example, contain the primary
host name. Same comment about failure. */
+DEBUG(D_any) if (Ustrchr(spool_directory, '$'))
+ debug_printf("Expanding spool_directory option\n");
+
if (!(s = expand_string(spool_directory)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand spool_directory "
"\"%s\": %s", spool_directory, expand_string_message);
if (syslog_facility_str)
{
int i;
- uschar *s = syslog_facility_str;
+ uschar * s = syslog_facility_str;
if ((Ustrlen(syslog_facility_str) >= 4) &&
(strncmpic(syslog_facility_str, US"log_", 4) == 0))
if (*pid_file_path)
{
- if (!(s = expand_string(pid_file_path)))
+ const uschar * t = expand_cstring(pid_file_path);
+ if (!t)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path "
"\"%s\": %s", pid_file_path, expand_string_message);
- pid_file_path = s;
+ pid_file_path = t;
}
/* Set default value of process_log_path */
/* Compile the regex for matching a UUCP-style "From_" line in an incoming
message. */
-regex_From = regex_must_compile(uucp_from_pattern, FALSE, TRUE);
+regex_From = regex_must_compile(uucp_from_pattern, MCS_NOFLAGS, TRUE);
/* Unpick the SMTP rate limiting options, if set */
"failed to expand localhost_number \"%s\": %s",
host_number_string, expand_string_message);
n = Ustrtol(s, &end, 0);
- while (isspace(*end)) end++;
- if (*end)
+ if (Uskip_whitespace(&end))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"localhost_number value is not a number: %s", s);
if (n > LOCALHOST_MAX)
+/* Add a driver info struct to a list. */
+
+void
+add_driver_info(driver_info ** drlist_p, const driver_info * newent,
+ size_t size)
+{
+driver_info * listent = store_get(size, newent);
+memcpy(listent, newent, size);
+listent->next = *drlist_p;
+*drlist_p= listent;
+}
+
/*************************************************
* Initialize one driver *
*************************************************/
Arguments:
d pointer to driver instance block, with generic
options filled in
- drivers_available vector of available drivers
+ info_anchor list of available drivers
size_of_info size of each block in drivers_available
- class class of driver, for error message
+ class class of driver
Returns: pointer to the driver info block
*/
static driver_info *
-init_driver(driver_instance *d, driver_info *drivers_available,
- int size_of_info, uschar *class)
+init_driver(driver_instance * d, driver_info ** info_anchor,
+ int size_of_info, const uschar * class)
{
-for (driver_info * dd = drivers_available; dd->driver_name[0] != 0;
- dd = (driver_info *)((US dd) + size_of_info))
- if (Ustrcmp(d->driver_name, dd->driver_name) == 0)
+driver_info * di;
+int len;
+#ifdef LOOKUP_MODULE_DIR
+DIR * dd;
+#endif
+
+/* First scan the list of driver seen so far. */
+
+for (di = *info_anchor; di; di = di->next)
+ if (Ustrcmp(d->driver_name, di->driver_name) == 0)
+ goto found;
+
+#ifdef LOOKUP_MODULE_DIR
+/* Potentially a loadable module. Look for a file with the right name. */
+
+if (!(dd = exim_opendir(CUS LOOKUP_MODULE_DIR)))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "Couldn't open %s: not loading driver modules\n", LOOKUP_MODULE_DIR);
+ }
+else
+ {
+ uschar * fname = string_sprintf("%s_%s." DYNLIB_FN_EXT, d->driver_name, class), * sname;
+ const char * errormsg;
+
+ DEBUG(D_any) debug_printf("Loading %s %s driver from %s\n",
+ d->driver_name, class, LOOKUP_MODULE_DIR);
+
+ for(struct dirent * ent; ent = readdir(dd); ) if (Ustrcmp(ent->d_name, fname) == 0)
{
- int len = dd->options_len;
- d->info = dd;
- d->options_block = store_get_perm(len, FALSE);
- memcpy(d->options_block, dd->options_block, len);
- for (int i = 0; i < *(dd->options_count); i++)
- dd->options[i].type &= ~opt_set;
- return dd;
+ void * dl = dlopen(CS string_sprintf(LOOKUP_MODULE_DIR "/%s", fname), RTLD_NOW);
+ static driver_magics dm[] = {
+ { ROUTER_MAGIC, US"router" },
+ { TRANSPORT_MAGIC, US"transport" },
+ { AUTH_MAGIC, US"auth" },
+ };
+
+ if (!dl)
+ {
+ errormsg = dlerror();
+ log_write(0, LOG_MAIN|LOG_PANIC, "Error loading %s %s driver: %s\n",
+ d->driver_name, class, errormsg);
+ break;
+ }
+ (void) dlerror(); /* cf. comment in init_lookup_list() */
+
+ di = (driver_info *) dlsym(dl, CS string_sprintf("_%s_info", class));
+ if ((errormsg = dlerror()))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "%s does not appear to be a %s module (%s)\n", fname, class, errormsg);
+ dlclose(dl);
+ break;
+ }
+ for(driver_magics * dmp = dm; dmp < dm + nelem(dm); dmp++)
+ if(Ustrcmp(dmp->class, class) == 0 && dmp->magic == di->dyn_magic)
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ add_driver_info(info_anchor, di, size_of_info);
+ store_pool = old_pool;
+ DEBUG(D_any) debug_printf("Loaded %s %s\n", d->driver_name, class);
+ closedir(dd);
+ goto found;
+ }
+
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "%s module %s is not compatible with this version of Exim\n",
+ class, d->driver_name);
+ dlclose(dl);
+ break;
}
+ closedir(dd);
+ }
+#endif /* LOOKUP_MODULE_DIR */
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
"%s %s: cannot find %s driver \"%s\"", class, d->name, class, d->driver_name);
-return NULL; /* never obeyed */
+found:
+
+len = di->options_len;
+d->info = di;
+d->options_block = store_get_perm(len, GET_UNTAINTED);
+memcpy(d->options_block, di->options_block, len);
+for (int i = 0; i < *di->options_count; i++)
+ di->options[i].type &= ~opt_set;
+return di;
}
+static void
+driver_init_fini(driver_instance * d, const uschar * class)
+{
+driver_info * di = d->info;
+
+if (!d->driver_name)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
+ "no driver defined for %s \"%s\"", class, d->name);
+(di->init)(d);
+}
+
+
/*************************************************
* Initialize driver list *
*************************************************/
blocks for this shared code to work.
Arguments:
- class "router", "transport", or "authenticator"
anchor &routers, &transports, &auths
- drivers_available available drivers
+ info_anchor available drivers
size_of_info size of each info block
instance_default points to default data for an instance
instance_size size of instance block
driver_optionlist generic option list
driver_optionlist_count count of generic option list
+ class "router", "transport", or "auth"
+ for filename component (and error message)
Returns: nothing
*/
void
readconf_driver_init(
- uschar *class,
- driver_instance **anchor,
- driver_info *drivers_available,
+ driver_instance ** anchor,
+ driver_info ** info_anchor,
int size_of_info,
- void *instance_default,
+ void * instance_default,
int instance_size,
- optionlist *driver_optionlist,
- int driver_optionlist_count)
+ optionlist * driver_optionlist,
+ int driver_optionlist_count,
+ const uschar * class)
{
-driver_instance **p = anchor;
-driver_instance *d = NULL;
-uschar *buffer;
+driver_instance ** p = anchor;
+driver_instance * d = NULL;
+uschar * buffer;
while ((buffer = get_config_line()))
{
uschar name[EXIM_DRIVERNAME_MAX];
- uschar *s;
+ const 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
{
if (d)
{
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
/* s is using big_buffer, so this call had better not */
- (d->info->init)(d);
+ driver_init_fini(d, class);
d = NULL;
}
if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE);
/* Finish off initializing the previous driver. */
if (d)
- {
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
- (d->info->init)(d);
- }
+ driver_init_fini(d, class);
/* Check that we haven't already got a driver of this name */
/* Set up a new driver instance data block on the chain, with
its default values installed. */
- d = store_get_perm(instance_size, FALSE);
+ d = store_get_perm(instance_size, GET_UNTAINTED);
memcpy(d, instance_default, instance_size);
*p = d;
- p = &d->next;
+ p = (driver_instance **)&d->next;
d->name = string_copy(name);
+ d->srcfile = config_filename;
+ d->srcline = config_lineno;
/* Clear out the "set" bits in the generic options */
driver_optionlist_count, d, NULL))
{
if (!d->info && d->driver_name)
- init_driver(d, drivers_available, size_of_info, class);
+ init_driver(d, info_anchor, size_of_info, class);
}
/* Handle private options - pass the generic block because some may
block. */
else if (d->info)
- readconf_handle_option(buffer, d->info->options,
- *(d->info->options_count), d, US"option \"%s\" unknown");
+ {
+ driver_info * di = d->info;
+ readconf_handle_option(buffer, di->options,
+ *di->options_count, d, US"option \"%s\" unknown");
+ }
/* The option is not generic and the driver name has not yet been given. */
/* Run the initialization function for the final driver. */
if (d)
- {
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
- (d->info->init)(d);
- }
+ driver_init_fini(d, class);
}
*/
BOOL
-readconf_depends(driver_instance *d, uschar *s)
+readconf_depends(driver_instance * d, uschar * s)
{
-int count = *(d->info->options_count);
-uschar *ss;
+driver_info * di = d->info;
+int count = *di->options_count;
+uschar * ss;
-for (optionlist * ol = d->info->options; ol < d->info->options + count; ol++)
+for (optionlist * ol = di->options; ol < di->options + count; ol++)
if ((ol->type & opt_mask) == opt_stringptr)
{
void * options_block = ol->type & opt_public ? (void *)d : d->options_block;
*/
static int
-retry_arg(const uschar **paddr, int type)
+retry_arg(const uschar ** paddr, int type)
{
-const uschar *p = *paddr;
-const uschar *pp;
+const uschar * p = *paddr, * pp;
if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected");
pp = p;
while (isalnum(*p) || (type == 1 && *p == '.')) p++;
-if (*p != 0 && !isspace(*p) && *p != ',' && *p != ';')
+if (*p && !isspace(*p) && *p != ',' && *p != ';')
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma or semicolon expected");
*paddr = p;
const uschar *pp;
uschar *error;
- next = store_get(sizeof(retry_config), FALSE);
+ next = store_get(sizeof(retry_config), GET_UNTAINTED);
next->next = NULL;
*chain = next;
chain = &(next->next);
while (*p)
{
- retry_rule *rule = store_get(sizeof(retry_rule), FALSE);
+ retry_rule * rule = store_get(sizeof(retry_rule), GET_UNTAINTED);
*rchain = rule;
rchain = &(rule->next);
rule->next = NULL;
#ifndef DISABLE_PIPE_CONNECT
int nauths = 0;
#endif
+int old_pool = store_pool;
+store_pool = POOL_PERM;
+ {
+ driver_info ** anchor = (driver_info **) &auths_available;
+
+ /* Add the transport drivers that are built for static linkage to the
+ list of availables. */
-readconf_driver_init(US"authenticator",
- (driver_instance **)(&auths), /* chain anchor */
- (driver_info *)auths_available, /* available drivers */
+#if defined(AUTH_CRAM_MD5) && AUTH_CRAM_MD5!=2
+ extern auth_info cram_md5_auth_info;
+ add_driver_info(anchor, &cram_md5_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_CYRUS_SASL) && AUTH_CYRUS_SASL!=2
+ extern auth_info cyrus_sasl_auth_info;
+ add_driver_info(anchor, &cyrus_sasl_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_DOVECOT) && AUTH_DOVECOT!=2
+ extern auth_info dovecot_auth_info;
+ add_driver_info(anchor, &dovecot_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_EXTERNAL) && AUTH_EXTERNAL!=2
+ extern auth_info external_auth_info;
+ add_driver_info(anchor, &external_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_GSASL) && AUTH_GSASL!=2
+ extern auth_info gsasl_auth_info;
+ add_driver_info(anchor, &gsasl_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_HEIMDAL_GSSAPI) && AUTH_HEIMDAL_GSSAPI!=2
+ extern auth_info heimdal_gssapi_auth_info;
+ add_driver_info(anchor, &heimdal_gssapi_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_PLAINTEXT) && AUTH_PLAINTEXT!=2
+ extern auth_info plaintext_auth_info;
+ add_driver_info(anchor, &plaintext_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_SPA) && AUTH_SPA!=2
+ extern auth_info spa_auth_info;
+ add_driver_info(anchor, &spa_auth_info.drinfo, sizeof(auth_info));
+#endif
+#if defined(AUTH_TLS) && AUTH_TLS!=2
+ extern auth_info tls_auth_info;
+ add_driver_info(anchor, &tls_auth_info.drinfo, sizeof(auth_info));
+#endif
+ }
+store_pool = old_pool;
+
+/* Read the config file "authenticators" section, creating an auth instance list.
+For any yet-undiscovered driver, check for a loadable module and add it to
+those available. */
+
+readconf_driver_init((driver_instance **)&auths, /* chain anchor */
+ (driver_info **)&auths_available, /* available drivers */
sizeof(auth_info), /* size of info block */
&auth_defaults, /* default values for generic options */
sizeof(auth_instance), /* size of instance block */
optionlist_auths, /* generic options */
- optionlist_auths_size);
+ optionlist_auths_size,
+ US"auth");
-for (auth_instance * au = auths; au; au = au->next)
+for (auth_instance * au = auths; au; au = au->drinst.next)
{
if (!au->public_name)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no public name specified for "
- "the %s authenticator", au->name);
+ "the %s authenticator", au->drinst.name);
- for (auth_instance * bu = au->next; bu; bu = bu->next)
+ for (auth_instance * bu = au->drinst.next; bu; bu = bu->drinst.next)
if (strcmpic(au->public_name, bu->public_name) == 0)
- if ((au->client && bu->client) || (au->server && bu->server))
+ if ( au->client && bu->client
+ || au->server && bu->server)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "two %s authenticators "
"(%s and %s) have the same public name (%s)",
- au->client ? US"client" : US"server", au->name, bu->name,
- au->public_name);
+ au->client && bu->client ? US"client" : US"server",
+ au->drinst.name, bu->drinst.name, au->public_name);
#ifndef DISABLE_PIPE_CONNECT
nauths++;
#endif
}
#ifndef DISABLE_PIPE_CONNECT
-f.smtp_in_early_pipe_no_auth = nauths > 16;
+f.smtp_in_early_pipe_no_auth = nauths > 16; /* bits in bitmap limit */
#endif
}
+/* For error messages, a string describing the config location associated
+with current processing. NULL if we are not in an authenticator. */
+
+uschar *
+authenticator_current_name(void)
+{
+if (!authenticator_name) return NULL;
+return string_sprintf(" (authenticator %s, %s %d)", authenticator_name, driver_srcfile, driver_srcline);
+}
+
+
+
/*************************************************
static void
readconf_acl(void)
{
-uschar *p;
-
/* Read each ACL and add it into the tree. Macro (re)definitions are allowed
between ACLs. */
while(acl_line)
{
uschar name[EXIM_DRIVERNAME_MAX];
- tree_node *node;
- uschar *error;
+ tree_node * node;
+ uschar * error;
+ const uschar * p = readconf_readname(name, sizeof(name), acl_line);
- p = readconf_readname(name, sizeof(name), acl_line);
if (isupper(*name) && *p == '=')
{
if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE);
if (*p != ':' || name[0] == 0)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
- node = store_get_perm(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
+ node = store_get_perm(sizeof(tree_node) + Ustrlen(name), name);
Ustrcpy(node->name, name);
if (!tree_insertnode(&acl_anchor, node))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
int mid = last/2;
int n = Ustrlen(next_section);
+ READCONF_DEBUG fprintf(stderr, "%s: %s\n", __FUNCTION__, next_section);
if (tolower(next_section[n-1]) != 's') Ustrcpy(next_section+n, US"s");
for (;;)
}
(void)fclose(config_file);
+config_lineno = 0; /* Ensure we don't log a spurious position */
}
/* Init the storage for the pre-parsed config lines */
static config_line_item *current;
config_line_item *next;
-next = (config_line_item*) store_get(sizeof(config_line_item), FALSE);
+next = (config_line_item*) store_get(sizeof(config_line_item), GET_UNTAINTED);
next->line = string_copy(line);
next->next = NULL;
{
const int TS = terse ? 0 : 2;
int indent = 0;
+rmark r = NULL;
-for (config_line_item * i = config_lines; i; i = i->next)
+for (const config_line_item * i = config_lines; i; i = i->next)
{
- uschar *current;
- uschar *p;
+ uschar * current, * p;
- /* skip over to the first non-space */
- for (current = i->line; *current && isspace(*current); ++current)
- ;
+ if (r) store_reset(r);
+ r = store_mark();
- if (*current == '\0')
+ /* skip over to the first non-space */
+ current = string_copy(i->line);
+ if (!Uskip_whitespace(¤t))
continue;
/* Collapse runs of spaces. We stop this if we encounter one of the
- * following characters: "'$, as this may indicate careful formatting */
- for (p = current; *p; ++p)
+ following characters: "'$, as this may indicate careful formatting */
+
+ for (p = current; *p; p++) if (isspace(*p))
{
- uschar *next;
- if (!isspace(*p)) continue;
+ uschar * next = p;
if (*p != ' ') *p = ' ';
- for (next = p; isspace(*next); ++next)
- ;
+ Uskip_whitespace(&p);
if (next - p > 1)
memmove(p+1, next, Ustrlen(next)+1);
/* rest is public */
printf("%*s%s\n", indent, "", current);
}
+if (r) store_reset(r);
}
#endif /*!MACRO_PREDEF*/