config-parser.c revision 36175032e12e7dfe67f92ee7c2065fdc6865aefd
e8058322725ba050014777ee2484f7e833ab1e3aLukas Slebodnik/* Copyright (c) 2005-2011 Dovecot authors, see the included COPYING file */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic const enum settings_parser_flags settings_parser_flags =
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstruct config_module_parser *config_module_parsers;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid (*hook_config_parser_begin)(struct config_parser_context *ctx);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic const char *info_type_name_find(const struct setting_parser_info *info)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int i;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (i = 0; info->defines[i].key != NULL; i++) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (info->defines[i].offset == info->type_offset)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_panic("setting parser: Invalid type_offset value");
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int config_add_type(struct setting_parser_context *parser,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *p;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* section inside strlist */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterconfig_parser_is_in_localremote(struct config_section_stack *section)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const struct config_filter *filter = §ion->filter;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return filter->local_name != NULL || filter->local_bits > 0 ||
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterint config_apply_line(struct config_parser_context *ctx, const char *key,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (l = ctx->cur_section->parsers; l->root != NULL; l++) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* FIXME: remove once auth does support these. */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (strcmp(l->root->module_name, "auth") == 0 &&
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter config_parser_is_in_localremote(ctx->cur_section)) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter "Auth settings not supported inside local/remote blocks: ",
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (config_add_type(l->parser, line, section_name) < 0) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter } else if (ret < 0) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = settings_parser_get_error(l->parser);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = p_strconcat(ctx->pool, "Unknown setting: ",
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstatic const char *
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterfix_relative_path(const char *path, struct input_stack *input)
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter const char *p;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter return t_strconcat(t_strdup_until(input->path, p+1), path, NULL);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int i, count;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (count = 0; all_roots[count] != NULL; count++) ;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter dest = p_new(pool, struct config_module_parser, count + 1);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (i = 0; i < count; i++) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter dest[i].parser = settings_parser_init(pool, all_roots[i],
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterconfig_add_new_parser(struct config_parser_context *ctx)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter struct config_section_stack *cur_section = ctx->cur_section;
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter parser = p_new(ctx->pool, struct config_filter_parser, 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter parser->parsers = cur_section->prev == NULL ? ctx->root_parsers :
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozekconfig_add_new_section(struct config_parser_context *ctx)
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek section = p_new(ctx->pool, struct config_section_stack, 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter section->open_path = p_strdup(ctx->pool, ctx->cur_input->path);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter section->open_linenum = ctx->cur_input->linenum;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterconfig_filter_parser_find(struct config_parser_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (config_filters_equal(&parser->filter, filter))
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterint config_parse_net(const char *value, struct ip_addr *ip_r,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *p;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ret = net_gethostbyname(value, &ips, &ip_count);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter *error_r = t_strdup_printf("gethostbyname(%s) failed: %s",
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (p == NULL || str_to_uint(p, &bits) < 0 || bits > max_bits)
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterconfig_filter_add_new_filter(struct config_parser_context *ctx,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct config_filter *filter = &ctx->cur_section->filter;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter struct config_filter *parent = &ctx->cur_section->prev->filter;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char *error;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = "protocol must not be under protocol";
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = "local must not be under protocol";
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = "local must not be under local_name";
66277b21179d95f6e96abed01a20ccbccf27ce99Pavel Březina else if (config_parse_net(value, &filter->local_net,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter else if (parent->local_bits > filter->local_bits ||
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ctx->error = "local not a subset of parent local";
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek filter->local_host = p_strdup(ctx->pool, value);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ctx->error = "local_name must not be under remote";
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ctx->error = "local_name must not be under protocol";
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek filter->local_name = p_strdup(ctx->pool, value);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ctx->error = "remote must not be under protocol";
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek else if (config_parse_net(value, &filter->remote_net,
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter else if (parent->remote_bits > filter->remote_bits ||
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek ctx->error = "remote not a subset of parent remote";
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek filter->remote_host = p_strdup(ctx->pool, value);
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter parser = config_filter_parser_find(ctx, filter);
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walterconfig_filter_parser_check(struct config_parser_context *ctx,
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek const struct config_module_parser *p,
58229439447d5617913a5a2e173b78105c694842Pavel Březina const char **error_r)
5de968e80ade1c02d1907834dcff95e9fc9ad10aJakub Hrozek /* skip checking settings we don't care about */
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter if (!settings_parser_check(p->parser, ctx->pool, error_r))
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekstatic const char *
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekget_str_setting(struct config_filter_parser *parser, const char *key,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek const char *const *set_value;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek for (; module_parser->parser != NULL; module_parser++) {
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek set_value = settings_parse_get_value(module_parser->parser,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek settings_parse_is_changed(module_parser->parser, key)) {
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_assert(set_type == SET_STR || set_type == SET_ENUM);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozekconfig_all_parsers_check(struct config_parser_context *ctx,
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek const char **error_r)
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek unsigned int i, count;
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek "Missing '}' (section started at %s:%u)",
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek tmp_pool = pool_alloconly_create("config parsers check", 1024*64);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek parsers = array_get(&ctx->all_parsers, &count);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_assert(count > 0 && parsers[count-1] == NULL);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek global_ssl_set = get_str_setting(parsers[0], "ssl", "");
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek if (config_filter_parsers_get(new_filter, tmp_pool, "",
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek ssl_set = get_str_setting(parsers[i], "ssl", global_ssl_set);
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek strcmp(global_ssl_set, "no") == 0 && !ssl_warned) {
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek i_warning("SSL is disabled because global ssl=no, "
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter ret = config_filter_parser_check(ctx, tmp_parsers, error_r);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterstr_append_file(string_t *str, const char *key, const char *path,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter const char **error_r)
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek *error_r = t_strdup_printf("%s: Can't open file %s: %m",
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek *error_r = t_strdup_printf("%s: read(%s) failed: %m",
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozekstatic int settings_add_include(struct config_parser_context *ctx, const char *path,
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter for (tmp = ctx->cur_input; tmp != NULL; tmp = tmp->prev) {
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter *error_r = t_strdup_printf("Recursive include file: %s", path);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter *error_r = t_strdup_printf("Couldn't open include file %s: %m",
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter new_input = p_new(ctx->pool, struct input_stack, 1);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter new_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter i_stream_set_return_partial_line(new_input->input, TRUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltersettings_include(struct config_parser_context *ctx, const char *pattern,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *error;
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter unsigned int i;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter switch (glob(pattern, GLOB_BRACE, NULL, &globbers)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx->error = "glob() failed: Not enough memory";
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* iterate throuth the different files matching the globbing */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (settings_add_include(ctx, globbers.gl_pathv[i-1],
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter if (settings_add_include(ctx, pattern, ignore_errors, &error) < 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterconfig_parse_line(struct config_parser_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *key;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int len;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* @UNSAFE: line is modified */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* skip whitespace */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* ignore comments or empty lines */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* strip away comments. pretty kludgy way really.. */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (*p == '\0')
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (*p == '#') {
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek "Ambiguous '#' character in line, treating it as comment. "
90e04eae7e54ec892a6f239783df94dab5d1ed9aJakub Hrozek "Add a space before it to remove this warning.",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* remove whitespace from end of line */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* continues in next line */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* a) key = value
4f7f714e118e95896fac5239c7a8b529c39a4758Jakub Hrozek b) section_type [section_name] {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter while (!IS_WHITE(*line) && *line != '\0' && *line != '=')
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* b) + errors */
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter /* get section name */
c2cc119de8eac712c040b3993f41c967ff2278deStef Walterstatic int config_parse_finish(struct config_parser_context *ctx, const char **error_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *error;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter config_filter_add_all(new_filter, array_idx(&ctx->all_parsers, 0));
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if ((ret = config_all_parsers_check(ctx, new_filter, &error)) < 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *error_r = t_strdup_printf("Error in configuration file %s: %s",
c2cc119de8eac712c040b3993f41c967ff2278deStef Walterstatic const void *
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walterconfig_get_value(struct config_section_stack *section, const char *key,
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter const void *value;
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter for (l = section->parsers; l->root != NULL; l++) {
fcd8093c58638dc7c4f9cddfc97f273b94ce2eadStef Walter value = settings_parse_get_value(l->parser, key, type_r);
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walter /* not changed by this parser. maybe parent has. */
dff909d473f43a6bd0f0286fa2d279c0ebe945c6Stef Walterconfig_require_key(struct config_parser_context *ctx, const char *key)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (l = ctx->cur_section->parsers; l->root != NULL; l++) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (config_module_want_parser(ctx->root_parsers,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterstatic int config_write_value(struct config_parser_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char *error;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* don't even try to open the file */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter } else if (str_append_file(str, key, value, &error) < 0) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* file reading failed */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* expand_parent=TRUE for "key = $key stuff".
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter we'll always expand it so that doveconf -n can give
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter usable output */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter var_value = config_get_value(ctx->cur_section, var_name,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "Unknown variable: $",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "Invalid variable: $",
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Waltervoid config_parser_apply_line(struct config_parser_context *ctx,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (config_write_value(ctx, type, key, value) < 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter (void)config_apply_line(ctx, key, str_c(ctx->str), NULL);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (config_filter_add_new_filter(ctx, key, value)) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* new filter */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* new config section */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter /* no section name, use a counter */
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter if (config_apply_line(ctx, key, str_c(ctx->str), value) < 0)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter (void)settings_include(ctx, fix_relative_path(value, ctx->cur_input),
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walterint config_parse_file(const char *path, bool expand_values, const char *module,
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter const char **error_r)
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter unsigned int i, count;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter *error_r = t_strdup_printf("open(%s) failed: %m", path);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx.pool = pool_alloconly_create("config file parser", 1024*256);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (count = 0; all_roots[count] != NULL; count++) ;
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter p_new(ctx.pool, struct config_module_parser, count+1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter for (i = 0; i < count; i++) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter ctx.cur_input->input = i_stream_create_fd(fd, (size_t)-1, TRUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter i_stream_set_return_partial_line(ctx.cur_input->input, TRUE);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter while ((line = i_stream_read_next_line(ctx.cur_input->input)) != NULL) {
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter handled = old_settings_handle(&ctx, type, key, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter config_parser_apply_line(&ctx, type, key, value);
b699c4d7f85a5404be1d1ee9450331aea869b886Stef Walter "Error in configuration file %s line %d: %s",
goto prevfile;
if (ret == 0)
void config_parse_load_modules(void)
struct module *m;
unsigned int i, count;
for (i = 0; i < count; i++)
const struct setting_parser_info *p;
if (p == root)
return TRUE;
return TRUE;
return FALSE;
const char *module,
struct config_module_parser *l;
return TRUE;
return TRUE;
return TRUE;
return FALSE;