This allows the global settings to always override default settings.
if (!ctx->filter_written) {
string_t *str = t_str_new(128);
str_append(str, ":FILTER ");
- config_dump_full_append_filter(str, ctx->filter, TRUE);
+ if (ctx->filter != NULL)
+ config_dump_full_append_filter(str, ctx->filter, TRUE);
str_append_c(str, '\n');
o_stream_nsend(ctx->output, str_data(str), str_len(str));
ctx->filter_written = TRUE;
config_dump_full_sections(struct config_dump_full_context *ctx,
unsigned int parser_idx,
const struct setting_parser_info *info,
- const string_t *delayed_filter)
+ const string_t *delayed_filter,
+ bool dump_defaults)
{
struct ostream *output = ctx->output;
enum config_dump_full_dest dest = ctx->dest;
const struct config_filter_parser *filter = ctx->filters[i];
uoff_t start_offset = output->offset;
+ if (filter->filter.default_settings != dump_defaults)
+ continue;
if (filter->module_parsers[parser_idx].settings == NULL &&
filter->module_parsers[parser_idx].delayed_error == NULL)
continue;
}
}
- if (str_len(delayed_filter) > 0) {
+ if (delayed_filter != NULL && str_len(delayed_filter) > 0) {
ctx->filter_indexes_be32[ctx->filter_output_count] =
0; /* empty/global filter */
ctx->filter_offsets_be64[ctx->filter_output_count] =
struct dump_context dump_ctx = {
.delayed_output = str_new(default_pool, 256),
- .filter_written = TRUE,
};
if (dest == CONFIG_DUMP_FULL_DEST_STDOUT) {
sizeof(filter_count));
}
+ /* Write default settings filters */
+ int ret;
+ T_BEGIN {
+ ret = config_dump_full_sections(&ctx, i, info, NULL, TRUE);
+ } T_END;
+ if (ret < 0)
+ break;
+
uoff_t blob_size_offset = output->offset;
/* Write base settings - add it as an empty filter */
ctx.filter_indexes_be32[ctx.filter_output_count] = 0;
ctx.filter_output_count++;
if (dest != CONFIG_DUMP_FULL_DEST_STDOUT) {
+ /* Write a filter for the base settings, even if there
+ are no settings. This allows lib-settings to apply
+ setting overrides at the proper position before
+ defaults. */
o_stream_nsend(output, &blob_size, sizeof(blob_size));
o_stream_nsend(output, "", 1); /* no error */
+ dump_ctx.filter_written = TRUE;
+ } else {
+ /* Make :FILTER visible */
+ dump_ctx.filter_written = FALSE;
}
dump_ctx.info = info;
if (config_export_parser(export_ctx, i, &error) < 0) {
break;
}
- int ret;
+ /* Write non-default settings filters */
T_BEGIN {
ret = config_dump_full_sections(&ctx, i, info,
- dump_ctx.delayed_output);
+ dump_ctx.delayed_output, FALSE);
} T_END;
if (ret < 0)
break;
return mask == NULL && filter == NULL;
}
-bool config_filters_equal(const struct config_filter *f1,
- const struct config_filter *f2)
+static bool
+config_filters_equal_without_defaults(const struct config_filter *f1,
+ const struct config_filter *f2)
{
if (null_strcmp(f1->service, f2->service) != 0)
return FALSE;
/* Check the parents' compatibility also. However, it's
possible that one of these parents is the empty root filter,
while the other parent is NULL. These are actually equal. */
- return config_filters_equal(
+ return config_filters_equal_without_defaults(
f1->parent != NULL ? f1->parent : &empty_filter,
f2->parent != NULL ? f2->parent : &empty_filter);
}
return TRUE;
}
+bool config_filters_equal(const struct config_filter *f1,
+ const struct config_filter *f2)
+{
+ if (f1->default_settings != f2->default_settings)
+ return FALSE;
+
+ /* For the rest of the settings don't check if the parents'
+ default_settings are equal. This makes it easier for callers to
+ do lookups with the wanted default_settings flag. */
+ return config_filters_equal_without_defaults(f1, f2);
+}
+
bool config_filter_is_empty(const struct config_filter *filter)
{
return config_filters_equal(filter, &empty_filter);
their filter_names will be merged into one filter path when dumping
the config. */
bool filter_hierarchical;
+
+ /* TRUE if default settings are being accessed. These will be stored in
+ separate filters so they can be ordered before global settings. */
+ bool default_settings;
};
/* Each unique config_filter (including its parents in hierarchy) has its own
old-set-parser. */
struct config_filter filter = {
.parent = &ctx->cur_section->filter_parser->filter,
+ .default_settings = (ctx->change_counter ==
+ CONFIG_PARSER_CHANGE_DEFAULTS),
};
/* find the type of the first prefix/ */
filter.filter_name = t_strdup_until(key_with_path, p);
filter_parser = config_filter_parser_find(ctx, &filter);
if (filter_parser == NULL) {
- if (filter.filter_name_array) {
- /* don't create new arrays */
+ if (filter.filter_name_array &&
+ !filter.default_settings) {
+ /* don't create new arrays, except for
+ default settings */
break;
}
/* Verify that this is a filter_name/ prefix. If not,
it should be a list/ */
if (l->info->defines[config_key->define_idx].type != SET_FILTER_NAME &&
+ l->info->defines[config_key->define_idx].type != SET_FILTER_ARRAY &&
l->info->defines[config_key->define_idx].type != SET_FILTER_HIERARCHY)
break;
void config_fill_set_parser(struct setting_parser_context *parser,
const struct config_module_parser *p)
{
+ if (p->change_counters == NULL)
+ return;
+
for (unsigned int i = 0; p->info->defines[i].key != NULL; i++) {
if (p->change_counters[i] == 0)
continue;
struct event *event,
struct config_filter_parser *filter_parser)
{
- struct config_module_parser *p;
+ struct config_module_parser *p, *default_p, *next_default_p;
const struct config_filter *filter;
const char *error = NULL;
pool_t tmp_pool;
event_add_ip(event, "remote_ip", &filter->remote_net);
}
+ /* Defaults are in a separate filter. Merge them with the non-defaults
+ filter before calling check_func()s. */
+ struct config_filter default_filter = filter_parser->filter;
+ default_filter.default_settings = TRUE;
+ struct config_filter_parser *default_filter_parser =
+ config_filter_parser_find(ctx, &default_filter);
+ default_p = default_filter_parser == NULL ? NULL :
+ default_filter_parser->module_parsers;
+
tmp_pool = pool_alloconly_create(MEMPOOL_GROWING"config parsers check", 1024);
- for (p = filter_parser->module_parsers; p->info != NULL; p++) {
+ for (p = filter_parser->module_parsers; p->info != NULL;
+ p++, default_p = next_default_p) {
+ next_default_p = default_p == NULL ? NULL : default_p + 1;
if (p->settings == NULL)
continue;
+
+ i_assert(default_p == NULL || default_p->info == p->info);
+
p_clear(tmp_pool);
struct setting_parser_context *tmp_parser =
settings_parser_init(tmp_pool, p->info,
settings_parser_flags);
+ if (default_p != NULL)
+ config_fill_set_parser(tmp_parser, default_p);
config_fill_set_parser(tmp_parser, p);
T_BEGIN {
ok = settings_parser_check(tmp_parser, tmp_pool,
the config code, so at least for now it's not done. */
global_ssl_set = get_str_setting(parsers[0], "ssl", "");
for (i = 0; i < count; i++) {
+ if (parsers[i]->filter.default_settings)
+ continue;
ssl_set = get_str_setting(parsers[i], "ssl", global_ssl_set);
if (strcmp(ssl_set, "no") != 0 &&
strcmp(global_ssl_set, "no") == 0 && !ssl_warned) {