config-parser.c revision 9f0f2de10e4ea0c99052bf4b2bef8179f2536228
e59faf65ce864fe95dc00f5d52b8323cdbd0608aTimo Sirainen/* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen /* root=NULL-terminated list of parsers */
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen unsigned int pathlen;
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen unsigned int linenum;
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen ARRAY_DEFINE(all_parsers, struct config_filter_parser *);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenstatic const enum settings_parser_flags settings_parser_flags =
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenstruct config_module_parser *config_module_parsers;
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainenstatic const char *info_type_name_find(const struct setting_parser_info *info)
9d6dec796909384293006e4289436579089d88d5Timo Sirainen unsigned int i;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen for (i = 0; info->defines[i].key != NULL; i++) {
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen if (info->defines[i].offset == info->type_offset)
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen i_panic("setting parser: Invalid type_offset value");
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainenstatic void config_add_type(struct setting_parser_context *parser,
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen const char *p;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen ret = settings_parse_line(parser, str_c(str));
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenconfig_apply_line(struct parser_context *ctx, const char *key,
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen const char **error_r)
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen for (l = ctx->cur_section->parsers; l->root != NULL; l++) {
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen config_add_type(l->parser, line, section_name);
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen } else if (ret < 0) {
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen *error_r = settings_parser_get_error(l->parser);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen *error_r = t_strconcat("Unknown setting: ", key, NULL);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenstatic const char *
2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3Timo Sirainenfix_relative_path(const char *path, struct input_stack *input)
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char *p;
2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3Timo Sirainen return t_strconcat(t_strdup_until(input->path, p+1), path, NULL);
2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3Timo Sirainen unsigned int i, count;
2c7ab05ef98c46eb70c8ba6ea85e49749aafb2a3Timo Sirainen for (count = 0; all_roots[count] != NULL; count++) ;
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen dest = p_new(pool, struct config_module_parser, count + 1);
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen for (i = 0; i < count; i++) {
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen dest[i].parser = settings_parser_init(pool, all_roots[i],
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainenconfig_add_new_parser(struct parser_context *ctx)
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen struct config_section_stack *cur_section = ctx->cur_section;
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen parser = p_new(ctx->pool, struct config_filter_parser, 1);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen parser->parsers = cur_section->prev == NULL ? ctx->root_parsers :
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainenconfig_add_new_section(struct parser_context *ctx)
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen section = p_new(ctx->pool, struct config_section_stack, 1);
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainenconfig_filter_parser_find(struct parser_context *ctx,
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen unsigned int i, count;
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen parsers = array_get(&ctx->all_parsers, &count);
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen for (i = 0; i < count; i++) {
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen if (config_filters_equal(&parsers[i]->filter, filter))
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainenconfig_filter_add_new_filter(struct parser_context *ctx,
a64adf62fa33f2463a86f990217b0c9078531a40Timo Sirainen const char **error_r)
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen struct config_filter *filter = &ctx->cur_section->filter;
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen struct config_filter *parent = &ctx->cur_section->prev->filter;
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen *error_r = "protocol must not be under protocol";
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen *error_r = "local_ip must not be under remote_ip";
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen *error_r = "local_ip must not be under protocol";
a505d1beb29cbffab724b92ad16d0c44ebbaffb9Timo Sirainen else if (net_parse_range(value, &filter->local_net,
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen else if (parent->local_bits > filter->local_bits ||
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen *error_r = "local_ip not a subset of parent local_ip";
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen *error_r = "remote_ip must not be under protocol";
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen else if (net_parse_range(value, &filter->remote_net,
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen else if (parent->remote_bits > filter->remote_bits ||
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen *error_r = "remote_ip not a subset of parent remote_ip";
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen parser = config_filter_parser_find(ctx, filter);
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainenconfig_filter_parser_check(struct parser_context *ctx,
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen const char **error_r)
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen if (!settings_parser_check(p->parser, ctx->pool, error_r))
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainenconfig_all_parsers_check(struct parser_context *ctx,
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen const char **error_r)
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen unsigned int i, count;
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainen tmp_pool = pool_alloconly_create("config parsers check", 1024*32);
d22301419109ed4a38351715e6760011421dadecTimo Sirainen parsers = array_get(&ctx->all_parsers, &count);
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen i_assert(count > 0 && parsers[count-1] == NULL);
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen if (config_filter_parsers_get(new_filter, tmp_pool,
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen ret = config_filter_parser_check(ctx, tmp_parsers, error_r);
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainenstr_append_file(string_t *str, const char *key, const char *path,
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen const char **error_r)
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen *error_r = t_strdup_printf("%s: Can't open file %s: %m",
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen while ((ret = read(fd, buf, sizeof(buf))) > 0)
0ff28df76775a0740faa4370ec72d5a6a97534bfTimo Sirainen *error_r = t_strdup_printf("%s: read(%s) failed: %m",
0d16525a729011f4fced989a3da74d755ea49e6dTimo Sirainenstatic int settings_add_include(struct parser_context *ctx, const char *path,
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen for (tmp = ctx->cur_input; tmp != NULL; tmp = tmp->prev) {
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen *error_r = t_strdup_printf("Recursive include file: %s", path);
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen *error_r = t_strdup_printf("Couldn't open include file %s: %m",
dcf49e7ccf042982fde41ce256723f925fb9f1e4Timo Sirainen new_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
dcf49e7ccf042982fde41ce256723f925fb9f1e4Timo Sirainen i_stream_set_return_partial_line(new_input->input, TRUE);
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainensettings_include(struct parser_context *ctx, const char *pattern,
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen unsigned int i;
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) {
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen *error_r = "glob() failed: Not enough memory";
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen /* iterate throuth the different files matching the globbing */
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainen if (settings_add_include(ctx, globbers.gl_pathv[i],
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen return settings_add_include(ctx, pattern, ignore_errors, error_r);
62ccd8152d2f0142ab3301da4f5270b9d4f06718Timo Sirainenconfig_parse_line(struct parser_context *ctx, char *line, string_t *full_line,
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen const char *key;
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen unsigned int len;
7f97ca94363c9e38fbbaaef204d6d01c54af6fc4Timo Sirainen /* @UNSAFE: line is modified */
9d6dec796909384293006e4289436579089d88d5Timo Sirainen /* skip whitespace */
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen /* ignore comments or empty lines */
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen /* strip away comments. pretty kludgy way really.. */
9d6dec796909384293006e4289436579089d88d5Timo Sirainen if (*p == '\0')
9d6dec796909384293006e4289436579089d88d5Timo Sirainen } else if (*p == '#') {
9d6dec796909384293006e4289436579089d88d5Timo Sirainen "Ambiguous '#' character in line, treating it as comment. "
9d6dec796909384293006e4289436579089d88d5Timo Sirainen "Add a space before it to remove this warning.",
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* remove whitespace from end of line */
b5e6f6f27c1461f0f9f202615eeb738a645188c3Timo Sirainen /* continues in next line */
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen /* a) key = value
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen b) section_type [section_name] {
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen while (!IS_WHITE(*line) && *line != '\0' && *line != '=')
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen /* b) + errors */
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen /* get section name */
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainenstatic int config_parse_finish(struct parser_context *ctx, const char **error_r)
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen config_filter_add_all(new_filter, array_idx(&ctx->all_parsers, 0));
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen if (config_all_parsers_check(ctx, new_filter, &error) < 0) {
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen *error_r = t_strdup_printf("Error in configuration file %s: %s",
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainenint config_parse_file(const char *path, bool expand_files,
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen const char **error_r)
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen unsigned int pathlen = 0;
35077ff71c0082c1370d9c296abce3153f645958Timo Sirainen const char *errormsg, *key, *value, *section_name;
fc1696e32dd732a5bbabc3c8f64810448e327043Timo Sirainen *error_r = t_strdup_printf("open(%s) failed: %m", path);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen ctx.pool = pool_alloconly_create("config file parser", 1024*64);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen for (count = 0; all_roots[count] != NULL; count++) ;
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen p_new(ctx.pool, struct config_module_parser, count+1);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen for (i = 0; i < count; i++) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen p_array_init(&ctx.all_parsers, ctx.pool, 128);
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ctx.cur_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
822b71c04ddd61cb08a0104f9e58f55334725e2aTimo Sirainen i_stream_set_return_partial_line(ctx.cur_input->input, TRUE);
822b71c04ddd61cb08a0104f9e58f55334725e2aTimo Sirainen while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) {
822b71c04ddd61cb08a0104f9e58f55334725e2aTimo Sirainen type = config_parse_line(&ctx, line, full_line,
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen } else if (str_append_file(str, key, value, &errormsg) < 0) {
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* file reading failed */
822b71c04ddd61cb08a0104f9e58f55334725e2aTimo Sirainen (void)config_apply_line(&ctx, key, str_c(str), NULL, &errormsg);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen ctx.cur_section = config_add_new_section(&ctx);
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen if (config_filter_add_new_filter(&ctx, key, value,
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen /* new filter */
d22301419109ed4a38351715e6760011421dadecTimo Sirainen /* new config section */
8fcff4c5b52f24d9c681805fdf06b486f1d0fcbeTimo Sirainen /* no section name, use a counter */
2767104d81e97a109f0aa9758792bfa1da325a97Timo Sirainen section_name = settings_section_escape(value);
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen if (config_apply_line(&ctx, key, str_c(str), value, &errormsg) < 0)
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen (void)settings_include(&ctx, fix_relative_path(value, ctx.cur_input),
704efd0b34e3611e3decf1d559fe6a93214b0bd0Timo Sirainen "Error in configuration file %s line %d: %s",