changeset 9179:f8460b27db00 HEAD

doveconf: With -p only protocol-specific settings are returned. -n and -a return human-readable output.
author Timo Sirainen <tss@iki.fi>
date Thu, 30 Apr 2009 15:02:44 -0400
parents 13bbdd7b15b8
children ea0b68301004
files src/config/Makefile.am src/config/common.h src/config/config-connection.c src/config/config-connection.h src/config/config-parser.c src/config/config-parser.h src/config/config-request.c src/config/config-request.h src/config/main.c src/config/settings-get.pl src/imap/imap-settings.c src/lib-lda/lda-settings.c src/lib-settings/settings-parser.h src/pop3/pop3-settings.c
diffstat 14 files changed, 481 insertions(+), 261 deletions(-) [+]
line wrap: on
line diff
--- a/src/config/Makefile.am	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/Makefile.am	Thu Apr 30 15:02:44 2009 -0400
@@ -22,11 +22,11 @@
 	all-settings.c \
 	config-connection.c \
 	config-parser.c \
+	config-request.c \
 	main.c
 
 noinst_HEADERS = \
 	all-settings.h \
-	common.h \
 	config-connection.h \
 	config-parser.h
 
--- a/src/config/common.h	Thu Apr 30 14:48:54 2009 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,8 +0,0 @@
-#ifndef COMMON_H
-#define COMMON_H
-
-#include "lib.h"
-
-extern ARRAY_TYPE(const_string) config_strings;
-
-#endif
--- a/src/config/config-connection.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/config-connection.c	Thu Apr 30 15:02:44 2009 -0400
@@ -1,19 +1,18 @@
 /* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "lib.h"
 #include "array.h"
-#include "str.h"
 #include "llist.h"
 #include "ioloop.h"
 #include "network.h"
 #include "istream.h"
 #include "ostream.h"
 #include "env-util.h"
+#include "config-request.h"
 #include "config-connection.h"
 
 #include <stdlib.h>
 #include <unistd.h>
-#include <fcntl.h>
 
 #define MAX_INBUF_SIZE 1024
 
@@ -46,24 +45,176 @@
 	return t_strsplit(line, "\t");
 }
 
+static void
+config_request_output(const char *key, const char *value,
+		      bool list ATTR_UNUSED, void *context)
+{
+	struct ostream *output = context;
+
+	o_stream_send_str(output, key);
+	o_stream_send_str(output, "=");
+	o_stream_send_str(output, value);
+	o_stream_send_str(output, "\n");
+}
+
+struct config_request_get_string_ctx {
+	pool_t pool;
+	ARRAY_TYPE(const_string) strings;
+};
+
+static void
+config_request_get_strings(const char *key, const char *value,
+			   bool list, void *context)
+{
+	struct config_request_get_string_ctx *ctx = context;
+
+	value = p_strdup_printf(ctx->pool, list ? "-%s=%s" : "%s=%s",
+				key, value);
+	array_append(&ctx->strings, &value, 1);
+}
+
+static int config_string_cmp(const void *p1, const void *p2)
+{
+	const char *s1 = *(const char *const *)p1;
+	const char *s2 = *(const char *const *)p2;
+	unsigned int i = 0;
+
+	while (s1[i] == s2[i]) {
+		if (s1[i] == '\0' || s1[i] == '=')
+			return 0;
+		i++;
+	}
+
+	if (s1[i] == '=')
+		return -1;
+	if (s2[i] == '=')
+		return 1;
+	return s1[i] - s2[i];
+}
+
+static unsigned int prefix_stack_pop(ARRAY_TYPE(uint) *stack)
+{
+	const unsigned int *indexes;
+	unsigned int idx, count;
+
+	indexes = array_get(stack, &count);
+	idx = count <= 1 ? -1U : indexes[count-2];
+	array_delete(stack, count-1, 1);
+	return idx;
+}
+
+static void config_connection_request_human(struct ostream *output,
+					    const char *service,
+					    enum config_dump_flags flags)
+{
+	static const char *ident_str = "               ";
+	ARRAY_TYPE(const_string) prefixes_arr;
+	ARRAY_TYPE(uint) prefix_idx_stack;
+	struct config_request_get_string_ctx ctx;
+	const char **strings, *const *args, *p, *str, *const *prefixes;
+	const char *key, *value;
+	unsigned int i, j, count, len, prefix_count, skip_len;
+	unsigned int indent = 0, prefix_idx = -1U;
+
+	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);
+
+	strings = array_get_modifiable(&ctx.strings, &count);
+	qsort(strings, count, sizeof(*strings), config_string_cmp);
+
+	p_array_init(&prefixes_arr, ctx.pool, 32);
+	for (i = 0; i < count && strings[i][0] == '-'; i++) T_BEGIN {
+		p = strchr(strings[i], '=');
+		i_assert(p != NULL);
+		for (args = t_strsplit(p + 1, " "); *args != NULL; args++) {
+			str = p_strdup_printf(ctx.pool, "%s/%s/",
+					      t_strcut(strings[i]+1, '='),
+					      *args);
+			array_append(&prefixes_arr, &str, 1);
+		}
+	} T_END;
+	prefixes = array_get(&prefixes_arr, &prefix_count);
+
+	p_array_init(&prefix_idx_stack, ctx.pool, 8);
+	for (; i < count; i++) T_BEGIN {
+		value = strchr(strings[i], '=');
+		i_assert(value != NULL);
+		key = t_strdup_until(strings[i], value);
+		value++;
+
+		j = 0;
+		while (prefix_idx != -1U) {
+			len = strlen(prefixes[prefix_idx]);
+			if (strncmp(prefixes[prefix_idx], key, len) != 0) {
+				prefix_idx = prefix_stack_pop(&prefix_idx_stack);
+				indent--;
+				o_stream_send(output, ident_str, indent*2);
+				o_stream_send_str(output, "}\n");
+			} else if (strchr(key + len, '/') == NULL) {
+				/* keep the prefix */
+				j = prefix_count;
+				break;
+			} else {
+				/* subprefix */
+				break;
+			}
+		}
+		for (; j < prefix_count; j++) {
+			len = strlen(prefixes[j]);
+			if (strncmp(prefixes[j], key, len) == 0 &&
+			    strchr(key + len, '/') == NULL) {
+				key += prefix_idx == -1U ? 0 :
+					strlen(prefixes[prefix_idx]);
+				o_stream_send(output, ident_str, indent*2);
+				o_stream_send_str(output, t_strcut(key, '/'));
+				o_stream_send_str(output, " {\n");
+				indent++;
+				prefix_idx = j;
+				array_append(&prefix_idx_stack, &prefix_idx, 1);
+				break;
+			}
+		}
+		skip_len = prefix_idx == -1U ? 0 : strlen(prefixes[prefix_idx]);
+		i_assert(strncmp(prefixes[prefix_idx], strings[i], skip_len) == 0);
+		o_stream_send(output, ident_str, indent*2);
+		key = strings[i] + skip_len;
+		value = strchr(key, '=');
+		o_stream_send(output, key, value-key);
+		o_stream_send_str(output, " = ");
+		o_stream_send_str(output, value+1);
+		o_stream_send(output, "\n", 1);
+	} T_END;
+
+	while (prefix_idx != -1U) {
+		prefix_idx = prefix_stack_pop(&prefix_idx_stack);
+		indent--;
+		o_stream_send(output, ident_str, indent*2);
+		o_stream_send_str(output, "}\n");
+	}
+
+	array_free(&ctx.strings);
+	pool_unref(&ctx.pool);
+}
+
 static void config_connection_request(struct config_connection *conn,
 				      const char *const *args,
 				      enum config_dump_flags flags)
 {
-	const char *const *strings;
-	unsigned int i, count;
-	string_t *str;
+	const char *service = "";
+
+	/* [<service> [<args>]] */
+	if (args[0] != NULL)
+		service = args[0];
 
-	/* <process> [<args>] */
-	str = t_str_new(256);
-	strings = array_get(&config_strings, &count);
 	o_stream_cork(conn->output);
-	for (i = 0; i < count; i += 2) {
-		str_truncate(str, 0);
-		str_printfa(str, "%s=%s\n", strings[i], strings[i+1]);
-		o_stream_send(conn->output, str_data(str), str_len(str));
+	if ((flags & CONFIG_DUMP_FLAG_HUMAN) == 0) {
+		config_request_handle(service, flags, config_request_output,
+				      conn->output);
+		o_stream_send_str(conn->output, "\n");
+	} else {
+		config_connection_request_human(conn->output, service, flags);
 	}
-	o_stream_send_str(conn->output, "\n");
 	o_stream_uncork(conn->output);
 }
 
@@ -138,20 +289,22 @@
 	const char *args[2] = { service, NULL };
 
 	conn = config_connection_create(fd);
-        config_connection_request(conn, args, flags);
+	config_connection_request(conn, args, flags);
 	config_connection_destroy(conn);
 }
 
