]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-settings, lib-storage: Support replacing the whole named list filter
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Tue, 2 Jan 2024 21:09:17 +0000 (16:09 -0500)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:11 +0000 (12:34 +0200)
Using list_filter=value now replaces the filter, while list_filter+=value
appends to it (which is the old behavior without "+").

For example -o namespace+=foo,foo2 adds two new namespaces, but
-o namespace=foo,foo2 only keeps the two namespaces.

src/lib-settings/settings-parser.c
src/lib-settings/settings-parser.h
src/lib-settings/settings.c
src/lib-storage/test-mail-storage-common.c
src/lib-storage/test-mail-storage.c

index 928483099f045307c813276c0383ae0f57f4186e..44c7143ae161bf5a1851957f6f83d5ebfdbe107a 100644 (file)
@@ -30,6 +30,7 @@ const char *set_value_unknown = "UNKNOWN_VALUE_WITH_VARIABLES";
 #ifdef DEBUG
 static const char *boollist_eol_sentry = "boollist-eol";
 #endif
+static const char *set_array_stop = "array-stop";
 
 static void
 setting_parser_copy_defaults(struct setting_parser_context *ctx,
@@ -536,13 +537,23 @@ settings_parse(struct setting_parser_context *ctx,
                break;
        case SET_FILTER_ARRAY: {
                /* Add filter names to the array. Userdb can add more simply
-                  by giving e.g. "namespace=newname" without it removing the
+                  by giving e.g. "namespace+=newname" without it removing the
                   existing ones. */
                ARRAY_TYPE(const_string) *arr = ptr;
                const char *const *list = t_strsplit(value, ",\t ");
                unsigned int i, count = str_array_length(list);
                if (!array_is_created(arr))
                        p_array_init(arr, ctx->set_pool, count);
+               else {
+                       /* If the next element after the visible array is
+                          set_array_stop, then the named list filter
+                          should not be modified any further. */
+                       unsigned int old_count;
+                       const char *const *old_values =
+                               array_get(arr, &old_count);
+                       if (old_values[old_count] == set_array_stop)
+                               break;
+               }
                unsigned int insert_pos = 0;
                for (i = 0; i < count; i++) {
                        const char *value = p_strdup(ctx->set_pool,
@@ -552,6 +563,10 @@ settings_parse(struct setting_parser_context *ctx,
                        else
                                array_push_back(arr, &value);
                }
+               /* Make sure the next element after the array is accessible for
+                  the set_array_stop check. */
+               array_append_zero(arr);
+               array_pop_back(arr);
                break;
        }
        case SET_FILTER_NAME:
@@ -641,6 +656,20 @@ int settings_parse_keyidx_value_nodup(struct setting_parser_context *ctx,
                              key, value, FALSE);
 }
 
+void settings_parse_array_stop(struct setting_parser_context *ctx,
+                              unsigned int key_idx)
+{
+       i_assert(ctx->info->defines[key_idx].type == SET_FILTER_ARRAY);
+
+       ARRAY_TYPE(const_string) *arr =
+               PTR_OFFSET(ctx->set_struct, ctx->info->defines[key_idx].offset);
+       if (!array_is_created(arr))
+               p_array_init(arr, ctx->set_pool, 1);
+       /* Use the next element hidden after the array to keep the stop-state */
+       array_push_back(arr, &set_array_stop);
+       array_pop_back(arr);
+}
+
 static int boollist_removal_cmp(const struct boollist_removal *r1,
                                const struct boollist_removal *r2)
 {
index c1e830783f8f9fe9f89fc41cb3235f1e2376346f..2ac181565deffc9c0703837643a60ad658d6144a 100644 (file)
@@ -205,6 +205,9 @@ int settings_parse_keyvalue_nodup(struct setting_parser_context *ctx,
 int settings_parse_keyidx_value_nodup(struct setting_parser_context *ctx,
                                      unsigned int key_idx, const char *key,
                                      const char *value);
+/* Ignore any further attempts to add to the named list filter. */
+void settings_parse_array_stop(struct setting_parser_context *ctx,
+                              unsigned int key_idx);
 /* Returns TRUE if list has the specific key. The key must NOT include the
    list/ prefix. */
 bool settings_parse_list_has_key(struct setting_parser_context *ctx,
index 4580967f2c525418ef77c359c05591b9f7a5fd28..48bb59445218902df628fa28b916bd0c7b135a33 100644 (file)
@@ -1292,7 +1292,8 @@ settings_override_get_value(struct settings_apply_ctx *ctx,
        else
                key = t_strconcat(ctx->info->defines[key_idx].key, list, NULL);
 
-       if (!set->append) {
+       if (!set->append ||
+           ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) {
                *_key = key;
                *key_idx_r = key_idx;
                *value_r = set->value;
@@ -1301,7 +1302,7 @@ settings_override_get_value(struct settings_apply_ctx *ctx,
 
        if (ctx->info->defines[key_idx].type != SET_STR) {
                *error_r = t_strdup_printf(
-                       "%s setting is not a string - can't use '+'", key);
+                       "%s setting is not a string or named list filter - can't use '+'", key);
                return -1;
        }
        const char *const *strp =
@@ -1541,6 +1542,14 @@ settings_instance_override(struct settings_apply_ctx *ctx,
                                key, value, settings_parser_get_error(ctx->parser));
                        return -1;
                }
+
+               if (!set->append &&
+                   ctx->info->defines[key_idx].type == SET_FILTER_ARRAY) {
+                       /* The named list filter is filled in reverse order.
+                          Mark it now as "stopped", so following filters won't
+                          try to insert anything more into it. */
+                       settings_parse_array_stop(ctx->parser, key_idx);
+               }
        }
        return seen_filter ? 1 : 0;
 }
@@ -1812,15 +1821,12 @@ settings_get_or_fatal(struct event *event,
        return set;
 }
 
-void settings_override(struct settings_instance *instance,
+static void
+settings_override_fill(struct settings_override *set, pool_t pool,
                       const char *key, const char *value,
                       enum settings_override_type type)
 {
-       if (!array_is_created(&instance->overrides))
-               p_array_init(&instance->overrides, instance->pool, 16);
-       struct settings_override *set =
-               array_append_space(&instance->overrides);
-       set->pool = instance->pool;
+       set->pool = pool;
        set->type = type;
        size_t len = strlen(key);
        T_BEGIN {
@@ -1829,12 +1835,23 @@ void settings_override(struct settings_instance *instance,
                        set->append = TRUE;
                        key = t_strndup(key, len-1);
                }
-               set->key = set->orig_key = p_strdup(instance->pool, key);
+               set->key = set->orig_key = p_strdup(pool, key);
                set->path_element_count = path_element_count(set->key);
-               set->value = p_strdup(instance->pool, value);
+               set->value = p_strdup(pool, value);
        } T_END;
 }
 
+void settings_override(struct settings_instance *instance,
+                      const char *key, const char *value,
+                      enum settings_override_type type)
+{
+       if (!array_is_created(&instance->overrides))
+               p_array_init(&instance->overrides, instance->pool, 16);
+       struct settings_override *set =
+               array_append_space(&instance->overrides);
+       settings_override_fill(set, instance->pool, key, value, type);
+}
+
 void settings_root_override(struct settings_root *root,
                            const char *key, const char *value,
                            enum settings_override_type type)
@@ -1843,11 +1860,7 @@ void settings_root_override(struct settings_root *root,
                p_array_init(&root->overrides, root->pool, 16);
        struct settings_override *set =
                array_append_space(&root->overrides);
-       set->pool = root->pool;
-       set->type = type;
-       set->key = set->orig_key = p_strdup(root->pool, key);
-       set->path_element_count = path_element_count(set->key);
-       set->value = p_strdup(root->pool, value);
+       settings_override_fill(set, root->pool, key, value, type);
 }
 
 static struct settings_instance *
index 4bb130b2edfe44746ffcac9ded6f848a50c21f44..e5dc928398073fcc5f7f03aae7c79474ea082027 100644 (file)
@@ -72,7 +72,7 @@ void test_mail_storage_init_user(struct test_mail_storage_ctx *ctx,
        const char *const default_input[] = {
                t_strdup_printf("mail_driver=%s", set->driver),
                "postmaster_address=postmaster@localhost",
-               "namespace=inbox",
+               "namespace+=inbox",
                "namespace/inbox/prefix=",
                "namespace/inbox/inbox=yes",
                t_strdup_printf("mail_path=%s", home),
index d6eb53e003c00022d41bcfaaabbdb9886d3ed655..25fd192840ebf2b5305793b0197b5ba269efce15 100644 (file)
@@ -411,7 +411,7 @@ test_mailbox_verify_name_driver_slash(const char *driver,
                mailbox_list_layout[0] == '\0' ? NULL :
                t_strdup_printf("mailbox_list_layout=%s", mailbox_list_layout);
        const char *const ns2[] = {
-               "namespace=subspace",
+               "namespace+=subspace",
                "namespace/subspace/separator=/",
                "namespace/subspace/prefix=SubSpace/",
                layout_option,
@@ -441,7 +441,7 @@ test_mailbox_verify_name_driver_dot(const char *driver,
                mailbox_list_layout[0] == '\0' ? NULL :
                t_strdup_printf("mailbox_list_layout=%s", mailbox_list_layout);
        const char *const ns2[] = {
-               "namespace=subspace",
+               "namespace+=subspace",
                "namespace/subspace/separator=.",
                "namespace/subspace/prefix=SubSpace.",
                layout_option,
@@ -555,7 +555,7 @@ static void test_mailbox_list_maildir_init(struct test_mail_storage_ctx *ctx,
                mailbox_list_layout[0] == '\0' ? NULL :
                t_strdup_printf("mailbox_list_layout=%s", mailbox_list_layout);
        const char *const ns2[] = {
-               "namespace=subspace",
+               "namespace+=subspace",
                t_strdup_printf("namespace/subspace/separator=%s", sep),
                t_strdup_printf("namespace/subspace/prefix=SubSpace%s", sep),
                layout_option,