changeset 9002:9d0037a997f4 HEAD

Initial commit for config rewrite.
author Timo Sirainen <tss@iki.fi>
date Tue, 27 Jan 2009 18:21:53 -0500
parents 77c67307d316
children f5e35bc340bf
files .hgignore TODO configure.in src/Makefile.am src/auth/Makefile.am src/auth/auth-client-connection.c src/auth/auth-master-connection.c src/auth/auth-request-handler.c src/auth/auth-request.c src/auth/auth-settings.c src/auth/auth-settings.h src/auth/auth-worker-server.c src/auth/auth-worker-server.h src/auth/auth.c src/auth/auth.h src/auth/db-ldap.c src/auth/main.c src/auth/mech-anonymous.c src/auth/mech-digest-md5.c src/auth/mech-gssapi.c src/auth/mech-rpa.c src/auth/mech-winbind.c src/auth/mech.c src/auth/mech.h src/auth/passdb-cache.c src/auth/passdb-cache.h src/auth/passdb-passwd-file.c src/auth/passdb.c src/auth/passdb.h src/auth/userdb-passwd-file.c src/auth/userdb-prefetch.c src/auth/userdb.c src/auth/userdb.h 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/main.c src/config/settings-get.pl src/deliver/Makefile.am src/deliver/deliver-settings.c src/deliver/deliver-settings.h src/deliver/deliver.c src/deliver/deliver.h src/imap-login/Makefile.am src/imap-login/client-authenticate.c src/imap-login/client.c src/imap-login/imap-proxy.c src/imap/Makefile.am src/imap/client.c src/imap/client.h src/imap/cmd-append.c src/imap/cmd-capability.c src/imap/cmd-delete.c src/imap/cmd-id.c src/imap/cmd-idle.c src/imap/cmd-subscribe.c src/imap/common.h src/imap/imap-fetch-body.c src/imap/imap-settings.c src/imap/imap-settings.h src/imap/imap-sync.c src/imap/main.c src/lib-settings/Makefile.am src/lib-settings/settings-parser.c src/lib-settings/settings-parser.h src/lib-storage/Makefile.am src/lib-storage/index/cydir/cydir-storage.c src/lib-storage/index/dbox/Makefile.am src/lib-storage/index/dbox/dbox-file.c src/lib-storage/index/dbox/dbox-settings.c src/lib-storage/index/dbox/dbox-settings.h src/lib-storage/index/dbox/dbox-storage.c src/lib-storage/index/dbox/dbox-storage.h src/lib-storage/index/index-mail.c src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/maildir/Makefile.am src/lib-storage/index/maildir/maildir-copy.c src/lib-storage/index/maildir/maildir-keywords.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-settings.c src/lib-storage/index/maildir/maildir-settings.h src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-storage.h src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/mbox/Makefile.am src/lib-storage/index/mbox/mbox-lock.c src/lib-storage/index/mbox/mbox-mail.c src/lib-storage/index/mbox/mbox-save.c src/lib-storage/index/mbox/mbox-settings.c src/lib-storage/index/mbox/mbox-settings.h src/lib-storage/index/mbox/mbox-storage.c src/lib-storage/index/mbox/mbox-storage.h src/lib-storage/index/mbox/mbox-sync.c src/lib-storage/index/raw/raw-storage.c src/lib-storage/index/shared/shared-storage.c src/lib-storage/list/index-mailbox-list.c src/lib-storage/list/mailbox-list-fs-iter.c src/lib-storage/list/mailbox-list-fs.c src/lib-storage/list/mailbox-list-maildir.c src/lib-storage/list/subscription-file.c src/lib-storage/mail-namespace.c src/lib-storage/mail-namespace.h src/lib-storage/mail-storage-private.h src/lib-storage/mail-storage-settings.c src/lib-storage/mail-storage-settings.h src/lib-storage/mail-storage.c src/lib-storage/mail-storage.h src/lib-storage/mail-user.c src/lib-storage/mail-user.h src/lib-storage/mailbox-list-private.h src/lib-storage/mailbox-list.c src/lib-storage/mailbox-list.h src/lib/array-decl.h src/login-common/Makefile.am src/login-common/client-common.c src/login-common/common.h src/login-common/login-settings.c src/login-common/login-settings.h src/login-common/main.c src/login-common/master.c src/login-common/sasl-server.c src/login-common/ssl-proxy-openssl.c src/master/Makefile.am src/master/auth-process.c src/master/child-process.c src/master/child-process.h src/master/dict-process.c src/master/listener.c src/master/listener.h src/master/login-process.c src/master/login-process.h src/master/mail-process.c src/master/mail-process.h src/master/main.c src/master/master-settings-defs.c src/master/master-settings.c src/master/master-settings.h src/master/ssl-init.c src/plugins/acl/acl-mailbox-list.c src/plugins/fts-lucene/fts-backend-lucene.c src/plugins/fts-squat/fts-backend-squat.c src/plugins/fts/fts-storage.c src/plugins/imap-acl/imap-acl-plugin.c src/plugins/imap-quota/imap-quota-plugin.c src/plugins/mbox-snarf/mbox-snarf-plugin.c src/plugins/virtual/virtual-storage.c src/pop3-login/Makefile.am src/pop3-login/client-authenticate.c src/pop3-login/client.c src/pop3-login/pop3-proxy.c src/pop3/Makefile.am src/pop3/client.c src/pop3/client.h src/pop3/commands.c src/pop3/common.h src/pop3/main.c src/pop3/pop3-settings.c src/pop3/pop3-settings.h
diffstat 162 files changed, 5469 insertions(+), 3750 deletions(-) [+]
line wrap: on
line diff
--- a/.hgignore	Mon Jan 26 19:17:54 2009 -0500
+++ b/.hgignore	Tue Jan 27 18:21:53 2009 -0500
@@ -51,6 +51,9 @@
 doc/wiki/Makefile.am
 src/auth/checkpassword-reply
 src/auth/dovecot-auth
+src/config/all-settings.c
+src/config/all-settings.h
+src/config/doveconf
 src/deliver/deliver
 src/dict/dict
 src/imap-login/imap-login
--- a/TODO	Mon Jan 26 19:17:54 2009 -0500
+++ b/TODO	Tue Jan 27 18:21:53 2009 -0500
@@ -1,3 +1,14 @@
+ - config rewrite
+   - deliver: plugin var expanding
+   - master: expands only known vars, not all_settings.. should it expand
+     anything after all? use the 1/0 prefix to mark which vars have been
+     expanded (config vs userdb)?
+   - go through mail-process.c. nfs test?
+   - support !include and !include_try
+   - check plugins to make sure they're using only internal getenv()s,
+     especially fix DEBUG env checks
+   - add back all setting verification code from master
+   - put plugin settings to some plugins array, not envs, var expand
  - proxying: support fallbacking to local (or other?) server if the first
    one is down
 user_attrs {
--- a/configure.in	Mon Jan 26 19:17:54 2009 -0500
+++ b/configure.in	Tue Jan 27 18:21:53 2009 -0500
@@ -2336,6 +2336,14 @@
 AM_CONDITIONAL(BUILD_SOLR, test "$have_solr" = "yes")
 
 dnl **
+dnl ** Settings
+dnl **
+
+dnl get a list of setting .[ch] files, but list .h files first
+SETTING_FILES=`find $srcdir/src -name '*settings.[[ch]]' ! -name all-settings.[[ch]] | sed -e s,$srcdir/src,./src,g -e 's,./src,$(top_srcdir)/src,g' -e 's/^\(.*\)\(.\)$/\2 \1\2/' | sort -r | sed s/^..//|tr '\n' ' '`
+AC_SUBST(SETTING_FILES)
+
+dnl **
 dnl ** capabilities
 dnl **
 
@@ -2380,6 +2388,7 @@
 src/lib-storage/index/shared/Makefile
 src/lib-storage/register/Makefile
 src/auth/Makefile
+src/config/Makefile
 src/deliver/Makefile
 src/dict/Makefile
 src/imap/Makefile
--- a/src/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -20,6 +20,7 @@
 	lib-storage \
 	lib-auth \
 	auth \
+	config \
 	dict \
 	master \
 	login-common \
--- a/src/auth/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -25,6 +25,7 @@
 	-I$(top_srcdir)/src/lib-otp \
 	-DAUTH_MODULE_DIR=\""$(auth_moduledir)"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
+	-DPKG_RUNDIR=\""$(rundir)"\" \
 	$(AUTH_CFLAGS)
 
 dovecot_auth_LDFLAGS = -export-dynamic
@@ -64,6 +65,7 @@
 	auth-master-listener.c \
 	auth-request.c \
 	auth-request-handler.c \
+	auth-settings.c \
 	auth-stream.c \
 	auth-worker-client.c \
 	auth-worker-server.c \
@@ -120,6 +122,7 @@
 	auth-master-listener.h \
 	auth-request.h \
 	auth-request-handler.h \
+	auth-settings.h \
 	auth-stream.h \
 	auth-worker-client.h \
 	auth-worker-server.h \
--- a/src/auth/auth-client-connection.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-client-connection.c	Tue Jan 27 18:21:53 2009 -0500
@@ -58,8 +58,8 @@
 			io_remove(&conn->io);
 	}
 
-	if (conn->auth->verbose_debug) {
-		i_info("client out: %s", conn->auth->verbose_debug_passwords ?
+	if (conn->auth->set->debug) {
+		i_info("client out: %s", conn->auth->set->debug_passwords ?
 		       cmd : reply_line_hide_pass(cmd));
 	}
 }
@@ -117,7 +117,7 @@
 	auth_request_handler_set(conn->request_handler, conn->connect_uid, pid);
 
 	conn->pid = pid;
-	if (conn->auth->verbose_debug)
+	if (conn->auth->set->debug)
 		i_info("new auth connection: pid=%u", conn->pid);
 	return TRUE;
 }
@@ -166,18 +166,18 @@
 auth_client_handle_line(struct auth_client_connection *conn, const char *line)
 {
 	if (strncmp(line, "AUTH\t", 5) == 0) {
-		if (conn->auth->verbose_debug) {
+		if (conn->auth->set->debug) {
 			i_info("client in: %s",
-			       conn->auth->verbose_debug_passwords ? line :
+			       conn->auth->set->debug_passwords ? line :
 			       auth_line_hide_pass(line));
 		}
 		return auth_request_handler_auth_begin(conn->request_handler,
 						       line + 5);
 	}
 	if (strncmp(line, "CONT\t", 5) == 0) {
-		if (conn->auth->verbose_debug) {
+		if (conn->auth->set->debug) {
 			i_info("client in: %s",
-			       conn->auth->verbose_debug_passwords ? line :
+			       conn->auth->set->debug_passwords ? line :
 			       cont_line_hide_pass(line));
 		}
 		return auth_request_handler_auth_continue(conn->request_handler,
--- a/src/auth/auth-master-connection.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-master-connection.c	Tue Jan 27 18:21:53 2009 -0500
@@ -38,7 +38,7 @@
 
 	reply_str = auth_stream_reply_export(reply);
 
-	if (conn->listener->auth->verbose_debug)
+	if (conn->listener->auth->set->debug)
 		i_info("master out: %s", reply_str);
 
 	iov[0].iov_base = reply_str;
@@ -105,7 +105,7 @@
 		break;
 	}
 
-	if (conn->listener->auth->verbose_debug)
+	if (conn->listener->auth->set->debug)
 		i_info("master out: %s", str_c(str));
 
 	str_append_c(str, '\n');
@@ -163,7 +163,7 @@
 static bool
 auth_master_input_line(struct auth_master_connection *conn, const char *line)
 {
-	if (conn->listener->auth->verbose_debug)
+	if (conn->listener->auth->set->debug)
 		i_info("master in: %s", line);
 
 	if (strncmp(line, "REQUEST\t", 8) == 0)
--- a/src/auth/auth-request-handler.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-request-handler.c	Tue Jan 27 18:21:53 2009 -0500
@@ -14,7 +14,6 @@
 
 #include <stdlib.h>
 
-#define DEFAULT_AUTH_FAILURE_DELAY 2
 #define AUTH_FAILURE_DELAY_CHECK_MSECS 500
 
 struct auth_request_handler {
@@ -34,7 +33,6 @@
 static ARRAY_DEFINE(auth_failures_arr, struct auth_request *);
 static struct aqueue *auth_failures;
 static struct timeout *to_auth_failures;
-static unsigned int auth_failure_delay;
 
 static void auth_failure_timeout(void *context);
 
@@ -356,7 +354,7 @@
 
 	hash_table_insert(handler->requests, POINTER_CAST(id), request);
 
-	if (request->auth->ssl_require_client_cert &&
+	if (request->auth->set->ssl_require_client_cert &&
 	    !request->valid_client_cert) {
 		/* we fail without valid certificate */
                 auth_request_handler_auth_fail(handler, request,
@@ -546,8 +544,10 @@
 	for (i = 0; i < count; i++) {
 		auth_request = auth_requests[aqueue_idx(auth_failures, 0)];
 
+		/* FIXME: assumess that failure_delay is always the same. */
 		diff = ioloop_time - auth_request->last_access;
-		if (diff < (time_t)auth_failure_delay && !flush_all)
+		if (diff < (time_t)auth_request->auth->set->failure_delay &&
+		    !flush_all)
 			break;
 
 		aqueue_delete_tail(auth_failures);
@@ -566,12 +566,6 @@
 
 void auth_request_handler_init(void)
 {
-	const char *env;
-
-	env = getenv("FAILURE_DELAY");
-	auth_failure_delay = env != NULL ? atoi(env) :
-		DEFAULT_AUTH_FAILURE_DELAY;
-
 	i_array_init(&auth_failures_arr, 128);
 	auth_failures = aqueue_init(&auth_failures_arr.arr);
 }
--- a/src/auth/auth-request.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-request.c	Tue Jan 27 18:21:53 2009 -0500
@@ -165,7 +165,7 @@
 	else if (strcmp(key, "original_username") == 0)
 		request->original_username = p_strdup(request->pool, value);
 	else if (strcmp(key, "cert_username") == 0) {
-		if (request->auth->ssl_username_from_cert) {
+		if (request->auth->set->ssl_username_from_cert) {
 			/* get username from SSL certificate. it overrides
 			   the username given by the auth mechanism. */
 			request->user = p_strdup(request->pool, value);
@@ -521,7 +521,7 @@
 			request->credentials_scheme,
                 	request->private_callback.lookup_credentials);
 	} else {
-		if (request->auth->verbose_debug_passwords &&
+		if (request->auth->set->debug_passwords &&
 		    result == PASSDB_RESULT_OK) {
 			auth_request_log_debug(request, "password",
 				"Credentials: %s",
@@ -772,10 +772,10 @@
 	unsigned char *p;
         char *user;
 
-	if (strchr(username, '@') == NULL &&
-	    request->auth->default_realm != NULL) {
+	if (*request->auth->set->default_realm != '\0' &&
+	    strchr(username, '@') == NULL) {
 		user = p_strconcat(request->pool, username, "@",
-                                   request->auth->default_realm, NULL);
+                                   request->auth->set->default_realm, NULL);
 	} else {
 		user = p_strdup(request->pool, username);
 	}
@@ -791,7 +791,7 @@
 		}
 	}
 
-	if (request->auth->username_format != NULL) {
+	if (*request->auth->set->username_format != '\0') {
 		/* username format given, put it through variable expansion.
 		   we'll have to temporarily replace request->user to get
 		   %u to be the wanted username */
@@ -804,7 +804,7 @@
 
 		dest = t_str_new(256);
 		table = auth_request_get_var_expand_table(request, NULL);
-		var_expand(dest, request->auth->username_format, table);
+		var_expand(dest, request->auth->set->username_format, table);
 		user = p_strdup(request->pool, str_c(dest));
 
 		request->user = old_username;
@@ -816,12 +816,12 @@
 bool auth_request_set_username(struct auth_request *request,
 			       const char *username, const char **error_r)
 {
+	const struct auth_settings *set = request->auth->set;
 	const char *p, *login_username = NULL;
 
-	if (request->auth->master_user_separator != '\0' &&
-	    !request->userdb_lookup) {
+	if (*set->master_user_separator != '\0' && !request->userdb_lookup) {
 		/* check if the username contains a master user */
-		p = strchr(username, request->auth->master_user_separator);
+		p = strchr(username, *set->master_user_separator);
 		if (p != NULL) {
 			/* it does, set it. */
 			login_username = t_strdup_until(username, p);
@@ -1312,7 +1312,7 @@
 	if (ret == 0) {
 		auth_request_log_info(request, subsystem,
 				      "Password mismatch");
-		if (request->auth->verbose_debug_passwords) {
+		if (request->auth->set->debug_passwords) {
 			auth_request_log_debug(request, subsystem,
 					       "%s(%s) != '%s'", scheme,
 					       plain_password,
@@ -1436,7 +1436,7 @@
 {
 	va_list va;
 
-	if (!auth_request->auth->verbose_debug)
+	if (!auth_request->auth->set->debug)
 		return;
 
 	va_start(va, format);
@@ -1452,7 +1452,7 @@
 {
 	va_list va;
 
-	if (!auth_request->auth->verbose)
+	if (!auth_request->auth->set->verbose)
 		return;
 
 	va_start(va, format);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,347 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "hostpid.h"
+#include "settings-parser.h"
+#include "auth-settings.h"
+
+#include <stddef.h>
+
+extern struct setting_parser_info auth_socket_setting_parser_info;
+extern struct setting_parser_info auth_setting_parser_info;
+extern struct setting_parser_info auth_root_setting_parser_info;
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
+
+static struct setting_define auth_socket_client_setting_defines[] = {
+	DEF(SET_STR, path),
+	DEF(SET_UINT, mode),
+	DEF(SET_STR, user),
+	DEF(SET_STR, group),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct auth_socket_unix_settings auth_socket_client_default_settings = {
+	MEMBER(path) "auth-client",
+	MEMBER(mode) 0660,
+	MEMBER(user) "",
+	MEMBER(group) ""
+};
+
+struct setting_parser_info auth_socket_client_setting_parser_info = {
+	MEMBER(defines) auth_socket_client_setting_defines,
+	MEMBER(defaults) &auth_socket_client_default_settings,
+
+	MEMBER(parent) &auth_socket_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
+};
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_socket_unix_settings, name), NULL }
+
+static struct setting_define auth_socket_master_setting_defines[] = {
+	DEF(SET_STR, path),
+	DEF(SET_UINT, mode),
+	DEF(SET_STR, user),
+	DEF(SET_STR, group),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct auth_socket_unix_settings auth_socket_master_default_settings = {
+	MEMBER(path) "auth-master",
+	MEMBER(mode) 0660,
+	MEMBER(user) "",
+	MEMBER(group) ""
+};
+
+struct setting_parser_info auth_socket_master_setting_parser_info = {
+	MEMBER(defines) auth_socket_master_setting_defines,
+	MEMBER(defaults) &auth_socket_master_default_settings,
+
+	MEMBER(parent) &auth_socket_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct auth_socket_unix_settings)
+};
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_socket_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, offsetof(struct auth_socket_settings, field), defines }
+
+static struct setting_define auth_socket_setting_defines[] = {
+	DEF(SET_STR, type),
+
+	DEFLIST(clients, "client", &auth_socket_client_setting_parser_info),
+	DEFLIST(masters, "master", &auth_socket_master_setting_parser_info),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct auth_socket_settings auth_socket_default_settings = {
+	MEMBER(type) "listen"
+};
+
+struct setting_parser_info auth_socket_setting_parser_info = {
+	MEMBER(defines) auth_socket_setting_defines,
+	MEMBER(defaults) &auth_socket_default_settings,
+
+	MEMBER(parent) &auth_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) offsetof(struct auth_socket_settings, type),
+	MEMBER(struct_size) sizeof(struct auth_socket_settings)
+};
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_passdb_settings, name), NULL }
+
+static struct setting_define auth_passdb_setting_defines[] = {
+	DEF(SET_STR, driver),
+	DEF(SET_STR, args),
+	DEF(SET_BOOL, deny),
+
+	SETTING_DEFINE_LIST_END
+};
+
+struct setting_parser_info auth_passdb_setting_parser_info = {
+	MEMBER(defines) auth_passdb_setting_defines,
+	MEMBER(defaults) NULL,
+
+	MEMBER(parent) &auth_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) offsetof(struct auth_passdb_settings, driver),
+	MEMBER(struct_size) sizeof(struct auth_passdb_settings)
+};
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_userdb_settings, name), NULL }
+
+static struct setting_define auth_userdb_setting_defines[] = {
+	DEF(SET_STR, driver),
+	DEF(SET_STR, args),
+
+	SETTING_DEFINE_LIST_END
+};
+
+struct setting_parser_info auth_userdb_setting_parser_info = {
+	MEMBER(defines) auth_userdb_setting_defines,
+	MEMBER(defaults) NULL,
+
+	MEMBER(parent) &auth_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) offsetof(struct auth_userdb_settings, driver),
+	MEMBER(struct_size) sizeof(struct auth_userdb_settings)
+};
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, offsetof(struct auth_settings, field), defines }
+
+static struct setting_define auth_setting_defines[] = {
+	DEF(SET_STR, name),
+	DEF(SET_STR, mechanisms),
+	DEF(SET_STR, realms),
+	DEF(SET_STR, default_realm),
+	DEF(SET_UINT, cache_size),
+	DEF(SET_UINT, cache_ttl),
+	DEF(SET_UINT, cache_negative_ttl),
+	DEF(SET_STR, username_chars),
+	DEF(SET_STR, username_translation),
+	DEF(SET_STR, username_format),
+	DEF(SET_STR, master_user_separator),
+	DEF(SET_STR, anonymous_username),
+	DEF(SET_STR, krb5_keytab),
+	DEF(SET_STR, gssapi_hostname),
+	DEF(SET_STR, winbind_helper_path),
+	DEF(SET_UINT, failure_delay),
+
+	DEF(SET_BOOL, verbose),
+	DEF(SET_BOOL, debug),
+	DEF(SET_BOOL, debug_passwords),
+	DEF(SET_BOOL, ssl_require_client_cert),
+	DEF(SET_BOOL, ssl_username_from_cert),
+	DEF(SET_BOOL, use_winbind),
+
+	DEF(SET_UINT, worker_max_count),
+
+	DEFLIST(sockets, "socket", &auth_socket_setting_parser_info),
+	DEFLIST(passdbs, "passdb", &auth_passdb_setting_parser_info),
+	DEFLIST(userdbs, "userdb", &auth_userdb_setting_parser_info),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct auth_settings auth_default_settings = {
+	MEMBER(name) NULL,
+	MEMBER(root) NULL,
+
+	MEMBER(mechanisms) "plain",
+	MEMBER(realms) "",
+	MEMBER(default_realm) "",
+	MEMBER(cache_size) 0,
+	MEMBER(cache_ttl) 3600,
+	MEMBER(cache_negative_ttl) 0,
+	MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@",
+	MEMBER(username_translation) "",
+	MEMBER(username_format) "",
+	MEMBER(master_user_separator) "",
+	MEMBER(anonymous_username) "anonymous",
+	MEMBER(krb5_keytab) "",
+	MEMBER(gssapi_hostname) "",
+	MEMBER(winbind_helper_path) "/usr/bin/ntlm_auth",
+	MEMBER(failure_delay) 2,
+
+	MEMBER(verbose) FALSE,
+	MEMBER(debug) FALSE,
+	MEMBER(debug_passwords) FALSE,
+	MEMBER(ssl_require_client_cert) FALSE,
+	MEMBER(ssl_username_from_cert) FALSE,
+	MEMBER(use_winbind) FALSE,
+
+	MEMBER(worker_max_count) 30,
+
+	MEMBER(sockets) ARRAY_INIT,
+	MEMBER(passdbs) ARRAY_INIT,
+	MEMBER(userdbs) ARRAY_INIT
+};
+
+struct setting_parser_info auth_setting_parser_info = {
+	MEMBER(defines) auth_setting_defines,
+	MEMBER(defaults) &auth_default_settings,
+
+	MEMBER(parent) &auth_root_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) offsetof(struct auth_settings, root),
+	MEMBER(type_offset) offsetof(struct auth_settings, name),
+	MEMBER(struct_size) sizeof(struct auth_settings)
+};
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct auth_root_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, offsetof(struct auth_root_settings, field), defines }
+
+static struct setting_define auth_root_setting_defines[] = {
+	DEF(SET_STR, base_dir),
+	DEFLIST(auths, "auth", &auth_setting_parser_info),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct auth_root_settings auth_root_default_settings = {
+	MEMBER(base_dir) PKG_RUNDIR,
+	MEMBER(auths) ARRAY_INIT
+};
+
+struct setting_parser_info auth_root_setting_parser_info = {
+	MEMBER(defines) auth_root_setting_defines,
+	MEMBER(defaults) &auth_root_default_settings,
+
+	MEMBER(parent) NULL,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct auth_root_settings)
+};
+
+static pool_t settings_pool = NULL;
+
+static void fix_base_path(struct auth_settings *set, const char **str)
+{
+	if (*str != NULL && **str != '\0' && **str != '/') {
+		*str = p_strconcat(settings_pool,
+				   set->root->base_dir, "/", *str, NULL);
+	}
+}
+
+static void auth_settings_check(struct auth_settings *set)
+{
+	struct auth_socket_unix_settings *const *u;
+	struct auth_socket_settings *const *sockets;
+	unsigned int i, j, count, count2;
+
+	if (!array_is_created(&set->sockets))
+		return;
+
+	sockets = array_get(&set->sockets, &count);
+	for (i = 0; i < count; i++) {
+		if (array_is_created(&sockets[i]->masters)) {
+			u = array_get(&sockets[i]->masters, &count2);
+			for (j = 0; j < count2; j++)
+				fix_base_path(set, &u[j]->path);
+		}
+		if (array_is_created(&sockets[i]->clients)) {
+			u = array_get(&sockets[i]->clients, &count2);
+			for (j = 0; j < count2; j++)
+				fix_base_path(set, &u[j]->path);
+		}
+	}
+}
+
+struct auth_settings *auth_settings_read(const char *name)
+{
+	struct setting_parser_context *parser;
+	struct auth_root_settings *set;
+	struct auth_settings *const *auths;
+	unsigned int i, count;
+
+	if (settings_pool == NULL)
+		settings_pool = pool_alloconly_create("auth settings", 1024);
+	else
+		p_clear(settings_pool);
+
+	parser = settings_parser_init(settings_pool,
+				      &auth_root_setting_parser_info,
+				      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+
+	auth_default_settings.gssapi_hostname = my_hostname;
+
+	if (settings_parse_environ(parser) < 0) {
+		i_fatal("Error reading configuration: %s",
+			settings_parser_get_error(parser));
+	}
+
+	set = settings_parser_get(parser);
+	settings_parser_deinit(&parser);
+
+	if (array_is_created(&set->auths)) {
+		auths = array_get(&set->auths, &count);
+		for (i = 0; i < count; i++) {
+			if (strcmp(auths[i]->name, name) == 0) {
+				auth_settings_check(auths[i]);
+				return auths[i];
+			}
+		}
+	}
+	i_fatal("Error reading configuration: No auth section: %s", name);
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/auth/auth-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,71 @@
+#ifndef AUTH_SETTINGS_H
+#define AUTH_SETTINGS_H
+
+struct auth_socket_unix_settings {
+	const char *path;
+	unsigned int mode;
+	const char *user;
+	const char *group;
+};
+
+struct auth_socket_settings {
+	const char *type;
+
+	ARRAY_DEFINE(clients, struct auth_socket_unix_settings *);
+	ARRAY_DEFINE(masters, struct auth_socket_unix_settings *);
+};
+
+struct auth_passdb_settings {
+	const char *driver;
+	const char *args;
+	bool deny;
+	bool pass;
+	bool master;
+};
+
+struct auth_userdb_settings {
+	const char *driver;
+	const char *args;
+};
+
+struct auth_settings {
+	const char *name;
+	struct auth_root_settings *root;
+
+	const char *mechanisms;
+	const char *realms;
+	const char *default_realm;
+	unsigned int cache_size;
+	unsigned int cache_ttl;
+	unsigned int cache_negative_ttl;
+	const char *username_chars;
+	const char *username_translation;
+	const char *username_format;
+	const char *master_user_separator;
+	const char *anonymous_username;
+	const char *krb5_keytab;
+	const char *gssapi_hostname;
+	const char *winbind_helper_path;
+	unsigned int failure_delay;
+
+	bool verbose, debug, debug_passwords;
+	bool ssl_require_client_cert;
+	bool ssl_username_from_cert;
+	bool use_winbind;
+
+	unsigned int worker_max_count;
+
+	ARRAY_DEFINE(sockets, struct auth_socket_settings *);
+	ARRAY_DEFINE(passdbs, struct auth_passdb_settings *);
+	ARRAY_DEFINE(userdbs, struct auth_userdb_settings *);
+};
+
+struct auth_root_settings {
+	const char *base_dir;
+
+	ARRAY_DEFINE(auths, struct auth_settings *);
+};
+
+struct auth_settings *auth_settings_read(const char *name);
+
+#endif
--- a/src/auth/auth-worker-server.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-worker-server.c	Tue Jan 27 18:21:53 2009 -0500
@@ -28,6 +28,7 @@
 };
 
 struct auth_worker_connection {
+	struct auth *auth;
 	int fd;
 
 	struct io *io;
@@ -43,8 +44,6 @@
 
 static ARRAY_DEFINE(connections, struct auth_worker_connection *) = ARRAY_INIT;
 static unsigned int idle_count;
-static unsigned int auth_workers_max;
-
 static ARRAY_DEFINE(worker_request_array, struct auth_worker_request *);
 static struct aqueue *worker_request_queue;
 static time_t auth_worker_last_warn;
@@ -120,12 +119,12 @@
 	auth_worker_request_send(conn, request);
 }
 
-static struct auth_worker_connection *auth_worker_create(void)
+static struct auth_worker_connection *auth_worker_create(struct auth *auth)
 {
 	struct auth_worker_connection *conn;
 	int fd, try;
 
-	if (array_count(&connections) >= auth_workers_max)
+	if (array_count(&connections) >= auth->set->worker_max_count)
 		return NULL;
 
 	for (try = 0;; try++) {
@@ -154,6 +153,7 @@
 	}
 
 	conn = i_new(struct auth_worker_connection, 1);
+	conn->auth = auth;
 	conn->fd = fd;
 	conn->input = i_stream_create_fd(fd, AUTH_WORKER_MAX_LINE_LENGTH,
 					 FALSE);
@@ -172,6 +172,7 @@
 				const char *reason, bool restart)
 {
 	struct auth_worker_connection *conn = *_conn;
+	struct auth *auth = conn->auth;
 	struct auth_worker_connection **connp;
 	unsigned int i, count;
 
@@ -208,7 +209,7 @@
 	i_free(conn);
 
 	if (idle_count == 0 && restart) {
-		conn = auth_worker_create();
+		conn = auth_worker_create(auth);
 		if (conn != NULL)
 			auth_worker_request_send_next(conn);
 	}
@@ -323,7 +324,7 @@
 		conn = auth_worker_find_free();
 		if (conn == NULL) {
 			/* no free connections, create a new one */
-			conn = auth_worker_create();
+			conn = auth_worker_create(auth_request->auth);
 		}
 	}
 	if (conn != NULL)
@@ -334,7 +335,7 @@
 	}
 }
 
-void auth_worker_server_init(void)
+void auth_worker_server_init(struct auth *auth)
 {
 	const char *env;
 
@@ -348,16 +349,11 @@
 		i_fatal("AUTH_WORKER_PATH environment not set");
 	worker_socket_path = i_strdup(env);
 
-	env = getenv("AUTH_WORKER_MAX_COUNT");
-	if (env == NULL)
-		i_fatal("AUTH_WORKER_MAX_COUNT environment not set");
-	auth_workers_max = atoi(env);
-
 	i_array_init(&worker_request_array, 128);
 	worker_request_queue = aqueue_init(&worker_request_array.arr);
 
 	i_array_init(&connections, 16);
-	(void)auth_worker_create();
+	(void)auth_worker_create(auth);
 }
 
 void auth_worker_server_deinit(void)
--- a/src/auth/auth-worker-server.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth-worker-server.h	Tue Jan 27 18:21:53 2009 -0500
@@ -11,7 +11,7 @@
 		      struct auth_stream_reply *data,
 		      auth_worker_callback_t *callback);
 
-void auth_worker_server_init(void);
+void auth_worker_server_init(struct auth *auth);
 void auth_worker_server_deinit(void);
 
 #endif
--- a/src/auth/auth.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth.c	Tue Jan 27 18:21:53 2009 -0500
@@ -2,9 +2,9 @@
 
 #include "common.h"
 #include "network.h"
-#include "buffer.h"
+#include "array.h"
 #include "str.h"
-#include "hostpid.h"
+#include "env-util.h"
 #include "mech.h"
 #include "userdb.h"
 #include "passdb.h"
@@ -15,72 +15,65 @@
 #include <stdlib.h>
 #include <unistd.h>
 
-struct auth *auth_preinit(void)
+struct auth_userdb_settings userdb_dummy_set = {
+	MEMBER(driver) "static",
+	MEMBER(args) ""
+};
+
+struct auth *auth_preinit(struct auth_settings *set)
 {
+	struct auth_passdb_settings *const *passdbs;
+	struct auth_userdb_settings *const *userdbs;
 	struct auth *auth;
-	struct auth_passdb *auth_passdb, **passdb_p, **masterdb_p;
-	const char *driver, *args;
 	pool_t pool;
-	unsigned int i;
+	unsigned int i, count, db_count, passdb_count, last_passdb = 0;
 
 	pool = pool_alloconly_create("auth", 2048);
 	auth = p_new(pool, struct auth, 1);
 	auth->pool = pool;
-
-	auth->verbose_debug_passwords =
-		getenv("VERBOSE_DEBUG_PASSWORDS") != NULL;
-	auth->verbose_debug = getenv("VERBOSE_DEBUG") != NULL ||
-		auth->verbose_debug_passwords;
-	auth->verbose = getenv("VERBOSE") != NULL || auth->verbose_debug;
+	auth->set = set;
 
-	passdb_p = &auth->passdbs;
-	masterdb_p = &auth->masterdbs;
-	auth_passdb = NULL;
-	for (i = 1; ; i++) {
-		driver = getenv(t_strdup_printf("PASSDB_%u_DRIVER", i));
-		if (driver == NULL)
-			break;
+	if (array_is_created(&set->passdbs))
+		passdbs = array_get(&set->passdbs, &db_count);
+	else {
+		passdbs = NULL;
+		db_count = 0;
+	}
 
-                args = getenv(t_strdup_printf("PASSDB_%u_ARGS", i));
-		auth_passdb = passdb_preinit(auth, driver, args, i);
+	/* initialize passdbs first and count them */
+	for (passdb_count = 0, i = 0; i < db_count; i++) {
+		if (passdbs[i]->master)
+			continue;
 
-                auth_passdb->deny =
-                        getenv(t_strdup_printf("PASSDB_%u_DENY", i)) != NULL;
-		auth_passdb->pass =
-                        getenv(t_strdup_printf("PASSDB_%u_PASS", i)) != NULL;
+		passdb_preinit(auth, passdbs[i]);
+		passdb_count++;
+		last_passdb = i;
+	}
+	if (passdb_count != 0 && passdbs[last_passdb]->pass)
+		i_fatal("Last passdb can't have pass=yes");
 
-		if (getenv(t_strdup_printf("PASSDB_%u_MASTER", i)) == NULL) {
-			*passdb_p = auth_passdb;
-			passdb_p = &auth_passdb->next;
-                } else {
-			if (auth_passdb->deny)
-				i_fatal("Master passdb can't have deny=yes");
+	for (passdb_count = 0, i = 0; i < db_count; i++) {
+		if (!passdbs[i]->master)
+			continue;
 
-			*masterdb_p = auth_passdb;
-			masterdb_p = &auth_passdb->next;
-		}
-	}
-	if (auth_passdb != NULL && auth_passdb->pass) {
-		if (masterdb_p != &auth_passdb->next)
-			i_fatal("Last passdb can't have pass=yes");
-		else if (auth->passdbs == NULL) {
+		if (passdbs[i]->deny)
+			i_fatal("Master passdb can't have deny=yes");
+		if (passdbs[i]->pass && passdb_count == 0) {
 			i_fatal("Master passdb can't have pass=yes "
 				"if there are no passdbs");
 		}
+		passdb_preinit(auth, passdbs[i]);
 	}
 
-	for (i = 1; ; i++) {
-		driver = getenv(t_strdup_printf("USERDB_%u_DRIVER", i));
-		if (driver == NULL)
-			break;
-
-                args = getenv(t_strdup_printf("USERDB_%u_ARGS", i));
-		userdb_preinit(auth, driver, args);
+	if (array_is_created(&set->userdbs)) {
+		userdbs = array_get(&set->userdbs, &count);
+		for (i = 0; i < count; i++)
+			userdb_preinit(auth, userdbs[i]);
 	}
 
 	if (auth->userdbs == NULL) {
 		/* use a dummy userdb static. */
-		userdb_preinit(auth, "static", "");
+		userdb_preinit(auth, &userdb_dummy_set);
 	}
 	return auth;
 }
@@ -209,7 +202,7 @@
 	struct auth_userdb *userdb;
 	const struct mech_module *mech;
 	const char *const *mechanisms;
-	const char *env;
+	const char *p;
 
 	for (passdb = auth->masterdbs; passdb != NULL; passdb = passdb->next)
 		passdb_init(passdb);
@@ -219,26 +212,17 @@
 		userdb_init(userdb);
 	/* caching is handled only by the main auth process */
 	if (!worker)
-		passdb_cache_init();
+		passdb_cache_init(auth->set);
 
 	auth->mech_handshake = str_new(auth->pool, 512);
 
-	auth->anonymous_username = getenv("ANONYMOUS_USERNAME");
-	if (auth->anonymous_username != NULL &&
-	    *auth->anonymous_username == '\0')
-                auth->anonymous_username = NULL;
-
 	/* register wanted mechanisms */
-	env = getenv("MECHANISMS");
-	if (env == NULL)
-		i_fatal("MECHANISMS environment is unset");
-
-	mechanisms = t_strsplit_spaces(env, " ");
+	mechanisms = t_strsplit_spaces(auth->set->mechanisms, " ");
 	while (*mechanisms != NULL) {
 		if (strcasecmp(*mechanisms, "ANONYMOUS") == 0) {
-			if (auth->anonymous_username == NULL) {
+			if (*auth->set->anonymous_username == '\0') {
 				i_fatal("ANONYMOUS listed in mechanisms, "
-					"but anonymous_username not given");
+					"but anonymous_username not set");
 			}
 		}
 		mech = mech_module_find(*mechanisms);
@@ -255,48 +239,22 @@
 		i_fatal("No authentication mechanisms configured");
 	auth_mech_list_verify_passdb(auth);
 
-	env = getenv("REALMS");
-	if (env == NULL)
-		env = "";
-	auth->auth_realms = p_strsplit_spaces(auth->pool, env, " ");
+	auth->auth_realms = (const char *const *)
+		p_strsplit_spaces(auth->pool, auth->set->realms, " ");
 
-	env = getenv("DEFAULT_REALM");
-	if (env != NULL && *env != '\0')
-		auth->default_realm = env;
-
-	env = getenv("USERNAME_CHARS");
-	if (env == NULL || *env == '\0') {
+	if (*auth->set->username_chars == '\0') {
 		/* all chars are allowed */
 		memset(auth->username_chars, 1, sizeof(auth->username_chars));
 	} else {
-		for (; *env != '\0'; env++)
-			auth->username_chars[(int)(uint8_t)*env] = 1;
-	}
-
-	env = getenv("USERNAME_TRANSLATION");
-	if (env != NULL) {
-		for (; *env != '\0' && env[1] != '\0'; env += 2)
-			auth->username_translation[(int)(uint8_t)*env] = env[1];
+		for (p = auth->set->username_chars; *p != '\0'; p++)
+			auth->username_chars[(int)(uint8_t)*p] = 1;
 	}
 
-	env = getenv("USERNAME_FORMAT");
-	if (env != NULL && *env != '\0')
-		auth->username_format = env;
-
-	env = getenv("GSSAPI_HOSTNAME");
-	if (env != NULL && *env != '\0')
-		auth->gssapi_hostname = env;
-	else
-		auth->gssapi_hostname = my_hostname;
-
-	env = getenv("MASTER_USER_SEPARATOR");
-	if (env != NULL)
-		auth->master_user_separator = env[0];
-
-	auth->ssl_require_client_cert =
-		getenv("SSL_REQUIRE_CLIENT_CERT") != NULL;
-	auth->ssl_username_from_cert =
-		getenv("SSL_USERNAME_FROM_CERT") != NULL;
+	if (*auth->set->username_translation != '\0') {
+		p = auth->set->username_translation;
+		for (; *p != '\0' && p[1] != '\0'; p += 2)
+			auth->username_translation[(int)(uint8_t)*p] = p[1];
+	}
 }
 
 void auth_deinit(struct auth **_auth)
--- a/src/auth/auth.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/auth.h	Tue Jan 27 18:21:53 2009 -0500
@@ -1,6 +1,8 @@
 #ifndef AUTH_H
 #define AUTH_H
 
+#include "auth-settings.h"
+
 #define PASSWORD_HIDDEN_STR "<hidden>"
 
 struct auth_passdb {
@@ -29,6 +31,7 @@
 
 struct auth {
 	pool_t pool;
+	const struct auth_settings *set;
 
 	struct mech_module_list *mech_modules;
 	buffer_t *mech_handshake;
@@ -37,23 +40,14 @@
 	struct auth_passdb *passdbs;
 	struct auth_userdb *userdbs;
 
-	char *const *auth_realms;
-	const char *default_realm;
-	const char *anonymous_username;
-	const char *username_format;
-	const char *gssapi_hostname;
+	const char *const *auth_realms;
 	char username_chars[256];
 	char username_translation[256];
-	char master_user_separator;
-	bool ssl_require_client_cert;
-	bool ssl_username_from_cert;
-
-	bool verbose, verbose_debug, verbose_debug_passwords;
 };
 
 const string_t *auth_mechanisms_get_list(struct auth *auth);
 
-struct auth *auth_preinit(void);
+struct auth *auth_preinit(struct auth_settings *set);
 void auth_init(struct auth *auth);
 void auth_deinit(struct auth **auth);
 
--- a/src/auth/db-ldap.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/db-ldap.c	Tue Jan 27 18:21:53 2009 -0500
@@ -997,7 +997,7 @@
 		ctx->static_attrs = t_strsplit(str_c(str), ",");
 	}
 
-	if (auth_request->auth->verbose_debug)
+	if (auth_request->auth->set->debug)
 		ctx->debug = t_str_new(256);
 
 	ctx->attr = ldap_first_attribute(conn->ld, entry, &ctx->ber);
@@ -1069,7 +1069,7 @@
 	if (ctx->debug != NULL) {
 		if (!first)
 			str_append_c(ctx->debug, '/');
-		if (ctx->auth_request->auth->verbose_debug_passwords ||
+		if (ctx->auth_request->auth->set->debug_passwords ||
 		    strcmp(ctx->name, "password") != 0)
 			str_append(ctx->debug, ctx->value);
 		else
--- a/src/auth/main.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/main.c	Tue Jan 27 18:21:53 2009 -0500
@@ -175,7 +175,7 @@
 
 static void drop_privileges(void)
 {
-	const char *version;
+	const char *version, *name;
 
 	version = getenv("DOVECOT_VERSION");
 	if (version != NULL && strcmp(version, PACKAGE_VERSION) != 0) {
@@ -191,6 +191,9 @@
 			"it standalone, you'll need to set AUTH_* "
 			"environment variables (AUTH_1 isn't set).");
 	}
+	name = getenv("AUTH_NAME");
+	if (name == NULL)
+		i_fatal("Missing AUTH_NAME environment");
 
 	open_logfile();
 
@@ -207,7 +210,7 @@
 	userdbs_init();
 	modules = module_dir_load(AUTH_MODULE_DIR, NULL, TRUE, version);
 	module_dir_init(modules);
-	auth = auth_preinit();
+	auth = auth_preinit(auth_settings_read(name));
 	auth_master_listeners_init();
 	if (!worker)
 		add_extra_listeners();
@@ -233,7 +236,7 @@
 	lib_signals_ignore(SIGUSR2, TRUE);
 
 	child_wait_init();
-	mech_init();
+	mech_init(auth->set);
 	password_schemes_init();
 	auth_init(auth);
 	auth_request_handler_init();
@@ -283,11 +286,11 @@
         auth_worker_server_deinit();
 	auth_master_listeners_deinit();
 
-	auth_deinit(&auth);
 	module_dir_unload(&modules);
 	userdbs_deinit();
 	passdbs_deinit();
-	mech_deinit();
+	mech_deinit(auth->set);
+	auth_deinit(&auth);
 
         password_schemes_deinit();
 	sql_drivers_deinit();
--- a/src/auth/mech-anonymous.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech-anonymous.c	Tue Jan 27 18:21:53 2009 -0500
@@ -7,9 +7,9 @@
 mech_anonymous_auth_continue(struct auth_request *request,
 			     const unsigned char *data, size_t data_size)
 {
-	i_assert(request->auth->anonymous_username != NULL);
+	i_assert(*request->auth->set->anonymous_username != '\0');
 
-	if (request->auth->verbose) {
+	if (request->auth->set->verbose) {
 		/* temporarily set the user to the one that was given,
 		   so that the log message goes right */
 		request->user =
@@ -18,7 +18,7 @@
 	}
 
 	request->user = p_strdup(request->pool,
-				 request->auth->anonymous_username);
+				 request->auth->set->anonymous_username);
 
 	auth_request_success(request, NULL, 0);
 }
--- a/src/auth/mech-digest-md5.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech-digest-md5.c	Tue Jan 27 18:21:53 2009 -0500
@@ -60,7 +60,7 @@
 	struct auth *auth = request->auth_request.auth;
 	buffer_t *buf;
 	string_t *str;
-	char *const *tmp;
+	const char *const *tmp;
 	unsigned char nonce[16];
 	int i;
 	bool first_qop;
@@ -220,7 +220,7 @@
 
 static bool verify_realm(struct digest_auth_request *request, const char *realm)
 {
-	char *const *tmp;
+	const char *const *tmp;
 
 	if (*realm == '\0')
 		return TRUE;
--- a/src/auth/mech-gssapi.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech-gssapi.c	Tue Jan 27 18:21:53 2009 -0500
@@ -13,13 +13,13 @@
  */
 
 #include "common.h"
-#include "mech.h"
-#include "passdb.h"
+#include "env-util.h"
 #include "str.h"
 #include "str-sanitize.h"
-#include "buffer.h"
 #include "hex-binary.h"
 #include "safe-memset.h"
+#include "mech.h"
+#include "passdb.h"
 
 #include <stdlib.h>
 
@@ -98,12 +98,13 @@
 	} while (message_context != 0);
 }
 
-static void mech_gssapi_initialize(void)
+static void mech_gssapi_initialize(struct auth *auth)
 {
-	const char *path;
+	const char *path = auth->set->krb5_keytab;
 
-	path = getenv("KRB5_KTNAME");
-	if (path != NULL) {
+	if (*path != '\0') {
+		/* environment may be used by Kerberos 5 library directly */
+		env_put(t_strconcat("KRB5_KTNAME=", path, NULL));
 #ifdef HAVE_GSSKRB5_REGISTER_ACCEPTOR_IDENTITY
 		gsskrb5_register_acceptor_identity(path);
 #elif defined (HAVE_KRB5_GSS_REGISTER_ACCEPTOR_IDENTITY)
@@ -117,11 +118,6 @@
 	struct gssapi_auth_request *request;
 	pool_t pool;
 
-	if (!gssapi_initialized) {
-		gssapi_initialized = TRUE;
-		mech_gssapi_initialize();
-	}
-
 	pool = pool_alloconly_create("gssapi_auth_request", 1024);
 	request = p_new(pool, struct gssapi_auth_request, 1);
 	request->pool = pool;
@@ -141,7 +137,12 @@
 	gss_name_t gss_principal;
 	const char *service_name;
 
-	if (strcmp(request->auth->gssapi_hostname, "$ALL") == 0) {
+	if (!gssapi_initialized) {
+		gssapi_initialized = TRUE;
+		mech_gssapi_initialize(request->auth);
+	}
+
+	if (strcmp(request->auth->set->gssapi_hostname, "$ALL") == 0) {
 		auth_request_log_info(request, "gssapi",
 				      "Using all keytab entries");
 		*ret = GSS_C_NO_CREDENTIAL;
@@ -159,7 +160,7 @@
 	principal_name = t_str_new(128);
 	str_append(principal_name, service_name);
 	str_append_c(principal_name, '@');
-	str_append(principal_name, request->auth->gssapi_hostname);
+	str_append(principal_name, request->auth->set->gssapi_hostname);
 
 	auth_request_log_info(request, "gssapi",
 		"Obtaining credentials for %s", str_c(principal_name));
--- a/src/auth/mech-rpa.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech-rpa.c	Tue Jan 27 18:21:53 2009 -0500
@@ -238,18 +238,17 @@
 static bool
 rpa_verify_realm(struct rpa_auth_request *request, const char *realm)
 {
+	const struct auth *auth = request->auth_request.auth;
 	const char *default_realm;
-	char *const *tmp;
+	const char *const *tmp;
 
-	tmp = request->auth_request.auth->auth_realms;
-	for (; *tmp != NULL; tmp++) {
+	for (tmp = auth->auth_realms; *tmp != NULL; tmp++) {
 		if (strcasecmp(realm, *tmp) == 0)
 			return TRUE;
 	}
 
-	default_realm = request->auth_request.auth->default_realm != NULL ?
-			request->auth_request.auth->default_realm :
-			my_hostname;
+	default_realm = *auth->set->default_realm != '\0' ?
+		auth->set->default_realm : my_hostname;
 
 	return strcasecmp(realm, default_realm) == 0;
 }
@@ -353,7 +352,7 @@
 	string_t *realms;
 	buffer_t *buf;
 	unsigned char timestamp[RPA_TIMESTAMP_LEN / 2];
-	char *const *tmp;
+	const char *const *tmp;
 
 	realms = t_str_new(64);
 	for (tmp = auth->auth_realms; *tmp != NULL; tmp++) {
@@ -361,8 +360,8 @@
 	}
 
 	if (str_len(realms) == 0) {
-		rpa_add_realm(realms, auth->default_realm != NULL ?
-			      auth->default_realm : my_hostname,
+		rpa_add_realm(realms, *auth->set->default_realm != '\0' ?
+			      auth->set->default_realm : my_hostname,
 			      request->auth_request.service);
 	}
 
--- a/src/auth/mech-winbind.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech-winbind.c	Tue Jan 27 18:21:53 2009 -0500
@@ -95,7 +95,8 @@
 	winbind_wait_pid(&winbind_spnego_context);
 }
 
-static void winbind_helper_connect(struct winbind_helper *winbind)
+static void
+winbind_helper_connect(struct auth *auth, struct winbind_helper *winbind)
 {
 	int infd[2], outfd[2];
 	pid_t pid;
@@ -122,7 +123,7 @@
 
 	if (pid == 0) {
 		/* child */
-		const char *helper_path, *args[3];
+		const char *args[3];
 
 		(void)close(infd[0]);
 		(void)close(outfd[1]);
@@ -131,11 +132,7 @@
 		    dup2(infd[1], STDOUT_FILENO) < 0)
 			i_fatal("dup2() failed: %m");
 
-		helper_path = getenv("WINBIND_HELPER_PATH");
-		if (helper_path == NULL)
-			helper_path = DEFAULT_WINBIND_HELPER_PATH;
-
-		args[0] = helper_path;
+		args[0] = auth->set->winbind_helper_path;
 		args[1] = winbind->param;
 		args[2] = NULL;
 		execv(args[0], (void *)args);
@@ -281,6 +278,17 @@
 }
 
 static void
+mech_winbind_auth_initial(struct auth_request *auth_request,
+			  const unsigned char *data, size_t data_size)
+{
+	struct winbind_auth_request *request =
+		(struct winbind_auth_request *)auth_request;
+
+	winbind_helper_connect(auth_request->auth, request->winbind);
+	mech_generic_auth_initial(auth_request, data, data_size);
+}
+
+static void
 mech_winbind_auth_continue(struct auth_request *auth_request,
 			   const unsigned char *data, size_t data_size)
 {
@@ -306,7 +314,6 @@
 	request->auth_request.pool = pool;
 
 	request->winbind = winbind;
-	winbind_helper_connect(request->winbind);
 	return &request->auth_request;
 }
 
@@ -339,7 +346,7 @@
 	MEMBER(passdb_need) MECH_PASSDB_NEED_NOTHING,
 
 	mech_winbind_spnego_auth_new,
-	mech_generic_auth_initial,
+	mech_winbind_auth_initial,
 	mech_winbind_auth_continue,
 	mech_generic_auth_free
 };
--- a/src/auth/mech.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech.c	Tue Jan 27 18:21:53 2009 -0500
@@ -81,14 +81,14 @@
 extern const struct mech_module mech_winbind_ntlm;
 extern const struct mech_module mech_winbind_spnego;
 
-void mech_init(void)
+void mech_init(const struct auth_settings *set)
 {
 	mech_register_module(&mech_plain);
 	mech_register_module(&mech_login);
 	mech_register_module(&mech_apop);
 	mech_register_module(&mech_cram_md5);
 	mech_register_module(&mech_digest_md5);
-	if (getenv("USE_WINBIND") != NULL) {
+	if (set->use_winbind) {
 		mech_register_module(&mech_winbind_ntlm);
 		mech_register_module(&mech_winbind_spnego);
 	} else {
@@ -106,14 +106,14 @@
 #endif
 }
 
-void mech_deinit(void)
+void mech_deinit(const struct auth_settings *set)
 {
 	mech_unregister_module(&mech_plain);
 	mech_unregister_module(&mech_login);
 	mech_unregister_module(&mech_apop);
 	mech_unregister_module(&mech_cram_md5);
 	mech_unregister_module(&mech_digest_md5);
-	if (getenv("NTLM_USE_WINBIND") != NULL) {
+	if (set->use_winbind) {
 		mech_unregister_module(&mech_winbind_ntlm);
 		mech_unregister_module(&mech_winbind_spnego);
 	} else {
--- a/src/auth/mech.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/mech.h	Tue Jan 27 18:21:53 2009 -0500
@@ -63,7 +63,7 @@
 			       const unsigned char *data, size_t data_size);
 void mech_generic_auth_free(struct auth_request *request);
 
-void mech_init(void);
-void mech_deinit(void);
+void mech_init(const struct auth_settings *set);
+void mech_deinit(const struct auth_settings *set);
 
 #endif
--- a/src/auth/passdb-cache.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/passdb-cache.c	Tue Jan 27 18:21:53 2009 -0500
@@ -14,7 +14,7 @@
 {
 	const char *p;
 
-	if (!request->auth->verbose_debug_passwords &&
+	if (!request->auth->set->debug_passwords &&
 	    *value != '\0' && *value != '\t') {
 		/* hide the password */
 		p = strchr(value, '\t');
@@ -125,31 +125,13 @@
 	return TRUE;
 }
 
-void passdb_cache_init(void)
+void passdb_cache_init(const struct auth_settings *set)
 {
-	const char *env;
-	size_t max_size;
-	unsigned int cache_ttl, neg_cache_ttl;
-
-	env = getenv("CACHE_SIZE");
-	if (env == NULL)
+	if (set->cache_size == 0 || set->cache_ttl == 0)
 		return;
 
-	max_size = (size_t)strtoul(env, NULL, 10) * 1024;
-	if (max_size == 0)
-		return;
-
-	env = getenv("CACHE_TTL");
-	if (env == NULL)
-		return;
-
-	cache_ttl = (unsigned int)strtoul(env, NULL, 10);
-	if (cache_ttl == 0)
-		return;
-
-	env = getenv("CACHE_NEGATIVE_TTL");
-	neg_cache_ttl = env == NULL ? 0 : (unsigned int)strtoul(env, NULL, 10);
-	passdb_cache = auth_cache_new(max_size, cache_ttl, neg_cache_ttl);
+	passdb_cache = auth_cache_new(set->cache_size * 1024UL, set->cache_ttl,
+				      set->cache_negative_ttl);
 }
 
 void passdb_cache_deinit(void)
--- a/src/auth/passdb-cache.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/passdb-cache.h	Tue Jan 27 18:21:53 2009 -0500
@@ -14,7 +14,7 @@
 				     enum passdb_result *result_r,
 				     bool use_expired);
 
-void passdb_cache_init(void);
+void passdb_cache_init(const struct auth_settings *set);
 void passdb_cache_deinit(void);
 
 #endif
--- a/src/auth/passdb-passwd-file.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/passdb-passwd-file.c	Tue Jan 27 18:21:53 2009 -0500
@@ -147,7 +147,7 @@
 		       struct passwd_file_passdb_module, 1);
 	module->auth = auth_passdb->auth;
 	module->pwf = db_passwd_file_init(args, format, FALSE,
-					  module->auth->verbose_debug);
+					  module->auth->set->debug);
 
 	if (!module->pwf->vars)
 		module->module.cache_key = format;
--- a/src/auth/passdb.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/passdb.c	Tue Jan 27 18:21:53 2009 -0500
@@ -81,7 +81,7 @@
 			const char *error = t_strdup_printf(
 				"Requested %s scheme, but we have only %s",
 				wanted_scheme, input_scheme);
-			if (auth_request->auth->verbose_debug_passwords) {
+			if (auth_request->auth->set->debug_passwords) {
 				error = t_strdup_printf("%s (input: %s)",
 							error, input);
 			}
@@ -92,7 +92,7 @@
 
 		/* we can generate anything out of plaintext passwords */
 		plaintext = t_strndup(*credentials_r, *size_r);
-		if (auth_request->auth->verbose_debug_passwords) {
+		if (auth_request->auth->set->debug_passwords) {
 			auth_request_log_info(auth_request, "password",
 				"Generating %s from user %s password %s",
 				wanted_scheme, auth_request->original_username,
@@ -131,31 +131,34 @@
 	callback(result, credentials, size, auth_request);
 }
 
-struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver,
-				   const char *args, unsigned int id)
+struct auth_passdb *
+passdb_preinit(struct auth *auth, struct auth_passdb_settings *set)
 {
 	struct passdb_module_interface *iface;
-        struct auth_passdb *auth_passdb;
-
-	if (args == NULL) args = "";
+        struct auth_passdb *auth_passdb, **dest;
 
 	auth_passdb = p_new(auth->pool, struct auth_passdb, 1);
 	auth_passdb->auth = auth;
-        auth_passdb->args = p_strdup(auth->pool, args);
-        auth_passdb->id = id;
+	auth_passdb->args = set->args == NULL ? "" :
+		p_strdup(auth->pool, set->args);
+	auth_passdb->deny = set->deny;
 
-	iface = passdb_interface_find(driver);
+	for (dest = &auth->passdbs; *dest != NULL; dest = &(*dest)->next)
+		auth_passdb->id++;
+	*dest = auth_passdb;
+
+	iface = passdb_interface_find(set->driver);
 	if (iface == NULL)
-		i_fatal("Unknown passdb driver '%s'", driver);
+		i_fatal("Unknown passdb driver '%s'", set->driver);
 	if (iface->verify_plain == NULL) {
 		i_fatal("Support not compiled in for passdb driver '%s'",
-			driver);
+			set->driver);
 	}
 
 	if (iface->preinit == NULL && iface->init == NULL &&
 	    *auth_passdb->args != '\0') {
 		i_fatal("passdb %s: No args are supported: %s",
-			driver, auth_passdb->args);
+			set->driver, auth_passdb->args);
 	}
 
 	if (iface->preinit == NULL) {
@@ -179,7 +182,7 @@
 
 	if (passdb->passdb->blocking && !worker) {
 		/* blocking passdb - we need an auth server */
-		auth_worker_server_init();
+		auth_worker_server_init(passdb->auth);
 	}
 }
 
--- a/src/auth/passdb.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/passdb.h	Tue Jan 27 18:21:53 2009 -0500
@@ -80,8 +80,9 @@
 			       lookup_credentials_callback_t *callback,
                                struct auth_request *auth_request);
 
-struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver,
-				   const char *args, unsigned int id);
+struct auth_passdb *
+	passdb_preinit(struct auth *auth, struct auth_passdb_settings *set);
+
 void passdb_init(struct auth_passdb *passdb);
 void passdb_deinit(struct auth_passdb *passdb);
 
--- a/src/auth/userdb-passwd-file.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/userdb-passwd-file.c	Tue Jan 27 18:21:53 2009 -0500
@@ -97,7 +97,7 @@
 		       struct passwd_file_userdb_module, 1);
 	module->auth = auth_userdb->auth;
 	module->pwf = db_passwd_file_init(args, format, TRUE,
-					  module->auth->verbose_debug);
+					  module->auth->set->debug);
 
 	if (!module->pwf->vars)
 		module->module.cache_key = PASSWD_FILE_CACHE_KEY;
--- a/src/auth/userdb-prefetch.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/userdb-prefetch.c	Tue Jan 27 18:21:53 2009 -0500
@@ -26,7 +26,7 @@
 					"passdb didn't return userdb entries");
 			}
 		} else if (!auth_request->userdb_lookup ||
-			   auth_request->auth->verbose_debug) {
+			   auth_request->auth->set->debug) {
 			/* more userdbs, they may know the user */
 			auth_request_log_info(auth_request, "prefetch",
 				"passdb didn't return userdb entries, "
--- a/src/auth/userdb.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/userdb.c	Tue Jan 27 18:21:53 2009 -0500
@@ -100,33 +100,32 @@
 	return gr->gr_gid;
 }
 
-void userdb_preinit(struct auth *auth, const char *driver, const char *args)
+void userdb_preinit(struct auth *auth, struct auth_userdb_settings *set)
 {
 	struct userdb_module_interface *iface;
         struct auth_userdb *auth_userdb, **dest;
 
-	if (args == NULL) args = "";
-
 	auth_userdb = p_new(auth->pool, struct auth_userdb, 1);
 	auth_userdb->auth = auth;
-	auth_userdb->args = p_strdup(auth->pool, args);
+	auth_userdb->args = set->args == NULL ? "" :
+		p_strdup(auth->pool, set->args);
 
 	for (dest = &auth->userdbs; *dest != NULL; dest = &(*dest)->next)
 		auth_userdb->num++;
 	*dest = auth_userdb;
 
-	iface = userdb_interface_find(driver);
+	iface = userdb_interface_find(set->driver);
 	if (iface == NULL)
-		i_fatal("Unknown userdb driver '%s'", driver);
+		i_fatal("Unknown userdb driver '%s'", set->driver);
 	if (iface->lookup == NULL) {
 		i_fatal("Support not compiled in for userdb driver '%s'",
-			driver);
+			set->driver);
 	}
 
 	if (iface->preinit == NULL && iface->init == NULL &&
 	    *auth_userdb->args != '\0') {
 		i_fatal("userdb %s: No args are supported: %s",
-			driver, auth_userdb->args);
+			set->driver, auth_userdb->args);
 	}
 
 	if (iface->preinit == NULL) {
@@ -146,7 +145,7 @@
 
 	if (userdb->userdb->blocking && !worker) {
 		/* blocking userdb - we need an auth server */
-		auth_worker_server_init();
+		auth_worker_server_init(userdb->auth);
 	}
 }
 
--- a/src/auth/userdb.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/auth/userdb.h	Tue Jan 27 18:21:53 2009 -0500
@@ -41,7 +41,7 @@
 uid_t userdb_parse_uid(struct auth_request *request, const char *str);
 gid_t userdb_parse_gid(struct auth_request *request, const char *str);
 
-void userdb_preinit(struct auth *auth, const char *driver, const char *args);
+void userdb_preinit(struct auth *auth, struct auth_userdb_settings *set);
 void userdb_init(struct auth_userdb *userdb);
 void userdb_deinit(struct auth_userdb *userdb);
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,33 @@
+bin_PROGRAMS = doveconf
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
+	-DSYSCONFDIR=\""$(sysconfdir)"\" \
+	-DPKG_RUNDIR=\""$(rundir)"\" \
+	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
+	-DMODULEDIR=\""$(moduledir)"\" \
+	-DSSLDIR=\""$(ssldir)\""
+
+doveconf_LDADD = \
+	../lib-settings/libsettings.a \
+	../lib/liblib.a \
+	$(RAND_LIBS)
+
+doveconf_SOURCES = \
+	all-settings.c \
+	config-connection.c \
+	config-parser.c \
+	main.c
+
+noinst_HEADERS = \
+	all-settings.h \
+	common.h \
+	config-connection.h \
+	config-parser.h
+
+all-settings.c: $(SETTING_FILES) $(top_srcdir)/src/config/settings-get.pl
+	$(top_srcdir)/src/config/settings-get.pl $(SETTING_FILES) > all-settings.c || rm -f all-settings.c
+
+EXTRA_DIST = \
+	settings-get.pl
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/common.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,9 @@
+#ifndef __COMMON_H
+#define __COMMON_H
+
+#include "lib.h"
+
+extern struct master_service *service;
+extern string_t *config_string;
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-connection.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,123 @@
+/* Copyright (C) 2005 Timo Sirainen */
+
+#include "common.h"
+#include "str.h"
+#include "ioloop.h"
+#include "network.h"
+#include "istream.h"
+#include "ostream.h"
+#include "config-connection.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define MAX_INBUF_SIZE 1024
+
+#define CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION 1
+#define CONFIG_CLIENT_PROTOCOL_MINOR_VERSION 0
+
+struct config_connection {
+	int fd;
+	struct istream *input;
+	struct ostream *output;
+	struct io *io;
+
+	unsigned int version_received:1;
+	unsigned int handshaked:1;
+};
+
+static const char *const *
+config_connection_next_line(struct config_connection *conn)
+{
+	const char *line;
+
+	line = i_stream_next_line(conn->input);
+	if (line == NULL)
+		return NULL;
+
+	return t_strsplit(line, "\t");
+}
+
+static void config_connection_request(struct config_connection *conn,
+				      const char *const *args)
+{
+	/* <process> [<args>] */
+	// FIXME
+	o_stream_send(conn->output, str_data(config_string),
+		      str_len(config_string));
+	o_stream_flush(conn->output);
+}
+
+static void config_connection_input(void *context)
+{
+	struct config_connection *conn = context;
+	const char *const *args, *line;
+
+	switch (i_stream_read(conn->input)) {
+	case -2:
+		i_error("BUG: Config client connection sent too much data");
+                config_connection_destroy(conn);
+		return;
+	case -1:
+                config_connection_destroy(conn);
+		return;
+	}
+
+	if (!conn->version_received) {
+		line = i_stream_next_line(conn->input);
+		if (line == NULL)
+			return;
+
+		if (strncmp(line, "VERSION\t", 8) != 0 ||
+		    atoi(t_strcut(line + 8, '\t')) !=
+		    CONFIG_CLIENT_PROTOCOL_MAJOR_VERSION) {
+			i_error("Config client not compatible with this server "
+				"(mixed old and new binaries?)");
+			config_connection_destroy(conn);
+			return;
+		}
+		conn->version_received = TRUE;
+	}
+
+	t_push();
+	while ((args = config_connection_next_line(conn)) != NULL) {
+		if (args[0] == NULL)
+			continue;
+		if (strcmp(args[0], "REQ") == 0)
+			config_connection_request(conn, args + 1);
+	}
+	t_pop();
+}
+
+struct config_connection *config_connection_create(int fd)
+{
+	struct config_connection *conn;
+
+	conn = i_new(struct config_connection, 1);
+	conn->fd = fd;
+	conn->input = i_stream_create_fd(fd, MAX_INBUF_SIZE, FALSE);
+	conn->output = o_stream_create_fd(fd, (size_t)-1, FALSE);
+	conn->io = io_add(fd, IO_READ, config_connection_input, conn);
+	return conn;
+}
+
+void config_connection_destroy(struct config_connection *conn)
+{
+	io_remove(&conn->io);
+	i_stream_destroy(&conn->input);
+	o_stream_destroy(&conn->output);
+	if (close(conn->fd) < 0)
+		i_error("close(config conn) failed: %m");
+	i_free(conn);
+}
+
+void config_connection_dump_request(int fd, const char *service)
+{
+	struct config_connection *conn;
+	const char *args[2] = { service, NULL };
+
+	conn = config_connection_create(fd);
+        config_connection_request(conn, args);
+	config_connection_destroy(conn);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-connection.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,9 @@
+#ifndef __CONFIG_CONNECTION_H
+#define __CONFIG_CONNECTION_H
+
+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);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-parser.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,534 @@
+/* Copyright (C) 2005 Timo Sirainen */
+
+#include "lib.h"
+#include "array.h"
+#include "str.h"
+#include "hash.h"
+#include "strescape.h"
+#include "istream.h"
+#include "settings-parser.h"
+#include "all-settings.h"
+#include "config-parser.h"
+
+#include <unistd.h>
+#include <fcntl.h>
+
+#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
+
+void config_parsers_fix_parents(pool_t pool)
+{
+#if 0
+	struct config_setting_parser_list *l;
+	const struct setting_define *d;
+	ARRAY_DEFINE(parents, ARRAY_TYPE(dynamic_settings_parsers));
+	ARRAY_TYPE(dynamic_settings_parsers) *parsers;
+	struct dynamic_settings_parsers *parser;
+	unsigned int i, count;
+
+	/* FIXME: currently we assume everyone are under a single parent */
+	t_push();
+	t_array_init(&parents, 4);
+	for (l = config_setting_parsers; l->module_name != NULL; l++) {
+		if (l->root->parent == NULL)
+			continue;
+
+		for (d = l->root->parent->defines; d->key != NULL; d++) {
+			if (d->list_info == l->root)
+				break;
+		}
+
+		if (d->key == NULL) {
+			parsers = array_get_modifiable(&parents, &count);
+			for (i = 0; i < count; i++) {
+				parser = array_idx_modifiable(&parsers[i], 0);
+				if (parser->info->parent == l->root->parent)
+					break;
+			}
+			if (i == count) {
+				parsers = array_append_space(&parents);
+				t_array_init(parsers, 16);
+			}
+
+			parser = array_append_space(parsers);
+			parser->name = l->module_name;
+			parser->info = l->root;
+		}
+	}
+
+	parsers = array_get_modifiable(&parents, &count);
+	for (i = 0; i < count; i++) {
+		(void)array_append_space(&parsers[i]); /* NULL-terminate */
+		parser = array_idx_modifiable(&parsers[i], 0);
+
+		settings_parser_info_update(pool, parser->info->parent, parser);
+	}
+
+	t_pop();
+#endif
+}
+
+static const char *
+config_parse_line(pool_t pool, 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;
+			*info_r = settings_parse_get_prev_info(l->parser);
+		} else if (ret < 0)
+			return settings_parser_get_error(l->parser);
+	}
+
+	return found ? NULL : t_strconcat("Unknown setting: ", key, NULL);
+}
+
+struct settings_export_context {
+	string_t *dest;
+	string_t *value;
+	string_t *prefix;
+	struct hash_table *keys;
+	pool_t pool;
+	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;
+	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;
+			unsigned int pos = str_len(ctx->dest);
+			const char *const *strings;
+
+			if (!array_is_created(val))
+				break;
+
+			str_append_str(ctx->dest, ctx->prefix);
+			str_append(ctx->dest, def->key);
+			str_append(ctx->dest, "=0\n");
+
+			if (hash_table_lookup(ctx->keys,
+					      str_c(ctx->dest) + pos) != NULL) {
+				/* already added all of these */
+				str_truncate(ctx->dest, pos);
+				break;
+			}
+			key = p_strdup(ctx->pool, str_c(ctx->dest) + pos);
+			hash_table_insert(ctx->keys, key, key);
+
+			strings = array_get(val, &count);
+			i_assert(count % 2 == 0);
+			for (i = 0; i < count; i += 2) {
+				str_append_str(ctx->dest, ctx->prefix);
+				str_append(ctx->dest, def->key);
+				str_append_c(ctx->dest, SETTINGS_SEPARATOR);
+				str_append_c(ctx->dest, '0');
+				str_append_c(ctx->dest, SETTINGS_SEPARATOR);
+				str_append(ctx->dest, strings[i+0]);
+				str_append_c(ctx->dest, '=');
+				str_append(ctx->dest, strings[i+1]);
+				str_append_c(ctx->dest, '\n');
+			}
+			count = 0;
+			break;
+		}
+		}
+		if (str_len(ctx->value) > 0) {
+			unsigned int pos = str_len(ctx->dest);
+			str_append_str(ctx->dest, ctx->prefix);
+			str_append(ctx->dest, def->key);
+
+			if (hash_table_lookup(ctx->keys,
+					      str_c(ctx->dest) + pos) != NULL) {
+				/* already exists */
+				str_truncate(ctx->dest, pos);
+			} else {
+				str_append_c(ctx->dest, '=');
+				str_append_str(ctx->dest, ctx->value);
+				str_append_c(ctx->dest, '\n');
+				key = p_strconcat(ctx->pool, str_c(ctx->prefix),
+						  def->key, NULL);
+				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(string_t *dest)
+{
+	struct config_setting_parser_list *l;
+	struct settings_export_context ctx;
+	const void *set;
+
+	memset(&ctx, 0, sizeof(ctx));
+	ctx.dest = dest;
+	ctx.export_defaults = FALSE;
+	ctx.value = t_str_new(256);
+	ctx.prefix = t_str_new(64);
+	ctx.pool = pool_alloconly_create("config keys", 10240);
+	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);
+	pool_unref(&ctx.pool);
+}
+
+static const char *info_type_name_find(const struct setting_parser_info *info)
+{
+	unsigned int i;
+
+	for (i = 0; info->defines[i].key != NULL; i++) {
+		if (info->defines[i].offset == info->type_offset)
+			return info->defines[i].key;
+	}
+	i_panic("setting parser: Invalid type_offset value");
+	return NULL;
+}
+
+void config_parse_file(string_t *dest, const char *path, const char *service)
+{
+	ARRAY_DEFINE(pathlen_stack, unsigned int);
+	ARRAY_TYPE(const_string) auth_defaults;
+	const struct setting_parser_info *info;
+	unsigned int pathlen = 0;
+	unsigned int counter = 0, auth_counter = 0, cur_counter;
+	struct istream *input;
+	const char *errormsg, *name, *type_name;
+	char *line, *key, *p;
+	int fd, linenum, ret, ignore;
+	string_t *str, *full_line;
+	size_t len;
+	pool_t pool;
+	bool asis;
+
+	pool = pool_alloconly_create("config file parser", 10240);
+
+	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);
+
+	errormsg = config_parse_line(pool, "0", "auth=0", &info);
+	i_assert(errormsg == NULL);
+
+	str = t_str_new(256);
+	full_line = t_str_new(512);
+	linenum = 0; errormsg = NULL; ignore = 0; asis = FALSE;
+	input = i_stream_create_fd(fd, (size_t)-1, FALSE);
+	while ((line = i_stream_read_next_line(input)) != NULL) {
+		linenum++;
+
+		/* @UNSAFE: line is modified */
+
+		/* skip whitespace */
+		while (IS_WHITE(*line))
+			line++;
+
+		/* ignore comments or empty lines */
+		if (*line == '#' || *line == '\0')
+			continue;
+
+		/* strip away comments. pretty kludgy way really.. */
+		for (p = line; *p != '\0'; p++) {
+			if (*p == '\'' || *p == '"') {
+				char quote = *p;
+				for (p++; *p != quote && *p != '\0'; p++) {
+					if (*p == '\\' && p[1] != '\0')
+						p++;
+				}
+				if (*p == '\0')
+					break;
+			} else if (*p == '#') {
+				*p = '\0';
+				break;
+			}
+		}
+
+		/* remove whitespace from end of line */
+		len = strlen(line);
+		while (IS_WHITE(line[len-1]))
+			len--;
+		line[len] = '\0';
+
+		if (len > 0 && line[len-1] == '\\') {
+			/* continues in next line */
+			line[len-1] = '\0';
+			str_append(full_line, line);
+			continue;
+		}
+		if (str_len(full_line) > 0) {
+			str_append(full_line, line);
+			line = str_c_modifiable(full_line);
+		}
+
+		/* a) key = value
+		   b) section_type [section_name] {
+		   c) } */
+		key = line;
+		while (!IS_WHITE(*line) && *line != '\0' && *line != '=')
+			line++;
+		if (IS_WHITE(*line)) {
+			*line++ = '\0';
+			while (IS_WHITE(*line)) line++;
+		}
+
+		ret = 1;
+		if (*line == '=') {
+			/* a) */
+			*line++ = '\0';
+			while (IS_WHITE(*line)) line++;
+
+			len = strlen(line);
+			if (len > 0 &&
+			    ((*line == '"' && line[len-1] == '"') ||
+			     (*line == '\'' && line[len-1] == '\''))) {
+				line[len-1] = '\0';
+				line = str_unescape(line+1);
+			}
+
+			str_truncate(str, pathlen);
+			str_append(str, key);
+			str_append_c(str, '=');
+			str_append(str, line);
+			if (ignore > 0) {
+				/* ignore this setting */
+			} else if (pathlen == 0 &&
+				   strncmp(str_c(str), "auth_", 5) == 0) {
+				/* verify that the setting is valid,
+				   but delay actually adding it */
+				const char *s = t_strdup(str_c(str) + 5);
+
+				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);
+				array_append(&auth_defaults, &s, 1);
+			} else if (asis) {
+				/* don't do any parsing, just add it */
+				str_append(dest, str_c(str));
+				str_append_c(dest, '\n');
+			} else {
+				errormsg = config_parse_line(pool, key, str_c(str), &info);
+			}
+		} else if (strcmp(key, "}") != 0 || *line != '\0') {
+			/* b) + errors */
+			line[-1] = '\0';
+
+			if (*line == '{')
+				name = "";
+			else {
+				name = line;
+				while (!IS_WHITE(*line) && *line != '\0')
+					line++;
+
+				if (*line != '\0') {
+					*line++ = '\0';
+					while (IS_WHITE(*line))
+						line++;
+				}
+			}
+
+			if (*line != '{')
+				errormsg = "Expecting '='";
+			else if (ignore > 0) {
+				ignore++;
+			} else if (strcmp(key, "protocol") == 0) {
+				array_append(&pathlen_stack, &pathlen, 1);
+				ignore = strcmp(name, service) != 0;
+			} else {
+				array_append(&pathlen_stack, &pathlen, 1);
+
+				str_truncate(str, pathlen);
+				str_append(str, key);
+				pathlen = str_len(str);
+
+				if (strcmp(key, "auth") == 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(pool, key,
+								     str_c(str), &info);
+				}
+
+				str_truncate(str, pathlen);
+				str_append_c(str, SETTINGS_SEPARATOR);
+				str_printfa(str, "%u", cur_counter);
+				str_append_c(str, SETTINGS_SEPARATOR);
+				pathlen = str_len(str);
+
+				if (errormsg == NULL && info->type_offset != (size_t)-1) {
+					type_name = info_type_name_find(info);
+					str_append(str, type_name);
+					str_append_c(str, '=');
+					str_append(str, name);
+					errormsg = config_parse_line(pool, type_name,
+								     str_c(str), &info);
+
+					str_truncate(str, pathlen);
+				}
+
+				if (strcmp(key, "auth") == 0 && errormsg == NULL) {
+					/* add auth default settings */
+					const char *const *lines;
+					unsigned int i, count;
+
+					lines = array_get(&auth_defaults, &count);
+					for (i = 0; i < count; i++) {
+						str_truncate(str, pathlen);
+
+						p = strchr(lines[i], '=');
+						str_append(str, lines[i]);
+
+						errormsg = config_parse_line(pool, t_strdup_until(lines[i], p), str_c(str), &info);
+						i_assert(errormsg == NULL);
+					}
+				}
+			}
+		} else {
+			/* c) */
+			unsigned int pathlen_count;
+			const unsigned int *arr;
+
+			if (ignore > 0)
+				ignore--;
+			asis = FALSE;
+
+			arr = array_get(&pathlen_stack, &pathlen_count);
+			if (pathlen_count == 0)
+				errormsg = "Unexpected '}'";
+			else {
+				pathlen = arr[pathlen_count - 1];
+				array_delete(&pathlen_stack,
+					     pathlen_count - 1, 1);
+			}
+		}
+
+		if (errormsg != NULL) {
+			i_fatal("Error in configuration file %s line %d: %s",
+				path, linenum, errormsg);
+			break;
+		}
+		str_truncate(full_line, 0);
+	}
+	i_stream_unref(&input);
+	config_export(dest);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/config-parser.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,8 @@
+#ifndef __CONFIG_PARSER_H
+#define __CONFIG_PARSER_H
+
+void config_parsers_fix_parents(pool_t pool);
+
+void config_parse_file(string_t *dest, const char *path, const char *service);
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/main.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,69 @@
+/* Copyright (C) 2005-2008 Timo Sirainen */
+
+#include "common.h"
+#include "lib-signals.h"
+#include "ioloop.h"
+#include "str.h"
+#include "config-connection.h"
+#include "config-parser.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+string_t *config_string;
+pool_t parsers_pool;
+
+static const char *config_path = SYSCONFDIR "/" PACKAGE ".conf";
+
+static void main_init(const char *service)
+{
+	i_set_failure_internal();
+
+	parsers_pool = pool_alloconly_create("parent parsers", 2048);
+	config_parsers_fix_parents(parsers_pool);
+
+	config_string = str_new(default_pool, 10240);
+	config_parse_file(config_string, config_path, service);
+	str_append_c(config_string, '\n');
+}
+
+int main(int argc, char *argv[])
+{
+	struct ioloop *ioloop;
+	const char *path, *service = "";
+	bool dump_nondefaults = FALSE, human_readable = FALSE;
+	int c;
+
+	lib_init();
+
+	path = getenv("CONFIG_FILE_PATH");
+	if (path != NULL)
+		config_path = path;
+
+	while ((c = getopt(argc, argv, "c:s:na")) > 0) {
+		switch (c) {
+		case 'c':
+			config_path = optarg;
+			break;
+		case 's':
+			service = optarg;
+			break;
+		case 'n':
+			dump_nondefaults = TRUE;
+			/* fall through */
+		case 'a':
+			/* FIXME: make it work */
+			human_readable = TRUE;
+			break;
+		default:
+			i_fatal("Unknown parameter: %c", c);
+		}
+	}
+
+	main_init(service);
+	ioloop = io_loop_create();
+	config_connection_dump_request(STDOUT_FILENO, "master");
+	io_loop_destroy(&ioloop);
+	lib_deinit();
+        return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config/settings-get.pl	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,87 @@
+#!/usr/bin/env perl
+use strict;
+
+print '#include "lib.h"'."\n";
+print '#include "settings-parser.h"'."\n";
+print '#include "all-settings.h"'."\n";
+print '#include <stddef.h>'."\n";
+print '#define CONFIG_BINARY'."\n";
+
+my %parsers = {};
+
+foreach my $file (@ARGV) {
+  my $f;
+  open($f, $file) || die "Can't open $file: $@";
+  
+  my $state = 0;
+  my $file_contents = "";
+  my $externs = "";
+  
+  while (<$f>) {
+    my $write = 0;
+    if ($state == 0) {
+      if (/struct .*_settings {/ ||
+	  /struct setting_define.*{/ ||
+	  /struct .*_default_settings = {/) {
+	$state++;
+      } elsif (/^(static )?struct setting_parser_info (.*) = {/) {
+	$state++;
+	$parsers{$2} = 1;
+      } elsif (/^extern struct setting_parser_info (.*);/) {
+	$externs .= "extern struct setting_parser_info $1;\n";
+      }
+
+      if (/#define.*DEF/ || /^#undef.*DEF/) {
+	$write = 1;
+	$state = 2 if (/\\$/);
+      }
+    } elsif ($state == 2) {
+      $write = 1;
+      $state = 0 if (!/\\$/);
+    }
+    
+    if ($state == 1 || $state == 3) {
+      if ($state == 1) {
+	if (/DEFLIST.*".*",(.*)$/) {
+	  my $value = $1;
+	  if ($value =~ /.*&(.*)\)/) {
+	    $parsers{$1} = 0;
+	    $externs .= "extern struct setting_parser_info $1;\n";
+	  } else {
+	    $state = 3;
+	  }
+	}
+      } elsif ($state == 3) {
+	if (/.*&(.*)\)/) {
+	  $parsers{$1} = 0;
+	}        
+      }
+      
+      $write = 1;
+      if (/};/) {
+	$state = 0;
+      }
+    }
+  
+    $file_contents .= $_ if ($write);
+  }
+  
+  print "/* $file */\n";
+  print $externs;
+  print $file_contents;
+
+  close $f;
+}
+
+print "struct config_setting_parser_list config_setting_parsers[] = {\n";
+foreach my $name (keys %parsers) {
+  next if (!$parsers{$name});
+
+  my $module = "";
+  if ($name =~ /^([^_]*)/) {
+    $module = $1;
+  }
+  print "  { \"$module\", &".$name.", NULL, NULL }, \n";
+}
+print "  { NULL, NULL, NULL, NULL }\n";
+print "};\n";
--- a/src/deliver/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/deliver/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -5,6 +5,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-auth \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
@@ -13,6 +14,7 @@
 	-I$(top_srcdir)/src/lib-storage \
 	-I$(top_srcdir)/src/lib-storage/index \
 	-I$(top_srcdir)/src/lib-storage/index/raw \
+	-DBINDIR=\""$(bindir)"\" \
 	-DSYSCONFDIR=\""$(sysconfdir)"\" \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DMODULEDIR=\""$(moduledir)"\"
@@ -28,6 +30,7 @@
 libs = \
 	$(STORAGE_LIBS) \
 	../lib-dict/libdict.a \
+	../lib-settings/libsettings.a \
 	$(unused_objects)
 
 deliver_LDADD = \
@@ -40,6 +43,7 @@
 deliver_SOURCES = \
 	auth-client.c \
 	deliver.c \
+	deliver-settings.c \
 	duplicate.c \
 	mail-send.c \
 	smtp-client.c
@@ -47,6 +51,7 @@
 headers = \
 	auth-client.h \
 	deliver.h \
+	deliver-settings.h \
 	duplicate.h \
 	mail-send.h \
 	smtp-client.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/deliver/deliver-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,159 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "deliver.h"
+#include "array.h"
+#include "hostpid.h"
+#include "istream.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "deliver-settings.h"
+
+#include <stddef.h>
+
+#define DOVECOT_CONFIG_BIN_PATH BINDIR"/doveconf"
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct deliver_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, offsetof(struct deliver_settings, field), defines }
+
+static struct setting_define deliver_setting_defines[] = {
+	DEF(SET_STR, base_dir),
+	DEF(SET_STR, log_path),
+	DEF(SET_STR, info_log_path),
+	DEF(SET_STR, log_timestamp),
+	DEF(SET_STR, syslog_facility),
+	DEF(SET_BOOL, version_ignore),
+	DEF(SET_UINT, umask),
+
+	DEF(SET_STR, mail_plugins),
+	DEF(SET_STR, mail_plugin_dir),
+	DEF(SET_STR_VARS, mail_log_prefix),
+
+	DEF(SET_STR, postmaster_address),
+	DEF(SET_STR, hostname),
+	DEF(SET_STR, sendmail_path),
+	DEF(SET_STR, rejection_subject),
+	DEF(SET_STR, rejection_reason),
+	DEF(SET_STR, auth_socket_path),
+	DEF(SET_STR, deliver_log_format),
+	DEF(SET_BOOL, quota_full_tempfail),
+
+	{ SET_STRLIST, "plugin", offsetof(struct deliver_settings, plugin_envs), NULL },
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct deliver_settings deliver_default_settings = {
+	MEMBER(base_dir) PKG_RUNDIR,
+	MEMBER(log_path) "",
+	MEMBER(info_log_path) "",
+	MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
+	MEMBER(syslog_facility) "mail",
+	MEMBER(version_ignore) FALSE,
+	MEMBER(umask) 0077,
+
+	MEMBER(mail_plugins) "",
+	MEMBER(mail_plugin_dir) MODULEDIR"/lda",
+	MEMBER(mail_log_prefix) "%Us(%u): ",
+
+	MEMBER(postmaster_address) "",
+	MEMBER(hostname) "",
+	MEMBER(sendmail_path) "/usr/lib/sendmail",
+	MEMBER(rejection_subject) "Rejected: %s",
+	MEMBER(rejection_reason)
+		"Your message to <%t> was automatically rejected:%n%r",
+	MEMBER(auth_socket_path) "auth-master",
+	MEMBER(deliver_log_format) "msgid=%m: %$",
+	MEMBER(quota_full_tempfail) FALSE
+};
+
+struct setting_parser_info deliver_setting_parser_info = {
+	MEMBER(defines) deliver_setting_defines,
+	MEMBER(defaults) &deliver_default_settings,
+
+	MEMBER(parent) NULL,
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct deliver_settings)
+};
+
+static pool_t settings_pool = NULL;
+
+static void fix_base_path(struct deliver_settings *set, const char **str)
+{
+	if (*str != NULL && **str != '\0' && **str != '/') {
+		*str = p_strconcat(settings_pool,
+				   set->base_dir, "/", *str, NULL);
+	}
+}
+
+struct setting_parser_context *
+deliver_settings_read(const char *path,
+		      struct deliver_settings **set_r,
+		      struct mail_user_settings **user_set_r)
+{
+	static const struct setting_parser_info *roots[] = {
+                &deliver_setting_parser_info,
+                &mail_user_setting_parser_info
+	};
+	void **sets;
+	struct deliver_settings *deliver_set;
+	struct setting_parser_context *parser;
+
+	if (settings_pool == NULL)
+		settings_pool = pool_alloconly_create("deliver settings", 1024);
+	else
+		p_clear(settings_pool);
+
+	mail_storage_namespace_defines_init(settings_pool);
+
+	parser = settings_parser_init_list(settings_pool,
+				roots, N_ELEMENTS(roots),
+				SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+	if (settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH,
+				path, "lda") < 0) {
+		i_fatal_status(EX_CONFIG, "Error reading configuration: %s",
+			       settings_parser_get_error(parser));
+	}
+
+	sets = settings_parser_get_list(parser);
+
+	deliver_set = sets[0];
+	if (*deliver_set->hostname == '\0')
+		deliver_set->hostname = my_hostname;
+	fix_base_path(deliver_set, &deliver_set->auth_socket_path);
+
+	if (*deliver_set->postmaster_address == '\0') {
+		i_fatal_status(EX_CONFIG,
+			       "postmaster_address setting not given");
+	}
+
+	*set_r = deliver_set;
+	*user_set_r = sets[1];
+	return parser;
+}
+
+void deliver_settings_add(struct setting_parser_context *parser,
+			  const ARRAY_TYPE(const_string) *extra_fields)
+{
+	const char *const *str, *p, *line;
+	unsigned int i, count;
+
+	str = array_get(extra_fields, &count);
+	for (i = 0; i < count; i++) T_BEGIN {
+		p = strchr(str[i], '=');
+		if (p != NULL)
+			line = str[i];
+		else
+			line = t_strconcat(str[i], "=yes", NULL);
+		if (settings_parse_line(parser, str[i]) < 0) {
+			i_fatal_status(EX_CONFIG,
+				       "Invalid userdb input '%s': %s", str[i],
+				       settings_parser_get_error(parser));
+		}
+	} T_END;
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/deliver/deliver-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,39 @@
+#ifndef DELIVER_SETTINGS_H
+#define DELIVER_SETTINGS_H
+
+struct mail_user_settings;
+
+struct deliver_settings {
+	const char *base_dir;
+	const char *log_path;
+	const char *info_log_path;
+	const char *log_timestamp;
+	const char *syslog_facility;
+	bool version_ignore;
+	unsigned int umask;
+
+	const char *mail_plugins;
+	const char *mail_plugin_dir;
+	const char *mail_log_prefix;
+
+	/* deliver: */
+	const char *postmaster_address;
+	const char *hostname;
+	const char *sendmail_path;
+	const char *rejection_subject;
+	const char *rejection_reason;
+	const char *auth_socket_path;
+	const char *deliver_log_format;
+	bool quota_full_tempfail;
+
+	ARRAY_DEFINE(plugin_envs, const char *);
+};
+
+struct setting_parser_context *
+deliver_settings_read(const char *path,
+		      struct deliver_settings **set_r,
+		      struct mail_user_settings **user_set_r);
+void deliver_settings_add(struct setting_parser_context *parser,
+			  const ARRAY_TYPE(const_string) *extra_fields);
+
+#endif
--- a/src/deliver/deliver.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/deliver/deliver.c	Tue Jan 27 18:21:53 2009 -0500
@@ -25,6 +25,7 @@
 #include "mail-namespace.h"
 #include "raw-storage.h"
 #include "imap-utf7.h"
+#include "settings-parser.h"
 #include "dict.h"
 #include "auth-client.h"
 #include "mail-send.h"
@@ -56,6 +57,8 @@
 
 struct deliver_settings *deliver_set;
 deliver_mail_func_t *deliver_mail = NULL;
+bool mailbox_autosubscribe;
+bool mailbox_autocreate;
 bool tried_default_save = FALSE;
 
 /* FIXME: these two should be in some context struct instead of as globals.. */
@@ -66,10 +69,6 @@
 static struct module *modules;
 static struct ioloop *ioloop;
 
-static pool_t plugin_pool;
-static ARRAY_DEFINE(lda_envs, const char *);
-static ARRAY_DEFINE(plugin_envs, const char *);
-
 static void sig_die(int signo, void *context ATTR_UNUSED)
 {
 	/* warn about being killed because of some signal, except SIGINT (^C)
@@ -135,7 +134,7 @@
 	msg = t_strdup_vprintf(fmt, args);
 
 	str = t_str_new(256);
-	var_expand(str, deliver_set->log_format,
+	var_expand(str, deliver_set->deliver_log_format,
 		   get_log_var_expand_table(mail, msg));
 	i_info("%s", str_c(str));
 	va_end(args);
@@ -173,7 +172,7 @@
 	}
 
 	box = mailbox_open(storage_r, name, NULL, open_flags);
-	if (box != NULL || !deliver_set->mailbox_autocreate)
+	if (box != NULL || !mailbox_autocreate)
 		return box;
 
 	(void)mail_storage_get_last_error(*storage_r, &error);
@@ -183,7 +182,7 @@
 	/* try creating it. */
 	if (mail_storage_mailbox_create(*storage_r, name, FALSE) < 0)
 		return NULL;
-	if (deliver_set->mailbox_autosubscribe) {
+	if (mailbox_autosubscribe) {
 		/* (try to) subscribe to it */
 		(void)mailbox_list_set_subscribed(ns->list, name, TRUE);
 	}
@@ -276,294 +275,6 @@
 			       count++, deliver_set->hostname);
 }
 
-#include "settings.h"
-#include "../master/master-settings.h"
-#include "../master/master-settings-defs.c"
-
-#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
-
-static bool setting_is_bool(const char *name)
-{
-	const struct setting_def *def;
-
-	for (def = setting_defs; def->name != NULL; def++) {
-		if (strcmp(def->name, name) == 0)
-			return def->type == SET_BOOL;
-	}
-	if (strncmp(name, "NAMESPACE_", 10) == 0) {
-		return strstr(name, "_list") != NULL ||
-			strstr(name, "_inbox") != NULL ||
-			strstr(name, "_hidden") != NULL ||
-			strstr(name, "_subscriptions") != NULL;
-	}
-	if (strcmp(name, "quota_full_tempfail") == 0)
-		return TRUE;
-	return FALSE;
-}
-
-/* more ugly kludging because we have our own config parsing code.
-   hopefully this goes away in v1.2. */
-static struct {
-	const char *name;
-	bool set;
-} default_yes_settings[] = {
-	{ "dotlock_use_excl", TRUE },
-	{ "maildir_copy_with_hardlinks", TRUE },
-	{ "mbox_dirty_syncs", TRUE },
-	{ "mbox_lazy_writes", TRUE }
-};
-
-static void config_file_init(const char *path)
-{
-	struct istream *input;
-	const char *key, *value, *str, *ukey;
-	char *line, *p, quote;
-	int fd, sections = 0;
-	bool lda_section = FALSE, pop3_section = FALSE, plugin_section = FALSE;
-	bool ns_section = FALSE, ns_location = FALSE, ns_list = FALSE;
-	bool ns_subscriptions = FALSE;
-	unsigned int i, ns_idx = 0;
-	size_t len;
-
-	plugin_pool = pool_alloconly_create("Plugin strings", 512);
-	i_array_init(&lda_envs, 16);
-	i_array_init(&plugin_envs, 16);
-
-	fd = open(path, O_RDONLY);
-	if (fd < 0)
-		i_fatal_status(EX_CONFIG, "open(%s) failed: %m", path);
-
-	input = i_stream_create_fd(fd, 1024, TRUE);
-	i_stream_set_return_partial_line(input, TRUE);
-	while ((line = i_stream_read_next_line(input)) != NULL) {
-		/* @UNSAFE: line is modified */
-
-		/* skip whitespace */
-		while (IS_WHITE(*line))
-			line++;
-
-		/* ignore comments or empty lines */
-		if (*line == '#' || *line == '\0')
-			continue;
-
-		/* strip away comments. pretty kludgy way really.. */
-		for (p = line; *p != '\0'; p++) {
-			if (*p == '\'' || *p == '"') {
-				quote = *p;
-				for (p++; *p != quote && *p != '\0'; p++) {
-					if (*p == '\\' && p[1] != '\0')
-						p++;
-				}
-				if (*p == '\0')
-					break;
-			} else if (*p == '#') {
-				*p = '\0';
-				break;
-			}
-		}
-
-		/* remove whitespace from end of line */
-		len = strlen(line);
-		while (IS_WHITE(line[len-1]))
-			len--;
-		line[len] = '\0';
-
-		if (strncmp(line, "!include_try ", 13) == 0)
-			continue;
-		if (strncmp(line, "!include ", 9) == 0) {
-			i_fatal_status(EX_CONFIG, "Error in config file %s: "
-				       "deliver doesn't support !include directive", path);
-		}
-
-		value = p = strchr(line, '=');
-		if (value == NULL) {
-			if (strchr(line, '{') != NULL) {
-				if (strcmp(line, "protocol lda {") == 0)
-					lda_section = TRUE;
-				else if (strcmp(line, "plugin {") == 0)
-					plugin_section = TRUE;
-				else if (strcmp(line, "protocol pop3 {") == 0)
-					pop3_section = TRUE;
-				else if (strncmp(line, "namespace ", 10) == 0) {
-					ns_section = TRUE;
-					ns_idx++;
-					line += 10;
-					env_put(t_strdup_printf(
-						"NAMESPACE_%u_TYPE=%s", ns_idx,
-						t_strcut(line, ' ')));
-				}
-				sections++;
-			}
-			if (*line == '}') {
-				sections--;
-				lda_section = FALSE;
-				plugin_section = FALSE;
-				pop3_section = FALSE;
-				if (ns_section) {
-					ns_section = FALSE;
-					if (ns_location)
-						ns_location = FALSE;
-					else {
-						env_put(t_strdup_printf(
-							"NAMESPACE_%u=", ns_idx));
-					}
-					if (ns_list)
-						ns_list = FALSE;
-					else {
-						env_put(t_strdup_printf(
-							"NAMESPACE_%u_LIST=1", ns_idx));
-					}
-					if (ns_subscriptions)
-						ns_subscriptions = FALSE;
-					else {
-						env_put(t_strdup_printf(
-							"NAMESPACE_%u_SUBSCRIPTIONS=1",
-							ns_idx));
-					}
-				}
-			}
-			continue;
-		}
-
-		while (p > line && IS_WHITE(p[-1])) p--;
-		key = t_strdup_until(line, p);
-
-		if (sections > 0 && !lda_section && !plugin_section) {
-			if (pop3_section) {
-				if (strcmp(key, "pop3_uidl_format") != 0)
-					continue;
-			} else if (ns_section) {
-				if (strcmp(key, "separator") == 0) {
-					key = t_strdup_printf(
-						"NAMESPACE_%u_SEP", ns_idx);
-				} else if (strcmp(key, "location") == 0) {
-					ns_location = TRUE;
-					key = t_strdup_printf("NAMESPACE_%u",
-							      ns_idx);
-				} else {
-					if (strcmp(key, "list") == 0)
-						ns_list = TRUE;
-					if (strcmp(key, "subscriptions") == 0)
-						ns_subscriptions = TRUE;
-					key = t_strdup_printf("NAMESPACE_%u_%s",
-							      ns_idx, key);
-				}
-			} else {
-				/* unwanted section */
-				continue;
-			}
-		}
-
-		do {
-			value++;
-		} while (IS_WHITE(*value));
-
-		len = strlen(value);
-		if (len > 0 &&
-		    ((*value == '"' && value[len-1] == '"') ||
-		     (*value == '\'' && value[len-1] == '\''))) {
-			value = str_unescape(p_strndup(unsafe_data_stack_pool,
-						       value+1, len - 2));
-		}
-		ukey = t_str_ucase(key);
-		if (setting_is_bool(key) && strcasecmp(value, "yes") != 0) {
-			for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) {
-				if (strcmp(default_yes_settings[i].name,
-					   key) == 0)
-					default_yes_settings[i].set = FALSE;
-			}
-			env_remove(ukey);
-			continue;
-		}
-
-		if (lda_section) {
-			str = p_strconcat(plugin_pool, ukey, "=", value, NULL);
-			array_append(&lda_envs, &str, 1);
-		}
-		if (!plugin_section) {
-			env_put(t_strconcat(ukey, "=", value, NULL));
-		} else {
-			/* %variables need to be expanded.
-			   store these for later. */
-			value = p_strconcat(plugin_pool,
-					    ukey, "=", value, NULL);
-			array_append(&plugin_envs, &value, 1);
-		}
-	}
-	i_stream_unref(&input);
-
-	for (i = 0; i < N_ELEMENTS(default_yes_settings); i++) {
-		if (default_yes_settings[i].set) {
-			key = default_yes_settings[i].name;
-			env_put(t_strconcat(t_str_ucase(key), "=1", NULL));
-		}
-	}
-}
-
-static const struct var_expand_table *
-get_var_expand_table(const char *user, const char *home)
-{
-	static struct var_expand_table static_tab[] = {
-		{ 'u', NULL, "user" },
-		{ 'n', NULL, "username" },
-		{ 'd', NULL, "domain" },
-		{ 's', NULL, "service" },
-		{ 'h', NULL, "home" },
-		{ 'l', NULL, "lip" },
-		{ 'r', NULL, "rip" },
-		{ 'p', NULL, "pid" },
-		{ 'i', NULL, "uid" },
-		{ '\0', NULL, NULL }
-	};
-	struct var_expand_table *tab;
-
-	tab = t_malloc(sizeof(static_tab));
-	memcpy(tab, static_tab, sizeof(static_tab));
-
-	tab[0].value = user;
-	tab[1].value = t_strcut(user, '@');
-	tab[2].value = strchr(user, '@');
-	if (tab[2].value != NULL) tab[2].value++;
-	tab[3].value = "DELIVER";
-	tab[4].value = home != NULL ? home :
-		"/HOME_DIRECTORY_USED_BUT_NOT_GIVEN_BY_USERDB";
-	tab[5].value = NULL;
-	tab[6].value = NULL;
-	tab[7].value = my_pid;
-	tab[8].value = dec2str(geteuid());
-
-	return tab;
-}
-
-static const char *
-expand_mail_env(const char *env, const struct var_expand_table *table)
-{
-	string_t *str;
-	const char *p;
-
-	str = t_str_new(256);
-
-	/* it's either type:data or just data */
-	p = strchr(env, ':');
-	if (p != NULL) {
-		while (env != p) {
-			str_append_c(str, *env);
-			env++;
-		}
-
-		str_append_c(str, *env++);
-	}
-
-	if (env[0] == '~' && env[1] == '/') {
-		/* expand home */
-		env = t_strconcat("%h", env+1, NULL);
-	}
-
-	/* expand %vars */
-	var_expand(str, env, table);
-	return str_c(str);
-}
-
 static const char *escape_local_part(const char *local_part)
 {
 	const char *p;
@@ -685,15 +396,15 @@
 
 static void open_logfile(const char *username)
 {
-	const char *prefix, *log_path, *stamp;
+	const char *prefix, *log_path;
 
 	prefix = t_strdup_printf("deliver(%s): ", username);
-	log_path = home_expand(getenv("LOG_PATH"));
-	if (log_path == NULL || *log_path == '\0') {
-		const char *env = getenv("SYSLOG_FACILITY");
+	log_path = home_expand(deliver_set->log_path);
+	if (*log_path == '\0') {
 		int facility;
 
-		if (env == NULL || !syslog_facility_find(env, &facility))
+		if (!syslog_facility_find(deliver_set->syslog_facility,
+					  &facility))
 			facility = LOG_MAIL;
 		i_set_failure_prefix(prefix);
 		i_set_failure_syslog("dovecot", LOG_NDELAY, facility);
@@ -702,14 +413,11 @@
 		i_set_failure_file(log_path, prefix);
 	}
 
-	log_path = home_expand(getenv("INFO_LOG_PATH"));
-	if (log_path != NULL && *log_path != '\0')
+	log_path = home_expand(deliver_set->info_log_path);
+	if (*log_path != '\0')
 		i_set_info_file(log_path);
 
-	stamp = getenv("LOG_TIMESTAMP");
-	if (stamp == NULL)
-		stamp = DEFAULT_FAILURE_STAMP_FORMAT;
-	i_set_failure_timestamp_format(stamp);
+	i_set_failure_timestamp_format(deliver_set->log_timestamp);
 }
 
 static void print_help(void)
@@ -739,72 +447,20 @@
 	if (home != NULL) env_put(home);
 }
 
-static void expand_envs(const char *user)
+static void plugin_get_home(void)
 {
-        const struct var_expand_table *table;
-	const char *value, *const *envs, *home, *env_name;
-	unsigned int i, count;
-	string_t *str;
-
-	home = getenv("HOME");
-
-	str = t_str_new(256);
-	table = get_var_expand_table(user, home);
-	envs = array_get(&plugin_envs, &count);
-	for (i = 0; i < count; i++) {
-		str_truncate(str, 0);
-		var_expand(str, envs[i], table);
-		env_put(str_c(str));
-	}
-	/* add LDA envs again to make sure they override plugin settings */
-	envs = array_get(&lda_envs, &count);
-	for (i = 0; i < count; i++)
-		env_put(envs[i]);
-
-	/* get the table again in case plugin envs provided the home
-	   directory (yea, kludgy) */
-	if (home == NULL)
-		home = getenv("HOME");
-	table = get_var_expand_table(user, home);
-
-	value = getenv("MAIL_LOCATION");
-	if (value != NULL)
-		value = expand_mail_env(value, table);
-	env_put(t_strconcat("MAIL=", value, NULL));
-
-	for (i = 1;; i++) {
-		env_name = t_strdup_printf("NAMESPACE_%u", i);
-		value = getenv(env_name);
-		if (value == NULL)
-			break;
-
-		value = expand_mail_env(value, table);
-		env_put(t_strconcat(env_name, "=", value, NULL));
-
-		env_name = t_strdup_printf("NAMESPACE_%u_PREFIX", i);
-		value = getenv(env_name);
-		if (value != NULL) {
-			str_truncate(str, 0);
-			var_expand(str, value, table);
-			env_put(t_strconcat(env_name, "=", str_c(str), NULL));
-		}
-	}
-}
-
-static void putenv_extra_fields(const ARRAY_TYPE(const_string) *extra_fields)
-{
-	const char *const *fields;
-	const char *key, *p;
+	const char *const *envs;
 	unsigned int i, count;
 
-	fields = array_get(extra_fields, &count);
-	for (i = 0; i < count; i++) {
-		p = strchr(fields[i], '=');
-		if (p == NULL)
-			env_put(t_strconcat(fields[i], "=1", NULL));
-		else {
-			key = t_str_ucase(t_strdup_until(fields[i], p));
-			env_put(t_strconcat(key, p, NULL));
+	/* kludgy. this should be removed some day, but for now don't break
+	   existing setups that rely on it. */
+	if (array_is_created(&deliver_set->plugin_envs)) {
+		envs = array_get(&deliver_set->plugin_envs, &count);
+		for (i = 0; i < count; i++) {
+			if (strncmp(envs[i], "home=", 5) == 0) {
+				env_put(t_strconcat("HOME=", envs[i]+5, NULL));
+				break;
+			}
 		}
 	}
 }
@@ -813,17 +469,20 @@
 {
 	const char *config_path = DEFAULT_CONFIG_FILE;
 	const char *mailbox = "INBOX";
-	const char *auth_socket;
-	const char *home, *destaddr, *user, *value, *errstr, *path, *orig_user;
+	const char *home, *destaddr, *user, *error, *path, *orig_user;
 	ARRAY_TYPE(const_string) extra_fields = ARRAY_INIT;
+	struct setting_parser_context *parser;
 	struct mail_user *mail_user, *raw_mail_user;
 	struct mail_namespace *raw_ns;
+	struct mail_namespace_settings raw_ns_set;
 	struct mail_storage *storage;
 	struct mailbox *box;
 	struct raw_mailbox *raw_box;
 	struct istream *input;
 	struct mailbox_transaction_context *t;
 	struct mailbox_header_lookup_ctx *headers_ctx;
+	struct mail_user_settings *user_set;
+	const struct mail_storage_settings *mail_set;
 	struct mail *mail;
 	uid_t process_euid;
 	bool stderr_rejection = FALSE;
@@ -867,7 +526,7 @@
 #endif
 
 	deliver_set = i_new(struct deliver_settings, 1);
-	deliver_set->mailbox_autocreate = TRUE;
+	mailbox_autocreate = TRUE;
 
 	destaddr = user = path = NULL;
 	for (i = 1; i < argc; i++) {
@@ -918,9 +577,9 @@
 				mailbox = str_c(str);
 			}
 		} else if (strcmp(argv[i], "-n") == 0) {
-			deliver_set->mailbox_autocreate = FALSE;
+			mailbox_autocreate = FALSE;
 		} else if (strcmp(argv[i], "-s") == 0) {
-			deliver_set->mailbox_autosubscribe = TRUE;
+			mailbox_autosubscribe = TRUE;
 		} else if (strcmp(argv[i], "-f") == 0) {
 			/* envelope sender address */
 			i++;
@@ -964,42 +623,29 @@
 			"destination user parameter (-d user) not given");
 	}
 
-	T_BEGIN {
-		config_file_init(config_path);
-	} T_END;
+        mail_storage_init();
+	mail_storage_register_all();
+	mailbox_list_register_all();
+
+	parser = deliver_settings_read(config_path, &deliver_set, &user_set);
 	open_logfile(user);
 
-	if (getenv("MAIL_DEBUG") != NULL)
-		env_put("DEBUG=1");
-
-	if (getenv("MAIL_PLUGINS") == NULL)
+	mail_set = mail_user_set_get_driver_settings(user_set, "MAIL");
+	if (deliver_set->mail_plugins == '\0')
 		modules = NULL;
 	else {
-		const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
 		const char *version;
 
-		if (plugin_dir == NULL)
-			plugin_dir = MODULEDIR"/lda";
-
-		version = getenv("VERSION_IGNORE") != NULL ?
-			NULL : PACKAGE_VERSION;
-		modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
+		version = deliver_set->version_ignore ? NULL : PACKAGE_VERSION;
+		modules = module_dir_load(deliver_set->mail_plugin_dir,
+					  deliver_set->mail_plugins,
 					  TRUE, version);
 	}
 
 	if (user_auth) {
-		auth_socket = getenv("AUTH_SOCKET_PATH");
-		if (auth_socket == NULL) {
-			const char *base_dir = getenv("BASE_DIR");
-			if (base_dir == NULL)
-				base_dir = PKG_RUNDIR;
-			auth_socket = t_strconcat(base_dir, "/auth-master",
-						  NULL);
-		}
-
 		userdb_pool = pool_alloconly_create("userdb lookup replys", 512);
 		orig_user = user;
-		ret = auth_client_lookup_and_restrict(auth_socket,
+		ret = auth_client_lookup_and_restrict(deliver_set->auth_socket_path,
 						      &user, process_euid,
 						      userdb_pool,
 						      &extra_fields);
@@ -1008,7 +654,7 @@
 
 		if (strcmp(user, orig_user) != 0) {
 			/* auth lookup changed the user. */
-			if (getenv("DEBUG") != NULL)
+			if (mail_set->mail_debug)
 				i_info("userdb changed username to %s", user);
 			i_set_failure_prefix(t_strdup_printf("deliver(%s): ",
 							     user));
@@ -1018,90 +664,62 @@
 		user = t_strdup(user);
 	}
 
-	expand_envs(user);
 	if (userdb_pool != NULL) {
-		putenv_extra_fields(&extra_fields);
+		settings_parse_set_expanded(parser, TRUE);
+		deliver_settings_add(parser, &extra_fields);
 		pool_unref(&userdb_pool);
 	}
 
-	/* Fix namespaces with empty locations */
-	for (i = 1;; i++) {
-		value = getenv(t_strdup_printf("NAMESPACE_%u", i));
-		if (value == NULL)
-			break;
-
-		if (*value == '\0') {
-			env_put(t_strdup_printf("NAMESPACE_%u=%s", i,
-						getenv("MAIL")));
-		}
+	home = getenv("HOME");
+	if (home == NULL) {
+		plugin_get_home();
+		home = getenv("HOME");
 	}
 
 	/* If possible chdir to home directory, so that core file
 	   could be written in case we crash. */
-	home = getenv("HOME");
 	if (home != NULL) {
 		if (chdir(home) < 0) {
 			if (errno != ENOENT)
 				i_error("chdir(%s) failed: %m", home);
-			else if (getenv("DEBUG") != NULL)
+			else if (mail_set->mail_debug)
 				i_info("Home dir not found: %s", home);
 		}
 	}
 
 	env_put(t_strconcat("USER=", user, NULL));
-
-	value = getenv("UMASK");
-	if (value == NULL || sscanf(value, "%i", &i) != 1 || i < 0)
-		i = 0077;
-	(void)umask(i);
-
-	deliver_set->hostname = getenv("HOSTNAME");
-	if (deliver_set->hostname == NULL)
-		deliver_set->hostname = my_hostname;
-	deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS");
-	if (deliver_set->postmaster_address == NULL) {
-		i_fatal_status(EX_CONFIG,
-			       "postmaster_address setting not given");
-	}
-	deliver_set->sendmail_path = getenv("SENDMAIL_PATH");
-	if (deliver_set->sendmail_path == NULL)
-		deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH;
-	deliver_set->rejection_subject = getenv("REJECTION_SUBJECT");
-	if (deliver_set->rejection_subject == NULL)
-		deliver_set->rejection_subject = DEFAULT_MAIL_REJECTION_SUBJECT;
-	deliver_set->rejection_reason = getenv("REJECTION_REASON");
-	if (deliver_set->rejection_reason == NULL) {
-		deliver_set->rejection_reason =
-			DEFAULT_MAIL_REJECTION_HUMAN_REASON;
-	}
-	deliver_set->log_format = getenv("DELIVER_LOG_FORMAT");
-	if (deliver_set->log_format == NULL)
-		deliver_set->log_format = DEFAULT_LOG_FORMAT;
+	(void)umask(deliver_set->umask);
 
 	dict_drivers_register_builtin();
         duplicate_init();
-	mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL);
-        mail_storage_init();
-	mail_storage_register_all();
-	mailbox_list_register_all();
+	mail_users_init(deliver_set->auth_socket_path, mail_set->mail_debug);
 
 	module_dir_init(modules);
 
-	mail_user = mail_user_init(user);
+	mail_user = mail_user_alloc(user, user_set);
 	mail_user_set_home(mail_user, home);
-	if (mail_namespaces_init(mail_user) < 0)
-		i_fatal("Namespace initialization failed");
+	mail_user_set_vars(mail_user, geteuid(), "deliver", NULL, NULL);
+	if (mail_user_init(mail_user, &error) < 0)
+		i_fatal("Mail user initialization failed: %s", error);
+	if (mail_namespaces_init(mail_user, &error) < 0)
+		i_fatal("Namespace initialization failed: %s", error);
 
 	/* create a separate mail user for the internal namespace */
-	raw_mail_user = mail_user_init(user);
-	mail_user_set_home(raw_mail_user, NULL);
+	raw_mail_user = mail_user_alloc(user, user_set);
+	mail_user_set_home(raw_mail_user, "/");
+	if (mail_user_init(raw_mail_user, &error) < 0)
+		i_fatal("Raw user initialization failed: %s", error);
+
+	settings_parser_deinit(&parser);
+
+	memset(&raw_ns_set, 0, sizeof(raw_ns_set));
+	raw_ns_set.location = "/tmp";
+
 	raw_ns = mail_namespaces_init_empty(raw_mail_user);
 	raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
-
-	if (mail_storage_create(raw_ns, "raw", "/tmp",
-				MAIL_STORAGE_FLAG_FULL_FS_ACCESS,
-				FILE_LOCK_METHOD_FCNTL, &errstr) < 0)
-		i_fatal("Couldn't create internal raw storage: %s", errstr);
+	raw_ns->set = &raw_ns_set;
+	if (mail_storage_create(raw_ns, "raw", 0, &error) < 0)
+		i_fatal("Couldn't create internal raw storage: %s", error);
 	if (path == NULL) {
 		input = create_raw_stream(0, &mtime);
 		box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail",
@@ -1186,7 +804,7 @@
 		}
 
 		if (error != MAIL_ERROR_NOSPACE ||
-		    getenv("QUOTA_FULL_TEMPFAIL") != NULL) {
+		    deliver_set->quota_full_tempfail) {
 			/* Saving to INBOX should always work unless
 			   we're over quota. If it didn't, it's probably a
 			   configuration problem. */
--- a/src/deliver/deliver.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/deliver/deliver.h	Tue Jan 27 18:21:53 2009 -0500
@@ -9,25 +9,11 @@
 
 #include "lib.h"
 #include "mail-storage.h"
-
-#define DEFAULT_MAIL_REJECTION_SUBJECT \
-	"Rejected: %s"
-#define DEFAULT_MAIL_REJECTION_HUMAN_REASON \
-	"Your message to <%t> was automatically rejected:%n%r"
-#define DEFAULT_LOG_FORMAT "msgid=%m: %$"
-
-struct deliver_settings {
-	const char *hostname;
-	const char *postmaster_address;
-	const char *sendmail_path;
-	const char *rejection_subject;
-	const char *rejection_reason;
-	const char *log_format;
-	bool mailbox_autosubscribe;
-	bool mailbox_autocreate;
-};
+#include "deliver-settings.h"
 
 extern struct deliver_settings *deliver_set;
+extern bool mailbox_autosubscribe;
+extern bool mailbox_autocreate;
 extern bool tried_default_save;
 
 typedef int deliver_mail_func_t(struct mail_namespace *namespaces,
--- a/src/imap-login/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap-login/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -12,6 +12,7 @@
 	../login-common/liblogin-common.a \
 	../lib-imap/libimap.a \
 	../lib-auth/libauth.a \
+	../lib-settings/libsettings.a \
 	../lib/liblib.a \
 	$(SSL_LIBS)
 
--- a/src/imap-login/client-authenticate.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap-login/client-authenticate.c	Tue Jan 27 18:21:53 2009 -0500
@@ -39,7 +39,7 @@
 		   c) we allow insecure authentication
 		*/
 		if ((mech[i].flags & MECH_SEC_PRIVATE) == 0 &&
-		    (secured || !disable_plaintext_auth ||
+		    (secured || !login_settings->disable_plaintext_auth ||
 		     (mech[i].flags & MECH_SEC_PLAINTEXT) == 0)) {
 			str_append_c(str, ' ');
 			str_append(str, "AUTH=");
@@ -158,7 +158,7 @@
 			master_user = *args + 7;
 		else if (strncmp(*args, "user=", 5) == 0) {
 			/* already handled in login-common */
-		} else if (auth_debug) {
+		} else if (login_settings->auth_debug) {
 			i_info("Ignoring unknown passdb extra field: %s",
 			       *args);
 		}
@@ -347,8 +347,9 @@
 		init_resp = IMAP_ARG_STR(&args[1]);
 	}
 
-	if (!client->common.secured && ssl_required) {
-		if (verbose_auth) {
+	if (!client->common.secured &&
+	    strcmp(login_settings->ssl, "required") == 0) {
+		if (login_settings->verbose_auth) {
 			client_syslog(&client->common, "Login failed: "
 				      "SSL required for authentication");
 		}
@@ -381,8 +382,8 @@
 	user = IMAP_ARG_STR(&args[0]);
 	pass = IMAP_ARG_STR(&args[1]);
 
-	if (!client->common.secured && disable_plaintext_auth) {
-		if (verbose_auth) {
+	if (!client->common.secured && login_settings->disable_plaintext_auth) {
+		if (login_settings->verbose_auth) {
 			client_syslog(&client->common, "Login failed: "
 				      "Plaintext authentication disabled");
 		}
--- a/src/imap-login/client.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap-login/client.c	Tue Jan 27 18:21:53 2009 -0500
@@ -52,7 +52,8 @@
 {
 	const char *addr;
 
-	if (!verbose_proctitle || !process_per_connection)
+	if (!login_settings->verbose_proctitle ||
+	    !login_settings->login_process_per_connection)
 		return;
 
 	addr = net_ip2addr(&client->common.ip);
@@ -99,7 +100,8 @@
 	return t_strconcat(full ? capability_string : CAPABILITY_BANNER_STRING,
 			   (ssl_initialized && !client->common.tls) ?
 			   " STARTTLS" : "",
-			   disable_plaintext_auth && !client->common.secured ?
+			   login_settings->disable_plaintext_auth &&
+			   !client->common.secured ?
 			   " LOGINDISABLED" : "", auths, NULL);
 }
 
@@ -423,6 +425,7 @@
 
 void client_destroy_oldest(void)
 {
+	unsigned int max_connections = login_settings->login_max_connections;
 	struct client *client;
 	struct imap_client *destroy_buf[CLIENT_DESTROY_OLDEST_COUNT];
 	unsigned int i, destroy_count;
@@ -465,7 +468,7 @@
 	greet = t_str_new(128);
 	str_append(greet, "* OK ");
 	str_printfa(greet, "[CAPABILITY %s] ", get_capability(client, FALSE));
-	str_append(greet, greeting);
+	str_append(greet, login_settings->login_greeting);
 
 	client_send_line(client, str_c(greet));
 	client->greeting_sent = TRUE;
--- a/src/imap-login/imap-proxy.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap-login/imap-proxy.c	Tue Jan 27 18:21:53 2009 -0500
@@ -201,7 +201,7 @@
 		   be using only Dovecot as their backend :) */
 		client_send_tagline(client, line + 2);
 
-		if (verbose_auth) {
+		if (login_settings->verbose_auth) {
 			str = t_str_new(128);
 			str_printfa(str, "proxy(%s): Login failed to %s:%u",
 				    client->common.virtual_user,
--- a/src/imap/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -4,6 +4,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-dict \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
@@ -21,6 +22,7 @@
 libs = \
 	$(STORAGE_LIBS) \
 	../lib-dict/libdict.a \
+	../lib-settings/libsettings.a \
 	$(unused_objects)
 
 imap_LDADD = \
@@ -72,6 +74,7 @@
 	imap-fetch.c \
 	imap-fetch-body.c \
 	imap-search.c \
+	imap-settings.c \
 	imap-sort.c \
 	imap-status.c \
 	imap-sync.c \
@@ -87,6 +90,7 @@
 	imap-expunge.h \
 	imap-fetch.h \
 	imap-search.h \
+	imap-settings.h \
 	imap-sort.h \
 	imap-status.h \
 	imap-sync.h
--- a/src/imap/client.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/client.c	Tue Jan 27 18:21:53 2009 -0500
@@ -26,7 +26,8 @@
 	client_destroy(client, "Disconnected for inactivity");
 }
 
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user)
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct imap_settings *set)
 {
 	struct client *client;
 	struct mail_namespace *ns;
@@ -36,9 +37,11 @@
 	net_set_nonblock(fd_out, TRUE);
 
 	client = i_new(struct client, 1);
+	client->set = set;
 	client->fd_in = fd_in;
 	client->fd_out = fd_out;
-	client->input = i_stream_create_fd(fd_in, imap_max_line_length, FALSE);
+	client->input = i_stream_create_fd(fd_in,
+					   set->imap_max_line_length, FALSE);
 	client->output = o_stream_create_fd(fd_out, (size_t)-1, FALSE);
 
 	o_stream_set_flush_callback(client->output, client_output, client);
@@ -56,6 +59,11 @@
 					   &mail_storage_callbacks, client);
 	}
 
+	client->capability_string =
+		str_new(default_pool, sizeof(CAPABILITY_STRING)+32);
+	str_append(client->capability_string, *set->imap_capability != '\0' ?
+		   set->imap_capability : CAPABILITY_STRING);
+
 	i_assert(my_client == NULL);
 	my_client = client;
 
@@ -114,7 +122,7 @@
 	tab[1].value = dec2str(client->output->offset);
 
 	str = t_str_new(128);
-	var_expand(str, logout_format, tab);
+	var_expand(str, client->set->imap_logout_format, tab);
 	return str_c(str);
 }
 
@@ -186,6 +194,7 @@
 		array_free(&client->search_saved_uidset);
 	if (array_is_created(&client->search_updates))
 		array_free(&client->search_updates);
+	str_free(&client->capability_string);
 	pool_unref(&client->command_pool);
 	i_free(client);
 
@@ -431,8 +440,9 @@
 		cmd->parser = client->free_parser;
 		client->free_parser = NULL;
 	} else {
-		cmd->parser = imap_parser_create(client->input, client->output,
-						 imap_max_line_length);
+		cmd->parser =
+			imap_parser_create(client->input, client->output,
+					   client->set->imap_max_line_length);
 	}
 
 	DLLIST_PREPEND(&client->command_queue, cmd);
--- a/src/imap/client.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/client.h	Tue Jan 27 18:21:53 2009 -0500
@@ -74,7 +74,11 @@
 	struct ostream *output;
 	struct timeout *to_idle, *to_idle_output;
 
-	struct mail_user *user;
+        const struct imap_settings *set;
+	enum client_workarounds workarounds;
+	string_t *capability_string;
+
+        struct mail_user *user;
 	struct mailbox *mailbox;
         struct mailbox_keywords keywords;
 	unsigned int select_counter; /* increased when mailbox is changed */
@@ -124,7 +128,8 @@
 
 /* Create new client with specified input/output handles. socket specifies
    if the handle is a socket. */
-struct client *client_create(int fd_in, int fd_out, struct mail_user *user);
+struct client *client_create(int fd_in, int fd_out, struct mail_user *user,
+			     const struct imap_settings *set);
 void client_destroy(struct client *client, const char *reason);
 
 /* Disconnect client connection */
--- a/src/imap/cmd-append.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-append.c	Tue Jan 27 18:21:53 2009 -0500
@@ -499,7 +499,7 @@
 	o_stream_unset_flush_callback(client->output);
 
 	ctx->save_parser = imap_parser_create(client->input, client->output,
-					      imap_max_line_length);
+					      client->set->imap_max_line_length);
 
 	cmd->func = cmd_append_continue_parsing;
 	cmd->context = ctx;
--- a/src/imap/cmd-capability.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-capability.c	Tue Jan 27 18:21:53 2009 -0500
@@ -6,9 +6,8 @@
 
 bool cmd_capability(struct client_command_context *cmd)
 {
-	client_send_line(cmd->client,
-			 t_strconcat("* CAPABILITY ",
-				     str_c(capability_string), NULL));
+	client_send_line(cmd->client, t_strconcat(
+		"* CAPABILITY ", str_c(cmd->client->capability_string), NULL));
 
 	client_send_tagline(cmd, "OK Capability completed.");
 	return TRUE;
--- a/src/imap/cmd-delete.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-delete.c	Tue Jan 27 18:21:53 2009 -0500
@@ -36,7 +36,7 @@
 			return TRUE;
 	}
 
-	if ((client_workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
+	if ((client->workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
 	    *name != '\0' &&
 	    name[strlen(name)-1] == mail_storage_get_hierarchy_sep(storage)) {
 		/* drop the extra trailing hierarchy separator */
--- a/src/imap/cmd-id.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-id.c	Tue Jan 27 18:21:53 2009 -0500
@@ -5,6 +5,7 @@
 
 bool cmd_id(struct client_command_context *cmd)
 {
+	const struct imap_settings *set = cmd->client->set;
 	const struct imap_arg *args;
 	const char *value;
 
@@ -13,13 +14,13 @@
 
 	if (!cmd->client->id_logged) {
 		cmd->client->id_logged = TRUE;
-		value = imap_id_args_get_log_reply(args, imap_id_log);
+		value = imap_id_args_get_log_reply(args, set->imap_id_log);
 		if (value != NULL)
 			i_info("ID sent: %s", value);
 	}
 
 	client_send_line(cmd->client, t_strdup_printf(
-		"* ID %s", imap_id_reply_generate(imap_id_send)));
+		"* ID %s", imap_id_reply_generate(set->imap_id_send)));
 	client_send_tagline(cmd, "OK ID completed.");
 	return TRUE;
 }
--- a/src/imap/cmd-idle.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-idle.c	Tue Jan 27 18:21:53 2009 -0500
@@ -4,12 +4,12 @@
 #include "ioloop.h"
 #include "istream.h"
 #include "ostream.h"
+#include "mail-storage-settings.h"
 #include "commands.h"
 #include "imap-sync.h"
 
 #include <stdlib.h>
 
-#define DEFAULT_IDLE_CHECK_INTERVAL 30
 /* Send some noice to client every few minutes to avoid NATs and stateful
    firewalls from closing the connection */
 #define KEEPALIVE_TIMEOUT (2*60)
@@ -197,8 +197,6 @@
 {
 	struct client *client = cmd->client;
 	struct cmd_idle_context *ctx;
-	const char *str;
-	unsigned int interval;
 
 	ctx = p_new(cmd->pool, struct cmd_idle_context, 1);
 	ctx->cmd = cmd;
@@ -207,13 +205,12 @@
 	ctx->keepalive_to = timeout_add(KEEPALIVE_TIMEOUT * 1000,
 					keepalive_timeout, ctx);
 
-	str = getenv("MAILBOX_IDLE_CHECK_INTERVAL");
-	interval = str == NULL ? 0 : (unsigned int)strtoul(str, NULL, 10);
-	if (interval == 0)
-		interval = DEFAULT_IDLE_CHECK_INTERVAL;
+	if (client->mailbox != NULL) {
+		const struct mail_storage_settings *set;
 
-	if (client->mailbox != NULL) {
-		mailbox_notify_changes(client->mailbox, interval,
+		set = mailbox_get_settings(client->mailbox);
+		mailbox_notify_changes(client->mailbox,
+				       set->mailbox_idle_check_interval,
 				       idle_callback, ctx);
 	}
 	client_send_line(client, "+ idling");
--- a/src/imap/cmd-subscribe.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/cmd-subscribe.c	Tue Jan 27 18:21:53 2009 -0500
@@ -60,7 +60,7 @@
 		mailbox += strlen(ns->prefix);
 	}
 
-	if ((client_workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
+	if ((cmd->client->workarounds & WORKAROUND_TB_EXTRA_MAILBOX_SEP) != 0 &&
 	    *mailbox != '\0' && mailbox[strlen(mailbox)-1] ==
 	    mail_storage_get_hierarchy_sep(ns->storage)) {
 		/* verify the validity without the trailing '/' */
--- a/src/imap/common.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/common.h	Tue Jan 27 18:21:53 2009 -0500
@@ -1,9 +1,6 @@
 #ifndef COMMON_H
 #define COMMON_H
 
-#include "lib.h"
-#include "client.h"
-
 /* Disconnect client after idling this many milliseconds */
 #define CLIENT_IDLE_TIMEOUT_MSECS (60*30*1000)
 
@@ -16,24 +13,17 @@
 /* Disconnect client when it sends too many bad commands in a row */
 #define CLIENT_MAX_BAD_COMMANDS 20
 
-/* RFC-2683 recommends at least 8000 bytes. Some clients however don't
-   break large message sets to multiple commands, so we're pretty liberal
-   by default. */
-#define DEFAULT_IMAP_MAX_LINE_LENGTH 65536
-
 enum client_workarounds {
 	WORKAROUND_DELAY_NEWMAIL		= 0x01,
 	WORKAROUND_NETSCAPE_EOH			= 0x04,
 	WORKAROUND_TB_EXTRA_MAILBOX_SEP		= 0x08
 };
 
+#include "lib.h"
+#include "client.h"
+#include "imap-settings.h"
+
 extern struct ioloop *ioloop;
-extern unsigned int imap_max_line_length;
-extern enum client_workarounds client_workarounds;
-extern const char *logout_format;
-extern const char *imap_id_send, *imap_id_log;
-
-extern string_t *capability_string;
 
 extern void (*hook_client_created)(struct client **client);
 
--- a/src/imap/imap-fetch-body.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/imap-fetch-body.c	Tue Jan 27 18:21:53 2009 -0500
@@ -431,7 +431,7 @@
 	i_stream_seek(ctx->cur_input, old_offset);
 
 	if (!ctx->cur_have_eoh &&
-	    (client_workarounds & WORKAROUND_NETSCAPE_EOH) != 0) {
+	    (ctx->client->workarounds & WORKAROUND_NETSCAPE_EOH) != 0) {
 		/* Netscape 4.x doesn't like if end of headers line is
 		   missing. */
 		msg_size.virtual_size += 2;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,102 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "imap-settings.h"
+
+#include <stddef.h>
+#include <stdlib.h>
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct imap_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, offsetof(struct imap_settings, field), defines }
+
+static struct setting_define imap_setting_defines[] = {
+	DEF(SET_BOOL, mail_debug),
+	DEF(SET_BOOL, shutdown_clients),
+	DEF(SET_BOOL, verbose_proctitle),
+
+	DEF(SET_STR, mail_plugins),
+	DEF(SET_STR, mail_plugin_dir),
+	DEF(SET_STR_VARS, mail_log_prefix),
+
+	DEF(SET_UINT, imap_max_line_length),
+	DEF(SET_STR, imap_capability),
+	DEF(SET_STR, imap_client_workarounds),
+	DEF(SET_STR, imap_logout_format),
+	DEF(SET_STR, imap_id_send),
+	DEF(SET_STR, imap_id_log),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct imap_settings imap_default_settings = {
+	MEMBER(mail_debug) FALSE,
+	MEMBER(shutdown_clients) FALSE,
+	MEMBER(verbose_proctitle) FALSE,
+
+	MEMBER(mail_plugins) "",
+	MEMBER(mail_plugin_dir) MODULEDIR"/imap",
+	MEMBER(mail_log_prefix) "%Us(%u): ",
+
+	/* RFC-2683 recommends at least 8000 bytes. Some clients however don't
+	   break large message sets to multiple commands, so we're pretty
+	   liberal by default. */
+	MEMBER(imap_max_line_length) 65536,
+	MEMBER(imap_capability) "",
+	MEMBER(imap_client_workarounds) "outlook-idle",
+	MEMBER(imap_logout_format) "bytes=%i/%o",
+	MEMBER(imap_id_send) "",
+	MEMBER(imap_id_log) ""
+};
+
+struct setting_parser_info imap_setting_parser_info = {
+	MEMBER(defines) imap_setting_defines,
+	MEMBER(defaults) &imap_default_settings,
+
+	MEMBER(parent) NULL,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct imap_settings)
+};
+
+static pool_t settings_pool = NULL;
+
+void imap_settings_read(const struct imap_settings **set_r,
+			const struct mail_user_settings **user_set_r)
+{
+	static const struct setting_parser_info *roots[] = {
+                &imap_setting_parser_info,
+                &mail_user_setting_parser_info
+	};
+	struct setting_parser_context *parser;
+	void **sets;
+
+	if (settings_pool == NULL)
+		settings_pool = pool_alloconly_create("imap settings", 2048);
+	else
+		p_clear(settings_pool);
+
+	mail_storage_namespace_defines_init(settings_pool);
+
+	parser = settings_parser_init_list(settings_pool,
+				roots, N_ELEMENTS(roots),
+				SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+
+	settings_parse_set_expanded(parser, TRUE);
+	if (settings_parse_environ(parser) < 0) {
+		i_fatal("Error reading configuration: %s",
+			settings_parser_get_error(parser));
+	}
+
+	sets = settings_parser_get_list(parser);
+	*set_r = sets[0];
+	*user_set_r = sets[1];
+	settings_parser_deinit(&parser);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/imap/imap-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,27 @@
+#ifndef IMAP_SETTINGS_H
+#define IMAP_SETTINGS_H
+
+struct mail_user_settings;
+
+struct imap_settings {
+	bool mail_debug;
+	bool shutdown_clients;
+	bool verbose_proctitle;
+
+	const char *mail_plugins;
+	const char *mail_plugin_dir;
+	const char *mail_log_prefix;
+
+	/* imap: */
+	unsigned int imap_max_line_length;
+	const char *imap_capability;
+	const char *imap_client_workarounds;
+	const char *imap_logout_format;
+	const char *imap_id_send;
+	const char *imap_id_log;
+};
+
+void imap_settings_read(const struct imap_settings **set_r,
+			const struct mail_user_settings **user_set_r);
+
+#endif
--- a/src/imap/imap-sync.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/imap-sync.c	Tue Jan 27 18:21:53 2009 -0500
@@ -562,7 +562,7 @@
 	get_common_sync_flags(client, &flags, &imap_flags);
 	client->sync_counter++;
 
-	no_newmail = (client_workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 &&
+	no_newmail = (client->workarounds & WORKAROUND_DELAY_NEWMAIL) != 0 &&
 		(imap_flags & IMAP_SYNC_FLAG_SAFE) == 0;
 	if (no_newmail) {
 		/* expunges might break the client just as badly as new mail
--- a/src/imap/main.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/imap/main.c	Tue Jan 27 18:21:53 2009 -0500
@@ -39,10 +39,6 @@
 };
 
 struct ioloop *ioloop;
-unsigned int imap_max_line_length;
-enum client_workarounds client_workarounds = 0;
-const char *logout_format;
-const char *imap_id_send, *imap_id_log;
 
 static struct io *log_io = NULL;
 static struct module *modules = NULL;
@@ -50,8 +46,6 @@
 
 void (*hook_client_created)(struct client **client) = NULL;
 
-string_t *capability_string;
-
 static void sig_die(int signo, void *context ATTR_UNUSED)
 {
 	/* warn about being killed because of some signal, except SIGINT (^C)
@@ -66,16 +60,15 @@
 	io_loop_stop(ioloop);
 }
 
-static void parse_workarounds(void)
+static enum client_workarounds
+parse_workarounds(const struct imap_settings *set)
 {
+        enum client_workarounds client_workarounds = 0;
         struct client_workaround_list *list;
-	const char *env, *const *str;
+	const char *const *str;
 
-	env = getenv("IMAP_CLIENT_WORKAROUNDS");
-	if (env == NULL)
-		return;
-
-	for (str = t_strsplit_spaces(env, " ,"); *str != NULL; str++) {
+        str = t_strsplit_spaces(set->imap_client_workarounds, " ,");
+	for (; *str != NULL; str++) {
 		list = client_workaround_list;
 		for (; list->name != NULL; list++) {
 			if (strcasecmp(*str, list->name) == 0) {
@@ -86,6 +79,8 @@
 		if (list->name == NULL)
 			i_fatal("Unknown client workaround: %s", *str);
 	}
+
+	return client_workarounds;
 }
 
 static void open_logfile(void)
@@ -129,7 +124,8 @@
 	i_set_failure_timestamp_format(getenv("LOGSTAMP"));
 }
 
-static void drop_privileges(void)
+static void main_preinit(const struct imap_settings **set_r,
+			 const struct mail_user_settings **user_set_r)
 {
 	const char *version;
 
@@ -143,25 +139,30 @@
 	/* Log file or syslog opening probably requires roots */
 	open_logfile();
 
-	/* Load the plugins before chrooting. Their init() is called later. */
-	if (getenv("MAIL_PLUGINS") != NULL) {
-		const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+        mail_storage_init();
+	mail_storage_register_all();
+	mailbox_list_register_all();
 
-		if (plugin_dir == NULL)
-			plugin_dir = MODULEDIR"/imap";
-		modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
-					  TRUE, version);
-	}
+	/* read settings after registering storages so they can have their
+	   own setting definitions too */
+	imap_settings_read(set_r, user_set_r);
+
+	/* Load the plugins before chrooting. Their init() is called later. */
+	modules = *(*set_r)->mail_plugins == '\0' ? NULL :
+		module_dir_load((*set_r)->mail_plugin_dir,
+				(*set_r)->mail_plugins, TRUE, version);
 
 	restrict_access_by_env(!IS_STANDALONE());
 }
 
-static void main_init(void)
+static void main_init(const struct imap_settings *set,
+		      const struct mail_user_settings *user_set)
 {
 	struct client *client;
 	struct ostream *output;
 	struct mail_user *user;
-	const char *username, *home, *str, *tag;
+	const char *username, *home, *str, *tag, *error;
+	bool dump_capability;
 
 	lib_signals_init();
         lib_signals_set_handler(SIGINT, TRUE, sig_die, NULL);
@@ -169,6 +170,8 @@
         lib_signals_ignore(SIGPIPE, TRUE);
         lib_signals_ignore(SIGALRM, FALSE);
 
+	dump_capability = getenv("DUMP_CAPABILITY") != NULL;
+
 	username = getenv("USER");
 	if (username == NULL) {
 		if (IS_STANDALONE())
@@ -178,63 +181,40 @@
 	}
 
 	home = getenv("HOME");
-	if (getenv("DEBUG") != NULL) {
+	if (set->mail_debug) {
+		home = getenv("HOME");
 		i_info("Effective uid=%s, gid=%s, home=%s",
 		       dec2str(geteuid()), dec2str(getegid()),
 		       home != NULL ? home : "(none)");
 	}
 
-	if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
+	if (set->shutdown_clients && !dump_capability) {
 		/* If master dies, the log fd gets closed and we'll quit */
 		log_io = io_add(STDERR_FILENO, IO_ERROR,
 				log_error_callback, NULL);
 	}
 
-	capability_string = str_new(default_pool, sizeof(CAPABILITY_STRING)+32);
-	str_append(capability_string, CAPABILITY_STRING);
-
 	dict_drivers_register_builtin();
 	mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL);
-        mail_storage_init();
-	mail_storage_register_all();
-	mailbox_list_register_all();
 	clients_init();
 	commands_init();
 
 	module_dir_init(modules);
 
-	if (getenv("DUMP_CAPABILITY") != NULL) {
-		printf("%s\n", str_c(capability_string));
+	user = mail_user_alloc(username, user_set);
+	mail_user_set_home(user, home);
+	if (mail_user_init(user, &error) < 0)
+		i_fatal("Mail user initialization failed: %s", error);
+	if (mail_namespaces_init(user, &error) < 0)
+		i_fatal("Namespace initialization failed: %s", error);
+	client = client_create(0, 1, user, set);
+        client->workarounds = parse_workarounds(set);
+
+	if (dump_capability) {
+		printf("%s\n", str_c(client->capability_string));
 		exit(0);
 	}
 
-	str = getenv("IMAP_CAPABILITY");
-	if (str != NULL && *str != '\0') {
-		/* Overrides all capabilities */
-		str_truncate(capability_string, 0);
-		str_append(capability_string, str);
-	}
-
-	str = getenv("IMAP_MAX_LINE_LENGTH");
-	imap_max_line_length = str != NULL ?
-		(unsigned int)strtoul(str, NULL, 10) :
-		DEFAULT_IMAP_MAX_LINE_LENGTH;
-
-	logout_format = getenv("IMAP_LOGOUT_FORMAT");
-	if (logout_format == NULL)
-		logout_format = "bytes=%i/%o";
-
-	imap_id_send = getenv("IMAP_ID_SEND");
-	imap_id_log = getenv("IMAP_ID_LOG");
-
-        parse_workarounds();
-
-	user = mail_user_init(username);
-	mail_user_set_home(user, home);
-	if (mail_namespaces_init(user) < 0)
-		i_fatal("Namespace initialization failed");
-	client = client_create(0, 1, user);
-
 	output = client->output;
 	o_stream_ref(output);
 	o_stream_cork(output);
@@ -244,12 +224,12 @@
 	if (tag == NULL) {
 		client_send_line(client, t_strconcat(
 			"* PREAUTH [CAPABILITY ",
-			str_c(capability_string), "] "
+			str_c(client->capability_string), "] "
 			"Logged in as ", user->username, NULL));
 	} else {
 		client_send_line(client, t_strconcat(
 			tag, " OK [CAPABILITY ",
-			str_c(capability_string), "] Logged in", NULL));
+			str_c(client->capability_string), "] Logged in", NULL));
 	}
 	str = getenv("CLIENT_INPUT");
 	if (str != NULL) T_BEGIN {
@@ -277,14 +257,15 @@
 	mail_users_deinit();
 	dict_drivers_unregister_builtin();
 
-	str_free(&capability_string);
-
 	lib_signals_deinit();
 	closelog();
 }
 
 int main(int argc ATTR_UNUSED, char *argv[], char *envp[])
 {
+	const struct imap_settings *set;
+	const struct mail_user_settings *user_set;
+
 #ifdef DEBUG
 	if (!IS_STANDALONE() && getenv("GDB") == NULL)
 		fd_debug_verify_leaks(3, 1024);
@@ -299,7 +280,7 @@
 	/* NOTE: we start rooted, so keep the code minimal until
 	   restrict_access_by_env() is called */
 	lib_init();
-	drop_privileges();
+	main_preinit(&set, &user_set);
 
         process_title_init(argv, envp);
 	ioloop = io_loop_create();
@@ -307,7 +288,7 @@
 	/* fake that we're running, so we know if client was destroyed
 	   while initializing */
 	io_loop_set_running(ioloop);
-	main_init();
+	main_init(set, user_set);
 	if (io_loop_is_running(ioloop))
 		io_loop_run(ioloop);
 	main_deinit();
--- a/src/lib-settings/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-settings/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -4,10 +4,12 @@
 	-I$(top_srcdir)/src/lib
 
 libsettings_a_SOURCES = \
-	settings.c
+	settings.c \
+	settings-parser.c
 
 headers = \
-	settings.h
+	settings.h \
+	settings-parser.h
 
 if INSTALL_HEADERS
   pkginc_libdir=$(pkgincludedir)/src/lib-settings
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-settings/settings-parser.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,903 @@
+/* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "hash.h"
+#include "network.h"
+#include "istream.h"
+#include "str.h"
+#include "strescape.h"
+#include "var-expand.h"
+#include "settings-parser.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
+
+struct setting_link {
+        struct setting_link *parent;
+	const struct setting_parser_info *info;
+
+	ARRAY_TYPE(void_array) *array;
+	void *set_struct;
+};
+
+struct setting_parser_context {
+	pool_t set_pool, parser_pool;
+        enum settings_parser_flags flags;
+	bool str_vars_are_expanded;
+
+	struct setting_link *roots;
+	unsigned int root_count;
+	struct hash_table *links;
+	string_t *save_input_str;
+
+	unsigned int linenum;
+	const char *error;
+	const struct setting_parser_info *prev_info;
+};
+
+static const struct setting_parser_info strlist_info = {
+	MEMBER(defines) NULL,
+	MEMBER(defaults) NULL,
+
+	MEMBER(parent) NULL,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) 0
+};
+
+struct setting_parser_context *
+settings_parser_init(pool_t set_pool, const struct setting_parser_info *root,
+		     enum settings_parser_flags flags)
+{
+        return settings_parser_init_list(set_pool, &root, 1, flags);
+}
+
+static void
+setting_parser_copy_defaults(const struct setting_parser_info *info,
+			     pool_t pool, void *dest)
+{
+	const struct setting_define *def;
+	const char *p, **strp;
+
+	if (info->defaults == NULL)
+		return;
+
+	memcpy(dest, info->defaults, info->struct_size);
+	for (def = info->defines; def->key != NULL; def++) {
+		switch (def->type) {
+		case SET_ENUM: {
+			/* fix enums by dropping everything after the
+			   first ':' */
+			strp = STRUCT_MEMBER_P(dest, def->offset);
+			p = strchr(*strp, ':');
+			if (p != NULL)
+				*strp = p_strdup_until(pool, *strp, p);
+			break;
+		}
+		case SET_STR_VARS: {
+			/* insert the unexpanded-character */
+			strp = STRUCT_MEMBER_P(dest, def->offset);
+			if (*strp != NULL) {
+				*strp = p_strconcat(pool,
+						    SETTING_STRVAR_UNEXPANDED,
+						    *strp, NULL);
+			}
+			break;
+		}
+		default:
+			break;
+		}
+	}
+}
+
+struct setting_parser_context *
+settings_parser_init_list(pool_t set_pool,
+			  const struct setting_parser_info *const *roots,
+			  unsigned int count, enum settings_parser_flags flags)
+{
+	struct setting_parser_context *ctx;
+	unsigned int i;
+	pool_t parser_pool;
+
+	i_assert(count > 0);
+
+	parser_pool = pool_alloconly_create("settings parser", 8192);
+	ctx = p_new(parser_pool, struct setting_parser_context, 1);
+	ctx->set_pool = set_pool;
+	ctx->parser_pool = parser_pool;
+	ctx->flags = flags;
+
+	ctx->root_count = count;
+	ctx->roots = p_new(ctx->set_pool, struct setting_link, count);
+	for (i = 0; i < count; i++) {
+		ctx->roots[i].info = roots[i];
+		ctx->roots[i].set_struct =
+			p_malloc(ctx->set_pool, roots[i]->struct_size);
+		setting_parser_copy_defaults(roots[i], ctx->set_pool,
+					     ctx->roots[i].set_struct);
+	}
+
+	ctx->links = hash_table_create(default_pool, ctx->parser_pool, 0,
+				       str_hash, (hash_cmp_callback_t *)strcmp);
+	pool_ref(ctx->set_pool);
+	return ctx;
+}
+
+void settings_parser_deinit(struct setting_parser_context **_ctx)
+{
+	struct setting_parser_context *ctx = *_ctx;
+
+	*_ctx = NULL;
+	hash_table_destroy(&ctx->links);
+	pool_unref(&ctx->set_pool);
+	pool_unref(&ctx->parser_pool);
+}
+
+void *settings_parser_get(struct setting_parser_context *ctx)
+{
+	i_assert(ctx->root_count == 1);
+
+	return ctx->roots[0].set_struct;
+}
+
+void **settings_parser_get_list(struct setting_parser_context *ctx)
+{
+	unsigned int i;
+	void **sets;
+
+	sets = t_new(void *, ctx->root_count);
+	for (i = 0; i < ctx->root_count; i++)
+		sets[i] = ctx->roots[i].set_struct;
+	return sets;
+}
+
+const char *settings_parser_get_error(struct setting_parser_context *ctx)
+{
+	return ctx->error;
+}
+
+static const struct setting_define *
+setting_define_find(const struct setting_parser_info *info, const char *key)
+{
+	const struct setting_define *list;
+
+	for (list = info->defines; list->key != NULL; list++) {
+		if (strcmp(list->key, key) == 0 && list->type != SET_INTERNAL)
+			return list;
+	}
+	return NULL;
+}
+
+static int
+get_bool(struct setting_parser_context *ctx, const char *value, bool *result_r)
+{
+	/* FIXME: eventually we'd want to support only yes/no */
+	if (strcasecmp(value, "yes") == 0 ||
+	    strcasecmp(value, "y") == 0 || strcmp(value, "1") == 0)
+		*result_r = TRUE;
+	else if (strcasecmp(value, "no") == 0)
+		*result_r = FALSE;
+	else {
+		ctx->error = p_strconcat(ctx->parser_pool, "Invalid boolean: ",
+					 value, NULL);
+		return -1;
+	}
+
+	return 0;
+}
+
+static int
+get_uint(struct setting_parser_context *ctx, const char *value,
+	 unsigned int *result_r)
+{
+	int num;
+
+	/* use %i so we can handle eg. 0600 as octal value with umasks */
+	if (!sscanf(value, "%i", &num) || num < 0) {
+		ctx->error = p_strconcat(ctx->parser_pool, "Invalid number: ",
+					 value, NULL);
+		return -1;
+	}
+	*result_r = num;
+	return 0;
+}
+
+static int get_enum(struct setting_parser_context *ctx, const char *value,
+		    char **result_r, const char *allowed_values)
+{
+	const char *p;
+
+	while (allowed_values != NULL) {
+		p = strchr(allowed_values, ':');
+		if (p == NULL) {
+			if (strcmp(allowed_values, value) == 0)
+				break;
+
+			ctx->error = p_strconcat(ctx->parser_pool,
+						 "Invalid value: ",
+						 value, NULL);
+			return -1;
+		}
+
+		if (strncmp(allowed_values, value, p - allowed_values) == 0 &&
+		    value[p - allowed_values] == '\0')
+			break;
+
+		allowed_values = p + 1;
+	}
+
+	*result_r = p_strdup(ctx->set_pool, value);
+	return 0;
+}
+
+static int
+get_deflist(struct setting_parser_context *ctx, struct setting_link *parent,
+	    const struct setting_parser_info *info,
+	    const char *key, const char *value, ARRAY_TYPE(void_array) *result)
+{
+	struct setting_link *link;
+	const char *const *list;
+	char *full_key;
+
+	i_assert(info->defines != NULL || info == &strlist_info);
+
+	if (!array_is_created(result))
+		p_array_init(result, ctx->set_pool, 5);
+
+	list = t_strsplit(value, "\t ");
+	for (; *list != NULL; list++) {
+		if (**list == '\0')
+			continue;
+
+		full_key = p_strconcat(ctx->parser_pool, key,
+				       SETTINGS_SEPARATOR_S, *list, NULL);
+		if (hash_table_lookup(ctx->links, full_key) != NULL) {
+			ctx->error = p_strconcat(ctx->parser_pool, full_key,
+						 " already exists", NULL);
+			return -1;
+		}
+
+		link = p_new(ctx->parser_pool, struct setting_link, 1);
+		link->parent = parent;
+		link->info = info;
+		link->array = result;
+		hash_table_insert(ctx->links, full_key, link);
+	}
+	return 0;
+}
+
+static int
+settings_parse(struct setting_parser_context *ctx, struct setting_link *link,
+	       const struct setting_define *def,
+	       const char *key, const char *value)
+{
+        void *ptr, *ptr2;
+
+	ctx->prev_info = link->info;
+
+	if (link->set_struct == NULL) {
+		link->set_struct =
+			p_malloc(ctx->set_pool, link->info->struct_size);
+		setting_parser_copy_defaults(link->info, ctx->set_pool,
+					     link->set_struct);
+		array_append(link->array, &link->set_struct, 1);
+
+		if (link->info->parent_offset != (size_t)-1 &&
+		    link->parent != NULL) {
+			ptr = STRUCT_MEMBER_P(link->set_struct,
+					      link->info->parent_offset);
+			*((void **)ptr) = link->parent->set_struct;
+		}
+	}
+
+	ptr = STRUCT_MEMBER_P(link->set_struct, def->offset);
+	switch (def->type) {
+	case SET_INTERNAL:
+		i_unreached();
+	case SET_BOOL:
+		return get_bool(ctx, value, (bool *)ptr);
+	case SET_UINT:
+		return get_uint(ctx, value, (unsigned int *)ptr);
+	case SET_STR:
+		*((char **)ptr) = p_strdup(ctx->set_pool, value);
+		return 0;
+	case SET_STR_VARS:
+		*((char **)ptr) = p_strconcat(ctx->set_pool,
+					      ctx->str_vars_are_expanded ?
+					      SETTING_STRVAR_EXPANDED :
+					      SETTING_STRVAR_UNEXPANDED,
+					      value, NULL);
+		return 0;
+	case SET_ENUM:
+		/* get the available values from default string */
+		i_assert(link->info->defaults != NULL);
+		ptr2 = STRUCT_MEMBER_P(link->info->defaults, def->offset);
+		return get_enum(ctx, value, (char **)ptr, *(const char **)ptr2);
+	case SET_DEFLIST:
+		ctx->prev_info = def->list_info;
+		return get_deflist(ctx, link, def->list_info,
+				   key, value, (ARRAY_TYPE(void_array) *)ptr);
+	case SET_STRLIST: {
+		ctx->prev_info = &strlist_info;
+		return get_deflist(ctx, link, &strlist_info,
+				   key, value, (ARRAY_TYPE(void_array) *)ptr);
+	}
+	}
+
+	i_unreached();
+	return -1;
+}
+
+static int settings_parse_keyvalue(struct setting_parser_context *ctx,
+				   const char *key, const char *value)
+{
+	const struct setting_define *def = NULL;
+	unsigned int i;
+	int ret = 1;
+
+	/* try to find from roots */
+	for (i = 0; i < ctx->root_count; i++) {
+		def = setting_define_find(ctx->roots[i].info, key);
+		if (def != NULL)
+			break;
+	}
+	if (def != NULL) {
+		if (settings_parse(ctx, &ctx->roots[i], def, key, value) < 0)
+			ret = -1;
+	} else {
+		/* try to find from links */
+		const char *end = strrchr(key, SETTINGS_SEPARATOR);
+		struct setting_link *link;
+
+		link = end == NULL ? NULL :
+			hash_table_lookup(ctx->links, t_strdup_until(key, end));
+		if (link == NULL)
+			def = NULL;
+		else if (link->info == &strlist_info) {
+			void *vkey, *vvalue;
+
+			vkey = p_strdup(ctx->set_pool, end + 1);
+			vvalue = p_strdup(ctx->set_pool, value);
+			array_append(link->array, &vkey, 1);
+			array_append(link->array, &vvalue, 1);
+			return 1;
+		} else {
+			def = setting_define_find(link->info, end + 1);
+		}
+		if (def != NULL) {
+			if (settings_parse(ctx, link, def, key, value) < 0)
+				ret = -1;
+		} else {
+			ctx->error = p_strconcat(ctx->parser_pool,
+				"Unknown setting: ", key, NULL);
+			ret = 0;
+		}
+	}
+	return ret;
+}
+
+int settings_parse_line(struct setting_parser_context *ctx, const char *line)
+{
+	const char *key, *value;
+	int ret;
+
+	ctx->error = NULL;
+	ctx->prev_info = NULL;
+
+	key = line;
+	value = strchr(line, '=');
+	if (value == NULL) {
+		ctx->error = "Missing '='";
+		return -1;
+	}
+
+	if (key == value) {
+		ctx->error = "Missing key name ('=' at the beginning of line)";
+		return -1;
+	}
+
+	T_BEGIN {
+		key = t_strdup_until(key, value);
+		ret = settings_parse_keyvalue(ctx, key, value + 1);
+	} T_END;
+	return ret;
+}
+
+const struct setting_parser_info *
+settings_parse_get_prev_info(struct setting_parser_context *ctx)
+{
+	return ctx->prev_info;
+}
+
+int settings_parse_stream(struct setting_parser_context *ctx,
+			  struct istream *input)
+{
+	const char *line;
+	string_t *full_line;
+	size_t len;
+	int ret = 1;
+
+	full_line = str_new(default_pool, 512);
+	while ((line = i_stream_next_line(input)) != NULL) {
+		if (*line == '\0') {
+			/* empty line finishes it */
+			ret = 0;
+			break;
+		}
+
+		ctx->linenum++;
+		while (IS_WHITE(*line == ' '))
+			line++;
+		if (*line == '\0' || *line == '#')
+			continue;
+
+		len = strlen(line);
+		while (len > 0 && IS_WHITE(line[len-1]))
+			len--;
+		if (line[len] == '\\') {
+			/* line continues */
+			str_append_n(full_line, line, len - 1);
+		} else {
+			/* full line */
+			if (str_len(full_line) > 0) {
+				str_append_n(full_line, line, len);
+				line = str_c(full_line);
+			}
+
+			ret = settings_parse_line(ctx, line);
+			if (ret == 0 &&
+			    (ctx->flags &
+			     SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS) == 0)
+				ret = -1;
+
+			if (ret < 0) {
+				ctx->error = p_strdup_printf(ctx->parser_pool,
+							     "Line %u: %s",
+							     ctx->linenum,
+							     ctx->error);
+				ret = -1;
+				break;
+			}
+			str_truncate(full_line, 0);
+		}
+	}
+	str_free(&full_line);
+	return ret;
+}
+
+int settings_parse_stream_read(struct setting_parser_context *ctx,
+			       struct istream *input)
+{
+	const unsigned char *data;
+	size_t size;
+	int ret;
+
+	while ((ret = i_stream_read(input)) > 0) {
+		if (ctx->save_input_str != NULL) {
+			data = i_stream_get_data(input, &size);
+			str_append_n(ctx->save_input_str, data, size);
+		}
+		if ((ret = settings_parse_stream(ctx, input)) < 0)
+			return -1;
+		if (ret == 0) {
+			/* empty line read */
+			return 0;
+		}
+	}
+
+	switch (ret) {
+	case -1:
+		if (input->stream_errno != 0) {
+			ctx->error = p_strdup_printf(ctx->parser_pool,
+						     "read() failed: %m");
+		} else {
+			ctx->error = "input is missing end-of-settings line";
+		}
+		break;
+	case -2:
+		ctx->error = p_strdup_printf(ctx->parser_pool,
+					     "Line %u: line too long",
+					     ctx->linenum);
+		break;
+	case 0:
+		/* blocks */
+		return 1;
+	default:
+		i_unreached();
+	}
+	return -1;
+}
+
+int settings_parse_file(struct setting_parser_context *ctx,
+			const char *path, size_t max_line_length)
+{
+	struct istream *input;
+	int fd, ret;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0) {
+		ctx->error = p_strdup_printf(ctx->parser_pool,
+					     "open(%s) failed: %m", path);
+		return -1;
+	}
+
+	input = i_stream_create_fd(fd, max_line_length, TRUE);
+	ret = settings_parse_stream_read(ctx, input);
+	i_stream_unref(&input);
+
+	return ret;
+}
+
+int settings_parse_environ(struct setting_parser_context *ctx)
+{
+	extern char **environ;
+	const char *key, *value;
+	unsigned int i;
+	int ret = 0;
+
+	for (i = 0; environ[i] != NULL && ret == 0; i++) {
+		value = strchr(environ[i], '=');
+		if (value != NULL) T_BEGIN {
+			key = t_strdup_until(environ[i], value++);
+			key = t_str_lcase(key);
+			if (settings_parse_keyvalue(ctx, key, value) < 0) {
+				ctx->error = p_strdup_printf(ctx->parser_pool,
+					"Invalid setting %s: %s",
+					key, ctx->error);
+				ret = -1;
+			}
+		} T_END;
+	}
+	return ret;
+}
+
+int settings_parse_exec(struct setting_parser_context *ctx,
+			const char *bin_path, const char *config_path,
+			const char *service)
+{
+	struct istream *input;
+	pid_t pid;
+	int ret, fd[2], status;
+
+	if (pipe(fd) < 0) {
+		i_error("pipe() failed: %m");
+		return -1;
+	}
+
+	pid = fork();
+	if (pid == (pid_t)-1) {
+		i_error("fork() failed: %m");
+		(void)close(fd[0]);
+		(void)close(fd[1]);
+		return -1;
+	}
+	if (pid == 0) {
+		/* child */
+		static const char *argv[] = {
+			NULL,
+			"-c", NULL,
+			"-s", NULL,
+			NULL
+		};
+		argv[0] = bin_path;
+		argv[2] = config_path;
+		argv[4] = service;
+		(void)close(fd[0]);
+		if (dup2(fd[1], STDOUT_FILENO) < 0)
+			i_fatal("dup2() failed: %m");
+
+		execv(argv[0], (void *)argv);
+		i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", bin_path);
+		return -1;
+	}
+	(void)close(fd[1]);
+
+	input = i_stream_create_fd(fd[0], (size_t)-1, TRUE);
+	ret = settings_parse_stream_read(ctx, input);
+	i_stream_destroy(&input);
+
+	if (waitpid(pid, &status, 0) < 0) {
+		i_error("waitpid() failed: %m");
+		ret = -1;
+	} else if (status != 0) {
+		i_error("%s returned failure: %d", bin_path, status);
+		ret = -1;
+	}
+	return ret;
+}
+
+void settings_parse_set_expanded(struct setting_parser_context *ctx,
+				 bool is_expanded)
+{
+	ctx->str_vars_are_expanded = is_expanded;
+}
+
+static void
+settings_var_expand_info(const struct setting_parser_info *info,
+			 pool_t pool, void *set,
+			 const struct var_expand_table *table, string_t *str)
+{
+	const struct setting_define *def;
+	void *value, *const *children;
+	unsigned int i, count;
+
+	for (def = info->defines; def->key != NULL; def++) {
+		value = PTR_OFFSET(set, def->offset);
+		switch (def->type) {
+		case SET_STR_VARS: {
+			const char **val = value;
+
+			if (*val == NULL)
+				break;
+
+			if (**val == SETTING_STRVAR_UNEXPANDED[0]) {
+				str_truncate(str, 0);
+				var_expand(str, *val + 1, table);
+				*val = p_strdup(pool, str_c(str));
+			} else {
+				i_assert(**val == SETTING_STRVAR_EXPANDED[0]);
+				*val += 1;
+			}
+			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++) {
+				settings_var_expand_info(def->list_info,
+							 pool, children[i],
+							 table, str);
+			}
+			break;
+		}
+		default:
+			break;
+		}
+	}
+}
+
+void settings_var_expand(const struct setting_parser_info *info,
+			 void *set, pool_t pool,
+			 const struct var_expand_table *table)
+{
+	string_t *str;
+
+	T_BEGIN {
+		str = t_str_new(256);
+		settings_var_expand_info(info, pool, set, table, str);
+	} T_END;
+}
+
+bool settings_vars_have_key(const struct setting_parser_info *info, void *set,
+			    char var_key, const char *long_var_key,
+			    const char **key_r, const char **value_r)
+{
+	const struct setting_define *def;
+	const void *value;
+	void *const *children;
+	unsigned int i, count;
+
+	for (def = info->defines; def->key != NULL; def++) {
+		value = CONST_PTR_OFFSET(set, def->offset);
+		switch (def->type) {
+		case SET_STR_VARS: {
+			const char *const *val = value;
+
+			if (*val == NULL)
+				break;
+
+			if (**val == SETTING_STRVAR_UNEXPANDED[0]) {
+				if (var_has_key(*val + 1, var_key,
+						long_var_key)) {
+					*key_r = def->key;
+					*value_r = *val + 1;
+					return TRUE;
+				}
+			} else {
+				i_assert(**val == SETTING_STRVAR_EXPANDED[0]);
+			}
+			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 (settings_vars_have_key(def->list_info,
+							   children[i], var_key,
+							   long_var_key,
+							   key_r, value_r))
+					return TRUE;
+			}
+			break;
+		}
+		default:
+			break;
+		}
+	}
+	return FALSE;
+}
+
+void *settings_dup(const struct setting_parser_info *info,
+		   const void *set, pool_t pool)
+{
+	const struct setting_define *def;
+	const void *src;
+	void *dest_set, *dest, *const *children;
+	unsigned int i, count;
+
+	dest_set = p_malloc(pool, info->struct_size);
+	memcpy(dest_set, set, info->struct_size);
+	for (def = info->defines; def->key != NULL; def++) {
+		src = CONST_PTR_OFFSET(set, def->offset);
+		dest = PTR_OFFSET(dest_set, def->offset);
+
+		switch (def->type) {
+		case SET_INTERNAL:
+		case SET_BOOL:
+		case SET_UINT:
+			break;
+		case SET_STR_VARS:
+		case SET_STR:
+		case SET_ENUM: {
+			const char *const *src_str = src;
+			const char **dest_str = dest;
+
+			*dest_str = p_strdup(pool, *src_str);
+			break;
+		}
+		case SET_DEFLIST: {
+			const ARRAY_TYPE(void_array) *src_arr = src;
+			ARRAY_TYPE(void_array) *dest_arr = dest;
+			void *child_set;
+
+			if (!array_is_created(src_arr))
+				break;
+
+			children = array_get(src_arr, &count);
+			p_array_init(dest_arr, pool, count);
+			for (i = 0; i < count; i++) {
+				child_set = settings_dup(def->list_info,
+							 children[i], pool);
+				array_append(dest_arr, &child_set, 1);
+			}
+			break;
+		}
+		case SET_STRLIST: {
+			const ARRAY_TYPE(const_string) *src_arr = src;
+			ARRAY_TYPE(const_string) *dest_arr = dest;
+			const char *const *strings, *dup;
+
+			if (!array_is_created(src_arr))
+				break;
+
+			strings = array_get(src_arr, &count);
+			p_array_init(dest_arr, pool, count);
+			for (i = 0; i < count; i += 2)
+				dup = p_strdup(pool, strings[i]);
+			break;
+		}
+		}
+	}
+	return dest_set;
+}
+
+void settings_parse_save_input(struct setting_parser_context *ctx,
+			       string_t *dest)
+{
+	ctx->save_input_str = dest;
+}
+
+static void
+info_update_real(pool_t pool, struct setting_parser_info *parent,
+		 const struct dynamic_settings_parser *parsers)
+{
+	/* @UNSAFE */
+	ARRAY_DEFINE(defines, struct setting_define);
+	ARRAY_TYPE(dynamic_settings_parser) dynamic_parsers;
+	struct dynamic_settings_parser new_parser;
+	const struct setting_define *cur_defines;
+	struct setting_define *new_defines, new_define;
+	void *parent_defaults;
+	unsigned int i, j;
+	size_t offset, new_struct_size;
+
+	t_array_init(&defines, 128);
+	/* add existing defines */
+	for (j = 0; parent->defines[j].key != NULL; j++)
+		array_append(&defines, &parent->defines[j], 1);
+	new_struct_size = parent->struct_size;
+
+	/* add new dynamic defines */
+	for (i = 0; parsers[i].name != NULL; i++) {
+		i_assert(parsers[i].info->parent == parent);
+		cur_defines = parsers[i].info->defines;
+		for (j = 0; cur_defines[j].key != NULL; j++) {
+			new_define = cur_defines[j];
+			new_define.offset += new_struct_size;
+			array_append(&defines, &new_define, 1);
+		}
+		new_struct_size += MEM_ALIGN(parsers[i].info->struct_size);
+	}
+	new_defines = p_new(pool, struct setting_define,
+			    array_count(&defines) + 1);
+	memcpy(new_defines, array_idx(&defines, 0),
+	       sizeof(*parent->defines) * array_count(&defines));
+	parent->defines = new_defines;
+
+	/* update defaults */
+	parent_defaults = p_malloc(pool, new_struct_size);
+	memcpy(parent_defaults, parent->defaults, parent->struct_size);
+	offset = parent->struct_size;
+	for (i = 0; parsers[i].name != NULL; i++) {
+		memcpy(PTR_OFFSET(parent_defaults, offset),
+		       parsers[i].info->defaults, parsers[i].info->struct_size);
+		offset += MEM_ALIGN(parsers[i].info->struct_size);
+	}
+	parent->defaults = parent_defaults;
+
+	/* update dynamic parsers list */
+	t_array_init(&dynamic_parsers, 32);
+	if (parent->dynamic_parsers != NULL) {
+		for (i = 0; parent->dynamic_parsers[i].name != NULL; i++) {
+			array_append(&dynamic_parsers,
+				     &parent->dynamic_parsers[i], 1);
+		}
+	}
+	offset = parent->struct_size;
+	for (i = 0; parsers[i].name != NULL; i++) {
+		new_parser = parsers[i];
+		new_parser.name = p_strdup(pool, new_parser.name);
+		new_parser.struct_offset = offset;
+		array_append(&dynamic_parsers, &new_parser, 1);
+		offset += MEM_ALIGN(parsers[i].info->struct_size);
+	}
+	parent->dynamic_parsers =
+		p_new(pool, struct dynamic_settings_parser,
+		      array_count(&dynamic_parsers) + 1);
+	memcpy(parent->dynamic_parsers, array_idx(&dynamic_parsers, 0),
+	       sizeof(*parent->dynamic_parsers) *
+	       array_count(&dynamic_parsers));
+	parent->struct_size = new_struct_size;
+}
+
+void
+settings_parser_info_update(pool_t pool, struct setting_parser_info *parent,
+			    const struct dynamic_settings_parser *parsers)
+{
+	T_BEGIN {
+		info_update_real(pool, parent, parsers);
+	} T_END;
+}
+
+const void *settings_find_dynamic(struct setting_parser_info *info,
+				  const void *base_set, const char *name)
+{
+	unsigned int i;
+
+	if (info->dynamic_parsers == NULL)
+		return NULL;
+
+	for (i = 0; info->dynamic_parsers[i].name != NULL; i++) {
+		if (strcmp(info->dynamic_parsers[i].name, name) == 0) {
+			return CONST_PTR_OFFSET(base_set,
+				info->dynamic_parsers[i].struct_offset);
+		}
+	}
+	return NULL;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-settings/settings-parser.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,147 @@
+#ifndef SETTINGS_PARSER_H
+#define SETTINGS_PARSER_H
+
+struct var_expand_table;
+
+#define SETTINGS_SEPARATOR '/'
+#define SETTINGS_SEPARATOR_S "/"
+
+/* STR_VARS pointer begins with either of these initially. Before actually
+   using the variables all variables in all unexpanded strings need to be
+   expanded. Afterwards the string pointers should be increased to skip
+   the initial '1' so it'll be easy to use them. */
+#define SETTING_STRVAR_UNEXPANDED "0"
+#define SETTING_STRVAR_EXPANDED "1"
+
+enum setting_type {
+	SET_INTERNAL, /* don't set this variable */
+	SET_BOOL,
+	SET_UINT,
+	SET_STR,
+	SET_STR_VARS, /* string with %variables */
+	SET_ENUM,
+	SET_DEFLIST, /* of type array_t */
+	SET_STRLIST /* of type ARRAY_TYPE(const_string) */
+};
+
+#define SETTING_DEFINE_LIST_END { 0, NULL, 0, NULL }
+
+struct setting_define {
+	enum setting_type type;
+	const char *key;
+
+	size_t offset;
+	const struct setting_parser_info *list_info;
+};
+
+#define SETTING_DEFINE_STRUCT_BOOL(name, struct_name) \
+	{ SET_BOOL + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+		((struct struct_name *)0)->name, bool), \
+	  #name, offsetof(struct struct_name, name), NULL }
+#define SETTING_DEFINE_STRUCT_UINT(name, struct_name) \
+	{ SET_UINT + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+		((struct struct_name *)0)->name, unsigned int), \
+	  #name, offsetof(struct struct_name, name), NULL }
+#define SETTING_DEFINE_STRUCT_STR(name, struct_name) \
+	{ SET_STR + COMPILE_ERROR_IF_TYPES_NOT_COMPATIBLE( \
+		((struct struct_name *)0)->name, const char *), \
+	  #name, offsetof(struct struct_name, name), NULL }
+
+struct setting_parser_info {
+	const struct setting_define *defines;
+	const void *defaults;
+
+	struct setting_parser_info *parent;
+	struct dynamic_settings_parser *dynamic_parsers;
+
+	size_t parent_offset;
+	size_t type_offset;
+	size_t struct_size;
+};
+ARRAY_DEFINE_TYPE(setting_parser_info, struct setting_parser_info);
+
+/* name=NULL-terminated list of parsers. These follow the static settings.
+   After this list follows the actual settings. */
+struct dynamic_settings_parser {
+	const char *name;
+	const struct setting_parser_info *info;
+	size_t struct_offset;
+};
+ARRAY_DEFINE_TYPE(dynamic_settings_parser, struct dynamic_settings_parser);
+
+enum settings_parser_flags {
+	SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS	= 0x01
+};
+
+struct setting_parser_context;
+
+struct setting_parser_context *
+settings_parser_init(pool_t set_pool, const struct setting_parser_info *root,
+		     enum settings_parser_flags flags);
+struct setting_parser_context *
+settings_parser_init_list(pool_t set_pool,
+			  const struct setting_parser_info *const *roots,
+			  unsigned int count, enum settings_parser_flags flags);
+void settings_parser_deinit(struct setting_parser_context **ctx);
+
+/* Return pointer to root setting structure. */
+void *settings_parser_get(struct setting_parser_context *ctx);
+/* If there are multiple roots, return list to all of their settings. */
+void **settings_parser_get_list(struct setting_parser_context *ctx);
+
+/* Return the last error. */
+const char *settings_parser_get_error(struct setting_parser_context *ctx);
+/* Return the parser info used for the previously parsed line. */
+const struct setting_parser_info *
+settings_parse_get_prev_info(struct setting_parser_context *ctx);
+/* Save all parsed input to given string. */
+void settings_parse_save_input(struct setting_parser_context *ctx,
+			       string_t *dest);
+
+/* Parse a single line. Returns 1 if OK, 0 if key is unknown, -1 if error. */
+int settings_parse_line(struct setting_parser_context *ctx, const char *line);
+/* Parse data already read in input stream. */
+int settings_parse_stream(struct setting_parser_context *ctx,
+			  struct istream *input);
+/* Read data from input stream and parser it. returns -1 = error,
+   0 = eof/stream error, 1 = not finished yet (stream is non-blocking) */
+int settings_parse_stream_read(struct setting_parser_context *ctx,
+         		       struct istream *input);
+/* Open file and parse it. */
+int settings_parse_file(struct setting_parser_context *ctx,
+			const char *path, size_t max_line_length);
+int settings_parse_environ(struct setting_parser_context *ctx);
+/* Execute the given binary and wait for it to return the configuration. */
+int settings_parse_exec(struct setting_parser_context *ctx,
+			const char *bin_path, const char *config_path,
+			const char *service);
+
+/* While parsing values, specifies if STR_VARS strings are already expanded. */
+void settings_parse_set_expanded(struct setting_parser_context *ctx,
+				 bool is_expanded);
+/* Expand all unexpanded variables using the given table. Update the string
+   pointers so that they can be used without skipping over the '1'. */
+void settings_var_expand(const struct setting_parser_info *info,
+			 void *set, pool_t pool,
+			 const struct var_expand_table *table);
+/* Go through all the settings and return the first one that has an unexpanded
+   setting containing the given %key. */
+bool settings_vars_have_key(const struct setting_parser_info *info, void *set,
+			    char var_key, const char *long_var_key,
+			    const char **key_r, const char **value_r);
+/* Duplicate the entire settings structure. */
+void *settings_dup(const struct setting_parser_info *info,
+		   const void *set, pool_t pool);
+/* parsers is a name=NULL -terminated list. The parsers are appended as
+   dynamic_settings_list structures to parent. The new structures are allocated
+   from the given pool. */
+void
+settings_parser_info_update(pool_t pool, struct setting_parser_info *parent,
+			    const struct dynamic_settings_parser *parsers);
+
+/* Return pointer to beginning of settings for given name, or NULL if there is
+   no such registered name. */
+const void *settings_find_dynamic(struct setting_parser_info *info,
+				  const void *base_set, const char *name);
+
+#endif
--- a/src/lib-storage/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -5,6 +5,7 @@
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
 	-I$(top_srcdir)/src/lib-auth \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
@@ -18,6 +19,7 @@
 	mail-search.c \
 	mail-search-build.c \
 	mail-storage.c \
+	mail-storage-settings.c \
 	mail-user.c \
 	mailbox-list.c \
 	mailbox-search-result.c \
@@ -33,6 +35,7 @@
 	mail-thread.h \
 	mail-storage.h \
 	mail-storage-private.h \
+	mail-storage-settings.h \
 	mail-user.h \
 	mailbox-list.h \
 	mailbox-list-private.h \
--- a/src/lib-storage/index/cydir/cydir-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/cydir/cydir-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -37,7 +37,7 @@
 			const char *data, struct mail_storage *storage,
 			const char **layout_r, const char **error_r)
 {
-	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 
 	*layout_r = "fs";
 
@@ -125,8 +125,7 @@
 				storage, &storage->list_module_ctx);
 
 	/* finish list init after we've overridden vfuncs */
-	mailbox_list_init(_storage->list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(_storage->flags));
+	mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
 	return 0;
 }
 
@@ -409,6 +408,7 @@
 	MEMBER(mailbox_is_file) FALSE,
 
 	{
+		NULL,
 		cydir_class_init,
 		cydir_class_deinit,
 		cydir_alloc,
--- a/src/lib-storage/index/dbox/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/dbox/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -2,6 +2,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
@@ -14,6 +15,7 @@
 	dbox-index.c \
 	dbox-mail.c \
 	dbox-save.c \
+	dbox-settings.c \
 	dbox-sync.c \
 	dbox-sync-file.c \
 	dbox-sync-rebuild.c \
@@ -24,6 +26,7 @@
 	dbox-file.h \
 	dbox-file-maildir.h \
 	dbox-index.h \
+	dbox-settings.h \
 	dbox-storage.h \
 	dbox-sync.h
 
--- a/src/lib-storage/index/dbox/dbox-file.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/dbox/dbox-file.c	Tue Jan 27 18:21:53 2009 -0500
@@ -154,6 +154,7 @@
 struct dbox_file *
 dbox_file_init(struct dbox_mailbox *mbox, unsigned int file_id)
 {
+	const struct dbox_settings *set = mbox->storage->set;
 	struct dbox_file *file;
 	unsigned int count;
 	bool maildir;
@@ -166,8 +167,8 @@
 	}
 
 	count = array_count(&mbox->open_files);
-	if (count > mbox->max_open_files)
-		dbox_close_open_files(mbox, count - mbox->max_open_files);
+	if (count > set->dbox_max_open_files)
+		dbox_close_open_files(mbox, count - set->dbox_max_open_files);
 
 	file = i_new(struct dbox_file, 1);
 	file->refcount = 1;
@@ -254,7 +255,8 @@
 
 	if (file->file_id != 0) {
 		files = array_get(&file->mbox->open_files, &count);
-		if (!file->deleted && count <= file->mbox->max_open_files) {
+		if (!file->deleted &&
+		    count <= file->mbox->storage->set->dbox_max_open_files) {
 			/* we can leave this file open for now */
 			return;
 		}
@@ -292,6 +294,8 @@
 
 bool dbox_file_can_append(struct dbox_file *file, uoff_t mail_size)
 {
+	const struct dbox_settings *set = file->mbox->storage->set;
+
 	if (file->nonappendable)
 		return FALSE;
 
@@ -300,12 +304,12 @@
 		return FALSE;
 	}
 
-	if (file->append_offset < file->mbox->rotate_min_size ||
+	if (file->append_offset < set->dbox_rotate_min_size ||
 	    file->append_offset == file->file_header_size)
 		return TRUE;
-	if (file->append_offset + mail_size >= file->mbox->rotate_size)
+	if (file->append_offset + mail_size >= set->dbox_rotate_size)
 		return FALSE;
-	return file->create_time >= day_begin_stamp(file->mbox->rotate_days);
+	return file->create_time >= day_begin_stamp(set->dbox_rotate_days);
 }
 
 static int dbox_file_parse_header(struct dbox_file *file, const char *line)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/dbox/dbox-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,45 @@
+/* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "dbox-settings.h"
+
+#include <stddef.h>
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct dbox_settings, name), NULL }
+
+static struct setting_define dbox_setting_defines[] = {
+	DEF(SET_UINT, dbox_rotate_size),
+	DEF(SET_UINT, dbox_rotate_min_size),
+	DEF(SET_UINT, dbox_rotate_days),
+	DEF(SET_UINT, dbox_max_open_files),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct dbox_settings dbox_default_settings = {
+	MEMBER(dbox_rotate_size) 2048*1024,
+	MEMBER(dbox_rotate_min_size) 16*1024,
+	MEMBER(dbox_rotate_days) 0,
+	MEMBER(dbox_max_open_files) 64
+};
+
+static struct setting_parser_info dbox_setting_parser_info = {
+	MEMBER(defines) dbox_setting_defines,
+	MEMBER(defaults) &dbox_default_settings,
+
+	MEMBER(parent) &mail_user_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct dbox_settings)
+};
+
+const struct setting_parser_info *dbox_get_setting_parser_info(void)
+{
+	return &dbox_setting_parser_info;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/dbox/dbox-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,13 @@
+#ifndef DBOX_SETTINGS_H
+#define DBOX_SETTINGS_H
+
+struct dbox_settings {
+	unsigned int dbox_rotate_size;
+	unsigned int dbox_rotate_min_size;
+	unsigned int dbox_rotate_days;
+	unsigned int dbox_max_open_files;
+};
+
+const struct setting_parser_info *dbox_get_setting_parser_info(void);
+
+#endif
--- a/src/lib-storage/index/dbox/dbox-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/dbox/dbox-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -50,7 +50,7 @@
 		       const char **layout_r, const char **alt_dir_r,
 		       const char **error_r)
 {
-	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 
 	*layout_r = "fs";
 
@@ -132,6 +132,8 @@
 		return -1;
 	}
 
+	storage->set = mail_storage_get_driver_settings(_storage);
+
 	if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
 		return -1;
 	storage->list_module_ctx.super = _storage->list->v;
@@ -145,8 +147,7 @@
 				storage, &storage->list_module_ctx);
 
 	/* finish list init after we've overridden vfuncs */
-	mailbox_list_init(_storage->list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(_storage->flags));
+	mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
 	return 0;
 }
 
@@ -194,7 +195,7 @@
 	struct mail_storage *_storage = &storage->storage;
 	struct dbox_mailbox *mbox;
 	struct mail_index *index;
-	const char *path, *value;
+	const char *path;
 	pool_t pool;
 
 	path = mailbox_list_get_path(_storage->list, name,
@@ -216,31 +217,8 @@
 	mbox->storage = storage;
 	mbox->last_interactive_change = ioloop_time;
 
-	value = getenv("DBOX_ROTATE_SIZE");
-	if (value != NULL)
-		mbox->rotate_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
-	else
-		mbox->rotate_size = DBOX_DEFAULT_ROTATE_SIZE;
-	mbox->rotate_size = 0; /* FIXME: currently anything else doesn't work */
-	value = getenv("DBOX_ROTATE_MIN_SIZE");
-	if (value != NULL)
-		mbox->rotate_min_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
-	else
-		mbox->rotate_min_size = DBOX_DEFAULT_ROTATE_MIN_SIZE;
-	if (mbox->rotate_min_size > mbox->rotate_size)
-		mbox->rotate_min_size = mbox->rotate_size;
-	value = getenv("DBOX_ROTATE_DAYS");
-	if (value != NULL)
-		mbox->rotate_days = (unsigned int)strtoul(value, NULL, 10);
-	else
-		mbox->rotate_days = DBOX_DEFAULT_ROTATE_DAYS;
-
-	value = getenv("DBOX_MAX_OPEN_FILES");
-	if (value != NULL)
-		mbox->max_open_files = (unsigned int)strtoul(value, NULL, 10);
-	else
-		mbox->max_open_files = DBOX_DEFAULT_MAX_OPEN_FILES;
-	i_array_init(&mbox->open_files, I_MIN(mbox->max_open_files, 128));
+	i_array_init(&mbox->open_files,
+		     I_MIN(storage->set->dbox_max_open_files, 128));
 
 	mbox->dbox_ext_id =
 		mail_index_ext_register(index, "dbox", 0,
@@ -679,6 +657,7 @@
 	MEMBER(mailbox_is_file) FALSE,
 
 	{
+                dbox_get_setting_parser_info,
 		dbox_class_init,
 		dbox_class_deinit,
 		dbox_alloc,
--- a/src/lib-storage/index/dbox/dbox-storage.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/dbox/dbox-storage.h	Tue Jan 27 18:21:53 2009 -0500
@@ -3,6 +3,7 @@
 
 #include "index-storage.h"
 #include "mailbox-list-private.h"
+#include "dbox-settings.h"
 
 #define DBOX_STORAGE_NAME "dbox"
 #define DBOX_SUBSCRIPTION_FILE_NAME ".dbox-subscriptions"
@@ -21,12 +22,6 @@
 /* Delete temp files having ctime older than this. */
 #define DBOX_TMP_DELETE_SECS (36*60*60)
 
-/* Default rotation settings */
-#define DBOX_DEFAULT_ROTATE_SIZE (2*1024*1024)
-#define DBOX_DEFAULT_ROTATE_MIN_SIZE (1024*16)
-#define DBOX_DEFAULT_ROTATE_DAYS 0
-#define DBOX_DEFAULT_MAX_OPEN_FILES 64
-
 /* Flag specifies if the message should be in primary or alternative storage */
 #define DBOX_INDEX_FLAG_ALT MAIL_INDEX_MAIL_FLAG_BACKEND
 
@@ -37,6 +32,7 @@
 struct dbox_storage {
 	struct mail_storage storage;
 	union mailbox_list_module_context list_module_ctx;
+	const struct dbox_settings *set;
 	const char *alt_dir;
 };
 
@@ -56,11 +52,7 @@
 	/* set while rebuilding indexes with converted maildir files */
 	struct maildir_keywords_sync_ctx *maildir_sync_keywords;
 
-	uoff_t rotate_size, rotate_min_size;
-	unsigned int rotate_days;
-
 	ARRAY_DEFINE(open_files, struct dbox_file *);
-	unsigned int max_open_files;
 
 	const char *path, *alt_path;
 };
--- a/src/lib-storage/index/index-mail.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/index-mail.c	Tue Jan 27 18:21:53 2009 -0500
@@ -418,13 +418,14 @@
 void index_mail_cache_add_idx(struct index_mail *mail, unsigned int field_idx,
 			      const void *data, size_t data_size)
 {
+	const struct mail_storage_settings *set = mail->ibox->box.storage->set;
 	const struct mail_index_header *hdr;
 
-	if (mail->ibox->mail_cache_min_mail_count > 0) {
+	if (set->mail_cache_min_mail_count > 0) {
 		/* First check if we've configured caching not to be used with
 		   low enough message count. */
 		hdr = mail_index_get_header(mail->ibox->view);
-		if (hdr->messages_count < mail->ibox->mail_cache_min_mail_count)
+		if (hdr->messages_count < set->mail_cache_min_mail_count)
 			return;
 	}
 
--- a/src/lib-storage/index/index-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/index-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -17,9 +17,6 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
-#define DEFAULT_CACHE_FIELDS ""
-#define DEFAULT_NEVER_CACHE_FIELDS "imap.envelope"
-
 /* How many seconds to keep index opened for reuse after it's been closed */
 #define INDEX_CACHE_TIMEOUT 10
 /* How many closed indexes to keep */
@@ -305,29 +302,20 @@
 
 static void index_cache_register_defaults(struct index_mailbox *ibox)
 {
+	const struct mail_storage_settings *set = ibox->box.storage->set;
 	static bool initialized = FALSE;
 	struct mail_cache *cache = ibox->cache;
-	const char *cache_env, *never_env, *env;
 
 	if (!initialized) {
 		initialized = TRUE;
 
-		cache_env = getenv("MAIL_CACHE_FIELDS");
-		if (cache_env == NULL)
-			cache_env = DEFAULT_CACHE_FIELDS;
-		never_env = getenv("MAIL_NEVER_CACHE_FIELDS");
-		if (never_env == NULL)
-			never_env = DEFAULT_NEVER_CACHE_FIELDS;
-
-		set_cache_decisions("mail_cache_fields", cache_env,
+		set_cache_decisions("mail_cache_fields",
+				    set->mail_cache_fields,
 				    MAIL_CACHE_DECISION_TEMP);
-		set_cache_decisions("mail_never_cache_fields", never_env,
+		set_cache_decisions("mail_never_cache_fields",
+				    set->mail_never_cache_fields,
 				    MAIL_CACHE_DECISION_NO |
 				    MAIL_CACHE_DECISION_FORCED);
-
-		env = getenv("MAIL_CACHE_MIN_MAIL_COUNT");
-		if (env != NULL)
-			ibox->mail_cache_min_mail_count = atoi(env);
 	}
 
 	ibox->cache_fields = i_malloc(sizeof(global_cache_fields));
@@ -399,20 +387,9 @@
 
 	i_assert(!ibox->box.opened);
 
+	index_flags = mail_storage_settings_to_index_flags(storage->set);
 	if (!ibox->move_to_memory)
 		index_flags |= MAIL_INDEX_OPEN_FLAG_CREATE;
-#ifndef MMAP_CONFLICTS_WRITE
-	if ((storage->flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0)
-#endif
-		index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
-	if ((storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0)
-		index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL;
-	if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX) != 0)
-		index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH;
-	if ((storage->flags & MAIL_STORAGE_FLAG_FSYNC_DISABLE) != 0) {
-		index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE;
-		ibox->fsync_disable = TRUE;
-	}
 
 	ret = mail_index_open(ibox->index, index_flags, storage->lock_method);
 	if (ret <= 0 || ibox->move_to_memory) {
@@ -563,7 +540,7 @@
 			return FALSE;
 		}
 	}
-	if (i > ibox->box.storage->keyword_max_len) {
+	if (i > ibox->box.storage->set->mail_max_keyword_length) {
 		*error_r = "Keyword length too long";
 		return FALSE;
 	}
--- a/src/lib-storage/index/index-storage.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/index-storage.h	Tue Jan 27 18:21:53 2009 -0500
@@ -47,7 +47,6 @@
 
 	const ARRAY_TYPE(keywords) *keyword_names;
 	struct mail_cache_field *cache_fields;
-	unsigned int mail_cache_min_mail_count;
 
 	ARRAY_TYPE(seq_range) recent_flags;
 	uint32_t recent_flags_prev_uid;
--- a/src/lib-storage/index/maildir/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -2,6 +2,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
@@ -14,6 +15,7 @@
 	maildir-keywords.c \
 	maildir-mail.c \
 	maildir-save.c \
+	maildir-settings.c \
 	maildir-storage.c \
 	maildir-sync.c \
 	maildir-sync-index.c \
@@ -25,6 +27,7 @@
 	maildir-filename.h \
 	maildir-keywords.h \
 	maildir-storage.h \
+	maildir-settings.h \
 	maildir-sync.h \
 	maildir-uidlist.h
 
--- a/src/lib-storage/index/maildir/maildir-copy.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-copy.c	Tue Jan 27 18:21:53 2009 -0500
@@ -94,8 +94,7 @@
 			do_save_mail_vsize(path, ctx);
 	}
 
-	if ((mbox->storage->storage.flags &
-	     MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
+	if (mbox->storage->storage.set->mail_nfs_storage)
 		ret = nfs_safe_link(path, str_c(ctx->dest_path), FALSE);
 	else
 		ret = link(path, str_c(ctx->dest_path));
@@ -162,7 +161,8 @@
 	memset(&do_ctx, 0, sizeof(do_ctx));
 	do_ctx.dest_path = str_new(default_pool, 512);
 
-	if (dest_mbox->storage->copy_preserve_filename && src_mbox != NULL) {
+	if (dest_mbox->storage->set->maildir_copy_preserve_filename &&
+	    src_mbox != NULL) {
 		enum maildir_uidlist_rec_flag src_flags;
 		const char *src_fname;
 
@@ -268,7 +268,7 @@
 	struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox;
 	int ret;
 
-	if (mbox->storage->copy_with_hardlinks &&
+	if (mbox->storage->set->maildir_copy_with_hardlinks &&
 	    maildir_compatible_file_modes(&mbox->ibox.box, mail->box)) {
 		T_BEGIN {
 			ret = maildir_copy_hardlink(t, mail, flags,
--- a/src/lib-storage/index/maildir/maildir-keywords.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-keywords.c	Tue Jan 27 18:21:53 2009 -0500
@@ -75,10 +75,9 @@
 				     strcase_hash, (hash_cmp_callback_t *)strcasecmp);
 
 	mk->dotlock_settings.use_excl_lock =
-		(box->storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
+		box->storage->set->dotlock_use_excl;
 	mk->dotlock_settings.nfs_flush =
-		(box->storage->flags &
-		 MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0;
+		box->storage->set->mail_nfs_storage;
 	mk->dotlock_settings.timeout = KEYWORDS_LOCK_STALE_TIMEOUT + 2;
 	mk->dotlock_settings.stale_timeout = KEYWORDS_LOCK_STALE_TIMEOUT;
 	mk->dotlock_settings.temp_prefix =
@@ -117,7 +116,7 @@
            we rely on stat()'s timestamp and don't bother handling ESTALE
            errors. */
 
-	if ((mk->storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
+	if (mk->storage->set->mail_nfs_storage) {
 		/* file is updated only by replacing it, no need to flush
 		   attribute cache */
 		nfs_flush_file_handle_cache(mk->path);
--- a/src/lib-storage/index/maildir/maildir-save.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-save.c	Tue Jan 27 18:21:53 2009 -0500
@@ -378,11 +378,10 @@
 		if (ctx->fd == -1)
 			ctx->failed = TRUE;
 		else {
-			ctx->input = (ctx->mbox->storage->storage.flags &
-				      MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
-				i_stream_create_crlf(input) :
-				i_stream_create_lf(input);
-
+			if (ctx->mbox->storage->storage.set->mail_save_crlf)
+				ctx->input = i_stream_create_crlf(input);
+			else
+				ctx->input = i_stream_create_lf(input);
 			maildir_save_add(t, fname, _ctx->flags, _ctx->keywords,
 					 _ctx->dest_mail);
 		}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/maildir/maildir-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,44 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "maildir-settings.h"
+
+#include <stddef.h>
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct maildir_settings, name), NULL }
+
+static struct setting_define maildir_setting_defines[] = {
+	DEF(SET_BOOL, maildir_stat_dirs),
+	DEF(SET_BOOL, maildir_copy_with_hardlinks),
+	DEF(SET_BOOL, maildir_copy_preserve_filename),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct maildir_settings maildir_default_settings = {
+	MEMBER(maildir_stat_dirs) FALSE,
+	MEMBER(maildir_copy_with_hardlinks) TRUE,
+	MEMBER(maildir_copy_preserve_filename) FALSE
+};
+
+static struct setting_parser_info maildir_setting_parser_info = {
+	MEMBER(defines) maildir_setting_defines,
+	MEMBER(defaults) &maildir_default_settings,
+
+	MEMBER(parent) &mail_user_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct maildir_settings)
+};
+
+const struct setting_parser_info *maildir_get_setting_parser_info(void)
+{
+	return &maildir_setting_parser_info;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/maildir/maildir-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,12 @@
+#ifndef MAILDIR_SETTINGS_H
+#define MAILDIR_SETTINGS_H
+
+struct maildir_settings {
+	bool maildir_stat_dirs;
+	bool maildir_copy_with_hardlinks;
+	bool maildir_copy_preserve_filename;
+};
+
+const struct setting_parser_info *maildir_get_setting_parser_info(void);
+
+#endif
--- a/src/lib-storage/index/maildir/maildir-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -63,9 +63,8 @@
 			  const char *data, struct mail_storage *storage,
 			  const char **layout_r, const char **error_r)
 {
-	enum mail_storage_flags flags = storage->flags;
 	struct mail_user *user = storage->ns->user;
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 	const char *path, *home;
 
 	*layout_r = MAILDIR_PLUSPLUS_DRIVER_NAME;
@@ -75,7 +74,8 @@
 	list_set->maildir_name = "";
 
 	if (data == NULL || *data == '\0') {
-		if ((flags & MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
+		if ((storage->flags &
+		     MAIL_STORAGE_FLAG_NO_AUTODETECTION) != 0) {
 			*error_r = "Root mail directory not given";
 			return -1;
 		}
@@ -229,6 +229,8 @@
 	if (mailbox_list_alloc(layout, &list, error_r) < 0)
 		return -1;
 
+	storage->set = mail_storage_get_driver_settings(_storage);
+
 	_storage->list = list;
 	storage->list_module_ctx.super = list->v;
 	if (strcmp(layout, MAILDIR_PLUSPLUS_DRIVER_NAME) == 0) {
@@ -248,14 +250,7 @@
 				storage, &storage->list_module_ctx);
 
 	/* finish list init after we've overridden vfuncs */
-	mailbox_list_init(list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(flags));
-
-	storage->copy_with_hardlinks =
-		getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
-	storage->copy_preserve_filename =
-		getenv("MAILDIR_COPY_PRESERVE_FILENAME") != NULL;
-	storage->stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL;
+	mailbox_list_init(list, _storage->ns, &list_set, 0);
 
 	storage->temp_prefix = mailbox_list_get_temp_prefix(list);
 	if (list_set.control_dir == NULL) {
@@ -267,15 +262,13 @@
 	return 0;
 }
 
-static bool maildir_autodetect(const char *data, enum mail_storage_flags flags)
+static bool maildir_autodetect(const struct mail_namespace *ns)
 {
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = ns->mail_set->mail_debug;
 	struct stat st;
 	const char *path;
 
-	data = t_strcut(data, ':');
-
-	path = t_strconcat(data, "/cur", NULL);
+	path = t_strconcat(t_strcut(ns->set->location, ':'), "/cur", NULL);
 	if (stat(path, &st) < 0) {
 		if (debug)
 			i_info("maildir autodetect: stat(%s) failed: %m", path);
@@ -629,7 +622,7 @@
 	const char *root_dir;
 	char sep;
 
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+	if (list->mail_set->mail_full_filesystem_access &&
 	    (*name == '/' || *name == '~'))
 		return NULL;
 
@@ -1014,7 +1007,7 @@
 
 	/* Check files beginning with .nfs always because they may be
 	   temporary files created by the kernel */
-	if (storage->stat_dirs || *fname == '\0' ||
+	if (storage->set->maildir_stat_dirs || *fname == '\0' ||
 	    strncmp(fname, ".nfs", 4) == 0) {
 		const char *path;
 		struct stat st;
@@ -1071,6 +1064,7 @@
 	MEMBER(mailbox_is_file) FALSE,
 
 	{
+                maildir_get_setting_parser_info,
 		maildir_class_init,
 		maildir_class_deinit,
 		maildir_alloc,
--- a/src/lib-storage/index/maildir/maildir-storage.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-storage.h	Tue Jan 27 18:21:53 2009 -0500
@@ -1,6 +1,8 @@
 #ifndef MAILDIR_STORAGE_H
 #define MAILDIR_STORAGE_H
 
+#include "maildir-settings.h"
+
 #define MAILDIR_STORAGE_NAME "maildir"
 #define MAILDIR_SUBSCRIPTION_FILE_NAME "subscriptions"
 #define MAILDIR_INDEX_PREFIX "dovecot.index"
@@ -69,14 +71,12 @@
 	struct mail_storage storage;
 
 	union mailbox_list_module_context list_module_ctx;
+	const struct maildir_settings *set;
 	const char *temp_prefix;
 
 	uint32_t maildir_list_ext_id;
 
-	unsigned int copy_with_hardlinks:1;
-	unsigned int copy_preserve_filename:1;
 	unsigned int save_size_in_filename:1;
-	unsigned int stat_dirs:1;
 };
 
 struct maildir_mailbox {
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Tue Jan 27 18:21:53 2009 -0500
@@ -236,10 +236,9 @@
 
 	uidlist->dotlock_settings.use_io_notify = TRUE;
 	uidlist->dotlock_settings.use_excl_lock =
-		(box->storage->flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
+		box->storage->set->dotlock_use_excl;
 	uidlist->dotlock_settings.nfs_flush =
-		(box->storage->flags &
-		 MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0;
+		box->storage->set->mail_nfs_storage;
 	uidlist->dotlock_settings.timeout =
 		MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT + 2;
 	uidlist->dotlock_settings.stale_timeout =
@@ -711,7 +710,7 @@
 {
 	struct mail_storage *storage = uidlist->ibox->box.storage;
 
-	if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
+	if (storage->set->mail_nfs_storage) {
 		nfs_flush_file_handle_cache(uidlist->path);
 		nfs_flush_attr_cache_unlocked(uidlist->path);
 	}
@@ -745,7 +744,7 @@
 		return 1;
 	}
 
-	if ((storage->flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
+	if (storage->set->mail_nfs_storage) {
 		/* NFS: either the file hasn't been changed, or it has already
 		   been deleted and the inodes just happen to be the same.
 		   check if the fd is still valid. */
@@ -1432,7 +1431,6 @@
 
 	if (ctx->failed)
 		return -1;
-
 	for (p = filename; *p != '\0'; p++) {
 		if (*p == 13 || *p == 10) {
 			i_warning("Maildir %s: Ignoring a file with #0x%x: %s",
--- a/src/lib-storage/index/mbox/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -2,6 +2,7 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-mail \
 	-I$(top_srcdir)/src/lib-imap \
 	-I$(top_srcdir)/src/lib-index \
@@ -15,6 +16,7 @@
 	mbox-mail.c \
 	mbox-md5.c \
 	mbox-save.c \
+	mbox-settings.c \
 	mbox-sync-parse.c \
 	mbox-sync-rewrite.c \
 	mbox-sync-update.c \
@@ -27,6 +29,7 @@
 	mbox-file.h \
 	mbox-lock.h \
 	mbox-md5.h \
+	mbox-settings.h \
 	mbox-storage.h \
 	mbox-sync-private.h
 
--- a/src/lib-storage/index/mbox/mbox-lock.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-lock.c	Tue Jan 27 18:21:53 2009 -0500
@@ -22,14 +22,6 @@
 /* 0.1 .. 0.2msec */
 #define LOCK_RANDOM_USLEEP_TIME (100000 + (unsigned int)rand() % 100000)
 
-/* lock methods to use in wanted order */
-#define DEFAULT_READ_LOCK_METHODS "fcntl"
-#define DEFAULT_WRITE_LOCK_METHODS "dotlock fcntl"
-/* lock timeout */
-#define MBOX_DEFAULT_LOCK_TIMEOUT (5*60)
-/* assume stale dotlock if mbox file hasn't changed for n seconds */
-#define DEFAULT_DOTLOCK_CHANGE_TIMEOUT (120)
-
 enum mbox_lock_type {
 	MBOX_LOCK_DOTLOCK,
 	MBOX_LOCK_DOTLOCK_TRY,
@@ -92,11 +84,6 @@
 	{ 0, NULL, NULL }
 };
 
-static bool lock_settings_initialized = FALSE;
-static enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
-static enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
-static int lock_timeout, dotlock_change_timeout;
-
 static int mbox_lock_list(struct mbox_lock_context *ctx, int lock_type,
 			  time_t max_wait_time, int idx);
 static int mbox_unlock_files(struct mbox_lock_context *ctx);
@@ -133,18 +120,16 @@
 	locks[dest] = (enum mbox_lock_type)-1;
 }
 
-static void mbox_init_lock_settings(void)
+static void mbox_init_lock_settings(struct mbox_storage *storage)
 {
-	const char *str;
+	enum mbox_lock_type read_locks[MBOX_LOCK_COUNT+1];
+	enum mbox_lock_type write_locks[MBOX_LOCK_COUNT+1];
 	int r, w;
 
-	str = getenv("MBOX_READ_LOCKS");
-	if (str == NULL) str = DEFAULT_READ_LOCK_METHODS;
-	mbox_read_lock_methods(str, "MBOX_READ_LOCKS", read_locks);
-
-	str = getenv("MBOX_WRITE_LOCKS");
-	if (str == NULL) str = DEFAULT_WRITE_LOCK_METHODS;
-	mbox_read_lock_methods(str, "MBOX_WRITE_LOCKS", write_locks);
+	mbox_read_lock_methods(storage->set->mbox_read_locks,
+			       "mbox_read_locks", read_locks);
+	mbox_read_lock_methods(storage->set->mbox_write_locks,
+			       "mbox_write_locks", write_locks);
 
 	/* check that read/write list orders match. write_locks must contain
 	   at least read_locks and possibly more. */
@@ -161,14 +146,17 @@
 			"(and possibly more)");
 	}
 
-	str = getenv("MBOX_LOCK_TIMEOUT");
-	lock_timeout = str == NULL ? MBOX_DEFAULT_LOCK_TIMEOUT : atoi(str);
+	storage->read_locks = p_new(storage->storage.pool,
+				    enum mbox_lock_type, MBOX_LOCK_COUNT+1);
+	memcpy(storage->read_locks, read_locks,
+	       sizeof(*storage->read_locks) * (MBOX_LOCK_COUNT+1));
 
-	str = getenv("MBOX_DOTLOCK_CHANGE_TIMEOUT");
-	dotlock_change_timeout = str == NULL ?
-		DEFAULT_DOTLOCK_CHANGE_TIMEOUT : atoi(str);
+	storage->write_locks = p_new(storage->storage.pool,
+				     enum mbox_lock_type, MBOX_LOCK_COUNT+1);
+	memcpy(storage->write_locks, write_locks,
+	       sizeof(*storage->write_locks) * (MBOX_LOCK_COUNT+1));
 
-        lock_settings_initialized = TRUE;
+        storage->lock_settings_initialized = TRUE;
 }
 
 static int mbox_file_open_latest(struct mbox_lock_context *ctx, int lock_type)
@@ -221,7 +209,8 @@
 		lock_types = ctx->lock_type == F_WRLCK ||
 			(ctx->lock_type == F_UNLCK &&
 			 ctx->mbox->mbox_lock_type == F_WRLCK) ?
-			write_locks : read_locks;
+			ctx->mbox->storage->write_locks :
+			ctx->mbox->storage->read_locks;
 
 		for (i = 0; lock_types[i] != (enum mbox_lock_type)-1; i++) {
 			if (lock_types[i] == MBOX_LOCK_DOTLOCK)
@@ -370,12 +359,10 @@
         ctx->dotlock_last_stale = -1;
 
 	memset(&set, 0, sizeof(set));
-	set.use_excl_lock = (mbox->storage->storage.flags &
-			     MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0;
-	set.nfs_flush = (mbox->storage->storage.flags &
-			 MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0;
-	set.timeout = lock_timeout;
-	set.stale_timeout = dotlock_change_timeout;
+	set.use_excl_lock = mbox->storage->storage.set->dotlock_use_excl;
+	set.nfs_flush = mbox->storage->storage.set->mail_nfs_storage;
+	set.timeout = mbox->storage->set->mbox_lock_timeout;
+	set.stale_timeout = mbox->storage->set->mbox_dotlock_change_timeout;
 	set.callback = dotlock_callback;
 	set.context = ctx;
 
@@ -588,7 +575,8 @@
 
 	lock_types = lock_type == F_WRLCK ||
 		(lock_type == F_UNLCK && ctx->mbox->mbox_lock_type == F_WRLCK) ?
-		write_locks : read_locks;
+		ctx->mbox->storage->write_locks :
+		ctx->mbox->storage->read_locks;
 	for (i = idx; lock_types[i] != (enum mbox_lock_type)-1; i++) {
 		type = lock_types[i];
 		lock_status = lock_type != F_UNLCK;
@@ -616,8 +604,8 @@
 
         index_storage_lock_notify_reset(&mbox->ibox);
 
-	if (!lock_settings_initialized)
-                mbox_init_lock_settings();
+	if (!mbox->storage->lock_settings_initialized)
+                mbox_init_lock_settings(mbox->storage);
 
 	if (mbox->mbox_fd == -1 && mbox->mbox_file_stream != NULL) {
 		/* read-only mbox stream. no need to lock. */
@@ -626,7 +614,7 @@
 		return 1;
 	}
 
-	max_wait_time = time(NULL) + lock_timeout;
+	max_wait_time = time(NULL) + mbox->storage->set->mbox_lock_timeout;
 
 	memset(&ctx, 0, sizeof(ctx));
 	ctx.mbox = mbox;
@@ -634,6 +622,9 @@
 	if (mbox->mbox_lock_type == F_WRLCK) {
 		/* dropping to shared lock. first drop those that we
 		   don't remove completely. */
+		const enum mbox_lock_type *read_locks =
+                        mbox->storage->read_locks;
+
 		for (i = 0; i < MBOX_LOCK_COUNT; i++)
 			ctx.lock_status[i] = 1;
 		for (i = 0; read_locks[i] != (enum mbox_lock_type)-1; i++)
@@ -658,6 +649,11 @@
 	if (drop_locks) {
 		/* dropping to shared lock: drop the locks that are only
 		   in write list */
+		const enum mbox_lock_type *read_locks =
+                        mbox->storage->read_locks;
+		const enum mbox_lock_type *write_locks =
+			mbox->storage->write_locks;
+
 		memset(ctx.lock_status, 0, sizeof(ctx.lock_status));
 		for (i = 0; write_locks[i] != (enum mbox_lock_type)-1; i++)
 			ctx.lock_status[write_locks[i]] = 1;
@@ -691,8 +687,7 @@
 		if (ret <= 0)
 			return ret;
 
-		if ((mbox->storage->storage.flags &
-		     MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) {
+		if (mbox->storage->storage.set->mail_nfs_storage) {
 			if (fcntl_locked) {
 				nfs_flush_attr_cache_fd_locked(mbox->path,
 							       mbox->mbox_fd);
--- a/src/lib-storage/index/mbox/mbox-mail.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-mail.c	Tue Jan 27 18:21:53 2009 -0500
@@ -208,8 +208,8 @@
 
 	if (seq == hdr->messages_count) {
 		/* last message, use the synced mbox size */
-		trailer_size = (mbox->storage->storage.flags &
-				MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ? 2 : 1;
+		trailer_size =
+			mbox->storage->storage.set->mail_save_crlf ? 2 : 1;
 		*next_offset_r = mbox->mbox_hdr.sync_size - trailer_size;
 	} else {
 		if (mbox_file_lookup_offset(mbox, view, seq + 1,
--- a/src/lib-storage/index/mbox/mbox-save.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-save.c	Tue Jan 27 18:21:53 2009 -0500
@@ -393,8 +393,7 @@
 	}
 
 	/* convert linefeeds to wanted format */
-	ret = (ctx->mbox->storage->storage.flags &
-	       MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
+	ret = ctx->mbox->storage->storage.set->mail_save_crlf ?
 		i_stream_create_crlf(filter) : i_stream_create_lf(filter);
 	i_stream_unref(&filter);
 
@@ -542,8 +541,7 @@
 		   this makes it impossible to save a mail that doesn't
 		   end with LF though. */
 		const char *linefeed =
-			(ctx->mbox->storage->storage.flags &
-			 MAIL_STORAGE_FLAG_SAVE_CRLF) != 0 ?
+			ctx->mbox->storage->storage.set->mail_save_crlf ?
 			"\r\n" : "\n";
 		if (o_stream_send_str(ctx->output, linefeed) < 0)
 			return write_error(ctx);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,53 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "mail-storage-settings.h"
+#include "mbox-settings.h"
+
+#include <stddef.h>
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct mbox_settings, name), NULL }
+
+static struct setting_define mbox_setting_defines[] = {
+	DEF(SET_STR, mbox_read_locks),
+	DEF(SET_STR, mbox_write_locks),
+	DEF(SET_UINT, mbox_lock_timeout),
+	DEF(SET_UINT, mbox_dotlock_change_timeout),
+	DEF(SET_UINT, mbox_min_index_size),
+	DEF(SET_BOOL, mbox_dirty_syncs),
+	DEF(SET_BOOL, mbox_very_dirty_syncs),
+	DEF(SET_BOOL, mbox_lazy_writes),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct mbox_settings mbox_default_settings = {
+	MEMBER(mbox_read_locks) "fcntl",
+	MEMBER(mbox_write_locks) "dotlock fcntl",
+	MEMBER(mbox_lock_timeout) 5*60,
+	MEMBER(mbox_dotlock_change_timeout) 2*60,
+	MEMBER(mbox_min_index_size) 0,
+	MEMBER(mbox_dirty_syncs) TRUE,
+	MEMBER(mbox_very_dirty_syncs) FALSE,
+	MEMBER(mbox_lazy_writes) TRUE
+};
+
+static struct setting_parser_info mbox_setting_parser_info = {
+	MEMBER(defines) mbox_setting_defines,
+	MEMBER(defaults) &mbox_default_settings,
+
+	MEMBER(parent) &mail_user_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct mbox_settings)
+};
+
+const struct setting_parser_info *mbox_get_setting_parser_info(void)
+{
+	return &mbox_setting_parser_info;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/index/mbox/mbox-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,17 @@
+#ifndef MBOX_SETTINGS_H
+#define MBOX_SETTINGS_H
+
+struct mbox_settings {
+	const char *mbox_read_locks;
+	const char *mbox_write_locks;
+	unsigned int mbox_lock_timeout;
+	unsigned int mbox_dotlock_change_timeout;
+	unsigned int mbox_min_index_size;
+	bool mbox_dirty_syncs;
+	bool mbox_very_dirty_syncs;
+	bool mbox_lazy_writes;
+};
+
+const struct setting_parser_info *mbox_get_setting_parser_info(void);
+
+#endif
--- a/src/lib-storage/index/mbox/mbox-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -155,13 +155,13 @@
 	return TRUE;
 }
 
-static bool mbox_autodetect(const char *data, enum mail_storage_flags flags)
+static bool mbox_autodetect(const struct mail_namespace *ns)
 {
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = ns->mail_set->mail_debug;
+	const char *data = ns->set->location;
 	const char *path;
 
 	path = t_strcut(data, ':');
-
 	if (debug) {
 		if (strchr(data, ':') != NULL) {
 			i_info("mbox autodetect: data=%s, splitting ':' -> %s",
@@ -187,8 +187,10 @@
 
 static const char *get_root_dir(struct mail_storage *storage)
 {
+	struct mail_namespace auto_ns;
+	struct mail_namespace_settings ns_set;
 	const char *home, *path;
-	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 
 	if (mail_user_get_home(storage->ns->user, &home) > 0) {
 		path = t_strconcat(home, "/mail", NULL);
@@ -212,12 +214,17 @@
 
 	if (debug)
 		i_info("mbox: checking if we are chrooted:");
-	if (mbox_autodetect("", storage->flags))
+
+	memset(&ns_set, 0, sizeof(ns_set));
+	ns_set.location = "";
+	memset(&auto_ns, 0, sizeof(auto_ns));
+	auto_ns.set = &ns_set;
+	auto_ns.mail_set = storage->set;
+	if (mbox_autodetect(&auto_ns))
 		return "/";
 
 	if (debug)
 		i_info("mbox: root mail directory not found");
-
 	return NULL;
 }
 
@@ -270,7 +277,7 @@
 		return NULL;
 	}
 
-	if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
+	if (storage->set->mail_debug)
 		i_info("mbox: root directory created: %s", path);
 	return path;
 }
@@ -281,7 +288,7 @@
 		       const char **layout_r, const char **error_r)
 {
 	enum mail_storage_flags flags = storage->flags;
-	bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 	const char *p;
 	struct stat st;
 	bool autodetect;
@@ -453,6 +460,8 @@
 	if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
 		return -1;
 
+	storage->set = mail_storage_get_driver_settings(_storage);
+
 	storage->list_module_ctx.super = _storage->list->v;
 	if (strcmp(layout, "fs") == 0 && *list_set.maildir_name == '\0') {
 		/* have to use .imap/ directories */
@@ -468,7 +477,6 @@
 
 	/* finish list init after we've overridden vfuncs */
 	mailbox_list_init(_storage->list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(_storage->flags) |
 			  MAILBOX_LIST_FLAG_MAILBOX_FILES);
 	return 0;
 }
@@ -515,16 +523,9 @@
 
 static bool want_memory_indexes(struct mbox_storage *storage, const char *path)
 {
-	const char *env;
 	struct stat st;
-	unsigned int min_size;
 
-	env = getenv("MBOX_MIN_INDEX_SIZE");
-	if (env == NULL)
-		return FALSE;
-
-	min_size = strtoul(env, NULL, 10);
-	if (min_size == 0)
+	if (storage->set->mbox_min_index_size == 0)
 		return FALSE;
 
 	if (stat(path, &st) < 0) {
@@ -536,7 +537,7 @@
 			return FALSE;
 		}
 	}
-	return st.st_size / 1024 < min_size;
+	return st.st_size / 1024 < storage->set->mbox_min_index_size;
 }
 
 static void mbox_lock_touch_timeout(struct mbox_mailbox *mbox)
@@ -569,10 +570,6 @@
 					sizeof(mbox->mbox_hdr),
 					sizeof(uint64_t), sizeof(uint64_t));
 
-        mbox->mbox_very_dirty_syncs = getenv("MBOX_VERY_DIRTY_SYNCS") != NULL;
-	mbox->mbox_do_dirty_syncs = mbox->mbox_very_dirty_syncs ||
-		getenv("MBOX_DIRTY_SYNCS") != NULL;
-
 	if ((storage->storage.flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) != 0)
 		mbox->mbox_save_md5 = TRUE;
 
@@ -989,6 +986,7 @@
 	MEMBER(mailbox_is_file) TRUE,
 
 	{
+                mbox_get_setting_parser_info,
 		mbox_class_init,
 		mbox_class_deinit,
 		mbox_alloc,
--- a/src/lib-storage/index/mbox/mbox-storage.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-storage.h	Tue Jan 27 18:21:53 2009 -0500
@@ -1,6 +1,10 @@
 #ifndef MBOX_STORAGE_H
 #define MBOX_STORAGE_H
 
+#include "index-storage.h"
+#include "mbox-settings.h"
+#include "mailbox-list-private.h"
+
 /* Padding to leave in X-Keywords header when rewriting mbox */
 #define MBOX_HEADER_PADDING 50
 /* Don't write Content-Length header unless it's value is larger than this. */
@@ -11,9 +15,6 @@
 #define MBOX_INDEX_PREFIX "dovecot.index"
 #define MBOX_INDEX_DIR_NAME ".imap"
 
-#include "index-storage.h"
-#include "mailbox-list-private.h"
-
 struct mbox_index_header {
 	uint64_t sync_size;
 	uint32_t sync_mtime;
@@ -23,6 +24,11 @@
 struct mbox_storage {
 	struct mail_storage storage;
 
+	const struct mbox_settings *set;
+	enum mbox_lock_type *read_locks;
+	enum mbox_lock_type *write_locks;
+	unsigned int lock_settings_initialized:1;
+
 	union mailbox_list_module_context list_module_ctx;
 };
 
@@ -49,8 +55,6 @@
 	unsigned int no_mbox_file:1;
 	unsigned int invalid_mbox_file:1;
 	unsigned int mbox_broken_offsets:1;
-	unsigned int mbox_do_dirty_syncs:1;
-	unsigned int mbox_very_dirty_syncs:1;
 	unsigned int mbox_save_md5:1;
 	unsigned int mbox_dotlocked:1;
 	unsigned int mbox_used_privileges:1;
--- a/src/lib-storage/index/mbox/mbox-sync.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/mbox/mbox-sync.c	Tue Jan 27 18:21:53 2009 -0500
@@ -1675,9 +1675,10 @@
 
 	delay_writes = mbox->ibox.backend_readonly ||
 		((flags & MBOX_SYNC_REWRITE) == 0 &&
-		 getenv("MBOX_LAZY_WRITES") != NULL);
+		 mbox->storage->set->mbox_lazy_writes);
 
-	if (!mbox->mbox_do_dirty_syncs)
+	if (!mbox->storage->set->mbox_dirty_syncs &&
+	    !mbox->storage->set->mbox_very_dirty_syncs)
 		flags |= MBOX_SYNC_UNDIRTY;
 
 	if ((flags & MBOX_SYNC_LOCK_READING) != 0) {
@@ -1865,8 +1866,8 @@
 
 	i_assert(*lock_id != 0);
 
-	if ((mbox->storage->storage.flags &
-	     MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0 && mbox->mbox_fd != -1) {
+	if (mbox->storage->storage.set->mail_nfs_storage &&
+	    mbox->mbox_fd != -1) {
 		if (fdatasync(mbox->mbox_fd) < 0) {
 			mbox_set_syscall_error(mbox, "fdatasync()");
 			ret = -1;
@@ -1923,7 +1924,7 @@
 
 	if (index_mailbox_want_full_sync(&mbox->ibox, flags)) {
 		if ((flags & MAILBOX_SYNC_FLAG_FULL_READ) != 0 &&
-		    !mbox->mbox_very_dirty_syncs)
+		    !mbox->storage->set->mbox_very_dirty_syncs)
 			mbox_sync_flags |= MBOX_SYNC_UNDIRTY;
 		if ((flags & MAILBOX_SYNC_FLAG_FULL_WRITE) != 0)
 			mbox_sync_flags |= MBOX_SYNC_REWRITE;
--- a/src/lib-storage/index/raw/raw-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/raw/raw-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -30,7 +30,7 @@
 		      const char *data, struct mail_storage *storage,
 		      const char **layout_r, const char **error_r)
 {
-	bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
+	bool debug = storage->set->mail_debug;
 
 	*layout_r = "fs";
 
@@ -102,7 +102,7 @@
 
 	/* finish list init after we've overridden vfuncs */
 	mailbox_list_init(_storage->list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(_storage->flags));
+			  MAILBOX_LIST_FLAG_MAILBOX_FILES);
 	return 0;
 }
 
@@ -258,6 +258,7 @@
 	MEMBER(mailbox_is_file) TRUE,
 
 	{
+		NULL,
 		raw_class_init,
 		raw_class_deinit,
 		raw_alloc,
--- a/src/lib-storage/index/shared/shared-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/index/shared/shared-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -69,7 +69,6 @@
 		return -1;
 	}
 	storage->ns_prefix_pattern = p_strdup(_storage->pool, wildcardp);
-	*wildcardp = '\0';
 
 	have_username = FALSE;
 	for (p = storage->ns_prefix_pattern; *p != '\0'; p++) {
@@ -91,6 +90,10 @@
 		return -1;
 	}
 
+	/* truncate prefix after the above checks are done, so they can log
+	   the full prefix in error conditions */
+	*wildcardp = '\0';
+
 	if (mailbox_list_alloc("shared", &_storage->list, error_r) < 0)
 		return -1;
 	MODULE_CONTEXT_SET_FULL(_storage->list, shared_mailbox_list_module,
@@ -99,8 +102,7 @@
 	memset(&list_set, 0, sizeof(list_set));
 	list_set.mail_storage_flags = &_storage->flags;
 	list_set.lock_method = &_storage->lock_method;
-	mailbox_list_init(_storage->list, _storage->ns, &list_set,
-			  mail_storage_get_list_flags(_storage->flags));
+	mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
 	return 0;
 }
 
@@ -134,6 +136,7 @@
 	};
 	struct var_expand_table *tab;
 	struct mail_namespace *ns;
+	struct mail_namespace_settings *ns_set;
 	struct mail_user *owner;
 	const char *domain = NULL, *username = NULL, *userdomain = NULL;
 	const char *name, *p, *next, **dest, *error;
@@ -207,7 +210,7 @@
 		return 0;
 	}
 
-	owner = mail_user_init(userdomain);
+	owner = mail_user_alloc(userdomain, user->unexpanded_set);
 	if (!var_has_key(storage->location, 'h', "home"))
 		ret = 1;
 	else {
@@ -220,6 +223,13 @@
 			return -1;
 		}
 	}
+	if (mail_user_init(owner, &error) < 0) {
+		mail_storage_set_critical(_storage,
+			"Couldn't create namespace '%s' for user %s: %s",
+			_storage->ns->prefix, userdomain, error);
+		mail_user_unref(&owner);
+		return -1;
+	}
 
 	/* create the new namespace */
 	ns = i_new(struct mail_namespace, 1);
@@ -230,14 +240,24 @@
 	ns->flags = NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN |
 		NAMESPACE_FLAG_AUTOCREATED;
 	ns->sep = _storage->ns->sep;
+	ns->mail_set = _storage->set;
 
 	location = t_str_new(256);
 	if (ret > 0)
 		var_expand(location, storage->location, tab);
 	else
 		get_nonexisting_user_location(storage, userdomain, location);
-	if (mail_storage_create(ns, NULL, str_c(location), _storage->flags,
-				_storage->lock_method, &error) < 0) {
+
+	ns_set = p_new(user->pool, struct mail_namespace_settings, 1);
+	ns_set->type = "shared";
+	ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep);
+	ns_set->prefix = ns->prefix;
+	ns_set->location = p_strdup(user->pool, str_c(location));
+	ns_set->hidden = TRUE;
+	ns_set->list = "yes";
+	ns->set = ns_set;
+
+	if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) {
 		mail_storage_set_critical(_storage, "Namespace '%s': %s",
 					  ns->prefix, error);
 		mail_namespace_destroy(ns);
@@ -310,6 +330,7 @@
 	{
 		NULL,
 		NULL,
+		NULL,
 		shared_alloc,
 		shared_create,
 		index_storage_destroy,
--- a/src/lib-storage/list/index-mailbox-list.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/list/index-mailbox-list.c	Tue Jan 27 18:21:53 2009 -0500
@@ -457,16 +457,17 @@
 	struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list);
 	const char *path;
 	enum mail_index_open_flags index_flags;
-	enum mail_storage_flags storage_flags;
+	enum file_lock_method lock_method;
 	int ret;
 
-	/* FIXME: a bit ugly way to get the flags, but this will do for now.. */
-	index_flags = MAIL_INDEX_OPEN_FLAG_CREATE;
-	storage_flags = *list->set.mail_storage_flags;
-#ifndef MMAP_CONFLICTS_WRITE
-	if ((storage_flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0)
-#endif
-		index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
+	index_flags = MAIL_INDEX_OPEN_FLAG_CREATE |
+		mail_storage_settings_to_index_flags(list->mail_set);
+
+	if (!file_lock_method_parse(list->mail_set->lock_method,
+				    &lock_method)) {
+		i_error("Unknown lock_method: %s", list->mail_set->lock_method);
+		return -1;
+	}
 
 	if (mail_index_open(ilist->mail_index, index_flags,
 			    *list->set.lock_method) < 0) {
@@ -507,7 +508,7 @@
 
 	/* FIXME: always disabled for now */
 	dir = mailbox_list_get_path(list, NULL, MAILBOX_LIST_PATH_TYPE_INDEX);
-	if (*dir == '\0' || getenv("MAILBOX_LIST_INDEX_DISABLE") != NULL ||
+	if (*dir == '\0' || list->mail_set->mailbox_list_index_disable ||
 	    strcmp(list->name, "maildir++") != 0 || 1) {
 		/* reserve the module context anyway, so syncing code knows
 		   that the index is disabled */
--- a/src/lib-storage/list/mailbox-list-fs-iter.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/list/mailbox-list-fs-iter.c	Tue Jan 27 18:21:53 2009 -0500
@@ -178,7 +178,7 @@
 {
 	const char *const *patterns, *name, *p, *last;
 
-	if ((ctx->ctx.list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) == 0)
+	if (!ctx->ctx.list->mail_set->mail_full_filesystem_access)
 		return NULL;
 
 	/* see if there are any absolute paths in patterns */
--- a/src/lib-storage/list/mailbox-list-fs.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/list/mailbox-list-fs.c	Tue Jan 27 18:21:53 2009 -0500
@@ -97,7 +97,7 @@
 static bool
 fs_is_valid_pattern(struct mailbox_list *list, const char *pattern)
 {
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+	if (list->mail_set->mail_full_filesystem_access)
 		return TRUE;
 
 	return fs_list_is_valid_common_nonfs(list, pattern);
@@ -111,7 +111,7 @@
 	if (!fs_list_is_valid_common(name, &len))
 		return FALSE;
 
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+	if (list->mail_set->mail_full_filesystem_access)
 		return TRUE;
 
 	return fs_list_is_valid_common_nonfs(list, name);
@@ -127,7 +127,7 @@
 	if (len > FS_MAX_CREATE_MAILBOX_NAME_LENGTH)
 		return FALSE;
 
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+	if (list->mail_set->mail_full_filesystem_access)
 		return TRUE;
 
 	if (mailbox_list_name_is_too_large(name, '/'))
--- a/src/lib-storage/list/mailbox-list-maildir.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/list/mailbox-list-maildir.c	Tue Jan 27 18:21:53 2009 -0500
@@ -137,7 +137,7 @@
 	if (!maildir_list_is_valid_common(list, name, &len))
 		return FALSE;
 
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+	if (list->mail_set->mail_full_filesystem_access)
 		return TRUE;
 
 	return maildir_list_is_valid_common_nonfs(name);
@@ -153,7 +153,7 @@
 	if (len > MAILDIR_MAX_CREATE_MAILBOX_NAME_LENGTH)
 		return FALSE;
 
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0)
+	if (list->mail_set->mail_full_filesystem_access)
 		return TRUE;
 
 	if (!maildir_list_is_valid_common_nonfs(name))
@@ -168,9 +168,6 @@
 maildir_list_get_path(struct mailbox_list *_list, const char *name,
 		      enum mailbox_list_path_type type)
 {
-	struct maildir_mailbox_list *list =
-		(struct maildir_mailbox_list *)_list;
-
 	if (name == NULL) {
 		/* return root directories */
 		switch (type) {
@@ -187,7 +184,7 @@
 		i_unreached();
 	}
 
-	if ((list->list.flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0 &&
+	if (_list->mail_set->mail_full_filesystem_access &&
 	    (*name == '/' || *name == '~'))
 		return maildir_list_get_absolute_path(_list, name);
 
--- a/src/lib-storage/list/subscription-file.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/list/subscription-file.c	Tue Jan 27 18:21:53 2009 -0500
@@ -27,8 +27,7 @@
 static void subsread_set_syscall_error(struct mailbox_list *list,
 				       const char *function, const char *path)
 {
-	if (errno == EACCES &&
-	    (list->flags & MAILBOX_LIST_FLAG_DEBUG) == 0) {
+	if (errno == EACCES && !list->mail_set->mail_debug) {
 		mailbox_list_set_error(list, MAIL_ERROR_PERM,
 				       "No permission to read subscriptions");
 	} else {
@@ -41,8 +40,7 @@
 static void subswrite_set_syscall_error(struct mailbox_list *list,
 					const char *function, const char *path)
 {
-	if (errno == EACCES &&
-	    (list->flags & MAILBOX_LIST_FLAG_DEBUG) == 0) {
+	if (errno == EACCES && !list->mail_set->mail_debug) {
 		mailbox_list_set_error(list, MAIL_ERROR_PERM,
 				       "No permission to modify subscriptions");
 	} else {
@@ -103,10 +101,8 @@
 		name = "INBOX";
 
 	memset(&dotlock_set, 0, sizeof(dotlock_set));
-	dotlock_set.use_excl_lock =
-		(list->flags & MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL) != 0;
-	dotlock_set.nfs_flush =
-		(list->flags & MAILBOX_LIST_FLAG_NFS_FLUSH) != 0;
+	dotlock_set.use_excl_lock = list->mail_set->dotlock_use_excl;
+	dotlock_set.nfs_flush = list->mail_set->mail_nfs_storage;
 	dotlock_set.temp_prefix = temp_prefix;
 	dotlock_set.timeout = SUBSCRIPTION_FILE_LOCK_TIMEOUT;
 	dotlock_set.stale_timeout = SUBSCRIPTION_FILE_CHANGE_TIMEOUT;
--- a/src/lib-storage/mail-namespace.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-namespace.c	Tue Jan 27 18:21:53 2009 -0500
@@ -1,9 +1,11 @@
 /* Copyright (c) 2005-2009 Dovecot authors, see the included COPYING file */
 
 #include "lib.h"
+#include "array.h"
 #include "str.h"
 #include "file-lock.h"
 #include "mail-storage.h"
+#include "mail-storage-settings.h"
 #include "mail-namespace.h"
 
 #include <stdlib.h>
@@ -35,82 +37,85 @@
 	i_free(ns);
 }
 
-static struct mail_namespace *
-namespace_add_env(const char *data, unsigned int num,
-		  struct mail_user *user, enum mail_storage_flags flags,
-		  enum file_lock_method lock_method,
-		  struct mail_namespace *prev_namespaces)
+static int
+namespace_add(struct mail_user *user,
+	      struct mail_namespace_settings *ns_set,
+	      const struct mail_storage_settings *mail_set,
+	      struct mail_namespace *prev_namespaces,
+	      struct mail_namespace **ns_p, const char **error_r)
 {
         struct mail_namespace *ns;
-	const char *sep, *type, *prefix, *driver, *error, *list, *alias_for;
+	const char *driver, *error;
 
 	ns = i_new(struct mail_namespace, 1);
+	if (strncmp(ns_set->type, "private", 7) == 0) {
+		ns->owner = user;
+		ns->type = NAMESPACE_PRIVATE;
+	} else if (strncmp(ns_set->type, "shared", 6) == 0)
+		ns->type = NAMESPACE_SHARED;
+	else if (strncmp(ns_set->type, "public", 6) == 0)
+		ns->type = NAMESPACE_PUBLIC;
+	else {
+		*error_r = t_strdup_printf("Unknown namespace type: %s",
+					   ns_set->type);
+		mail_namespace_free(ns);
+		return -1;
+	}
 
-	sep = getenv(t_strdup_printf("NAMESPACE_%u_SEP", num));
-	type = getenv(t_strdup_printf("NAMESPACE_%u_TYPE", num));
-	prefix = getenv(t_strdup_printf("NAMESPACE_%u_PREFIX", num));
-	list = getenv(t_strdup_printf("NAMESPACE_%u_LIST", num));
-	alias_for = getenv(t_strdup_printf("NAMESPACE_%u_ALIAS", num));
-	if (getenv(t_strdup_printf("NAMESPACE_%u_INBOX", num)) != NULL)
+	if (strcmp(ns_set->list, "children") == 0)
+		ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN;
+	else if (strcmp(ns_set->list, "yes") == 0)
+		ns->flags |= NAMESPACE_FLAG_LIST_PREFIX;
+	else if (strcmp(ns_set->list, "no") != 0) {
+		*error_r = t_strdup_printf("Invalid list setting value: %s",
+					   ns_set->list);
+		mail_namespace_free(ns);
+		return -1;
+	}
+
+	if (ns_set->inbox)
 		ns->flags |= NAMESPACE_FLAG_INBOX;
-	if (getenv(t_strdup_printf("NAMESPACE_%u_HIDDEN", num)) != NULL)
+	if (ns_set->hidden)
 		ns->flags |= NAMESPACE_FLAG_HIDDEN;
-	if (list != NULL) {
-		if (strcmp(list, "children") == 0)
-			ns->flags |= NAMESPACE_FLAG_LIST_CHILDREN;
-		else
-			ns->flags |= NAMESPACE_FLAG_LIST_PREFIX;
-	}
-	if (getenv(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS", num)) != NULL)
+	if (ns_set->subscriptions)
 		ns->flags |= NAMESPACE_FLAG_SUBSCRIPTIONS;
 
-	if (type == NULL || *type == '\0' || strncmp(type, "private", 7) == 0) {
-		ns->type = NAMESPACE_PRIVATE;
-		ns->owner = user;
-	} else if (strncmp(type, "shared", 6) == 0)
-		ns->type = NAMESPACE_SHARED;
-	else if (strncmp(type, "public", 6) == 0)
-		ns->type = NAMESPACE_PUBLIC;
-	else {
-		i_error("Unknown namespace type: %s", type);
-		mail_namespace_free(ns);
-		return NULL;
-	}
-
-	if (alias_for != NULL) {
+	if (ns_set->alias_for != NULL) {
 		ns->alias_for = mail_namespace_find_prefix(prev_namespaces,
-							   alias_for);
+							   ns_set->alias_for);
 		if (ns->alias_for == NULL) {
-			i_error("Invalid namespace alias_for: %s", alias_for);
+			*error_r = t_strdup_printf("Invalid namespace alias_for: %s",
+						   ns_set->alias_for);
 			mail_namespace_free(ns);
-			return NULL;
+			return -1;
 		}
 		if (ns->alias_for->alias_for != NULL) {
-			i_error("Chained namespace alias_for: %s", alias_for);
+			*error_r = t_strdup_printf("Chained namespace alias_for: %s",
+						   ns_set->alias_for);
 			mail_namespace_free(ns);
-			return NULL;
+			return -1;
 		}
 		ns->alias_chain_next = ns->alias_for->alias_chain_next;
 		ns->alias_for->alias_chain_next = ns;
 	}
 
-	if (prefix == NULL)
-		prefix = "";
-
-	if ((flags & MAIL_STORAGE_FLAG_DEBUG) != 0) {
+	if (mail_set->mail_debug) {
 		i_info("Namespace: type=%s, prefix=%s, sep=%s, "
 		       "inbox=%s, hidden=%s, list=%s, subscriptions=%s",
-		       type == NULL ? "" : type, prefix, sep == NULL ? "" : sep,
-		       (ns->flags & NAMESPACE_FLAG_INBOX) ? "yes" : "no",
-		       (ns->flags & NAMESPACE_FLAG_HIDDEN) ? "yes" : "no",
-		       list,
-		       (ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) ?
-		       "yes" : "no");
+		       ns_set->type, ns_set->prefix,
+		       ns_set->separator == NULL ? "" : ns_set->separator,
+		       ns_set->inbox ? "yes" : "no",
+		       ns_set->hidden ? "yes" : "no",
+		       ns_set->list ? "yes" : "no",
+		       ns_set->subscriptions ? "yes" : "no");
 	}
 
-	if (sep != NULL)
-		ns->sep = *sep;
-	ns->prefix = i_strdup(prefix);
+	if (*ns_set->location == '\0')
+		ns_set->location = mail_set->mail_location;
+
+	ns->set = ns_set;
+	ns->mail_set = mail_set;
+	ns->prefix = i_strdup(ns_set->prefix);
 	ns->user = user;
 
 	if (ns->type == NAMESPACE_SHARED && strchr(ns->prefix, '%') != NULL) {
@@ -121,16 +126,21 @@
 		driver = NULL;
 	}
 
-	if (mail_storage_create(ns, driver, data, flags, lock_method,
-				&error) < 0) {
-		i_error("Namespace '%s': %s", ns->prefix, error);
+	if (mail_storage_create(ns, driver, 0, &error) < 0) {
+		*error_r = t_strdup_printf("Namespace '%s': %s",
+					   ns->prefix, error);
 		mail_namespace_free(ns);
-		return NULL;
+		return -1;
 	}
-	return ns;
+	if (ns_set->separator != NULL)
+		ns->sep = *ns_set->separator;
+
+	*ns_p = ns;
+	return 0;
 }
 
-static bool namespaces_check(struct mail_namespace *namespaces)
+static bool
+namespaces_check(struct mail_namespace *namespaces, const char **error_r)
 {
 	struct mail_namespace *ns, *inbox_ns = NULL, *private_ns = NULL;
 	unsigned int private_ns_count = 0;
@@ -140,9 +150,9 @@
 	for (ns = namespaces; ns != NULL; ns = ns->next) {
 		if ((ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
 			if (inbox_ns != NULL) {
-				i_error("namespace configuration error: "
+				*error_r = "namespace configuration error: "
 					"There can be only one namespace with "
-					"inbox=yes");
+					"inbox=yes";
 				return FALSE;
 			}
 			inbox_ns = ns;
@@ -154,7 +164,8 @@
 		if (*ns->prefix != '\0' &&
 		    (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 &&
 		    ns->prefix[strlen(ns->prefix)-1] != ns->sep) {
-			i_error("namespace configuration error: "
+			*error_r = t_strdup_printf(
+				"namespace configuration error: "
 				"list=yes requires prefix=%s "
 				"to end with separator", ns->prefix);
 			return FALSE;
@@ -164,16 +175,16 @@
 			if (list_sep == '\0')
 				list_sep = ns->sep;
 			else if (list_sep != ns->sep) {
-				i_error("namespace configuration error: "
+				*error_r = "namespace configuration error: "
 					"All list=yes namespaces must use "
-					"the same separator");
+					"the same separator";
 				return FALSE;
 			}
 		}
 		if (*ns->prefix == '\0' &&
 		    (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) == 0) {
-			i_error("namespace configuration error: "
-				"Empty prefix requires list=yes");
+			*error_r = "namespace configuration error: "
+				"Empty prefix requires list=yes";
 			return FALSE;
 		}
 		if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) != 0)
@@ -186,57 +197,53 @@
 			   the INBOX namespace. */
 			private_ns->flags |= NAMESPACE_FLAG_INBOX;
 		} else {
-			i_error("namespace configuration error: "
-				"inbox=yes namespace missing");
+			*error_r = "namespace configuration error: "
+				"inbox=yes namespace missing";
 			return FALSE;
 		}
 	}
 	if (list_sep == '\0') {
-		i_error("namespace configuration error: "
-			"no list=yes namespaces");
+		*error_r = "namespace configuration error: "
+			"no list=yes namespaces";
 		return FALSE;
 	}
 	if (subscriptions_count == 0) {
-		i_error("namespace configuration error: "
-			"no subscriptions=yes namespaces");
+		*error_r = "namespace configuration error: "
+			"no subscriptions=yes namespaces";
 		return FALSE;
 	}
 	return TRUE;
 }
 
-int mail_namespaces_init(struct mail_user *user)
+int mail_namespaces_init(struct mail_user *user, const char **error_r)
 {
+	const struct mail_storage_settings *mail_set;
+	struct mail_namespace_settings *const *ns_set;
 	struct mail_namespace *namespaces, *ns, **ns_p;
-	enum mail_storage_flags flags;
-        enum file_lock_method lock_method;
-	const char *mail, *data, *error;
-	unsigned int i;
+	struct mail_namespace_settings *inbox_set;
+	const char *error, *driver, *env;
+	unsigned int i, count;
 
-	mail_storage_parse_env(&flags, &lock_method);
+	i_assert(user->initialized);
+
         namespaces = NULL; ns_p = &namespaces;
 
-	/* first try NAMESPACE_* environments */
-	for (i = 1; ; i++) {
-		T_BEGIN {
-			data = getenv(t_strdup_printf("NAMESPACE_%u", i));
-		} T_END;
-
-		if (data == NULL)
-			break;
-
-		T_BEGIN {
-			*ns_p = namespace_add_env(data, i, user, flags,
-						  lock_method, namespaces);
-		} T_END;
-
-		if (*ns_p == NULL)
+	mail_set = mail_user_set_get_driver_settings(user->set, "MAIL");
+	if (array_is_created(&user->set->namespaces))
+		ns_set = array_get(&user->set->namespaces, &count);
+	else {
+		ns_set = NULL;
+		count = 0;
+	}
+	for (i = 0; i < count; i++) {
+		if (namespace_add(user, ns_set[i], mail_set, namespaces,
+				  ns_p, error_r) < 0)
 			return -1;
-
 		ns_p = &(*ns_p)->next;
 	}
 
 	if (namespaces != NULL) {
-		if (!namespaces_check(namespaces)) {
+		if (!namespaces_check(namespaces, error_r)) {
 			while (namespaces != NULL) {
 				ns = namespaces;
 				namespaces = ns->next;
@@ -254,35 +261,50 @@
 		return 0;
 	}
 
-	/* fallback to MAIL */
-	mail = getenv("MAIL");
-	if (mail == NULL) {
-		/* support also maildir-specific environment */
-		mail = getenv("MAILDIR");
-		if (mail != NULL)
-			mail = t_strconcat("maildir:", mail, NULL);
-	}
-
+	/* fallback to using environment variables */
 	ns = i_new(struct mail_namespace, 1);
 	ns->type = NAMESPACE_PRIVATE;
 	ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX |
 		NAMESPACE_FLAG_SUBSCRIPTIONS;
-	ns->prefix = i_strdup("");
-	ns->user = user;
 	ns->owner = user;
 
-	if (mail_storage_create(ns, NULL, mail, flags, lock_method,
-				&error) < 0) {
-		if (mail != NULL && *mail != '\0')
-			i_error("mail_location: %s", error);
+	inbox_set = p_new(user->pool, struct mail_namespace_settings, 1);
+	*inbox_set = mail_namespace_default_settings;
+	inbox_set->inbox = TRUE;
+
+	driver = NULL;
+	env = "MAIL";
+	inbox_set->location = getenv("MAIL");
+	if (inbox_set->location == NULL) {
+		/* support also maildir-specific environment */
+		inbox_set->location = getenv("MAILDIR");
+		if (inbox_set->location == NULL)
+			inbox_set->location = "";
 		else {
-			i_error("mail_location not set and "
-				"autodetection failed: %s", error);
+			driver = "maildir";
+			env = "MAILDIR";
+		}
+	}
+
+	ns->set = inbox_set;
+	ns->mail_set = mail_set;
+	ns->prefix = i_strdup(ns->set->prefix);
+	ns->user = user;
+
+	if (mail_storage_create(ns, driver, 0, &error) < 0) {
+		if (*inbox_set->location != '\0') {
+			*error_r = t_strdup_printf(
+				"Initializing mail storage from environment %s "
+				"failed: %s", env, error);
+		} else {
+			*error_r = t_strdup_printf("mail_location not set and "
+					"autodetection failed: %s", error);
 		}
 		mail_namespace_free(ns);
 		return -1;
 	}
 	user->namespaces = ns;
+	mail_namespace_init_storage(ns);
 
 	if (hook_mail_namespaces_created != NULL) {
 		T_BEGIN {
@@ -292,8 +314,7 @@
 	return 0;
 }
 
-struct mail_namespace *
-mail_namespaces_init_empty(struct mail_user *user)
+struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user)
 {
 	struct mail_namespace *ns;
 
@@ -303,6 +324,7 @@
 	ns->prefix = i_strdup("");
 	ns->flags = NAMESPACE_FLAG_INBOX | NAMESPACE_FLAG_LIST_PREFIX |
 		NAMESPACE_FLAG_SUBSCRIPTIONS;
+	ns->mail_set = mail_user_set_get_driver_settings(user->set, "MAIL");
 	user->namespaces = ns;
 	return ns;
 }
--- a/src/lib-storage/mail-namespace.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-namespace.h	Tue Jan 27 18:21:53 2009 -0500
@@ -3,6 +3,8 @@
 
 #include "mail-user.h"
 
+struct mail_storage_root_settings;
+
 enum namespace_type {
 	NAMESPACE_PRIVATE,
 	NAMESPACE_SHARED,
@@ -55,12 +57,15 @@
 	struct mailbox_list *list;
 	/* FIXME: we should support multiple storages in one namespace */
 	struct mail_storage *storage;
+
+	const struct mail_namespace_settings *set;
+	const struct mail_storage_settings *mail_set;
 };
 
 /* Called after namespaces has been created */
 extern void (*hook_mail_namespaces_created)(struct mail_namespace *namespaces);
 
-int mail_namespaces_init(struct mail_user *user);
+int mail_namespaces_init(struct mail_user *user, const char **error_r);
 struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user);
 void mail_namespaces_deinit(struct mail_namespace **namespaces);
 
--- a/src/lib-storage/mail-storage-private.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-storage-private.h	Tue Jan 27 18:21:53 2009 -0500
@@ -4,6 +4,7 @@
 #include "module-context.h"
 #include "file-lock.h"
 #include "mail-storage.h"
+#include "mail-storage-settings.h"
 #include "mail-index-private.h"
 
 /* Called after mail storage has been created */
@@ -22,6 +23,8 @@
 };
 
 struct mail_storage_vfuncs {
+	const struct setting_parser_info *(*get_setting_parser_info)(void);
+
 	void (*class_init)(void);
 	void (*class_deinit)(void);
 
@@ -30,7 +33,7 @@
 		      const char **error_r);
 	void (*destroy)(struct mail_storage *storage);
 
-	bool (*autodetect)(const char *data, enum mail_storage_flags flags);
+	bool (*autodetect)(const struct mail_namespace *ns);
 
 	struct mailbox *(*mailbox_open)(struct mail_storage *storage,
 					const char *name,
@@ -61,10 +64,10 @@
         const struct mail_storage *storage_class;
 	struct mail_namespace *ns;
 	struct mailbox_list *list;
+	const struct mail_storage_settings *set;
 
 	enum mail_storage_flags flags;
 	enum file_lock_method lock_method;
-	unsigned int keyword_max_len;
 
 	struct mail_storage_callbacks *callbacks;
 	void *callback_context;
@@ -380,7 +383,5 @@
 void mail_set_expunged(struct mail *mail);
 void mailbox_set_deleted(struct mailbox *box);
 
-enum mailbox_list_flags
-mail_storage_get_list_flags(enum mail_storage_flags storage_flags);
 
 #endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mail-storage-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,208 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "settings-parser.h"
+#include "mail-index.h"
+#include "mail-user.h"
+#include "mail-namespace.h"
+#include "mail-storage-private.h"
+#include "mail-storage-settings.h"
+
+#include <stddef.h>
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct mail_storage_settings, name), NULL }
+
+static struct setting_define mail_storage_setting_defines[] = {
+	DEF(SET_STR_VARS, mail_location),
+	DEF(SET_STR, mail_cache_fields),
+	DEF(SET_STR, mail_never_cache_fields),
+	DEF(SET_UINT, mail_cache_min_mail_count),
+	DEF(SET_UINT, mailbox_idle_check_interval),
+	DEF(SET_UINT, mail_max_keyword_length),
+	DEF(SET_BOOL, mail_save_crlf),
+	DEF(SET_BOOL, fsync_disable),
+	DEF(SET_BOOL, mmap_disable),
+	DEF(SET_BOOL, dotlock_use_excl),
+	DEF(SET_BOOL, mail_nfs_storage),
+	DEF(SET_BOOL, mail_nfs_index),
+	DEF(SET_BOOL, mailbox_list_index_disable),
+	DEF(SET_BOOL, mail_debug),
+	DEF(SET_BOOL, mail_full_filesystem_access),
+	DEF(SET_ENUM, lock_method),
+	DEF(SET_STR, pop3_uidl_format),
+
+	SETTING_DEFINE_LIST_END
+};
+
+struct mail_storage_settings mail_storage_default_settings = {
+	MEMBER(mail_location) "",
+	MEMBER(mail_cache_fields) "flags",
+	MEMBER(mail_never_cache_fields) "imap.envelope",
+	MEMBER(mail_cache_min_mail_count) 0,
+	MEMBER(mailbox_idle_check_interval) 30,
+	MEMBER(mail_max_keyword_length) 50,
+	MEMBER(mail_save_crlf) FALSE,
+	MEMBER(fsync_disable) FALSE,
+	MEMBER(mmap_disable) FALSE,
+	MEMBER(dotlock_use_excl) FALSE,
+	MEMBER(mail_nfs_storage) FALSE,
+	MEMBER(mail_nfs_index) FALSE,
+	MEMBER(mailbox_list_index_disable) FALSE,
+	MEMBER(mail_debug) FALSE,
+	MEMBER(mail_full_filesystem_access) FALSE,
+	MEMBER(lock_method) "fcntl:flock:dotlock",
+	MEMBER(pop3_uidl_format) "%08Xu%08Xv"
+};
+
+struct setting_parser_info mail_storage_setting_parser_info = {
+	MEMBER(defines) mail_storage_setting_defines,
+	MEMBER(defaults) &mail_storage_default_settings,
+
+	MEMBER(parent) &mail_user_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct mail_storage_settings)
+};
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct mail_namespace_settings, name), NULL }
+
+static struct setting_define mail_namespace_setting_defines[] = {
+	DEF(SET_ENUM, type),
+	DEF(SET_STR, separator),
+	DEF(SET_STR_VARS, prefix),
+	DEF(SET_STR_VARS, location),
+	DEF(SET_STR_VARS, alias_for),
+
+	DEF(SET_BOOL, inbox),
+	DEF(SET_BOOL, hidden),
+	DEF(SET_ENUM, list),
+	DEF(SET_BOOL, subscriptions),
+
+	SETTING_DEFINE_LIST_END
+};
+
+struct mail_namespace_settings mail_namespace_default_settings = {
+	MEMBER(type) "private:shared:public",
+	MEMBER(separator) "",
+	MEMBER(prefix) "",
+	MEMBER(location) "",
+	MEMBER(alias_for) NULL,
+
+	MEMBER(inbox) FALSE,
+	MEMBER(hidden) FALSE,
+	MEMBER(list) "yes:no:children",
+	MEMBER(subscriptions) TRUE
+};
+
+struct setting_parser_info mail_namespace_setting_parser_info = {
+	MEMBER(defines) mail_namespace_setting_defines,
+	MEMBER(defaults) &mail_namespace_default_settings,
+
+	MEMBER(parent) &mail_user_setting_parser_info,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) offsetof(struct mail_namespace_settings, type),
+	MEMBER(struct_size) sizeof(struct mail_namespace_settings)
+};
+
+#undef DEF
+#undef DEFLIST
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct mail_user_settings, name), NULL }
+#define DEFLIST(field, name, defines) \
+	{ SET_DEFLIST, name, \
+	  offsetof(struct mail_user_settings, field), defines }
+
+static struct setting_define mail_user_setting_defines[] = {
+	DEF(SET_UINT, umask),
+
+	DEFLIST(namespaces, "namespace", &mail_namespace_setting_parser_info),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct mail_user_settings mail_user_default_settings = {
+	MEMBER(umask) 0077,
+
+	MEMBER(namespaces) ARRAY_INIT
+};
+
+struct setting_parser_info mail_user_setting_parser_info = {
+	MEMBER(defines) mail_user_setting_defines,
+	MEMBER(defaults) &mail_user_default_settings,
+
+	MEMBER(parent) NULL,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct mail_user_settings)
+};
+
+const void *
+mail_user_set_get_driver_settings(const struct mail_user_settings *set,
+				  const char *driver)
+{
+	const void *dset;
+
+	dset = settings_find_dynamic(&mail_user_setting_parser_info,
+				     set, driver);
+	if (dset == NULL) {
+		i_panic("Default settings not found for storage driver %s",
+			driver);
+	}
+	return dset;
+}
+
+const void *mail_storage_get_driver_settings(struct mail_storage *storage)
+{
+	return mail_user_set_get_driver_settings(storage->ns->user->set,
+						 storage->name);
+}
+
+enum mail_index_open_flags
+mail_storage_settings_to_index_flags(const struct mail_storage_settings *set)
+{
+	enum mail_index_open_flags index_flags = 0;
+
+	if (set->fsync_disable)
+		index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE;
+#ifndef MMAP_CONFLICTS_WRITE
+	if (set->mmap_disable)
+#endif
+		index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
+	if (set->dotlock_use_excl)
+		index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL;
+	return index_flags;
+}
+
+void mail_storage_namespace_defines_init(pool_t pool)
+{
+	struct dynamic_settings_parser *parsers;
+	struct mail_storage *const *storages;
+	unsigned int i, j, count;
+
+	storages = array_get(&mail_storage_classes, &count);
+	parsers = t_new(struct dynamic_settings_parser, count + 1);
+	parsers[0].name = "MAIL";
+	parsers[0].info = &mail_storage_setting_parser_info;
+
+	for (i = 0, j = 1; i < count; i++) {
+		if (storages[i]->v.get_setting_parser_info == NULL)
+			continue;
+
+		parsers[j].name = storages[i]->name;
+		parsers[j].info = storages[i]->v.get_setting_parser_info();
+		j++;
+	}
+
+	settings_parser_info_update(pool, parsers[j-1].info->parent, parsers);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib-storage/mail-storage-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,61 @@
+#ifndef MAIL_STORAGE_SETTINGS_H
+#define MAIL_STORAGE_SETTINGS_H
+
+struct mail_user;
+struct mail_storage;
+
+struct mail_storage_settings {
+	const char *mail_location;
+	const char *mail_cache_fields;
+	const char *mail_never_cache_fields;
+	unsigned int mail_cache_min_mail_count;
+	unsigned int mailbox_idle_check_interval;
+	unsigned int mail_max_keyword_length;
+	bool mail_save_crlf;
+	bool fsync_disable;
+	bool mmap_disable;
+	bool dotlock_use_excl;
+	bool mail_nfs_storage;
+	bool mail_nfs_index;
+	bool mailbox_list_index_disable;
+	bool mail_debug;
+	bool mail_full_filesystem_access;
+	const char *lock_method;
+	const char *pop3_uidl_format;
+};
+
+struct mail_namespace_settings {
+	const char *type;
+	const char *separator;
+	const char *prefix;
+	const char *location;
+	const char *alias_for;
+
+	bool inbox;
+	bool hidden;
+	const char *list;
+	bool subscriptions;
+};
+
+struct mail_user_settings {
+	unsigned int umask;
+
+	ARRAY_DEFINE(namespaces, struct mail_namespace_settings *);
+};
+
+extern struct setting_parser_info mail_user_setting_parser_info;
+extern struct setting_parser_info mail_namespace_setting_parser_info;
+extern struct setting_parser_info mail_storage_setting_parser_info;
+extern struct mail_namespace_settings mail_namespace_default_settings;
+
+const void *
+mail_user_set_get_driver_settings(const struct mail_user_settings *set,
+				  const char *driver);
+const void *mail_storage_get_driver_settings(struct mail_storage *storage);
+
+enum mail_index_open_flags
+mail_storage_settings_to_index_flags(const struct mail_storage_settings *set);
+
+void mail_storage_namespace_defines_init(pool_t pool);
+
+#endif
--- a/src/lib-storage/mail-storage.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-storage.c	Tue Jan 27 18:21:53 2009 -0500
@@ -7,6 +7,7 @@
 #include "mail-index-private.h"
 #include "mailbox-list-private.h"
 #include "mail-storage-private.h"
+#include "mail-storage-settings.h"
 #include "mail-namespace.h"
 #include "mail-search.h"
 #include "mailbox-search-result-private.h"
@@ -14,8 +15,6 @@
 #include <stdlib.h>
 #include <ctype.h>
 
-#define DEFAULT_MAX_KEYWORD_LENGTH 50
-
 struct mail_storage_module_register mail_storage_module_register = { 0 };
 struct mail_module_register mail_module_register = { 0 };
 
@@ -26,28 +25,30 @@
 void (*hook_mailbox_opened)(struct mailbox *box) = NULL;
 void (*hook_mailbox_index_opened)(struct mailbox *box) = NULL;
 
-static ARRAY_DEFINE(storages, struct mail_storage *);
+ARRAY_TYPE(mail_storage) mail_storage_classes;
 
 void mail_storage_init(void)
 {
 	mailbox_lists_init();
-	i_array_init(&storages, 8);
+	i_array_init(&mail_storage_classes, 8);
 }
 
 void mail_storage_deinit(void)
 {
-	if (array_is_created(&storages))
-		array_free(&storages);
+	if (array_is_created(&mail_storage_classes))
+		array_free(&mail_storage_classes);
 	mailbox_lists_deinit();
 }
 
 void mail_storage_class_register(struct mail_storage *storage_class)
 {
+	i_assert(mail_storage_find_class(storage_class->name) == NULL);
+
 	if (storage_class->v.class_init != NULL)
 		storage_class->v.class_init();
 
 	/* append it after the list, so the autodetection order is correct */
-	array_append(&storages, &storage_class, 1);
+	array_append(&mail_storage_classes, &storage_class, 1);
 }
 
 void mail_storage_class_unregister(struct mail_storage *storage_class)
@@ -55,10 +56,10 @@
 	struct mail_storage *const *classes;
 	unsigned int i, count;
 
-	classes = array_get(&storages, &count);
+	classes = array_get(&mail_storage_classes, &count);
 	for (i = 0; i < count; i++) {
 		if (classes[i] == storage_class) {
-			array_delete(&storages, i, 1);
+			array_delete(&mail_storage_classes, i, 1);
 			break;
 		}
 	}
@@ -66,48 +67,6 @@
 	storage_class->v.class_deinit();
 }
 
-void mail_storage_parse_env(enum mail_storage_flags *flags_r,
-			    enum file_lock_method *lock_method_r)
-{
-	const char *str;
-
-	*flags_r = 0;
-	if (getenv("FULL_FILESYSTEM_ACCESS") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_FULL_FS_ACCESS;
-	if (getenv("DEBUG") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_DEBUG;
-	if (getenv("MMAP_DISABLE") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_MMAP_DISABLE;
-	if (getenv("MMAP_NO_WRITE") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_MMAP_NO_WRITE;
-	if (getenv("DOTLOCK_USE_EXCL") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL;
-	if (getenv("MAIL_SAVE_CRLF") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_SAVE_CRLF;
-	if (getenv("FSYNC_DISABLE") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_FSYNC_DISABLE;
-	if (getenv("MAIL_NFS_STORAGE") != NULL)
-		*flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE;
-	if (getenv("MAIL_NFS_INDEX") != NULL) {
-		*flags_r |= MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX;
-		if ((*flags_r & MAIL_STORAGE_FLAG_MMAP_DISABLE) == 0)
-			i_fatal("mail_nfs_index=yes requires mmap_disable=yes");
-		if ((*flags_r & MAIL_STORAGE_FLAG_FSYNC_DISABLE) != 0)
-			i_fatal("mail_nfs_index=yes requires fsync_disable=no");
-	}
-
-	str = getenv("POP3_UIDL_FORMAT");
-	if (str != NULL && (str = strchr(str, '%')) != NULL &&
-	    str != NULL && var_get_key(str + 1) == 'm')
-		*flags_r |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
-
-	str = getenv("LOCK_METHOD");
-	if (str == NULL)
-		*lock_method_r = FILE_LOCK_METHOD_FCNTL;
-	else if (!file_lock_method_parse(str, lock_method_r))
-		i_fatal("Unknown lock_method: %s", str);
-}
-
 struct mail_storage *mail_storage_find_class(const char *name)
 {
 	struct mail_storage *const *classes;
@@ -115,7 +74,7 @@
 
 	i_assert(name != NULL);
 
-	classes = array_get(&storages, &count);
+	classes = array_get(&mail_storage_classes, &count);
 	for (i = 0; i < count; i++) {
 		if (strcasecmp(classes[i]->name, name) == 0)
 			return classes[i];
@@ -124,15 +83,15 @@
 }
 
 static struct mail_storage *
-mail_storage_autodetect(const char *data, enum mail_storage_flags flags)
+mail_storage_autodetect(const struct mail_namespace *ns)
 {
 	struct mail_storage *const *classes;
 	unsigned int i, count;
 
-	classes = array_get(&storages, &count);
+	classes = array_get(&mail_storage_classes, &count);
 	for (i = 0; i < count; i++) {
 		if (classes[i]->v.autodetect != NULL &&
-		    classes[i]->v.autodetect(data, flags))
+		    classes[i]->v.autodetect(ns))
 			return classes[i];
 	}
 	return NULL;
@@ -158,15 +117,30 @@
 }
 
 int mail_storage_create(struct mail_namespace *ns, const char *driver,
-			const char *data, enum mail_storage_flags flags,
-			enum file_lock_method lock_method,
-			const char **error_r)
+			enum mail_storage_flags flags, const char **error_r)
 {
 	struct mail_storage *storage_class, *storage;
 	struct mail_storage *const *classes;
-	const char *home, *value;
+	const char *data = ns->set->location;
+	const char *home, *p;
 	unsigned int i, count;
 
+	if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
+	    ns->mail_set->pop3_uidl_format != NULL) {
+		/* if pop3_uidl_format contains %m, we want to keep the
+		   header MD5 sums stored even if we're not running POP3
+		   right now. */
+		p = ns->mail_set->pop3_uidl_format;
+		while ((p = strchr(p, '%')) != NULL) {
+			if (p[1] == '%')
+				p += 2;
+			else if (var_get_key(++p) == 'm') {
+				flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
+				break;
+			}
+		}
+	}
+
 	if (data == NULL)
 		data = "";
 	else if (driver == NULL)
@@ -174,9 +148,9 @@
 
 	if (*data == '\0' && driver == NULL) {
 		/* use the first driver that works */
-		classes = array_get(&storages, &count);
+		classes = array_get(&mail_storage_classes, &count);
 	} else if (driver == NULL) {
-		storage_class = mail_storage_autodetect(data, flags);
+		storage_class = mail_storage_autodetect(ns);
 		if (storage_class == NULL) {
 			*error_r = t_strdup_printf(
 				"Ambiguous mail location setting, "
@@ -200,8 +174,13 @@
 
 	for (i = 0; i < count; i++) {
 		storage = classes[i]->v.alloc();
+		storage->set = ns->mail_set;
 		storage->flags = flags;
-		storage->lock_method = lock_method;
+		if (!file_lock_method_parse(storage->set->lock_method,
+					    &storage->lock_method)) {
+			i_fatal("Unknown lock_method: %s",
+				storage->set->lock_method);
+		}
 		storage->ns = ns;
 
 		storage->callbacks =
@@ -211,7 +190,7 @@
 		if (classes[i]->v.create(storage, data, error_r) == 0)
 			break;
 
-		if ((flags & MAIL_STORAGE_FLAG_DEBUG) != 0 && count > 1) {
+		if (ns->mail_set->mail_debug && count > 1) {
 			i_info("%s: Couldn't create mail storage %s: %s",
 			       classes[i]->name, data, *error_r);
 		}
@@ -234,10 +213,6 @@
 		return -1;
 	}
 
-	value = getenv("MAIL_MAX_KEYWORD_LENGTH");
-	storage->keyword_max_len = value != NULL ?
-		atoi(value) : DEFAULT_MAX_KEYWORD_LENGTH;
-	
 	if (hook_mail_storage_created != NULL) {
 		T_BEGIN {
 			hook_mail_storage_created(storage);
@@ -327,6 +302,12 @@
 	return storage->ns;
 }
 
+const struct mail_storage_settings *
+mail_storage_get_settings(struct mail_storage *storage)
+{
+	return storage->set;
+}
+
 void mail_storage_set_callbacks(struct mail_storage *storage,
 				struct mail_storage_callbacks *callbacks,
 				void *context)
@@ -404,22 +385,6 @@
 				     MAILBOX_LIST_PATH_TYPE_INDEX);
 }
 
-enum mailbox_list_flags
-mail_storage_get_list_flags(enum mail_storage_flags storage_flags)
-{
-	enum mailbox_list_flags list_flags = 0;
-
-	if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
-		list_flags |= MAILBOX_LIST_FLAG_DEBUG;
-	if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-		list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
-	if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0)
-		list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL;
-	if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
-		list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH;
-	return list_flags;
-}
-
 bool mail_storage_set_error_from_errno(struct mail_storage *storage)
 {
 	const char *error_string;
@@ -427,8 +392,7 @@
 
 	if (!mail_error_from_errno(&error, &error_string))
 		return FALSE;
-	if ((storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0 &&
-	    error != MAIL_ERROR_NOTFOUND) {
+	if (storage->set->mail_debug && error != MAIL_ERROR_NOTFOUND) {
 		/* debugging is enabled - admin may be debugging a
 		   (permission) problem, so return FALSE to get the caller to
 		   log the full error message. */
@@ -493,6 +457,11 @@
 	return box->storage;
 }
 
+const struct mail_storage_settings *mailbox_get_settings(struct mailbox *box)
+{
+	return box->storage->set;
+}
+
 const char *mailbox_get_name(const struct mailbox *box)
 {
 	return box->name;
--- a/src/lib-storage/mail-storage.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-storage.h	Tue Jan 27 18:21:53 2009 -0500
@@ -13,33 +13,14 @@
 #define MAIL_STORAGE_STAYALIVE_SECS 15
 
 enum mail_storage_flags {
-	/* Print debugging information while initializing the storage */
-	MAIL_STORAGE_FLAG_DEBUG			= 0x01,
-	/* Allow full filesystem access with absolute or relative paths. */
-	MAIL_STORAGE_FLAG_FULL_FS_ACCESS	= 0x02,
-	/* Don't try to mmap() files */
-	MAIL_STORAGE_FLAG_MMAP_DISABLE		= 0x04,
-	/* Don't try to write() to mmap()ed files. Required for the few
-	   OSes that don't have unified buffer cache
-	   (currently OpenBSD <= 3.5) */
-	MAIL_STORAGE_FLAG_MMAP_NO_WRITE		= 0x08,
 	/* Remember message headers' MD5 sum */
-	MAIL_STORAGE_FLAG_KEEP_HEADER_MD5	= 0x10,
-	/* Use CRLF linefeeds when saving mails. */
-	MAIL_STORAGE_FLAG_SAVE_CRLF		= 0x40,
+	MAIL_STORAGE_FLAG_KEEP_HEADER_MD5	= 0x01,
 	/* Don't try to autodetect anything, require that the given data 
 	   contains all the necessary information. */
-	MAIL_STORAGE_FLAG_NO_AUTODETECTION	= 0x100,
+	MAIL_STORAGE_FLAG_NO_AUTODETECTION	= 0x02,
 	/* Don't autocreate any directories. If they don't exist,
 	   fail to create the storage. */
-	MAIL_STORAGE_FLAG_NO_AUTOCREATE		= 0x200,
-	/* Rely on O_EXCL when creating dotlocks */
-	MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL	= 0x400,
-	/* Flush NFS caches for mail storage / index */
-	MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE	= 0x800,
-	MAIL_STORAGE_FLAG_NFS_FLUSH_INDEX	= 0x1000,
-	/* Don't use fsync() or fdatasync() */
-	MAIL_STORAGE_FLAG_FSYNC_DISABLE		= 0x2000
+	MAIL_STORAGE_FLAG_NO_AUTOCREATE		= 0x04
 };
 
 enum mailbox_open_flags {
@@ -239,8 +220,10 @@
 	const char *pattern;
 };
 ARRAY_DEFINE_TYPE(mailbox_virtual_patterns, struct mailbox_virtual_pattern);
+ARRAY_DEFINE_TYPE(mail_storage, struct mail_storage *);
+ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *);
 
-ARRAY_DEFINE_TYPE(mailboxes, struct mailbox *);
+extern ARRAY_TYPE(mail_storage) mail_storage_classes;
 
 typedef void mailbox_notify_callback_t(struct mailbox *box, void *context);
 
@@ -257,25 +240,25 @@
 /* Find mail storage class by name */
 struct mail_storage *mail_storage_find_class(const char *name);
 
-/* Returns flags and lock_method based on environment settings. */
-void mail_storage_parse_env(enum mail_storage_flags *flags_r,
-			    enum file_lock_method *lock_method_r);
-
 /* Create a new instance of registered mail storage class with given
    storage-specific data. If driver is NULL, it's tried to be autodetected
-   from data. If data is NULL, it uses the first storage that exists.
-   The storage is put into ns->storage. */
+   from ns location. If ns location is NULL, it uses the first storage that
+   exists. The storage is put into ns->storage. */
 int mail_storage_create(struct mail_namespace *ns, const char *driver,
-			const char *data, enum mail_storage_flags flags,
-			enum file_lock_method lock_method,
-			const char **error_r);
+			enum mail_storage_flags flags, const char **error_r);
 void mail_storage_destroy(struct mail_storage **storage);
 
+/* Returns the storage's real hierarchy separator. */
 char mail_storage_get_hierarchy_sep(struct mail_storage *storage);
+/* Returns the storage's mailbox list backend. */
 struct mailbox_list *
 mail_storage_get_list(const struct mail_storage *storage) ATTR_PURE;
+/* Returns the storage's namespace. */
 struct mail_namespace *
 mail_storage_get_namespace(const struct mail_storage *storage) ATTR_PURE;
+/* Returns the mail storage settings. */
+const struct mail_storage_settings *
+mail_storage_get_settings(struct mail_storage *storage) ATTR_PURE;
 
 /* Set storage callback functions to use. */
 void mail_storage_set_callbacks(struct mail_storage *storage,
@@ -326,10 +309,15 @@
 
 /* Enable the given feature for the mailbox. */
 int mailbox_enable(struct mailbox *box, enum mailbox_feature features);
-enum mailbox_feature mailbox_get_enabled_features(struct mailbox *box);
+/* Returns all enabled features. */
+enum mailbox_feature
+mailbox_get_enabled_features(struct mailbox *box) ATTR_PURE;
 
 /* Returns storage of given mailbox */
 struct mail_storage *mailbox_get_storage(const struct mailbox *box) ATTR_PURE;
+/* Returns the storage's settings. */
+const struct mail_storage_settings *
+mailbox_get_settings(struct mailbox *box) ATTR_PURE;
 
 /* Returns name of given mailbox */
 const char *mailbox_get_name(const struct mailbox *box) ATTR_PURE;
--- a/src/lib-storage/mail-user.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-user.c	Tue Jan 27 18:21:53 2009 -0500
@@ -2,7 +2,12 @@
 
 #include "lib.h"
 #include "array.h"
+#include "hostpid.h"
+#include "network.h"
+#include "var-expand.h"
+#include "settings-parser.h"
 #include "auth-master.h"
+#include "mail-storage-settings.h"
 #include "mail-namespace.h"
 #include "mail-user.h"
 
@@ -19,7 +24,8 @@
 	pool_unref(&user->pool);
 }
 
-struct mail_user *mail_user_init(const char *username)
+struct mail_user *mail_user_alloc(const char *username,
+				  const struct mail_user_settings *set)
 {
 	struct mail_user *user;
 	pool_t pool;
@@ -27,17 +33,39 @@
 	i_assert(username != NULL);
 	i_assert(*username != '\0');
 
-	pool = pool_alloconly_create("mail user", 512);
+	pool = pool_alloconly_create("mail user", 2048);
 	user = p_new(pool, struct mail_user, 1);
 	user->pool = pool;
 	user->refcount = 1;
 	user->username = p_strdup(pool, username);
+	user->unexpanded_set = set;
+	user->set = settings_dup(&mail_user_setting_parser_info, set, pool);
 	user->v.deinit = mail_user_deinit_base;
 	p_array_init(&user->module_contexts, user->pool, 5);
+	return user;
+}
 
+int mail_user_init(struct mail_user *user, const char **error_r)
+{
+	const char *home, *key, *value;
+
+	if (user->_home == NULL &&
+	    settings_vars_have_key(&mail_user_setting_parser_info, user->set,
+				   'h', "home", &key, &value) &&
+	    mail_user_get_home(user, &home) <= 0) {
+		*error_r = t_strdup_printf(
+			"userdb didn't return a home directory, "
+			"but %s used it (%%h): %s", key, value);
+		return -1;
+	}
+
+	settings_var_expand(&mail_user_setting_parser_info, user->set,
+			    user->pool, mail_user_var_expand_table(user));
+
+	user->initialized = TRUE;
 	if (hook_mail_user_created != NULL)
 		hook_mail_user_created(user);
-	return user;
+	return 0;
 }
 
 void mail_user_ref(struct mail_user *user)
@@ -69,6 +97,62 @@
 	return NULL;
 }
 
+void mail_user_set_vars(struct mail_user *user, uid_t uid, const char *service,
+			const struct ip_addr *local_ip,
+			const struct ip_addr *remote_ip)
+{
+	user->uid = uid;
+	user->service = p_strdup(user->pool, service);
+	if (local_ip != NULL) {
+		user->local_ip = p_new(user->pool, struct ip_addr, 1);
+		*user->local_ip = *local_ip;
+	}
+	if (remote_ip != NULL) {
+		user->remote_ip = p_new(user->pool, struct ip_addr, 1);
+		*user->remote_ip = *remote_ip;
+	}
+}
+
+const struct var_expand_table *
+mail_user_var_expand_table(struct mail_user *user)
+{
+	static struct var_expand_table static_tab[] = {
+		{ 'u', NULL, "user" },
+		{ 'n', NULL, "username" },
+		{ 'd', NULL, "domain" },
+		{ 's', NULL, "service" },
+		{ 'h', NULL, "home" },
+		{ 'l', NULL, "lip" },
+		{ 'r', NULL, "rip" },
+		{ 'p', NULL, "pid" },
+		{ 'i', NULL, "uid" },
+		{ '\0', NULL, NULL }
+	};
+	struct var_expand_table *tab;
+
+	if (user->var_expand_table != NULL)
+		return user->var_expand_table;
+
+	tab = p_malloc(user->pool, sizeof(static_tab));
+	memcpy(tab, static_tab, sizeof(static_tab));
+
+	tab[0].value = user->username;
+	tab[1].value = p_strdup(user->pool, t_strcut(user->username, '@'));
+	tab[2].value = strchr(user->username, '@');
+	if (tab[2].value != NULL) tab[2].value++;
+	tab[3].value = user->service;
+	tab[4].value = user->_home; /* don't look it up unless we need it */
+	tab[5].value = user->local_ip == NULL ? NULL :
+		p_strdup(user->pool, net_ip2addr(user->local_ip));
+	tab[6].value = user->remote_ip == NULL ? NULL :
+		p_strdup(user->pool, net_ip2addr(user->remote_ip));
+	tab[7].value = my_pid;
+	tab[8].value = p_strdup(user->pool, dec2str(user->uid));
+
+	user->var_expand_table = tab;
+	return user->var_expand_table;
+}
+
 void mail_user_set_home(struct mail_user *user, const char *home)
 {
 	user->_home = p_strdup(user->pool, home);
--- a/src/lib-storage/mail-user.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mail-user.h	Tue Jan 27 18:21:53 2009 -0500
@@ -16,6 +16,15 @@
 	/* don't access the home directly. It may be set lazily. */
 	const char *_home;
 
+	uid_t uid;
+	const char *service;
+	struct ip_addr *local_ip, *remote_ip;
+	const struct var_expand_table *var_expand_table;
+	/* error during initialization */
+	const char *error;
+
+	const struct mail_user_settings *unexpanded_set;
+	struct mail_user_settings *set;
 	struct mail_namespace *namespaces;
 
 	/* Module-specific contexts. See mail_storage_module_id. */
@@ -26,6 +35,8 @@
 	/* User is an administrator. Allow operations not normally allowed
 	   for other people. */
 	unsigned int admin:1;
+	/* mail_user_init() has been called */
+	unsigned int initialized:1;
 };
 
 struct mail_user_module_register {
@@ -44,13 +55,25 @@
 void mail_users_init(const char *auth_socket_path, bool debug);
 void mail_users_deinit(void);
 
-struct mail_user *mail_user_init(const char *username);
+struct mail_user *mail_user_alloc(const char *username,
+				  const struct mail_user_settings *set);
+/* Returns -1 if settings were invalid. */
+int mail_user_init(struct mail_user *user, const char **error_r);
+
 void mail_user_ref(struct mail_user *user);
 void mail_user_unref(struct mail_user **user);
 
 /* Find another user from the given user's namespaces. */
 struct mail_user *mail_user_find(struct mail_user *user, const char *name);
 
+/* Specify mail location %variable expansion data. */
+void mail_user_set_vars(struct mail_user *user, uid_t uid, const char *service,
+			const struct ip_addr *local_ip,
+			const struct ip_addr *remote_ip);
+/* Return %variable expansion table for the user. */
+const struct var_expand_table *
+mail_user_var_expand_table(struct mail_user *user);
+
 /* Specify the user's home directory. This should be called also when it's
    known that the user doesn't have a home directory to avoid the internal
    lookup. */
--- a/src/lib-storage/mailbox-list-private.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mailbox-list-private.h	Tue Jan 27 18:21:53 2009 -0500
@@ -3,6 +3,7 @@
 
 #include "mail-namespace.h"
 #include "mailbox-list.h"
+#include "mail-storage-settings.h"
 
 struct dirent;
 struct imap_match_glob;
@@ -76,6 +77,7 @@
 	pool_t pool;
 	struct mail_namespace *ns;
 	struct mailbox_list_settings set;
+	const struct mail_storage_settings *mail_set;
 	enum mailbox_list_flags flags;
 
 	/* -1 if not set yet. use mailbox_list_get_permissions() to set them */
--- a/src/lib-storage/mailbox-list.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mailbox-list.c	Tue Jan 27 18:21:53 2009 -0500
@@ -192,6 +192,7 @@
 		 *set->subscription_fname != '\0');
 
 	list->ns = ns;
+	list->mail_set = ns->mail_set;
 	list->flags = flags;
 	list->file_create_mode = (mode_t)-1;
 	list->file_create_gid = (gid_t)-1;
@@ -215,7 +216,7 @@
 	list->set.mail_storage_flags = set->mail_storage_flags;
 	list->set.lock_method = set->lock_method;
 
-	if ((flags & MAILBOX_LIST_FLAG_DEBUG) != 0) {
+	if (ns->mail_set->mail_debug) {
 		i_info("%s: root=%s, index=%s, control=%s, inbox=%s",
 		       list->name,
 		       list->set.root_dir == NULL ? "" : list->set.root_dir,
@@ -278,7 +279,7 @@
 		if (!ENOTFOUND(errno)) {
 			mailbox_list_set_critical(list, "stat(%s) failed: %m",
 						  path);
-		} else if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0) {
+		} else if (list->mail_set->mail_debug) {
 			i_info("Namespace %s: Permission lookup failed from %s",
 			       list->ns->prefix, path);
 		}
@@ -303,7 +304,7 @@
 		list->file_create_gid = st.st_gid;
 	}
 
-	if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0) {
+	if (list->mail_set->mail_debug) {
 		i_info("Namespace %s: Using permissions from %s: "
 		       "mode=0%o gid=%ld", list->ns->prefix, path,
 		       (int)list->file_create_mode,
@@ -726,7 +727,7 @@
 bool mailbox_list_try_get_absolute_path(struct mailbox_list *list,
 					const char **name)
 {
-	if ((list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) == 0)
+	if (!list->mail_set->mail_full_filesystem_access)
 		return FALSE;
 
 	if (**name == '/')
--- a/src/lib-storage/mailbox-list.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib-storage/mailbox-list.h	Tue Jan 27 18:21:53 2009 -0500
@@ -13,16 +13,8 @@
 };
 
 enum mailbox_list_flags {
-	/* Print debugging information while initializing the driver */
-	MAILBOX_LIST_FLAG_DEBUG			= 0x01,
-	/* Allow full filesystem access with absolute or relative paths. */
-	MAILBOX_LIST_FLAG_FULL_FS_ACCESS	= 0x04,
-	/* Rely on O_EXCL when creating dotlocks */
-	MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL	= 0x08,
 	/* Mailboxes are files, not directories. */
-	MAILBOX_LIST_FLAG_MAILBOX_FILES		= 0x10,
-	/* Flush NFS attribute cache when needed */
-	MAILBOX_LIST_FLAG_NFS_FLUSH		= 0x20
+	MAILBOX_LIST_FLAG_MAILBOX_FILES		= 0x01
 };
 
 enum mailbox_info_flags {
--- a/src/lib/array-decl.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/lib/array-decl.h	Tue Jan 27 18:21:53 2009 -0500
@@ -17,5 +17,6 @@
 ARRAY_DEFINE_TYPE(string, char *);
 ARRAY_DEFINE_TYPE(const_string, const char *);
 ARRAY_DEFINE_TYPE(uint32_t, uint32_t);
+ARRAY_DEFINE_TYPE(void_array, void *);
 
 #endif
--- a/src/login-common/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -2,14 +2,17 @@
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/src/lib \
+	-I$(top_srcdir)/src/lib-settings \
 	-I$(top_srcdir)/src/lib-auth \
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_STATEDIR=\""$(statedir)"\" \
-	-DSBINDIR=\""$(sbindir)"\"
+	-DSBINDIR=\""$(sbindir)"\" \
+	-DSSLDIR=\""$(ssldir)\""
 
 liblogin_common_a_SOURCES = \
 	client-common.c \
 	login-proxy.c \
+	login-settings.c \
 	main.c \
 	master.c \
 	sasl-server.c \
@@ -20,6 +23,7 @@
 noinst_HEADERS = \
 	client-common.h \
 	login-proxy.h \
+	login-settings.h \
 	common.h \
 	master.h \
 	sasl-server.h \
--- a/src/login-common/client-common.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/client-common.c	Tue Jan 27 18:21:53 2009 -0500
@@ -128,7 +128,7 @@
 	memcpy(tab, static_tab, sizeof(static_tab));
 
 	str = t_str_new(256);
-	for (e = log_format_elements; *e != NULL; e++) {
+	for (e = login_settings->log_format_elements_split; *e != NULL; e++) {
 		for (p = *e; *p != '\0'; p++) {
 			if (*p != '%' || p[1] == '\0')
 				continue;
@@ -147,7 +147,7 @@
 	tab[1].value = msg;
 	str_truncate(str, 0);
 
-	var_expand(str, log_format, tab);
+	var_expand(str, login_settings->login_log_format, tab);
 	return str_c(str);
 }
 
@@ -171,10 +171,10 @@
 	struct ip_addr net_ip;
 	unsigned int bits;
 
-	if (trusted_networks == NULL)
+	if (login_settings->login_trusted_networks == NULL)
 		return FALSE;
 
-	net = t_strsplit_spaces(trusted_networks, ", ");
+	net = t_strsplit_spaces(login_settings->login_trusted_networks, ", ");
 	for (; *net != NULL; net++) {
 		if (net_parse_range(*net, &net_ip, &bits) < 0) {
 			i_error("login_trusted_networks: "
@@ -190,7 +190,7 @@
 
 const char *client_get_extra_disconnect_reason(struct client *client)
 {
-	if (ssl_require_client_cert && client->proxy != NULL) {
+	if (login_settings->ssl_require_client_cert && client->proxy != NULL) {
 		if (ssl_proxy_has_broken_client_cert(client->proxy))
 			return "(client sent an invalid cert)";
 		if (!ssl_proxy_has_valid_client_cert(client->proxy))
@@ -203,7 +203,7 @@
 	/* some auth attempts without SSL/TLS */
 	if (client->auth_tried_disabled_plaintext)
 		return "(tried to use disabled plaintext auth)";
-	if (ssl_require_client_cert)
+	if (login_settings->ssl_require_client_cert)
 		return "(cert required, client didn't start TLS)";
 
 	return t_strdup_printf("(auth failed, %u attempts)",
--- a/src/login-common/common.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/common.h	Tue Jan 27 18:21:53 2009 -0500
@@ -2,6 +2,7 @@
 #define COMMON_H
 
 #include "lib.h"
+#include "login-settings.h"
 
 /* Used only for string sanitization */
 #define MAX_MECH_NAME 64
@@ -13,17 +14,11 @@
 
 extern const char *login_protocol;
 
-extern bool disable_plaintext_auth, process_per_connection;
-extern bool verbose_proctitle, verbose_ssl, verbose_auth, auth_debug;
-extern bool ssl_required, ssl_require_client_cert;
-extern const char *greeting, *log_format;
-extern const char *const *log_format_elements;
-extern const char *capability_string;
-extern const char *trusted_networks;
-extern unsigned int max_connections;
-extern unsigned int login_process_uid;
 extern struct auth_client *auth_client;
 extern bool closing_down;
+extern unsigned int login_process_uid;
+
+extern struct login_settings *login_settings;
 
 void main_ref(void);
 void main_unref(void);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/login-settings.c	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,184 @@
+/* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "settings-parser.h"
+#include "login-settings.h"
+
+#include <stddef.h>
+#include <unistd.h>
+
+#undef DEF
+#define DEF(type, name) \
+	{ type, #name, offsetof(struct login_settings, name), NULL }
+
+static struct setting_define login_setting_defines[] = {
+	DEF(SET_STR, login_dir),
+	DEF(SET_BOOL, login_chroot),
+	DEF(SET_STR, login_trusted_networks),
+	DEF(SET_STR, login_greeting),
+	DEF(SET_STR, login_log_format_elements),
+	DEF(SET_STR, login_log_format),
+
+	DEF(SET_BOOL, login_process_per_connection),
+	DEF(SET_STR, capability_string),
+
+	DEF(SET_ENUM, ssl),
+	DEF(SET_STR, ssl_ca_file),
+	DEF(SET_STR, ssl_cert_file),
+	DEF(SET_STR, ssl_key_file),
+	DEF(SET_STR, ssl_key_password),
+	DEF(SET_STR, ssl_parameters_file),
+	DEF(SET_STR, ssl_cipher_list),
+	DEF(SET_STR, ssl_cert_username_field),
+	DEF(SET_BOOL, ssl_verify_client_cert),
+	DEF(SET_BOOL, ssl_require_client_cert),
+	DEF(SET_BOOL, ssl_username_from_cert),
+	DEF(SET_BOOL, verbose_ssl),
+
+	DEF(SET_BOOL, disable_plaintext_auth),
+	DEF(SET_BOOL, verbose_auth),
+	DEF(SET_BOOL, auth_debug),
+	DEF(SET_BOOL, verbose_proctitle),
+
+	DEF(SET_UINT, login_max_connections),
+
+	SETTING_DEFINE_LIST_END
+};
+
+static struct login_settings login_default_settings = {
+	MEMBER(login_dir) "login",
+	MEMBER(login_chroot) TRUE,
+	MEMBER(login_trusted_networks) "",
+	MEMBER(login_greeting) PACKAGE" ready.",
+	MEMBER(login_log_format_elements) "user=<%u> method=%m rip=%r lip=%l %c",
+	MEMBER(login_log_format) "%$: %s",
+
+	MEMBER(login_process_per_connection) TRUE,
+	MEMBER(capability_string) NULL,
+
+	MEMBER(ssl) "yes:no:required",
+	MEMBER(ssl_ca_file) "",
+	MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem",
+	MEMBER(ssl_key_file) SSLDIR"/private/dovecot.pem",
+	MEMBER(ssl_key_password) "",
+	MEMBER(ssl_parameters_file) "ssl-parameters.dat",
+	MEMBER(ssl_cipher_list) "ALL:!LOW:!SSLv2",
+	MEMBER(ssl_cert_username_field) "commonName",
+	MEMBER(ssl_verify_client_cert) FALSE,
+	MEMBER(ssl_require_client_cert) FALSE,
+	MEMBER(ssl_username_from_cert) FALSE,
+	MEMBER(verbose_ssl) FALSE,
+
+	MEMBER(disable_plaintext_auth) TRUE,
+	MEMBER(verbose_auth) FALSE,
+	MEMBER(auth_debug) FALSE,
+	MEMBER(verbose_proctitle) FALSE,
+
+	MEMBER(login_max_connections) 256
+};
+
+struct setting_parser_info login_setting_parser_info = {
+	MEMBER(defines) login_setting_defines,
+	MEMBER(defaults) &login_default_settings,
+
+	MEMBER(parent) NULL,
+	MEMBER(dynamic_parsers) NULL,
+
+	MEMBER(parent_offset) (size_t)-1,
+	MEMBER(type_offset) (size_t)-1,
+	MEMBER(struct_size) sizeof(struct login_settings)
+};
+
+static pool_t settings_pool = NULL;
+
+static int ssl_settings_check(struct login_settings *set ATTR_UNUSED)
+{
+#ifndef HAVE_SSL
+        i_error("SSL support not compiled in but ssl_disable=no");
+	return FALSE;
+#else
+	if (*set->ssl_cert_file == '\0') {
+		i_error("ssl_cert_file not set");
+		return FALSE;
+	}
+	if (access(set->ssl_cert_file, R_OK) < 0) {
+		i_error("ssl_cert_file: access(%s) failed: %m",
+			set->ssl_cert_file);
+		return FALSE;
+	}
+
+	if (*set->ssl_key_file == '\0') {
+		i_error("ssl_key_file not set");
+		return FALSE;
+	}
+	if (access(set->ssl_key_file, R_OK) < 0) {
+		i_error("ssl_key_file: access(%s) failed: %m",
+			set->ssl_key_file);
+		return FALSE;
+	}
+
+	if (*set->ssl_ca_file != '\0' &&
+	    access(set->ssl_ca_file, R_OK) < 0) {
+		i_error("ssl_ca_file: access(%s) failed: %m",
+			set->ssl_ca_file);
+		return FALSE;
+	}
+
+	if (set->ssl_verify_client_cert && *set->ssl_ca_file == '\0') {
+		i_error("ssl_verify_client_cert set, but ssl_ca_file not");
+		return FALSE;
+	}
+	return TRUE;
+#endif
+}
+
+static void login_settings_check(struct login_settings *set)
+{
+	if (strcmp(set->ssl, "no") == 0) {
+		/* disabled */
+	} else if (strcmp(set->ssl, "yes") == 0) {
+		if (!ssl_settings_check(set))
+			set->ssl = "no";
+	} else if (strcmp(set->ssl, "required") == 0) {
+		if (!ssl_settings_check(set))
+			i_fatal("Couldn't initialize ssl with ssl=required");
+		set->disable_plaintext_auth = TRUE;
+	} else {
+		i_fatal("Unknown ssl setting value: %s", set->ssl);
+	}
+
+	set->log_format_elements_split =
+		t_strsplit(set->login_log_format_elements, " ");
+
+	if (set->ssl_require_client_cert || set->ssl_username_from_cert) {
+		/* if we require valid cert, make sure we also ask for it */
+		set->ssl_verify_client_cert = TRUE;
+	}
+	if (set->login_max_connections < 1)
+		i_fatal("login_max_connections must be at least 1");
+}
+
+struct login_settings *login_settings_read(void)
+{
+	struct setting_parser_context *parser;
+        struct login_settings *set;
+
+	if (settings_pool == NULL)
+		settings_pool = pool_alloconly_create("settings pool", 512);
+	else
+		p_clear(settings_pool);
+
+	parser = settings_parser_init(settings_pool,
+				      &login_setting_parser_info,
+				      SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+
+	if (settings_parse_environ(parser) < 0) {
+		i_fatal("Error reading configuration: %s",
+			settings_parser_get_error(parser));
+	}
+
+	set = settings_parser_get(parser);
+	settings_parser_deinit(&parser);
+	login_settings_check(set);
+	return set;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/login-common/login-settings.h	Tue Jan 27 18:21:53 2009 -0500
@@ -0,0 +1,40 @@
+#ifndef __LOGIN_SETTINGS_H
+#define __LOGIN_SETTINGS_H
+
+struct login_settings {
+	const char *login_dir;
+	bool login_chroot;
+	const char *login_trusted_networks;
+	const char *login_greeting;
+	const char *login_log_format_elements, *login_log_format;
+
+	bool login_process_per_connection;
+	const char *capability_string;
+
+	const char *ssl;
+	const char *ssl_ca_file;
+	const char *ssl_cert_file;
+	const char *ssl_key_file;
+	const char *ssl_key_password;
+	const char *ssl_parameters_file;
+	const char *ssl_cipher_list;
+	const char *ssl_cert_username_field;
+	bool ssl_verify_client_cert;
+	bool ssl_require_client_cert;
+	bool ssl_username_from_cert;
+	bool verbose_ssl;
+
+	bool disable_plaintext_auth;
+	bool verbose_auth;
+	bool auth_debug;
+	bool verbose_proctitle;
+
+	unsigned int login_max_connections;
+
+	/* generated: */
+	const char *const *log_format_elements_split;
+};
+
+struct login_settings *login_settings_read(void);
+
+#endif
--- a/src/login-common/main.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/main.c	Tue Jan 27 18:21:53 2009 -0500
@@ -19,13 +19,7 @@
 #include <unistd.h>
 #include <syslog.h>
 
-bool disable_plaintext_auth, process_per_connection;
-bool verbose_proctitle, verbose_ssl, verbose_auth, auth_debug;
-bool ssl_required, ssl_require_client_cert;
-const char *greeting, *log_format;
-const char *const *log_format_elements;
-const char *trusted_networks;
-unsigned int max_connections;
+struct login_settings *login_settings;
 unsigned int login_process_uid;
 struct auth_client *auth_client;
 bool closing_down;
@@ -101,7 +95,7 @@
 	client->remote_port = remote_port;
 	client->local_port = local_port;
 
-	if (process_per_connection) {
+	if (login_settings->login_process_per_connection) {
 		closing_down = TRUE;
 		main_listen_stop();
 	}
@@ -139,7 +133,7 @@
 		client->local_port = local_port;
 	}
 
-	if (process_per_connection) {
+	if (login_settings->login_process_per_connection) {
 		closing_down = TRUE;
 		main_listen_stop();
 	}
@@ -161,7 +155,7 @@
 	}
 
 	current_count = ssl_proxy_get_count() + login_proxy_get_count();
-	if (current_count >= max_connections) {
+	if (current_count >= login_settings->login_max_connections) {
 		/* can't accept any more connections until existing proxies
 		   get destroyed */
 		return;
@@ -220,9 +214,10 @@
 
 void connection_queue_add(unsigned int connection_count)
 {
+	unsigned int max_connections = login_settings->login_max_connections;
 	unsigned int current_count;
 
-	if (process_per_connection)
+	if (login_settings->login_process_per_connection)
 		return;
 
 	current_count = clients_get_count() + ssl_proxy_get_count() +
@@ -256,6 +251,8 @@
 {
 	const char *value;
 
+        login_settings = login_settings_read();
+
 	if (!is_inetd)
 		i_set_failure_internal();
 	else {
@@ -271,6 +268,7 @@
 	if (chdir(value) < 0)
 		i_error("chdir(%s) failed: %m", value);
 
+
 	/* Initialize SSL proxy so it can read certificate and private
 	   key file. */
 	random_init();
@@ -280,14 +278,13 @@
 	listen_count = value == NULL ? 0 : atoi(value);
 	value = getenv("SSL_LISTEN_FDS");
 	ssl_listen_count = value == NULL ? 0 : atoi(value);
-	value = getenv("MAX_CONNECTIONS");
-	max_connections = value == NULL ? 1 : strtoul(value, NULL, 10);
 
 	/* set the number of fds we want to use. it may get increased or
 	   decreased. leave a couple of extra fds for auth sockets and such.
 	   normal connections each use one fd, but SSL connections use two */
 	*max_fds_r = LOGIN_MASTER_SOCKET_FD + 16 +
-		listen_count + ssl_listen_count + max_connections*2;
+		listen_count + ssl_listen_count +
+		login_settings->login_max_connections*2;
 	restrict_fd_limit(*max_fds_r);
 
 	/* Refuse to run as root - we should never need it and it's
@@ -314,31 +311,6 @@
         lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
         lib_signals_ignore(SIGPIPE, TRUE);
 
-	process_per_connection = getenv("PROCESS_PER_CONNECTION") != NULL;
-	verbose_proctitle = getenv("VERBOSE_PROCTITLE") != NULL;
-        verbose_ssl = getenv("VERBOSE_SSL") != NULL;
-        verbose_auth = getenv("VERBOSE_AUTH") != NULL;
-        auth_debug = getenv("AUTH_DEBUG") != NULL;
-	ssl_required = getenv("SSL_REQUIRED") != NULL;
-	ssl_require_client_cert = getenv("SSL_REQUIRE_CLIENT_CERT") != NULL;
-	disable_plaintext_auth = ssl_required ||
-		getenv("DISABLE_PLAINTEXT_AUTH") != NULL;
-
-	greeting = getenv("GREETING");
-	if (greeting == NULL)
-		greeting = PACKAGE" ready.";
-
-	value = getenv("LOG_FORMAT_ELEMENTS");
-	if (value == NULL)
-		value = "user=<%u> method=%m rip=%r lip=%l %c : %$";
-	log_format_elements = t_strsplit(value, " ");
-
-	log_format = getenv("LOG_FORMAT");
-	if (log_format == NULL)
-		log_format = "%$: %s";
-
-	trusted_networks = getenv("TRUSTED_NETWORKS");
-
 	value = getenv("PROCESS_UID");
 	if (value == NULL)
 		i_fatal("BUG: PROCESS_UID environment not given");
@@ -346,11 +318,6 @@
 	if (login_process_uid == 0)
 		i_fatal("BUG: PROCESS_UID environment is 0");
 
-	/* capability default is set in imap/pop3-login */
-	value = getenv("CAPABILITY_STRING");
-	if (value != NULL && *value != '\0')
-		capability_string = value;
-
         closing_down = FALSE;
 	main_refcount = 0;
 
--- a/src/login-common/master.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/master.c	Tue Jan 27 18:21:53 2009 -0500
@@ -39,7 +39,7 @@
 {
 	struct client *client;
 
-	if (reply->tag == 0 && !process_per_connection) {
+	if (reply->tag == 0 && !login_settings->login_process_per_connection) {
 		/* this means we have to start listening again.
 		   we've reached maximum number of login processes. */
 		main_listen_start();
--- a/src/login-common/sasl-server.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/sasl-server.c	Tue Jan 27 18:21:53 2009 -0500
@@ -149,7 +149,7 @@
 		return;
 	}
 
-	if (!client->secured && disable_plaintext_auth &&
+	if (!client->secured && login_settings->disable_plaintext_auth &&
 	    (mech->flags & MECH_SEC_PLAINTEXT) != 0) {
 		sasl_server_auth_failed(client,
 			"Plaintext authentication disabled.");
@@ -182,7 +182,7 @@
 {
 	i_assert(client->authenticating);
 
-	if (verbose_auth && reason != NULL) {
+	if (login_settings->verbose_auth && reason != NULL) {
 		const char *auth_name =
 			str_sanitize(client->auth_mech_name, MAX_MECH_NAME);
 		client_syslog(client,
--- a/src/login-common/ssl-proxy-openssl.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/login-common/ssl-proxy-openssl.c	Tue Jan 27 18:21:53 2009 -0500
@@ -6,6 +6,7 @@
 #include "network.h"
 #include "ostream.h"
 #include "read-full.h"
+#include "safe-memset.h"
 #include "llist.h"
 #include "ssl-proxy.h"
 
@@ -22,7 +23,6 @@
 #include <openssl/err.h>
 #include <openssl/rand.h>
 
-#define DOVECOT_SSL_DEFAULT_CIPHER_LIST "ALL:!LOW:!SSLv2"
 /* Check every 30 minutes if parameters file has been updated */
 #define SSL_PARAMFILE_CHECK_INTERVAL (60*30)
 
@@ -679,7 +679,8 @@
 	proxy = SSL_get_ex_data(ssl, extdata_index);
 	proxy->cert_received = TRUE;
 
-	if (verbose_ssl || (verbose_auth && !preverify_ok)) {
+	if (login_settings->verbose_ssl ||
+	    (login_settings->verbose_auth && !preverify_ok)) {
 		char buf[1024];
 		X509_NAME *subject;
 
@@ -755,23 +756,13 @@
 void ssl_proxy_init(void)
 {
 	static char dovecot[] = "dovecot";
-	const char *cafile, *certfile, *keyfile, *cipher_list, *username_field;
+	const struct login_settings *set = login_settings;
+	unsigned char buf;
 	char *password;
-	unsigned char buf;
 	unsigned long err;
 
-	memset(&ssl_params, 0, sizeof(ssl_params));
-
-	cafile = getenv("SSL_CA_FILE");
-	certfile = getenv("SSL_CERT_FILE");
-	keyfile = getenv("SSL_KEY_FILE");
-	ssl_params.fname = getenv("SSL_PARAM_FILE");
-	password = getenv("SSL_KEY_PASSWORD");
-
-	if (certfile == NULL || keyfile == NULL || ssl_params.fname == NULL) {
-		/* SSL support is disabled */
+	if (strcmp(set->ssl, "no") == 0)
 		return;
-	}
 
 	CRYPTO_set_mem_functions(ssl_clean_malloc, ssl_clean_realloc,
 				 ssl_clean_free);
@@ -785,55 +776,56 @@
 
 	SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL);
 
-	cipher_list = getenv("SSL_CIPHER_LIST");
-	if (cipher_list == NULL)
-		cipher_list = DOVECOT_SSL_DEFAULT_CIPHER_LIST;
-	if (SSL_CTX_set_cipher_list(ssl_ctx, cipher_list) != 1) {
+	if (SSL_CTX_set_cipher_list(ssl_ctx, set->ssl_cipher_list) != 1) {
 		i_fatal("Can't set cipher list to '%s': %s",
-			cipher_list, ssl_last_error());
+			set->ssl_cipher_list, ssl_last_error());
 	}
 
-	if (cafile != NULL) {
-		if (SSL_CTX_load_verify_locations(ssl_ctx, cafile, NULL) != 1) {
+	if (*set->ssl_ca_file != '\0') {
+		if (SSL_CTX_load_verify_locations(ssl_ctx, set->ssl_ca_file,
+						  NULL) != 1) {
 			i_fatal("Can't load CA file %s: %s",
-				cafile, ssl_last_error());
+				set->ssl_ca_file, ssl_last_error());
 		}
 	}
 
-	if (SSL_CTX_use_certificate_chain_file(ssl_ctx, certfile) != 1) {
+	if (SSL_CTX_use_certificate_chain_file(ssl_ctx,
+					       set->ssl_cert_file) != 1) {
 		err = ERR_peek_error();
 		if (ERR_GET_LIB(err) != ERR_LIB_PEM ||
 		    ERR_GET_REASON(err) != PEM_R_NO_START_LINE) {
 			i_fatal("Can't load certificate file %s: %s",
-				certfile, ssl_last_error());
-		} else if (is_pem_key_file(certfile)) {
+			set->ssl_cert_file, ssl_last_error());
+		} else if (is_pem_key_file(set->ssl_cert_file)) {
 			i_fatal("Can't load certificate file %s: "
 				"The file contains a private key "
 				"(you've mixed ssl_cert_file and ssl_key_file settings)",
-				certfile);
+				set->ssl_cert_file);
 		} else {
 			i_fatal("Can't load certificate file %s: "
 				"The file doesn't contain a certificate.",
-				certfile);
+				set->ssl_cert_file);
 		}
 	}
 
+	password = t_strdup_noconst(set->ssl_key_password);
         SSL_CTX_set_default_passwd_cb(ssl_ctx, pem_password_callback);
         SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, password);
-	if (SSL_CTX_use_PrivateKey_file(ssl_ctx, keyfile,
+	if (SSL_CTX_use_PrivateKey_file(ssl_ctx, set->ssl_key_file,
 					SSL_FILETYPE_PEM) != 1) {
 		i_fatal("Can't load private key file %s: %s",
-			keyfile, ssl_last_error());
+			set->ssl_key_file, ssl_last_error());
 	}
+	safe_memset(password, 0, strlen(password));
 
 	if (SSL_CTX_need_tmp_RSA(ssl_ctx))
 		SSL_CTX_set_tmp_rsa_callback(ssl_ctx, ssl_gen_rsa_key);
 	SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_tmp_dh_callback);
 
-	if (verbose_ssl)
+	if (set->verbose_ssl)
 		SSL_CTX_set_info_callback(ssl_ctx, ssl_info_callback);
 
-	if (getenv("SSL_VERIFY_CLIENT_CERT") != NULL) {
+	if (set->ssl_verify_client_cert) {
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
 		X509_STORE *store;
 
@@ -845,18 +837,13 @@
 				   SSL_VERIFY_CLIENT_ONCE,
 				   ssl_verify_client_cert);
 		SSL_CTX_set_client_CA_list(ssl_ctx,
-					   SSL_load_client_CA_file(cafile));
+			SSL_load_client_CA_file(set->ssl_ca_file));
 	}
 
-	username_field = getenv("SSL_CERT_USERNAME_FIELD");
-	if (username_field == NULL)
-		ssl_username_nid = NID_commonName;
-	else {
-		ssl_username_nid = OBJ_txt2nid(username_field);
-		if (ssl_username_nid == NID_undef) {
-			i_fatal("Invalid ssl_cert_username_field: %s",
-				username_field);
-		}
+	ssl_username_nid = OBJ_txt2nid(set->ssl_cert_username_field);
+	if (ssl_username_nid == NID_undef) {
+		i_fatal("Invalid ssl_cert_username_field: %s",
+			set->ssl_cert_username_field);
 	}
 
 	/* PRNG initialization might want to use /dev/urandom, make sure it
--- a/src/master/Makefile.am	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/Makefile.am	Tue Jan 27 18:21:53 2009 -0500
@@ -10,6 +10,7 @@
 	-DPKG_RUNDIR=\""$(rundir)"\" \
 	-DPKG_STATEDIR=\""$(statedir)"\" \
 	-DPKG_LIBEXECDIR=\""$(pkglibexecdir)"\" \
+	-DBINDIR=\""$(bindir)"\" \
 	-DMODULEDIR=\""$(moduledir)"\" \
 	-DSSLDIR=\""$(ssldir)\""
 
@@ -29,8 +30,8 @@
 	log.c \
 	login-process.c \
 	mail-process.c \
+	master-settings.c \
 	main.c \
-	master-settings.c \
 	syslog-util.c \
 	ssl-init.c \
 	sysinfo-get.c
@@ -53,9 +54,6 @@
 	ssl-init.h \
 	sysinfo-get.h
 
-EXTRA_DIST = \
-	master-settings-defs.c
-
 ssl_build_param_SOURCES = \
 	ssl-init-main.c \
 	ssl-init-openssl.c \
--- a/src/master/auth-process.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/auth-process.c	Tue Jan 27 18:21:53 2009 -0500
@@ -1,6 +1,7 @@
 /* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
 #include "common.h"
+#include "array.h"
 #include "hash.h"
 #include "ioloop.h"
 #include "env-util.h"
@@ -30,7 +31,8 @@
 	struct auth_process_group *next;
 
 	int listen_fd;
-	struct auth_settings *set;
+	const struct master_settings *master_set;
+	const struct master_auth_settings *set;
 
 	unsigned int process_count;
 	struct auth_process *processes;
@@ -323,7 +325,7 @@
 	path = t_strdup_printf("%s/auth-worker.%s",
 			       *group->set->chroot != '\0' ?
 			       group->set->chroot :
-			       group->set->parent->defaults->base_dir,
+			       group->master_set->base_dir,
 			       dec2str(pid));
 	p->worker_listen_fd =
 		unix_socket_create(path, 0600, group->set->uid,
@@ -371,7 +373,7 @@
 	path = t_strdup_printf("%s/auth-worker.%s",
 			       *p->group->set->chroot != '\0' ?
 			       p->group->set->chroot :
-			       p->group->set->parent->defaults->base_dir,
+			       p->group->master_set->base_dir,
 			       dec2str(p->pid));
 	(void)unlink(path);
 
@@ -393,21 +395,6 @@
 	i_free(p);
 }
 
-static void
-socket_settings_env_put(const char *env_base, struct socket_settings *set)
-{
-	if (!set->used)
-		return;
-
-	env_put(t_strdup_printf("%s=%s", env_base, set->path));
-	if (set->mode != 0)
-		env_put(t_strdup_printf("%s_MODE=%o", env_base, set->mode));
-	if (*set->user != '\0')
-		env_put(t_strdup_printf("%s_USER=%s", env_base, set->user));
-	if (*set->group != '\0')
-		env_put(t_strdup_printf("%s_GROUP=%s", env_base, set->group));
-}
-
 static int connect_auth_socket(struct auth_process_group *group,
 			       const char *path)
 {
@@ -427,13 +414,10 @@
 	return 0;
 }
 
-static void auth_set_environment(struct auth_settings *set)
+static void auth_set_environment(const struct master_settings *master_set,
+				 const struct master_auth_settings *set)
 {
-	struct auth_socket_settings *as;
-	struct auth_passdb_settings *ap;
-	struct auth_userdb_settings *au;
-	const char *str;
-	int i;
+	master_settings_export_to_env(master_set);
 
 	/* setup access environment */
 	restrict_access_set_env(set->user, set->uid, set->gid,
@@ -442,93 +426,42 @@
 	/* set other environment */
 	env_put("DOVECOT_MASTER=1");
 	env_put(t_strconcat("AUTH_NAME=", set->name, NULL));
-	env_put(t_strconcat("MECHANISMS=", set->mechanisms, NULL));
-	env_put(t_strconcat("REALMS=", set->realms, NULL));
-	env_put(t_strconcat("DEFAULT_REALM=", set->default_realm, NULL));
-	env_put(t_strconcat("USERNAME_CHARS=", set->username_chars, NULL));
-	env_put(t_strconcat("ANONYMOUS_USERNAME=",
-			    set->anonymous_username, NULL));
-	env_put(t_strconcat("USERNAME_TRANSLATION=",
-			    set->username_translation, NULL));
-	env_put(t_strconcat("USERNAME_FORMAT=", set->username_format, NULL));
-	env_put(t_strconcat("MASTER_USER_SEPARATOR=",
-			    set->master_user_separator, NULL));
-	env_put(t_strdup_printf("CACHE_SIZE=%u", set->cache_size));
-	env_put(t_strdup_printf("CACHE_TTL=%u", set->cache_ttl));
-	env_put(t_strdup_printf("CACHE_NEGATIVE_TTL=%u",
-				set->cache_negative_ttl));
+	restrict_process_size(set->process_size, (unsigned int)-1);
+}
 
-	for (ap = set->passdbs, i = 1; ap != NULL; ap = ap->next, i++) {
-		env_put(t_strdup_printf("PASSDB_%u_DRIVER=%s", i, ap->driver));
-		if (ap->args != NULL) {
-			env_put(t_strdup_printf("PASSDB_%u_ARGS=%s",
-						i, ap->args));
-		}
-		if (ap->deny)
-			env_put(t_strdup_printf("PASSDB_%u_DENY=1", i));
-                if (ap->pass)
-                        env_put(t_strdup_printf("PASSDB_%u_PASS=1", i));
-		if (ap->master)
-                        env_put(t_strdup_printf("PASSDB_%u_MASTER=1", i));
-	}
-	for (au = set->userdbs, i = 1; au != NULL; au = au->next, i++) {
-		env_put(t_strdup_printf("USERDB_%u_DRIVER=%s", i, au->driver));
-		if (au->args != NULL) {
-			env_put(t_strdup_printf("USERDB_%u_ARGS=%s",
-						i, au->args));
-		}
-	}
+static const struct master_auth_socket_settings *
+get_connect_socket(const struct master_auth_settings *auth_set)
+{
+	struct master_auth_socket_settings *const *as;
+	unsigned int count;
 
-	for (as = set->sockets, i = 1; as != NULL; as = as->next, i++) {
-		if (strcmp(as->type, "listen") != 0)
-			continue;
-
-		str = t_strdup_printf("AUTH_%u", i);
-		socket_settings_env_put(str, &as->client);
-		socket_settings_env_put(t_strconcat(str, "_MASTER", NULL),
-					&as->master);
-	}
+	if (!array_is_created(&auth_set->sockets))
+		return NULL;
 
-	if (set->verbose)
-		env_put("VERBOSE=1");
-	if (set->debug)
-		env_put("VERBOSE_DEBUG=1");
-	if (set->debug_passwords)
-		env_put("VERBOSE_DEBUG_PASSWORDS=1");
-	if (set->ssl_require_client_cert)
-		env_put("SSL_REQUIRE_CLIENT_CERT=1");
-	if (set->ssl_username_from_cert)
-		env_put("SSL_USERNAME_FROM_CERT=1");
-	if (set->use_winbind)
-		env_put("USE_WINBIND=1");
-	if (*set->krb5_keytab != '\0') {
-		/* Environment may be used by Kerberos 5 library directly,
-		   although we also try to use it directly as well */
-		env_put(t_strconcat("KRB5_KTNAME=", set->krb5_keytab, NULL));
-	}
-	if (*set->gssapi_hostname != '\0') {
-		env_put(t_strconcat("GSSAPI_HOSTNAME=",
-				    set->gssapi_hostname, NULL));
-	}
-	env_put(t_strconcat("WINBIND_HELPER_PATH=",
-			    set->winbind_helper_path, NULL));
-	env_put(t_strdup_printf("FAILURE_DELAY=%u", set->failure_delay));
-
-	restrict_process_size(set->process_size, (unsigned int)-1);
+	as = array_get(&auth_set->sockets, &count);
+	if (count > 0 && strcmp(as[0]->type, "connect") == 0)
+		return as[0];
+	else
+		return NULL;
 }
 
 static int create_auth_process(struct auth_process_group *group)
 {
-	struct auth_socket_settings *as;
+	const struct master_auth_socket_settings *as;
+	struct master_auth_socket_unix_settings *const *masters;
 	const char *prefix, *executable;
 	struct log_io *log;
 	pid_t pid;
+	unsigned int count;
 	int fd[2], log_fd, i;
 
 	/* see if this is a connect socket */
-	as = group->set->sockets;
-	if (as != NULL && strcmp(as->type, "connect") == 0)
-		return connect_auth_socket(group, as->master.path);
+	as = get_connect_socket(group->set);
+	if (as != NULL) {
+		masters = array_get(&as->masters, &count);
+		if (count > 0)
+			return connect_auth_socket(group, masters[0]->path);
+	}
 
 	/* create communication to process with a socket pair */
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
@@ -583,7 +516,7 @@
 	if (dup2(log_fd, 2) < 0)
 		i_fatal("dup2(stderr) failed: %m");
 
-	child_process_init_env();
+	child_process_init_env(group->master_set);
 
 	if (group->listen_fd != 3) {
 		if (dup2(group->listen_fd, 3) < 0)
@@ -594,14 +527,12 @@
 	for (i = 0; i <= 2; i++)
 		fd_close_on_exec(i, FALSE);
 
-        auth_set_environment(group->set);
+        auth_set_environment(group->master_set, group->set);
 
 	env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
 				*group->set->chroot != '\0' ? "" :
-				group->set->parent->defaults->base_dir,
+				group->master_set->base_dir,
 				dec2str(getpid())));
-	env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u",
-				group->set->worker_max_count));
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
@@ -666,8 +597,8 @@
 		fd_close_on_exec(i, FALSE);
 	fd_close_on_exec(4, FALSE);
 
-	child_process_init_env();
-        auth_set_environment(process->group->set);
+	child_process_init_env(process->group->master_set);
+        auth_set_environment(process->group->master_set, process->group->set);
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
@@ -694,25 +625,25 @@
 	return NULL;
 }
 
-static void auth_process_group_create(struct auth_settings *auth_set)
+static void auth_process_group_create(struct master_settings *set,
+				      struct master_auth_settings *auth_set)
 {
 	struct auth_process_group *group;
 	const char *path;
 
 	group = i_new(struct auth_process_group, 1);
+	group->master_set = set;
 	group->set = auth_set;
 
 	group->next = process_groups;
 	process_groups = group;
 
-	if (auth_set->sockets != NULL &&
-	    strcmp(auth_set->sockets->type, "connect") == 0)
+	if (get_connect_socket(auth_set) != NULL)
 		return;
 
-	path = t_strconcat(auth_set->parent->defaults->login_dir, "/",
-			   auth_set->name, NULL);
+	path = t_strconcat(set->login_dir, "/", auth_set->name, NULL);
 	group->listen_fd = unix_socket_create(path, 0660, master_uid,
-					      auth_set->parent->login_gid, 128);
+					      set->server->login_gid, 128);
 	if (group->listen_fd == -1)
 		i_fatal("Couldn't create auth process listener");
 
@@ -731,7 +662,7 @@
                 group->processes = next;
 	}
 
-	path = t_strconcat(group->set->parent->defaults->login_dir, "/",
+	path = t_strconcat(group->master_set->login_dir, "/",
 			   group->set->name, NULL);
 	(void)unlink(path);
 
@@ -753,17 +684,14 @@
 	have_initialized_auth_processes = FALSE;
 }
 
-static void auth_process_groups_create(struct server_settings *server)
+static void auth_process_groups_create(struct master_settings *set)
 {
-	struct auth_settings *auth_set;
+	struct master_auth_settings *const *auth_sets;
+	unsigned int i, count;
 
-	while (server != NULL) {
-		auth_set = server->auths;
-		for (; auth_set != NULL; auth_set = auth_set->next)
-			auth_process_group_create(auth_set);
-
-                server = server->next;
-	}
+	auth_sets = array_get(&set->auths, &count);
+	for (i = 0; i < count; i++)
+		auth_process_group_create(set, auth_sets[i]);
 }
 
 static void auth_processes_stall(void)
@@ -787,7 +715,7 @@
 
 	if (process_groups == NULL) {
 		/* first time here, create the groups */
-		auth_process_groups_create(settings_root);
+		auth_process_groups_create(master_set->defaults);
 	}
 
 	for (group = process_groups; group != NULL; group = group->next) {
--- a/src/master/child-process.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/child-process.c	Tue Jan 27 18:21:53 2009 -0500
@@ -41,7 +41,7 @@
 	hash_table_remove(processes, POINTER_CAST(pid));
 }
 
-void child_process_init_env(void)
+void child_process_init_env(const struct master_settings *set)
 {
 	int facility;
 
@@ -53,13 +53,12 @@
 	if (env_tz != NULL)
 		env_put(t_strconcat("TZ=", env_tz, NULL));
 
-	if (settings_root == NULL ||
-	    !syslog_facility_find(settings_root->defaults->syslog_facility,
-				  &facility))
+	if (master_set == NULL ||
+	    !syslog_facility_find(set->syslog_facility, &facility))
 		facility = LOG_MAIL;
 	env_put(t_strdup_printf("SYSLOG_FACILITY=%d", facility));
 
-	if (settings_root != NULL && !settings_root->defaults->version_ignore)
+	if (master_set != NULL && !set->version_ignore)
 		env_put("DOVECOT_VERSION="PACKAGE_VERSION);
 #ifdef DEBUG
 	if (gdb) env_put("GDB=1");
--- a/src/master/child-process.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/child-process.h	Tue Jan 27 18:21:53 2009 -0500
@@ -32,7 +32,7 @@
 void child_process_add(pid_t pid, struct child_process *process);
 void child_process_remove(pid_t pid);
 
-void child_process_init_env(void);
+void child_process_init_env(const struct master_settings *set);
 void client_process_exec(const char *cmd, const char *title);
 void client_process_exec_argv(const char *executable, const char **argv);
 
--- a/src/master/dict-process.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/dict-process.c	Tue Jan 27 18:21:53 2009 -0500
@@ -94,17 +94,17 @@
 	for (i = 0; i <= 3; i++)
 		fd_close_on_exec(i, FALSE);
 
-	child_process_init_env();
+	child_process_init_env(master_set->defaults);
 	env_put(t_strconcat("DICT_LISTEN_FROM_FD=",
 			    process->listener->path, NULL));
 
-	if (settings_root->defaults->dict_db_config != NULL) {
+	if (master_set->defaults->dict_db_config != NULL) {
 		env_put(t_strconcat("DB_CONFIG=",
-				    settings_root->defaults->dict_db_config,
+				    master_set->defaults->dict_db_config,
 				    NULL));
 	}
 
-	dicts = array_get(&settings_root->dicts, &count);
+	dicts = array_get(&master_set->defaults->dicts, &count);
 	i_assert((count % 2) == 0);
 	for (i = 0; i < count; i += 2)
 		env_put(t_strdup_printf("DICT_%s=%s", dicts[i], dicts[i+1]));
@@ -146,14 +146,16 @@
 
 static void dict_listener_input(struct dict_listener *listener)
 {
-	unsigned int i;
+	unsigned int i = 0;
 	int fd;
 
 	i_assert(listener->processes == NULL);
 
-	for (i = 0; i < settings_root->defaults->dict_process_count; i++) {
-		if (dict_process_create(listener) < 0)
-			break;
+	if (array_is_created(&master_set->defaults->dicts)) {
+		for (i = 0; i < master_set->defaults->dict_process_count; i++) {
+			if (dict_process_create(listener) < 0)
+				break;
+		}
 	}
 	if (i > 0)
 		io_remove(&listener->io);
@@ -220,7 +222,7 @@
 {
 	const char *path;
 
-	path = t_strconcat(settings_root->defaults->base_dir,
+	path = t_strconcat(master_set->defaults->base_dir,
 			   "/"DICT_SERVER_SOCKET_NAME, NULL);
 	dict_listener = dict_listener_init(path);
 
--- a/src/master/listener.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/listener.c	Tue Jan 27 18:21:53 2009 -0500
@@ -79,7 +79,7 @@
 }
 
 static void
-check_conflicts_set(const struct settings *set, const struct ip_addr *ip,
+check_conflicts_set(const struct master_settings *set, const struct ip_addr *ip,
 		    unsigned int port, const char *name1, const char *name2)
 {
 	const struct listener *listens = NULL;
@@ -115,17 +115,13 @@
 static void check_conflicts(const struct ip_addr *ip, unsigned int port,
 			    const char *proto)
 {
-	struct server_settings *server;
-
-	for (server = settings_root; server != NULL; server = server->next) {
-		if (server->imap != NULL) {
-			check_conflicts_set(server->imap, ip, port,
-					    "imap", proto);
-		}
-		if (server->pop3 != NULL) {
-			check_conflicts_set(server->pop3, ip, port,
-					    "pop3", proto);
-		}
+	if (master_set->imap != NULL) {
+		check_conflicts_set(master_set->imap, ip, port,
+				    "imap", proto);
+	}
+	if (master_set->pop3 != NULL) {
+		check_conflicts_set(master_set->pop3, ip, port,
+				    "pop3", proto);
 	}
 }
 
@@ -200,7 +196,7 @@
 	array_free(listens_arr);
 }
 
-static void listen_parse_and_close_unneeded(struct settings *set)
+static void listen_parse_and_close_unneeded(struct master_settings *set)
 {
 	const char *const *proto;
 	unsigned int default_port;
@@ -246,7 +242,8 @@
 	}
 }
 
-static void listen_copy_old(struct settings *old_set, struct settings *new_set)
+static void listen_copy_old(struct master_settings *old_set,
+			    struct master_settings *new_set)
 {
 	if (old_set == NULL || new_set == NULL) {
 		if (old_set != NULL) {
@@ -317,49 +314,37 @@
 }
 
 static void
-listener_listen_missing(struct settings *set, const char *proto, bool retry)
+listener_listen_missing(struct master_settings *set,
+			const char *proto, bool retry)
 {
 	listener_array_listen_missing(proto, &set->listens, retry);
 	listener_array_listen_missing(t_strconcat(proto, "s", NULL),
 				      &set->ssl_listens, retry);
 }
 
-void listeners_open_fds(struct server_settings *old_set, bool retry)
+void listeners_open_fds(struct master_server_settings *old_set, bool retry)
 {
-	struct server_settings *server;
+	if (old_set != NULL) {
+		listen_copy_old(old_set->imap, master_set->imap);
+		listen_copy_old(old_set->pop3, master_set->pop3);
+	}
+	listen_parse_and_close_unneeded(master_set->imap);
+	listen_parse_and_close_unneeded(master_set->pop3);
 
-	for (server = settings_root; server != NULL; server = server->next) {
-		if (old_set != NULL) {
-			listen_copy_old(old_set->imap, server->imap);
-			listen_copy_old(old_set->pop3, server->pop3);
-		}
-		listen_parse_and_close_unneeded(server->imap);
-		listen_parse_and_close_unneeded(server->pop3);
-
-		if (old_set != NULL)
-			old_set = old_set->next;
-	}
-
-	for (server = settings_root; server != NULL; server = server->next) {
-		if (server->imap != NULL)
-			listener_listen_missing(server->imap, "imap", retry);
-		if (server->pop3 != NULL)
-			listener_listen_missing(server->pop3, "pop3", retry);
-	}
+	if (master_set->imap != NULL)
+		listener_listen_missing(master_set->imap, "imap", retry);
+	if (master_set->pop3 != NULL)
+		listener_listen_missing(master_set->pop3, "pop3", retry);
 }
 
 void listeners_close_fds(void)
 {
-	struct server_settings *server;
-
-	for (server = settings_root; server != NULL; server = server->next) {
-		if (server->imap != NULL) {
-			listener_close_fds(&server->imap->listens);
-			listener_close_fds(&server->imap->ssl_listens);
-		}
-		if (server->pop3 != NULL) {
-			listener_close_fds(&server->pop3->listens);
-			listener_close_fds(&server->pop3->ssl_listens);
-		}
+	if (master_set->imap != NULL) {
+		listener_close_fds(&master_set->imap->listens);
+		listener_close_fds(&master_set->imap->ssl_listens);
+	}
+	if (master_set->pop3 != NULL) {
+		listener_close_fds(&master_set->pop3->listens);
+		listener_close_fds(&master_set->pop3->ssl_listens);
 	}
 }
--- a/src/master/listener.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/listener.h	Tue Jan 27 18:21:53 2009 -0500
@@ -1,7 +1,7 @@
 #ifndef LISTENER_H
 #define LISTENER_H
 
-void listeners_open_fds(struct server_settings *old_set, bool retry);
+void listeners_open_fds(struct master_server_settings *old_set, bool retry);
 void listeners_close_fds(void);
 
 #endif
--- a/src/master/login-process.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/login-process.c	Tue Jan 27 18:21:53 2009 -0500
@@ -16,6 +16,7 @@
 #include "auth-process.h"
 #include "mail-process.h"
 #include "master-login-interface.h"
+#include "master-settings.h"
 #include "log.h"
 #include "ssl-init.h"
 
@@ -65,7 +66,7 @@
 static bool login_process_init_group(struct login_process *p);
 static void login_processes_start_missing(void *context);
 
-static void login_group_create(struct settings *set)
+static void login_group_create(struct master_settings *set)
 {
 	struct login_group *group;
 
@@ -217,18 +218,14 @@
 
 static void login_process_groups_create(void)
 {
-	struct server_settings *server;
-
-	for (server = settings_root; server != NULL; server = server->next) {
-		if (server->imap != NULL)
-			login_group_create(server->imap);
-		if (server->pop3 != NULL)
-			login_group_create(server->pop3);
-	}
+	if (master_set->imap != NULL)
+		login_group_create(master_set->imap);
+	if (master_set->pop3 != NULL)
+		login_group_create(master_set->pop3);
 }
 
 static struct login_group *
-login_group_process_find(const char *name, enum mail_protocol protocol)
+login_group_process_find(enum mail_protocol protocol)
 {
 	struct login_group *group;
 
@@ -236,8 +233,7 @@
                 login_process_groups_create();
 
 	for (group = login_groups; group != NULL; group = group->next) {
-		if (strcmp(group->set->server->name, name) == 0 &&
-		    group->set->protocol == protocol)
+		if (group->set->protocol == protocol)
 			return group;
 	}
 
@@ -247,7 +243,7 @@
 static bool login_process_read_group(struct login_process *p)
 {
 	struct login_group *group;
-	const char *name, *proto;
+	const char *proto;
 	unsigned char buf[256];
 	enum mail_protocol protocol;
 	unsigned int len;
@@ -272,15 +268,7 @@
 	else if (len == 0 || (size_t)ret != len)
 		i_error("login: Server name wasn't sent");
 	else {
-		name = t_strndup(buf, len);
-		proto = strchr(name, '/');
-		if (proto == NULL) {
-			proto = name;
-			name = "default";
-		} else {
-			name = t_strdup_until(name, proto++);
-		}
-
+		proto = t_strndup(buf, len);
 		if (strcmp(proto, "imap") == 0)
 			protocol = MAIL_PROTOCOL_IMAP;
 		else if (strcmp(proto, "pop3") == 0)
@@ -290,9 +278,9 @@
 			return FALSE;
 		}
 
-		group = login_group_process_find(name, protocol);
+		group = login_group_process_find(protocol);
 		if (group == NULL) {
-			i_error("login: Unknown server name '%s'", name);
+			i_error("login: No group for protocol '%s'", proto);
 			return FALSE;
 		}
 
@@ -534,11 +522,9 @@
 
 static void login_process_init_env(struct login_group *group, pid_t pid)
 {
-	struct settings *set = group->set;
-	const struct auth_settings *auth;
-	bool require_cert;
+	struct master_settings *set = group->set;
 
-	child_process_init_env();
+	child_process_init_env(group->set);
 
 	/* setup access environment - needs to be done after
 	   clean_child_process() since it clears environment. Don't set user
@@ -551,79 +537,17 @@
 
 	env_put("DOVECOT_MASTER=1");
 
-	if (strcmp(set->ssl, "no") != 0) {
-		const char *ssl_key_password;
-
-		ssl_key_password = *set->ssl_key_password != '\0' ?
-			set->ssl_key_password : ssl_manual_key_password;
-
-		if (*set->ssl_ca_file != '\0') {
-			env_put(t_strconcat("SSL_CA_FILE=",
-					    set->ssl_ca_file, NULL));
-		}
-		if (strcmp(set->ssl, "required") == 0)
-			env_put("SSL_REQUIRED=1");
-		env_put(t_strconcat("SSL_CERT_FILE=",
-				    set->ssl_cert_file, NULL));
-		env_put(t_strconcat("SSL_KEY_FILE=",
-				    set->ssl_key_file, NULL));
-		env_put(t_strconcat("SSL_KEY_PASSWORD=",
-				    ssl_key_password, NULL));
-		env_put("SSL_PARAM_FILE="SSL_PARAMETERS_FILENAME);
-		if (*set->ssl_cipher_list != '\0') {
-			env_put(t_strconcat("SSL_CIPHER_LIST=",
-					    set->ssl_cipher_list, NULL));
-		}
-		env_put(t_strconcat("SSL_CERT_USERNAME_FIELD=",
-				    set->ssl_cert_username_field, NULL));
-		if (set->ssl_verify_client_cert)
-			env_put("SSL_VERIFY_CLIENT_CERT=1");
-	}
-
-	if (set->disable_plaintext_auth)
-		env_put("DISABLE_PLAINTEXT_AUTH=1");
-	if (set->verbose_proctitle)
-		env_put("VERBOSE_PROCTITLE=1");
-	if (set->verbose_ssl)
-		env_put("VERBOSE_SSL=1");
-	if (set->server->auths->verbose)
-		env_put("VERBOSE_AUTH=1");
-	if (set->server->auths->debug)
-		env_put("AUTH_DEBUG=1");
-	require_cert = TRUE;
-	for (auth = set->server->auths; auth != NULL; auth = auth->next) {
-		if (!auth->ssl_require_client_cert)
-			require_cert = FALSE;
-	}
-	if (require_cert)
-		env_put("SSL_REQUIRE_CLIENT_CERT=1");
-
-	if (set->login_process_per_connection) {
-		env_put("PROCESS_PER_CONNECTION=1");
-		env_put("MAX_CONNECTIONS=1");
-	} else {
-		env_put(t_strdup_printf("MAX_CONNECTIONS=%u",
-					set->login_max_connections));
+	master_settings_export_to_env(group->set);
+	if (ssl_manual_key_password != NULL) {
+		env_put(t_strconcat("SSL_MANUAL_KEY_PASSWORD=",
+				    ssl_manual_key_password, NULL));
 	}
 
 	env_put(t_strconcat("PROCESS_UID=", dec2str(pid), NULL));
-	env_put(t_strconcat("GREETING=", set->login_greeting, NULL));
-	env_put(t_strconcat("LOG_FORMAT_ELEMENTS=",
-			    set->login_log_format_elements, NULL));
-	env_put(t_strconcat("LOG_FORMAT=", set->login_log_format, NULL));
-	env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL));
-	env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL));
-
 	if (group->mail_process_type == PROCESS_TYPE_IMAP) {
-		env_put(t_strconcat("CAPABILITY_STRING=",
-				    *set->imap_capability != '\0' ?
-				    set->imap_capability :
+		env_put(t_strconcat("GENERATED_CAPABILITY=",
 				    set->imap_generated_capability, NULL));
 	}
-	if (*set->login_trusted_networks != '\0') {
-		env_put(t_strconcat("TRUSTED_NETWORKS=",
-				    set->login_trusted_networks, NULL));
-	}
 	env_put(t_strconcat("LOGIN_DIR=", set->login_dir, NULL));
 }
 
--- a/src/master/login-process.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/login-process.h	Tue Jan 27 18:21:53 2009 -0500
@@ -8,7 +8,7 @@
 	int refcount;
 
 	enum process_type mail_process_type;
-	struct settings *set;
+	struct master_settings *set;
 
 	unsigned int processes;
 	unsigned int listening_processes;
--- a/src/master/mail-process.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/mail-process.c	Tue Jan 27 18:21:53 2009 -0500
@@ -13,6 +13,7 @@
 #include "restrict-process-size.h"
 #include "home-expand.h"
 #include "var-expand.h"
+#include "settings-parser.h"
 #include "mail-process.h"
 #include "login-process.h"
 #include "log.h"
@@ -112,7 +113,7 @@
 	i_free(group);
 }
 
-static bool validate_uid_gid(struct settings *set, uid_t uid, gid_t gid,
+static bool validate_uid_gid(struct master_settings *set, uid_t uid, gid_t gid,
 			     const char *user)
 {
 	if (uid == 0) {
@@ -146,7 +147,7 @@
 	return TRUE;
 }
 
-static bool validate_chroot(struct settings *set, const char *dir)
+static bool validate_chroot(struct master_settings *set, const char *dir)
 {
 	const char *const *chroot_dirs;
 
@@ -214,257 +215,59 @@
 		var_has_key(str, 'h', "home");
 }
 
-static const char *
-expand_mail_env(const char *env, const struct var_expand_table *table)
+static void
+mail_process_set_environment(struct master_settings *set,
+			     const struct var_expand_table *table)
 {
-	string_t *str;
-	const char *p;
-
-	str = t_str_new(256);
-
-	/* it's either type:data or just data */
-	p = strchr(env, ':');
-	if (p != NULL) {
-		while (env != p) {
-			str_append_c(str, *env);
-			env++;
-		}
-
-		str_append_c(str, *env++);
-	}
-
-	if (has_missing_used_home(env, table)) {
-		i_fatal("userdb didn't return a home directory, "
-			"but mail location used it (%%h): %s", env);
-	}
-
-	/* expand %vars */
-	var_expand(str, env, table);
-	return str_c(str);
-}
-
-static void
-env_put_namespace(struct namespace_settings *ns, const char *default_location,
-		  const struct var_expand_table *table)
-{
-	const char *location;
-	unsigned int i;
-	string_t *str;
-
-	if (default_location == NULL)
-		default_location = "";
 
-	for (i = 1; ns != NULL; i++, ns = ns->next) {
-		location = *ns->location != '\0' ? ns->location :
-			default_location;
-		location = expand_mail_env(location, table);
-		env_put(t_strdup_printf("NAMESPACE_%u=%s", i, location));
-
-		if (ns->separator != NULL) {
-			env_put(t_strdup_printf("NAMESPACE_%u_SEP=%s",
-						i, ns->separator));
-		}
-		if (ns->type != NULL) {
-			env_put(t_strdup_printf("NAMESPACE_%u_TYPE=%s",
-						i, ns->type));
-		}
-		if (ns->alias_for != NULL) {
-			env_put(t_strdup_printf("NAMESPACE_%u_ALIAS=%s",
-						i, ns->alias_for));
-		}
-		if (ns->prefix != NULL) {
-			/* expand variables, eg. ~%u/ can be useful */
-			str = t_str_new(256);
-			str_printfa(str, "NAMESPACE_%u_PREFIX=", i);
-			var_expand(str, ns->prefix, table);
-			env_put(str_c(str));
-		}
-		if (ns->inbox)
-			env_put(t_strdup_printf("NAMESPACE_%u_INBOX=1", i));
-		if (ns->hidden)
-			env_put(t_strdup_printf("NAMESPACE_%u_HIDDEN=1", i));
-		if (strcmp(ns->list, "no") != 0) {
-			env_put(t_strdup_printf("NAMESPACE_%u_LIST=%s",
-						i, ns->list));
-		}
-		if (ns->subscriptions)
-			env_put(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS=1",
-						i));
-	}
-}
-
-static void
-mail_process_set_environment(struct settings *set, const char *mail,
-			     const struct var_expand_table *var_expand_table,
-			     bool exec_mail)
-{
-	const char *const *envs;
+	const char **envs;
 	string_t *str;
 	unsigned int i, count;
 
-	env_put(t_strconcat("MAIL_CACHE_FIELDS=",
-			    set->mail_cache_fields, NULL));
-	env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
-			    set->mail_never_cache_fields, NULL));
-	env_put(t_strdup_printf("MAIL_CACHE_MIN_MAIL_COUNT=%u",
-				set->mail_cache_min_mail_count));
-	env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u",
-				set->mailbox_idle_check_interval));
-	env_put(t_strdup_printf("MAIL_MAX_KEYWORD_LENGTH=%u",
-				set->mail_max_keyword_length));
-
-	if (set->protocol == MAIL_PROTOCOL_IMAP) {
-		env_put(t_strdup_printf("IMAP_MAX_LINE_LENGTH=%u",
-					set->imap_max_line_length));
-		if (*set->imap_capability != '\0') {
-			env_put(t_strconcat("IMAP_CAPABILITY=",
-					    set->imap_capability, NULL));
-		}
-		env_put(t_strconcat("IMAP_CLIENT_WORKAROUNDS=",
-				    set->imap_client_workarounds, NULL));
-		env_put(t_strconcat("IMAP_LOGOUT_FORMAT=",
-				    set->imap_logout_format, NULL));
-		env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL));
-		env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL));
-	}
-	if (set->protocol == MAIL_PROTOCOL_POP3) {
-		env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=",
-				    set->pop3_client_workarounds, NULL));
-		env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
-				    set->pop3_logout_format, NULL));
-		if (set->pop3_no_flag_updates)
-			env_put("POP3_NO_FLAG_UPDATES=1");
-		if (set->pop3_reuse_xuidl)
-			env_put("POP3_REUSE_XUIDL=1");
-		if (set->pop3_enable_last)
-			env_put("POP3_ENABLE_LAST=1");
-		if (set->pop3_lock_session)
-			env_put("POP3_LOCK_SESSION=1");
-	}
+	settings_var_expand(&master_setting_parser_info, set,
+			    system_pool, table);
 
-	/* We care about POP3 UIDL format in all process types */
-	env_put(t_strconcat("POP3_UIDL_FORMAT=", set->pop3_uidl_format, NULL));
-
-	if (set->mail_save_crlf)
-		env_put("MAIL_SAVE_CRLF=1");
-	if (set->mmap_disable)
-		env_put("MMAP_DISABLE=1");
-	if (set->dotlock_use_excl)
-		env_put("DOTLOCK_USE_EXCL=1");
-	if (set->fsync_disable)
-		env_put("FSYNC_DISABLE=1");
-	if (set->mail_nfs_storage)
-		env_put("MAIL_NFS_STORAGE=1");
-	if (set->mail_nfs_index)
-		env_put("MAIL_NFS_INDEX=1");
-	if (set->mailbox_list_index_disable)
-		env_put("MAILBOX_LIST_INDEX_DISABLE=1");
-	if (set->maildir_stat_dirs)
-		env_put("MAILDIR_STAT_DIRS=1");
-	if (set->maildir_copy_with_hardlinks)
-		env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
-	if (set->maildir_copy_preserve_filename)
-		env_put("MAILDIR_COPY_PRESERVE_FILENAME=1");
-	if (set->mail_debug)
-		env_put("DEBUG=1");
-	if (set->mail_full_filesystem_access)
-		env_put("FULL_FILESYSTEM_ACCESS=1");
-	if (set->mbox_dirty_syncs)
-		env_put("MBOX_DIRTY_SYNCS=1");
-	if (set->mbox_very_dirty_syncs)
-		env_put("MBOX_VERY_DIRTY_SYNCS=1");
-	if (set->mbox_lazy_writes)
-		env_put("MBOX_LAZY_WRITES=1");
-	/* when we're not certain that the log fd points to the master
-	   process's log pipe (dump-capability, --exec-mail), don't let
-	   the imap process listen for stderr since it might break
-	   (e.g. epoll_ctl() gives EPERM). */
-	if (set->shutdown_clients && !exec_mail)
-		env_put("STDERR_CLOSE_SHUTDOWN=1");
 	(void)umask(set->umask);
 
-	env_put(t_strconcat("LOCK_METHOD=", set->lock_method, NULL));
-	env_put(t_strconcat("MBOX_READ_LOCKS=", set->mbox_read_locks, NULL));
-	env_put(t_strconcat("MBOX_WRITE_LOCKS=", set->mbox_write_locks, NULL));
-	env_put(t_strdup_printf("MBOX_LOCK_TIMEOUT=%u",
-				set->mbox_lock_timeout));
-	env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u",
-				set->mbox_dotlock_change_timeout));
-	env_put(t_strdup_printf("MBOX_MIN_INDEX_SIZE=%u",
-				set->mbox_min_index_size));
-
-	env_put(t_strdup_printf("DBOX_ROTATE_SIZE=%u",
-				set->dbox_rotate_size));
-	env_put(t_strdup_printf("DBOX_ROTATE_MIN_SIZE=%u",
-				set->dbox_rotate_min_size));
-	env_put(t_strdup_printf("DBOX_ROTATE_DAYS=%u",
-				set->dbox_rotate_days));
-
-	if (*set->mail_plugins != '\0') {
-		env_put(t_strconcat("MAIL_PLUGIN_DIR=",
-				    set->mail_plugin_dir, NULL));
-		env_put(t_strconcat("MAIL_PLUGINS=", set->mail_plugins, NULL));
+	if (array_is_created(&set->plugin_envs))
+		envs = array_get_modifiable(&set->plugin_envs, &count);
+	else {
+		count = 0;
+		envs = NULL;
 	}
-
-	/* user given environment - may be malicious. virtual_user comes from
-	   auth process, but don't trust that too much either. Some auth
-	   mechanism might allow leaving extra data there. */
-	if ((mail == NULL || *mail == '\0') && *set->mail_location != '\0')
-		mail = expand_mail_env(set->mail_location, var_expand_table);
-	env_put(t_strconcat("MAIL=", mail, NULL));
-
-	if (set->server->namespaces != NULL) {
-		env_put_namespace(set->server->namespaces,
-				  mail, var_expand_table);
-	}
-
 	str = t_str_new(256);
-	envs = array_get(&set->plugin_envs, &count);
 	i_assert((count % 2) == 0);
 	for (i = 0; i < count; i += 2) {
-		str_truncate(str, 0);
-		var_expand(str, envs[i+1], var_expand_table);
-
-		if (has_missing_used_home(envs[i+1], var_expand_table)) {
+		if (has_missing_used_home(envs[i+1], table)) {
 			i_error("userdb didn't return a home directory, "
 				"but it's used in plugin setting %s: %s",
 				envs[i], envs[i+1]);
 		}
+		str_truncate(str, 0);
+		var_expand(str, envs[i+1], table);
+		envs[i+1] = t_strdup(str_c(str));
+	}
 
-		env_put(t_strconcat(t_str_ucase(envs[i]), "=",
-				    str_c(str), NULL));
-	}
+	master_settings_export_to_env(set);
 }
 
 void mail_process_exec(const char *protocol, const char **args)
 {
-	struct server_settings *server = settings_root;
 	const struct var_expand_table *var_expand_table;
-	struct settings *set;
+	struct master_settings *set;
 	const char *executable;
 
 	if (strcmp(protocol, "ext") == 0) {
 		/* external binary. section contains path for it. */
 		if (*args == NULL)
 			i_fatal("External binary parameter not given");
-		set = server->defaults;
+		set = master_set->defaults;
 		executable = *args;
 	} else {
-		const char *section = *args;
-
-		if (section != NULL) {
-			for (; server != NULL; server = server->next) {
-				if (strcmp(server->name, section) == 0)
-					break;
-			}
-			if (server == NULL)
-				i_fatal("Section not found: '%s'", section);
-		}
-
 		if (strcmp(protocol, "imap") == 0)
-			set = server->imap;
+			set = master_set->imap;
 		else if (strcmp(protocol, "pop3") == 0)
-			set = server->pop3;
+			set = master_set->pop3;
 		else
 			i_fatal("Unknown protocol: '%s'", protocol);
 		executable = set->mail_executable;
@@ -493,8 +296,7 @@
 		env_put(str_c(str));
 	}
 
-	mail_process_set_environment(set, getenv("MAIL"), var_expand_table,
-				     TRUE);
+	mail_process_set_environment(set, var_expand_table);
 	if (args == NULL)
 		client_process_exec(executable, "");
 	else
@@ -542,7 +344,7 @@
 }
 
 enum master_login_status
-create_mail_process(enum process_type process_type, struct settings *set,
+create_mail_process(enum process_type process_type, struct master_settings *set,
 		    const struct mail_login_request *request,
 		    const char *user, const char *const *args,
 		    const unsigned char *data, bool dump_capability,
@@ -752,7 +554,7 @@
 		log_set_prefix(log, str_c(str));
 	}
 
-	child_process_init_env();
+	child_process_init_env(set);
 
 	/* move the client socket into stdin and stdout fds, log to stderr */
 	if (dup2(dump_capability ? null_fd : request->fd, 0) < 0)
@@ -825,8 +627,7 @@
 			i_fatal("chdir(/tmp) failed: %m");
 	}
 
-	mail_process_set_environment(set, mail, var_expand_table,
-				     dump_capability);
+	mail_process_set_environment(set, var_expand_table);
 
 	/* extra args. uppercase key value. */
 	args = array_get(&extra_args, &count);
@@ -851,7 +652,7 @@
 	if (nfs_check) {
 		/* ideally we should check all of the namespaces,
 		   but for now don't bother. */
-		const char *mail_location = getenv("NAMESPACE_1");
+		const char *mail_location = getenv("NAMESPACE_1"); //FIXME
 
 		if (mail_location == NULL)
 			mail_location = getenv("MAIL");
--- a/src/master/mail-process.h	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/mail-process.h	Tue Jan 27 18:21:53 2009 -0500
@@ -17,7 +17,7 @@
 void mail_process_exec(const char *protocol, const char **args) ATTR_NORETURN;
 
 enum master_login_status
-create_mail_process(enum process_type process_type, struct settings *set,
+create_mail_process(enum process_type process_type, struct master_settings *set,
 		    const struct mail_login_request *request,
 		    const char *user, const char *const *args,
 		    const unsigned char *data, bool dump_capability,
--- a/src/master/main.c	Mon Jan 26 19:17:54 2009 -0500
+++ b/src/master/main.c	Tue Jan 27 18:21:53 2009 -0500
@@ -54,7 +54,7 @@
 master_fatal_callback(enum log_type type, int status,
 		      const char *format, va_list args)
 {
-	const struct settings *set = settings_root->defaults;
+	const struct master_settings *set = master_set->defaults;
 	const char *path, *str;
 	va_list args2;
 	int fd;
@@ -82,7 +82,7 @@
 
 static void fatal_log_check(void)
 {
-	const struct settings *set = settings_root->defaults;
+	const struct master_settings *set = master_set->defaults;
 	const char *path;
 	char buf[1024];
 	ssize_t ret;
@@ -107,12 +107,15 @@
 		i_error("unlink(%s) failed: %m", path);
 }
 
-static void auth_warning_print(const struct server_settings *set)
+static void auth_warning_print(const struct master_server_settings *set)
 {
+	struct master_auth_settings *const *auths;
+	unsigned int count;
 	struct stat st;
 
 	auth_success_written = stat(AUTH_SUCCESS_PATH, &st) == 0;
-	if (!auth_success_written && !set->auths->debug &&
+	auths = array_get(&set->defaults->auths, &count);
+	if (!auth_success_written && count > 0 && !auths[0]->debug &&
 	    strcmp(set->defaults->protocols, "none") != 0) {
 		i_info("If you have trouble with authentication failures,\n"
 		       "enable auth_debug setting. "
@@ -121,7 +124,7 @@
 	}
 }
 
-static void set_logfile(struct settings *set)
+static void set_logfile(struct master_settings *set)
 {
 	int facility;
 
@@ -144,7 +147,9 @@
 
 static void settings_reload(void)
 {
-	struct server_settings *old_set = settings_root;
+	/* this old_set wrapping works because master settings are
+	   alternatingly read using two different pools */
+	struct master_server_settings *set, *old_set = master_set;
 
 	i_warning("SIGHUP received - reloading configuration");
 
@@ -156,12 +161,13 @@
 	/* see if hostname changed */
 	hostpid_init();
 
-	if (!master_settings_read(configfile, FALSE, FALSE))
+	if (master_settings_read(configfile, &set) < 0 ||
+	    !master_settings_check(set, FALSE, FALSE))
 		i_warning("Invalid configuration, keeping old one");
 	else {
 		if (!IS_INETD())
 			listeners_open_fds(old_set, TRUE);
-                set_logfile(settings_root->defaults);
+                set_logfile(set->defaults);
 	}
 }
 
@@ -183,10 +189,10 @@
 static void sig_reopen_logs(int signo ATTR_UNUSED,
 			    void *context ATTR_UNUSED)
 {
-	set_logfile(settings_root->defaults);
+	set_logfile(master_set->defaults);
 }
 
-static bool have_stderr_set(struct settings *set)
+static bool have_stderr_set(struct master_settings *set)
 {
 	if (*set->log_path != '\0' &&
 	    strcmp(set->log_path, "/dev/stderr") == 0)
@@ -199,17 +205,12 @@
 	return FALSE;
 }
 
-static bool have_stderr(struct server_settings *server)
+static bool have_stderr(struct master_server_settings *server)
 {
-	while (server != NULL) {
-		if (server->imap != NULL && have_stderr_set(server->imap))
-			return TRUE;
-		if (server->pop3 != NULL && have_stderr_set(server->pop3))
-			return TRUE;
-
-		server = server->next;
-	}
-
+	if (server->imap != NULL && have_stderr_set(server->imap))
+		return TRUE;
+	if (server->pop3 != NULL && have_stderr_set(server->pop3))
+		return TRUE;
 	return FALSE;
 }
 
@@ -259,6 +260,44 @@
 	(void)close(fd);
 }
 
+static void pid_file_check_running(const struct master_settings *set)
+{
+	const char *path;
+	char buf[32];
+	int fd;
+	ssize_t ret;
+
+	path = t_strconcat(set->base_dir, "/master.pid", NULL);
+	fd = open(path, O_RDONLY);
+	if (fd == -1) {
+		if (errno == ENOENT)
+			return;
+		i_fatal("open(%s) failed: %m", path);
+	}
+
+	ret = read(fd, buf, sizeof(buf));
+	if (ret <= 0) {
+		if (ret == 0)
+			i_error("Empty PID file in %s, overriding", path);
+		else
+			i_fatal("read(%s) failed: %m", path);
+	} else {
+		pid_t pid;
+
+		if (buf[ret-1] == '\n')
+			ret--;
+		buf[ret] = '\0';
+		pid = atoi(buf);
+		if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) {
+			/* doesn't exist */
+		} else {
+			i_fatal("Dovecot is already running with PID %s "
+				"(read from %s)", buf, path);
+		}
+	}
+	(void)close(fd);
+}
+
 static void main_log_startup(void)
 {
 #define STARTUP_STRING PACKAGE_NAME" v"VERSION" starting up"
@@ -277,9 +316,9 @@
 	/* deny file access from everyone else except owner */
         (void)umask(0077);
 
-	set_logfile(settings_root->defaults);
+	set_logfile(master_set->defaults);
 	/* close stderr unless we're logging into /dev/stderr. */
-	if (!have_stderr(settings_root)) {
+	if (!have_stderr(master_set)) {
 		if (dup2(null_fd, 2) < 0)
 			i_fatal("dup2(2) failed: %m");
 	}
@@ -309,13 +348,13 @@
 	login_processes_init();
 	mail_processes_init();
 
-	create_pid_file(t_strconcat(settings_root->defaults->base_dir,
+	create_pid_file(t_strconcat(master_set->defaults->base_dir,
 				    "/master.pid", NULL));
 }
 
 static void main_deinit(void)
 {
-	(void)unlink(t_strconcat(settings_root->defaults->base_dir,