-void config_connection_putenv(void)
+static void config_request_putenv(const char *key, const char *value,
+				  bool list ATTR_UNUSED,
+				  void *context ATTR_UNUSED)
 {
-	const char *const *strings;
-	unsigned int i, count;
+	T_BEGIN {
+		env_put(t_strconcat(t_str_ucase(key), "=", value, NULL));
+	} T_END;
+}
 
-	strings = array_get(&config_strings, &count);
-	for (i = 0; i < count; i += 2) T_BEGIN {
-		env_put(t_strconcat(t_str_ucase(strings[i]), "=",
-				    strings[i+1], NULL));
-	} T_END;
+void config_connection_putenv(const char *service)
+{
+	config_request_handle(service, 0, config_request_putenv, NULL);
 }
 
 void config_connections_destroy_all(void)
--- a/src/config/config-connection.h	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/config-connection.h	Thu Apr 30 15:02:44 2009 -0400
@@ -1,17 +1,14 @@
 #ifndef CONFIG_CONNECTION_H
 #define CONFIG_CONNECTION_H
 
-enum config_dump_flags {
-	CONFIG_DUMP_FLAG_HUMAN		= 0x01,
-	CONFIG_DUMP_FLAG_DEFAULTS	= 0x02
-};
+enum config_dump_flags;
 
 struct config_connection *config_connection_create(int fd);
 void config_connection_destroy(struct config_connection *conn);
 
 void config_connection_dump_request(int fd, const char *service,
 				    enum config_dump_flags flags);
