changeset 9859:f9ca1a1ebcf8 HEAD

config: Several fixes. Now per-ip settings work properly. doveconf parameters were also changed. Now it's possible to ask configuration for a specified filter.
author Timo Sirainen <tss@iki.fi>
date Thu, 03 Sep 2009 16:36:38 -0400
parents 2e502c0e23e7
children d3d8ba13faa6
files src/config/config-connection.c src/config/config-filter.c src/config/config-filter.h src/config/config-parser.c src/config/config-request.c src/config/config-request.h src/config/doveconf.c
diffstat 7 files changed, 303 insertions(+), 193 deletions(-) [+]
line wrap: on
line diff
--- a/src/config/config-connection.c	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-connection.c	Thu Sep 03 16:36:38 2009 -0400
@@ -102,8 +102,11 @@
 	}
 
 	o_stream_cork(conn->output);
-	config_request_handle(&filter, module, 0, config_request_output,
-			      conn->output);
+	if (config_request_handle(&filter, module, CONFIG_DUMP_SCOPE_SET, FALSE,
+				  config_request_output, conn->output) < 0) {
+		config_connection_destroy(conn);
+		return -1;
+	}
 	o_stream_send_str(conn->output, "\n");
 	o_stream_uncork(conn->output);
 	return 0;
--- a/src/config/config-filter.c	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-filter.c	Thu Sep 03 16:36:38 2009 -0400
@@ -2,12 +2,13 @@
 
 #include "lib.h"
 #include "array.h"
+#include "settings-parser.h"
 #include "config-parser.h"
 #include "config-filter.h"
 
 struct config_filter_context {
 	pool_t pool;
-	struct config_filter_parser_list *const *parsers;
+	struct config_filter_parser *const *parsers;
 };
 
 bool config_filter_match(const struct config_filter *mask,
@@ -75,47 +76,123 @@
 }
 
 void config_filter_add_all(struct config_filter_context *ctx,
-			   struct config_filter_parser_list *const *parsers)
+			   struct config_filter_parser *const *parsers)
 {
 	ctx->parsers = parsers;
 }
 
-static int filter_cmp(const struct config_filter *f1,
-		      const struct config_filter *f2)
+static int
+config_filter_parser_cmp(struct config_filter_parser *const *p1,
+			 struct config_filter_parser *const *p2)
 {
-	int ret;
+	const struct config_filter *f1 = &(*p1)->filter, *f2 = &(*p2)->filter;
+
+	/* remote_ip and local_ips are first, although it doesn't really
+	   matter which one comes first */
+	if (f1->local_bits > f2->local_bits)
+		return -1;
+	if (f1->local_bits < f2->local_bits)
+		return 1;
 
-	ret = f2->remote_bits - f1->remote_bits;
-	if (ret != 0)
-		return ret;
+	if (f1->remote_bits > f2->remote_bits)
+		return -1;
+	if (f1->remote_bits < f2->remote_bits)
+		return 1;
+
+	if (f1->service != NULL && f2->service == NULL)
+		return -1;
+	if (f1->service == NULL && f2->service != NULL)
+		return 1;
+	return 0;
+}
 
-	ret = f2->local_bits - f1->local_bits;
-	if (ret != 0)
-		return ret;
+static struct config_filter_parser *const *
+config_filter_find_all(struct config_filter_context *ctx,
+		       const struct config_filter *filter)
+{
+	ARRAY_TYPE(config_filter_parsers) matches;
+	unsigned int i;
 
-	if (f1->service != NULL)
-		return -1;
-	else
-		return 1;
+	t_array_init(&matches, 8);
+	for (i = 0; ctx->parsers[i] != NULL; i++) {
+		if (config_filter_match(&ctx->parsers[i]->filter, filter))
+			array_append(&matches, &ctx->parsers[i], 1);
+	}
+	array_sort(&matches, config_filter_parser_cmp);
+	(void)array_append_space(&matches);
+	return array_idx(&matches, 0);
+}
+
+static bool
+config_filter_is_superset(const struct config_filter *sup,
+			  const struct config_filter *filter)
+{
+	/* assume that both of the filters match the same subset, so we don't
+	   need to compare IPs and service name. */
+	if (sup->local_bits < filter->local_bits)
+		return FALSE;
+	if (sup->remote_bits < filter->remote_bits)
+		return FALSE;
+	if (sup->service != NULL && filter->service == NULL)
+		return FALSE;
+	return TRUE;
 }
 
