From: Timo Sirainen Date: Tue, 29 Jul 2025 10:00:37 +0000 (+0300) Subject: config: Prepare for storing unexpanded $SET variables into setting values X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=2296ed44996905dd87ff3d4462f7407e01ad0658;p=thirdparty%2Fdovecot%2Fcore.git config: Prepare for storing unexpanded $SET variables into setting values --- diff --git a/src/config/config-parser-private.h b/src/config/config-parser-private.h index bcaa0c9971..00b496c49f 100644 --- a/src/config/config-parser-private.h +++ b/src/config/config-parser-private.h @@ -125,7 +125,8 @@ extern int (*hook_config_parser_end)(struct config_parser_context *ctx, static inline const char * set_str_expanded(const union config_module_parser_setting *value) { - return value->str; + i_assert(value->prefixed_str[0] == CONFIG_VALUE_PREFIX_EXPANDED); + return value->prefixed_str + 1; } /* Apply a key-value setting. $VARIABLE expansion is not done for the value. */ diff --git a/src/config/config-parser.c b/src/config/config-parser.c index d50a3d1f36..512407ef33 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -481,10 +481,14 @@ static int settings_value_check(struct config_parser_context *ctx, const struct setting_parser_info *info, const struct setting_define *def, - const char *value) + const char *prefixed_value) { const char *error; + if (prefixed_value[0] != CONFIG_VALUE_PREFIX_EXPANDED) + return 0; + const char *value = prefixed_value + 1; + switch (def->type) { case SET_BOOL: { bool b; @@ -660,13 +664,13 @@ config_list_add_defaults(struct config_parser_context *ctx, return; const ARRAY_TYPE(const_string) *src = - ldef->settings[config_key->define_idx].list.values; + ldef->settings[config_key->define_idx].list.prefixed_values; if (src != NULL && array_is_created(src)) array_append_array(dest, src); } static int config_apply_strlist(struct config_parser_context *ctx, - const char *key, const char *value, + const char *key, const char *prefixed_value, struct config_parser_key *config_key, ARRAY_TYPE(const_string) **strlistp, bool *stop_list) @@ -675,7 +679,8 @@ static int config_apply_strlist(struct config_parser_context *ctx, suffix = strchr(key, SETTINGS_SEPARATOR); if (suffix == NULL) { - if (value[0] == '\0') { + if (prefixed_value[0] == CONFIG_VALUE_PREFIX_EXPANDED && + prefixed_value[1] == '\0') { /* clear out the whole strlist */ if (*strlistp != NULL) array_clear(*strlistp); @@ -700,26 +705,28 @@ static int config_apply_strlist(struct config_parser_context *ctx, config_list_add_defaults(ctx, config_key, *strlistp); } - value = p_strdup(ctx->pool, value); + prefixed_value = p_strdup(ctx->pool, prefixed_value); /* replace if it already exists */ unsigned int i, count; const char *const *items = array_get(*strlistp, &count); for (i = 0; i < count; i += 2) { - if (strcmp(items[i], key) == 0) { - array_idx_set(*strlistp, i + 1, &value); + if (strcmp(items[i] + 1, key) == 0) { + array_idx_set(*strlistp, i + 1, &prefixed_value); return 0; } } - key = p_strdup(ctx->pool, key); - array_push_back(*strlistp, &key); - array_push_back(*strlistp, &value); + const char *prefixed_key = + p_strdup_printf(ctx->pool, "%c%s", + CONFIG_VALUE_PREFIX_EXPANDED, key); + array_push_back(*strlistp, &prefixed_key); + array_push_back(*strlistp, &prefixed_value); return 0; } static int config_apply_boollist(struct config_parser_context *ctx, - const char *key, const char *value, + const char *key, const char *prefixed_value, struct config_parser_key *config_key, ARRAY_TYPE(const_string) **strlistp, bool *stop_list) @@ -729,22 +736,24 @@ static int config_apply_boollist(struct config_parser_context *ctx, bool b; if (strchr(key, SETTINGS_SEPARATOR) != NULL) { - if (setting_value_can_check(value, ctx->expand_values) && - str_parse_get_bool(value, &b, &error) < 0) { + if (prefixed_value[0] == CONFIG_VALUE_PREFIX_EXPANDED && + setting_value_can_check(prefixed_value + 1, + ctx->expand_values) && + str_parse_get_bool(prefixed_value + 1, &b, &error) < 0) { 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, config_key, - strlistp, stop_list); + return config_apply_strlist(ctx, key, prefixed_value, + config_key, strlistp, stop_list); } /* replace the whole list */ t_array_init(&boollist, 16); - if (settings_parse_boollist_string(value, ctx->pool, &boollist, - &error) < 0) { + if (settings_parse_boollist_string(prefixed_value + 1, ctx->pool, + &boollist, &error) < 0) { ctx->error = p_strdup(ctx->pool, error); return -1; } @@ -754,9 +763,12 @@ static int config_apply_boollist(struct config_parser_context *ctx, } else { array_clear(*strlistp); } - const char *yes = "yes"; + const char *yes = CONFIG_VALUE_PREFIX_EXPANDED_S"yes"; array_foreach_elem(&boollist, key) { - array_push_back(*strlistp, &key); + const char *prefixed_key = + p_strdup_printf(ctx->pool, "%c%s", + CONFIG_VALUE_PREFIX_EXPANDED, key); + array_push_back(*strlistp, &prefixed_key); array_push_back(*strlistp, &yes); } *stop_list = TRUE; @@ -794,20 +806,21 @@ static int config_apply_filter_array(struct config_parser_context *ctx, static int config_apply_file(struct config_parser_context *ctx, const struct config_line *line, - const char *path, const char **output_r) + const char *path, const char **prefixed_output_r) { struct stat st; const char *full_path, *error; if (path[0] == '\0') { - *output_r = ""; + *prefixed_output_r = CONFIG_VALUE_PREFIX_EXPANDED_S; return 0; } /* Do not attempt to expand paths that contain variable expansions. These will be expanded later. */ if (!ctx->expand_values || strstr(path, "%{") != NULL) { - *output_r = p_strdup(ctx->pool, path); + *prefixed_output_r = p_strdup_printf(ctx->pool, "%c%s", + CONFIG_VALUE_PREFIX_EXPANDED, path); return 0; } full_path = fix_relative_path(path, ctx->cur_input); @@ -815,12 +828,13 @@ static int config_apply_file(struct config_parser_context *ctx, if (full_path != path && ctx->expand_values) path = full_path; if (settings_parse_read_file(full_path, path, ctx->pool, &st, - "", output_r, &error) < 0) { + CONFIG_VALUE_PREFIX_EXPANDED_S, + prefixed_output_r, &error) < 0) { ctx->error = p_strdup(ctx->pool, error); if (config_apply_error(ctx, line->key) < 0) return -1; /* delayed error */ - *output_r = ""; + *prefixed_output_r = CONFIG_VALUE_PREFIX_EXPANDED_S; } else { config_parser_add_seen_file(ctx, &st, full_path); } @@ -834,9 +848,6 @@ config_apply_exact_line(struct config_parser_context *ctx, { struct config_parser_key *config_key; - i_assert(prefixed_value[0] == CONFIG_VALUE_PREFIX_EXPANDED); - const char *value = prefixed_value + 1; - if (ctx->cur_section->filter_def != NULL && !ctx->cur_section->filter_parser->filter_required_setting_seen && ctx->cur_section->filter_def->required_setting != NULL && @@ -869,18 +880,26 @@ 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, config_key, - &l->settings[config_key->define_idx].list.values, + if (config_apply_strlist(ctx, key, prefixed_value, config_key, + &l->settings[config_key->define_idx].list.prefixed_values, &l->settings[config_key->define_idx].list.stop_list) < 0) return -1; break; case SET_BOOLLIST: - if (config_apply_boollist(ctx, key, value, config_key, - &l->settings[config_key->define_idx].list.values, + if (config_apply_boollist(ctx, key, prefixed_value, config_key, + &l->settings[config_key->define_idx].list.prefixed_values, &l->settings[config_key->define_idx].list.stop_list) < 0) return -1; break; case SET_FILTER_ARRAY: + if (prefixed_value[0] != CONFIG_VALUE_PREFIX_EXPANDED) { + i_assert(prefixed_value[0] == CONFIG_VALUE_PREFIX_SET_UNEXPANDED); + ctx->error = p_strdup_printf(ctx->pool, + "Named list filter name must not contain $SET: %s", + prefixed_value + 1); + return -1; + } + const char *value = prefixed_value + 1; if (str_begins_with(value, "__")) { /* These are reserved for internal filters */ ctx->error = p_strdup_printf(ctx->pool, @@ -894,22 +913,33 @@ config_apply_exact_line(struct config_parser_context *ctx, break; case SET_FILE: { const char *inline_value; + if (prefixed_value[0] != CONFIG_VALUE_PREFIX_EXPANDED) { + /* FIXME: implement in a later commit */ + i_assert(prefixed_value[0] == CONFIG_VALUE_PREFIX_SET_UNEXPANDED); + ctx->error = p_strdup_printf(ctx->pool, + "File settings must not contain $SET: %s", + prefixed_value + 1); + return -1; + } + const char *value = prefixed_value + 1; if (str_begins(value, SET_FILE_INLINE_PREFIX, &inline_value)) { - l->settings[config_key->define_idx].str = - value[0] == '\0' ? "" : - p_strconcat(ctx->pool, "\n", inline_value, NULL); + l->settings[config_key->define_idx].prefixed_str = + value[0] == '\0' ? CONFIG_VALUE_PREFIX_EXPANDED_S : + p_strdup_printf(ctx->pool, "%c\n%s", + CONFIG_VALUE_PREFIX_EXPANDED, + inline_value); break; } i_assert(line != NULL); if (config_apply_file(ctx, line, value, - &l->settings[config_key->define_idx].str) < 0) + &l->settings[config_key->define_idx].prefixed_str) < 0) return -1; break; } default: - l->settings[config_key->define_idx].str = - p_strdup(ctx->pool, value); + l->settings[config_key->define_idx].prefixed_str = + p_strdup(ctx->pool, prefixed_value); break; } if (l->change_counters[config_key->define_idx] < ctx->change_counter) { @@ -918,7 +948,7 @@ config_apply_exact_line(struct config_parser_context *ctx, } if (settings_value_check(ctx, l->info, &l->info->defines[config_key->define_idx], - value) < 0) + prefixed_value) < 0) return -1; /* FIXME: remove once auth does support these. */ if (strcmp(l->info->name, "auth") == 0 && @@ -1607,17 +1637,19 @@ void config_fill_set_parser(struct setting_parser_context *parser, switch (p->info->defines[i].type) { case SET_STRLIST: case SET_BOOLLIST: { - if (p->settings[i].list.values == NULL) + if (p->settings[i].list.prefixed_values == NULL) break; unsigned int j, count; - const char *const *strings = - array_get(p->settings[i].list.values, &count); + const char *const *prefixed_strings = + array_get(p->settings[i].list.prefixed_values, &count); for (j = 0; j < count; j += 2) T_BEGIN { + i_assert(prefixed_strings[j][0] == CONFIG_VALUE_PREFIX_EXPANDED); + i_assert(prefixed_strings[j + 1][0] == CONFIG_VALUE_PREFIX_EXPANDED); const char *key = t_strdup_printf("%s/%s", p->info->defines[i].key, - settings_section_escape(strings[j])); + settings_section_escape(prefixed_strings[j] + 1)); (void)settings_parse_keyidx_value_nodup(parser, i, - key, strings[j + 1]); + key, prefixed_strings[j + 1] + 1); } T_END; break; } @@ -2894,7 +2926,10 @@ void config_parser_apply_line(struct config_parser_context *ctx, ctx->error = "Recursive include groups not allowed"; break; } - i_assert(str_c(ctx->prefixed_value)[0] == CONFIG_VALUE_PREFIX_EXPANDED); + if (str_c(ctx->prefixed_value)[0] != CONFIG_VALUE_PREFIX_EXPANDED) { + ctx->error = "Include groups cannot contain $variables"; + break; + } config_parser_include_add_or_update(ctx, line->key + 1, str_c(ctx->prefixed_value) + 1); } else { @@ -3366,15 +3401,18 @@ config_parsed_strlist_append(string_t *keyvals, strlist = array_get(values, &len); for (i = 0; i < len; i += 2) { + i_assert(strlist[i][0] == CONFIG_VALUE_PREFIX_EXPANDED); + i_assert(strlist[i + 1][0] == CONFIG_VALUE_PREFIX_EXPANDED); + if (str_len(keyvals) > 0) str_append_c(keyvals, ' '); for (j = 0; j < drop_len; j += 2) { - if (strcmp(strlist[i], drop_strlist[j]) == 0) + if (strcmp(strlist[i] + 1, drop_strlist[j]) == 0) break; } if (j == drop_len) { - str_printfa(keyvals, "%s=%s", strlist[i], - strlist[i + 1]); + str_printfa(keyvals, "%s=%s", strlist[i] + 1, + strlist[i + 1] + 1); } } } @@ -3416,9 +3454,9 @@ config_parsed_get_setting_full(const struct config_parsed *config, if (strcmp(key, "import_environment") == 0) { string_t *keyvals = t_str_new(64); const ARRAY_TYPE(const_string) *strlist_set = - l[info_idx].settings[key_idx].list.values; + l[info_idx].settings[key_idx].list.prefixed_values; const ARRAY_TYPE(const_string) *strlist_defaults = - ldef[info_idx].settings[key_idx].list.values; + ldef[info_idx].settings[key_idx].list.prefixed_values; config_parsed_strlist_append(keyvals, strlist_set, NULL); config_parsed_strlist_append(keyvals, strlist_defaults, strlist_set); return str_c(keyvals); diff --git a/src/config/config-parser.h b/src/config/config-parser.h index 33ebeafd31..1c203ac2be 100644 --- a/src/config/config-parser.h +++ b/src/config/config-parser.h @@ -42,7 +42,10 @@ enum config_parse_flags { CONFIG_PARSE_FLAG_DEFAULT_VERSION = BIT(11), }; -#define CONFIG_VALUE_PREFIX_EXPANDED '\001' +#define CONFIG_VALUE_PREFIX_EXPANDED_S "\001" +#define CONFIG_VALUE_PREFIX_EXPANDED CONFIG_VALUE_PREFIX_EXPANDED_S[0] +/* value contains $SET variables (and perhaps other variables) */ +#define CONFIG_VALUE_PREFIX_SET_UNEXPANDED '\002' /* Used to track changed settings for a setting_parser_info. Initially only the "info" is set, while everything else is NULL. Once the first setting @@ -54,9 +57,10 @@ struct config_module_parser { /* The rest are filled only after the first setting is changed: */ unsigned int set_count; union config_module_parser_setting { - const char *str; + const char *prefixed_str; struct { - ARRAY_TYPE(const_string) *values; + /* [prefixed_key, prefixed_value, prefixed_key2, ...] */ + ARRAY_TYPE(const_string) *prefixed_values; bool stop_list; } list; ARRAY_TYPE(const_string) *filter_array; diff --git a/src/config/config-request.c b/src/config/config-request.c index cb31d2371c..ecc5c8a231 100644 --- a/src/config/config-request.c +++ b/src/config/config-request.c @@ -301,9 +301,9 @@ settings_export(struct config_export_context *ctx, } case SET_STRLIST: case SET_BOOLLIST: { - const ARRAY_TYPE(const_string) *val = - module_parser->settings[define_idx].list.values; - const char *const *strings; + const ARRAY_TYPE(const_string) *prefixed_val = + module_parser->settings[define_idx].list.prefixed_values; + const char *const *prefixed_strings; value_stop_list = module_parser->settings[define_idx].list.stop_list; if (hash_table_is_created(ctx->keys) && @@ -314,11 +314,11 @@ settings_export(struct config_export_context *ctx, if ((ctx->flags & CONFIG_DUMP_FLAG_DEDUPLICATE_KEYS) != 0) hash_table_insert(ctx->keys, def->key, def->key); - if (val != NULL) { - strings = array_get(val, &count); + if (prefixed_val != NULL) { + prefixed_strings = array_get(prefixed_val, &count); i_assert(count % 2 == 0); } else { - strings = NULL; + prefixed_strings = NULL; count = 0; } @@ -337,12 +337,14 @@ settings_export(struct config_export_context *ctx, export_set.type = def->type == SET_STRLIST ? CONFIG_KEY_NORMAL : CONFIG_KEY_BOOLLIST_ELEM; for (i = 0; i < count; i += 2) T_BEGIN { + i_assert(prefixed_strings[i][0] == CONFIG_VALUE_PREFIX_EXPANDED); + i_assert(prefixed_strings[i + 1][0] == CONFIG_VALUE_PREFIX_EXPANDED); export_set.key = t_strdup_printf("%s%c%s", def->key, SETTINGS_SEPARATOR, - strings[i]); + prefixed_strings[i] + 1); export_set.list_idx = i / 2; - export_set.value = strings[i+1]; + export_set.value = prefixed_strings[i + 1] + 1; /* only the last element stops the list */ export_set.value_stop_list = value_stop_list && i + 2 == count;