]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Support replace and append for boollist settings
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 9 Jan 2024 14:28:58 +0000 (15:28 +0100)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:12 +0000 (12:34 +0200)
src/config/config-dump-full.c
src/config/config-parser.c
src/config/config-parser.h
src/config/config-request.c
src/config/config-request.h
src/config/doveconf.c
src/lib-settings/settings-parser.h
src/lib-settings/settings.c

index 5c84a15b2a5bf6222c0768e73d0e27cba481b814..7b1dd5919296b12b5a24bf4d8d83f15a7ce0779c 100644 (file)
@@ -43,7 +43,7 @@
                               NUL = no error, followed by settings>
        Repeat until "filter settings size" is reached:
          <32bit big-endian: key index number>
-        [<strlist/boollist key>]
+        [+|$ <strlist/boollist key>]
         <NUL-terminated string: value>
      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);
index 9fd4554e9db7554f112f4157b0a334a7b096796c..07c68714eda8d9e9f0e2dc7c02277c73bf0bcdc1 100644 (file)
@@ -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)
index 6f56acc424cbaf4b56267365ce6ff8c9254ab302..295f6b1464eaf2318928a343f24e56a5f0c9eb5b 100644 (file)
@@ -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
index 0dd7b723189451816eca3dcb811e96a28e4611db..81fa839454dc660c36de787ab16042dd6f5e09d0 100644 (file)
@@ -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)
index 8b23243788fe5e029180b5fc4139db034124c2bf..29af027f1a7a601a7d1f70497a38180babc039ff 100644 (file)
@@ -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,
index 079e92056a69cdc14a8b680205a55ddc7bc8d34d..2386b00ac560620fba53963457aaae6a44c82222 100644 (file)
@@ -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;
index 16aee7a3e3de883d462363b35f34d38babb958b9..10e87320e682d3731d2fd10bb12c6c5a18dc8035 100644 (file)
@@ -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 {
index a540283f4e5a3cacce97dfc07ac2f939c2995ed5..77e47bf2df0da3eee1ac7432fc990f762bd59ff3 100644 (file)
@@ -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;