-const struct config_filter_parser_list *
-config_filter_find(struct config_filter_context *ctx,
-		   const struct config_filter *filter)
+static int
+config_module_parser_apply_changes(struct config_module_parser *dest,
+				   const struct config_filter_parser *src,
+				   pool_t pool, const char **error_r)
 {
-	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;
+	for (i = 0; dest[i].module_name != NULL; i++) {
+		if (settings_parser_apply_changes(dest[i].parser,
+						  src->parsers[i].parser, pool,
+						  error_r) < 0) {
+			*error_r = t_strdup_printf("Conflict in setting %s",
+						   *error_r);
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int config_filter_get_parsers(struct config_filter_context *ctx, pool_t pool,
+			      const struct config_filter *filter,
+			      const struct config_module_parser **parsers_r,
+			      const char **error_r)
+{
+	struct config_filter_parser *const *src;
+	struct config_module_parser *dest;
+	const char *error, **error_p;
+	unsigned int i, count;
+
+	src = config_filter_find_all(ctx, filter);
 
-		if (best == NULL ||
-		    filter_cmp(&best->filter, &ctx->parsers[i]->filter) > 0)
-			best = ctx->parsers[i];
+	/* all of them should have the same number of parsers.
+	   duplicate our initial parsers from the first match */
+	for (count = 0; src[0]->parsers[count].module_name != NULL; count++) ;
+	dest = p_new(pool, struct config_module_parser, count + 1);
+	for (i = 0; i < count; i++) {
+		dest[i] = src[0]->parsers[i];
+		dest[i].parser =
+			settings_parser_dup(src[0]->parsers[i].parser, pool);
 	}
-	return best;
+
+	/* apply the changes from rest of the matches */
+	for (i = 1; src[i] != NULL; i++) {
+		if (config_filter_is_superset(&src[i-1]->filter,
+					      &src[i]->filter))
+			error_p = NULL;
+		else
+			error_p = &error;
+
+		if (config_module_parser_apply_changes(dest, src[i], pool,
+						       error_p) < 0) {
+			*error_r = error;
+			return -1;
+		}
+	}
+	*parsers_r = dest;
+	return 0;
 }
--- a/src/config/config-filter.h	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-filter.h	Thu Sep 03 16:36:38 2009 -0400
@@ -9,23 +9,25 @@
 	unsigned int local_bits, remote_bits;
 };
 
-struct config_filter_parser_list {
+struct config_filter_parser {
 	struct config_filter filter;
 	/* NULL-terminated array of parsers */
 	struct config_module_parser *parsers;
 };
+ARRAY_DEFINE_TYPE(config_filter_parsers, struct config_filter_parser *);
 
 struct config_filter_context *config_filter_init(pool_t pool);
 void config_filter_deinit(struct config_filter_context **ctx);
 
 /* Replace filter's parsers with given parser list. */
 void config_filter_add_all(struct config_filter_context *ctx,
-			   struct config_filter_parser_list *const *parsers);
+			   struct config_filter_parser *const *parsers);
 
-/* Find the filter that best matches what we have. */
-const struct config_filter_parser_list *
-config_filter_find(struct config_filter_context *ctx,
-		   const struct config_filter *filter);
+/* Build new parsers from all existing ones matching the given filter. */
+int config_filter_get_parsers(struct config_filter_context *ctx, pool_t pool,
+			      const struct config_filter *filter,
+			      const struct config_module_parser **parsers_r,
+			      const char **error_r);
 
 /* Returns TRUE if filter matches mask. */
 bool config_filter_match(const struct config_filter *mask,
--- a/src/config/config-parser.c	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-parser.c	Thu Sep 03 16:36:38 2009 -0400
@@ -23,9 +23,12 @@
 
 #define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
 
-struct config_filter_stack {
-	struct config_filter_stack *prev;
+struct config_section_stack {
+	struct config_section_stack *prev;
+
 	struct config_filter filter;
+	/* module_name=NULL-terminated list of parsers */
+	struct config_module_parser *parsers;
 	unsigned int pathlen;
 };
 
@@ -41,16 +44,18 @@
 	pool_t pool;
 	const char *path;
 
-	ARRAY_DEFINE(all_parsers, struct config_filter_parser_list *);
-	/* parsers matching cur_filter */
-	ARRAY_TYPE(config_module_parsers) cur_parsers;
+	ARRAY_DEFINE(all_parsers, struct config_filter_parser *);
 	struct config_module_parser *root_parsers;
-	struct config_filter_stack *cur_filter;
+	struct config_section_stack *cur_section;
 	struct input_stack *cur_input;
 
 	struct config_filter_context *filter;
 };
 
+static const enum settings_parser_flags settings_parser_flags =
+	SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS |
+	SETTINGS_PARSER_FLAG_TRACK_CHANGES;
+
 struct config_module_parser *config_module_parsers;
 struct config_filter_context *config_filter;
 
@@ -93,15 +98,15 @@
 }
 
 static int
-config_parsers_parse_line(struct config_module_parser *parsers,
-			  const char *key, const char *line,
-			  const char *section_name, const char **error_r)
+config_apply_line(struct parser_context *ctx, const char *key,
+		  const char *line, const char *section_name,
+		  const char **error_r)
 {
 	struct config_module_parser *l;
 	bool found = FALSE;
 	int ret;
 
-	for (l = parsers; l->module_name != NULL; l++) {
+	for (l = ctx->cur_section->parsers; l->module_name != NULL; l++) {
 		ret = settings_parse_line(l->parser, line);
 		if (ret > 0) {
 			found = TRUE;
@@ -116,23 +121,6 @@
 		*error_r = t_strconcat("Unknown setting: ", key, NULL);
 		return -1;
 	}
-	return 0;
-}
-
-static int
-config_apply_line(struct parser_context *ctx, const char *key,
-		  const char *line, const char *section_name,
-		  const char **error_r)
-{
-	struct config_module_parser *const *parsers;
-	unsigned int i, count;
-
-	parsers = array_get(&ctx->cur_parsers, &count);
-	for (i = 0; i < count; i++) {
-		if (config_parsers_parse_line(parsers[i], key, line,
-					      section_name, error_r) < 0)
-			return -1;
-	}
 	*error_r = NULL;
 	return 0;
 }
@@ -153,61 +141,72 @@
 }
 
 static struct config_module_parser *
-config_module_parsers_dup(pool_t pool, const struct config_module_parser *src)
+config_module_parsers_init(pool_t pool)
 {
 	struct config_module_parser *dest;
 	unsigned int i, count;
 
-	for (count = 0; src[count].module_name != NULL; count++) ;
+	for (count = 0; all_roots[count].module_name != NULL; count++) ;
 
 	dest = p_new(pool, struct config_module_parser, count + 1);
 	for (i = 0; i < count; i++) {
-		dest[i] = src[i];
-		dest[i].parser = settings_parser_dup(src[i].parser, pool);
+		dest[i].module_name = all_roots[i].module_name;
+		dest[i].root = all_roots[i].root;
+		dest[i].parser = settings_parser_init(pool, all_roots[i].root,
+						      settings_parser_flags);
 	}
 	return dest;
 }
 
-static struct config_filter_parser_list *
+static void
 config_add_new_parser(struct parser_context *ctx)
 {
-	struct config_filter_parser_list *parser;
-	struct config_module_parser *const *cur_parsers;
-	unsigned int count;
-
-	parser = p_new(ctx->pool, struct config_filter_parser_list, 1);
-	parser->filter = ctx->cur_filter->filter;
+	struct config_section_stack *cur_section = ctx->cur_section;
+	struct config_filter_parser *parser;
 
-	cur_parsers = array_get(&ctx->cur_parsers, &count);
-	if (count == 0) {
-		/* first one */
-		parser->parsers = ctx->root_parsers;
-	} else {
-		/* duplicate the first settings list */
-		parser->parsers =
-			config_module_parsers_dup(ctx->pool, cur_parsers[0]);
-	}
+	parser = p_new(ctx->pool, struct config_filter_parser, 1);
+	parser->filter = cur_section->filter;
+	parser->parsers = cur_section->prev == NULL ? ctx->root_parsers :
+		config_module_parsers_init(ctx->pool);
+	array_append(&ctx->all_parsers, &parser, 1);
 
-	array_append(&ctx->all_parsers, &parser, 1);
-	return parser;
+	cur_section->parsers = parser->parsers;
 }
 
-static void config_add_new_filter(struct parser_context *ctx)
+static struct config_section_stack *
+config_add_new_section(struct parser_context *ctx)
 {
-	struct config_filter_stack *filter;
+	struct config_section_stack *section;
+
+	section = p_new(ctx->pool, struct config_section_stack, 1);
+	section->prev = ctx->cur_section;
+	section->filter = ctx->cur_section->filter;
+	section->parsers = ctx->cur_section->parsers;
+	return section;
+}
 
-	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_filter_parser *
+config_filter_parser_find(struct parser_context *ctx,
+			  const struct config_filter *filter)
+{
+	struct config_filter_parser *const *parsers;
+	unsigned int i, count;
+
+	parsers = array_get(&ctx->all_parsers, &count);
+	for (i = 0; i < count; i++) {
+		if (config_filters_equal(&parsers[i]->filter, filter))
+			return parsers[i];
+	}
+	return NULL;
 }
 
 static bool
-config_filter_add_new_parser(struct parser_context *ctx,
+config_filter_add_new_filter(struct parser_context *ctx,
 			     const char *key, const char *value,
 			     const char **error_r)
 {
-	struct config_filter *filter = &ctx->cur_filter->filter;
+	struct config_filter *filter = &ctx->cur_section->filter;
+	struct config_filter_parser *parser;
 
 	if (strcmp(key, "protocol") == 0) {
 		filter->service = p_strdup(ctx->pool, value);
@@ -223,69 +222,51 @@
 		return FALSE;
 	}
 
-	config_add_new_parser(ctx);
+	parser = config_filter_parser_find(ctx, filter);
+	if (parser != NULL)
+		ctx->cur_section->parsers = parser->parsers;
+	else
+		config_add_new_parser(ctx);
 	return TRUE;
 }
 
-static void config_update_cur_parsers(struct parser_context *ctx)
+static int
+config_filter_parser_check(struct parser_context *ctx,
+			   const struct config_module_parser *p,
+			   const char **error_r)
 {
-	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]->parsers, 1);
-			full_found = TRUE;
-		} else {
-			array_append(&ctx->cur_parsers,
-				     &all_parsers[i]->parsers, 1);
-		}
-	}
-	i_assert(full_found);
-}
-
-static int
-config_filter_parser_list_check(struct parser_context *ctx,
-				struct config_filter_parser_list *parser,
-				const char **error_r)
-{
-	struct config_module_parser *l = parser->parsers;
-	const char *errormsg;
-
-	for (; l->module_name != NULL; l++) {
-		if (!settings_parser_check(l->parser, ctx->pool, &errormsg)) {
-			*error_r = t_strdup_printf(
-				"Error in configuration file %s: %s",
-				ctx->path, errormsg);
+	for (; p->module_name != NULL; p++) {
+		if (!settings_parser_check(p->parser, ctx->pool, error_r))
 			return -1;
-		}
 	}
 	return 0;
 }
 
 static int
-config_all_parsers_check(struct parser_context *ctx, const char **error_r)
+config_all_parsers_check(struct parser_context *ctx,
+			 struct config_filter_context *new_filter,
+			 const char **error_r)
 {
-	struct config_filter_parser_list *const *parsers;
+	struct config_filter_parser *const *parsers;
+	const struct config_module_parser *tmp_parsers;
 	unsigned int i, count;
+	pool_t tmp_pool;
 
+	tmp_pool = pool_alloconly_create("config parsers check", 10240);
 	parsers = array_get(&ctx->all_parsers, &count);
+	i_assert(count > 0 && parsers[count-1] == NULL);
+	count--;
 	for (i = 0; i < count; i++) {
-		if (config_filter_parser_list_check(ctx, parsers[i],
-						    error_r) < 0)
-			return -1;
+		p_clear(tmp_pool);
+		if (config_filter_get_parsers(new_filter, tmp_pool,
+					      &parsers[i]->filter,
+					      &tmp_parsers, error_r) < 0)
+			break;
+
+		if (config_filter_parser_check(ctx, tmp_parsers, error_r) < 0)
+			break;
 	}
-	return 0;
+	return i == count ? 0 : -1;
 }
 
 static int
@@ -514,12 +495,31 @@
 	return CONFIG_LINE_TYPE_SECTION_BEGIN;
 }
 
