]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-settings, config: Add support for SET_FILTER_HIERARCHY
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Fri, 28 Jul 2023 12:36:22 +0000 (15:36 +0300)
committerAki Tuomi <aki.tuomi@open-xchange.com>
Wed, 12 Feb 2025 10:34:09 +0000 (12:34 +0200)
src/config/config-dump-full.c
src/config/config-filter.h
src/config/config-parser.c
src/config/config-request.c
src/lib-settings/settings-parser.c
src/lib-settings/settings-parser.h
src/lib-settings/settings.c

index 96648320430e2de682ffeae72583027957ed5be7..7b307dd690743c917fd274ceecf8832341aafbfd 100644 (file)
@@ -83,10 +83,22 @@ static int output_blob_size(struct ostream *output, uoff_t blob_size_offset)
        return 0;
 }
 
+static void
+config_filter_write_hierarchical(string_t *str,
+                                const struct config_filter *filter)
+{
+       if (filter == NULL || !filter->filter_hierarchical)
+               return;
+       if (str_len(str) > 0)
+               str_append_c(str, '/');
+       str_append(str, filter->filter_name);
+       config_filter_write_hierarchical(str, filter->parent);
+}
+
 static void
 config_dump_full_append_filter_query(string_t *str,
                                     const struct config_filter *filter,
-                                    bool leaf)
+                                    bool leaf, bool parent_hierarchical)
 {
        if (filter->service != NULL) {
                if (filter->service[0] != '!') {
@@ -131,8 +143,23 @@ config_dump_full_append_filter_query(string_t *str,
                }
                str_append(str, " AND ");
        } else if (filter->filter_name != NULL) {
+               const char *filter_name = filter->filter_name;
+
+               if (!parent_hierarchical &&
+                   filter->filter_hierarchical &&
+                   filter->parent->filter_hierarchical) {
+                       /* beginning of a hierarchical filter. */
+                       string_t *str = t_str_new(128);
+                       config_filter_write_hierarchical(str, filter);
+                       filter_name = str_c(str);
+               } else if (parent_hierarchical &&
+                          filter->filter_hierarchical) {
+                       /* hierarchical filter was already written. */
+                       return;
+               }
+
                str_printfa(str, SETTINGS_EVENT_FILTER_NAME"=\"%s\" AND ",
-                           wildcard_str_escape(filter->filter_name));
+                           wildcard_str_escape(filter_name));
        }
 }
 