-void config_connection_putenv(void);
+void config_connection_putenv(const char *service);
 
 void config_connections_destroy_all(void);
 
--- a/src/config/config-parser.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/config-parser.c	Thu Apr 30 15:02:44 2009 -0400
@@ -24,22 +24,15 @@
 };
 
 static const char *
-config_parse_line(pool_t pool, const char *key, const char *line,
+config_parse_line(const char *key, const char *line,
 		  const struct setting_parser_info **info_r)
 {
-	enum settings_parser_flags parser_flags =
-                SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS;
 	struct config_setting_parser_list *l;
 	bool found = FALSE;
 	int ret;
 
 	*info_r = NULL;
 	for (l = config_setting_parsers; l->module_name != NULL; l++) {
-		if (l->parser == NULL) {
-			l->parser = settings_parser_init(pool, l->root,
-							 parser_flags);
-		}
-
 		ret = settings_parse_line(l->parser, line);
 		if (ret > 0) {
 			found = TRUE;
@@ -51,189 +44,6 @@
 	return found ? NULL : t_strconcat("Unknown setting: ", key, NULL);
 }
 
-struct settings_export_context {
-	pool_t pool;
-	ARRAY_TYPE(const_string) *dest;
-	string_t *value;
-	string_t *prefix;
-	struct hash_table *keys;
-	bool export_defaults;
-};
-
-static void settings_export(struct settings_export_context *ctx,
-			    const struct setting_parser_info *info,
-			    const void *set)
-{
-	const struct setting_define *def;
-	const void *value, *default_value;
-	void *const *children = NULL;
-	unsigned int i, count, prefix_len;
-	const char *str;
-	char *key;
-
-	for (def = info->defines; def->key != NULL; def++) {
-		value = CONST_PTR_OFFSET(set, def->offset);
-		default_value = info->defaults == NULL ? NULL :
-			CONST_PTR_OFFSET(info->defaults, def->offset);
-
-		count = 0;
-		str_truncate(ctx->value, 0);
-		switch (def->type) {
-		case SET_INTERNAL:
-			break;
-		case SET_BOOL: {
-			const bool *val = value, *dval = default_value;
-			if (ctx->export_defaults ||
-			    dval == NULL || *val != *dval) {
-				str_append(ctx->value,
-					   *val ? "yes" : "no");
-			}
-			break;
-		}
-		case SET_UINT: {
-			const unsigned int *val = value, *dval = default_value;
-			if (ctx->export_defaults ||
-			    dval == NULL || *val != *dval)
-				str_printfa(ctx->value, "%u", *val);
-			break;
-		}
-		case SET_STR_VARS: {
-			const char *const *val = value, *sval;
-			const char *const *_dval = default_value;
-			const char *dval = _dval == NULL ? NULL : *_dval;
-
-			i_assert(*val == NULL ||
-				 **val == SETTING_STRVAR_UNEXPANDED[0]);
-
-			sval = *val == NULL ? NULL : (*val + 1);
-			if ((ctx->export_defaults ||
-			     null_strcmp(sval, dval) != 0) && sval != NULL)
-				str_append(ctx->value, sval);
-			break;
-		}
-		case SET_STR: {
-			const char *const *val = value;
-			const char *const *_dval = default_value;
-			const char *dval = _dval == NULL ? NULL : *_dval;
-
-			if ((ctx->export_defaults ||
-			     null_strcmp(*val, dval) != 0) && *val != NULL)
-				str_append(ctx->value, *val);
-			break;
-		}
-		case SET_ENUM: {
-			const char *const *val = value;
-			const char *const *_dval = default_value;
-			const char *dval = _dval == NULL ? NULL : *_dval;
-			unsigned int len = strlen(*val);
-
-			if (ctx->export_defaults ||
-			    strncmp(*val, dval, len) != 0 ||
-			    ((*val)[len] != ':' && (*val)[len] != '\0'))
-				str_append(ctx->value, *val);
-			break;
-		}
-		case SET_DEFLIST: {
-			const ARRAY_TYPE(void_array) *val = value;
-
-			if (!array_is_created(val))
-				break;
-
-			children = array_get(val, &count);
-			for (i = 0; i < count; i++) {
-				if (i > 0)
-					str_append_c(ctx->value, ' ');
-				str_printfa(ctx->value, "%u", i);
-			}
-			break;
-		}
-		case SET_STRLIST: {
-			const ARRAY_TYPE(const_string) *val = value;
-			const char *const *strings;
-
-			if (!array_is_created(val))
-				break;
-
-			key = p_strconcat(ctx->pool, str_c(ctx->prefix),
-					  def->key, NULL);
-
-			if (hash_table_lookup(ctx->keys, key) != NULL) {
-				/* already added all of these */
-				break;
-			}
-			hash_table_insert(ctx->keys, key, key);
-
-			str = key;
-			array_append(ctx->dest, &str, 1);
-			str = "0";
-			array_append(ctx->dest, &str, 1);
-
-			strings = array_get(val, &count);
-			i_assert(count % 2 == 0);
-			for (i = 0; i < count; i += 2) {
-				str = p_strdup_printf(ctx->pool, "%s%s%c0%c%s",
-						      str_c(ctx->prefix),
-						      def->key,
-						      SETTINGS_SEPARATOR,
-						      SETTINGS_SEPARATOR,
-						      strings[i]);
-				array_append(ctx->dest, &str, 1);
-				str = p_strdup(ctx->pool, strings[i+1]);
-				array_append(ctx->dest, &str, 1);
-			}
-			count = 0;
-			break;
-		}
-		}
-		if (str_len(ctx->value) > 0) {
-			key = p_strconcat(ctx->pool, str_c(ctx->prefix),
-					  def->key, NULL);
-			if (hash_table_lookup(ctx->keys, key) == NULL) {
-				str = key;
-				array_append(ctx->dest, &str, 1);
-				str = p_strdup(ctx->pool, str_c(ctx->value));
-				array_append(ctx->dest, &str, 1);
-				hash_table_insert(ctx->keys, key, key);
-			}
-		}
-
-		prefix_len = str_len(ctx->prefix);
-		for (i = 0; i < count; i++) {
-			str_append(ctx->prefix, def->key);
-			str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
-			str_printfa(ctx->prefix, "%u", i);
-			str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
-			settings_export(ctx, def->list_info, children[i]);
-
-			str_truncate(ctx->prefix, prefix_len);
-		}
-	}
-}
-
-static void config_export(pool_t pool, ARRAY_TYPE(const_string) *dest)
-{
-	struct config_setting_parser_list *l;
-	struct settings_export_context ctx;
-	const void *set;
-
-	memset(&ctx, 0, sizeof(ctx));
-	ctx.pool = pool;
-	ctx.dest = dest;
-	ctx.export_defaults = FALSE;
-	ctx.value = t_str_new(256);
-	ctx.prefix = t_str_new(64);
-	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 (l->parser != NULL) {
-			set = settings_parser_get(l->parser);
-			settings_export(&ctx, l->root, set);
-		}
-	}
-	hash_table_destroy(&ctx.keys);
-}
-
 static const char *info_type_name_find(const struct setting_parser_info *info)
 {
 	unsigned int i;
@@ -261,9 +71,10 @@
 	return t_strconcat(t_strdup_until(input->path, p+1), path, NULL);
 }
 
-void config_parse_file(pool_t dest_pool, ARRAY_TYPE(const_string) *dest,
-		       const char *path, const char *service)
+void config_parse_file(const char *path, const char *service)
 {
+	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);
 	ARRAY_TYPE(const_string) auth_defaults;
@@ -287,7 +98,12 @@
 	t_array_init(&pathlen_stack, 10);
 	t_array_init(&auth_defaults, 32);
 
-	errormsg = config_parse_line(pool, "0", "auth=0", &info);
+	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);
+	}
+
+	errormsg = config_parse_line("0", "auth=0", &info);
 	i_assert(errormsg == NULL);
 
 	memset(&root, 0, sizeof(root));