+static int config_parse_finish(struct parser_context *ctx, const char **error_r)
+{
+	struct config_filter_context *new_filter;
+	const char *error;
+
+	new_filter = config_filter_init(ctx->pool);
+	(void)array_append_space(&ctx->all_parsers);
+	config_filter_add_all(new_filter, array_idx(&ctx->all_parsers, 0));
+
+	if (config_all_parsers_check(ctx, new_filter, &error) < 0) {
+		*error_r = t_strdup_printf("Error in configuration file %s: %s",
+					   ctx->path, error);
+		return -1;
+	}
+
+	if (config_filter != NULL)
+		config_filter_deinit(&config_filter);
+	config_module_parsers = ctx->root_parsers;
+	config_filter = new_filter;
+	return 0;
+}
+
 int config_parse_file(const char *path, bool expand_files,
 		      const char **error_r)
 {
-	enum settings_parser_flags parser_flags =
-		SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS |
-		SETTINGS_PARSER_FLAG_TRACK_CHANGES;
 	struct input_stack root;
 	struct parser_context ctx;
 	unsigned int pathlen = 0;
@@ -548,14 +548,12 @@
 		ctx.root_parsers[i].root = all_roots[i].root;
 		ctx.root_parsers[i].parser =
 			settings_parser_init(ctx.pool, all_roots[i].root,
-					     parser_flags);
+					     settings_parser_flags);
 	}
 