@@ -141,10 +168,13 @@ config_dump_full_append_filter(string_t *str,
                               const struct config_filter *filter)
 {
        bool leaf = TRUE;
+       bool parent_hierarchical = FALSE;
 
        do {
-               config_dump_full_append_filter_query(str, filter, leaf);
+               config_dump_full_append_filter_query(str, filter, leaf,
+                                                    parent_hierarchical);
                leaf = FALSE;
+               parent_hierarchical = filter->filter_hierarchical;
                filter = filter->parent;
        } while (filter != NULL);
 
index c63806ac5e680061e9663f41c614afc66a140f5f..04b0dbb1d19ce6027280550f3dbe67ec239c15b6 100644 (file)
@@ -18,6 +18,10 @@ struct config_filter {
        /* named_filter { .. } */
        const char *filter_name;
        bool filter_name_array;
+       /* This filter is hierarchical. If a child event is also hierarchical,
+          their filter_names will be merged into one filter path when dumping
+          the config. */
+       bool filter_hierarchical;
 };
 
 struct config_filter_parser {
index dfacd8e8936166a931e97254b1f25fc972994df4..f1d176e16776bcc6563ea5ef0ede1ee28fb1b6c8 100644 (file)
@@ -411,6 +411,7 @@ settings_value_check(struct config_parser_context *ctx,
        case SET_FILTER_ARRAY:
                break;
        case SET_FILTER_NAME:
+       case SET_FILTER_HIERARCHY:
                ctx->error = p_strdup_printf(ctx->pool,
                        "Setting is a named filter, use '%s {'", def->key);
                return -1;
@@ -432,7 +433,9 @@ config_is_filter_name(struct config_parser_context *ctx, const char *key,
                return FALSE;
 
        def = &all_infos[config_key->info_idx]->defines[config_key->define_idx];
-       if (def->type != SET_FILTER_NAME && def->type != SET_FILTER_ARRAY)
+       if (def->type != SET_FILTER_NAME &&
+           def->type != SET_FILTER_HIERARCHY &&
+           def->type != SET_FILTER_ARRAY)
                return FALSE;
 
        *def_r = def;
@@ -636,7 +639,8 @@ int config_apply_line(struct config_parser_context *ctx,
                                break;
                        struct config_module_parser *l =
                                &ctx->cur_section->module_parsers[config_key->info_idx];
-                       if (l->info->defines[config_key->define_idx].type != SET_FILTER_NAME)
+                       if (l->info->defines[config_key->define_idx].type != SET_FILTER_NAME &&
+                           l->info->defines[config_key->define_idx].type != SET_FILTER_HIERARCHY)
                                break;
 
                        ctx->cur_section->filter.filter_name =
@@ -926,20 +930,16 @@ config_filter_add_new_filter(struct config_parser_context *ctx,
                else
                        filter->remote_host = p_strdup(ctx->pool, value);
        } else if (config_is_filter_name(ctx, key, &filter_def)) {
-               if (filter_def->type == SET_FILTER_NAME) {
+               if (filter_def->type == SET_FILTER_NAME ||
+                   filter_def->type == SET_FILTER_HIERARCHY) {
                        if (value[0] != '\0' || line->value_quoted) {
                                ctx->error = p_strdup_printf(ctx->pool,
                                        "%s { } must not have a section name",
                                        key);
                                return TRUE;
                        }
-                       if (parent->filter_name != NULL &&
-                           !parent->filter_name_array) {
-                               ctx->error = p_strdup_printf(ctx->pool,
-                                       "Nested named filters not allowed: %s { %s { .. } }",
-                                       parent->filter_name, key);
-                               return FALSE;
-                       }
+                       if (filter_def->type == SET_FILTER_HIERARCHY)
+                               filter->filter_hierarchical = TRUE;
                        filter->filter_name = p_strdup(ctx->pool, key);
                } else {
                        if (parent->filter_name != NULL &&
index 5ac12c518d725df8afa8fb4b5a21f193cf1f1caa..78be50b9be83861760319457f26d86b1530dd1f0 100644 (file)
@@ -312,6 +312,7 @@ settings_export(struct config_export_context *ctx,
                        break;
                }
                case SET_FILTER_NAME:
+               case SET_FILTER_HIERARCHY:
                case SET_ALIAS:
                        break;
                }
index 3272c0b2b82776beb65a6937ecf19dd8ee881102..cc5b9413ecb8c16cf85ac03d8c703d124c949536 100644 (file)
@@ -557,6 +557,7 @@ settings_parse(struct setting_parser_context *ctx,
                break;
        }
        case SET_FILTER_NAME:
+       case SET_FILTER_HIERARCHY:
                settings_parser_set_error(ctx, t_strdup_printf(
                        "Setting is a named filter, use '%s {'", key));
                return -1;
@@ -575,7 +576,8 @@ settings_find_key(struct setting_parser_context *ctx, const char *key,
 
        /* try to find the exact key */
        def = setting_define_find(ctx->info, key);
-       if (def != NULL && (def->type != SET_FILTER_NAME ||
+       if (def != NULL && ((def->type != SET_FILTER_NAME &&
+                            def->type != SET_FILTER_HIERARCHY) ||
                            allow_filter_name)) {
                *def_r = def;
                return TRUE;
index e79dd2fe0f6e91ab86e8a63a48d156c0c6fb7471..9e10e9f151407581f520f1720daf3db2633349c4 100644 (file)
@@ -34,6 +34,7 @@ enum setting_type {
        SET_BOOLLIST, /* of type ARRAY_TYPE(const_string) - guaranteed NULL-terminted */
        SET_ALIAS, /* alias name for above setting definition */
        SET_FILTER_NAME,
+       SET_FILTER_HIERARCHY,
        SET_FILTER_ARRAY,
 };
 enum setting_flags {
index 25ec46d21da9dcf0d51c8efcaf0f3d1adec01c91..30ba8915fbde0e472e8b294fe6ee0996461ef387 100644 (file)
@@ -1042,6 +1042,25 @@ settings_override_filter_match(struct settings_apply_ctx *ctx,
                        str_printfa(filter_string, SETTINGS_EVENT_FILTER_NAME"=\"%s\"",
                                    wildcard_str_escape(last_filter_key));
                        break;
+               case SET_FILTER_HIERARCHY: {
+                       /* add the full repeated hierarchy here */
+                       const char *next;
+
+                       str_printfa(filter_string, SETTINGS_EVENT_FILTER_NAME"=\"%s",
+                                   wildcard_str_escape(part));
+                       while (str_begins(p + 1, part, &next) &&
+                              next[0] == SETTINGS_SEPARATOR) {
+                               str_append_c(filter_string, '/');
+                               str_append(filter_string,
+                                          wildcard_str_escape(part));
+                               p = next;
+                       }
+                       str_append_c(filter_string, '"');
+
+                       last_filter_key = t_strdup_until(set->key, p);
+                       last_filter_value = NULL;
+                       break;
+               }
                case SET_FILTER_ARRAY: {
                        const char *value = p + 1;
                        p = strchr(value, SETTINGS_SEPARATOR);
@@ -1372,6 +1391,8 @@ settings_get_full(struct event *event,
                filter_name_required = TRUE;
        } else if (filter_key != NULL) {
                filter_name = filter_key;
+               event_strlist_append(lookup_event, SETTINGS_EVENT_FILTER_NAME,
+                                    filter_name);
                filter_name_required = TRUE;
        }