@@ -411,10 +227,10 @@
 
 				str_truncate(str, 0);
 				str_printfa(str, "auth/0/%s=%s", key + 5, line);
-				errormsg = config_parse_line(pool, key + 5, str_c(str), &info);
+				errormsg = config_parse_line(key + 5, str_c(str), &info);
 				array_append(&auth_defaults, &s, 1);
 			} else {
-				errormsg = config_parse_line(pool, key, str_c(str), &info);
+				errormsg = config_parse_line(key, str_c(str), &info);
 			}
 		} else if (strcmp(key, "}") != 0 || *line != '\0') {
 			/* b) + errors */
@@ -458,7 +274,7 @@
 				if (cur_counter == 0 && strcmp(key, "auth") == 0) {
 					/* already added this */
 				} else {
-					errormsg = config_parse_line(pool, key,
+					errormsg = config_parse_line(key,
 								     str_c(str), &info);
 				}
 
@@ -473,7 +289,7 @@
 					str_append(str, type_name);
 					str_append_c(str, '=');
 					str_append(str, name);
-					errormsg = config_parse_line(pool, type_name,
+					errormsg = config_parse_line(type_name,
 								     str_c(str), &info);
 
 					str_truncate(str, pathlen);
@@ -491,7 +307,7 @@
 						p = strchr(lines[i], '=');
 						str_append(str, lines[i]);
 
-						errormsg = config_parse_line(pool, t_strdup_until(lines[i], p), str_c(str), &info);
+						errormsg = config_parse_line(t_strdup_until(lines[i], p), str_c(str), &info);
 						i_assert(errormsg == NULL);
 					}
 				}
@@ -528,14 +344,9 @@
 		goto prevfile;
 
 	for (l = config_setting_parsers; l->module_name != NULL; l++) {
-		if (l->parser == NULL)
-			continue;
-
 		if (!settings_parser_check(l->parser, pool, &errormsg)) {
 			i_fatal("Error in configuration file %s: %s",
 				path, errormsg);
 		}
 	}
-
-	config_export(dest_pool, dest);
 }
