]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Support dovecot_config_version to rename settings or change default settings
authorTimo Sirainen <timo.sirainen@open-xchange.com>
Sun, 2 Mar 2025 08:45:19 +0000 (10:45 +0200)
committerTimo Sirainen <timo.sirainen@open-xchange.com>
Wed, 5 Mar 2025 18:20:34 +0000 (20:20 +0200)
.gitignore
src/config/config-parser.c
src/config/config-request.c
src/config/old-set-parser.c
src/config/old-set-parser.h
src/lib-settings/Makefile.am
src/lib-settings/settings-history-core.txt [new file with mode: 0644]
src/lib-settings/settings-history.c [new file with mode: 0644]
src/lib-settings/settings-history.h [new file with mode: 0644]
src/lib-settings/settings-history.pl [new file with mode: 0755]

index 8e0bd569e41ed65d6810d249ceb76256b4c61c35..2a64cd6c0a4c23a4c706662f51a7adb1945bb569 100644 (file)
@@ -77,6 +77,7 @@ doc/wiki/*.txt
 doc/wiki/Makefile.am
 src/anvil/anvil
 src/auth/auth
+src/lib-settings/settings-history-core.c
 src/config/all-settings.c
 src/config/config
 src/config/doveconf
index e18bf9da04b7f47bbc3f538c4907bc812359bf27..2c1a0fb25e4d49eb0be2f8a6d2a577c9e123c8be 100644 (file)
@@ -2825,7 +2825,7 @@ int config_parse_file(const char *path, enum config_parse_flags flags,
        string_t *full_line;
        char *line;
        int fd, ret = 0;
-       bool handled, dump_defaults = (path == NULL);
+       bool dump_defaults = (path == NULL);
 
        *config_r = NULL;
 
@@ -2956,9 +2956,8 @@ prevfile:
 
                if (!config_parser_get_version(&ctx, &config_line) &&
                    ctx.error == NULL) T_BEGIN {
-                       handled = old_settings_handle(&ctx, &config_line);
-                       if (!handled)
-                               config_parser_apply_line(&ctx, &config_line);
+                       old_settings_handle(&ctx, &config_line);
+                       config_parser_apply_line(&ctx, &config_line);
                } T_END;
 
                if (ctx.error != NULL) {
index 2eeb80aebaf1cf0d4fd351b246d2bb95ab7ca952..bb2ff067f35e9c137cbb833548b20e6eeca3c9fd 100644 (file)
@@ -224,8 +224,22 @@ settings_export(struct config_export_context *ctx,
                case SET_STR_NOVARS:
                case SET_ENUM: {
                        string_t *default_str = NULL;
+                       bool default_changed = FALSE;
+                       const char *old_default;
                        i_assert(info->defaults != NULL);
-                       if (!dump_default || module_parser->change_counters[define_idx] == 0) {
+                       if (module_parser->change_counters[define_idx] <= CONFIG_PARSER_CHANGE_DEFAULTS) {
+                               /* Setting isn't explicitly set. We need to see
+                                  if its default has changed. */
+                               if (old_settings_default(ctx->dovecot_config_version,
+                                                        def->key, &old_default)) {
+                                       default_str = t_str_new(strlen(old_default));
+                                       str_append(default_str, old_default);
+                                       default_changed = TRUE;
+                               }
+                       }
+
+                       if ((!dump_default || module_parser->change_counters[define_idx] == 0) &&
+                           default_str == NULL) {
                                const void *default_value =
                                        CONST_PTR_OFFSET(info->defaults,
                                                         def->offset);
@@ -250,7 +264,14 @@ settings_export(struct config_export_context *ctx,
                                   actually changed from its default. */
                                break;
                        }
-                       if (module_parser->change_counters[define_idx] != 0) {
+                       if (module_parser->change_counters[define_idx] >
+                                       CONFIG_PARSER_CHANGE_DEFAULTS) {
+                               /* explicitly set */
+                               str_append(ctx->value,
+                                       module_parser->settings[define_idx].str);
+                       } else if (module_parser->change_counters[define_idx] ==
+                                  CONFIG_PARSER_CHANGE_DEFAULTS && !default_changed) {
+                               /* default not changed by old version checks */
                                str_append(ctx->value,
                                        module_parser->settings[define_idx].str);
                        } else {
index cf0ebddcf42b800a7ddc639cbcfbd42481c2889e..2515f2163c2677d7d490af194670887d24202b0c 100644 (file)
@@ -1,7 +1,9 @@
 /* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "master-service.h"
+#include "settings-history.h"
 #include "config-parser-private.h"
 #include "old-set-parser.h"
 
@@ -27,8 +29,30 @@ obsolete(struct config_parser_context *ctx, const char *str, ...)
        va_end(args);
 }
 
-bool old_settings_handle(struct config_parser_context *ctx ATTR_UNUSED,
-                        const struct config_line *line)
+static void old_settings_handle_rename(struct config_parser_context *ctx,
+                                      struct config_line *line)
+{
+       struct settings_history *history = settings_history_get();
+       const struct setting_history_rename *rename;
+
+       if (ctx->dovecot_config_version[0] == '\0')
+               return;
+
+       array_foreach(&history->renames, rename) {
+               if (version_cmp(rename->version,
+                               ctx->dovecot_config_version) <= 0)
+                       break;
+               if (strcmp(rename->old_key, line->key) == 0) {
+                       obsolete(ctx, "%s has been renamed to %s",
+                                rename->old_key, rename->new_key);
+                       line->key = rename->new_key;
+                       break;
+               }
+       }
+}
+
+void old_settings_handle(struct config_parser_context *ctx,
+                        struct config_line *line)
 {
        switch (line->type) {
        case CONFIG_LINE_TYPE_SKIP:
@@ -43,7 +67,27 @@ bool old_settings_handle(struct config_parser_context *ctx ATTR_UNUSED,
        case CONFIG_LINE_TYPE_KEYFILE:
        case CONFIG_LINE_TYPE_KEYVALUE:
        case CONFIG_LINE_TYPE_KEYVARIABLE:
+               old_settings_handle_rename(ctx, line);
                break;
        }
+}
+
+bool old_settings_default(const char *dovecot_config_version,
+                         const char *key, const char **old_default_r)
+{
+       struct settings_history *history = settings_history_get();
+       const struct setting_history_default *def;
+
+       if (dovecot_config_version[0] == '\0')
+               return FALSE;
+
+       array_foreach(&history->defaults, def) {
+               if (version_cmp(def->version, dovecot_config_version) <= 0)
+                       break;
+               if (strcmp(def->key, key) == 0) {
+                       *old_default_r = def->old_value;
+                       return TRUE;
+               }
+       }
        return FALSE;
 }
index 811a80975a18ce607d1ddd57161b0dc9fa1e2428..000f9c9fa837e91dc7c9be77d490467df44ab5ab 100644 (file)
@@ -5,7 +5,9 @@
 
 struct config_parser_context;
 
-bool old_settings_handle(struct config_parser_context *ctx,
-                        const struct config_line *line);
+void old_settings_handle(struct config_parser_context *ctx,
+                        struct config_line *line);
+bool old_settings_default(const char *dovecot_config_version,
+                         const char *key, const char **old_default_r);
 
 #endif
index ae01131cb91d20d2f53ab87e61c890a27e6b3602..f02649a9a4fc0df1cdda631e28f2c0e004c62a01 100644 (file)
@@ -7,15 +7,28 @@ AM_CPPFLAGS = \
 
 libsettings_la_SOURCES = \
        settings.c \
+       settings-history.c \
        settings-parser.c
 
 headers = \
        settings.h \
+       settings-history.h \
        settings-parser.h
 
 pkginc_libdir=$(pkgincludedir)
 pkginc_lib_HEADERS = $(headers)
 
+BUILT_SOURCES = settings-history-core.c
+
+settings-history.c: settings-history-core.c
+
+settings-history-core.c: $(top_srcdir)/src/lib-settings/settings-history.pl settings-history-core.txt
+       $(AM_V_GEN)$(top_srcdir)/src/lib-settings/settings-history.pl settings-history-core.txt $(DOVECOT_PRO_BUILD) > settings-history-core.c || rm -f settings-history-core.c
+
+EXTRA_DIST = \
+       settings-history-core.txt \
+       settings-history.pl
+
 test_programs = \
        test-settings-parser \
        test-settings
diff --git a/src/lib-settings/settings-history-core.txt b/src/lib-settings/settings-history-core.txt
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/lib-settings/settings-history.c b/src/lib-settings/settings-history.c
new file mode 100644 (file)
index 0000000..a2abb21
--- /dev/null
@@ -0,0 +1,51 @@
+/* Copyright (c) 2025 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "settings-history.h"
+
+#include "settings-history-core.c"
+
+static struct settings_history history;
+
+static void settings_history_free(void)
+{
+       array_free(&history.defaults);
+       array_free(&history.renames);
+}
+
+struct settings_history *settings_history_get(void)
+{
+       if (array_is_created(&history.defaults))
+               return &history;
+
+       i_array_init(&history.defaults,
+                    N_ELEMENTS(settings_history_core_defaults) + 16);
+       array_append(&history.defaults,
+                    settings_history_core_defaults,
+                    N_ELEMENTS(settings_history_core_defaults));
+       i_array_init(&history.renames,
+                    N_ELEMENTS(settings_history_core_renames) + 16);
+       array_append(&history.renames,
+                    settings_history_core_renames,
+                    N_ELEMENTS(settings_history_core_renames));
+
+       lib_atexit(settings_history_free);
+       return &history;
+}
+
+void settings_history_register_defaults(
+       const struct setting_history_default *defaults, unsigned int count)
+{
+       struct settings_history *history = settings_history_get();
+
+       array_append(&history->defaults, defaults, count);
+}
+
+void settings_history_register_renames(
+       const struct setting_history_rename *renames, unsigned int count)
+{
+       struct settings_history *history = settings_history_get();
+
+       array_append(&history->renames, renames, count);
+}
diff --git a/src/lib-settings/settings-history.h b/src/lib-settings/settings-history.h
new file mode 100644 (file)
index 0000000..5379387
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef SETTINGS_HISTORY_H
+#define SETTINGS_HISTORY_H
+
+struct setting_history_default {
+       const char *key;
+       const char *old_value;
+       const char *version;
+};
+
+struct setting_history_rename {
+       const char *old_key, *new_key;
+       const char *version;
+};
+
+struct settings_history {
+       ARRAY(struct setting_history_default) defaults;
+       ARRAY(struct setting_history_rename) renames;
+};
+
+struct settings_history *settings_history_get(void);
+
+/* Register new defaults/renames. The strings are assumed to be statically
+   allocated, i.e. they are not duplicated. */
+void settings_history_register_defaults(
+       const struct setting_history_default *defaults, unsigned int count);
+void settings_history_register_renames(
+       const struct setting_history_rename *renames, unsigned int count);
+
+#endif
diff --git a/src/lib-settings/settings-history.pl b/src/lib-settings/settings-history.pl
new file mode 100755 (executable)
index 0000000..11f7b54
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use version;
+use 5.010;
+
+my ($input_path, $pro_edition) = ($ARGV[0], $ARGV[1]);
+
+# output renames
+print "static const struct setting_history_rename settings_history_core_renames[] = {\n";
+my $f;
+open $f, "<:encoding(utf8)", $input_path or die "$!";
+my $prev_version = "";
+while (<$f>) {
+  chomp;
+  if (/^rename\t/) {
+    my ($type, $old_key, $new_key, $ce_version, $pro_version) = split("\t");
+    my $version = $pro_edition ? $pro_version : $ce_version;
+    if ($version ne "") {
+      die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/;
+      print "  { \"$old_key\", \"$new_key\", \"$version\" },\n";
+      if ($prev_version ne "") {
+       die "Bad version sorting order" if version->parse($version) > version->parse($prev_version);
+      }
+      $prev_version = $version;
+    }
+  } elsif (! /^default\t/) {
+    die "Unknown line: $_";
+  }
+}
+close $f;
+print "};\n";
+
+# output defaults
+print "static const struct setting_history_default settings_history_core_defaults[] = {\n";
+open $f, "<:encoding(utf8)", $input_path or die "$!";
+while (<$f>) {
+  chomp;
+  if (/^default\t/) {
+    my ($type, $key, $old_value, $ce_version, $pro_version) = split("\t");
+    my $version = $pro_edition ? $pro_version : $ce_version;
+    if ($version ne "") {
+      die "bad version: $version" if $version !~ /^((\d+)\.)*(\d+)$/;
+      print "  { \"$key\", \"$old_value\", \"$version\" },\n";
+      if ($prev_version ne "") {
+       die "Bad version sorting order" if version->parse($version) > version->parse($prev_version);
+      }
+      $prev_version = $version;
+    }
+  }
+}
+close $f;
+print "};\n";