]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-settings, config: Override settings now ignore named (non-list) filters
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 15 Dec 2023 23:00:48 +0000 (01:00 +0200)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:11 +0000 (12:34 +0200)
For example if configuration has:

sdbox {
  mail_path = ~/sdbox
}

Then userdb or CLI -o parameter can override mail_path without having to
specify sdbox/ prefix. This makes the behavior easier to understand and
change, especially when overriding the default settings which are inside
named filters.

This does not apply to named list filters, e.g. if you have
namespace/inbox/mail_path set, it won't be overridden with just changing
mail_path.

src/config/config-dump-full.c
src/lib-master/test-master-service-settings.c
src/lib-settings/settings.c

index 419385d5a71bab9877ea4f54d8d2bdea81706db0..e891541965b77e42889a7fdbf7ec1dc09ae801a7 100644 (file)
@@ -24,7 +24,9 @@
    <64bit big-endian: settings full size>
 
    <32bit big-endian: event filter strings count>
-   <NUL-terminated string: event filter string>[count]
+   Repeat for "event filter strings count":
+     <NUL-terminated string: event filter string>
+     <NUL-terminated string: override event filter string>
 
    Repeat until "settings full size" is reached:
      <64bit big-endian: settings block size>
@@ -98,7 +100,8 @@ config_filter_write_hierarchical(string_t *str,
 static void
 config_dump_full_append_filter_query(string_t *str,
                                     const struct config_filter *filter,
-                                    bool leaf, bool parent_hierarchical)
+                                    bool leaf, bool parent_hierarchical,
+                                    bool write_named_filters)
 {
        if (filter->service != NULL) {
                if (filter->service[0] != '!') {
@@ -158,27 +161,32 @@ config_dump_full_append_filter_query(string_t *str,
                        return;
                }
 
-               str_printfa(str, SETTINGS_EVENT_FILTER_NAME"=\"%s\" AND ",
-                           wildcard_str_escape(filter_name));
+               if (write_named_filters || filter->filter_hierarchical) {
+                       str_printfa(str, SETTINGS_EVENT_FILTER_NAME"=\"%s\" AND ",
+                                   wildcard_str_escape(filter_name));
+               }
        }
 }
 
 static void
 config_dump_full_append_filter(string_t *str,
-                              const struct config_filter *filter)
+                              const struct config_filter *filter,
+                              bool write_named_filters)
 {
        bool leaf = TRUE;
        bool parent_hierarchical = FALSE;
 
        do {
                config_dump_full_append_filter_query(str, filter, leaf,
-                                                    parent_hierarchical);
+                                                    parent_hierarchical,
+                                                    write_named_filters);
                leaf = FALSE;
                parent_hierarchical = filter->filter_hierarchical;
                filter = filter->parent;
        } while (filter != NULL);
 
-       str_truncate(str, str_len(str) - 5);
+       if (str_len(str) > 0)
+               str_truncate(str, str_len(str) - 5);
 }
 
 static void
