]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
config: Added support for reading v1.2 config files.
authorTimo Sirainen <tss@iki.fi>
Mon, 8 Mar 2010 22:34:22 +0000 (00:34 +0200)
committerTimo Sirainen <tss@iki.fi>
Mon, 8 Mar 2010 22:34:22 +0000 (00:34 +0200)
--HG--
branch : HEAD

src/config/Makefile.am
src/config/config-parser.c
src/config/old-set-parser.c [new file with mode: 0644]
src/config/old-set-parser.h [new file with mode: 0644]

index 80ef9c84b97e3586adf0c06077df962eab133e0d..6f802fcc9e328ef9ed025df71683cc4e8b2d033c 100644 (file)
@@ -32,6 +32,7 @@ common = \
        config-filter.c \
        config-parser.c \
        config-request.c \
+       old-set-parser.c \
        sysinfo-get.c
 
 config_SOURCES = \
@@ -47,7 +48,9 @@ noinst_HEADERS = \
        config-connection.h \
        config-filter.h \
        config-parser.h \
+       config-parser-private.h \
        config-request.h \
+       old-set-parser.h \
        sysinfo-get.h
 
 all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl
index c89ec49d9021fce9316bbb531d3cffe731560746..e0c0196f4ea1b82f883a7c448a5d33271631ea73 100644 (file)
@@ -11,9 +11,9 @@
 #include "service-settings.h"
 #include "master-service-settings.h"
 #include "all-settings.h"
-#include "config-filter.h"
+#include "old-set-parser.h"
 #include "config-request.h"
-#include "config-parser.h"
+#include "config-parser-private.h"
 
 #include <stdlib.h>
 #include <unistd.h>
 
 #define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
 
-struct config_section_stack {
-       struct config_section_stack *prev;
-
-       struct config_filter filter;
-       /* root=NULL-terminated list of parsers */
-       struct config_module_parser *parsers;
-       unsigned int pathlen;
-};
-
-struct input_stack {
-       struct input_stack *prev;
-
-       struct istream *input;
-       const char *path;
-       unsigned int linenum;
-};
-
-struct parser_context {
-       pool_t pool;
-       const char *path;
-
-       ARRAY_DEFINE(all_parsers, struct config_filter_parser *);
-       struct config_module_parser *root_parsers;
-       struct config_section_stack *cur_section;
-       struct input_stack *cur_input;
-
-       struct config_filter_context *filter;
-       unsigned int expand_values:1;
-};
-
 static const enum settings_parser_flags settings_parser_flags =
        SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS |
        SETTINGS_PARSER_FLAG_TRACK_CHANGES;
@@ -103,10 +73,8 @@ static void config_add_type(struct setting_parser_context *parser,
        i_assert(ret > 0);
 }
 
-static int
-config_apply_line(struct parser_context *ctx, const char *key,
-                 const char *line, const char *section_name,
-                 const char **error_r)
+int config_apply_line(struct config_parser_context *ctx, const char *key,
+                     const char *line, const char *section_name)
 {
        struct config_module_parser *l;
        bool found = FALSE;
@@ -119,15 +87,15 @@ config_apply_line(struct parser_context *ctx, const char *key,
                        if (section_name != NULL)
                                config_add_type(l->parser, line, section_name);
                } else if (ret < 0) {
-                       *error_r = settings_parser_get_error(l->parser);
+                       ctx->error = settings_parser_get_error(l->parser);
                        return -1;
                }
        }
        if (!found) {
-               *error_r = t_strconcat("Unknown setting: ", key, NULL);
+               ctx->error = p_strconcat(ctx->pool, "Unknown setting: ",
+                                        key, NULL);
                return -1;
        }
-       *error_r = NULL;
        return 0;
 }
 
@@ -164,7 +132,7 @@ config_module_parsers_init(pool_t pool)
 }
 
 static void
-config_add_new_parser(struct parser_context *ctx)
+config_add_new_parser(struct config_parser_context *ctx)
 {
        struct config_section_stack *cur_section = ctx->cur_section;
        struct config_filter_parser *parser;
@@ -188,7 +156,7 @@ config_add_new_parser(struct parser_context *ctx)
 }
 
 static struct config_section_stack *
-config_add_new_section(struct parser_context *ctx)
+config_add_new_section(struct config_parser_context *ctx)
 {
        struct config_section_stack *section;
 
@@ -200,7 +168,7 @@ config_add_new_section(struct parser_context *ctx)
 }
 
 static struct config_filter_parser *
