]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
New configuration file code. Some syntax changes, but tries to be somewhat
authorTimo Sirainen <tss@iki.fi>
Thu, 10 Jul 2003 03:04:07 +0000 (06:04 +0300)
committerTimo Sirainen <tss@iki.fi>
Thu, 10 Jul 2003 03:04:07 +0000 (06:04 +0300)
backwards compatible. SIGHUP now reverts back to old configuration if it
detected errors in new one.

--HG--
branch : HEAD

15 files changed:
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

index 53f426483148f4996d9836097534d759095b301f..07b47d92dce53ddcff581fc916297c77d3dcb60e 100644 (file)
 # "[::]" 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
 # 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 @@ login = imap
 # 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 @@ login = pop3
 # for multiple users, as the users could ptrace() each others processes then.
 #mail_drop_priv_before_exec = no
 
+# 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.
+#mail_process_size = 256
+
 ##
-## IMAP process
+## IMAP specific settings
 ##
 
-# 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
+protocol imap {
+  # Login executable location.
+  #login_executable = /usr/libexec/dovecot/imap-login
 
-# 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
+  # IMAP executable location
+  #mail_executable = /usr/libexec/dovecot/imap
 
-# Support for dynamically loadable modules.
-#imap_use_modules = no
-#imap_modules = /usr/lib/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
 
+  # Support for dynamically loadable modules.
+  #mail_use_modules = no
+  #mail_modules = /usr/lib/dovecot/imap
+}
+  
 ##
-## POP3 process
+## POP3 specific settings
 ##
 
-# Executable location
-#pop3_executable = /usr/libexec/dovecot/pop3
+protocol pop3 {
+  # Login executable location.
+  #login_executable = /usr/libexec/dovecot/pop3-login
 
-# 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
+  # POP3 executable location
+  #mail_executable = /usr/libexec/dovecot/pop3
 
-# Support for dynamically loadable modules.
-#pop3_use_modules = no
-#pop3_modules = /usr/lib/dovecot/pop3
+  # Support for dynamically loadable modules.
+  #mail_use_modules = no
+  #mail_modules = /usr/lib/dovecot/pop3
+}
 
 ##
 ## Authentication processes
@@ -358,12 +341,11 @@ login = pop3
 # 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 @@ auth_mechanisms = plain
 # 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 @@ auth_user = root
 # 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"
index dd33bc820c8f471a5e56b88656dcef6f7121ba8a..0f491558730c7ecbe86faefc5dd95e6348a94580 100644 (file)
@@ -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 @@ struct ldap_connection *db_ldap_init(const char *config_path)
 
        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");
index 821b28996778fb9c91c53c1ff835c249ea81474c..35dc6c8378d2c3e5eead48db17b0fcde503bdaa4 100644 (file)
@@ -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 @@ struct pgsql_connection *db_pgsql_init(const char *config_path)
 
        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);
 