@@ -196,10 +204,16 @@ config_dump_full_write_filters(struct ostream *output,
 
        /* the first filter is the global empty filter */
        o_stream_nsend(output, "", 1);
+       o_stream_nsend(output, "", 1);
        string_t *str = str_new(default_pool, 128);
        for (i = 1; i < filter_count; i++) T_BEGIN {
                str_truncate(str, 0);
-               config_dump_full_append_filter(str, &filters[i]->filter);
+               config_dump_full_append_filter(str, &filters[i]->filter, TRUE);
+               str_append_c(str, '\0');
+               o_stream_nsend(output, str_data(str), str_len(str));
+
+               str_truncate(str, 0);
+               config_dump_full_append_filter(str, &filters[i]->filter, FALSE);
                str_append_c(str, '\0');
                o_stream_nsend(output, str_data(str), str_len(str));
        } T_END;
@@ -233,7 +247,7 @@ config_dump_full_stdout_callback(const struct config_export_setting *set,
        if (!ctx->filter_written) {
                string_t *str = t_str_new(128);
                str_append(str, ":FILTER ");
-               config_dump_full_append_filter(str, ctx->filter);
+               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;
index 06bd3f582490f73bbf24e563b5fdfad197e671aa..0f05fc904e3ae7528862d66686671440af3aa6c0 100644 (file)
@@ -44,30 +44,34 @@ static const struct {
        /* full file size is 7 bytes, which makes the first block size
           truncated, since it needs 8 bytes */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x0C" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x0D" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00"), // block size
          "Area too small when reading size of 'block size'" },
        /* first block size is 0, which is too small */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x0D" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x0E" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x00"), // block size
          "'block name' points outside area" },
        /* first block size is 1, but full file size is too small */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x0D" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x0E" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x01"), // block size
          "'block size' points outside are" },
        /* block name is not NUL-terminated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x0F" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x10" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x01" // block size
               "N"
               "\x00"), // trailing garbage so we can have NUL
@@ -75,9 +79,10 @@ static const struct {
 
        /* settings count is truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x12" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x13" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x05" // block size
               "N\x00" // block name
               "\x00\x00\x00"),
@@ -85,9 +90,10 @@ static const struct {
 
        /* settings keys are truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x13" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x14" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x06" // block size
               "N\x00" // block name
               "\x00\x00\x01\x00"), // settings count
@@ -95,9 +101,10 @@ static const struct {
 
        /* base settings size is truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x1C" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x1D" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x0F" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -106,9 +113,10 @@ static const struct {
          "Area too small when reading size of 'base settings size'" },
        /* base settings size is zero */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x1D" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x1E" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x10" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -117,9 +125,10 @@ static const struct {
          "'base settings error' points outside area" },
        /* base settings error is not NUL-terminated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x1F" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x20" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x12" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -131,9 +140,10 @@ static const struct {
 
        /* filter count is truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x21" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x22" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x14" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -145,9 +155,10 @@ static const struct {
 
        /* filter settings size is truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x29" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x2A" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x1B" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -160,9 +171,10 @@ static const struct {
 
        /* filter settings is truncated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x2A" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x2B" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x1D" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -174,9 +186,10 @@ static const struct {
          "'filter settings size' points outside area" },
        /* filter error is missing */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x37" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x38" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x2A" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -191,9 +204,10 @@ static const struct {
          "'filter error string' points outside area" },
        /* filter error is not NUL-terminated */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x45" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x46" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x38" // block size
               "master_service\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -209,9 +223,10 @@ static const struct {
          "'filter error string' points outside area" },
        /* invalid filter string */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x39" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x3B" // full size
               "\x00\x00\x00\x01" // event filter count
               "F\x00" // event filter[0]
