changeset 9263:5d0a69504867 HEAD

config handling fixes and improvements. Separated module/service lookups. Added support for per-lip/rip settings.
author Timo Sirainen <tss@iki.fi>
date Mon, 11 May 2009 20:10:30 -0400
parents 7604073a4ccc
children 11b6aab8a203
files src/config/Makefile.am src/config/all-settings.h src/config/config-connection.c src/config/config-filter.c src/config/config-filter.h src/config/config-parser.c src/config/config-parser.h src/config/config-request.c src/config/config-request.h src/config/doveconf.c src/imap/main.c src/lda/main.c src/lib-master/master-service-settings.c src/lib-master/master-service-settings.h src/lib-storage/mail-storage-service.c src/lib-storage/mail-storage-service.h src/login-common/login-settings.c src/pop3/main.c
diffstat 18 files changed, 458 insertions(+), 74 deletions(-) [+]
line wrap: on
line diff
--- a/src/config/Makefile.am	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/Makefile.am	Mon May 11 20:10:30 2009 -0400
@@ -28,6 +28,7 @@
 common = \
 	all-settings.c \
 	config-connection.c \
+	config-filter.c \
 	config-parser.c \
 	config-request.c \
 	sysinfo-get.c
@@ -43,6 +44,7 @@
 noinst_HEADERS = \
 	all-settings.h \
 	config-connection.h \
+	config-filter.h \
 	config-parser.h \
 	config-request.h \
 	sysinfo-get.h
--- a/src/config/all-settings.h	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/all-settings.h	Mon May 11 20:10:30 2009 -0400
@@ -7,6 +7,7 @@
 	struct setting_parser_context *parser;
 	void *settings;
 };
+ARRAY_DEFINE_TYPE(config_setting_parsers, struct config_setting_parser_list *);
 
 extern struct config_setting_parser_list config_setting_parsers[];
 
--- a/src/config/config-connection.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/config-connection.c	Mon May 11 20:10:30 2009 -0400
@@ -56,16 +56,34 @@
 static void config_connection_request(struct config_connection *conn,
 				      const char *const *args)
 {
-	const char *service = "";
+	struct config_filter filter;
+	const char *module = "";
 
 	/* [<args>] */
+	memset(&filter, 0, sizeof(filter));
 	for (; *args != NULL; args++) {
 		if (strncmp(*args, "service=", 8) == 0)
-			service = *args + 8;
+			filter.service = *args + 8;
+		else if (strncmp(*args, "module=", 7) == 0)
+			module = *args + 7;
+		else if (strncmp(*args, "lip=", 4) == 0) {
+			if (net_addr2ip(*args + 4, &filter.local_net) == 0) {
+				filter.local_bits =
+					IPADDR_IS_V4(&filter.local_net) ?
+					32 : 128;
+			}
+		} else if (strncmp(*args, "rip=", 4) == 0) {
+			if (net_addr2ip(*args + 4, &filter.remote_net) == 0) {
+				filter.remote_bits =
+					IPADDR_IS_V4(&filter.remote_net) ?
+					32 : 128;
+			}
+		}
 	}
 
 	o_stream_cork(conn->output);
-	config_request_handle(service, 0, config_request_output, conn->output);
+	config_request_handle(&filter, module, 0, config_request_output,
+			      conn->output);
 	o_stream_send_str(conn->output, "\n");
 	o_stream_uncork(conn->output);
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-filter.c	Mon May 11 20:10:30 2009 -0400
@@ -0,0 +1,120 @@
+/* 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;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-filter.h	Mon May 11 20:10:30 2009 -0400
@@ -0,0 +1,35 @@
+#ifndef CONFIG_FILTER_H
+#define CONFIG_FILTER_H
+
+#include "network.h"
+#include "all-settings.h"
+
+struct config_filter {
+	const char *service;
+	struct ip_addr local_net, remote_net;
+	unsigned int local_bits, remote_bits;
+};
+
+struct config_filter_parser_list {
+	struct config_filter filter;
+	struct config_setting_parser_list *parser_list;
+};
+
+struct config_filter_context *config_filter_init(pool_t pool);
+void config_filter_deinit(struct config_filter_context **ctx);
+
+void config_filter_add_all(struct config_filter_context *ctx,
+			   struct config_filter_parser_list *const *parsers);
+
+const struct config_setting_parser_list *
+config_filter_match_parsers(struct config_filter_context *ctx,
+			    const struct config_filter *filter);
+
+/* Returns TRUE if filter matches mask. */
+bool config_filter_match(const struct config_filter *mask,
+			 const struct config_filter *filter);
+/* Returns TRUE if two filters are fully equal. */
+bool config_filters_equal(const struct config_filter *f1,
+			  const struct config_filter *f2);
+
+#endif
--- a/src/config/config-parser.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/config-parser.c	Mon May 11 20:10:30 2009 -0400
@@ -7,7 +7,7 @@
 #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>
@@ -15,6 +15,12 @@
 
 #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;
 
@@ -23,6 +29,21 @@
 	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;
@@ -62,13 +83,15 @@
 }
 
 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;