-config_filter_parser_find(struct parser_context *ctx,
+config_filter_parser_find(struct config_parser_context *ctx,
                          const struct config_filter *filter)
 {
        struct config_filter_parser *const *parsers;
@@ -215,7 +183,7 @@ config_filter_parser_find(struct parser_context *ctx,
 }
 
 static int
-config_parse_net(struct parser_context *ctx, const char *value,
+config_parse_net(struct config_parser_context *ctx, const char *value,
                 const char **host_r, struct ip_addr *ip_r,
                 unsigned int *bits_r, const char **error_r)
 {
@@ -249,47 +217,47 @@ config_parse_net(struct parser_context *ctx, const char *value,
 }
 
 static bool
-config_filter_add_new_filter(struct parser_context *ctx,
-                            const char *key, const char *value,
-                            const char **error_r)
+config_filter_add_new_filter(struct config_parser_context *ctx,
+                            const char *key, const char *value)
 {
        struct config_filter *filter = &ctx->cur_section->filter;
        struct config_filter *parent = &ctx->cur_section->prev->filter;
        struct config_filter_parser *parser;
+       const char *error;
 
        if (strcmp(key, "protocol") == 0) {
                if (parent->service != NULL)
-                       *error_r = "protocol must not be under protocol";
+                       ctx->error = "protocol must not be under protocol";
                else
                        filter->service = p_strdup(ctx->pool, value);
        } else if (strcmp(key, "local") == 0) {
                if (parent->remote_bits > 0)
-                       *error_r = "local must not be under remote";
+                       ctx->error = "local must not be under remote";
                else if (parent->service != NULL)
-                       *error_r = "local must not be under protocol";
+                       ctx->error = "local must not be under protocol";
                else if (config_parse_net(ctx, value, &filter->local_host,
                                          &filter->local_net,
-                                         &filter->local_bits, error_r) < 0)
-                       ;
+                                         &filter->local_bits, &error) < 0)
+                       ctx->error = p_strdup(ctx->pool, error);
                else if (parent->local_bits > filter->local_bits ||
                         (parent->local_bits > 0 &&
                          !net_is_in_network(&filter->local_net,
                                             &parent->local_net,
                                             parent->local_bits)))
-                       *error_r = "local not a subset of parent local";
+                       ctx->error = "local not a subset of parent local";
        } else if (strcmp(key, "remote") == 0) {
                if (parent->service != NULL)
-                       *error_r = "remote must not be under protocol";
+                       ctx->error = "remote must not be under protocol";
                else if (config_parse_net(ctx, value, &filter->remote_host,
                                          &filter->remote_net,
-                                         &filter->remote_bits, error_r) < 0)
-                       ;
+                                         &filter->remote_bits, &error) < 0)
+                       ctx->error = p_strdup(ctx->pool, error);
                else if (parent->remote_bits > filter->remote_bits ||
                         (parent->remote_bits > 0 &&
                          !net_is_in_network(&filter->remote_net,
                                             &parent->remote_net,
                                             parent->remote_bits)))
-                       *error_r = "remote not a subset of parent remote";
+                       ctx->error = "remote not a subset of parent remote";
        } else {
                return FALSE;
        }
@@ -303,7 +271,7 @@ config_filter_add_new_filter(struct parser_context *ctx,
 }
 
 static int
-config_filter_parser_check(struct parser_context *ctx,
+config_filter_parser_check(struct config_parser_context *ctx,
                           const struct config_module_parser *p,
                           const char **error_r)
 {
@@ -316,7 +284,7 @@ config_filter_parser_check(struct parser_context *ctx,
 }
 
 static int
-config_all_parsers_check(struct parser_context *ctx,
+config_all_parsers_check(struct config_parser_context *ctx,
                         struct config_filter_context *new_filter,
                         const char **error_r)
 {
@@ -372,7 +340,7 @@ str_append_file(string_t *str, const char *key, const char *path,
        return ret < 0 ? -1 : 0;
 }
 
-static int settings_add_include(struct parser_context *ctx, const char *path,
+static int settings_add_include(struct config_parser_context *ctx, const char *path,
                                bool ignore_errors, const char **error_r)
 {
        struct input_stack *tmp, *new_input;
@@ -406,9 +374,10 @@ static int settings_add_include(struct parser_context *ctx, const char *path,
 }
 
 static int
-settings_include(struct parser_context *ctx, const char *pattern,
-                bool ignore_errors, const char **error_r)
+settings_include(struct config_parser_context *ctx, const char *pattern,
+                bool ignore_errors)
 {
+       const char *error;
 #ifdef HAVE_GLOB
        glob_t globbers;
        unsigned int i;
@@ -417,48 +386,43 @@ settings_include(struct parser_context *ctx, const char *pattern,
        case 0:
                break;
        case GLOB_NOSPACE:
-               *error_r = "glob() failed: Not enough memory";
+               ctx->error = "glob() failed: Not enough memory";
                return -1;
        case GLOB_ABORTED:
-               *error_r = "glob() failed: Read error";
+               ctx->error = "glob() failed: Read error";
                return -1;
        case GLOB_NOMATCH:
                if (ignore_errors)
                        return 0;
-               *error_r = "No matches";
+               ctx->error = "No matches";
                return -1;
        default:
-               *error_r = "glob() failed: Unknown error";
+               ctx->error = "glob() failed: Unknown error";
                return -1;
        }
 
        /* iterate throuth the different files matching the globbing */
        for (i = 0; i < globbers.gl_pathc; i++) {
                if (settings_add_include(ctx, globbers.gl_pathv[i],
-                                        ignore_errors, error_r) < 0)
+                                        ignore_errors, &error) < 0) {
+                       ctx->error = p_strdup(ctx->pool, error);
                        return -1;
+               }
        }
        globfree(&globbers);
        return 0;
 #else
-       return settings_add_include(ctx, pattern, ignore_errors, error_r);
+       if (settings_add_include(ctx, pattern, ignore_errors, &error) < 0) {
+               ctx->error = p_strdup(ctx->pool, error);
+               return -1;
+       }
+       return 0;
 #endif
 }
 
-enum config_line_type {
-       CONFIG_LINE_TYPE_SKIP,
-       CONFIG_LINE_TYPE_ERROR,
-       CONFIG_LINE_TYPE_KEYVALUE,
-       CONFIG_LINE_TYPE_KEYFILE,
-       CONFIG_LINE_TYPE_KEYVARIABLE,
-       CONFIG_LINE_TYPE_SECTION_BEGIN,
-       CONFIG_LINE_TYPE_SECTION_END,
-       CONFIG_LINE_TYPE_INCLUDE,
-       CONFIG_LINE_TYPE_INCLUDE_TRY
-};
-
 static enum config_line_type
-config_parse_line(struct parser_context *ctx, char *line, string_t *full_line,
+config_parse_line(struct config_parser_context *ctx,
+                 char *line, string_t *full_line,
                  const char **key_r, const char **value_r)
 {
        const char *key;
@@ -595,7 +559,7 @@ config_parse_line(struct parser_context *ctx, char *line, string_t *full_line,
        return CONFIG_LINE_TYPE_SECTION_BEGIN;
 }
 
-static int config_parse_finish(struct parser_context *ctx, const char **error_r)
+static int config_parse_finish(struct config_parser_context *ctx, const char **error_r)
 {
        struct config_filter_context *new_filter;
        const char *error;
@@ -618,7 +582,7 @@ static int config_parse_finish(struct parser_context *ctx, const char **error_r)
 }
 
 static const void *
-config_get_value(struct parser_context *ctx, const char *key,
+config_get_value(struct config_parser_context *ctx, const char *key,
                 enum setting_type *type_r)
 {
        struct config_module_parser *l;
@@ -632,13 +596,14 @@ config_get_value(struct parser_context *ctx, const char *key,
        return NULL;
 }
 
-static int config_write_value(struct parser_context *ctx,
-                             string_t *str, enum config_line_type type,
-                             const char *key, const char *value,
-                             const char **errormsg_r)
+static int config_write_value(struct config_parser_context *ctx,
+                             enum config_line_type type,
+                             const char *key, const char *value)
 {
+       string_t *str = ctx->str;
        const void *var_name, *var_value, *p;
        enum setting_type var_type;
+       const char *error;
        bool dump;
 
        switch (type) {
@@ -650,8 +615,9 @@ static int config_write_value(struct parser_context *ctx,
                        str_append_c(str, '<');
                        str_append(str, value);
                } else {
-                       if (str_append_file(str, key, value, errormsg_r) < 0) {
+                       if (str_append_file(str, key, value, &error) < 0) {
                                /* file reading failed */
+                               ctx->error = p_strdup(ctx->pool, error);
                                return -1;
                        }
                }
@@ -669,14 +635,16 @@ static int config_write_value(struct parser_context *ctx,
 
                        var_value = config_get_value(ctx, var_name, &var_type);
                        if (var_value == NULL) {
-                               *errormsg_r = t_strconcat("Unknown variable: $",
-                                                         var_name, NULL);
+                               ctx->error = p_strconcat(ctx->pool,
+                                                        "Unknown variable: $",
+                                                        var_name, NULL);
                                return -1;
                        }
                        if (!config_export_type(str, var_value, NULL,
                                                var_type, TRUE, &dump)) {
-                               *errormsg_r = t_strconcat("Invalid variable: $",
-                                                         var_name, NULL);
+                               ctx->error = p_strconcat(ctx->pool,
+                                                        "Invalid variable: $",
+                                                        var_name, NULL);
                                return -1;
                        }
                        if (p != NULL)
@@ -689,15 +657,84 @@ static int config_write_value(struct parser_context *ctx,
        return 0;
 }
 
+void config_parser_apply_line(struct config_parser_context *ctx,
+                             enum config_line_type type,
+                             const char *key, const char *value)
+{
+       const char *section_name;
+
+       str_truncate(ctx->str, ctx->pathlen);
+       switch (type) {
+       case CONFIG_LINE_TYPE_SKIP:
+               break;
+       case CONFIG_LINE_TYPE_ERROR:
+               ctx->error = p_strdup(ctx->pool, value);
+               break;
+       case CONFIG_LINE_TYPE_KEYVALUE:
+       case CONFIG_LINE_TYPE_KEYFILE:
+       case CONFIG_LINE_TYPE_KEYVARIABLE:
+               str_append(ctx->str, key);
+               str_append_c(ctx->str, '=');
+
+               if (config_write_value(ctx, type, key, value) < 0)
+                       break;
+               (void)config_apply_line(ctx, key, str_c(ctx->str), NULL);
+               break;
+       case CONFIG_LINE_TYPE_SECTION_BEGIN:
+               ctx->cur_section = config_add_new_section(ctx);
+               ctx->cur_section->pathlen = ctx->pathlen;
+
+               if (config_filter_add_new_filter(ctx, key, value)) {
+                       /* new filter */
+                       break;
+               }
+
+               /* new config section */
+               if (*value == '\0') {
+                       /* no section name, use a counter */
+                       section_name = dec2str(ctx->section_counter++);
+               } else {
+                       section_name = settings_section_escape(value);
+               }
+               str_append(ctx->str, key);
+               ctx->pathlen = str_len(ctx->str);
+
+               str_append_c(ctx->str, '=');
+               str_append(ctx->str, section_name);
+
+               if (config_apply_line(ctx, key, str_c(ctx->str), value) < 0)
+                       break;
+
+               str_truncate(ctx->str, ctx->pathlen);
+               str_append_c(ctx->str, SETTINGS_SEPARATOR);
+               str_append(ctx->str, section_name);
+               str_append_c(ctx->str, SETTINGS_SEPARATOR);
+               ctx->pathlen = str_len(ctx->str);
+               break;
+       case CONFIG_LINE_TYPE_SECTION_END:
+               if (ctx->cur_section->prev == NULL)
+                       ctx->error = "Unexpected '}'";
+               else {
+                       ctx->pathlen = ctx->cur_section->pathlen;
+                       ctx->cur_section = ctx->cur_section->prev;
+               }
+               break;
+       case CONFIG_LINE_TYPE_INCLUDE:
+       case CONFIG_LINE_TYPE_INCLUDE_TRY:
+               (void)settings_include(ctx, fix_relative_path(value, ctx->cur_input),
+                                      type == CONFIG_LINE_TYPE_INCLUDE_TRY);
+               break;
+       }
+}
+
 int config_parse_file(const char *path, bool expand_values,
                      const char **error_r)
 {
        struct input_stack root;
-       struct parser_context ctx;
-       unsigned int pathlen = 0;
-       unsigned int i, count, counter = 0;
-       const char *errormsg, *key, *value, *section_name;
-       string_t *str, *full_line;
+       struct config_parser_context ctx;
+       unsigned int i, count;
+       const char *key, *value;
+       string_t *full_line;
        enum config_line_type type;
        char *line;
        int fd, ret = 0;
@@ -731,87 +768,27 @@ int config_parse_file(const char *path, bool expand_values,
        ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
        config_add_new_parser(&ctx);
 
-       str = t_str_new(256);
+       ctx.str = str_new(ctx.pool, 256);
        full_line = t_str_new(512);
-       errormsg = NULL;
        ctx.cur_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
        i_stream_set_return_partial_line(ctx.cur_input->input, TRUE);
+       old_settings_init(&ctx);
+
 prevfile:
        while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) {
                ctx.cur_input->linenum++;
                type = config_parse_line(&ctx, line, full_line,
                                         &key, &value);
-               switch (type) {
-               case CONFIG_LINE_TYPE_SKIP:
-                       break;
-               case CONFIG_LINE_TYPE_ERROR:
-                       errormsg = value;
-                       break;
-               case CONFIG_LINE_TYPE_KEYVALUE:
-               case CONFIG_LINE_TYPE_KEYFILE:
-               case CONFIG_LINE_TYPE_KEYVARIABLE:
-                       str_truncate(str, pathlen);
-                       str_append(str, key);
-                       str_append_c(str, '=');
-
-                       if (config_write_value(&ctx, str, type, key, value, &errormsg) < 0)
-                               break;
-                       (void)config_apply_line(&ctx, key, str_c(str), NULL, &errormsg);
-                       break;
-               case CONFIG_LINE_TYPE_SECTION_BEGIN:
-                       ctx.cur_section = config_add_new_section(&ctx);
-                       ctx.cur_section->pathlen = pathlen;
-
-                       if (config_filter_add_new_filter(&ctx, key, value,
-                                                        &errormsg)) {
-                               /* new filter */
-                               break;
-                       }
-
-                       /* new config section */
-                       if (*value == '\0') {
-                               /* no section name, use a counter */
-                               section_name = dec2str(counter++);
-                       } else {
-                               section_name = settings_section_escape(value);
-                       }
-                       str_truncate(str, pathlen);
-                       str_append(str, key);
-                       pathlen = str_len(str);
+               str_truncate(ctx.str, ctx.pathlen);
 
-                       str_append_c(str, '=');
-                       str_append(str, section_name);
-
-                       if (config_apply_line(&ctx, key, str_c(str), value, &errormsg) < 0)
-                               break;
-
-                       str_truncate(str, pathlen);
-                       str_append_c(str, SETTINGS_SEPARATOR);
-                       str_append(str, section_name);
-                       str_append_c(str, SETTINGS_SEPARATOR);
-                       pathlen = str_len(str);
-                       break;
-               case CONFIG_LINE_TYPE_SECTION_END:
-                       if (ctx.cur_section->prev == NULL)
-                               errormsg = "Unexpected '}'";
-                       else {
-                               pathlen = ctx.cur_section->pathlen;
-                               ctx.cur_section = ctx.cur_section->prev;
-                       }
-                       break;
-               case CONFIG_LINE_TYPE_INCLUDE:
-               case CONFIG_LINE_TYPE_INCLUDE_TRY:
-                       (void)settings_include(&ctx, fix_relative_path(value, ctx.cur_input),
-                                              type == CONFIG_LINE_TYPE_INCLUDE_TRY,
-                                              &errormsg);
-                       break;
-               }
+               if (!old_settings_handle(&ctx, type, key, value))
+                       config_parser_apply_line(&ctx, type, key, value);
 
-               if (errormsg != NULL) {
+               if (ctx.error != NULL) {
                        *error_r = t_strdup_printf(
                                "Error in configuration file %s line %d: %s",
                                ctx.cur_input->path, ctx.cur_input->linenum,
-                               errormsg);
+                               ctx.error);
                        ret = -2;
                        break;
                }
diff --git a/src/config/old-set-parser.c b/src/config/old-set-parser.c
new file mode 100644 (file)
index 0000000..b6cda5a
--- /dev/null
@@ -0,0 +1,529 @@
+/* Copyright (c) 2009-2010 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "settings-parser.h"
+#include "config-parser-private.h"
+#include "old-set-parser.h"
+
+struct socket_set {
+       const char *path, *mode, *user, *group;
+       bool master;
+};
+
+struct old_set_parser {
+       const char *base_dir;
+       /* 1 when in auth {} section, >1 when inside auth { .. { .. } } */
+       unsigned int auth_section;
+       /* 1 when in socket listen {}, >1 when inside more of its sections */
+       unsigned int socket_listen_section;
+       bool seen_auth_section;
+       struct socket_set socket_set;
+};
+
+static const struct config_filter any_filter = {
+       .service = NULL
+};
+
+static const struct config_filter imap_filter = {
+       .service = "imap"
+};
+static const struct config_filter pop3_filter = {
+       .service = "pop3"
+};
+static const struct config_filter managesieve_filter = {
+       .service = "managesieve"
+};
+
+static void ATTR_FORMAT(2, 3)
+obsolete(struct config_parser_context *ctx, const char *str, ...)
+{
+       va_list args;
+
+       va_start(args, str);
+       i_warning("Obsolete setting in %s:%u: %s",
+                 ctx->cur_input->path, ctx->cur_input->linenum,
+                 t_strdup_vprintf(str, args));
+       va_end(args);
+}
+
+static void set_rename(struct config_parser_context *ctx,
+                      const char *old_key, const char *key, const char *value)
+{
+       obsolete(ctx, "%s has been renamed to %s", old_key, key);
+       config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE, key, value);
+}
+
+static bool
+old_settings_handle_root(struct config_parser_context *ctx,
+                        const char *key, const char *value)
+{
+       const char *p;
+
+       if (strcmp(key, "base_dir") == 0)
+               ctx->old->base_dir = p_strdup(ctx->pool, value);
+       if (strcmp(key, "protocols") == 0) {
+               char **protos, **s;
+               bool have_imap = FALSE, have_imaps = FALSE;
+               bool have_pop3 = FALSE, have_pop3s = FALSE;
+
+               protos = p_strsplit_spaces(pool_datastack_create(), value, " ");
+               for (s = protos; *s != NULL; s++) {
+                       if (strcmp(*s, "imap") == 0)
+                               have_imap = TRUE;
+                       else if (strcmp(*s, "imaps") == 0) {
+                               *s = "";
+                               have_imaps = TRUE;
+                       } else if (strcmp(*s, "pop3") == 0)
+                               have_pop3 = TRUE;
+                       else if (strcmp(*s, "pop3s") == 0) {
+                               *s = "";
+                               have_pop3s = TRUE;
+                       }
+               }
+               value = t_strarray_join((const char *const *)protos, " ");
+               /* ugly way to drop extra spaces.. */
+               protos = p_strsplit_spaces(pool_datastack_create(), value, " ");
+               value = t_strarray_join((const char *const *)protos, " ");
+
+               if (have_imaps && !have_imap) {
+                       obsolete(ctx, "protocols=imaps is no longer supported. to disable non-ssl imap, use service imap-login { inet_listener imap { port=0 } }");
+                       value = t_strconcat(value, " imap", NULL);
+                       config_apply_line(ctx, "port",
+                               "service/imap-login/inet_listener/imap/port=0", NULL);
+               } else if (have_imaps)
+                       obsolete(ctx, "protocols=imaps is no longer necessary, remove it");
+               if (have_pop3s && !have_pop3) {
+                       obsolete(ctx, "protocols=pop3s is no longer supported. to disable non-ssl pop3, use service pop3-login { inet_listener pop3 { port=0 } }");
+                       value = t_strconcat(value, " pop3", NULL);
+                       config_apply_line(ctx, "port",
+                               "service/pop3-login/inet_listener/pop3/port=0", NULL);
+               } else if (have_pop3s)
+                       obsolete(ctx, "protocols=pop3s is no longer necessary, remove it");
+
+               if (*value == ' ') value++;
+               config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+                                        key, value);
+               return TRUE;
+       }
+       if (strcmp(key, "ssl_cert_file") == 0 ||
+           strcmp(key, "ssl_key_file") == 0 ||
+           strcmp(key, "ssl_ca_file") == 0) {
+               if (*value == '\0')
+                       return TRUE;
+               p = t_strdup_until(key, strrchr(key, '_'));
+               obsolete(ctx, "%s has been replaced by %s = <file", key, p);
+               config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYFILE,
+                                        p, value);
+               return TRUE;
+       }
+       if (strcmp(key, "dbox_rotate_size") == 0) {
+               set_rename(ctx, key, "mdbox_rotate_size", value);
+               return TRUE;
+       }
+       if (strcmp(key, "dbox_rotate_days") == 0) {
+               set_rename(ctx, key, "mdbox_rotate_interval",
+                          t_strconcat(value, "d", NULL));
+               return TRUE;
+       }
+
+       if (strcmp(key, "login_dir") == 0 ||
+           strcmp(key, "dbox_rotate_min_size") == 0 ||
+           strcmp(key, "mail_log_max_lines_per_sec") == 0 ||
+           strcmp(key, "maildir_copy_preserve_filename") == 0) {
+               obsolete(ctx, "%s has been removed", key);
+               return TRUE;
+       }
+       if (ctx->old->auth_section == 1) {
+               if (strncmp(key, "auth_", 5) != 0)
+                       key = t_strconcat("auth_", key, NULL);
+               config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+                                        key, value);
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static void
+config_apply_login_set(struct config_parser_context *ctx,
+                      struct config_section_stack *old_section,
+                      const char *old_key, const char *key, const char *value)
+{
+       obsolete(ctx, "%s has been replaced by service { %s }", old_key, key);
+
+       if (config_filter_match(&old_section->filter, &imap_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/imap-login/%s=%s", key, value), NULL);
+       }
+       if (config_filter_match(&old_section->filter, &pop3_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/pop3-login/%s=%s", key, value), NULL);
+       }
+       if (config_filter_match(&old_section->filter, &managesieve_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/managesieve-login/%s=%s", key, value), NULL);
+       }
+}
+
+static void
+config_apply_mail_set(struct config_parser_context *ctx,
+                     struct config_section_stack *old_section,
+                     const char *old_key, const char *key, const char *value)
+{
+       obsolete(ctx, "%s has been replaced by service { %s }", old_key, key);
+
+       if (config_filter_match(&old_section->filter, &imap_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/imap/%s=%s", key,value), NULL);
+       }
+       if (config_filter_match(&old_section->filter, &pop3_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/pop3/%s=%s", key,value), NULL);
+       }
+       if (config_filter_match(&old_section->filter, &managesieve_filter)) {
+               config_apply_line(ctx, key,
+                       t_strdup_printf("service/managesieve/%s=%s", key,value), NULL);
+       }
+}
+
+static void
+config_apply_auth_set(struct config_parser_context *ctx,
+                     const char *old_key, const char *key, const char *value)
+{
+       obsolete(ctx, "%s has been replaced by service auth { %s }", old_key, key);
+       config_apply_line(ctx, key,
+               t_strdup_printf("service/auth/%s=%s", key,value), NULL);
+}
+
+static bool
+old_settings_handle_proto(struct config_parser_context *ctx,
+                         const char *key, const char *value)
+{
+       struct config_section_stack *old_section = ctx->cur_section;
+       const char *p;
+       bool root;
+
+       while (ctx->cur_section->prev != NULL)
+               ctx->cur_section = ctx->cur_section->prev;
+
+       root = config_filter_match(&old_section->filter, &any_filter);
+
+       if (strcmp(key, "ssl_listen") == 0 ||
+           (strcmp(key, "listen") == 0 &&
+            (strstr(value, ":") != NULL || !root))) {
+               const char *ssl = strcmp(key, "ssl_listen") == 0 ? "s" : "";
+
+               if (*value == '\0') {
+                       /* default */
+                       return TRUE;
+               }
+               p = strstr(value, ":");
+               if (p != NULL) {
+                       obsolete(ctx, "%s=..:port has been replaced by service { inet_listener { port } }", key);
+                       value = t_strdup_until(value, p++);
+                       if (config_filter_match(&old_section->filter, &imap_filter)) {
+                               config_apply_line(ctx, "port",
+                                       t_strdup_printf("service/imap-login/inet_listener/imap%s/port=%s", ssl, p), NULL);
+                       }
+                       if (config_filter_match(&old_section->filter, &pop3_filter)) {
+                               config_apply_line(ctx, "port",
+                                       t_strdup_printf("service/pop3-login/inet_listener/pop3%s/port=%s", ssl, p), NULL);
+                       }
+                       if (*ssl == '\0' &&
+                           config_filter_match(&old_section->filter, &managesieve_filter)) {
+                               config_apply_line(ctx, "port",
+                                       t_strdup_printf("service/managesieve-login/inet_listener/managesieve/port=%s", p), NULL);
+                       }
+               }
+               if (root && *ssl == '\0') {
+                       config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+                                                key, value);
+               } else {
+                       obsolete(ctx, "protocol { %s } has been replaced by service { inet_listener { address } }", key);
+                       if (config_filter_match(&old_section->filter, &imap_filter)) {
+                               config_apply_line(ctx, "address",
+                                       t_strdup_printf("service/imap-login/inet_listener/imap%s/address=%s", ssl, value), NULL);
+                       }
+                       if (config_filter_match(&old_section->filter, &pop3_filter)) {
+                               config_apply_line(ctx, "address",
+                                       t_strdup_printf("service/pop3-login/inet_listener/pop3%s/address=%s", ssl, value), NULL);
+                       }
+                       if (*ssl == '\0' &&
+                           config_filter_match(&old_section->filter, &managesieve_filter)) {
+                               config_apply_line(ctx, "address",
+                                       t_strdup_printf("service/managesieve-login/inet_listener/managesieve/address=%s", value), NULL);
+                       }
+               }
+               return TRUE;
+       }
+       if (strcmp(key, "login_chroot") == 0) {
+               if (strcmp(value, "no") == 0)
+                       value = "";
+               else
+                       value = "login";
+
+               config_apply_login_set(ctx, old_section, key, "chroot", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_user") == 0) {
+               config_apply_login_set(ctx, old_section, key, "user", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_executable") == 0) {
+               config_apply_login_set(ctx, old_section, key, "executable", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_process_size") == 0) {
+               config_apply_login_set(ctx, old_section, key, "vsz_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_process_per_connection") == 0) {
+               config_apply_login_set(ctx, old_section, key, "service_count",
+                                      strcmp(value, "no") == 0 ? "0" : "1");
+               return TRUE;
+       }
+       if (strcmp(key, "login_processes_count") == 0) {
+               config_apply_login_set(ctx, old_section, key, "process_min_avail", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_max_processes_count") == 0) {
+               config_apply_login_set(ctx, old_section, key, "process_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_max_connections") == 0) {
+               config_apply_login_set(ctx, old_section, key, "client_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "login_process_size") == 0) {
+               config_apply_login_set(ctx, old_section, key, "vsz_limit", value);
+               return TRUE;
+       }
+
+       if (strcmp(key, "max_mail_processes") == 0) {
+               config_apply_mail_set(ctx, old_section, key, "process_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "mail_executable") == 0) {
+               config_apply_mail_set(ctx, old_section, key, "executable", value);
+               return TRUE;
+       }
+       if (strcmp(key, "mail_process_size") == 0) {
+               config_apply_mail_set(ctx, old_section, key, "vsz_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "mail_drop_priv_before_exec") == 0) {
+               config_apply_mail_set(ctx, old_section, key, "drop_priv_before_exec", value);
+               return TRUE;
+       }
+
+       if (ctx->old->auth_section == 1) {
+               if (strncmp(key, "auth_", 5) != 0)
+                       key = t_strconcat("auth_", key, NULL);
+       }
+
+       if (strcmp(key, "auth_executable") == 0) {
+               config_apply_auth_set(ctx, key, "executable", value);
+               return TRUE;
+       }
+       if (strcmp(key, "auth_process_size") == 0) {
+               config_apply_auth_set(ctx, key, "vsz_limit", value);
+               return TRUE;
+       }
+       if (strcmp(key, "auth_user") == 0) {
+               config_apply_auth_set(ctx, key, "user", value);
+               return TRUE;
+       }
+       if (strcmp(key, "auth_chroot") == 0) {
+               config_apply_auth_set(ctx, key, "chroot", value);
+               return TRUE;
+       }
+       if (strcmp(key, "auth_count") == 0) {
+               if (strcmp(value, "count") == 0)
+                       obsolete(ctx, "auth_count has been removed");
+               else
+                       obsolete(ctx, "auth_count has been removed, and its value must be 1");
+               return TRUE;
+       }
+       if (ctx->old->socket_listen_section == 2) {
+               const char **p = NULL;
+
+               if (strcmp(key, "path") == 0)
+                       p = &ctx->old->socket_set.path;
+               else if (strcmp(key, "mode") == 0)
+                       p = &ctx->old->socket_set.mode;
+               else if (strcmp(key, "user") == 0)
+                       p = &ctx->old->socket_set.user;
+               else if (strcmp(key, "group") == 0)
+                       p = &ctx->old->socket_set.group;
+
+               if (p != NULL) {
+                       *p = p_strdup(ctx->pool, value);
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
+static bool old_auth_section(struct config_parser_context *ctx,
+                            const char *key, const char *value)
+{
+       if (ctx->old->auth_section == 0 && ctx->old->seen_auth_section) {
+               obsolete(ctx, "Multiple auth sections are no longer supported");
+               return FALSE;
+       }
+       ctx->old->seen_auth_section = TRUE;
+       memset(&ctx->old->socket_set, 0, sizeof(ctx->old->socket_set));
+
+       ctx->old->auth_section++;
+       if ((strcmp(key, "passdb") == 0 || strcmp(key, "userdb") == 0) &&
+           ctx->old->auth_section == 2) {
+               obsolete(ctx, "%s %s {} has been replaced by %s { driver=%s }",
+                        key, value, key, value);
+               config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, key, "");
+               config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+                                        "driver", value);
+               return TRUE;
+       }
+       if (strcmp(key, "socket") == 0 && ctx->old->auth_section == 2) {
+               if (strcmp(value, "connect") == 0) {
+                       obsolete(ctx, "socket connect {} is no longer supported, configure external auth server separately");
+                       return FALSE;
+               }
+               if (strcmp(value, "listen") != 0)
+                       return FALSE;
+
+               /* socket listen { .. } */
+               ctx->old->socket_listen_section++;
+               return TRUE;
+       }
+
+       if (ctx->old->socket_listen_section > 0)
+               ctx->old->socket_listen_section++;
+       if ((strcmp(key, "master") == 0 || strcmp(key, "client") == 0) &&
+           ctx->old->socket_listen_section == 2) {
+               ctx->old->socket_set.master = strcmp(key, "master") == 0;
+               return TRUE;
+       }
+       return FALSE;
+}
+
+static bool old_namespace(struct config_parser_context *ctx,
+                         const char *value)
+{
+       if (strcmp(value, "private") != 0 &&
+           strcmp(value, "shared") != 0 &&
+           strcmp(value, "public") != 0)
+               return FALSE;
+
+       obsolete(ctx, "namespace %s {} has been replaced by namespace { type=%s }", value, value);
+       config_parser_apply_line(ctx, CONFIG_LINE_TYPE_SECTION_BEGIN, "namespace", "");
+       config_parser_apply_line(ctx, CONFIG_LINE_TYPE_KEYVALUE,
+                                "type", value);
+       return TRUE;
+}
+
+static void socket_apply(struct config_parser_context *ctx)
+{
+       const struct socket_set *set = &ctx->old->socket_set;
+       const char *path, *prefix;
+       unsigned int len;
+       bool master_suffix;
+
+       if (set->path == NULL) {
+               ctx->error = "socket listen {} is missing path";
+               return;
+       }
+       path = set->path;
+       len = strlen(ctx->old->base_dir);
+       if (strncmp(path, ctx->old->base_dir, len) == 0 &&
+           path[len] == '/')
+               path += len + 1;
+
+       len = strlen(path);
+       master_suffix = len >= 7 &&
+               (strcmp(path + len - 7, "-master") == 0 ||
+                strcmp(path + len - 7, "-userdb") == 0);
+
+       if (set->master && !master_suffix) {
+               ctx->error = "socket listen { master { path=.. } } must end with -master (or -userdb) suffix";
+               return;
+       } else if (!set->master && master_suffix) {
+               ctx->error = "socket listen { client { path=.. } } must end not with -master or -userdb suffix";
+               return;
+       }
+
+       config_apply_line(ctx, "unix_listener",
+               t_strdup_printf("service/auth/unix_listener=%s", settings_section_escape(path)), path);
+       prefix = t_strdup_printf("service/auth/unix_listener/%s", settings_section_escape(path));
+       if (set->mode != NULL) {
+               config_apply_line(ctx, "mode",
+                         t_strdup_printf("%s/mode=%s", prefix, set->mode), NULL);
+       }
+       if (set->user != NULL) {
+               config_apply_line(ctx, "user",
+                         t_strdup_printf("%s/user=%s", prefix, set->user), NULL);
+       }
+       if (set->group != NULL) {
+               config_apply_line(ctx, "group",
+                         t_strdup_printf("%s/group=%s", prefix, set->group), NULL);
+       }
+       memset(&ctx->old->socket_set, 0, sizeof(ctx->old->socket_set));
+}
+
+bool old_settings_handle(struct config_parser_context *ctx,
+                        enum config_line_type type,
+                        const char *key, const char *value)
+{
+       switch (type) {
+       case CONFIG_LINE_TYPE_SKIP:
+       case CONFIG_LINE_TYPE_ERROR:
+       case CONFIG_LINE_TYPE_INCLUDE:
+       case CONFIG_LINE_TYPE_INCLUDE_TRY:
+       case CONFIG_LINE_TYPE_KEYFILE:
+       case CONFIG_LINE_TYPE_KEYVARIABLE:
+               break;
+       case CONFIG_LINE_TYPE_KEYVALUE:
+               if (ctx->pathlen == 0) {
+                       struct config_section_stack *old_section =
+                               ctx->cur_section;
+                       bool ret;
+
+                       ret = old_settings_handle_proto(ctx, key, value);
+                       ctx->cur_section = old_section;
+                       if (ret)
+                               return TRUE;
+
+                       return old_settings_handle_root(ctx, key, value);
+               }
+               break;
+       case CONFIG_LINE_TYPE_SECTION_BEGIN:
+               if (ctx->old->auth_section > 0)
+                       return old_auth_section(ctx, key, value);
+               else if (ctx->pathlen == 0 && strcmp(key, "auth") == 0) {
+                       obsolete(ctx, "add auth_ prefix to all settings inside auth {} and remove the auth {} section completely");
+                       ctx->old->auth_section = 1;
+                       return TRUE;
+               } else if (ctx->pathlen == 0 && strcmp(key, "namespace") == 0)
+                       return old_namespace(ctx, value);
+               break;
+       case CONFIG_LINE_TYPE_SECTION_END:
+               if (ctx->old->auth_section > 0) {
+                       if (--ctx->old->auth_section == 0)
+                               return TRUE;
+               }
+               if (ctx->old->socket_listen_section > 0) {
+                       if (ctx->old->socket_listen_section == 2)
+                               socket_apply(ctx);
+                       ctx->old->socket_listen_section--;
+                       return TRUE;
+               }
+               break;
+       }
+       return FALSE;
+}
+
+void old_settings_init(struct config_parser_context *ctx)
+{
+       ctx->old = p_new(ctx->pool, struct old_set_parser, 1);
+       ctx->old->base_dir = PKG_RUNDIR;
+}
diff --git a/src/config/old-set-parser.h b/src/config/old-set-parser.h
new file mode 100644 (file)
index 0000000..2fc307f
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef OLD_SET_PARSER_H
+#define OLD_SET_PARSER_H
+
+enum config_line_type;
+struct config_parser_context;
+
+bool old_settings_handle(struct config_parser_context *ctx,
+                        enum config_line_type type,
+                        const char *key, const char *value);
+void old_settings_init(struct config_parser_context *ctx);
+
+#endif