-	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);
+	ctx.cur_section = p_new(ctx.pool, struct config_section_stack, 1);
 	config_add_new_parser(&ctx);
-	config_update_cur_parsers(&ctx);
 
 	memset(&root, 0, sizeof(root));
 	root.path = path;
@@ -591,13 +589,12 @@
 			(void)config_apply_line(&ctx, key, str_c(str), NULL, &errormsg);
 			break;
 		case CONFIG_LINE_TYPE_SECTION_BEGIN:
-			config_add_new_filter(&ctx);
-			ctx.cur_filter->pathlen = pathlen;
+			ctx.cur_section = config_add_new_section(&ctx);
+			ctx.cur_section->pathlen = pathlen;
 
-			if (config_filter_add_new_parser(&ctx, key, value,
+			if (config_filter_add_new_filter(&ctx, key, value,
 							 &errormsg)) {
-				/* new real filter */
-				config_update_cur_parsers(&ctx);
+				/* new filter */
 				break;
 			}
 
@@ -620,12 +617,11 @@
 			pathlen = str_len(str);
 			break;
 		case CONFIG_LINE_TYPE_SECTION_END:
-			if (ctx.cur_filter->prev == NULL)
+			if (ctx.cur_section->prev == NULL)
 				errormsg = "Unexpected '}'";
 			else {
-				pathlen = ctx.cur_filter->pathlen;
-				ctx.cur_filter = ctx.cur_filter->prev;
-				config_update_cur_parsers(&ctx);
+				pathlen = ctx.cur_section->pathlen;
+				ctx.cur_section = ctx.cur_section->prev;
 			}
 			break;
 		case CONFIG_LINE_TYPE_INCLUDE:
@@ -652,21 +648,11 @@
 	if (line == NULL && ctx.cur_input != NULL)
 		goto prevfile;
 
-	if (ret == 0) {
-		if (config_all_parsers_check(&ctx, error_r) < 0)
-			ret = -1;
-	}
+	if (ret == 0)
+		ret = config_parse_finish(&ctx, error_r);
 	if (ret < 0) {
 		pool_unref(&ctx.pool);
 		return -1;
 	}
-
-	if (config_filter != NULL)
-		config_filter_deinit(&config_filter);
-	config_module_parsers = ctx.root_parsers;
-
-	(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));
 	return 1;
 }