+              "F\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x2B" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
@@ -228,9 +243,10 @@ static const struct {
 
        /* Duplicate block name */
        { DATA("DOVECOT-CONFIG\t1.0\n"
-              "\x00\x00\x00\x00\x00\x00\x00\x42" // full size
+              "\x00\x00\x00\x00\x00\x00\x00\x43" // full size
               "\x00\x00\x00\x01" // event filter count
               "\x00" // event filter[0]
+              "\x00" // override event filter[0]
               "\x00\x00\x00\x00\x00\x00\x00\x2B" // block size
               "N\x00" // block name
               "\x00\x00\x00\x01" // settings count
index 0c6ba85706b064da2b3bafc846712b636d6008a2..e61c921ba994fb3b0b11d1c70acaffe996c1aa87 100644 (file)
@@ -103,6 +103,7 @@ struct settings_mmap {
        size_t mmap_size;
 
        struct event_filter **event_filters;
+       struct event_filter **override_event_filters;
        unsigned int event_filters_count;
 
        HASH_TABLE(const char *, struct settings_mmap_block *) blocks;
@@ -252,14 +253,19 @@ settings_read_filters(struct settings_mmap *mmap, const char *service_name,
 
        mmap->event_filters = mmap->event_filters_count == 0 ? NULL :
                p_new(mmap->pool, struct event_filter *, mmap->event_filters_count);
+       mmap->override_event_filters = mmap->event_filters_count == 0 ? NULL :
+               p_new(mmap->pool, struct event_filter *, mmap->event_filters_count);
 
-       for (uint32_t i = 0; i < mmap->event_filters_count; i++) {
+       for (uint32_t i = 0; i < 2 * mmap->event_filters_count; i++) {
+               struct event_filter **filter_dest =
+                       i % 2 == 0 ? &mmap->event_filters[i / 2] :
+                       &mmap->override_event_filters[i / 2];
                if (settings_block_read_str(mmap, offset, mmap->mmap_size,
                                            "filter string", &filter_string,
                                            error_r) < 0)
                        return -1;
                if (filter_string[0] == '\0') {
-                       mmap->event_filters[i] = EVENT_FILTER_MATCH_ALWAYS;
+                       *filter_dest = EVENT_FILTER_MATCH_ALWAYS;
                        continue;
                }
 
@@ -287,7 +293,7 @@ settings_read_filters(struct settings_mmap *mmap, const char *service_name,
                            (strcmp(mmap->root->protocol_name, value) == 0) == op_not &&
                            (flags & SETTINGS_READ_NO_PROTOCOL_FILTER) == 0) {
                                /* protocol doesn't match */
-                               mmap->event_filters[i] = EVENT_FILTER_MATCH_NEVER;
+                               *filter_dest = EVENT_FILTER_MATCH_NEVER;
                                event_filter_unref(&tmp_filter);
                                continue;
                        }
@@ -296,14 +302,14 @@ settings_read_filters(struct settings_mmap *mmap, const char *service_name,
                if (value != NULL && service_name != NULL &&
                    (strcmp(value, service_name) == 0) == op_not) {
                        /* service name doesn't match */
-                       mmap->event_filters[i] = EVENT_FILTER_MATCH_NEVER;
+                       *filter_dest = EVENT_FILTER_MATCH_NEVER;
                        event_filter_unref(&tmp_filter);
                        continue;
                }
 
-               mmap->event_filters[i] = event_filter_create_with_pool(mmap->pool);
+               *filter_dest = event_filter_create_with_pool(mmap->pool);
                pool_ref(mmap->pool);
-               event_filter_merge(mmap->event_filters[i], tmp_filter,
+               event_filter_merge(*filter_dest, tmp_filter,
                                   EVENT_FILTER_MERGE_OP_OR);
                event_filter_unref(&tmp_filter);
        }
@@ -821,7 +827,8 @@ settings_mmap_apply(struct settings_apply_ctx *ctx, const char **error_r)
 
                        if (event_filter != EVENT_FILTER_MATCH_ALWAYS) {
                                int ret = settings_instance_override(ctx,
-                                               event_filter, error_r);
+                                               mmap->override_event_filters[event_filter_idx],
+                                               error_r);
                                if (ret < 0)
                                        return -1;
                                if (ret > 0)
@@ -876,6 +883,9 @@ static void settings_mmap_unref(struct settings_mmap **_mmap)
                if (mmap->event_filters[i] != EVENT_FILTER_MATCH_ALWAYS &&
                    mmap->event_filters[i] != EVENT_FILTER_MATCH_NEVER)
                        event_filter_unref(&mmap->event_filters[i]);
+               if (mmap->override_event_filters[i] != EVENT_FILTER_MATCH_ALWAYS &&
+                   mmap->override_event_filters[i] != EVENT_FILTER_MATCH_NEVER)
+                       event_filter_unref(&mmap->override_event_filters[i]);
        }
        hash_table_destroy(&mmap->blocks);
 
@@ -1448,6 +1458,7 @@ settings_instance_override(struct settings_apply_ctx *ctx,
                   the overrides that have a matching filter. This preserves
                   the expected order in which settings are applied. */
                if (event_filter != NULL && !set->always_match &&
+                   event_filter != EVENT_FILTER_MATCH_ALWAYS &&
                    (set->filter_event == NULL ||
                     !event_filter_match(event_filter, set->filter_event,
                                         &failure_ctx)))