--- /dev/null
+/* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "config-filter.h"
+
+struct config_filter_context {
+ pool_t pool;
+ struct config_filter_parser_list *const *parsers;
+};
+
+bool config_filter_match(const struct config_filter *mask,
+ const struct config_filter *filter)
+{
+ if (mask->service != NULL) {
+ if (filter->service == NULL)
+ return FALSE;
+ if (strcasecmp(filter->service, mask->service) != 0)
+ return FALSE;
+ }
+ /* FIXME: it's not comparing full masks */
+ if (mask->remote_bits != 0) {
+ if (filter->remote_bits == 0)
+ return FALSE;
+ if (!net_is_in_network(&filter->remote_net, &mask->remote_net,
+ mask->remote_bits))
+ return FALSE;
+ }
+ if (mask->local_bits != 0) {
+ if (filter->local_bits == 0)
+ return FALSE;
+ if (!net_is_in_network(&filter->local_net, &mask->local_net,
+ mask->local_bits))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+bool config_filters_equal(const struct config_filter *f1,
+ const struct config_filter *f2)
+{
+ if (null_strcmp(f1->service, f2->service) != 0)
+ return FALSE;
+
+ if (f1->remote_bits != f2->remote_bits)
+ return FALSE;
+ if (!net_ip_compare(&f1->remote_net, &f2->remote_net))
+ return FALSE;
+
+ if (f1->local_bits != f2->local_bits)
+ return FALSE;
+ if (!net_ip_compare(&f1->local_net, &f2->local_net))
+ return FALSE;
+
+ return TRUE;
+}
+
+struct config_filter_context *config_filter_init(pool_t pool)
+{
+ struct config_filter_context *ctx;
+
+ ctx = p_new(pool, struct config_filter_context, 1);
+ ctx->pool = pool;
+ return ctx;
+}
+
+void config_filter_deinit(struct config_filter_context **_ctx)
+{
+ struct config_filter_context *ctx = *_ctx;
+
+ *_ctx = NULL;
+
+ pool_unref(&ctx->pool);
+}
+
+void config_filter_add_all(struct config_filter_context *ctx,
+ struct config_filter_parser_list *const *parsers)
+{
+ ctx->parsers = parsers;
+}
+
+static int filter_cmp(const struct config_filter *f1,
+ const struct config_filter *f2)
+{
+ int ret;
+
+ ret = f2->remote_bits - f1->remote_bits;
+ if (ret != 0)
+ return ret;
+
+ ret = f2->local_bits - f1->local_bits;
+ if (ret != 0)
+ return ret;
+
+ if (f1->service != NULL)
+ return -1;
+ else
+ return 1;
+}
+
+const struct config_setting_parser_list *
+config_filter_match_parsers(struct config_filter_context *ctx,
+ const struct config_filter *filter)
+{
+ struct config_filter_parser_list *best = NULL;
+ unsigned int i;
+
+ /* find the filter that best matches what we have.
+ FIXME: this can't really work. we'd want to merge changes from
+ different matches.. requires something larger after all. */
+ for (i = 0; ctx->parsers[i] != NULL; i++) {
+ if (!config_filter_match(&ctx->parsers[i]->filter, filter))
+ continue;
+
+ if (best == NULL ||
+ filter_cmp(&best->filter, &ctx->parsers[i]->filter) > 0)
+ best = ctx->parsers[i];
+ }
+ return best == NULL ? NULL : best->parser_list;
+}
#include "strescape.h"
#include "istream.h"
#include "settings-parser.h"
-#include "all-settings.h"
+#include "config-filter.h"
#include "config-parser.h"
#include <unistd.h>
#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
+struct config_filter_stack {
+ struct config_filter_stack *prev;
+ struct config_filter filter;
+ unsigned int pathlen;
+};
+
struct input_stack {
struct input_stack *prev;
unsigned int linenum;
};
+struct parser_context {
+ pool_t pool;
+ const char *path;
+
+ ARRAY_DEFINE(all_parsers, struct config_filter_parser_list *);
+ /* parsers matching cur_filter */
+ ARRAY_TYPE(config_setting_parsers) cur_parsers;
+ struct config_filter_stack *cur_filter;
+ struct input_stack *cur_input;
+
+ struct config_filter_context *filter;
+};
+
+struct config_filter_context *config_filter;
+
static const char *info_type_name_find(const struct setting_parser_info *info)
{
unsigned int i;
}
static const char *
-config_parse_line(const char *key, const char *line, const char *section_name)
+config_parsers_parse_line(struct config_setting_parser_list *parsers,
+ const char *key, const char *line,
+ const char *section_name)
{
struct config_setting_parser_list *l;
bool found = FALSE;
int ret;
- for (l = config_setting_parsers; l->module_name != NULL; l++) {
+ for (l = parsers; l->module_name != NULL; l++) {
ret = settings_parse_line(l->parser, line);
if (ret > 0) {
found = TRUE;
return found ? NULL : t_strconcat("Unknown setting: ", key, NULL);
}
+static const char *
+config_parse_line(struct config_setting_parser_list *const *all_parsers,
+ const char *key, const char *line, const char *section_name)
+{
+ const char *ret;
+
+ for (; *all_parsers != NULL; all_parsers++) {
+ ret = config_parsers_parse_line(*all_parsers, key, line,
+ section_name);
+ if (ret != NULL)
+ return ret;
+ }
+ return NULL;
+}
+
static const char *
fix_relative_path(const char *path, struct input_stack *input)
{
return t_strconcat(t_strdup_until(input->path, p+1), path, NULL);
}
+static struct config_setting_parser_list *
+config_setting_parser_list_dup(pool_t pool,
+ const struct config_setting_parser_list *src)
+{
+ struct config_setting_parser_list *dest;
+ unsigned int i, count;
+
+ for (count = 0; src[count].module_name != NULL; count++) ;
+
+ dest = p_new(pool, struct config_setting_parser_list, count + 1);
+ for (i = 0; i < count; i++) {
+ dest[i] = src[i];
+ dest[i].parser = settings_parser_dup(src[i].parser, pool);
+ }
+ return dest;
+}
+
+static struct config_filter_parser_list *
+config_add_new_parser(struct parser_context *ctx)
+{
+ struct config_filter_parser_list *parser;
+ struct config_setting_parser_list *const *cur_parsers;
+ unsigned int count;
+
+ parser = p_new(ctx->pool, struct config_filter_parser_list, 1);
+ parser->filter = ctx->cur_filter->filter;
+
+ cur_parsers = array_get(&ctx->cur_parsers, &count);
+ if (count == 0) {
+ /* first one */
+ parser->parser_list = config_setting_parsers;
+ } else {
+ /* duplicate the first settings list */
+ parser->parser_list =
+ config_setting_parser_list_dup(ctx->pool,
+ cur_parsers[0]);
+ }
+
+ array_append(&ctx->all_parsers, &parser, 1);
+ return parser;
+}
+
+static void config_add_new_filter(struct parser_context *ctx)
+{
+ struct config_filter_stack *filter;
+
+ filter = p_new(ctx->pool, struct config_filter_stack, 1);
+ filter->prev = ctx->cur_filter;
+ filter->filter = ctx->cur_filter->filter;
+ ctx->cur_filter = filter;
+}
+
+static struct config_setting_parser_list *const *
+config_update_cur_parsers(struct parser_context *ctx)
+{
+ struct config_filter_parser_list *const *all_parsers;
+ unsigned int i, count;
+ bool full_found = FALSE;
+
+ array_clear(&ctx->cur_parsers);
+
+ all_parsers = array_get(&ctx->all_parsers, &count);
+ for (i = 0; i < count; i++) {
+ if (!config_filter_match(&ctx->cur_filter->filter,
+ &all_parsers[i]->filter))
+ continue;
+
+ if (config_filters_equal(&all_parsers[i]->filter,
+ &ctx->cur_filter->filter)) {
+ array_insert(&ctx->cur_parsers, 0,
+ &all_parsers[i]->parser_list, 1);
+ full_found = TRUE;
+ } else {
+ array_append(&ctx->cur_parsers,
+ &all_parsers[i]->parser_list, 1);
+ }
+ }
+ i_assert(full_found);
+ (void)array_append_space(&ctx->cur_parsers);
+ return array_idx(&ctx->cur_parsers, 0);
+}
+
+static void
+config_filter_parser_list_check(struct parser_context *ctx,
+ struct config_filter_parser_list *parser)
+{
+ struct config_setting_parser_list *l = parser->parser_list;
+ const char *errormsg;
+
+ for (; l->module_name != NULL; l++) {
+ if (!settings_parser_check(l->parser, ctx->pool, &errormsg)) {
+ i_fatal("Error in configuration file %s: %s",
+ ctx->path, errormsg);
+ }
+ }
+}
+
+static void
+config_all_parsers_check(struct parser_context *ctx)
+{
+ struct config_filter_parser_list *const *parsers;
+ unsigned int i, count;
+
+ parsers = array_get(&ctx->all_parsers, &count);
+ for (i = 0; i < count; i++)
+ config_filter_parser_list_check(ctx, parsers[i]);
+}
+
void config_parse_file(const char *path)
{
enum settings_parser_flags parser_flags =
SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS;
- struct input_stack root, *input, *new_input;
- ARRAY_DEFINE(pathlen_stack, unsigned int);
+ struct input_stack root, *new_input;
ARRAY_TYPE(const_string) auth_defaults;
- struct config_setting_parser_list *l;
+ struct config_setting_parser_list *l, *const *parsers;
+ struct parser_context ctx;
unsigned int pathlen = 0;
unsigned int counter = 0, auth_counter = 0, cur_counter;
const char *errormsg, *name;
int fd, ret;
string_t *str, *full_line;
size_t len;
- pool_t pool;
- pool = pool_alloconly_create("config file parser", 10240);
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.pool = pool_alloconly_create("config file parser", 10240);
+ ctx.path = path;
fd = open(path, O_RDONLY);
if (fd < 0)
i_fatal("open(%s) failed: %m", path);
- t_array_init(&pathlen_stack, 10);
t_array_init(&auth_defaults, 32);
for (l = config_setting_parsers; l->module_name != NULL; l++) {
i_assert(l->parser == NULL);
- l->parser = settings_parser_init(pool, l->root, parser_flags);
+ l->parser = settings_parser_init(ctx.pool, l->root, parser_flags);
}
- errormsg = config_parse_line("0", "auth=0", NULL);
+ t_array_init(&ctx.cur_parsers, 128);
+ p_array_init(&ctx.all_parsers, ctx.pool, 128);
+ ctx.cur_filter = p_new(ctx.pool, struct config_filter_stack, 1);
+ config_add_new_parser(&ctx);
+ parsers = config_update_cur_parsers(&ctx);
+
+ errormsg = config_parse_line(parsers, "0", "auth=0", NULL);
+ i_assert(errormsg == NULL);
+ errormsg = config_parse_line(parsers, "name", "auth/0/name=default", NULL);
i_assert(errormsg == NULL);
memset(&root, 0, sizeof(root));
root.path = path;
- input = &root;
+ ctx.cur_input = &root;
str = t_str_new(256);
full_line = t_str_new(512);
errormsg = NULL;
newfile:
- input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
- i_stream_set_return_partial_line(input->input, TRUE);
+ ctx.cur_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
+ i_stream_set_return_partial_line(ctx.cur_input->input, TRUE);
prevfile:
- while ((line = i_stream_read_next_line(input->input)) != NULL) {
- input->linenum++;
+ while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) {
+ ctx.cur_input->linenum++;
/* @UNSAFE: line is modified */
struct input_stack *tmp;
const char *path;
- path = fix_relative_path(line, input);
- for (tmp = input; tmp != NULL; tmp = tmp->prev) {
+ path = fix_relative_path(line, ctx.cur_input);
+ for (tmp = ctx.cur_input; tmp != NULL; tmp = tmp->prev) {
if (strcmp(tmp->path, path) == 0)
break;
}
errormsg = "Recursive include";
} else if ((fd = open(path, O_RDONLY)) != -1) {
new_input = t_new(struct input_stack, 1);
- new_input->prev = input;
+ new_input->prev = ctx.cur_input;
new_input->path = t_strdup(path);
- input = new_input;
+ ctx.cur_input = new_input;
goto newfile;
} else {
/* failed, but ignore failures with include_try. */
str_truncate(str, 0);
str_printfa(str, "auth/0/%s=%s", key + 5, line);
- errormsg = config_parse_line(key + 5, str_c(str), NULL);
+ errormsg = config_parse_line(parsers, key + 5, str_c(str), NULL);
array_append(&auth_defaults, &s, 1);
} else {
- errormsg = config_parse_line(key, str_c(str), NULL);
+ errormsg = config_parse_line(parsers, key, str_c(str), NULL);
}
} else if (strcmp(key, "}") != 0 || *line != '\0') {
/* b) + errors */
if (*line != '{')
errormsg = "Expecting '='";
+
+ config_add_new_filter(&ctx);
+ ctx.cur_filter->pathlen = pathlen;
if (strcmp(key, "protocol") == 0) {
- array_append(&pathlen_stack, &pathlen, 1);
+ ctx.cur_filter->filter.service =
+ p_strdup(ctx.pool, name);
+ config_add_new_parser(&ctx);
+ parsers = config_update_cur_parsers(&ctx);
+ } else if (strcmp(key, "local_ip") == 0) {
+ if (net_parse_range(name, &ctx.cur_filter->filter.local_net,
+ &ctx.cur_filter->filter.local_bits) < 0)
+ errormsg = "Invalid network mask";
+ config_add_new_parser(&ctx);
+ parsers = config_update_cur_parsers(&ctx);
+ } else if (strcmp(key, "remote_ip") == 0) {
+ if (net_parse_range(name, &ctx.cur_filter->filter.remote_net,
+ &ctx.cur_filter->filter.remote_bits) < 0)
+ errormsg = "Invalid network mask";
+ config_add_new_parser(&ctx);
+ parsers = config_update_cur_parsers(&ctx);
} else {
- array_append(&pathlen_stack, &pathlen, 1);
-
str_truncate(str, pathlen);
str_append(str, key);
pathlen = str_len(str);
- if (strcmp(key, "auth") == 0)
+ if (strcmp(key, "auth") == 0) {
cur_counter = auth_counter++;
- else
+ if (cur_counter == 0 && strcmp(name, "default") != 0)
+ cur_counter = auth_counter++;
+ } else {
cur_counter = counter++;
+ }
str_append_c(str, '=');
str_printfa(str, "%u", cur_counter);
+
if (cur_counter == 0 && strcmp(key, "auth") == 0) {
/* already added this */
} else {
- errormsg = config_parse_line(key, str_c(str), name);
+ errormsg = config_parse_line(parsers, key, str_c(str), name);
}
str_truncate(str, pathlen);
p = strchr(lines[i], '=');
str_append(str, lines[i]);
- errormsg = config_parse_line(t_strdup_until(lines[i], p), str_c(str), NULL);
+ errormsg = config_parse_line(parsers, t_strdup_until(lines[i], p), str_c(str), NULL);
i_assert(errormsg == NULL);
}
}
}
} else {
/* c) */
- unsigned int pathlen_count;
- const unsigned int *arr;
-
- arr = array_get(&pathlen_stack, &pathlen_count);
- if (pathlen_count == 0)
+ if (ctx.cur_filter->prev == NULL)
errormsg = "Unexpected '}'";
else {
- pathlen = arr[pathlen_count - 1];
- array_delete(&pathlen_stack,
- pathlen_count - 1, 1);
+ pathlen = ctx.cur_filter->pathlen;
+ ctx.cur_filter = ctx.cur_filter->prev;
+ parsers = config_update_cur_parsers(&ctx);
}
}
if (errormsg != NULL) {
i_fatal("Error in configuration file %s line %d: %s",
- input->path, input->linenum, errormsg);
+ ctx.cur_input->path, ctx.cur_input->linenum,
+ errormsg);
break;
}
str_truncate(full_line, 0);
}
- i_stream_destroy(&input->input);
- input = input->prev;
- if (line == NULL && input != NULL)
+ i_stream_destroy(&ctx.cur_input->input);
+ ctx.cur_input = ctx.cur_input->prev;
+ if (line == NULL && ctx.cur_input != NULL)
goto prevfile;
- for (l = config_setting_parsers; l->module_name != NULL; l++) {
- if (!settings_parser_check(l->parser, pool, &errormsg)) {
- i_fatal("Error in configuration file %s: %s",
- path, errormsg);
- }
- }
+ config_all_parsers_check(&ctx);
+
+ (void)array_append_space(&ctx.all_parsers);
+ config_filter = config_filter_init(ctx.pool);
+ config_filter_add_all(config_filter, array_idx(&ctx.all_parsers, 0));
}
}
static void config_connection_request_human(struct ostream *output,
- const char *service,
+ const struct config_filter *filter,
+ const char *module,
enum config_dump_flags flags)
{
static const char *ident_str = " ";
ctx.pool = pool_alloconly_create("config human strings", 10240);
i_array_init(&ctx.strings, 256);
- config_request_handle(service, flags, config_request_get_strings, &ctx);
+ config_request_handle(filter, module, flags,
+ config_request_get_strings, &ctx);
strings = array_get_modifiable(&ctx.strings, &count);
qsort(strings, count, sizeof(*strings), config_string_cmp);
pool_unref(&ctx.pool);
}
-static void config_dump_human(const char *service, enum config_dump_flags flags)
+static void config_dump_human(const struct config_filter *filter,
+ const char *module,
+ enum config_dump_flags flags)
{
struct ostream *output;
output = o_stream_create_fd(STDOUT_FILENO, 0, FALSE);
o_stream_cork(output);
- config_connection_request_human(output, service, flags);
+ config_connection_request_human(output, filter, module, flags);
o_stream_uncork(output);
}
int main(int argc, char *argv[])
{
enum config_dump_flags flags = CONFIG_DUMP_FLAG_DEFAULTS;
- const char *getopt_str, *config_path, *service_name = "";
+ const char *getopt_str, *config_path, *module = "";
+ struct config_filter filter;
char **exec_args = NULL;
int c;
+ memset(&filter, 0, sizeof(filter));
service = master_service_init("config", MASTER_SERVICE_FLAG_STANDALONE,
argc, argv);
i_set_failure_prefix("doveconf: ");
- getopt_str = t_strconcat("anp:e", master_service_getopt_string(), NULL);
+ getopt_str = t_strconcat("am:np:e", master_service_getopt_string(), NULL);
while ((c = getopt(argc, argv, getopt_str)) > 0) {
if (c == 'e')
break;
switch (c) {
case 'a':
break;
+ case 'm':
+ module = optarg;
+ break;
case 'n':
flags &= ~CONFIG_DUMP_FLAG_DEFAULTS;
break;
case 'p':
- service_name = optarg;
+ filter.service = optarg;
break;
default:
if (!master_service_parse_option(service, c, optarg))
if (*info != '\0')
printf("# %s\n", info);
fflush(stdout);
- config_dump_human(service_name, flags);
+ config_dump_human(&filter, module, flags);
} else {
env_put("DOVECONF_ENV=1");
- config_request_handle(service_name, 0,
+ config_request_handle(&filter, module, 0,
config_request_putenv, NULL);
execvp(exec_args[0], exec_args);
i_fatal("execvp(%s) failed: %m", exec_args[0]);