--- a/src/config/config-request.c	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-request.c	Thu Sep 03 16:36:38 2009 -0400
@@ -231,16 +231,26 @@
 	}
 }
 
-void config_request_handle(const struct config_filter *filter,
-			   const char *module, enum config_dump_scope scope,
-			   config_request_callback_t *callback, void *context)
+int config_request_handle(const struct config_filter *filter,
+			  const char *module, enum config_dump_scope scope,
+			  bool check_settings,
+			  config_request_callback_t *callback, void *context)
 {
 	const struct config_module_parser *l;
-	const struct config_filter_parser_list *list;
 	struct settings_export_context ctx;
+	const char *error;
+	int ret = 0;
 
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.pool = pool_alloconly_create("config request", 10240);
+
+	if (config_filter_get_parsers(config_filter, ctx.pool, filter,
+				      &l, &error) < 0) {
+		i_error("%s", error);
+		pool_unref(&ctx.pool);
+		return -1;
+	}
+
 	ctx.callback = callback;
 	ctx.context = context;
 	ctx.scope = scope;
@@ -249,15 +259,24 @@
 	ctx.keys = hash_table_create(default_pool, ctx.pool, 0,
 				     str_hash, (hash_cmp_callback_t *)strcmp);
 
-	list = config_filter_find(config_filter, filter);
-	for (l = list->parsers; l->module_name != NULL; l++) {
-		if (*module == '\0' ||
-		    config_module_parser_is_in_service(l, module)) {
-			settings_export(&ctx, l->root,
-					settings_parser_get(l->parser),
-					settings_parser_get_changes(l->parser));
+	for (; l->module_name != NULL; l++) {
+		if (*module != '\0' &&
+		    !config_module_parser_is_in_service(l, module))
+			continue;
+
+		settings_export(&ctx, l->root, settings_parser_get(l->parser),
+				settings_parser_get_changes(l->parser));
+
+		if (check_settings) {
+			if (!settings_parser_check(l->parser, ctx.pool,
+						   &error)) {
+				i_error("%s", error);
+				ret = -1;
+				break;
+			}
 		}
 	}
 	hash_table_destroy(&ctx.keys);
 	pool_unref(&ctx.pool);
+	return ret;
 }
--- a/src/config/config-request.h	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/config-request.h	Thu Sep 03 16:36:38 2009 -0400
@@ -15,8 +15,9 @@
 typedef void config_request_callback_t(const char *key, const char *value,
 				       bool list, void *context);
 
-void config_request_handle(const struct config_filter *filter,
-			   const char *module, enum config_dump_scope scope,
-			   config_request_callback_t *callback, void *context);
+int config_request_handle(const struct config_filter *filter,
+			  const char *module, enum config_dump_scope scope,
+			  bool check_settings,
+			  config_request_callback_t *callback, void *context);
 
 #endif
--- a/src/config/doveconf.c	Thu Sep 03 16:35:19 2009 -0400
+++ b/src/config/doveconf.c	Thu Sep 03 16:36:38 2009 -0400
@@ -77,8 +77,9 @@
 
 	ctx.pool = pool_alloconly_create("config human strings", 10240);
 	i_array_init(&ctx.strings, 256);
-	config_request_handle(filter, module, scope,
-			      config_request_get_strings, &ctx);
+	if (config_request_handle(filter, module, scope, TRUE,
+				  config_request_get_strings, &ctx) < 0)
+		return;
 
 	array_sort(&ctx.strings, config_string_cmp);
 	strings = array_get(&ctx.strings, &count);
@@ -200,6 +201,23 @@
 	return "";
 }
 
+static void filter_parse_arg(struct config_filter *filter, const char *arg)
+{
+	if (strncmp(arg, "service=", 8) == 0)
+		filter->service = arg + 8;
+	else if (strncmp(arg, "lip=", 4) == 0) {
+		if (net_parse_range(arg + 4, &filter->local_net,
+				    &filter->local_bits) < 0)
+			i_fatal("lip: Invalid network mask");
+	} else if (strncmp(arg, "rip=", 4) == 0) {
+		if (net_parse_range(arg + 4, &filter->remote_net,
+				    &filter->remote_bits) < 0)
+			i_fatal("rip: Invalid network mask");
+	} else {
+		i_fatal("Unknown filter argument: %s", arg);
+	}
+}
+
 int main(int argc, char *argv[])
 {
 	enum config_dump_scope scope = CONFIG_DUMP_SCOPE_ALL;
@@ -214,7 +232,7 @@
 					     MASTER_SERVICE_FLAG_STANDALONE,
 					     argc, argv);
 	i_set_failure_prefix("doveconf: ");
-	getopt_str = t_strconcat("am:nNp:e",
+	getopt_str = t_strconcat("af:m:nN:e",
 				 master_service_getopt_string(), NULL);
 	while ((c = getopt(argc, argv, getopt_str)) > 0) {
 		if (c == 'e')
@@ -222,6 +240,9 @@
 		switch (c) {
 		case 'a':
 			break;
+		case 'f':
+			filter_parse_arg(&filter, optarg);
+			break;
 		case 'm':
 			module = optarg;
 			break;
@@ -231,9 +252,6 @@
 		case 'N':
 			scope = CONFIG_DUMP_SCOPE_SET;
 			break;
-		case 'p':
-			filter.service = optarg;
-			break;
 		default:
 			if (!master_service_parse_option(master_service,
 							 c, optarg))
@@ -242,9 +260,11 @@
 	}
 	config_path = master_service_get_config_path(master_service);
 
-	if (argv[optind] != NULL)
+	if (argv[optind] != NULL) {
+		if (c != 'e')
+			i_fatal("Unknown argument: %s", argv[optind]);
 		exec_args = &argv[optind];
-	else {
+	} else {
 		/* print the config file path before parsing it, so in case
 		   of errors it's still shown */
 		printf("# "VERSION": %s\n", config_path);
@@ -268,8 +288,10 @@
 		config_dump_human(&filter, module, scope);
 	} else {
 		env_put("DOVECONF_ENV=1");
-		config_request_handle(&filter, module, 0,
-				      config_request_putenv, NULL);
+		if (config_request_handle(&filter, module,
+					  CONFIG_DUMP_SCOPE_SET, TRUE,
+					  config_request_putenv, NULL) < 0)
+			i_fatal("Invalid configuration");
 		execvp(exec_args[0], exec_args);
 		i_fatal("execvp(%s) failed: %m", exec_args[0]);
 	}