From: Timo Sirainen Date: Tue, 9 Jan 2024 14:28:58 +0000 (+0100) Subject: config: Support replace and append for boollist settings X-Git-Tag: 2.4.1~1036 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b2e6557fac54419a5f88a80825aebe16e9ce7ab1;p=thirdparty%2Fdovecot%2Fcore.git config: Support replace and append for boollist settings --- diff --git a/src/config/config-dump-full.c b/src/config/config-dump-full.c index 5c84a15b2a..7b1dd59192 100644 --- a/src/config/config-dump-full.c +++ b/src/config/config-dump-full.c @@ -43,7 +43,7 @@ NUL = no error, followed by settings> Repeat until "filter settings size" is reached: <32bit big-endian: key index number> - [] + [+|$ ] Repeat for "filter count": <32bit big-endian: event filter string index number> @@ -203,7 +203,12 @@ static void config_dump_full_stdout_callback(const struct config_export_setting *set, struct dump_context *ctx) { - if (set->type == CONFIG_KEY_LIST) { + if (set->type != CONFIG_KEY_LIST) + ; + else if (set->list_count == 0 && set->value_stop_list && + set->def_type == SET_BOOLLIST) { + /* filter empties a boollist setting */ + } else { /* these aren't needed */ return; } @@ -222,20 +227,27 @@ config_dump_full_stdout_callback(const struct config_export_setting *set, &ctx->info->defines[set->key_define_idx]; if (def->type == SET_STRLIST || def->type == SET_BOOLLIST) { const char *suffix; - if (!str_begins(set->key, def->key, &suffix) || - suffix[0] != '/') + if (!str_begins(set->key, def->key, &suffix)) i_unreached(); - - suffix++; - o_stream_nsend_str(ctx->output, - t_strdup_until(set->key, suffix)); - o_stream_nsend_str(ctx->output, - settings_section_escape(suffix)); + else if (suffix[0] == '/') { + suffix++; + o_stream_nsend_str(ctx->output, + t_strdup_until(set->key, suffix)); + o_stream_nsend_str(ctx->output, + settings_section_escape(suffix)); + } else { + /* emptying boollist */ + i_assert(suffix[0] == '\0'); + i_assert(set->type == CONFIG_KEY_LIST); + o_stream_nsend_str(ctx->output, set->key); + } } else { o_stream_nsend_str(ctx->output, set->key); } o_stream_nsend_str(ctx->output, "="); o_stream_nsend_str(ctx->output, str_tabescape(set->value)); + if (set->value_stop_list) + o_stream_nsend_str(ctx->output, " # stop boollist"); o_stream_nsend_str(ctx->output, "\n"); } T_END; } @@ -245,7 +257,12 @@ static void config_dump_full_callback(const struct config_export_setting *set, { const char *suffix; - if (set->type == CONFIG_KEY_LIST) { + if (set->type != CONFIG_KEY_LIST) + ; + else if (set->list_count == 0 && set->value_stop_list && + set->def_type == SET_BOOLLIST) { + /* filter empties a boollist setting */ + } else { /* these aren't needed */ return; } @@ -275,13 +292,22 @@ static void config_dump_full_callback(const struct config_export_setting *set, &ctx->info->defines[set->key_define_idx]; if (def->type == SET_STRLIST || def->type == SET_BOOLLIST) { const char *suffix; - if (!str_begins(set->key, def->key, &suffix) || - suffix[0] != '/') + if (!str_begins(set->key, def->key, &suffix)) i_unreached(); - else { + else if (suffix[0] == '/') { suffix = settings_section_escape(suffix + 1); + o_stream_nsend(ctx->output, + set->value_stop_list ? + SET_BOOLLIST_REPLACE : + SET_BOOLLIST_APPEND, 1); o_stream_nsend(ctx->output, suffix, strlen(suffix) + 1); + } else { + /* emptying boollist */ + i_assert(suffix[0] == '\0'); + i_assert(set->type == CONFIG_KEY_LIST); + o_stream_nsend(ctx->output, + SET_BOOLLIST_CLEAR, 1 + 1); } } o_stream_nsend(ctx->output, set->value, strlen(set->value)+1); diff --git a/src/config/config-parser.c b/src/config/config-parser.c index 9fd4554e9d..07c68714ed 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -516,7 +516,8 @@ static int config_apply_strlist(struct config_parser_context *ctx, static int config_apply_boollist(struct config_parser_context *ctx, const char *key, const char *value, - ARRAY_TYPE(const_string) **strlistp) + ARRAY_TYPE(const_string) **strlistp, + bool *stop_list) { ARRAY_TYPE(const_string) boollist; const char *error; @@ -528,6 +529,9 @@ static int config_apply_boollist(struct config_parser_context *ctx, ctx->error = p_strdup(ctx->pool, error); return -1; } + /* Preserve stop_list's original value. We may be updating a + list within the same filter, and the previous setting might + have wanted to stop the list already. */ return config_apply_strlist(ctx, key, value, strlistp); } @@ -549,6 +553,7 @@ static int config_apply_boollist(struct config_parser_context *ctx, array_push_back(*strlistp, &key); array_push_back(*strlistp, &yes); } + *stop_list = TRUE; return 0; } @@ -635,12 +640,13 @@ config_apply_exact_line(struct config_parser_context *ctx, switch (l->info->defines[config_key->define_idx].type) { case SET_STRLIST: if (config_apply_strlist(ctx, key, value, - &l->settings[config_key->define_idx].array) < 0) + &l->settings[config_key->define_idx].array.values) < 0) return -1; break; case SET_BOOLLIST: if (config_apply_boollist(ctx, key, value, - &l->settings[config_key->define_idx].array) < 0) + &l->settings[config_key->define_idx].array.values, + &l->settings[config_key->define_idx].array.stop_list) < 0) return -1; break; case SET_FILTER_ARRAY: @@ -652,7 +658,7 @@ config_apply_exact_line(struct config_parser_context *ctx, return -1; } if (config_apply_filter_array(ctx, line, value, - &l->settings[config_key->define_idx].array) < 0) + &l->settings[config_key->define_idx].array.values) < 0) return -1; break; case SET_FILE: { @@ -1166,7 +1172,7 @@ void config_fill_set_parser(struct setting_parser_context *parser, case SET_BOOLLIST: { unsigned int j, count; const char *const *strings = - array_get(p->settings[i].array, &count); + array_get(p->settings[i].array.values, &count); for (j = 0; j < count; j += 2) T_BEGIN { const char *key = t_strdup_printf("%s/%s", p->info->defines[i].key, @@ -1178,7 +1184,7 @@ void config_fill_set_parser(struct setting_parser_context *parser, } case SET_FILTER_ARRAY: { const char *name; - array_foreach_elem(p->settings[i].array, name) T_BEGIN { + array_foreach_elem(p->settings[i].array.values, name) T_BEGIN { (void)settings_parse_keyidx_value(parser, i, p->info->defines[i].key, settings_section_escape(name)); @@ -2329,11 +2335,11 @@ config_module_parsers_get_setting(const struct config_module_parser *module_pars master_service_get_import_environment_keyvals() for the original implementation. */ if (strcmp(key, "import_environment") == 0) { - unsigned int len = array_count(l->settings[key_idx].array); + unsigned int len = array_count(l->settings[key_idx].array.values); string_t *keyvals = t_str_new(64); for (unsigned int i = 0; i < len; i += 2) { - const char *const *key = array_idx(l->settings[key_idx].array, i); - const char *const *val = array_idx(l->settings[key_idx].array, i + 1); + const char *const *key = array_idx(l->settings[key_idx].array.values, i); + const char *const *val = array_idx(l->settings[key_idx].array.values, i + 1); str_append(keyvals, t_strdup_printf("%s=%s", *key, *val)); if (i + 2 < len) diff --git a/src/config/config-parser.h b/src/config/config-parser.h index 6f56acc424..295f6b1464 100644 --- a/src/config/config-parser.h +++ b/src/config/config-parser.h @@ -44,7 +44,10 @@ struct config_module_parser { unsigned int set_count; union config_module_parser_setting { const char *str; - ARRAY_TYPE(const_string) *array; + struct { + ARRAY_TYPE(const_string) *values; + bool stop_list; + } array; } *settings; /* [set_count] */ uint8_t *change_counters; /* [set_count] */ /* Set if CONFIG_PARSE_FLAG_DELAY_ERRORS is enabled. The error won't diff --git a/src/config/config-request.c b/src/config/config-request.c index 0dd7b72318..81fa839454 100644 --- a/src/config/config-request.c +++ b/src/config/config-request.c @@ -201,6 +201,7 @@ settings_export(struct config_export_context *ctx, break; } + bool value_stop_list = FALSE; dump = FALSE; str_truncate(ctx->value, 0); switch (def->type) { @@ -254,9 +255,10 @@ settings_export(struct config_export_context *ctx, case SET_STRLIST: case SET_BOOLLIST: { const ARRAY_TYPE(const_string) *val = - module_parser->settings[define_idx].array; + module_parser->settings[define_idx].array.values; const char *const *strings; + value_stop_list = module_parser->settings[define_idx].array.stop_list; if (hash_table_is_created(ctx->keys) && hash_table_lookup(ctx->keys, def->key) != NULL) { /* already added all of these */ @@ -281,6 +283,7 @@ settings_export(struct config_export_context *ctx, .key_define_idx = define_idx, .value = "", .list_count = count / 2, + .value_stop_list = value_stop_list, }; ctx->callback(&export_set, ctx->context); @@ -293,13 +296,21 @@ settings_export(struct config_export_context *ctx, strings[i]); export_set.list_idx = i / 2; export_set.value = strings[i+1]; - ctx->callback(&export_set, ctx->context); + /* only the last element stops the list */ + export_set.value_stop_list = value_stop_list && + i + 2 == count; + if (def->type == SET_BOOLLIST && + strcmp(export_set.value, "no") == 0 && + value_stop_list) + ; /* ignore */ + else + ctx->callback(&export_set, ctx->context); } T_END; break; } case SET_FILTER_ARRAY: { const ARRAY_TYPE(const_string) *val = - module_parser->settings[define_idx].array; + module_parser->settings[define_idx].array.values; const char *name; if (val == NULL) diff --git a/src/config/config-request.h b/src/config/config-request.h index 8b23243788..29af027f1a 100644 --- a/src/config/config-request.h +++ b/src/config/config-request.h @@ -42,6 +42,7 @@ struct config_export_setting { unsigned int list_idx; unsigned int list_count; const char *value; + bool value_stop_list; }; typedef void config_request_callback_t(const struct config_export_setting *set, diff --git a/src/config/doveconf.c b/src/config/doveconf.c index 079e92056a..2386b00ac5 100644 --- a/src/config/doveconf.c +++ b/src/config/doveconf.c @@ -92,6 +92,10 @@ config_request_get_strings(const struct config_export_setting *set, value = p_strdup_printf(ctx->pool, "%s=", set->key); break; } + if (set->value_stop_list && set->def_type == SET_BOOLLIST) { + value = p_strdup_printf(ctx->pool, "%s=", set->key); + array_push_back(&ctx->strings, &value); + } value = p_strdup_printf(ctx->pool, LIST_KEY_PREFIX"%s=%s", set->key, set->value); break; diff --git a/src/lib-settings/settings-parser.h b/src/lib-settings/settings-parser.h index 16aee7a3e3..10e87320e6 100644 --- a/src/lib-settings/settings-parser.h +++ b/src/lib-settings/settings-parser.h @@ -19,6 +19,10 @@ struct var_expand_func_table; #define SET_TIME_INFINITE UINT_MAX #define SET_TIME_MSECS_INFINITE UINT_MAX +#define SET_BOOLLIST_APPEND "+" +#define SET_BOOLLIST_REPLACE "$" +#define SET_BOOLLIST_CLEAR "." + #define SET_FILE_INLINE_PREFIX "inline:" enum setting_type { diff --git a/src/lib-settings/settings.c b/src/lib-settings/settings.c index a540283f4e..77e47bf2df 100644 --- a/src/lib-settings/settings.c +++ b/src/lib-settings/settings.c @@ -659,6 +659,25 @@ settings_mmap_apply_blob(struct settings_apply_ctx *ctx, whole list is replaced. */ set_apply = FALSE; } + bool list_stop = FALSE, list_clear = FALSE; + if (ctx->info->defines[key_idx].type == SET_BOOLLIST || + ctx->info->defines[key_idx].type == SET_STRLIST) { + const char *type = + (const char *)mmap->mmap_base + offset; + if (type[0] == SET_BOOLLIST_REPLACE[0]) + list_stop = TRUE; + else if (type[0] == SET_BOOLLIST_CLEAR[0]) { + list_stop = TRUE; + list_clear = TRUE; + } else if (type[0] != SET_BOOLLIST_APPEND[0]) { + *error_r = t_strdup_printf( + "List type is invalid (offset=%zu)", + offset); + return -1; + } + offset++; + } + if (ctx->info->defines[key_idx].type == SET_STRLIST || ctx->info->defines[key_idx].type == SET_BOOLLIST) { list_key = (const char *)mmap->mmap_base + offset; @@ -692,8 +711,18 @@ settings_mmap_apply_blob(struct settings_apply_ctx *ctx, if (!set_apply) ret = 0; else T_BEGIN { - ret = settings_mmap_apply_key(ctx, key_idx, list_key, - value, error_r); + if (list_clear) + ret = 0; + else { + ret = settings_mmap_apply_key(ctx, key_idx, + list_key, value, error_r); + } + if (list_stop) { + /* The lists are filled in reverse order. + Mark it now as "stopped", so the following + list settings won't modify it. */ + settings_parse_array_stop(ctx->parser, key_idx); + } } T_END_PASS_STR_IF(ret < 0, error_r); if (ret < 0) return -1;