index e0fc8b08217860ce49389d59157aae1276a586ae..f3731375d20a745bf6f0415e2fdeb0e4e2a320c0 100644 (file)
@@ -59,19 +59,32 @@ parse_setting_from_defs(pool_t pool, struct setting_def *defs, void *base,
 
 #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();
+
+       if (section == NULL) {
+               skip = 0;
+                next_section = NULL;
+       } else {
+               skip = 1;
+               next_section = t_strcut(section, '/');
+       }
 
-       linenum = 0;
+       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 @@ void settings_read(const char *path, settings_callback_t *callback,
                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 @@ void settings_read(const char *path, settings_callback_t *callback,
                        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;
 }
index c56ea44a094790cc9d92d92bee47b7196c4cdeb5..f01ec3a3e689b33fff92d33b92267a89bf9a881d 100644 (file)
@@ -13,14 +13,20 @@ struct setting_def {
        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
index 94bb82b67d7bf46c5f9a64271f08441a463c7732..7a2732ce2289ccd85d3b56e9771ef6af49dbfbc3 100644 (file)
@@ -295,7 +295,7 @@ static pid_t create_auth_process(struct auth_process_group *group)
        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 @@ static void auth_process_group_create(struct auth_settings *auth_set)
        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 @@ static void auth_process_group_create(struct auth_settings *auth_set)
        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 @@ static void auth_process_group_destroy(struct auth_process_group *group)
                 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 @@ void auth_processes_destroy_all(void)
        }
 }
 
+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) {
index 43e745da932c303690e314b14ab9addd6af9822f..ed892cec839e17d5f3020a6e0e0c5a2ab0544293 100644 (file)
@@ -20,18 +20,9 @@ enum {
        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 @@ extern int null_fd, mail_fd[FD_MAX], inetd_login_fd;
 #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) \
index d28a556c4c08618955b6022e8dddcf2b1cbb63ea..74d042588d585ce1bf166a5d23a83d40eecbca60 100644 (file)
 #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_destroy(struct login_process *p);
 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 @@ void auth_master_callback(struct auth_master_reply *reply,
                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 void login_process_mark_nonlistening(struct login_process *p)
        }
 }
 
-static struct login_group *login_group_process_find(const char *name)
+static void login_process_groups_create(void)
 {
-       struct login_group *group;
-       struct login_settings *login;
+       struct server_settings *server;
 
-       if (login_groups == NULL) {
-               for (login = set->logins; login != NULL; login = login->next)
-                       login_group_create(login);
+       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;
+
+       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 struct login_group *login_group_process_find(const char *name)
 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 @@ static int login_process_read_group(struct login_process *p)
        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 @@ static int login_process_read_group(struct login_process *p)
        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_unref(struct login_process *p)
 
 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 @@ static void login_process_init_env(struct login_group *group, pid_t pid)
        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 @@ static void login_process_init_env(struct login_group *group, pid_t pid)
        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 @@ static pid_t create_login_process(struct login_group *group)
        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 @@ static pid_t create_login_process(struct login_group *group)
        }
 
        /* 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 @@ static pid_t create_login_process(struct login_group *group)
 
        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 @@ void login_processes_destroy_all(void)
 
 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 @@ static void login_group_start_missings(struct login_group *group)
           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 @@ static void
 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);
index 38883f02fc5daaeae2a26723b833f84dcf740816..b8eede788baf28bcd59caa0c1cb2e6b83232122a 100644 (file)
@@ -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);
 
index abeb2b63305dbee91a9f9969bf6fdaa64b53fbdd..2359020c289d55db5635bd03c49fff2ea92bf01f 100644 (file)
@@ -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>
 
 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 @@ static int validate_uid_gid(uid_t uid, gid_t gid, const char *user)
        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 @@ static const char *expand_mail_env(const char *env, const char *user,
        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 @@ int create_mail_process(int socket, struct ip_addr *ip,
        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 @@ int create_mail_process(int socket, struct ip_addr *ip,
                                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 @@ int create_mail_process(int socket, struct ip_addr *ip,
        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 @@ int create_mail_process(int socket, struct ip_addr *ip,
                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;
index d74a015a7b35af8bc7ea0b7d32031a7485d0a37a..9d22b4a646ef26acbba9708c3631faeab60831d4 100644 (file)
@@ -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);
index f469451599a83b7863ed5e4e9af60075d57e4405..3ac34ea2eace74c96ae16630bd0b52e3cb90f913 100644 (file)
@@ -34,7 +34,7 @@ static struct timeout *to;
 
 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 @@ int validate_str(const char *str, size_t max_len)
        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 @@ static void settings_reload(void)
         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 @@ static struct ip_addr *resolve_ip(const char *name, unsigned int *port)
        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 @@ static void listen_protocols(void)
                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 @@ static void open_fds(void)
                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 @@ static void open_fds(void)
        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 @@ static void main_init(void)
        /* 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_init(void)
 
 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 @@ static void main_deinit(void)
        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 @@ int main(int argc, char *argv[])
 
        /* 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);
 
index f045c66fcf5ce8496c07af83456f37ef91e3b04a..0ef0c5928a6e5e03c78b715938e86dd4de8171f3 100644 (file)
 #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 @@ static struct setting_def setting_defs[] = {
 
        /* 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 @@ static struct setting_def setting_defs[] = {
        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 @@ static struct setting_def setting_defs[] = {
        DEF(SET_INT, umask),
        DEF(SET_BOOL, mail_drop_priv_before_exec),
 
+       DEF(SET_STR, mail_executable),
+       DEF(SET_INT, mail_process_size),
+       DEF(SET_BOOL, mail_use_modules),
+       DEF(SET_STR, mail_modules),
+
        /* 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),
-
-       { 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),
 
        { 0, NULL, 0 }
 };
@@ -130,6 +130,9 @@ static struct setting_def auth_setting_defs[] = {
 };
 
 struct settings default_settings = {
+       MEMBER(server) NULL,
+       MEMBER(protocol) 0,
+
        /* common */
        MEMBER(base_dir) PKG_RUNDIR,
        MEMBER(log_path) NULL,
@@ -138,10 +141,8 @@ struct settings default_settings = {
 
        /* 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 @@ struct settings default_settings = {
        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 @@ struct settings default_settings = {
        MEMBER(umask) 0077,
        MEMBER(mail_drop_priv_before_exec) FALSE,
 
+       MEMBER(mail_executable) PKG_LIBEXECDIR"/imap",
+       MEMBER(mail_process_size) 256,
+       MEMBER(mail_use_modules) FALSE,
+       MEMBER(mail_modules) PKG_LIBDIR"/imap",
+
        /* 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",
-
-       /* pop3 */
-       MEMBER(pop3_executable) PKG_LIBEXECDIR"/pop3",
-       MEMBER(pop3_process_size) 256,
-       MEMBER(pop3_use_modules) FALSE,
-       MEMBER(pop3_modules) PKG_LIBDIR"/imap",
 
+       /* .. */
+       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 @@ struct auth_settings default_auth_settings = {
        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 fix_base_path(struct settings *set, const char **str)
        }
 }
 
-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);
+       if (access(auth->executable, X_OK) < 0) {
+               i_error("Can't use auth executable %s: %m", auth->executable);
+               return FALSE;
+       }
 
-       fix_base_path(set, &auth->chroot);
+       fix_base_path(auth->parent->defaults, &auth->chroot);
        if (auth->chroot != NULL && access(auth->chroot, X_OK) < 0) {
-               i_fatal("Can't access auth chroot directory %s: %m",
+               i_error("Can't access auth chroot directory %s: %m",
                        auth->chroot);
+               return FALSE;
        }
-}
-
-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 (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");
+       return TRUE;
 }
 
 static const char *get_directory(const char *path)
@@ -311,82 +295,78 @@ static const char *get_directory(const char *path)
        }
 }
 