--- a/src/config/config-parser.h	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/config-parser.h	Thu Apr 30 15:02:44 2009 -0400
@@ -1,7 +1,6 @@
 #ifndef CONFIG_PARSER_H
 #define CONFIG_PARSER_H
 
-void config_parse_file(pool_t dest_pool, ARRAY_TYPE(const_string) *dest,
-		       const char *path, const char *service);
+void config_parse_file(const char *path, const char *service);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-request.c	Thu Apr 30 15:02:44 2009 -0400
@@ -0,0 +1,236 @@
+/* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hash.h"
+#include "ostream.h"
+#include "settings-parser.h"
+#include "all-settings.h"
+#include "config-request.h"
+
+struct settings_export_context {
+	pool_t pool;
+	string_t *value;
+	string_t *prefix;
+	struct hash_table *keys;
+	bool export_defaults;
+
+	config_request_callback_t *callback;
+	void *context;
+};
+
+static bool parsers_are_connected(struct setting_parser_info *root,
+				  struct setting_parser_info *info)
+{
+	struct setting_parser_info *const *dep, *p;
+
+	/* we're trying to find info or its parents from root's dependencies. */
+
+	for (p = info; p != NULL; p = p->parent) {
+		if (p == root)
+			return TRUE;
+	}
+
+	if (root->dependencies != NULL) {
+		for (dep = root->dependencies; *dep != NULL; dep++) {
+			for (p = info; p != NULL; p = p->parent) {
+				if (p == *dep)
+					return TRUE;
+			}
+		}
+	}
+	return FALSE;
+}
+
+static bool
+config_setting_parser_is_in_service(struct config_setting_parser_list *list,
+				    const char *service)
+{
+	struct config_setting_parser_list *l;
+
+	if (strcmp(list->module_name, service) == 0)
+		return TRUE;
+
+	for (l = config_setting_parsers; l->module_name != NULL; l++) {
+		if (strcmp(l->module_name, service) != 0)
+			continue;
+
+		/* see if we can find a way to get from the original parser
+		   to this parser */
+		if (parsers_are_connected(l->root, list->root))
+			return TRUE;
+	}
+	return FALSE;
+}
+
+static void settings_export(struct settings_export_context *ctx,
+			    const struct setting_parser_info *info,
+			    const void *set)
+{
+	const struct setting_define *def;
+	const void *value, *default_value;
+	void *const *children = NULL;
+	unsigned int i, count, prefix_len;
+	const char *str;
+	char *key;
+
+	for (def = info->defines; def->key != NULL; def++) {
+		value = CONST_PTR_OFFSET(set, def->offset);
+		default_value = info->defaults == NULL ? NULL :
+			CONST_PTR_OFFSET(info->defaults, def->offset);
+
+		count = 0;
+		str_truncate(ctx->value, 0);
+		switch (def->type) {
+		case SET_INTERNAL:
+			break;
+		case SET_BOOL: {
+			const bool *val = value, *dval = default_value;
+			if (ctx->export_defaults ||
+			    dval == NULL || *val != *dval) {
+				str_append(ctx->value,
+					   *val ? "yes" : "no");
+			}
+			break;
+		}
+		case SET_UINT: {
+			const unsigned int *val = value, *dval = default_value;
+			if (ctx->export_defaults ||
+			    dval == NULL || *val != *dval)
+				str_printfa(ctx->value, "%u", *val);
+			break;
+		}
+		case SET_STR_VARS: {
+			const char *const *val = value, *sval;
+			const char *const *_dval = default_value;
+			const char *dval = _dval == NULL ? NULL : *_dval;
+
+			i_assert(*val == NULL ||
+				 **val == SETTING_STRVAR_UNEXPANDED[0]);
+
+			sval = *val == NULL ? NULL : (*val + 1);
+			if ((ctx->export_defaults ||
+			     null_strcmp(sval, dval) != 0) && sval != NULL)
+				str_append(ctx->value, sval);
+			break;
+		}
+		case SET_STR: {
+			const char *const *val = value;
+			const char *const *_dval = default_value;
+			const char *dval = _dval == NULL ? NULL : *_dval;
+
+			if ((ctx->export_defaults ||
+			     null_strcmp(*val, dval) != 0) && *val != NULL)
+				str_append(ctx->value, *val);
+			break;
+		}
+		case SET_ENUM: {
+			const char *const *val = value;
+			const char *const *_dval = default_value;
+			const char *dval = _dval == NULL ? NULL : *_dval;
+			unsigned int len = strlen(*val);
+
+			if (ctx->export_defaults ||
+			    strncmp(*val, dval, len) != 0 ||
+			    ((*val)[len] != ':' && (*val)[len] != '\0'))
+				str_append(ctx->value, *val);
+			break;
+		}
+		case SET_DEFLIST: {
+			const ARRAY_TYPE(void_array) *val = value;
+
+			if (!array_is_created(val))
+				break;
+
+			children = array_get(val, &count);
+			for (i = 0; i < count; i++) {
+				if (i > 0)
+					str_append_c(ctx->value, ' ');
+				str_printfa(ctx->value, "%u", i);
+			}
+			break;
+		}
+		case SET_STRLIST: {
+			const ARRAY_TYPE(const_string) *val = value;
+			const char *const *strings;
+
+			if (!array_is_created(val))
+				break;
+
+			key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+					  def->key, NULL);
+
+			if (hash_table_lookup(ctx->keys, key) != NULL) {
+				/* already added all of these */
+				break;
+			}
+			hash_table_insert(ctx->keys, key, key);
+			ctx->callback(key, "0", TRUE, ctx->context);
+
+			strings = array_get(val, &count);
+			i_assert(count % 2 == 0);
+			for (i = 0; i < count; i += 2) {
+				str = p_strdup_printf(ctx->pool, "%s%s%c0%c%s",
+						      str_c(ctx->prefix),
+						      def->key,
+						      SETTINGS_SEPARATOR,
+						      SETTINGS_SEPARATOR,
+						      strings[i]);
+				ctx->callback(str, strings[i+1], FALSE,
+					      ctx->context);
+			}
+			count = 0;
+			break;
+		}
+		}
+		if (str_len(ctx->value) > 0) {
+			key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+					  def->key, NULL);
+			if (hash_table_lookup(ctx->keys, key) == NULL) {
+				ctx->callback(key, str_c(ctx->value),
+					      def->type == SET_DEFLIST,
+					      ctx->context);
+				hash_table_insert(ctx->keys, key, key);
+			}
+		}
+
+		prefix_len = str_len(ctx->prefix);
+		for (i = 0; i < count; i++) {
+			str_append(ctx->prefix, def->key);
+			str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+			str_printfa(ctx->prefix, "%u", i);
+			str_append_c(ctx->prefix, SETTINGS_SEPARATOR);
+			settings_export(ctx, def->list_info, children[i]);
+
+			str_truncate(ctx->prefix, prefix_len);
+		}
+	}
+}
+
+void config_request_handle(const char *service, enum config_dump_flags flags,
+			   config_request_callback_t *callback, void *context)
+{
+	struct config_setting_parser_list *l;
+	struct settings_export_context ctx;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.pool = pool_alloconly_create("config request", 10240);
+	ctx.callback = callback;
+	ctx.context = context;
+	ctx.export_defaults = (flags & CONFIG_DUMP_FLAG_DEFAULTS) != 0;
+	ctx.value = t_str_new(256);
+	ctx.prefix = t_str_new(64);
+	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)) {
+			settings_export(&ctx, l->root,
+					settings_parser_get(l->parser));
+		}
+	}
+	hash_table_destroy(&ctx.keys);
+	pool_unref(&ctx.pool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-request.h	Thu Apr 30 15:02:44 2009 -0400
@@ -0,0 +1,15 @@
+#ifndef CONFIG_REQUEST_H
+#define CONFIG_REQUEST_H
+
+enum config_dump_flags {
+	CONFIG_DUMP_FLAG_HUMAN		= 0x01,
+	CONFIG_DUMP_FLAG_DEFAULTS	= 0x02
+};
+
+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,
+			   config_request_callback_t *callback, void *context);
+
+#endif
--- a/src/config/main.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/main.c	Thu Apr 30 15:02:44 2009 -0400
@@ -1,26 +1,21 @@
 /* Copyright (C) 2005-2009 Dovecot authors, see the included COPYING file */
 
