From b28a1c61a5d262fd16b46bebe47dbfb90ac9c5fc Mon Sep 17 00:00:00 2001 From: Timo Sirainen Date: Tue, 9 Mar 2010 00:34:22 +0200 Subject: [PATCH] config: Added support for reading v1.2 config files. --HG-- branch : HEAD --- src/config/Makefile.am | 3 + src/config/config-parser.c | 305 ++++++++++----------- src/config/old-set-parser.c | 529 ++++++++++++++++++++++++++++++++++++ src/config/old-set-parser.h | 12 + 4 files changed, 685 insertions(+), 164 deletions(-) create mode 100644 src/config/old-set-parser.c create mode 100644 src/config/old-set-parser.h diff --git a/src/config/Makefile.am b/src/config/Makefile.am index 80ef9c84b9..6f802fcc9e 100644 --- a/src/config/Makefile.am +++ b/src/config/Makefile.am @@ -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 diff --git a/src/config/config-parser.c b/src/config/config-parser.c index c89ec49d90..e0c0196f4e 100644 --- a/src/config/config-parser.c +++ b/src/config/config-parser.c @@ -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 #include @@ -28,36 +28,6 @@ #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 index 0000000000..b6cda5a556 --- /dev/null +++ b/src/config/old-set-parser.c @@ -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 = 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 index 0000000000..2fc307f169 --- /dev/null +++ b/src/config/old-set-parser.h @@ -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 -- 2.47.3