-static void settings_verify(struct settings *set)
+static int settings_is_active(struct settings *set)
+{
+       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)
 {
-       struct login_settings *login;
-       struct auth_settings *auth;
        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 (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 (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, "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);
-               }
+       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;
+       }
 #else
-               if (set->pop3_use_modules) {
-                       i_warning("Module support wasn't built into Dovecot, "
-                                 "ignoring pop3_use_modules setting");
-               }
-#endif
+       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 @@ static void settings_verify(struct settings *set)
        }
 
        /* 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 @@ static void settings_verify(struct settings *set)
                        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 @@ static void settings_verify(struct settings *set)
                 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 @@ static void auth_settings_new(struct settings *set, const char *name)
 
        /* 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;
+       return auth_settings_new(server, name);
 }
 
-static void login_settings_new(struct settings *set, const char *name)
+static const char *parse_setting(const char *key, const char *value,
+                                void *context)
 {
-       struct login_settings *login;
+       struct settings_parse_ctx *ctx = context;
+       const char *error;
 
-       login = p_new(settings_pool, struct login_settings, 1);
+       /* backwards compatibility */
+       if (strcmp(key, "auth") == 0) {
+               ctx->auth = parse_new_auth(ctx->server, value, &error);
+               return ctx->auth == NULL ? error : NULL;
+       }
 
-       /* 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);
+       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;
        }
 
-       login->next = set->logins;
-       set->logins = login;
-}
+       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);
+               }
 
-static const char *parse_new_login(struct settings *set, const char *name)
-{
-       struct login_settings *login;
+               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);
+               }
 
-       for (login = set->logins; login != NULL; login = login->next) {
-               if (strcmp(login->name, name) == 0) {
-                       return "Login process already exists "
-                               "with the same name";
+               if (error == NULL)
+                       return NULL;
+
+               /* backwards compatibility */
+               if (strncmp(key, "auth_", 5) == 0) {
+                       if (ctx->auth == 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);
        }
 
-       login_settings_new(set, name);
-       return NULL;
+       i_unreached();
 }
 
-static const char *parse_setting(const char *key, const char *value,
-                                void *context)
+static struct server_settings *
+create_new_server(const char *name,
+                 struct settings *imap_defaults,
+                 struct settings *pop3_defaults)
 {
-       struct settings *set = context;
-       const char *error;
+       struct server_settings *server;
 
-       /* 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)
-               return NULL;
+       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);
 
-       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";
+       *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;
+       }
+
+       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;
+               }
 
-               return parse_setting_from_defs(settings_pool,
-                                              login_setting_defs,
-                                              set->logins, key + 6, value);
+               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;
        }
 
-       return error;
+       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;
+       }
+
+       *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;
 
-       settings_read(path, parse_setting, set);
+       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;
+               }
+
+               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;
+               }
+
+               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_verify(set);
+       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);
 }
index 3dd18c5f662a2c776fbaaa7474acdef2a1a75f5b..76583b2db6123bca48928bdcce03d50c67b8fb4c 100644 (file)
@@ -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 @@ struct settings {
 
        /* 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 @@ struct settings {
        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 @@ struct settings {
        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 @@ struct auth_settings {
        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);
index c4b72602ff725dce6b03c5b87a1930c924dcde0e..70d942f975724d9e5353ba369074fb76b4177b36 100644 (file)
@@ -38,7 +38,7 @@ static void generate_parameters_file(const char *fname)
                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 @@ void ssl_parameter_process_destroyed(pid_t pid __attr_unused__)
        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 @@ static void check_parameters_file(void *context __attr_unused__)
        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)