@@ -82,6 +105,21 @@
 }
 
 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)
 {
 	const char *p;
@@ -96,14 +134,122 @@
 	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;
@@ -111,38 +257,46 @@
 	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 */
 
@@ -204,8 +358,8 @@
 			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;
 			}
@@ -213,9 +367,9 @@
 				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. */
@@ -249,10 +403,10 @@
 
 				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 */
@@ -274,26 +428,46 @@
 
 			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);
@@ -314,43 +488,39 @@
 						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));
 }
--- a/src/config/config-parser.h	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/config-parser.h	Mon May 11 20:10:30 2009 -0400
@@ -1,6 +1,8 @@
 #ifndef CONFIG_PARSER_H
 #define CONFIG_PARSER_H
 
+extern struct config_filter_context *config_filter;
+
 void config_parse_file(const char *path);
 
 #endif
--- a/src/config/config-request.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/config-request.c	Mon May 11 20:10:30 2009 -0400
@@ -8,6 +8,7 @@
 #include "settings-parser.h"
 #include "master-service-settings.h"
 #include "all-settings.h"
+#include "config-parser.h"
 #include "config-request.h"
 
 struct settings_export_context {
@@ -45,12 +46,12 @@
 }
 
 static bool
-config_setting_parser_is_in_service(struct config_setting_parser_list *list,
-				    const char *service)
+config_setting_parser_is_in_service(const struct config_setting_parser_list *list,
+				    const char *module)
 {
 	struct config_setting_parser_list *l;
 
-	if (strcmp(list->module_name, service) == 0)
+	if (strcmp(list->module_name, module) == 0)
 		return TRUE;
 	if (list->root == &master_service_setting_parser_info) {
 		/* everyone wants master service settings */
@@ -58,7 +59,7 @@
 	}
 
 	for (l = config_setting_parsers; l->module_name != NULL; l++) {
-		if (strcmp(l->module_name, service) != 0)
+		if (strcmp(l->module_name, module) != 0)
 			continue;
 
 		/* see if we can find a way to get from the original parser
@@ -213,10 +214,11 @@
 	}
 }
 
-void config_request_handle(const char *service, enum config_dump_flags flags,
+void config_request_handle(const struct config_filter *filter,
+			   const char *module, enum config_dump_flags flags,
 			   config_request_callback_t *callback, void *context)
 {
-	struct config_setting_parser_list *l;
+	const struct config_setting_parser_list *l;
 	struct settings_export_context ctx;
 
 	memset(&ctx, 0, sizeof(ctx));
@@ -229,9 +231,10 @@
 	ctx.keys = hash_table_create(default_pool, ctx.pool, 0,
 				     str_hash, (hash_cmp_callback_t *)strcmp);
 
-	for (l = config_setting_parsers; l->module_name != NULL; l++) {
-		if (*service == '\0' ||
-		    config_setting_parser_is_in_service(l, service)) {
+	l = config_filter_match_parsers(config_filter, filter);
+	for (; l->module_name != NULL; l++) {
+		if (*module == '\0' ||
+		    config_setting_parser_is_in_service(l, module)) {
 			settings_export(&ctx, l->root,
 					settings_parser_get(l->parser));
 		}
--- a/src/config/config-request.h	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/config-request.h	Mon May 11 20:10:30 2009 -0400
@@ -1,6 +1,8 @@
 #ifndef CONFIG_REQUEST_H
 #define CONFIG_REQUEST_H
 
+#include "config-filter.h"
+
 enum config_dump_flags {
 	CONFIG_DUMP_FLAG_DEFAULTS	= 0x01
 };
@@ -8,7 +10,8 @@
 typedef void config_request_callback_t(const char *key, const char *value,
 				       bool list, void *context);
 
-void config_request_handle(const char *service, enum config_dump_flags flags,
+void config_request_handle(const struct config_filter *filter,
+			   const char *module, enum config_dump_flags flags,
 			   config_request_callback_t *callback, void *context);
 
 #endif
--- a/src/config/doveconf.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/config/doveconf.c	Mon May 11 20:10:30 2009 -0400
@@ -65,7 +65,8 @@
 }
 
 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 = "               ";
@@ -79,7 +80,8 @@
 
 	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);
@@ -158,13 +160,15 @@
 	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);
 }
 
@@ -202,25 +206,30 @@
 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))
@@ -248,10 +257,10 @@
 		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]);
--- a/src/imap/main.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/imap/main.c	Mon May 11 20:10:30 2009 -0400
@@ -186,6 +186,8 @@
 	}
 
 	memset(&input, 0, sizeof(input));
+	input.module = "imap";
+	input.service = "imap";
 	input.username = getenv("USER");
 	if (input.username == NULL) {
 		if (IS_STANDALONE())
--- a/src/lda/main.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/lda/main.c	Mon May 11 20:10:30 2009 -0400
@@ -315,6 +315,8 @@
 	}
 
 	memset(&service_input, 0, sizeof(service_input));
+	service_input.module = "lda";
+	service_input.service = "lda";
 	service_input.username = user;
 
 	service_flags |= MAIL_STORAGE_SERVICE_FLAG_DISALLOW_ROOT;
--- a/src/lib-master/master-service-settings.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/lib-master/master-service-settings.c	Mon May 11 20:10:30 2009 -0400
@@ -124,8 +124,11 @@
 		string_t *str;
 
 		str = t_str_new(128);
-		str_append(str, CONFIG_HANDSHAKE);
-		str_printfa(str, "REQ\tservice=%s", service->name);
+		str_append(str, CONFIG_HANDSHAKE"REQ");
+		if (input->module != NULL)
+			str_printfa(str, "\tmodule=%s", input->module);
+		if (input->service != NULL)
+			str_printfa(str, "\tservice=%s", input->service);
 		if (input->username != NULL)
 			str_printfa(str, "\tuser=%s", input->username);
 		if (input->local_ip.family != 0) {
@@ -269,6 +272,7 @@
 
 	memset(&input, 0, sizeof(input));
 	input.roots = roots;
+	input.module = service->name;
 	return master_service_settings_read(service, &input, error_r);
 }
 
--- a/src/lib-master/master-service-settings.h	Mon May 11 20:09:33 2009 -0400
+++ b/src/lib-master/master-service-settings.h	Mon May 11 20:10:30 2009 -0400
@@ -20,6 +20,8 @@
 	const struct dynamic_settings_parser *dyn_parsers;
 	bool preserve_home;
 
+	const char *module;
+	const char *service;
 	const char *username;
 	struct ip_addr local_ip, remote_ip;
 };
--- a/src/lib-storage/mail-storage-service.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/lib-storage/mail-storage-service.c	Mon May 11 20:10:30 2009 -0400
@@ -334,6 +334,8 @@
 	set_input.dyn_parsers = mail_storage_get_dynamic_parsers();
 	set_input.preserve_home = preserve_home;
 	if (input != NULL) {
+		set_input.module = input->module;
+		set_input.service = input->service;
 		set_input.username = input->username;
 		set_input.local_ip = input->local_ip;
 		set_input.remote_ip = input->remote_ip;
--- a/src/lib-storage/mail-storage-service.h	Mon May 11 20:09:33 2009 -0400
+++ b/src/lib-storage/mail-storage-service.h	Mon May 11 20:10:30 2009 -0400
@@ -17,6 +17,8 @@
 };
 
 struct mail_storage_service_input {
+	const char *module;
+	const char *service;
 	const char *username;
 	struct ip_addr local_ip, remote_ip;
 };
--- a/src/login-common/login-settings.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/login-common/login-settings.c	Mon May 11 20:10:30 2009 -0400
@@ -1,6 +1,6 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "lib.h"
+#include "common.h"
 #include "settings-parser.h"
 #include "master-service-settings.h"
 #include "login-settings.h"
@@ -178,11 +178,16 @@
 		&login_setting_parser_info,
 		NULL
 	};
+	struct master_service_settings_input input;
 	const char *error;
 	void **sets;
 
-	if (master_service_settings_read_simple(service, set_roots,
-						&error) < 0)
+	memset(&input, 0, sizeof(input));
+	input.roots = set_roots;
+	input.module = "login";
+	input.service = login_protocol;
+
+	if (master_service_settings_read(service, &input, &error) < 0)
 		i_fatal("Error reading configuration: %s", error);
 
 	sets = master_service_settings_get_others(service);
--- a/src/pop3/main.c	Mon May 11 20:09:33 2009 -0400
+++ b/src/pop3/main.c	Mon May 11 20:10:30 2009 -0400
@@ -112,6 +112,8 @@
 	}
 
 	memset(&input, 0, sizeof(input));
+	input.module = "pop3";
+	input.service = "pop3";
 	input.username = getenv("USER");
 	if (input.username == NULL) {
 		if (IS_STANDALONE())