-#include "common.h"
+#include "lib.h"
 #include "array.h"
 #include "env-util.h"
 #include "master-service.h"
 #include "config-connection.h"
 #include "config-parser.h"
+#include "config-request.h"
 
 #include <stdlib.h>
 #include <unistd.h>
 
-ARRAY_TYPE(const_string) config_strings;
-
 static struct master_service *service;
-static pool_t config_pool;
 
 static void main_init(const char *service_name)
 {
-	config_pool = pool_alloconly_create("config parser", 10240);
-	p_array_init(&config_strings, config_pool, 256);
-	config_parse_file(config_pool, &config_strings,
-			  master_service_get_config_path(service),
+	config_parse_file(master_service_get_config_path(service),
 			  service_name);
 }
 
@@ -67,16 +62,16 @@
 
 	if (master_service_get_socket_count(service) > 0)
 		master_service_run(service, client_connected);
-	else if (exec_args == NULL)
-		config_connection_dump_request(STDOUT_FILENO, "master", flags);
-	else {
-		config_connection_putenv();
+	else if (exec_args == NULL) {
+		config_connection_dump_request(STDOUT_FILENO,
+					       service_name, flags);
+	} else {
+		config_connection_putenv(service_name);
 		env_put("DOVECONF_ENV=1");
 		execvp(exec_args[0], exec_args);
 		i_fatal("execvp(%s) failed: %m", exec_args[0]);
 	}
 	config_connections_destroy_all();
-	pool_unref(&config_pool);
 	master_service_deinit(&service);
         return 0;
 }
--- a/src/config/settings-get.pl	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/config/settings-get.pl	Thu Apr 30 15:02:44 2009 -0400
@@ -31,7 +31,8 @@
 	$state++;
       } elsif (/^(static )?struct setting_parser_info (.*) = {/) {
 	$state++;
-	$parsers{$2} = 1;
+	my $name = $2;
+	$parsers{$name} = 1 if ($name !~ /\*/);
       } elsif (/^extern struct setting_parser_info (.*);/) {
 	$externs .= "extern struct setting_parser_info $1;\n";
       } elsif (/\/\* <settings checks> \*\//) {
--- a/src/imap/imap-settings.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/imap/imap-settings.c	Thu Apr 30 15:02:44 2009 -0400
@@ -49,6 +49,11 @@
 	MEMBER(imap_id_log) ""
 };
 
+static struct setting_parser_info *imap_setting_dependencies[] = {
+	&mail_user_setting_parser_info,
+	NULL
+};
+
 struct setting_parser_info imap_setting_parser_info = {
 	MEMBER(defines) imap_setting_defines,
 	MEMBER(defaults) &imap_default_settings,
@@ -59,5 +64,6 @@
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
 	MEMBER(struct_size) sizeof(struct imap_settings),
-	MEMBER(check_func) NULL
+	MEMBER(check_func) NULL,
+	MEMBER(dependencies) imap_setting_dependencies
 };
--- a/src/lib-lda/lda-settings.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/lib-lda/lda-settings.c	Thu Apr 30 15:02:44 2009 -0400
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "settings-parser.h"
+#include "mail-storage-settings.h"
 #include "lda-settings.h"
 
 #include <stddef.h>
@@ -42,6 +43,11 @@
 	MEMBER(lda_mailbox_autosubscribe) FALSE
 };
 
