changeset 1610:6850142c4e25 HEAD

New configuration file code. Some syntax changes, but tries to be somewhat backwards compatible. SIGHUP now reverts back to old configuration if it detected errors in new one.
author Timo Sirainen <tss@iki.fi>
date Thu, 10 Jul 2003 06:04:07 +0300
parents 5c2ad8ec50db
children fcc5d6bb6244
files dovecot-example.conf src/auth/db-ldap.c src/auth/db-pgsql.c src/lib-settings/settings.c src/lib-settings/settings.h src/master/auth-process.c src/master/common.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.c src/master/master-settings.h src/master/ssl-init.c
diffstat 15 files changed, 909 insertions(+), 596 deletions(-) [+]
line wrap: on
line diff
--- a/dovecot-example.conf	Thu Jul 10 03:40:25 2003 +0300
+++ b/dovecot-example.conf	Thu Jul 10 06:04:07 2003 +0300
@@ -18,13 +18,11 @@
 # "[::]" listens in all IPv6 interfaces, but may also listen in all IPv4
 # interfaces depending on the operating system. You can specify ports with
 # "host:port".
-#imap_listen = *
-#pop3_listen = *
+#listen = *
 
 # IP or host address where to listen in for SSL connections. Defaults
-# to above non-SSL equilevants if not specified.
-#imaps_listen = 
-#pop3s_listen = 
+# to above if not specified.
+#ssl_listen =
 
 # Disable SSL/TLS support.
 #ssl_disable = no
@@ -73,16 +71,6 @@
 # wish to run the whole Dovecot without roots.
 #login_chroot = yes
 
-
-##
-## IMAP login process
-##
-
-login = imap
-
-# Executable location.
-#login_executable = /usr/libexec/dovecot/imap-login
-
 # User to use for the login process. Create a completely new user for this,
 # and don't use it anywhere else. The user must also belong to a group where
 # only it has access, it's used to control access for authentication process.
@@ -116,17 +104,6 @@
 # logging in actually login_processes_count * max_logging_users.
 #login_max_logging_users = 256
 
-##
-## POP3 login process
-##
-
-# Settings default to same as above, so you don't have to set anything
-# unless you want to override them.
-
-login = pop3
-
-# Exception to above rule being the executable location.
-#login_executable = /usr/libexec/dovecot/pop3-login
 
 ##
 ## Mail processes
@@ -309,40 +286,46 @@
 # for multiple users, as the users could ptrace() each others processes then.
 #mail_drop_priv_before_exec = no
 
-##
-## IMAP process
-##
-
-# Executable location
-#imap_executable = /usr/libexec/dovecot/imap
-
 # Set max. process size in megabytes. Most of the memory goes to mmap()ing
 # files, so it shouldn't harm much even if this limit is set pretty high.
-#imap_process_size = 256
-
-# Maximum IMAP command line length in bytes. Some clients generate very long
-# command lines with huge mailboxes, so you may need to raise this if you get
-# "Too long argument" or "IMAP command line too large" errors often.
-#imap_max_line_length = 65536
-
-# Support for dynamically loadable modules.
-#imap_use_modules = no
-#imap_modules = /usr/lib/dovecot/imap
+#mail_process_size = 256
 
 ##
-## POP3 process
+## IMAP specific settings
 ##
 
-# Executable location
-#pop3_executable = /usr/libexec/dovecot/pop3
+protocol imap {
+  # Login executable location.
+  #login_executable = /usr/libexec/dovecot/imap-login
+
+  # IMAP executable location
+  #mail_executable = /usr/libexec/dovecot/imap
+
+  # Maximum IMAP command line length in bytes. Some clients generate very long
+  # command lines with huge mailboxes, so you may need to raise this if you get
+  # "Too long argument" or "IMAP command line too large" errors often.
+  #imap_max_line_length = 65536
 
-# Set max. process size in megabytes. Most of the memory goes to mmap()ing
-# files, so it shouldn't harm much even if this limit is set pretty high.
-#pop3_process_size = 256
+  # Support for dynamically loadable modules.
+  #mail_use_modules = no
+  #mail_modules = /usr/lib/dovecot/imap
+}
+  
+##
+## POP3 specific settings
+##
 
-# Support for dynamically loadable modules.
-#pop3_use_modules = no
-#pop3_modules = /usr/lib/dovecot/pop3
+protocol pop3 {
+  # Login executable location.
+  #login_executable = /usr/libexec/dovecot/pop3-login
+
+  # POP3 executable location
+  #mail_executable = /usr/libexec/dovecot/pop3
+
+  # Support for dynamically loadable modules.
+  #mail_use_modules = no
+  #mail_modules = /usr/lib/dovecot/pop3
+}
 
 ##
 ## Authentication processes
@@ -358,12 +341,11 @@
 # processes (unless they have different auth methods, and you're ok with
 # having different password for each method).
 
-# Authentication process name.
-auth = default
+# Executable location
+#auth_executable = /usr/libexec/dovecot/dovecot-auth
 
-# Space separated list of wanted authentication mechanisms:
-#   plain digest-md5 anonymous
-auth_mechanisms = plain
+# Set max. process size in megabytes.
+#auth_process_size = 256
 
 # Space separated list of realms for SASL authentication mechanisms that need
 # them. You can leave it empty if you don't want to support multiple realms.
@@ -374,43 +356,6 @@
 # Default realm to use if none was specified.
 #auth_default_realm = 
 
-# Where user database is kept:
-#   passwd: /etc/passwd or similiar, using getpwnam()
-#   passwd-file <path>: passwd-like file with specified location
-#   static uid=<uid> gid=<gid> home=<dir template>: static settings
-#   vpopmail: vpopmail library
-#   ldap <config path>: LDAP, see doc/dovecot-ldap.conf
-#   pgsql <config path>: a PostgreSQL database, see doc/dovecot-pgsql.conf
-auth_userdb = passwd
-
-# Where password database is kept:
-#   passwd: /etc/passwd or similiar, using getpwnam()
-#   shadow: /etc/shadow or similiar, using getspnam()
-#   pam [<service> | *]: PAM authentication
-#   passwd-file <path>: passwd-like file with specified location
-#   vpopmail: vpopmail authentication
-#   ldap <config path>: LDAP, see doc/dovecot-ldap.conf
-#   pgsql <config path>: a PostgreSQL database, see doc/dovecot-pgsql.conf
-auth_passdb = pam
-
-# Executable location
-#auth_executable = /usr/libexec/dovecot/dovecot-auth
-
-# Set max. process size in megabytes.
-#auth_process_size = 256
-
-# User to use for the process. This user needs access to only user and
-# password databases, nothing else. Only shadow and pam authentication
-# requires roots, so use something else if possible.
-auth_user = root
-
-# Directory where to chroot the process. Most authentication backends don't
-# work if this is set, and there's no point chrooting if auth_user is root.
-#auth_chroot = 
-
-# Number of authentication processes to create
-#auth_count = 1
-
 # List of allowed characters in username. If the user-given username contains
 # a character not listed in here, the login automatically fails. This is just
 # an extra check to make sure user can't exploit any potential quote escaping
@@ -425,18 +370,54 @@
 # working.
 #auth_verbose = no
 
+auth default {
+  # Space separated list of wanted authentication mechanisms:
+  #   plain digest-md5 anonymous
+  auth_mechanisms = plain
+
+  # Where user database is kept:
+  #   passwd: /etc/passwd or similiar, using getpwnam()
+  #   passwd-file <path>: passwd-like file with specified location
+  #   static uid=<uid> gid=<gid> home=<dir template>: static settings
+  #   vpopmail: vpopmail library
+  #   ldap <config path>: LDAP, see doc/dovecot-ldap.conf
+  #   pgsql <config path>: a PostgreSQL database, see doc/dovecot-pgsql.conf
+  auth_userdb = passwd
+
+  # Where password database is kept:
+  #   passwd: /etc/passwd or similiar, using getpwnam()
+  #   shadow: /etc/shadow or similiar, using getspnam()
+  #   pam [<service> | *]: PAM authentication
+  #   passwd-file <path>: passwd-like file with specified location
+  #   vpopmail: vpopmail authentication
+  #   ldap <config path>: LDAP, see doc/dovecot-ldap.conf
+  #   pgsql <config path>: a PostgreSQL database, see doc/dovecot-pgsql.conf
+  auth_passdb = pam
+
+  # User to use for the process. This user needs access to only user and
+  # password databases, nothing else. Only shadow and pam authentication
+  # requires roots, so use something else if possible.
+  auth_user = root
+
+  # Directory where to chroot the process. Most authentication backends don't
+  # work if this is set, and there's no point chrooting if auth_user is root.
+  #auth_chroot = 
+
+  # Number of authentication processes to create
+  #auth_count = 1
+}
+
 # digest-md5 authentication process. It requires special MD5 passwords which
 # /etc/shadow and PAM doesn't support, so we never need roots to handle it.
 # Note that the passwd-file is opened before chrooting and dropping root
 # privileges, so it may be 0600-root owned file.
 
-#auth = digest_md5
-#auth_methods = digest-md5
-#auth_realms = 
-#auth_userdb = passwd-file /etc/passwd.imap
-#auth_passdb = passwd-file /etc/passwd.imap
-#auth_user = imapauth
-#auth_chroot = 
+#auth digest_md5 {
+#  auth_methods = digest-md5
+#  auth_userdb = passwd-file /etc/passwd.imap
+#  auth_passdb = passwd-file /etc/passwd.imap
+#  auth_user = imapauth
+#}
 
 # if you plan to use only passwd-file, you don't need the two auth processes,
 # simply set "auth_methods = plain digest-md5"
--- a/src/auth/db-ldap.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/auth/db-ldap.c	Thu Jul 10 06:04:07 2003 +0300
@@ -14,6 +14,7 @@
 #include "db-ldap.h"
 
 #include <stddef.h>
+#include <stdlib.h>
 
 /* Older versions may require calling ldap_result() twice */
 #if LDAP_VENDOR_VERSION <= 20112
@@ -335,7 +336,8 @@
 
 	conn->config_path = p_strdup(pool, config_path);
 	conn->set = default_ldap_settings;
