#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;
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;
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;
}
}
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;
}
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;
}
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;
}
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)
{
}
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;
}
}
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)
{
}
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)
{
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;
}
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;
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;
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;
}
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;
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) {
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;
}
}
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)
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;
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;
}
--- /dev/null
+/* 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;
+}