+static struct setting_parser_info *lda_setting_dependencies[] = {
+	&mail_user_setting_parser_info,
+	NULL
+};
+
 struct setting_parser_info lda_setting_parser_info = {
 	MEMBER(defines) lda_setting_defines,
 	MEMBER(defaults) &lda_default_settings,
@@ -53,10 +59,11 @@
 	MEMBER(type_offset) (size_t)-1,
 	MEMBER(struct_size) sizeof(struct lda_settings),
 #ifdef CONFIG_BINARY
-	MEMBER(check_func) NULL
+	MEMBER(check_func) NULL,
 #else
-	MEMBER(check_func) lda_settings_check
+	MEMBER(check_func) lda_settings_check,
 #endif
+	MEMBER(dependencies) lda_setting_dependencies
 };
 
 static bool lda_settings_check(void *_set, pool_t pool ATTR_UNUSED,
--- a/src/lib-settings/settings-parser.h	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/lib-settings/settings-parser.h	Thu Apr 30 15:02:44 2009 -0400
@@ -58,6 +58,8 @@
 	size_t type_offset;
 	size_t struct_size;
 	bool (*check_func)(void *set, pool_t pool, const char **error_r);
+	struct setting_parser_info *const *dependencies;
+
 };
 ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info);
 
--- a/src/pop3/pop3-settings.c	Thu Apr 30 14:48:54 2009 -0400
+++ b/src/pop3/pop3-settings.c	Thu Apr 30 15:02:44 2009 -0400
@@ -44,6 +44,11 @@
 	MEMBER(pop3_logout_format) "top=%t/%p, retr=%r/%b, del=%d/%m, size=%s"
 };
 
+static struct setting_parser_info *pop3_setting_dependencies[] = {
+	&mail_user_setting_parser_info,
+	NULL
+};
+
 struct setting_parser_info pop3_setting_parser_info = {
 	MEMBER(defines) pop3_setting_defines,
 	MEMBER(defaults) &pop3_default_settings,
@@ -54,5 +59,6 @@
 	MEMBER(parent_offset) (size_t)-1,
 	MEMBER(type_offset) (size_t)-1,
 	MEMBER(struct_size) sizeof(struct pop3_settings),
-	MEMBER(check_func) NULL
+	MEMBER(check_func) NULL,
+	MEMBER(dependencies) pop3_setting_dependencies
 };