-	settings_read(config_path, parse_setting, conn);
+	if (!settings_read(config_path, NULL, parse_setting, NULL, conn))
+		exit(FATAL_DEFAULT);
 
 	if (conn->set.base == NULL)
 		i_fatal("LDAP: No base given");
--- a/src/auth/db-pgsql.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/auth/db-pgsql.c	Thu Jul 10 06:04:07 2003 +0300
@@ -12,6 +12,7 @@
 #include "db-pgsql.h"
 
 #include <stddef.h>
+#include <stdlib.h>
 
 #define DEF(type, name) { type, #name, offsetof(struct pgsql_settings, name) }
 
@@ -142,7 +143,8 @@
 
 	conn->config_path = p_strdup(pool, config_path);
 	conn->set = default_pgsql_settings;
-	settings_read(config_path, parse_setting, conn);
+	if (!settings_read(config_path, NULL, parse_setting, NULL, conn))
+		exit(FATAL_DEFAULT);
 
 	(void)pgsql_conn_open(conn);
 
--- a/src/lib-settings/settings.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/lib-settings/settings.c	Thu Jul 10 06:04:07 2003 +0300
@@ -59,19 +59,32 @@
 
 #define IS_WHITE(c) ((c) == ' ' || (c) == '\t')
 
-void settings_read(const char *path, settings_callback_t *callback,
-		   void *context)
+int settings_read(const char *path, const char *section,
+		  settings_callback_t *callback,
+		  settings_section_callback_t *sect_callback, void *context)
 {
 	struct istream *input;
-	const char *errormsg;
-	char *line, *key;
-	int fd, linenum;
+	const char *errormsg, *next_section;
+	char *line, *key, *name;
+	int fd, linenum, skip, sections, root_section;
 
 	fd = open(path, O_RDONLY);
-	if (fd < 0)
-		i_fatal("Can't open configuration file %s: %m", path);
+	if (fd < 0) {
+		i_error("Can't open configuration file %s: %m", path);
+		return FALSE;
+	}
+
+	t_push();
 
-	linenum = 0;
+	if (section == NULL) {
+		skip = 0;
+                next_section = NULL;
+	} else {
+		skip = 1;
+		next_section = t_strcut(section, '/');
+	}
+
+	linenum = 0; sections = 0; root_section = 0; errormsg = NULL;
 	input = i_stream_create_file(fd, default_pool, 2048, TRUE);
 	while ((line = i_stream_read_next_line(input)) != NULL) {
 		linenum++;
@@ -86,7 +99,9 @@
 		if (*line == '#' || *line == '\0')
 			continue;
 
-		/* all lines must be in format "key = value" */
+		/* a) "key = value"
+		   b) section_type section_name {
+		   c) } */
 		key = line;
 		while (!IS_WHITE(*line) && *line != '\0')
 			line++;
@@ -95,21 +110,82 @@
 			while (IS_WHITE(*line)) line++;
 		}
 
-		if (*line != '=') {
-			errormsg = "Missing value";
-		} else {
-			/* skip whitespace after '=' */
+		if (strcmp(key, "}") == 0 && *line == '\0') {
+			if (sections == 0)
+				errormsg = "Unexpected '}'";
+			else {
+				if (skip > 0)
+					skip--;
+				else {
+					sect_callback(NULL, NULL, context,
+						      &errormsg);
+					if (root_section == sections &&
+					    errormsg == NULL) {
+						/* we found the section,
+						   now quit */
+						break;
+					}
+				}
+				sections--;
+			}
+		} else if (*line == '=') {
 			*line++ = '\0';
 			while (IS_WHITE(*line)) line++;
 
-			errormsg = callback(key, line, context);
+			errormsg = skip ? NULL :
+				callback(key, line, context);
+		} else {
+			line[-1] = '\0';
+
+			name = line;
+			while (!IS_WHITE(*line) && *line != '\0')
+				line++;
+
+			if (*line != '\0') {
+				*line++ = '\0';
+				while (IS_WHITE(*line))
+					line++;
+			}
+
+			if (*line != '{' || strcspn(line+1, " \t") != 0)
+				errormsg = "Missing value";
+			else {
+				sections++;
+				if (next_section != NULL &&
+				    strcmp(next_section, name) == 0) {
+					section += strlen(next_section);
+					if (*section == '\0') {
+						skip = 0;
+						next_section = NULL;
+						root_section = sections;
+					} else {
+						i_assert(*section == '/');
+						section++;
+						next_section =
+							t_strcut(section, '/');
+					}
+				}
+
+				if (skip > 0)
+					skip++;
+				else {
+					skip = sect_callback == NULL ? 1 :
+						!sect_callback(key, name,
+							       context,
+							       &errormsg);
+				}
+			}
 		}
 
 		if (errormsg != NULL) {
-			i_fatal("Error in configuration file %s line %d: %s",
+			i_error("Error in configuration file %s line %d: %s",
 				path, linenum, errormsg);
+			break;
 		}
-	};
+	}
 
 	i_stream_unref(input);
+	t_pop();
+
+	return errormsg == NULL;
 }
--- a/src/lib-settings/settings.h	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/lib-settings/settings.h	Thu Jul 10 06:04:07 2003 +0300
@@ -13,14 +13,20 @@
 	size_t offset;
 };
 
+/* Return error message. When closing section, key = NULL, value = NULL. */
 typedef const char *settings_callback_t(const char *key, const char *value,
 					void *context);
 
+/* Return TRUE if we want to go inside the section */
+typedef int settings_section_callback_t(const char *type, const char *name,
+					void *context, const char **errormsg);
+
 const char *
 parse_setting_from_defs(pool_t pool, struct setting_def *defs, void *base,
 			const char *key, const char *value);
 
-void settings_read(const char *path, settings_callback_t *callback,
-		   void *context);
+int settings_read(const char *path, const char *section,
+		  settings_callback_t *callback,
+		  settings_section_callback_t *sect_callback, void *context);
 
 #endif
--- a/src/master/auth-process.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/auth-process.c	Thu Jul 10 06:04:07 2003 +0300
@@ -295,7 +295,7 @@
 	if (dup2(null_fd, 1) < 0)
 		i_fatal("login: dup2(1) failed: %m");
 
-	child_process_init_env();
+	child_process_init_env(group->set->parent->defaults);
 
 	/* move login communication handle to 3. do it last so we can be
 	   sure it's not closed afterwards. */
@@ -371,7 +371,8 @@
 	group->set = auth_set;
 
 	/* create socket for listening auth requests from login */
-	path = t_strconcat(set->login_dir, "/", auth_set->name, NULL);
+	path = t_strconcat(auth_set->parent->defaults->login_dir, "/",
+			   auth_set->name, NULL);
 	(void)unlink(path);
         (void)umask(0117); /* we want 0660 mode for the socket */
 
@@ -382,9 +383,10 @@
 	fd_close_on_exec(group->listen_fd, TRUE);
 
 	/* set correct permissions */
-	if (chown(path, geteuid(), set->login_gid) < 0) {
+	if (chown(path, geteuid(), auth_set->parent->defaults->login_gid) < 0) {
 		i_fatal("login: chown(%s, %s, %s) failed: %m",
-			path, dec2str(geteuid()), dec2str(set->login_gid));
+			path, dec2str(geteuid()),
+			dec2str(auth_set->parent->defaults->login_gid));
 	}
 
 	group->next = process_groups;
@@ -401,7 +403,8 @@
                 group->processes = next;
 	}
 
-	(void)unlink(t_strconcat(set->login_dir, "/", group->set->name, NULL));
+	(void)unlink(t_strconcat(group->set->parent->defaults->login_dir, "/",
+				 group->set->name, NULL));
 
 	if (close(group->listen_fd) < 0)
 		i_error("close(auth group %s) failed: %m", group->set->name);
@@ -419,18 +422,28 @@
 	}
 }
 
+static void auth_process_groups_create(struct server_settings *server)
+{
+	struct auth_settings *auth_set;
+
+	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;
+	}
+}
+
 static void
 auth_processes_start_missing(void *context __attr_unused__)
 {
-	struct auth_settings *auth_set;
 	struct auth_process_group *group;
 	unsigned int count;
 
 	if (process_groups == NULL) {
 		/* first time here, create the groups */
-		auth_set = set->auths;
-		for (; auth_set != NULL; auth_set = auth_set->next)
-                        auth_process_group_create(auth_set);
+		auth_process_groups_create(settings_root);
 	}
 
 	for (group = process_groups; group != NULL; group = group->next) {
--- a/src/master/common.h	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/common.h	Thu Jul 10 06:04:07 2003 +0300
@@ -20,18 +20,9 @@
 	PROCESS_TYPE_MAX
 };
 
-enum {
-	FD_IMAP,
-	FD_IMAPS,
-	FD_POP3,
-	FD_POP3S,
-
-	FD_MAX
-};
-
 extern struct ioloop *ioloop;
 extern struct hash_table *pids;
-extern int null_fd, mail_fd[FD_MAX], inetd_login_fd;
+extern int null_fd, inetd_login_fd;
 
 #define IS_INETD() \
 	(inetd_login_fd != -1)
@@ -46,7 +37,7 @@
 #define PID_REMOVE_PROCESS_TYPE(pid) \
 	hash_remove(pids, POINTER_CAST(pid))
 
-void child_process_init_env(void);
+void child_process_init_env(struct settings *set);
 
 /* misc */
 #define VALIDATE_STR(str) \
--- a/src/master/login-process.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/login-process.c	Thu Jul 10 06:04:07 2003 +0300
@@ -17,25 +17,6 @@
 #include <unistd.h>
 #include <syslog.h>
 
-struct login_group {
-	struct login_group *next;
-
-	struct login_settings *set;
-
-	unsigned int processes;
-	unsigned int listening_processes;
-	unsigned int wanted_processes_count;
-
-	struct login_process *oldest_nonlisten_process;
-	struct login_process *newest_nonlisten_process;
-
-	const char *executable;
-	const char *module_dir;
-	unsigned int process_size;
-	int process_type;
-	int *listen_fd, *ssl_listen_fd;
-};
-
 struct login_process {
 	struct login_group *group;
 	struct login_process *prev_nonlisten, *next_nonlisten;
@@ -71,36 +52,14 @@
 static void login_process_unref(struct login_process *p);
 static int login_process_init_group(struct login_process *p);
 
-static void login_group_create(struct login_settings *login_set)
+static void login_group_create(struct settings *set)
 {
 	struct login_group *group;
 
-	if (strstr(set->protocols, login_set->name) == NULL) {
-		/* not enabled */
-		return;
-	}
-
 	group = i_new(struct login_group, 1);
-	group->set = login_set;
-
-	if (strcmp(login_set->name, "imap") == 0) {
-		group->executable = set->imap_executable;
-		group->process_size = set->imap_process_size;
-		group->process_type = PROCESS_TYPE_IMAP;
-		group->listen_fd = &mail_fd[FD_IMAP];
-		group->ssl_listen_fd = &mail_fd[FD_IMAPS];
-		group->module_dir = !set->imap_use_modules ? NULL :
-                        set->imap_modules;
-	} else if (strcmp(login_set->name, "pop3") == 0) {
-		group->executable = set->pop3_executable;
-		group->process_size = set->pop3_process_size;
-		group->process_type = PROCESS_TYPE_POP3;
-		group->listen_fd = &mail_fd[FD_POP3];
-		group->ssl_listen_fd = &mail_fd[FD_POP3S];
-		group->module_dir = !set->pop3_use_modules ? NULL :
-                        set->pop3_modules;
-	} else
-		i_panic("Unknown login group name '%s'", login_set->name);
+	group->set = set;
+	group->process_type = set->protocol == MAIL_PROTOCOL_IMAP ?
+		PROCESS_TYPE_IMAP : PROCESS_TYPE_POP3;
 
 	group->next = login_groups;
 	login_groups = group;
@@ -123,11 +82,7 @@
 		struct login_group *group = request->process->group;
 
 		master_reply.success =
-			create_mail_process(request->fd, &request->ip,
-					    group->executable,
-					    group->module_dir,
-					    group->process_size,
-					    group->process_type,
+			create_mail_process(group, request->fd, &request->ip,
 					    reply, (const char *) data);
 	}
 
@@ -168,18 +123,29 @@
 	}
 }
 
-static struct login_group *login_group_process_find(const char *name)
+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);
+	}
+}
+
+static struct login_group *
+login_group_process_find(const char *name, enum mail_protocol protocol)
 {
 	struct login_group *group;
-	struct login_settings *login;
 
-	if (login_groups == NULL) {
-		for (login = set->logins; login != NULL; login = login->next)
-			login_group_create(login);
-	}
+	if (login_groups == NULL)
+                login_process_groups_create();
 
 	for (group = login_groups; group != NULL; group = group->next) {
-		if (strcmp(group->set->name, name) == 0)
+		if (strcmp(group->set->server->name, name) == 0 &&
+		    group->set->protocol == protocol)
 			return group;
 	}
 
@@ -189,8 +155,9 @@
 static int login_process_read_group(struct login_process *p)
 {
 	struct login_group *group;
-	const char *name;
+	const char *name, *proto;
 	char buf[256];
+	enum mail_protocol protocol;
 	unsigned int len;
 	ssize_t ret;
 
@@ -201,7 +168,7 @@
 	else {
 		len = buf[0];
 		if (len >= sizeof(buf)) {
-			i_error("login: Process name length too large");
+			i_error("login: Server name length too large");
 			return FALSE;
 		}
 
@@ -211,12 +178,29 @@
 	if (ret < 0)
 		i_error("login: read() failed: %m");
 	else if (len == 0 || (size_t)ret != len)
-		i_error("login: Process name wasn't sent");
+		i_error("login: Server name wasn't sent");
 	else {
 		name = t_strndup(buf, len);
-		group = login_group_process_find(name);
+		proto = strchr(buf, '/');
+		if (proto == NULL) {
+			i_error("login: Missing protocol from server name '%s'",
+				name);
+			return FALSE;
+		}
+		name = t_strdup_until(buf, proto++);
+
+		if (strcmp(proto, "imap") == 0)
+			protocol = MAIL_PROTOCOL_IMAP;
+		else if (strcmp(proto, "pop3") == 0)
+			protocol = MAIL_PROTOCOL_IMAP;
+		else {
+			i_error("login: Unknown protocol '%s'", proto);
+			return FALSE;
+		}
+
+		group = login_group_process_find(name, protocol);
 		if (group == NULL) {
-			i_error("login: Unknown process group '%s'", name);
+			i_error("login: Unknown server name '%s'", name);
 			return FALSE;
 		}
 
@@ -384,12 +368,13 @@
 
 static void login_process_init_env(struct login_group *group, pid_t pid)
 {
-	child_process_init_env();
+	struct settings *set = group->set;
+
+	child_process_init_env(set);
 
 	/* setup access environment - needs to be done after
 	   clean_child_process() since it clears environment */
-	restrict_access_set_env(group->set->user,
-				group->set->uid, set->login_gid,
+	restrict_access_set_env(set->login_user, set->login_uid, set->login_gid,
 				set->login_chroot ? set->login_dir : NULL,
 				0, 0);
 
@@ -398,7 +383,8 @@
 	if (!set->ssl_disable) {
 		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_FILE=",
+				    set->ssl_key_file, NULL));
 		env_put(t_strconcat("SSL_PARAM_FILE=",
 				    set->ssl_parameters_file, NULL));
 	}
@@ -410,12 +396,12 @@
 	if (set->verbose_ssl)
 		env_put("VERBOSE_SSL=1");
 
-	if (group->set->process_per_connection) {
+	if (set->login_process_per_connection) {
 		env_put("PROCESS_PER_CONNECTION=1");
 		env_put("MAX_LOGGING_USERS=1");
 	} else {
 		env_put(t_strdup_printf("MAX_LOGGING_USERS=%u",
-					group->set->max_logging_users));
+					set->login_max_logging_users));
 	}
 
 	env_put(t_strdup_printf("PROCESS_UID=%s", dec2str(pid)));
@@ -427,14 +413,14 @@
 	pid_t pid;
 	int fd[2];
 
-	if (group->set->process_per_connection &&
+	if (group->set->login_process_per_connection &&
 	    group->processes - group->listening_processes >=
-	    group->set->max_logging_users) {
+	    group->set->login_max_logging_users) {
 		if (group->oldest_nonlisten_process != NULL)
 			login_process_destroy(group->oldest_nonlisten_process);
 	}
 
-	if (group->set->uid == 0)
+	if (group->set->login_uid == 0)
 		i_fatal("Login process must not run as root");
 
 	/* create communication to process with a socket pair */
@@ -461,12 +447,12 @@
 	}
 
 	/* move the listen handle */
-	if (dup2(*group->listen_fd, LOGIN_LISTEN_FD) < 0)
+	if (dup2(group->set->listen_fd, LOGIN_LISTEN_FD) < 0)
 		i_fatal("login: dup2(listen_fd) failed: %m");
 	fd_close_on_exec(LOGIN_LISTEN_FD, FALSE);
 
 	/* move the SSL listen handle */
-	if (dup2(*group->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0)
+	if (dup2(group->set->ssl_listen_fd, LOGIN_SSL_LISTEN_FD) < 0)
 		i_fatal("login: dup2(ssl_listen_fd) failed: %m");
 	fd_close_on_exec(LOGIN_SSL_LISTEN_FD, FALSE);
 
@@ -480,26 +466,31 @@
 
 	login_process_init_env(group, getpid());
 
-	if (!set->login_chroot) {
+	if (!group->set->login_chroot) {
 		/* no chrooting, but still change to the directory */
-		if (chdir(set->login_dir) < 0)
-			i_fatal("chdir(%s) failed: %m", set->login_dir);
+		if (chdir(group->set->login_dir) < 0) {
+			i_fatal("chdir(%s) failed: %m",
+				group->set->login_dir);
+		}
 	}
 
-	restrict_process_size(group->set->process_size, (unsigned int)-1);
+	restrict_process_size(group->set->login_process_size, (unsigned int)-1);
 
 	/* make sure we don't leak syslog fd, but do it last so that
 	   any errors above will be logged */
 	closelog();
 
 	/* hide the path, it's ugly */
-	argv[0] = strrchr(group->set->executable, '/');
-	if (argv[0] == NULL) argv[0] = group->set->executable; else argv[0]++;
+	argv[0] = strrchr(group->set->login_executable, '/');
+	if (argv[0] == NULL)
+		argv[0] = group->set->login_executable;
+	else
+		argv[0]++;
 
-	execv(group->set->executable, (char **) argv);
+	execv(group->set->login_executable, (char **) argv);
 
 	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
-		       group->set->executable);
+		       group->set->login_executable);
 	return -1;
 }
 
@@ -534,10 +525,11 @@
 
 static void login_group_start_missings(struct login_group *group)
 {
-	if (!group->set->process_per_connection) {
+	if (!group->set->login_process_per_connection) {
 		/* create max. one process every second, that way if it keeps
 		   dying all the time we don't eat all cpu with fork()ing. */
-		if (group->listening_processes < group->set->processes_count)
+		if (group->listening_processes <
+		    group->set->login_processes_count)
 			(void)create_login_process(group);
 		return;
 	}
@@ -549,15 +541,20 @@
 	   double their amount (unless we've hit the high limit).
 	   Then for each second that didn't use all existing processes,
 	   drop the max. process count by one. */
-	if (group->wanted_processes_count < group->set->processes_count)
-		group->wanted_processes_count = group->set->processes_count;
-	else if (group->listening_processes == 0)
+	if (group->wanted_processes_count < group->set->login_processes_count) {
+		group->wanted_processes_count =
+			group->set->login_processes_count;
+	} else if (group->listening_processes == 0)
 		group->wanted_processes_count *= 2;
-	else if (group->wanted_processes_count > group->set->processes_count)
+	else if (group->wanted_processes_count >
+		 group->set->login_processes_count)
 		group->wanted_processes_count--;
 
-	if (group->wanted_processes_count > group->set->max_processes_count)
-		group->wanted_processes_count = group->set->max_processes_count;
+	if (group->wanted_processes_count >
+	    group->set->login_max_processes_count) {
+		group->wanted_processes_count =
+			group->set->login_max_processes_count;
+	}
 
 	while (group->listening_processes < group->wanted_processes_count)
 		(void)create_login_process(group);
@@ -567,12 +564,9 @@
 login_processes_start_missing(void *context __attr_unused__)
 {
 	struct login_group *group;
-	struct login_settings *login;
 
-	if (login_groups == NULL) {
-		for (login = set->logins; login != NULL; login = login->next)
-			login_group_create(login);
-	}
+	if (login_groups == NULL)
+		login_process_groups_create();
 
 	for (group = login_groups; group != NULL; group = group->next)
 		login_group_start_missings(group);
--- a/src/master/login-process.h	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/login-process.h	Thu Jul 10 06:04:07 2003 +0300
@@ -1,6 +1,20 @@
 #ifndef __LOGIN_PROCESS_H
 #define __LOGIN_PROCESS_H
 
+struct login_group {
+	struct login_group *next;
+
+	int process_type;
+	struct settings *set;
+
+	unsigned int processes;
+	unsigned int listening_processes;
+	unsigned int wanted_processes_count;
+
+	struct login_process *oldest_nonlisten_process;
+	struct login_process *newest_nonlisten_process;
+};
+
 void login_process_abormal_exit(pid_t pid);
 void login_processes_destroy_all(void);
 
--- a/src/master/mail-process.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/mail-process.c	Thu Jul 10 06:04:07 2003 +0300
@@ -9,6 +9,7 @@
 #include "restrict-process-size.h"
 #include "var-expand.h"
 #include "mail-process.h"
+#include "login-process.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -18,13 +19,20 @@
 
 static unsigned int mail_process_count = 0;
 
-static int validate_uid_gid(uid_t uid, gid_t gid, const char *user)
+static int validate_uid_gid(struct settings *set, uid_t uid, gid_t gid,
+			    const char *user)
 {
 	if (uid == 0) {
 		i_error("Logins with UID 0 not permitted (user %s)", user);
 		return FALSE;
 	}
 
+	if (set->login_uid == uid && geteuid() != uid) {
+		i_error("Can't log in using login processes UID %s (user %s) "
+			"(see login_user in config file).",
+			dec2str(uid), user);
+	}
+
 	if (uid < (uid_t)set->first_valid_uid ||
 	    (set->last_valid_uid != 0 && uid > (uid_t)set->last_valid_uid)) {
 		i_error("Logins with UID %s (user %s) not permitted "
@@ -44,7 +52,7 @@
 	return TRUE;
 }
 
-static int validate_chroot(const char *dir)
+static int validate_chroot(struct settings *set, const char *dir)
 {
 	const char *const *chroot_dirs;
 
@@ -95,30 +103,31 @@
 	return str_c(str);
 }
 
-int create_mail_process(int socket, struct ip_addr *ip,
-			const char *executable, const char *module_dir,
-			unsigned int process_size, int process_type,
+int create_mail_process(struct login_group *group, int socket,
+			struct ip_addr *ip,
 			struct auth_master_reply *reply, const char *data)
 {
 	static const char *argv[] = { NULL, NULL, NULL };
+	struct settings *set = group->set;
 	const char *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
 	char title[1024];
 	pid_t pid;
 	int i, err;
 
+	// FIXME: per-group
 	if (mail_process_count == set->max_mail_processes) {
 		i_error("Maximum number of mail processes exceeded");
 		return FALSE;
 	}
 
-	if (!validate_uid_gid(reply->uid, reply->gid,
+	if (!validate_uid_gid(set, reply->uid, reply->gid,
 			      data + reply->virtual_user_idx))
 		return FALSE;
 
 	home_dir = data + reply->home_idx;
 	chroot_dir = data + reply->chroot_idx;
 
-	if (*chroot_dir != '\0' && !validate_chroot(chroot_dir)) {
+	if (*chroot_dir != '\0' && !validate_chroot(set, chroot_dir)) {
 		i_error("Invalid chroot directory: %s", chroot_dir);
 		return FALSE;
 	}
@@ -132,11 +141,11 @@
 	if (pid != 0) {
 		/* master */
 		mail_process_count++;
-		PID_ADD_PROCESS_TYPE(pid, process_type);
+		PID_ADD_PROCESS_TYPE(pid, group->process_type);
 		return TRUE;
 	}
 
-	child_process_init_env();
+	child_process_init_env(set);
 
 	/* move the client socket into stdin and stdout fds */
 	fd_close_on_exec(socket, FALSE);
@@ -154,7 +163,7 @@
 				reply->uid, reply->gid, chroot_dir,
 				set->first_valid_gid, set->last_valid_gid);
 
-	restrict_process_size(process_size, (unsigned int)-1);
+	restrict_process_size(group->set->mail_process_size, (unsigned int)-1);
 
 	if (*home_dir != '\0') {
 		full_home_dir = *chroot_dir == '\0' ? home_dir :
@@ -200,8 +209,12 @@
 	if (set->mbox_read_dotlock)
 		env_put("MBOX_READ_DOTLOCK=1");
 
-	if (module_dir != NULL && *module_dir != '\0')
-		env_put(t_strconcat("MODULE_DIR=", module_dir, NULL));
+	if (group->set->mail_use_modules &&
+	    group->set->mail_modules != NULL &&
+	    *group->set->mail_modules != '\0') {
+		env_put(t_strconcat("MODULE_DIR=",
+				    group->set->mail_modules, NULL));
+	}
 
 	/* user given environment - may be malicious. virtual_user comes from
 	   auth process, but don't trust that too much either. Some auth
@@ -236,16 +249,20 @@
 		restrict_access_by_env(TRUE);
 
 	/* hide the path, it's ugly */
-	argv[0] = strrchr(executable, '/');
-	if (argv[0] == NULL) argv[0] = executable; else argv[0]++;
+	argv[0] = strrchr(group->set->mail_executable, '/');
+	if (argv[0] == NULL)
+		argv[0] = group->set->mail_executable;
+	else
+		argv[0]++;
 
-	execv(executable, (char **) argv);
+	execv(group->set->mail_executable, (char **) argv);
 	err = errno;
 
 	for (i = 0; i < 3; i++)
 		(void)close(i);
 
-	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
+	i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m",
+		       group->set->mail_executable);
 
 	/* not reached */
 	return FALSE;
--- a/src/master/mail-process.h	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/mail-process.h	Thu Jul 10 06:04:07 2003 +0300
@@ -1,11 +1,11 @@
 #ifndef __MAIL_PROCESS_H
 #define __MAIL_PROCESS_H
 
+struct login_group;
 struct auth_master_reply;
 
-int create_mail_process(int socket, struct ip_addr *ip,
-			const char *executable, const char *module_dir,
-			unsigned int process_size, int process_type,
+int create_mail_process(struct login_group *group, int socket,
+			struct ip_addr *ip,
 			struct auth_master_reply *reply, const char *data);
 
 void mail_process_destroyed(pid_t pid);
--- a/src/master/main.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/main.c	Thu Jul 10 06:04:07 2003 +0300
@@ -34,7 +34,7 @@
 
 struct ioloop *ioloop;
 struct hash_table *pids;
-int null_fd, mail_fd[FD_MAX], inetd_login_fd;
+int null_fd, inetd_login_fd;
 
 int validate_str(const char *str, size_t max_len)
 {
@@ -48,7 +48,7 @@
 	return FALSE;
 }
 
-void child_process_init_env(void)
+void child_process_init_env(struct settings *set)
 {
 	/* remove all environment, we don't need them */
 	env_clean();
@@ -83,7 +83,8 @@
         login_processes_destroy_all();
         auth_processes_destroy_all();
 
-	master_settings_read(configfile);
+	if (!master_settings_read(configfile))
+		i_warning("Invalid configuration, keeping old one");
 }
 
 static const char *get_exit_status_message(enum fatal_exit_status status)
@@ -218,57 +219,65 @@
 	return ip;
 }
 
-static void listen_protocols(void)
+static void listen_protocols(struct settings *set)
 {
-	struct ip_addr *imap_ip, *imaps_ip, *pop3_ip, *pop3s_ip, *ip;
+	struct ip_addr *normal_ip, *ssl_ip, *ip;
 	const char *const *proto;
-	unsigned int imap_port = 143;
-	unsigned int pop3_port = 110;
+	unsigned int normal_port, ssl_port, port;
+	int *fd;
+
+	normal_port = set->protocol == MAIL_PROTOCOL_IMAP ? 143 : 110;
 #ifdef HAVE_SSL
-	unsigned int imaps_port = 993;
-	unsigned int pop3s_port = 995;
+	ssl_port = set->protocol == MAIL_PROTOCOL_IMAP ? 993 : 995;
 #else
-	unsigned int imaps_port = 0;
-	unsigned int pop3s_port = 0;
+	ssl_port = 0;
 #endif
-	unsigned int port;
-	int *fd, i;
 
 	/* resolve */
-	imap_ip = resolve_ip(set->imap_listen, &imap_port);
-	imaps_ip = resolve_ip(set->imaps_listen, &imaps_port);
-	pop3_ip = resolve_ip(set->pop3_listen, &pop3_port);
-	pop3s_ip = resolve_ip(set->pop3s_listen, &pop3s_port);
+	normal_ip = resolve_ip(set->listen, &normal_port);
+	ssl_ip = resolve_ip(set->ssl_listen, &ssl_port);
 
-	if (imaps_ip == NULL && set->imaps_listen == NULL)
-		imaps_ip = imap_ip;
-	if (pop3s_ip == NULL && set->pop3s_listen == NULL)
-		pop3s_ip = pop3_ip;
+	if (ssl_ip == NULL && set->ssl_listen == NULL)
+		ssl_ip = normal_ip;
 
 	/* register wanted protocols */
 	for (proto = t_strsplit(set->protocols, " "); *proto != NULL; proto++) {
+		fd = NULL; ip = NULL; port = 0;
 		if (strcasecmp(*proto, "imap") == 0) {
-			fd = &mail_fd[FD_IMAP]; ip = imap_ip; port = imap_port;
+			if (set->protocol == MAIL_PROTOCOL_IMAP) {
+				fd = &set->listen_fd;
+				port = normal_port; ip = normal_ip;
+			}
 		} else if (strcasecmp(*proto, "imaps") == 0) {
-			fd = &mail_fd[FD_IMAPS]; ip = imaps_ip;
-			port = set->ssl_disable ? 0 : imaps_port;
+			if (set->protocol == MAIL_PROTOCOL_IMAP &&
+			    !set->ssl_disable) {
+				fd = &set->ssl_listen_fd;
+				port = ssl_port; ip = ssl_ip;
+			}
 		} else if (strcasecmp(*proto, "pop3") == 0) {
-			fd = &mail_fd[FD_POP3]; ip = pop3_ip; port = pop3_port;
+			if (set->protocol == MAIL_PROTOCOL_POP3) {
+				fd = &set->listen_fd;
+				port = normal_port; ip = normal_ip;
+			}
 		} else if (strcasecmp(*proto, "pop3s") == 0) {
-			fd = &mail_fd[FD_POP3S]; ip = pop3s_ip;
-			port = set->ssl_disable ? 0 : pop3s_port;
+			if (set->protocol == MAIL_PROTOCOL_POP3 &&
+			    !set->ssl_disable) {
+				fd = &set->ssl_listen_fd;
+				port = ssl_port; ip = ssl_ip;
+			}
 		} else {
 			i_fatal("Unknown protocol %s", *proto);
 		}
 
+		if (fd == NULL)
+			continue;
+
 		if (*fd != -1)
 			i_fatal("Protocol %s given more than once", *proto);
 
-		if (port == 0) {
-			*fd = dup(null_fd);
-			if (*fd == -1)
-				i_fatal("dup(null_fd) failed: %m");
-		} else {
+		if (port == 0)
+			*fd = null_fd;
+		else {
 			*fd = net_listen(ip, &port);
 			if (*fd == -1)
 				i_fatal("listen(%d) failed: %m", port);
@@ -277,19 +286,42 @@
 		fd_close_on_exec(*fd, TRUE);
 	}
 
-	for (i = 0; i < FD_MAX; i++) {
-		if (mail_fd[i] == -1) {
-			mail_fd[i] = dup(null_fd);
-			if (mail_fd[i] == -1)
-				i_fatal("dup(mail_fd[%d]) failed: %m", i);
-			fd_close_on_exec(mail_fd[i], TRUE);
-		}
+	if (set->listen_fd == -1)
+		set->listen_fd = null_fd;
+	if (set->ssl_listen_fd == -1)
+		set->ssl_listen_fd = null_fd;
+}
+
+static int have_stderr_set(struct settings *set)
+{
+	if (set->log_path != NULL &&
+	    strcmp(set->log_path, "/dev/stderr") == 0)
+		return TRUE;
+
+	if (set->info_log_path != NULL &&
+	    strcmp(set->info_log_path, "/dev/stderr") == 0)
+		return TRUE;
+
+	return FALSE;
+}
+
+static int have_stderr(struct 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;
 	}
+
+	return FALSE;
 }
 
 static void open_fds(void)
 {
-	int i;
+	struct server_settings *server;
 
 	/* initialize fds. */
 	null_fd = open("/dev/null", O_RDONLY);
@@ -303,11 +335,15 @@
 		fd_close_on_exec(null_fd, TRUE);
 	}
 
-	for (i = 0; i < FD_MAX; i++)
-		mail_fd[i] = -1;
-
-	if (!IS_INETD())
-		listen_protocols();
+	if (!IS_INETD()) {
+		server = settings_root;
+		for (; server != NULL; server = server->next) {
+			if (server->imap != NULL)
+				listen_protocols(server->imap);
+			if (server->pop3 != NULL)
+				listen_protocols(server->pop3);
+		}
+	}
 
 	/* close stdin and stdout. close stderr unless we're logging
 	   into /dev/stderr. */
@@ -316,16 +352,13 @@
 	if (dup2(null_fd, 1) < 0)
 		i_fatal("dup2(1) failed: %m");
 
-	if ((set->log_path == NULL ||
-	     strcmp(set->log_path, "/dev/stderr") != 0) &&
-	    (set->info_log_path == NULL ||
-	     strcmp(set->info_log_path, "/dev/stderr") != 0)) {
+	if (!have_stderr(settings_root)) {
 		if (dup2(null_fd, 2) < 0)
 			i_fatal("dup2(2) failed: %m");
 	}
 }
 
-static void open_logfile(void)
+static void open_logfile(struct settings *set)
 {
 	if (set->log_path == NULL)
 		i_set_failure_syslog("dovecot", LOG_NDELAY, LOG_MAIL);
@@ -347,7 +380,7 @@
 	/* deny file access from everyone else except owner */
         (void)umask(0077);
 
-	open_logfile();
+	open_logfile(settings_root->defaults);
 
 	lib_init_signals(sig_quit);
 
@@ -361,8 +394,6 @@
 
 static void main_deinit(void)
 {
-	int i;
-
         if (lib_signal_kill != 0)
 		i_warning("Killed with signal %d", lib_signal_kill);
 
@@ -378,18 +409,11 @@
 	if (close(null_fd) < 0)
 		i_error("close(null_fd) failed: %m");
 
-	for (i = 0; i < FD_MAX; i++) {
-		if (mail_fd[i] != -1) {
-			if (close(mail_fd[i]) < 0)
-				i_error("close(mail_fd[%d]) failed: %m", i);
-		}
-	}
-
 	hash_destroy(pids);
 	closelog();
 }
 
-static void daemonize(void)
+static void daemonize(struct settings *set)
 {
 	pid_t pid;
 
@@ -448,14 +472,15 @@
 
 	/* read and verify settings before forking */
 	master_settings_init();
-	master_settings_read(configfile);
+	if (!master_settings_read(configfile))
+		exit(FATAL_DEFAULT);
 	open_fds();
 
 	/* we don't need any environment */
 	env_clean();
 
 	if (!foreground)
-		daemonize();
+		daemonize(settings_root->defaults);
 
 	ioloop = io_loop_create(system_pool);
 
--- a/src/master/master-settings.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/master-settings.c	Thu Jul 10 06:04:07 2003 +0300
@@ -12,6 +12,22 @@
 #include <fcntl.h>
 #include <pwd.h>
 
+enum settings_type {
+	SETTINGS_TYPE_ROOT,
+	SETTINGS_TYPE_SERVER,
+	SETTINGS_TYPE_AUTH
+};
+
+struct settings_parse_ctx {
+	enum settings_type type, parent_type;
+	enum mail_protocol protocol;
+
+	struct server_settings *root, *server;
+	struct auth_settings *auth;
+
+	int level;
+};
+
 #define DEF(type, name) \
 	{ type, #name, offsetof(struct settings, name) }
 
@@ -24,10 +40,8 @@
 
 	/* general */
 	DEF(SET_STR, protocols),
-	DEF(SET_STR, imap_listen),
-	DEF(SET_STR, imaps_listen),
-	DEF(SET_STR, pop3_listen),
-	DEF(SET_STR, pop3s_listen),
+	DEF(SET_STR, listen),
+	DEF(SET_STR, ssl_listen),
 
 	DEF(SET_BOOL, ssl_disable),
 	DEF(SET_STR, ssl_cert_file),
@@ -35,11 +49,20 @@
 	DEF(SET_STR, ssl_parameters_file),
 	DEF(SET_STR, ssl_parameters_regenerate),
 	DEF(SET_BOOL, disable_plaintext_auth),
+	DEF(SET_BOOL, verbose_ssl),
 
 	/* login */
 	DEF(SET_STR, login_dir),
+	DEF(SET_STR, login_executable),
+	DEF(SET_STR, login_user),
+
+	DEF(SET_BOOL, login_process_per_connection),
 	DEF(SET_BOOL, login_chroot),
-	DEF(SET_BOOL, verbose_ssl),
+
+	DEF(SET_INT, login_process_size),
+	DEF(SET_INT, login_processes_count),
+	DEF(SET_INT, login_max_processes_count),
+	DEF(SET_INT, login_max_logging_users),
 
 	/* mail */
 	DEF(SET_STR, valid_chroot_dirs),
@@ -70,36 +93,13 @@
 	DEF(SET_INT, umask),
 	DEF(SET_BOOL, mail_drop_priv_before_exec),
 
-	/* imap */
-	DEF(SET_STR, imap_executable),
-	DEF(SET_INT, imap_process_size),
-	DEF(SET_INT, imap_max_line_length),
-	DEF(SET_BOOL, imap_use_modules),
-	DEF(SET_STR, imap_modules),
-
-	/* pop3 */
-	DEF(SET_STR, pop3_executable),
-	DEF(SET_INT, pop3_process_size),
-	DEF(SET_BOOL, pop3_use_modules),
-	DEF(SET_STR, pop3_modules),
+	DEF(SET_STR, mail_executable),
+	DEF(SET_INT, mail_process_size),
+	DEF(SET_BOOL, mail_use_modules),
+	DEF(SET_STR, mail_modules),
 
-	{ 0, NULL, 0 }
-};
-
-#undef DEF
-#define DEF(type, name) \
-	{ type, #name, offsetof(struct login_settings, name) }
-
-static struct setting_def login_setting_defs[] = {
-	DEF(SET_STR, executable),
-	DEF(SET_STR, user),
-
-	DEF(SET_BOOL, process_per_connection),
-
-	DEF(SET_INT, process_size),
-	DEF(SET_INT, processes_count),
-	DEF(SET_INT, max_processes_count),
-	DEF(SET_INT, max_logging_users),
+	/* imap */
+	DEF(SET_INT, imap_max_line_length),
 
 	{ 0, NULL, 0 }
 };
@@ -130,6 +130,9 @@
 };
 
 struct settings default_settings = {
+	MEMBER(server) NULL,
+	MEMBER(protocol) 0,
+
 	/* common */
 	MEMBER(base_dir) PKG_RUNDIR,
 	MEMBER(log_path) NULL,
@@ -138,10 +141,8 @@
 
 	/* general */
 	MEMBER(protocols) "imap imaps",
-	MEMBER(imap_listen) "*",
-	MEMBER(imaps_listen) NULL,
-	MEMBER(pop3_listen) "*",
-	MEMBER(pop3s_listen) NULL,
+	MEMBER(listen) "*",
+	MEMBER(ssl_listen) NULL,
 
 	MEMBER(ssl_disable) FALSE,
 	MEMBER(ssl_cert_file) SSLDIR"/certs/dovecot.pem",
@@ -149,11 +150,20 @@
 	MEMBER(ssl_parameters_file) "ssl-parameters.dat",
 	MEMBER(ssl_parameters_regenerate) 24,
 	MEMBER(disable_plaintext_auth) FALSE,
+	MEMBER(verbose_ssl) FALSE,
 
 	/* login */
 	MEMBER(login_dir) "login",
+	MEMBER(login_executable) NULL,
+	MEMBER(login_user) "dovecot",
+
+	MEMBER(login_process_per_connection) TRUE,
 	MEMBER(login_chroot) TRUE,
-	MEMBER(verbose_ssl) FALSE,
+
+	MEMBER(login_process_size) 16,
+	MEMBER(login_processes_count) 3,
+	MEMBER(login_max_processes_count) 128,
+	MEMBER(login_max_logging_users) 256,
 
 	/* mail */
 	MEMBER(valid_chroot_dirs) NULL,
@@ -184,42 +194,23 @@
 	MEMBER(umask) 0077,
 	MEMBER(mail_drop_priv_before_exec) FALSE,
 
-	/* imap */
-	MEMBER(imap_executable) PKG_LIBEXECDIR"/imap",
-	MEMBER(imap_process_size) 256,
-	MEMBER(imap_max_line_length) 65536,
-	MEMBER(imap_use_modules) FALSE,
-	MEMBER(imap_modules) PKG_LIBDIR"/imap",
+	MEMBER(mail_executable) PKG_LIBEXECDIR"/imap",
+	MEMBER(mail_process_size) 256,
+	MEMBER(mail_use_modules) FALSE,
+	MEMBER(mail_modules) PKG_LIBDIR"/imap",
 
-	/* pop3 */
-	MEMBER(pop3_executable) PKG_LIBEXECDIR"/pop3",
-	MEMBER(pop3_process_size) 256,
-	MEMBER(pop3_use_modules) FALSE,
-	MEMBER(pop3_modules) PKG_LIBDIR"/imap",
+	/* imap */
+	MEMBER(imap_max_line_length) 65536,
 
+	/* .. */
+	MEMBER(login_uid) 0,
 	MEMBER(login_gid) 0,
-	MEMBER(auths) NULL,
-	MEMBER(logins) NULL
-};
-
-struct login_settings default_login_settings = {
-	MEMBER(next) NULL,
-	MEMBER(name) NULL,
-
-	MEMBER(executable) NULL,
-	MEMBER(user) "dovecot",
-
-	MEMBER(process_per_connection) TRUE,
-
-	MEMBER(process_size) 16,
-	MEMBER(processes_count) 3,
-	MEMBER(max_processes_count) 128,
-	MEMBER(max_logging_users) 256,
-
-	MEMBER(uid) 0 /* generated */
+	MEMBER(listen_fd) -1,
+	MEMBER(ssl_listen_fd) -1
 };
 
 struct auth_settings default_auth_settings = {
+	MEMBER(parent) NULL,
 	MEMBER(next) NULL,
 
 	MEMBER(name) NULL,
@@ -241,8 +232,8 @@
 	MEMBER(process_size) 256
 };
 
-static pool_t settings_pool;
-struct settings *set = NULL;
+static pool_t settings_pool, settings2_pool;
+struct server_settings *settings_root = NULL;
 
 static void fix_base_path(struct settings *set, const char **str)
 {
@@ -252,49 +243,42 @@
 	}
 }
 
-static void get_login_uid(struct settings *set,
-			  struct login_settings *login_set)
+static int get_login_uid(struct settings *set)
 {
 	struct passwd *pw;
 
-	if ((pw = getpwnam(login_set->user)) == NULL)
-		i_fatal("Login user doesn't exist: %s", login_set->user);
+	if ((pw = getpwnam(set->login_user)) == NULL) {
+		i_error("Login user doesn't exist: %s", set->login_user);
+		return FALSE;
+	}
 
 	if (set->login_gid == 0)
 		set->login_gid = pw->pw_gid;
 	else if (set->login_gid != pw->pw_gid) {
-		i_fatal("All login process users must belong to same group "
+		i_error("All login process users must belong to same group "
 			"(%s vs %s)", dec2str(set->login_gid),
 			dec2str(pw->pw_gid));
+		return FALSE;
 	}
 
-	login_set->uid = pw->pw_uid;
+	set->login_uid = pw->pw_uid;
+	return TRUE;
 }
 
-static void auth_settings_verify(struct auth_settings *auth)
+static int auth_settings_verify(struct auth_settings *auth)
 {
-	if (access(auth->executable, X_OK) < 0)
-		i_fatal("Can't use auth executable %s: %m", auth->executable);
-
-	fix_base_path(set, &auth->chroot);
-	if (auth->chroot != NULL && access(auth->chroot, X_OK) < 0) {
-		i_fatal("Can't access auth chroot directory %s: %m",
-			auth->chroot);
-	}
-}
-
-static void login_settings_verify(struct login_settings *login)
-{
-	if (strstr(set->protocols, login->name) != NULL) {
-		if (access(login->executable, X_OK) < 0)
-			i_fatal("Can't use login executable %s: %m",
-				login->executable);
+	if (access(auth->executable, X_OK) < 0) {
+		i_error("Can't use auth executable %s: %m", auth->executable);
+		return FALSE;
 	}
 
-	if (login->processes_count < 1)
-		i_fatal("login_processes_count must be at least 1");
-	if (login->max_logging_users < 1)
-		i_fatal("max_logging_users must be at least 1");
+	fix_base_path(auth->parent->defaults, &auth->chroot);
+	if (auth->chroot != NULL && access(auth->chroot, X_OK) < 0) {
+		i_error("Can't access auth chroot directory %s: %m",
+			auth->chroot);
+		return FALSE;
+	}
+	return TRUE;
 }
 
 static const char *get_directory(const char *path)
@@ -311,82 +295,78 @@
 	}
 }
 
-static void settings_verify(struct settings *set)
+static int settings_is_active(struct settings *set)
 {
-	struct login_settings *login;
-	struct auth_settings *auth;
+	if (set->protocol == MAIL_PROTOCOL_IMAP) {
+		if (strstr(set->protocols, "imap") == NULL)
+			return FALSE;
+	} else {
+		if (strstr(set->protocols, "pop3") == NULL)
+			return FALSE;
+	}
+
+	return TRUE;
+}
+
+static int settings_verify(struct settings *set)
+{
 	const char *const *str;
 	const char *dir;
 	int dotlock_got, fcntl_got, flock_got;
 
-	for (login = set->logins; login != NULL; login = login->next) {
-		get_login_uid(set, login);
-		login_settings_verify(login);
+	if (!get_login_uid(set))
+		return FALSE;
+
+	if (access(set->mail_executable, X_OK) < 0) {
+		i_error("Can't use mail executable %s: %m",
+			set->mail_executable);
+		return FALSE;
 	}
 
-	if (strstr(set->protocols, "imap") != NULL) {
-		if (access(set->imap_executable, X_OK) < 0) {
-			i_fatal("Can't use imap executable %s: %m",
-				set->imap_executable);
-		}
 #ifdef HAVE_MODULES
-		if (set->imap_use_modules &&
-		    access(set->imap_modules, R_OK | X_OK) < 0) {
-			i_fatal("Can't access imap module directory: %s: %m",
-				set->imap_modules);
-		}
-#else
-		if (set->imap_use_modules) {
-			i_warning("Module support wasn't built into Dovecot, "
-				  "ignoring imap_use_modules setting");
-		}
-#endif
+	if (set->mail_use_modules &&
+	    access(set->mail_modules, R_OK | X_OK) < 0) {
+		i_error("Can't access mail module directory: %s: %m",
+			set->mail_modules);
+		return FALSE;
 	}
-
-	if (strstr(set->protocols, "pop3") != NULL) {
-		if (access(set->pop3_executable, X_OK) < 0) {
-			i_fatal("Can't use pop3 executable %s: %m",
-				set->pop3_executable);
-		}
-#ifdef HAVE_MODULES
-		if (set->pop3_use_modules &&
-		    access(set->pop3_modules, R_OK | X_OK) < 0) {
-			i_fatal("Can't access pop3 module directory: %s: %m",
-				set->imap_modules);
-		}
 #else
-		if (set->pop3_use_modules) {
-			i_warning("Module support wasn't built into Dovecot, "
-				  "ignoring pop3_use_modules setting");
-		}
+	if (set->mail_use_modules) {
+		i_warning("Module support wasn't built into Dovecot, "
+			  "ignoring mail_use_modules setting");
+	}
 #endif
-	}
 
 	if (set->log_path != NULL && access(set->log_path, W_OK) < 0) {
 		dir = get_directory(set->log_path);
-		if (access(dir, W_OK) < 0)
-			i_fatal("Can't write to log directory %s: %m", dir);
+		if (access(dir, W_OK) < 0) {
+			i_error("Can't write to log directory %s: %m", dir);
+			return FALSE;
+		}
 	}
 
 	if (set->info_log_path != NULL &&
 	    access(set->info_log_path, W_OK) < 0) {
 		dir = get_directory(set->info_log_path);
 		if (access(dir, W_OK) < 0) {
-			i_fatal("Can't write to info log directory %s: %m",
+			i_error("Can't write to info log directory %s: %m",
 				dir);
+			return FALSE;
 		}
 	}
 
 #ifdef HAVE_SSL
 	if (!set->ssl_disable) {
 		if (access(set->ssl_cert_file, R_OK) < 0) {
-			i_fatal("Can't use SSL certificate %s: %m",
+			i_error("Can't use SSL certificate %s: %m",
 				set->ssl_cert_file);
+			return FALSE;
 		}
 
 		if (access(set->ssl_key_file, R_OK) < 0) {
-			i_fatal("Can't use SSL key file %s: %m",
+			i_error("Can't use SSL key file %s: %m",
 				set->ssl_key_file);
+			return FALSE;
 		}
 	}
 #endif
@@ -403,23 +383,31 @@
 	}
 
 	/* wipe out contents of login directory, if it exists */
-	if (unlink_directory(set->login_dir, FALSE) < 0)
-		i_fatal("unlink_directory() failed for %s: %m", set->login_dir);
+	if (unlink_directory(set->login_dir, FALSE) < 0) {
+		i_error("unlink_directory() failed for %s: %m", set->login_dir);
+		return FALSE;
+	}
 
 	if (safe_mkdir(set->login_dir, 0750, geteuid(), set->login_gid) == 0) {
 		i_warning("Corrected permissions for login directory %s",
 			  set->login_dir);
 	}
 
-	if (set->max_mail_processes < 1)
-		i_fatal("max_mail_processes must be at least 1");
+	if (set->max_mail_processes < 1) {
+		i_error("max_mail_processes must be at least 1");
+		return FALSE;
+	}
 
 	if (set->last_valid_uid != 0 &&
-	    set->first_valid_uid > set->last_valid_uid)
-		i_fatal("first_valid_uid can't be larger than last_valid_uid");
+	    set->first_valid_uid > set->last_valid_uid) {
+		i_error("first_valid_uid can't be larger than last_valid_uid");
+		return FALSE;
+	}
 	if (set->last_valid_gid != 0 &&
-	    set->first_valid_gid > set->last_valid_gid)
-		i_fatal("first_valid_gid can't be larger than last_valid_gid");
+	    set->first_valid_gid > set->last_valid_gid) {
+		i_error("first_valid_gid can't be larger than last_valid_gid");
+		return FALSE;
+	}
 
 	dotlock_got = fcntl_got = flock_got = FALSE;
 	for (str = t_strsplit(set->mbox_locks, " "); *str != NULL; str++) {
@@ -429,20 +417,25 @@
 			fcntl_got = TRUE;
 		else if (strcasecmp(*str, "flock") == 0)
 			flock_got = TRUE;
-		else
-			i_fatal("mbox_locks: Invalid value %s", *str);
+		else {
+			i_error("mbox_locks: Invalid value %s", *str);
+			return FALSE;
+		}
 	}
 
 #ifndef HAVE_FLOCK
 	if (fcntl_got && !dotlock_got && !flock_got) {
-		i_fatal("mbox_locks: Only flock selected, "
+		i_error("mbox_locks: Only flock selected, "
 			"and flock() isn't supported in this system");
+		return FALSE;
 	}
 	flock_got = FALSE;
 #endif
 
-	if (!dotlock_got && !fcntl_got && !flock_got)
-		i_fatal("mbox_locks: No mbox locking methods selected");
+	if (!dotlock_got && !fcntl_got && !flock_got) {
+		i_error("mbox_locks: No mbox locking methods selected");
+		return FALSE;
+	}
 
 	if (dotlock_got && !set->mbox_read_dotlock &&
 	    !fcntl_got && !flock_got) {
@@ -451,11 +444,26 @@
                 set->mbox_read_dotlock = TRUE;
 	}
 
-	for (auth = set->auths; auth != NULL; auth = auth->next)
-		auth_settings_verify(auth);
+	if (access(set->login_executable, X_OK) < 0) {
+		i_error("Can't use login executable %s: %m",
+			set->login_executable);
+		return FALSE;
+	}
+
+	if (set->login_processes_count < 1) {
+		i_error("login_processes_count must be at least 1");
+		return FALSE;
+	}
+	if (set->login_max_logging_users < 1) {
+		i_error("login_max_logging_users must be at least 1");
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
-static void auth_settings_new(struct settings *set, const char *name)
+static struct auth_settings *
+auth_settings_new(struct server_settings *server, const char *name)
 {
 	struct auth_settings *auth;
 
@@ -463,124 +471,282 @@
 
 	/* copy defaults */
 	*auth = default_auth_settings;
+	auth->parent = server;
 	auth->name = p_strdup(settings_pool, name);
 
-	auth->next = set->auths;
-        set->auths = auth;
+	auth->next = server->auths;
+	server->auths = auth;
+
+	return auth;
 }
 
-static const char *parse_new_auth(struct settings *set, const char *name)
+static struct auth_settings *
+parse_new_auth(struct server_settings *server, const char *name,
+	       const char **errormsg)
 {
 	struct auth_settings *auth;
 
-	if (strchr(name, '/') != NULL)
-		return "Authentication process name must not contain '/'";
+	if (strchr(name, '/') != NULL) {
+		*errormsg = "Authentication process name must not contain '/'";
+		return NULL;
+	}
 
-	for (auth = set->auths; auth != NULL; auth = auth->next) {
+	for (auth = server->auths; auth != NULL; auth = auth->next) {
 		if (strcmp(auth->name, name) == 0) {
-			return "Authentication process already exists "
+			*errormsg = "Authentication process already exists "
 				"with the same name";
+			return NULL;
 		}
 	}
 
-	auth_settings_new(set, name);
-	return NULL;
-}
-
-static void login_settings_new(struct settings *set, const char *name)
-{
-	struct login_settings *login;
-
-	login = p_new(settings_pool, struct login_settings, 1);
-
-	/* copy defaults */
-	*login = set->logins != NULL ? *set->logins :
-		default_login_settings;
-
-	if (strcasecmp(name, "imap") == 0) {
-		login->name = "imap";
-		login->executable = PKG_LIBEXECDIR"/imap-login";
-	} else if (strcasecmp(name, "pop3") == 0) {
-		login->name = "pop3";
-		login->executable = PKG_LIBEXECDIR"/pop3-login";
-	} else {
-		i_fatal("Unknown login process type '%s'", name);
-	}
-
-	login->next = set->logins;
-	set->logins = login;
-}
-
-static const char *parse_new_login(struct settings *set, const char *name)
-{
-	struct login_settings *login;
-
-	for (login = set->logins; login != NULL; login = login->next) {
-		if (strcmp(login->name, name) == 0) {
-			return "Login process already exists "
-				"with the same name";
-		}
-	}
-
-	login_settings_new(set, name);
-	return NULL;
+	return auth_settings_new(server, name);
 }
 
 static const char *parse_setting(const char *key, const char *value,
 				 void *context)
 {
-	struct settings *set = context;
+	struct settings_parse_ctx *ctx = context;
 	const char *error;
 
-	/* check defaults first, there's a few login_ settings defined in it
-	   which need to be checked before trying to feed it to login
-	   handler.. */
-	error = parse_setting_from_defs(settings_pool, setting_defs,
-					set, key, value);
-	if (error == NULL)
+	/* backwards compatibility */
+	if (strcmp(key, "auth") == 0) {
+		ctx->auth = parse_new_auth(ctx->server, value, &error);
+		return ctx->auth == NULL ? error : NULL;
+	}
+
+	if (strcmp(key, "login") == 0) {
+		i_warning("Ignoring deprecated 'login' section handling. "
+			  "Use protocol imap/pop3 { .. } instead. "
+			  "Some settings may have been read incorrectly.");
 		return NULL;
+	}
+
+	switch (ctx->type) {
+	case SETTINGS_TYPE_ROOT:
+	case SETTINGS_TYPE_SERVER:
+		error = NULL;
+		if (ctx->protocol == MAIL_PROTOCOL_ANY ||
+		    ctx->protocol == MAIL_PROTOCOL_IMAP) {
+			error = parse_setting_from_defs(settings_pool,
+							setting_defs,
+							ctx->server->imap,
+							key, value);
+		}
+
+		if (error == NULL &&
+		    (ctx->protocol == MAIL_PROTOCOL_ANY ||
+		     ctx->protocol == MAIL_PROTOCOL_POP3)) {
+			error = parse_setting_from_defs(settings_pool,
+							setting_defs,
+							ctx->server->pop3,
+							key, value);
+		}
+
+		if (error == NULL)
+			return NULL;
+
+		/* backwards compatibility */
+		if (strncmp(key, "auth_", 5) == 0) {
+			if (ctx->auth == NULL) {
+				return "Authentication process name "
+					"not defined yet";
+			}
 
-	if (strcmp(key, "auth") == 0)
-		return parse_new_auth(set, value);
-	if (strncmp(key, "auth_", 5) == 0) {
-		if (set->auths == NULL)
-			return "Authentication process name not defined yet";
+			return parse_setting_from_defs(settings_pool,
+						       auth_setting_defs,
+						       ctx->auth,
+						       key + 5, value);
+		}
+		return error;
+	case SETTINGS_TYPE_AUTH:
+		return parse_setting_from_defs(settings_pool, auth_setting_defs,
+					       ctx->auth, key + 5, value);
+	}
+
+	i_unreached();
+}
+
+static struct server_settings *
+create_new_server(const char *name,
+		  struct settings *imap_defaults,
+		  struct settings *pop3_defaults)
+{
+	struct server_settings *server;
+
+	server = p_new(settings_pool, struct server_settings, 1);
+	server->name = p_strdup(settings_pool, name);
+	server->imap = p_new(settings_pool, struct settings, 1);
+	server->pop3 = p_new(settings_pool, struct settings, 1);
+
+	*server->imap = *imap_defaults;
+	*server->pop3 = *pop3_defaults;
 
-		return parse_setting_from_defs(settings_pool, auth_setting_defs,
-					       set->auths, key + 5, value);
+	server->imap->protocol = MAIL_PROTOCOL_IMAP;
+	server->imap->login_executable = PKG_LIBEXECDIR"/imap-login";
+	server->imap->mail_executable = PKG_LIBEXECDIR"/imap";
+
+	server->pop3->protocol = MAIL_PROTOCOL_POP3;
+	server->pop3->login_executable = PKG_LIBEXECDIR"/pop3-login";
+	server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3";
+
+	return server;
+}
+
+static int parse_section(const char *type, const char *name, void *context,
+			 const char **errormsg)
+{
+	struct settings_parse_ctx *ctx = context;
+	struct server_settings *server;
+
+	if (type == NULL) {
+		/* section closing */
+		if (ctx->level > 0) {
+			ctx->level--;
+			ctx->protocol = MAIL_PROTOCOL_ANY;
+		} else {
+			ctx->type = ctx->parent_type;
+			ctx->parent_type = SETTINGS_TYPE_ROOT;
+			ctx->server = ctx->root;
+			ctx->auth = NULL;
+		}
+		return TRUE;
 	}
 
-	if (strcmp(key, "login") == 0)
-		return parse_new_login(set, value);
-	if (strncmp(key, "login_", 6) == 0) {
-		if (set->logins == NULL)
-			return "Login process name not defined yet";
+	if (strcmp(type, "server") == 0) {
+		if (ctx->type != SETTINGS_TYPE_ROOT) {
+			*errormsg = "Server section not allowed here";
+			return FALSE;
+		}
+
+		ctx->parent_type = ctx->type;
+		ctx->type = SETTINGS_TYPE_SERVER;
+
+		ctx->server = create_new_server(name,
+						ctx->server->imap,
+						ctx->server->pop3);
+                server = ctx->root;
+		while (server->next != NULL)
+			server = server->next;
+		server->next = ctx->server;
+		return TRUE;
+	}
 
-		return parse_setting_from_defs(settings_pool,
-					       login_setting_defs,
-					       set->logins, key + 6, value);
+	if (strcmp(type, "protocol") == 0) {
+		if ((ctx->type != SETTINGS_TYPE_ROOT &&
+		     ctx->type != SETTINGS_TYPE_SERVER) ||
+		    ctx->level != 0) {
+			*errormsg = "Protocol section not allowed here";
+			return FALSE;
+		}
+
+		if (strcmp(name, "imap") == 0)
+			ctx->protocol = MAIL_PROTOCOL_IMAP;
+		else if (strcmp(name, "pop3") == 0)
+			ctx->protocol = MAIL_PROTOCOL_POP3;
+		else {
+			*errormsg = "Unknown protocol name";
+			return FALSE;
+		}
+		ctx->level++;
+		return TRUE;
+	}
+
+	if (strcmp(type, "auth") == 0) {
+		if (ctx->type != SETTINGS_TYPE_ROOT &&
+		    ctx->type != SETTINGS_TYPE_SERVER) {
+			*errormsg = "Auth section not allowed here";
+			return FALSE;
+		}
+
+		ctx->type = SETTINGS_TYPE_AUTH;
+		ctx->auth = parse_new_auth(ctx->server, name, errormsg);
+		return ctx->auth != NULL;
 	}
 
-	return error;
+	*errormsg = "Unknown section type";
+	return FALSE;
 }
 
-void master_settings_read(const char *path)
+int master_settings_read(const char *path)
 {
+	struct settings_parse_ctx ctx;
+	struct server_settings *server, *prev;
+	struct auth_settings *auth;
+	pool_t temp;
+
+	memset(&ctx, 0, sizeof(ctx));
+
 	p_clear(settings_pool);
-	set = p_new(settings_pool, struct settings, 1);
-	*set = default_settings;
+
+	ctx.type = SETTINGS_TYPE_ROOT;
+	ctx.protocol = MAIL_PROTOCOL_ANY;
+	ctx.server = ctx.root =
+		create_new_server("default",
+				  &default_settings, &default_settings);
+
+	if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
+		return FALSE;
+
+	if (ctx.level != 0) {
+		i_error("Missing '}'");
+		return FALSE;
+	}
+
+	/* If server sections were defined, skip the root */
+	if (ctx.root->next != NULL)
+		ctx.root = ctx.root->next;
+
+	prev = NULL;
+	for (server = ctx.root; server != NULL; server = server->next) {
+		if (!settings_is_active(server->imap))
+			server->imap = NULL;
+		else {
+			if (!settings_verify(server->imap))
+				return FALSE;
+			server->defaults = server->imap;
+		}
 
-	settings_read(path, parse_setting, set);
+		if (!settings_is_active(server->pop3))
+			server->pop3 = NULL;
+		else {
+			if (!settings_verify(server->pop3))
+				return FALSE;
+			if (server->defaults == NULL)
+				server->defaults = server->pop3;
+		}
 
-        settings_verify(set);
+		if (server->defaults == NULL) {
+			if (prev == NULL)
+				ctx.root = server->next;
+			else
+				prev->next = server->next;
+		} else {
+                        auth = server->auths;
+			for (; auth != NULL; auth = auth->next) {
+				if (!auth_settings_verify(auth))
+					return FALSE;
+			}
+			prev = server;
+		}
+	}
+
+	/* settings ok, swap them */
+	temp = settings_pool;
+	settings_pool = settings2_pool;
+	settings2_pool = temp;
+
+	settings_root = ctx.root;
+	return TRUE;
 }
 
 void master_settings_init(void)
 {
-	settings_pool = pool_alloconly_create("settings", 1024);
+	settings_pool = pool_alloconly_create("settings", 2048);
+	settings2_pool = pool_alloconly_create("settings2", 2048);
 }
 
 void master_settings_deinit(void)
 {
 	pool_unref(settings_pool);
+	pool_unref(settings2_pool);
 }
--- a/src/master/master-settings.h	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/master-settings.h	Thu Jul 10 06:04:07 2003 +0300
@@ -1,7 +1,26 @@
 #ifndef __MASTER_SETTINGS_H
 #define __MASTER_SETTINGS_H
 
+enum mail_protocol {
+        MAIL_PROTOCOL_ANY,
+        MAIL_PROTOCOL_IMAP,
+        MAIL_PROTOCOL_POP3
+};
+
+struct server_settings {
+	struct server_settings *next;
+
+	const char *name;
+	struct settings *defaults;
+	struct settings *imap;
+	struct settings *pop3;
+	struct auth_settings *auths;
+};
+
 struct settings {
+	struct server_settings *server;
+	enum mail_protocol protocol;
+
 	/* common */
 	const char *base_dir;
 	const char *log_path;
@@ -10,10 +29,8 @@
 
 	/* general */
 	const char *protocols;
-	const char *imap_listen;
-	const char *imaps_listen;
-	const char *pop3_listen;
-	const char *pop3s_listen;
+	const char *listen;
+	const char *ssl_listen;
 
 	int ssl_disable;
 	const char *ssl_cert_file;
@@ -21,11 +38,20 @@
 	const char *ssl_parameters_file;
 	unsigned int ssl_parameters_regenerate;
 	int disable_plaintext_auth;
+	int verbose_ssl;
 
 	/* login */
 	const char *login_dir;
+	const char *login_executable;
+	const char *login_user;
+
+	int login_process_per_connection;
 	int login_chroot;
-	int verbose_ssl;
+
+	unsigned int login_process_size;
+	unsigned int login_processes_count;
+	unsigned int login_max_processes_count;
+	unsigned int login_max_logging_users;
 
 	/* mail */
 	const char *valid_chroot_dirs;
@@ -47,51 +73,30 @@
 	int mail_read_mmaped;
 	int maildir_copy_with_hardlinks;
 	int maildir_check_content_changes;
-	char *mbox_locks;
+	const char *mbox_locks;
 	int mbox_read_dotlock;
 	unsigned int mbox_lock_timeout;
 	unsigned int mbox_dotlock_change_timeout;
 	unsigned int umask;
 	int mail_drop_priv_before_exec;
 
+	const char *mail_executable;
+	unsigned int mail_process_size;
+	int mail_use_modules;
+	const char *mail_modules;
+
 	/* imap */
-	const char *imap_executable;
-	unsigned int imap_process_size;
 	unsigned int imap_max_line_length;
-	int imap_use_modules;
-	const char *imap_modules;
-
-	/* pop3 */
-	const char *pop3_executable;
-	unsigned int pop3_process_size;
-	int pop3_use_modules;
-	const char *pop3_modules;
 
 	/* .. */
+	uid_t login_uid;
 	gid_t login_gid;
 
-	struct auth_settings *auths;
-	struct login_settings *logins;
-};
-
-struct login_settings {
-	struct login_settings *next;
-
-	const char *name;
-	const char *executable;
-	const char *user;
-
-	int process_per_connection;
-
-	unsigned int process_size;
-	unsigned int processes_count;
-	unsigned int max_processes_count;
-	unsigned int max_logging_users;
-
-	uid_t uid; /* gid must be always same with all login processes */
+	int listen_fd, ssl_listen_fd;
 };
 
 struct auth_settings {
+	struct server_settings *parent;
 	struct auth_settings *next;
 
 	const char *name;
@@ -112,9 +117,9 @@
 	unsigned int process_size;
 };
 
-extern struct settings *set;
+extern struct server_settings *settings_root;
 
-void master_settings_read(const char *path);
+int master_settings_read(const char *path);
 
 void master_settings_init(void);
 void master_settings_deinit(void);
--- a/src/master/ssl-init.c	Thu Jul 10 03:40:25 2003 +0300
+++ b/src/master/ssl-init.c	Thu Jul 10 06:04:07 2003 +0300
@@ -38,7 +38,7 @@
 		i_fatal("rename(%s, %s) failed: %m", temp_fname, fname);
 }
 
-static void start_generate_process(void)
+static void start_generate_process(struct settings *set)
 {
 	pid_t pid;
 
@@ -64,19 +64,19 @@
 	generating = FALSE;
 }
 
-static void check_parameters_file(void *context __attr_unused__)
+static int check_parameters_file_set(struct settings *set)
 {
 	struct stat st;
 	time_t regen_time;
 
-	if (set->ssl_parameters_file == NULL || set->ssl_disable || generating)
-		return;
+	if (set->ssl_parameters_file == NULL || set->ssl_disable)
+		return TRUE;
 
 	if (lstat(set->ssl_parameters_file, &st) < 0) {
 		if (errno != ENOENT) {
 			i_error("lstat() failed for SSL parameters file %s: %m",
 				set->ssl_parameters_file);
-			return;
+			return TRUE;
 		}
 
 		st.st_mtime = 0;
@@ -86,8 +86,29 @@
 	regen_time = st.st_mtime +
 		(time_t)(set->ssl_parameters_regenerate*3600);
 	if (regen_time < ioloop_time || (st.st_mode & 077) != 0 ||
-	    st.st_uid != geteuid() || st.st_gid != getegid())
-		start_generate_process();
+	    st.st_uid != geteuid() || st.st_gid != getegid()) {
+		start_generate_process(set);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+static void check_parameters_file(void *context __attr_unused__)
+{
+	struct server_settings *server;
+
+	if (generating)
+		return;
+
+	for (server = settings_root; server != NULL; server = server->next) {
+		if (server->imap != NULL &&
+		    !check_parameters_file_set(server->imap))
+			break;
+		if (server->pop3 != NULL &&
+		    !check_parameters_file_set(server->pop3))
+			break;
+	}
 }
 
 void ssl_init(void)