]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Merged configuration rewrite.
authorTimo Sirainen <tss@iki.fi>
Fri, 3 Apr 2009 23:00:52 +0000 (19:00 -0400)
committerTimo Sirainen <tss@iki.fi>
Fri, 3 Apr 2009 23:00:52 +0000 (19:00 -0400)
--HG--
branch : HEAD

103 files changed:
1  2 
.hgignore
TODO
configure.in
src/auth/Makefile.am
src/auth/auth-request.c
src/auth/db-ldap.c
src/auth/main.c
src/auth/mech-digest-md5.c
src/auth/mech-gssapi.c
src/auth/mech-winbind.c
src/auth/passdb-cache.c
src/auth/passdb-ldap.c
src/auth/passdb.c
src/auth/passdb.h
src/auth/userdb.c
src/deliver/auth-client.c
src/deliver/auth-client.h
src/deliver/deliver-settings.c
src/deliver/deliver-settings.h
src/deliver/deliver.c
src/deliver/duplicate.c
src/deliver/duplicate.h
src/deliver/mail-send.c
src/imap-login/client.c
src/imap-login/imap-proxy.c
src/imap/Makefile.am
src/imap/client.c
src/imap/client.h
src/imap/cmd-append.c
src/imap/imap-fetch-body.c
src/imap/main.c
src/lib-storage/index/cydir/cydir-save.c
src/lib-storage/index/cydir/cydir-storage.c
src/lib-storage/index/dbox/Makefile.am
src/lib-storage/index/dbox/dbox-file.c
src/lib-storage/index/dbox/dbox-map.c
src/lib-storage/index/dbox/dbox-save.c
src/lib-storage/index/dbox/dbox-settings.c
src/lib-storage/index/dbox/dbox-settings.h
src/lib-storage/index/dbox/dbox-storage.c
src/lib-storage/index/dbox/dbox-storage.h
src/lib-storage/index/dbox/dbox-sync-rebuild.c
src/lib-storage/index/index-mail.c
src/lib-storage/index/index-storage.c
src/lib-storage/index/index-storage.h
src/lib-storage/index/maildir/maildir-copy.c
src/lib-storage/index/maildir/maildir-keywords.c
src/lib-storage/index/maildir/maildir-save.c
src/lib-storage/index/maildir/maildir-settings.c
src/lib-storage/index/maildir/maildir-settings.h
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/maildir/maildir-storage.h
src/lib-storage/index/maildir/maildir-sync.c
src/lib-storage/index/maildir/maildir-uidlist.c
src/lib-storage/index/mbox/mbox-lock.c
src/lib-storage/index/mbox/mbox-mail.c
src/lib-storage/index/mbox/mbox-save.c
src/lib-storage/index/mbox/mbox-storage.c
src/lib-storage/index/mbox/mbox-sync.c
src/lib-storage/index/shared/shared-storage.c
src/lib-storage/list/index-mailbox-list.c
src/lib-storage/list/mailbox-list-fs-iter.c
src/lib-storage/list/mailbox-list-fs.c
src/lib-storage/list/subscription-file.c
src/lib-storage/mail-namespace.c
src/lib-storage/mail-namespace.h
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage-settings.c
src/lib-storage/mail-storage.c
src/lib-storage/mail-storage.h
src/lib-storage/mail-user.c
src/lib-storage/mail-user.h
src/lib-storage/mailbox-list-private.h
src/lib-storage/mailbox-list.c
src/lib-storage/mailbox-list.h
src/login-common/main.c
src/master/auth-process.c
src/master/child-process.c
src/master/child-process.h
src/master/dict-process.c
src/master/login-process.c
src/master/mail-process.c
src/master/main.c
src/master/master-settings.c
src/master/master-settings.h
src/plugins/acl/acl-backend.c
src/plugins/acl/acl-mailbox-list.c
src/plugins/convert/convert-storage.c
src/plugins/expire/expire-plugin.c
src/plugins/fts-solr/fts-backend-solr.c
src/plugins/imap-acl/imap-acl-plugin.c
src/plugins/lazy-expunge/lazy-expunge-plugin.c
src/plugins/mail-log/mail-log-plugin.c
src/plugins/mbox-snarf/mbox-snarf-plugin.c
src/plugins/quota/quota-maildir.c
src/plugins/quota/quota-storage.c
src/plugins/virtual/virtual-storage.c
src/pop3-login/client.c
src/pop3-login/pop3-proxy.c
src/pop3/client.c
src/pop3/client.h
src/pop3/commands.c
src/pop3/main.c

diff --cc .hgignore
Simple merge
diff --cc TODO
index e964e1f6c019a1b34e006cd2ef9a29830eaa2704,b802b3b03b5d3bf39604c4838e747a7b465d8b94..c8e6ccb08559ce3cfd6871d1d6963e6b15ff43e9
--- 1/TODO
--- 2/TODO
+++ b/TODO
@@@ -1,24 -1,24 +1,31 @@@
+  - dict pooling
+  - config rewrite
+    - go through mail-process.c. nfs test?
+    - support !include and !include_try
+    - add back all setting verification code from master
+    - master_user, acl_groups are now plugin envs.. is it correct?
++
 +      /* currently non-external transactions can be applied multiple times,
 +         causing multiple increments. */
 +      //FIXME:i_assert((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0);
 +  ^ appears to work now though, probably because of the added syncing stuff..
 +
 + - dbox: save some stuff to map index header so we don't need to keep retrying
 +   it. like when saving the lowest file_id which to bother checking.
 + - transaction log corruption should make sure dovecot.index is rewritten
 +   and perhaps not delete the file.
 + - dbox: test crash-fixing
 + - dbox: cleanup tool / remove automatic cleanup. resyncing tool.
 + - use backup index in mail_index_fsck()
 + - dbox: mail_index_fsck() should perhaps cause dbox to be resynced?
 +
 + - convert plugin: convert_pop3_uidl_format setting? so old %f uidls could be
 +   converted to dbox..
   - proxying: support fallbacking to local (or other?) server if the first
     one is down
 -user_attrs {
 -  uid = %{ldap:uidNumber}
 -  home = %{ldap:homeDirectory}
 -  quota_bytes = *:bytes=%{ldap:quota}
 -}
 -
 -fts_solr: select() failed: Interrupted system call
 -fts_solr: Indexing failed: (null)
 -
 -imap(tss)(pid=12890): Error: dovecot-acl-list creation failed:
 -safe_mkstemp(/usr/local/var/run/dovecot/user-not-found/test/temp.hurina.12890.87eb6b37b351b733) failed: No such file or directory
 -
   - i_panic("Message count decreased") happens - why?
 +     - at least one backtrace shows client_destroy -> client_command_cancel ->
 +       imap_sync_deinit
   - fts-solr: handle DELETE, RENAME
   - fsck -> log_file_tail_offset 2273345664 -> 996 ->
     mail-transaction-log.c: line 341 (mail_transaction_log_set_mailbox_sync_pos):
diff --cc configure.in
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/auth/main.c
Simple merge
Simple merge
index a2b8c8aae3fd5e275bc9bb283a774b99cd9c4119,af8eca1ce865c35bb4642ea8f670e67d259edf3e..cd2847f76bc56607c4623a6ca71b0f2ade849ef3
@@@ -589,18 -575,18 +590,22 @@@ void mech_gssapi_init(void
  {
        mech_register_module(&mech_gssapi);
  #ifdef HAVE_GSSAPI_SPNEGO
--      if (getenv("NTLM_USE_WINBIND") == NULL)
++      /* load if we already didn't load it using winbind */
++      if (mech_module_find(mech_gssapi_spnego.name) == NULL)
                mech_register_module(&mech_gssapi_spnego);
  #endif
  }
  
  void mech_gssapi_deinit(void)
  {
--      mech_unregister_module(&mech_gssapi);
  #ifdef HAVE_GSSAPI_SPNEGO
--      if (getenv("NTLM_USE_WINBIND") == NULL)
++      struct mech_module *mech;
++
++      mech = mech_module_find(mech_gssapi_spnego.name);
++      if (mech != NULL && mech.auth_new == mech_gssapi_auth_new)
                mech_unregister_module(&mech_gssapi_spnego);
  #endif
++      mech_unregister_module(&mech_gssapi);
  }
  #endif
  
Simple merge
Simple merge
index e28287780f89a09559503fca845903daa59f0038,08a6c9d4286ab8ad0f7875fbaa1ce2abe54b8649..60bbd8992dcb8a9ba47ff1c29a9c3a19e863dd66
@@@ -165,13 -164,8 +165,13 @@@ ldap_auth_bind_callback(struct ldap_con
                if (ret == LDAP_SUCCESS)
                        passdb_result = PASSDB_RESULT_OK;
                else if (ret == LDAP_INVALID_CREDENTIALS) {
 -                      auth_request_log_info(auth_request, "ldap",
 -                                            "invalid credentials");
 +                      str = "invalid credentials";
-                       if (auth_request->auth->verbose_debug_passwords) {
++                      if (auth_request->auth->set->debug_passwords) {
 +                              str = t_strconcat(str, " (given password: ",
 +                                                auth_request->mech_password,
 +                                                ")", NULL);
 +                      }
 +                      auth_request_log_info(auth_request, "ldap", "%s", str);
                        passdb_result = PASSDB_RESULT_PASSWORD_MISMATCH;
                } else {
                        auth_request_log_error(auth_request, "ldap",
index 1be5a27483e463d8afb171c7bd005db479a9bddb,671b2b065032adc03bad89b0636ffa0ea7adb113..e7ea725110b175fc9760a47ec089956272db7b43
@@@ -105,19 -92,14 +105,19 @@@ bool passdb_get_credentials(struct auth
  
                /* we can generate anything out of plaintext passwords */
                plaintext = t_strndup(*credentials_r, *size_r);
-               if (auth_request->auth->verbose_debug_passwords) {
 +              username = auth_request->original_username;
 +              if (!auth_request->domain_is_realm &&
 +                  strchr(username, '@') != NULL) {
 +                      /* domain must not be used as realm. add the @realm. */
 +                      username = t_strconcat(username, "@",
 +                                             auth_request->realm, NULL);
 +              }
+               if (auth_request->auth->set->debug_passwords) {
                        auth_request_log_info(auth_request, "password",
 -                              "Generating %s from user %s password %s",
 -                              wanted_scheme, auth_request->original_username,
 -                              plaintext);
 +                              "Generating %s from user '%s', password '%s'",
 +                              wanted_scheme, username, plaintext);
                }
 -              if (!password_generate(plaintext,
 -                                     auth_request->original_username,
 +              if (!password_generate(plaintext, username,
                                       wanted_scheme, credentials_r, size_r)) {
                        auth_request_log_error(auth_request, "password",
                                "Requested unknown scheme %s", wanted_scheme);
index 94cc36a7ecf9f8669f5c84d44af29340c31eea30,123f0f62fddb60fb85ac850a225679540948384c..7b8b3f46fa19974a4b773bb6da8980f44898a635
@@@ -80,8 -80,9 +80,9 @@@ void passdb_handle_credentials(enum pas
                               lookup_credentials_callback_t *callback,
                                 struct auth_request *auth_request);
  
- struct auth_passdb *passdb_preinit(struct auth *auth, const char *driver,
-                                  const char *args, unsigned int id);
+ struct auth_passdb *
 -      passdb_preinit(struct auth *auth, struct auth_passdb_settings *set);
++passdb_preinit(struct auth *auth, struct auth_passdb_settings *set);
  void passdb_init(struct auth_passdb *passdb);
  void passdb_deinit(struct auth_passdb *passdb);
  
Simple merge
index d5425e3fabc8c2e395b8056b3e32d2c418b56c60,d5425e3fabc8c2e395b8056b3e32d2c418b56c60..c799ff9ea80b0ca07761b27c2d02e18e4c14df9e
@@@ -8,6 -8,6 +8,7 @@@
  #include "ostream.h"
  #include "env-util.h"
  #include "restrict-access.h"
++#include "deliver.h"
  #include "auth-client.h"
  #include "auth-master.h"
  
@@@ -65,8 -65,8 +66,8 @@@ static int set_env(struct auth_user_rep
                i_error("userdb(%s) returned 0 as uid", user);
                return -1;
        } else if (reply->uid == (uid_t)-1) {
--              if (getenv("MAIL_UID") != NULL) {
--                      if (!parse_uid(getenv("MAIL_UID"), &reply->uid) ||
++              if (*deliver_set->mail_uid != '\0') {
++                      if (!parse_uid(deliver_set->mail_uid, &reply->uid) ||
                            reply->uid == 0) {
                                i_error("mail_uid setting is invalid");
                                return -1;
@@@ -80,8 -80,8 +81,8 @@@
                i_error("userdb(%s) returned 0 as gid", user);
                return -1;
        } else if (reply->gid == (gid_t)-1) {
--              if (getenv("MAIL_GID") != NULL) {
--                      if (!parse_gid(getenv("MAIL_GID"), &reply->gid) ||
++              if (*deliver_set->mail_gid != '\0') {
++                      if (!parse_gid(deliver_set->mail_gid, &reply->gid) ||
                            reply->gid == 0) {
                                i_error("mail_gid setting is invalid");
                                return -1;
        }
  
        if (reply->chroot == NULL)
--              reply->chroot = getenv("MAIL_CHROOT");
++              reply->chroot = deliver_set->mail_chroot;
        if (reply->chroot != NULL) {
                len = strlen(reply->chroot);
                if (len > 2 && strcmp(reply->chroot + len - 2, "/.") == 0 &&
        if (reply->home != NULL)
                env_put(t_strconcat("HOME=", reply->home, NULL));
  
--      extra_groups = getenv("MAIL_EXTRA_GROUPS");
++      extra_groups = deliver_set->mail_access_groups;
        if (extra_groups != NULL) {
                env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
                                    extra_groups, NULL));
        return 0;
  }
  
--int auth_client_lookup_and_restrict(const char *auth_socket,
++int auth_client_lookup_and_restrict(const char *auth_socket, bool debug,
                                    const char **user, uid_t euid, pool_t pool,
                                    ARRAY_TYPE(const_string) *extra_fields_r)
  {
          struct auth_master_connection *conn;
        struct auth_user_reply reply;
--      bool debug = getenv("DEBUG") != NULL;
        int ret = EX_TEMPFAIL;
  
        conn = auth_master_init(auth_socket, debug);
index 2cc3cb01181e47d2d509eb47670ec5417c70dcd8,2cc3cb01181e47d2d509eb47670ec5417c70dcd8..1d9dbe1d8e205f7b6209e9e89817695b08f4f20d
@@@ -1,7 -1,7 +1,7 @@@
  #ifndef AUTH_CLIENT_H
  #define AUTH_CLIENT_H
  
--int auth_client_lookup_and_restrict(const char *auth_socket,
++int auth_client_lookup_and_restrict(const char *auth_socket, bool debug,
                                    const char **user, uid_t euid, pool_t pool,
                                    ARRAY_TYPE(const_string) *extra_fields_r);
  
index 0000000000000000000000000000000000000000,8e1cb20e070cd1b7b5e543eefc82524f9814f05b..4b82283a4e512887293c230b2fb6a39b2f98c9f2
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,155 +1,165 @@@
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+ #include "deliver.h"
+ #include "array.h"
+ #include "hostpid.h"
+ #include "istream.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "deliver-settings.h"
+ #include <stddef.h>
+ #include <stdlib.h>
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct deliver_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+       { SET_DEFLIST, name, offsetof(struct deliver_settings, field), defines }
+ static struct setting_define deliver_setting_defines[] = {
+       DEF(SET_STR, base_dir),
+       DEF(SET_STR, log_path),
+       DEF(SET_STR, info_log_path),
+       DEF(SET_STR, log_timestamp),
+       DEF(SET_STR, syslog_facility),
+       DEF(SET_BOOL, version_ignore),
+       DEF(SET_UINT, umask),
+       DEF(SET_STR, mail_plugins),
+       DEF(SET_STR, mail_plugin_dir),
++      DEF(SET_STR, mail_uid),
++      DEF(SET_STR, mail_gid),
++      DEF(SET_STR, mail_chroot),
++      DEF(SET_STR, mail_access_groups),
++
+       DEF(SET_STR, postmaster_address),
+       DEF(SET_STR, hostname),
+       DEF(SET_STR, sendmail_path),
+       DEF(SET_STR, rejection_subject),
+       DEF(SET_STR, rejection_reason),
+       DEF(SET_STR, auth_socket_path),
+       DEF(SET_STR, deliver_log_format),
+       DEF(SET_BOOL, quota_full_tempfail),
+       { SET_STRLIST, "plugin", offsetof(struct deliver_settings, plugin_envs), NULL },
+       SETTING_DEFINE_LIST_END
+ };
+ static struct deliver_settings deliver_default_settings = {
+       MEMBER(base_dir) PKG_RUNDIR,
+       MEMBER(log_path) "",
+       MEMBER(info_log_path) "",
+       MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
+       MEMBER(syslog_facility) "mail",
+       MEMBER(version_ignore) FALSE,
+       MEMBER(umask) 0077,
+       MEMBER(mail_plugins) "",
+       MEMBER(mail_plugin_dir) MODULEDIR"/lda",
++      MEMBER(mail_uid) "",
++      MEMBER(mail_gid) "",
++      MEMBER(mail_chroot) "",
++      MEMBER(mail_access_groups) "",
++
+       MEMBER(postmaster_address) "",
+       MEMBER(hostname) "",
+       MEMBER(sendmail_path) "/usr/lib/sendmail",
+       MEMBER(rejection_subject) "Rejected: %s",
+       MEMBER(rejection_reason)
+               "Your message to <%t> was automatically rejected:%n%r",
+       MEMBER(auth_socket_path) "auth-master",
+       MEMBER(deliver_log_format) "msgid=%m: %$",
+       MEMBER(quota_full_tempfail) FALSE
+ };
+ struct setting_parser_info deliver_setting_parser_info = {
+       MEMBER(defines) deliver_setting_defines,
+       MEMBER(defaults) &deliver_default_settings,
+       MEMBER(parent) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct deliver_settings)
+ };
+ static pool_t settings_pool = NULL;
+ static void fix_base_path(struct deliver_settings *set, const char **str)
+ {
+       if (*str != NULL && **str != '\0' && **str != '/') {
+               *str = p_strconcat(settings_pool,
+                                  set->base_dir, "/", *str, NULL);
+       }
+ }
+ struct setting_parser_context *
+ deliver_settings_read(struct deliver_settings **set_r,
+                     struct mail_user_settings **user_set_r)
+ {
+       static const struct setting_parser_info *roots[] = {
+                 &deliver_setting_parser_info,
+                 &mail_user_setting_parser_info
+       };
+       void **sets;
+       struct deliver_settings *deliver_set;
+       struct setting_parser_context *parser;
+       if (settings_pool == NULL)
+               settings_pool = pool_alloconly_create("deliver settings", 1024);
+       else
+               p_clear(settings_pool);
+       mail_storage_namespace_defines_init(settings_pool);
+       parser = settings_parser_init_list(settings_pool,
+                               roots, N_ELEMENTS(roots),
+                               SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+       if (settings_parse_environ(parser) < 0) {
+               i_fatal_status(EX_CONFIG, "Error reading configuration: %s",
+                              settings_parser_get_error(parser));
+       }
+       sets = settings_parser_get_list(parser);
+       deliver_set = sets[0];
+       if (*deliver_set->hostname == '\0')
+               deliver_set->hostname = my_hostname;
+       fix_base_path(deliver_set, &deliver_set->auth_socket_path);
+       if (*deliver_set->postmaster_address == '\0') {
+               i_fatal_status(EX_CONFIG,
+                              "postmaster_address setting not given");
+       }
+       *set_r = deliver_set;
+       *user_set_r = sets[1];
+       return parser;
+ }
+ void deliver_settings_add(struct setting_parser_context *parser,
+                         const ARRAY_TYPE(const_string) *extra_fields)
+ {
+       const char *const *str, *p, *line;
+       unsigned int i, count;
+       str = array_get(extra_fields, &count);
+       for (i = 0; i < count; i++) T_BEGIN {
+               p = strchr(str[i], '=');
+               if (p != NULL)
+                       line = str[i];
+               else
+                       line = t_strconcat(str[i], "=yes", NULL);
+               if (settings_parse_line(parser, str[i]) < 0) {
+                       i_fatal_status(EX_CONFIG,
+                                      "Invalid userdb input '%s': %s", str[i],
+                                      settings_parser_get_error(parser));
+               }
+       } T_END;
+ }
index 0000000000000000000000000000000000000000,103fe2fbe171b0680829518209a5bf2b4a7f034b..facd4efd34f2ca65e56def97aae968450d3d03d1
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,37 +1,42 @@@
+ #ifndef DELIVER_SETTINGS_H
+ #define DELIVER_SETTINGS_H
+ struct mail_user_settings;
+ struct deliver_settings {
+       const char *base_dir;
+       const char *log_path;
+       const char *info_log_path;
+       const char *log_timestamp;
+       const char *syslog_facility;
+       bool version_ignore;
+       unsigned int umask;
+       const char *mail_plugins;
+       const char *mail_plugin_dir;
++      const char *mail_uid;
++      const char *mail_gid;
++      const char *mail_chroot;
++      const char *mail_access_groups;
++
+       /* deliver: */
+       const char *postmaster_address;
+       const char *hostname;
+       const char *sendmail_path;
+       const char *rejection_subject;
+       const char *rejection_reason;
+       const char *auth_socket_path;
+       const char *deliver_log_format;
+       bool quota_full_tempfail;
+       ARRAY_DEFINE(plugin_envs, const char *);
+ };
+ struct setting_parser_context *
+ deliver_settings_read(struct deliver_settings **set_r,
+                     struct mail_user_settings **user_set_r);
+ void deliver_settings_add(struct setting_parser_context *parser,
+                         const ARRAY_TYPE(const_string) *extra_fields);
+ #endif
index 55adc44d972bba58cb5b5fbb7f67d67a01736955,658b026300fdf7f95350bce8346f51571c8310bf..be5dfbcd60b2ef8e8004a4d107a3ba06b03b121e
@@@ -66,11 -69,7 +69,7 @@@ static char *explicit_envelope_sender 
  static struct module *modules;
  static struct ioloop *ioloop;
  
- static pool_t plugin_pool;
- static ARRAY_DEFINE(lda_envs, const char *);
- static ARRAY_DEFINE(plugin_envs, const char *);
 -static void sig_die(int signo, void *context ATTR_UNUSED)
 +static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
  {
        /* warn about being killed because of some signal, except SIGINT (^C)
           which is too common at least while testing :) */
@@@ -821,19 -469,21 +477,22 @@@ int main(int argc, char *argv[]
  {
        const char *config_path = DEFAULT_CONFIG_FILE;
        const char *mailbox = "INBOX";
-       const char *auth_socket;
-       const char *home, *destaddr, *user, *value, *errstr, *path, *orig_user;
 -      const char *home, *destaddr, *user, *error, *path, *orig_user;
++      const char *home, *destaddr, *user, *errstr, *path, *orig_user;
        ARRAY_TYPE(const_string) extra_fields = ARRAY_INIT;
+       struct setting_parser_context *parser;
        struct mail_user *mail_user, *raw_mail_user;
        struct mail_namespace *raw_ns;
+       struct mail_namespace_settings raw_ns_set;
        struct mail_storage *storage;
        struct mailbox *box;
        struct raw_mailbox *raw_box;
        struct istream *input;
        struct mailbox_transaction_context *t;
        struct mailbox_header_lookup_ctx *headers_ctx;
+       struct mail_user_settings *user_set;
+       const struct mail_storage_settings *mail_set;
        struct mail *mail;
 +      char cwd[PATH_MAX];
        uid_t process_euid;
        bool stderr_rejection = FALSE;
        bool keep_environment = FALSE;
        }
  
        if (user_auth) {
-               auth_socket = getenv("AUTH_SOCKET_PATH");
-               if (auth_socket == NULL) {
-                       const char *base_dir = getenv("BASE_DIR");
-                       if (base_dir == NULL)
-                               base_dir = PKG_RUNDIR;
-                       auth_socket = t_strconcat(base_dir, "/auth-master",
-                                                 NULL);
-               }
                userdb_pool = pool_alloconly_create("userdb lookup replys", 512);
                orig_user = user;
-               ret = auth_client_lookup_and_restrict(auth_socket,
+               ret = auth_client_lookup_and_restrict(deliver_set->auth_socket_path,
++                                                    mail_set->mail_debug,
                                                      &user, process_euid,
                                                      userdb_pool,
                                                      &extra_fields);
        }
  
        env_put(t_strconcat("USER=", user, NULL));
-       value = getenv("UMASK");
-       if (value == NULL || sscanf(value, "%i", &i) != 1 || i < 0)
-               i = 0077;
-       (void)umask(i);
-       deliver_set->hostname = getenv("HOSTNAME");
-       if (deliver_set->hostname == NULL)
-               deliver_set->hostname = my_hostname;
-       deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS");
-       if (deliver_set->postmaster_address == NULL) {
-               i_fatal_status(EX_CONFIG,
-                              "postmaster_address setting not given");
-       }
-       deliver_set->sendmail_path = getenv("SENDMAIL_PATH");
-       if (deliver_set->sendmail_path == NULL)
-               deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH;
-       deliver_set->rejection_subject = getenv("REJECTION_SUBJECT");
-       if (deliver_set->rejection_subject == NULL)
-               deliver_set->rejection_subject = DEFAULT_MAIL_REJECTION_SUBJECT;
-       deliver_set->rejection_reason = getenv("REJECTION_REASON");
-       if (deliver_set->rejection_reason == NULL) {
-               deliver_set->rejection_reason =
-                       DEFAULT_MAIL_REJECTION_HUMAN_REASON;
-       }
-       deliver_set->log_format = getenv("DELIVER_LOG_FORMAT");
-       if (deliver_set->log_format == NULL)
-               deliver_set->log_format = DEFAULT_LOG_FORMAT;
+       (void)umask(deliver_set->umask);
  
        dict_drivers_register_builtin();
--        duplicate_init();
-       mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL);
-         mail_storage_init();
-       mail_storage_register_all();
-       mailbox_list_register_all();
++        duplicate_init(mail_set);
+       mail_users_init(deliver_set->auth_socket_path, mail_set->mail_debug);
  
        module_dir_init(modules);
  
-       mail_user = mail_user_init(user);
+       mail_user = mail_user_alloc(user, user_set);
        mail_user_set_home(mail_user, home);
-       if (mail_namespaces_init(mail_user) < 0)
-               i_fatal("Namespace initialization failed");
+       mail_user_set_vars(mail_user, geteuid(), "deliver", NULL, NULL);
 -      if (mail_user_init(mail_user, &error) < 0)
 -              i_fatal("Mail user initialization failed: %s", error);
 -      if (mail_namespaces_init(mail_user, &error) < 0)
 -              i_fatal("Namespace initialization failed: %s", error);
++      if (mail_user_init(mail_user, &errstr) < 0)
++              i_fatal("Mail user initialization failed: %s", errstr);
++      if (mail_namespaces_init(mail_user, &errstr) < 0)
++              i_fatal("Namespace initialization failed: %s", errstr);
  
        /* create a separate mail user for the internal namespace */
-       raw_mail_user = mail_user_init(user);
-       mail_user_set_home(raw_mail_user, NULL);
+       raw_mail_user = mail_user_alloc(user, user_set);
+       mail_user_set_home(raw_mail_user, "/");
 -      if (mail_user_init(raw_mail_user, &error) < 0)
 -              i_fatal("Raw user initialization failed: %s", error);
++      if (mail_user_init(raw_mail_user, &errstr) < 0)
++              i_fatal("Raw user initialization failed: %s", errstr);
+       settings_parser_deinit(&parser);
+       memset(&raw_ns_set, 0, sizeof(raw_ns_set));
+       raw_ns_set.location = "/tmp";
        raw_ns = mail_namespaces_init_empty(raw_mail_user);
        raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
-       if (mail_storage_create(raw_ns, "raw", "/tmp",
-                               MAIL_STORAGE_FLAG_FULL_FS_ACCESS,
-                               FILE_LOCK_METHOD_FCNTL, &errstr) < 0)
+       raw_ns->set = &raw_ns_set;
 -      if (mail_storage_create(raw_ns, "raw", 0, &error) < 0)
 -              i_fatal("Couldn't create internal raw storage: %s", error);
++      if (mail_storage_create(raw_ns, "raw", 0, &errstr) < 0)
 +              i_fatal("Couldn't create internal raw storage: %s", errstr);
        if (path == NULL) {
 -              input = create_raw_stream(0, &mtime);
 +              const char *prefix = mail_user_get_temp_prefix(mail_user);
 +              input = create_raw_stream(prefix, 0, &mtime);
                box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail",
                                   input, MAILBOX_OPEN_NO_INDEX_FILES);
                i_stream_unref(&input);
        }
  
        if (ret < 0 ) {
--              const char *error_string;
--              enum mail_error error;
--
                if (storage == NULL) {
                        /* This shouldn't happen */
                        i_error("BUG: Saving failed for unknown storage");
                        return EX_TEMPFAIL;
                }
  
--              error_string = mail_storage_get_last_error(storage, &error);
++              errstr = mail_storage_get_last_error(storage, &error);
  
                if (stderr_rejection) {
                        /* write to stderr also for tempfails so that MTA
                           can log the reason if it wants to. */
--                      fprintf(stderr, "%s\n", error_string);
++                      fprintf(stderr, "%s\n", errstr);
                }
  
                if (error != MAIL_ERROR_NOSPACE ||
  
                /* we'll have to reply with permanent failure */
                deliver_log(mail, "rejected: %s",
--                          str_sanitize(error_string, 512));
++                          str_sanitize(errstr, 512));
  
                if (stderr_rejection)
                        return EX_NOPERM;
--              ret = mail_send_rejection(mail, user, error_string);
++              ret = mail_send_rejection(mail, user, errstr);
                if (ret != 0)
                        return ret < 0 ? EX_TEMPFAIL : ret;
                /* ok, rejection sent */
index f05e7fc6169be1c7f0155d0563f3b6037f1d05bd,f05e7fc6169be1c7f0155d0563f3b6037f1d05bd..45884a562809dfad33609d7d53bb2d138ac5f15d
@@@ -307,12 -307,12 +307,10 @@@ void duplicate_flush(void
        file->new_fd = -1;
  }
  
--void duplicate_init(void)
++void duplicate_init(const struct mail_storage_settings *set)
  {
--      duplicate_dotlock_set.use_excl_lock =
--              getenv("DOTLOCK_USE_EXCL") != NULL;
--      duplicate_dotlock_set.nfs_flush =
--              getenv("MAIL_NFS_STORAGE") != NULL;
++      duplicate_dotlock_set.use_excl_lock = set->dotlock_use_excl;
++      duplicate_dotlock_set.nfs_flush = set->mail_nfs_storage;
  }
  
  void duplicate_deinit(void)
index c31402c71c75edbee6d16b9b186b79d5e9b9eda2,c31402c71c75edbee6d16b9b186b79d5e9b9eda2..4f220ae47e42c4a28169de1099d1c0519f13a3d9
@@@ -1,6 -1,6 +1,8 @@@
  #ifndef DUPLICATE_H
  #define DUPLICATE_H
  
++#include "mail-storage-settings.h"
++
  #define DUPLICATE_DEFAULT_KEEP (3600 * 24)
  
  int duplicate_check(const void *id, size_t id_size, const char *user);
@@@ -9,7 -9,7 +11,7 @@@ void duplicate_mark(const void *id, siz
  
  void duplicate_flush(void);
  
--void duplicate_init(void);
++void duplicate_init(const struct mail_storage_settings *set);
  void duplicate_deinit(void);
  
  #endif
index 41d89f7cb53f91671af62a3dabb4960b7995d268,41d89f7cb53f91671af62a3dabb4960b7995d268..fbdf6f98b755b207e4cd97ca03c8b880395ace99
@@@ -11,6 -11,6 +11,7 @@@
  #include "message-size.h"
  #include "duplicate.h"
  #include "istream-header-filter.h"
++#include "mail-storage-settings.h"
  #include "smtp-client.h"
  #include "deliver.h"
  #include "mail-send.h"
@@@ -71,7 -71,7 +72,7 @@@ int mail_send_rejection(struct mail *ma
            return 0;
      }
  
--    if (getenv("DEBUG") != NULL) {
++    if (mailbox_get_settings(mail->box)->mail_debug) {
            i_info("Sending a rejection to %s: %s", recipient,
                   str_sanitize(reason, 512));
      }
@@@ -178,7 -178,7 +179,7 @@@ int mail_send_forward(struct mail *mail
      if (mail_get_first_header(mail, "Return-Path", &return_path) <= 0)
            return_path = "";
  
--    if (getenv("DEBUG") != NULL) {
++    if (mailbox_get_settings(mail->box)->mail_debug) {
            i_info("Sending a forward to <%s> with return path <%s>",
                   forwardto, return_path);
      }
Simple merge
Simple merge
index 386301c0ec64b1313fab43abb7ea404733918488,80b3777df65dc7ca46ea59013203f20ea21882ac..e302bece79bc008ea813840ec6455260437e5a91
@@@ -72,7 -75,8 +75,8 @@@ imap_SOURCES = 
        imap-fetch.c \
        imap-fetch-body.c \
        imap-search.c \
 -      imap-sort.c \
 +      imap-search-args.c \
+       imap-settings.c \
        imap-status.c \
        imap-sync.c \
        mail-storage-callbacks.c \
@@@ -87,7 -91,8 +91,8 @@@ headers = 
        imap-expunge.h \
        imap-fetch.h \
        imap-search.h \
 -      imap-sort.h \
 +      imap-search-args.h \
+       imap-settings.h \
        imap-status.h \
        imap-sync.h
  
Simple merge
Simple merge
Simple merge
Simple merge
diff --cc src/imap/main.c
index af4815067d7836bab9e6c3e912ec83c045215cb6,153d6b1861564e647e279e8c0f4e85ed9eced48b..db5f8db1a560826110ba69042dd02d13412fdc42
@@@ -50,9 -46,7 +46,7 @@@ static char log_prefix[128]; /* syslog(
  
  void (*hook_client_created)(struct client **client) = NULL;
  
- string_t *capability_string;
 -static void sig_die(int signo, void *context ATTR_UNUSED)
 +static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
  {
        /* warn about being killed because of some signal, except SIGINT (^C)
           which is too common at least while testing :) */
@@@ -150,21 -139,24 +146,25 @@@ static void main_preinit(const struct i
        /* Log file or syslog opening probably requires roots */
        open_logfile();
  
-       /* Load the plugins before chrooting. Their init() is called later. */
-       if (getenv("MAIL_PLUGINS") != NULL) {
-               const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+         mail_storage_init();
+       mail_storage_register_all();
+       mailbox_list_register_all();
  
-               if (plugin_dir == NULL)
-                       plugin_dir = MODULEDIR"/imap";
-               modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
-                                         TRUE, version);
-       }
+       /* read settings after registering storages so they can have their
+          own setting definitions too */
+       imap_settings_read(set_r, user_set_r);
+       /* Load the plugins before chrooting. Their init() is called later. */
+       modules = *(*set_r)->mail_plugins == '\0' ? NULL :
+               module_dir_load((*set_r)->mail_plugin_dir,
+                               (*set_r)->mail_plugins, TRUE, version);
  
        restrict_access_by_env(!IS_STANDALONE());
 +      restrict_access_allow_coredumps(TRUE);
  }
  
- static void main_init(void)
+ static void main_init(const struct imap_settings *set,
+                     const struct mail_user_settings *user_set)
  {
        struct client *client;
        struct ostream *output;
          lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
          lib_signals_ignore(SIGPIPE, TRUE);
          lib_signals_ignore(SIGALRM, FALSE);
 -
++      
+       dump_capability = getenv("DUMP_CAPABILITY") != NULL;
  
        username = getenv("USER");
        if (username == NULL) {
index ca09bd70f4d7ce6056e5a21eb7427a3a4a768932,bb1e8f1a790bd9f9818647fd4d881c3a16455ec2..0528815b644d31ef0cfae3aad4b0ccf109d8ef83
@@@ -166,7 -167,7 +166,7 @@@ int cydir_save_finish(struct mail_save_
                ctx->failed = TRUE;
        }
  
--      if (!ctx->mbox->ibox.fsync_disable) {
++      if (!storage->set->fsync_disable) {
                if (fsync(ctx->fd) < 0) {
                        mail_storage_set_critical(storage,
                                                  "fsync(%s) failed: %m", path);
index 26a6d7316b7dab98850e73f0103242ecd584e999,995505cf70acb2cbf80e458fb9d43310ea3225f1..0ebadc7842b38d26d23f700b0ae6f638bc9c2876
@@@ -37,8 -37,8 +37,6 @@@ cydir_get_list_settings(struct mailbox_
                        const char *data, struct mail_storage *storage,
                        const char **layout_r, const char **error_r)
  {
-       bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 -      bool debug = storage->set->mail_debug;
--
        *layout_r = "fs";
  
        memset(list_set, 0, sizeof(*list_set));
  
        if (data == NULL || *data == '\0' || *data == ':') {
                /* we won't do any guessing for this format. */
--              if (debug)
++              if (storage->set->mail_debug)
                        i_info("cydir: mailbox location not given");
                *error_r = "Root mail directory not given";
                return -1;
        }
  
--      if (debug)
++      if (storage->set->mail_debug)
                i_info("cydir: data=%s", data);
        return mailbox_list_settings_parse(data, list_set, storage->ns,
                                           layout_r, NULL, error_r);
index 1225bda5bd5307fa9d06d122733de0180a32f7bd,05e5d539022028242d40039338fc68660512fdff..0bbf70349b1c39c98aa6e31edd0a1d512cac404f
@@@ -10,11 -11,11 +11,12 @@@ AM_CPPFLAGS = 
  
  libstorage_dbox_a_SOURCES = \
        dbox-file.c \
 +      dbox-file-fix.c \
        dbox-file-maildir.c \
 -      dbox-index.c \
        dbox-mail.c \
 +      dbox-map.c \
        dbox-save.c \
+       dbox-settings.c \
        dbox-sync.c \
        dbox-sync-file.c \
        dbox-sync-rebuild.c \
  headers = \
        dbox-file.h \
        dbox-file-maildir.h \
 -      dbox-index.h \
 +      dbox-map.h \
+       dbox-settings.h \
        dbox-storage.h \
 +      dbox-storage-rebuild.h \
        dbox-sync.h
  
  if INSTALL_HEADERS
index 7e93895531cdbc0d16cd8a5b87e1b08d17c6b811,8a415880d29edd404cdfab650241a197475c9bca..44234d3592782a63856ddab8a2e557d6ac56e6b7
@@@ -177,35 -194,13 +177,37 @@@ dbox_file_init_single(struct dbox_mailb
  }
  
  struct dbox_file *
 -dbox_file_init_new_maildir(struct dbox_mailbox *mbox, const char *fname)
 +dbox_file_init_multi(struct dbox_storage *storage, uint32_t file_id)
  {
        struct dbox_file *file;
 +      unsigned int count;
  
 -      file = dbox_file_init(mbox, 0);
 -      file->maildir_file = TRUE;
 -      file->fname = i_strdup(fname);
 +      file = file_id == 0 ? NULL :
 +              dbox_find_and_move_open_file(storage, file_id);
 +      if (file != NULL) {
 +              file->refcount++;
 +              return file;
 +      }
 +
 +      count = array_count(&storage->open_files);
-       if (count > storage->max_open_files)
-               dbox_close_open_files(storage, count - storage->max_open_files);
++      if (count > storage->set->dbox_max_open_files) {
++              dbox_close_open_files(storage, count -
++                                    storage->set->dbox_max_open_files);
++      }
 +
 +      file = i_new(struct dbox_file, 1);
 +      file->refcount = 1;
 +      file->storage = storage;
 +      file->file_id = file_id;
 +      file->fd = -1;
 +      file->cur_offset = (uoff_t)-1;
 +      file->fname = file_id == 0 ? dbox_generate_tmp_filename() :
 +              i_strdup_printf(DBOX_MAIL_FILE_MULTI_FORMAT, file_id);
 +      file->current_path =
 +              i_strdup_printf("%s/%s", storage->storage_dir, file->fname);
 +
 +      if (file_id != 0)
 +              array_append(&storage->open_files, &file, 1);
        return file;
  }
  
@@@ -265,11 -251,12 +267,12 @@@ void dbox_file_unref(struct dbox_file *
                return;
  
        /* don't cache metadata seeks while file isn't being referenced */
 -      file->metadata_read_offset = 0;
 +      file->metadata_read_offset = (uoff_t)-1;
  
        if (file->file_id != 0) {
 -              files = array_get(&file->mbox->open_files, &count);
 +              files = array_get(&file->storage->open_files, &count);
-               if (!file->deleted && count <= file->storage->max_open_files) {
+               if (!file->deleted &&
 -                  count <= file->mbox->storage->set->dbox_max_open_files) {
++                  count <= file->storage->set->dbox_max_open_files) {
                        /* we can leave this file open for now */
                        return;
                }
@@@ -743,35 -790,38 +746,34 @@@ uoff_t dbox_file_get_next_append_offset
  
  void dbox_file_cancel_append(struct dbox_file *file, uoff_t append_offset)
  {
 -      if (ftruncate(file->fd, append_offset) < 0) {
 -              dbox_file_set_syscall_error(file, "ftruncate");
 -              file->append_offset = 0;
 -              file->nonappendable = TRUE;
 -      }
 +      (void)o_stream_flush(file->output);
  
 -      o_stream_seek(file->output, append_offset);
 -      file->output_stream_offset = append_offset;
 +      if (file->output->offset != append_offset) {
 +              if (ftruncate(file->fd, append_offset) < 0)
 +                      dbox_file_set_syscall_error(file, "ftruncate()");
 +              o_stream_seek(file->output, append_offset);
 +      }
  }
  
 -void dbox_file_finish_append(struct dbox_file *file)
 +int dbox_file_flush_append(struct dbox_file *file)
  {
 -      file->output_stream_offset = file->output->offset;
 -      file->append_offset = file->output->offset;
 -      file->append_count++;
 -}
 +      i_assert(file->output != NULL);
  
 -static uoff_t
 -dbox_file_get_metadata_offset(struct dbox_file *file, uoff_t offset,
 -                            uoff_t physical_size)
 -{
 -      if (offset == 0) {
 -              if (file->maildir_file)
 -                      return 0;
 +      if (o_stream_flush(file->output) < 0) {
 +              dbox_file_set_syscall_error(file, "write()");
 +              return -1;
 +      }
  
-       if ((file->storage->storage.flags &
-            MAIL_STORAGE_FLAG_FSYNC_DISABLE) == 0) {
 -              i_assert(file->file_header_size != 0);
 -              offset = file->file_header_size;
++      if (!file->storage->storage.set->fsync_disable) {
 +              if (fdatasync(file->fd) < 0) {
 +                      dbox_file_set_syscall_error(file, "fdatasync()");
 +                      return -1;
 +              }
        }
 -      return offset + sizeof(struct dbox_message_header) + physical_size;
 +      return 0;
  }
  
 -static int dbox_file_metadata_skip_header(struct dbox_file *file)
 +int dbox_file_metadata_skip_header(struct dbox_file *file)
  {
        struct dbox_metadata_header metadata_hdr;
        const unsigned char *data;
@@@ -936,11 -1275,9 +938,10 @@@ int dbox_file_move(struct dbox_file *fi
        }
        o_stream_unref(&output);
  
-       if ((file->storage->storage.flags &
-            MAIL_STORAGE_FLAG_FSYNC_DISABLE) == 0 && ret == 0) {
 -      if (!file->mbox->ibox.fsync_disable && ret == 0) {
++      if (!file->storage->storage.set->fsync_disable && ret == 0) {
                if (fsync(out_fd) < 0) {
 -                      i_error("fsync(%s) failed: %m", temp_path);
 +                      mail_storage_set_critical(&file->storage->storage,
 +                              "fsync(%s) failed: %m", temp_path);
                        ret = -1;
                }
        }
                (void)unlink(temp_path);
                return -1;
        }
-       if ((file->storage->storage.flags &
-            MAIL_STORAGE_FLAG_FSYNC_DISABLE) == 0) {
 -      if (!file->mbox->ibox.fsync_disable) {
++      if (!file->storage->storage.set->fsync_disable) {
                if (fdatasync_path(dest_dir) < 0) {
 -                      i_error("fdatasync(%s) failed: %m", dest_dir);
 +                      mail_storage_set_critical(&file->storage->storage,
 +                              "fdatasync(%s) failed: %m", dest_dir);
                        (void)unlink(dest_path);
                        return -1;
                }
index 82fdbce0ff01c2070c3b37213b5644a1f270b6fd,0000000000000000000000000000000000000000..0ad091c27291138cea72bd933bcd907af4f65862
mode 100644,000000..100644
--- /dev/null
@@@ -1,1108 -1,0 +1,1109 @@@
-               index_storage_get_index_open_flags(storage);
 +/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
 +
 +#include "lib.h"
 +#include "array.h"
 +#include "hash.h"
 +#include "ostream.h"
 +#include "mkdir-parents.h"
 +#include "dbox-storage.h"
 +#include "dbox-file.h"
 +#include "dbox-map-private.h"
 +
 +#define MAX_BACKWARDS_LOOKUPS 10
 +
 +struct dbox_map_transaction_context {
 +      struct dbox_map *map;
 +      struct mail_index_transaction *trans;
 +      struct mail_index_sync_ctx *sync_ctx;
 +
 +      unsigned int changed:1;
 +      unsigned int success:1;
 +};
 +
 +void dbox_map_set_corrupted(struct dbox_map *map, const char *format, ...)
 +{
 +      va_list args;
 +
 +      va_start(args, format);
 +      mail_storage_set_critical(&map->storage->storage,
 +                                "dbox map %s corrupted: %s",
 +                                map->index->filepath,
 +                                t_strdup_vprintf(format, args));
 +      va_end(args);
 +}
 +
 +struct dbox_map *dbox_map_init(struct dbox_storage *storage)
 +{
 +      struct dbox_map *map;
 +
 +      map = i_new(struct dbox_map, 1);
 +      map->storage = storage;
 +      map->index = mail_index_alloc(storage->storage_dir,
 +                                    DBOX_GLOBAL_INDEX_PREFIX);
 +      map->map_ext_id = mail_index_ext_register(map->index, "map",
 +                              sizeof(struct dbox_mail_index_map_header),
 +                              sizeof(struct dbox_mail_index_map_record),
 +                              sizeof(uint32_t));
 +      map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0,
 +                              sizeof(uint16_t), sizeof(uint16_t));
 +      map->created_uid_validity = ioloop_time;
 +      return map;
 +}
 +
 +void dbox_map_deinit(struct dbox_map **_map)
 +{
 +      struct dbox_map *map = *_map;
 +
 +      *_map = NULL;
 +
 +      if (array_is_created(&map->ref0_file_ids))
 +              array_free(&map->ref0_file_ids);
 +      if (map->view != NULL)
 +              mail_index_view_close(&map->view);
 +      mail_index_free(&map->index);
 +      i_free(map);
 +}
 +
 +static int dbox_map_mkdir_storage(struct dbox_storage *storage)
 +{
 +      mode_t mode;
 +      gid_t gid;
 +
 +      mailbox_list_get_dir_permissions(storage->storage.list, NULL,
 +                                       &mode, &gid);
 +      if (mkdir_parents_chown(storage->storage_dir, mode,
 +                              (uid_t)-1, gid) < 0 && errno != EEXIST) {
 +              mail_storage_set_critical(&storage->storage,
 +                      "mkdir(%s) failed: %m", storage->storage_dir);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +int dbox_map_open(struct dbox_map *map, bool create_missing)
 +{
 +      struct mail_storage *storage = &map->storage->storage;
 +      enum mail_index_open_flags open_flags;
 +      int ret;
 +
 +      if (map->view != NULL) {
 +              /* already opened */
 +              return 0;
 +      }
 +
 +      open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY |
-                   map->storage->purge_min_percentage)
++              mail_storage_settings_to_index_flags(storage->set);
 +      if (create_missing) {
 +              open_flags |= MAIL_INDEX_OPEN_FLAG_CREATE;
 +              if (dbox_map_mkdir_storage(map->storage) < 0)
 +                      return -1;
 +      }
 +      ret = mail_index_open(map->index, open_flags, storage->lock_method);
 +      if (ret < 0) {
 +              mail_storage_set_internal_error(storage);
 +              mail_index_reset_error(map->index);
 +              return -1;
 +      }
 +      if (ret == 0) {
 +              /* index not found - for now just return failure */
 +              return -1;
 +      }
 +
 +      map->view = mail_index_view_open(map->index);
 +      return 0;
 +}
 +
 +int dbox_map_refresh(struct dbox_map *map)
 +{
 +      struct mail_index_view_sync_ctx *ctx;
 +      bool delayed_expunges;
 +
 +      if (mail_index_refresh(map->view->index) < 0) {
 +              mail_storage_set_internal_error(&map->storage->storage);
 +              mail_index_reset_error(map->index);
 +              return -1;
 +      }
 +      ctx = mail_index_view_sync_begin(map->view,
 +                              MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
 +      if (mail_index_view_sync_commit(&ctx, &delayed_expunges) < 0) {
 +              mail_storage_set_internal_error(&map->storage->storage);
 +              mail_index_reset_error(map->index);
 +              return -1;
 +      }
 +      return 0;
 +}
 +
 +static int dbox_map_lookup_seq(struct dbox_map *map, uint32_t seq,
 +                             uint32_t *file_id_r, uoff_t *offset_r,
 +                             uoff_t *size_r)
 +{
 +      const struct dbox_mail_index_map_record *rec;
 +      const void *data;
 +      uint32_t uid;
 +      bool expunged;
 +
 +      mail_index_lookup_ext(map->view, seq, map->map_ext_id,
 +                            &data, &expunged);
 +      rec = data;
 +
 +      if (rec == NULL || rec->file_id == 0) {
 +              mail_index_lookup_uid(map->view, seq, &uid);
 +              dbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid);
 +              return -1;
 +      }
 +
 +      *file_id_r = rec->file_id;
 +      *offset_r = rec->offset;
 +      *size_r = rec->size;
 +      return 0;
 +}
 +
 +static int
 +dbox_map_get_seq(struct dbox_map *map, uint32_t map_uid, uint32_t *seq_r)
 +{
 +      if (!mail_index_lookup_seq(map->view, map_uid, seq_r)) {
 +              /* not found - try again after a refresh */
 +              if (dbox_map_refresh(map) < 0)
 +                      return -1;
 +              if (!mail_index_lookup_seq(map->view, map_uid, seq_r))
 +                      return 0;
 +      }
 +      return 1;
 +}
 +
 +int dbox_map_lookup(struct dbox_map *map, uint32_t map_uid,
 +                  uint32_t *file_id_r, uoff_t *offset_r)
 +{
 +      uint32_t seq;
 +      uoff_t size;
 +      int ret;
 +
 +      if (dbox_map_open(map, TRUE) < 0)
 +              return -1;
 +
 +      if ((ret = dbox_map_get_seq(map, map_uid, &seq)) <= 0)
 +              return ret;
 +
 +      if (dbox_map_lookup_seq(map, seq, file_id_r, offset_r, &size) < 0)
 +              return -1;
 +      return 1;
 +}
 +
 +int dbox_map_view_lookup_rec(struct dbox_map *map, struct mail_index_view *view,
 +                           uint32_t seq, struct dbox_mail_lookup_rec *rec_r)
 +{
 +      const uint16_t *ref16_p;
 +      const void *data;
 +      bool expunged;
 +
 +      memset(rec_r, 0, sizeof(*rec_r));
 +      mail_index_lookup_uid(view, seq, &rec_r->map_uid);
 +
 +      mail_index_lookup_ext(view, seq, map->map_ext_id, &data, &expunged);
 +      if (data == NULL) {
 +              dbox_map_set_corrupted(map, "missing map extension");
 +              return -1;
 +      }
 +      memcpy(&rec_r->rec, data, sizeof(rec_r->rec));
 +
 +      mail_index_lookup_ext(view, seq, map->ref_ext_id, &data, &expunged);
 +      if (data == NULL) {
 +              dbox_map_set_corrupted(map, "missing ref extension");
 +              return -1;
 +      }
 +      ref16_p = data;
 +      rec_r->refcount = *ref16_p;
 +      return 0;
 +}
 +
 +int dbox_map_get_file_msgs(struct dbox_map *map, uint32_t file_id,
 +                         ARRAY_TYPE(dbox_map_file_msg) *recs)
 +{
 +      const struct mail_index_header *hdr;
 +      struct dbox_mail_lookup_rec rec;
 +      struct dbox_map_file_msg msg;
 +      uint32_t seq;
 +
 +      if (dbox_map_refresh(map) < 0)
 +              return -1;
 +      hdr = mail_index_get_header(map->view);
 +
 +      memset(&msg, 0, sizeof(msg));
 +      for (seq = 1; seq <= hdr->messages_count; seq++) {
 +              if (dbox_map_view_lookup_rec(map, map->view, seq, &rec) < 0)
 +                      return -1;
 +
 +              if (rec.rec.file_id == file_id) {
 +                      msg.map_uid = rec.map_uid;
 +                      msg.offset = rec.rec.offset;
 +                      msg.refcount = rec.refcount;
 +                      array_append(recs, &msg, 1);
 +              }
 +      }
 +      return 0;
 +}
 +
 +struct dbox_file_size {
 +      uoff_t file_size;
 +      uoff_t ref0_size;
 +};
 +
 +static void dbox_map_filter_zero_refs(struct dbox_map *map)
 +{
 +      ARRAY_TYPE(seq_range) new_ref0_file_ids;
 +      struct hash_table *hash;
 +      struct dbox_file_size *size;
 +      struct seq_range_iter iter;
 +      const struct mail_index_header *hdr;
 +      const struct dbox_mail_index_map_record *rec;
 +      const uint16_t *ref16_p;
 +      const void *data;
 +      uint32_t seq, file_id;
 +      unsigned int i;
 +      bool expunged;
 +      pool_t pool;
 +
 +      pool = pool_alloconly_create("dbox zero ref count", 8*1024);
 +      hash = hash_table_create(default_pool, pool, 0, NULL, NULL);
 +
 +      /* count file sizes */
 +      hdr = mail_index_get_header(map->view);
 +      for (seq = 1; seq <= hdr->messages_count; seq++) {
 +              mail_index_lookup_ext(map->view, seq, map->map_ext_id,
 +                                    &data, &expunged);
 +              if (data == NULL || expunged)
 +                      continue;
 +              rec = data;
 +
 +              if (!seq_range_exists(&map->ref0_file_ids, rec->file_id))
 +                      continue;
 +
 +              /* this file has at least some zero references. count how many
 +                 bytes it has in total and how much of it has refcount=0. */
 +              mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
 +                                    &data, &expunged);
 +              if (data == NULL || expunged)
 +                      continue;
 +              ref16_p = data;
 +
 +              size = hash_table_lookup(hash, POINTER_CAST(rec->file_id));
 +              if (size == NULL) {
 +                      size = p_new(pool, struct dbox_file_size, 1);
 +                      hash_table_insert(hash, POINTER_CAST(rec->file_id),
 +                                        size);
 +              }
 +              if (*ref16_p == 0)
 +                      size->ref0_size += rec->size;
 +              if (size->file_size < rec->offset + rec->size)
 +                      size->file_size = rec->offset + rec->size;
 +      }
 +
 +      /* now drop the files that don't have enough deleted space */
 +      seq_range_array_iter_init(&iter, &map->ref0_file_ids); i = 0;
 +      p_array_init(&new_ref0_file_ids, pool, 
 +                   array_count(&map->ref0_file_ids));
 +      while (seq_range_array_iter_nth(&iter, i++, &file_id)) {
 +              size = hash_table_lookup(hash, POINTER_CAST(file_id));
 +              if (size->ref0_size*100 / size->file_size >=
-       if (map->storage->purge_min_percentage >= 100) {
++                  map->storage->set->dbox_purge_min_percentage)
 +                      seq_range_array_add(&new_ref0_file_ids, 0, file_id);
 +      }
 +      seq_range_array_intersect(&map->ref0_file_ids, &new_ref0_file_ids);
 +
 +      hash_table_destroy(&hash);
 +      pool_unref(&pool);
 +}
 +
 +const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
 +{
 +      const struct mail_index_header *hdr;
 +      const struct dbox_mail_index_map_record *rec;
 +      const uint16_t *ref16_p;
 +      const void *data;
 +      uint32_t seq;
 +      bool expunged;
 +
 +      if (array_is_created(&map->ref0_file_ids))
 +              array_clear(&map->ref0_file_ids);
 +      else
 +              i_array_init(&map->ref0_file_ids, 64);
 +
-       if (map->storage->purge_min_percentage > 0 &&
++      if (map->storage->set->dbox_purge_min_percentage >= 100) {
 +              /* we're never purging anything */
 +              return &map->ref0_file_ids;
 +      }
 +
 +      if (dbox_map_open(map, FALSE) < 0) {
 +              /* some internal error */
 +              return &map->ref0_file_ids;
 +      }
 +      (void)dbox_map_refresh(map);
 +
 +      hdr = mail_index_get_header(map->view);
 +      for (seq = 1; seq <= hdr->messages_count; seq++) {
 +              mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
 +                                    &data, &expunged);
 +              if (data != NULL && !expunged) {
 +                      ref16_p = data;
 +                      if (*ref16_p != 0)
 +                              continue;
 +              }
 +
 +              mail_index_lookup_ext(map->view, seq, map->map_ext_id,
 +                                    &data, &expunged);
 +              if (data != NULL && !expunged) {
 +                      rec = data;
 +                      seq_range_array_add(&map->ref0_file_ids, 0,
 +                                          rec->file_id);
 +              }
 +      }
-       } else if (append_offset + mail_size > storage->rotate_size) {
++      if (map->storage->set->dbox_purge_min_percentage > 0 &&
 +          array_count(&map->ref0_file_ids) > 0)
 +              dbox_map_filter_zero_refs(map);
 +      return &map->ref0_file_ids;
 +}
 +
 +struct dbox_map_transaction_context *
 +dbox_map_transaction_begin(struct dbox_map *map, bool external)
 +{
 +      struct dbox_map_transaction_context *ctx;
 +      enum mail_index_transaction_flags flags =
 +              MAIL_INDEX_TRANSACTION_FLAG_FSYNC;
 +
 +      if (external)
 +              flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL;
 +
 +      ctx = i_new(struct dbox_map_transaction_context, 1);
 +      ctx->map = map;
 +      if (dbox_map_open(map, FALSE) == 0 &&
 +          dbox_map_refresh(map) == 0)
 +              ctx->trans = mail_index_transaction_begin(map->view, flags);
 +      return ctx;
 +}
 +
 +static void
 +dbox_map_sync_handle(struct dbox_map *map, struct mail_index_sync_ctx *sync_ctx)
 +{
 +      struct mail_index_sync_rec sync_rec;
 +      uint32_t seq1, seq2;
 +      uoff_t offset1, offset2;
 +
 +      mail_index_sync_get_offsets(sync_ctx, &seq1, &offset1, &seq2, &offset2);
 +      if (offset1 != offset2 || seq1 != seq2) {
 +              /* something had crashed. need a full resync. */
 +              i_warning("dbox %s: Inconsistency in map index "
 +                        "(%u,%"PRIuUOFF_T" != %u,%"PRIuUOFF_T")",
 +                        map->storage->storage_dir,
 +                        seq1, offset1, seq2, offset2);
 +              map->storage->sync_rebuild = TRUE;
 +      } else {
 +              while (mail_index_sync_next(sync_ctx, &sync_rec)) ;
 +      }
 +}
 +
 +int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx)
 +{
 +      struct dbox_map *map = ctx->map;
 +      struct mail_index_view *view;
 +      struct mail_index_transaction *sync_trans;
 +      int ret;
 +
 +      if (!ctx->changed)
 +              return 0;
 +
 +      /* use syncing to lock the transaction log, so that we always see
 +         log's head_offset = tail_offset */
 +      ret = mail_index_sync_begin(map->index, &ctx->sync_ctx,
 +                                  &view, &sync_trans, 0);
 +      if (ret <= 0) {
 +              i_assert(ret != 0);
 +              mail_storage_set_internal_error(&map->storage->storage);
 +              mail_index_reset_error(map->index);
 +              mail_index_transaction_rollback(&ctx->trans);
 +              return -1;
 +      }
 +      dbox_map_sync_handle(map, ctx->sync_ctx);
 +
 +      if (mail_index_transaction_commit(&ctx->trans) < 0) {
 +              mail_storage_set_internal_error(&map->storage->storage);
 +              mail_index_reset_error(map->index);
 +              return -1;
 +      }
 +      ctx->success = TRUE;
 +      return 0;
 +}
 +
 +void dbox_map_transaction_free(struct dbox_map_transaction_context **_ctx)
 +{
 +      struct dbox_map_transaction_context *ctx = *_ctx;
 +      struct dbox_map *map = ctx->map;
 +
 +      *_ctx = NULL;
 +      if (ctx->success) {
 +              if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
 +                      mail_storage_set_internal_error(&map->storage->storage);
 +                      mail_index_reset_error(map->index);
 +              }
 +      } else if (ctx->sync_ctx != NULL) {
 +              mail_index_sync_rollback(&ctx->sync_ctx);
 +      }
 +      if (ctx->trans != NULL)
 +              mail_index_transaction_rollback(&ctx->trans);
 +      i_free(ctx);
 +}
 +
 +int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
 +                            const ARRAY_TYPE(uint32_t) *map_uids, int diff)
 +{
 +      struct dbox_map *map = ctx->map;
 +      const uint32_t *uids;
 +      unsigned int i, count;
 +      const void *data;
 +      uint32_t seq;
 +      bool expunged;
 +      int cur_diff;
 +
 +      if (ctx->trans == NULL)
 +              return -1;
 +
 +      uids = array_get(map_uids, &count);
 +      for (i = 0; i < count; i++) {
 +              if (!mail_index_lookup_seq(map->view, uids[i], &seq)) {
 +                      /* we can't refresh map here since view has a
 +                         transaction open. */
 +                      dbox_map_set_corrupted(map,
 +                              "refcount update lost map_uid=%u", uids[i]);
 +                      return -1;
 +              }
 +              mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
 +                                    &data, &expunged);
 +              cur_diff = data == NULL ? 0 : *((const uint16_t *)data);
 +              ctx->changed = TRUE;
 +              cur_diff += mail_index_atomic_inc_ext(ctx->trans, seq,
 +                                                    map->ref_ext_id, diff);
 +              if (cur_diff >= 32768) {
 +                      /* we're getting close to the 64k limit. fail early
 +                         to make it less likely that two processes increase
 +                         the refcount enough times to cross the limit */
 +                      mail_storage_set_error(&map->storage->storage,
 +                              MAIL_ERROR_NOTPOSSIBLE,
 +                              "Message has been copied too many times");
 +                      return -1;
 +              }
 +      }
 +      return 0;
 +}
 +
 +int dbox_map_remove_file_id(struct dbox_map *map, uint32_t file_id)
 +{
 +      struct dbox_map_transaction_context *map_trans;
 +      const struct mail_index_header *hdr;
 +      const struct dbox_mail_index_map_record *rec;
 +      const void *data;
 +      bool expunged;
 +      uint32_t seq;
 +      int ret = 0;
 +
 +      /* make sure the map is refreshed, otherwise we might be expunging
 +         messages that have already been moved to other files. */
 +
 +      /* we need a per-file transaction, otherwise we can't refresh the map */
 +      map_trans = dbox_map_transaction_begin(map, TRUE);
 +
 +      hdr = mail_index_get_header(map->view);
 +      for (seq = 1; seq <= hdr->messages_count; seq++) {
 +              mail_index_lookup_ext(map->view, seq, map->map_ext_id,
 +                                    &data, &expunged);
 +              if (data == NULL) {
 +                      dbox_map_set_corrupted(map, "missing map extension");
 +                      ret = -1;
 +                      break;
 +              }
 +
 +              rec = data;
 +              if (rec->file_id == file_id) {
 +                      map_trans->changed = TRUE;
 +                      mail_index_expunge(map_trans->trans, seq);
 +              }
 +      }
 +      if (ret == 0)
 +              (void)dbox_map_transaction_commit(map_trans);
 +      dbox_map_transaction_free(&map_trans);
 +      return ret;
 +}
 +
 +struct dbox_map_append_context *
 +dbox_map_append_begin_storage(struct dbox_storage *storage)
 +{
 +      struct dbox_map_append_context *ctx;
 +
 +      ctx = i_new(struct dbox_map_append_context, 1);
 +      ctx->map = storage->map;
 +      ctx->first_new_file_id = (uint32_t)-1;
 +      i_array_init(&ctx->files, 64);
 +      i_array_init(&ctx->appends, 128);
 +
 +      if (dbox_map_open(ctx->map, TRUE) < 0)
 +              ctx->failed = TRUE;
 +      else {
 +              /* refresh the map so we can try appending to the
 +                 latest files */
 +              (void)dbox_map_refresh(ctx->map);
 +      }
 +      return ctx;
 +}
 +
 +struct dbox_map_append_context *
 +dbox_map_append_begin(struct dbox_mailbox *mbox)
 +{
 +      struct dbox_map_append_context *ctx;
 +
 +      ctx = dbox_map_append_begin_storage(mbox->storage);
 +      ctx->mbox = mbox;
 +      return ctx;
 +}
 +
 +static time_t day_begin_stamp(unsigned int days)
 +{
 +      struct tm tm;
 +      time_t stamp;
 +
 +      if (days == 0)
 +              return 0;
 +
 +      /* get beginning of today */
 +      tm = *localtime(&ioloop_time);
 +      tm.tm_hour = 0;
 +      tm.tm_min = 0;
 +      tm.tm_sec = 0;
 +      stamp = mktime(&tm);
 +      if (stamp == (time_t)-1)
 +              i_panic("mktime(today) failed");
 +
 +      return stamp - (3600*24 * (days-1));
 +}
 +
 +static bool
 +dbox_map_file_try_append(struct dbox_map_append_context *ctx,
 +                       uint32_t file_id, time_t stamp, uoff_t mail_size,
 +                       struct dbox_file **file_r, struct ostream **output_r,
 +                       bool *retry_later_r)
 +{
 +      struct dbox_map *map = ctx->map;
 +      struct dbox_storage *storage = map->storage;
 +      struct dbox_file *file;
 +      struct stat st;
 +      uoff_t append_offset;
 +      bool deleted, file_too_old = FALSE;
 +      int ret;
 +
 +      *file_r = NULL;
 +      *retry_later_r = FALSE;
 +
 +      file = dbox_file_init_multi(storage, file_id);
 +      if (dbox_file_open_or_create(file, &deleted) <= 0 || deleted) {
 +              dbox_file_unref(&file);
 +              return TRUE;
 +      }
 +      if (file->lock != NULL) {
 +              /* already locked, we're possibly in the middle of purging it
 +                 in which case we really don't want to write there. */
 +              dbox_file_unref(&file);
 +              return TRUE;
 +      }
 +
 +      if (file->create_time < stamp)
 +              file_too_old = TRUE;
 +      else if ((ret = dbox_file_try_lock(file)) <= 0) {
 +              /* locking failed */
 +              *retry_later_r = ret == 0;
 +      } else if (stat(file->current_path, &st) < 0) {
 +              if (errno != ENOENT)
 +                      i_error("stat(%s) failed: %m", file->current_path);
 +              /* the file was unlinked between opening and locking it. */
 +      } else if (dbox_file_get_append_stream(file, &append_offset,
 +                                             output_r) <= 0) {
 +              /* couldn't append to this file */
-       if (mail_size >= map->storage->rotate_size)
++      } else if (append_offset + mail_size > storage->set->dbox_rotate_size) {
 +              /* file was too large after all */
 +              dbox_file_cancel_append(file, append_offset);
 +      } else {
 +              /* success */
 +              *file_r = file;
 +              return TRUE;
 +      }
 +
 +      /* failure */
 +      dbox_file_unlock(file);
 +      dbox_file_unref(&file);
 +      return !file_too_old;
 +}
 +
 +static bool
 +dbox_map_is_appending(struct dbox_map_append_context *ctx, uint32_t file_id)
 +{
 +      struct dbox_file *const *files;
 +      unsigned int i, count;
 +
 +      /* there shouldn't be many files open, don't bother with anything
 +         faster. */
 +      files = array_get(&ctx->files, &count);
 +      for (i = 0; i < count; i++) {
 +              if (files[i]->file_id == file_id)
 +                      return TRUE;
 +      }
 +      return FALSE;
 +}
 +
 +static int
 +dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
 +                            uoff_t mail_size, struct dbox_file **file_r,
 +                            struct ostream **output_r, bool *existing_r)
 +{
 +      struct dbox_map *map = ctx->map;
++      const struct dbox_settings *set = map->storage->set;
 +      struct dbox_file *const *files;
 +      const struct mail_index_header *hdr;
 +      unsigned int i, count, backwards_lookup_count;
 +      uint32_t seq, seq1, uid, file_id, min_seen_file_id;
 +      uoff_t offset, append_offset, size;
 +      time_t stamp;
 +      bool retry_later;
 +
 +      *existing_r = FALSE;
 +
-               if (append_offset + mail_size <= map->storage->rotate_size &&
++      if (mail_size >= set->dbox_rotate_size)
 +              return 0;
 +
 +      /* first try to use files already used in this append */
 +      files = array_get(&ctx->files, &count);
 +      for (i = count; i > ctx->files_nonappendable_count; i--) {
 +              if (files[i-1]->output == NULL) {
 +                      /* we already decided we can't append to this */
 +                      continue;
 +              }
 +
 +              append_offset = dbox_file_get_next_append_offset(files[i-1]);
-       stamp = day_begin_stamp(map->storage->rotate_days);
++              if (append_offset + mail_size <= set->dbox_rotate_size &&
 +                  dbox_file_get_append_stream(files[i-1], &append_offset,
 +                                              output_r) > 0) {
 +                      *file_r = files[i-1];
 +                      *existing_r = TRUE;
 +                      return 1;
 +              }
 +              /* can't append to this file anymore */
 +#if 0 /* FIXME: we can't close files, otherwise we lose the lock too early */
 +              if (files[i-1]->fd != -1) {
 +                      /* avoid wasting fds by closing the file, but not if
 +                         we're also reading from it. */
 +                      if (dbox_file_flush_append(files[i-1]) < 0)
 +                              return -1;
 +                      dbox_file_unlock(files[i-1]);
 +                      if (files[i-1]->refcount == 1)
 +                              dbox_file_close(files[i-1]);
 +              }
 +#endif
 +      }
 +      ctx->files_nonappendable_count = count;
 +
 +      /* try to find an existing appendable file */
-               if (offset + size + mail_size >= map->storage->rotate_size)
++      stamp = day_begin_stamp(set->dbox_rotate_days);
 +      hdr = mail_index_get_header(map->view);
 +      min_seen_file_id = (uint32_t)-1;
 +
 +      ctx->orig_next_uid = hdr->next_uid;
 +      backwards_lookup_count = 0;
 +      for (seq = hdr->messages_count; seq > 0; seq--) {
 +              if (dbox_map_lookup_seq(map, seq, &file_id, &offset, &size) < 0)
 +                      return -1;
 +              if (file_id >= min_seen_file_id)
 +                      continue;
 +              min_seen_file_id = file_id;
 +
 +              if (++backwards_lookup_count > MAX_BACKWARDS_LOOKUPS) {
 +                      /* we've wasted enough time here */
 +                      break;
 +              }
 +
 +              /* first lookup: this should be enough usually, but we can't
 +                 be sure until after locking. also if messages were recently
 +                 moved, this message might not be the last one in the file. */
-               file = ctx->map->storage->rotate_size == 0 ?
++              if (offset + size + mail_size >= set->dbox_rotate_size)
 +                      continue;
 +
 +              if (dbox_map_is_appending(ctx, file_id)) {
 +                      /* already checked this */
 +                      continue;
 +              }
 +
 +              mail_index_lookup_uid(map->view, seq, &uid);
 +              if (!dbox_map_file_try_append(ctx, file_id, stamp, mail_size,
 +                                            file_r, output_r, &retry_later)) {
 +                      /* file is too old. the rest of the files are too. */
 +                      break;
 +              }
 +              /* NOTE: we've now refreshed map view. there are no guarantees
 +                 about sequences anymore. */
 +              if (*file_r != NULL)
 +                      return 1;
 +              /* FIXME: use retry_later somehow */
 +              if (uid == 1 ||
 +                  !mail_index_lookup_seq_range(map->view, 1, uid-1,
 +                                               &seq1, &seq))
 +                      break;
 +              seq++;
 +      }
 +      return 0;
 +}
 +
 +int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
 +                       struct dbox_file **file_r, struct ostream **output_r)
 +{
 +      struct dbox_file *file = NULL;
 +      struct dbox_map_append *append;
 +      uoff_t append_offset;
 +      bool existing;
 +      int ret;
 +
 +      if (ctx->failed)
 +              return -1;
 +
 +      ret = dbox_map_find_appendable_file(ctx, mail_size, &file,
 +                                          output_r, &existing);
 +      if (ret < 0)
 +              return -1;
 +
 +      if (ret == 0) {
 +              /* create a new file */
++              file = ctx->map->storage->set->dbox_rotate_size == 0 ?
 +                      dbox_file_init_single(ctx->mbox, 0) :
 +                      dbox_file_init_multi(ctx->map->storage, 0);
 +              ret = dbox_file_get_append_stream(file, &append_offset,
 +                                                output_r);
 +              if (ret <= 0) {
 +                      i_assert(ret < 0);
 +                      (void)unlink(file->current_path);
 +                      dbox_file_unref(&file);
 +                      return -1;
 +              }
 +      }
 +
 +      if (file->single_mbox == NULL) {
 +              append = array_append_space(&ctx->appends);
 +              append->file = file;
 +              append->offset = (*output_r)->offset;
 +              append->size = (uint32_t)-1;
 +      }
 +      if (!existing) {
 +              i_assert(file->first_append_offset == 0);
 +              i_assert(file->output != NULL);
 +              file->first_append_offset = file->output->offset;
 +              array_append(&ctx->files, &file, 1);
 +      }
 +      *file_r = file;
 +      return 0;
 +}
 +
 +void dbox_map_append_finish_multi_mail(struct dbox_map_append_context *ctx)
 +{
 +      struct dbox_map_append *appends;
 +      unsigned int count;
 +
 +      appends = array_get_modifiable(&ctx->appends, &count);
 +      i_assert(count > 0 && appends[count-1].size == (uint32_t)-1);
 +      appends[count-1].size = appends[count-1].file->output->offset -
 +              appends[count-1].offset;
 +}
 +
 +static int
 +dbox_map_get_next_file_id(struct dbox_map *map, struct mail_index_view *view,
 +                        uint32_t *file_id_r)
 +{
 +      const struct dbox_mail_index_map_header *hdr;
 +      const void *data;
 +      size_t data_size;
 +
 +      mail_index_get_header_ext(view, map->map_ext_id, &data, &data_size);
 +      if (data_size != sizeof(*hdr)) {
 +              if (data_size != 0) {
 +                      dbox_map_set_corrupted(map, "hdr size=%u", data_size);
 +                      return -1;
 +              }
 +              /* first file */
 +              *file_id_r = 1;
 +      } else {
 +              hdr = data;
 +              *file_id_r = hdr->highest_file_id + 1;
 +      }
 +      return 0;
 +}
 +
 +static int dbox_map_assign_file_ids(struct dbox_map_append_context *ctx,
 +                                  bool separate_transaction)
 +{
 +      struct dbox_file *const *files;
 +      unsigned int i, count;
 +      uint32_t first_file_id, file_id;
 +      int ret;
 +
 +      /* start the syncing. we'll need it even if there are no file ids to
 +         be assigned. */
 +      ret = mail_index_sync_begin(ctx->map->index, &ctx->sync_ctx,
 +                                  &ctx->sync_view, &ctx->sync_trans, 0);
 +      if (ret <= 0) {
 +              i_assert(ret != 0);
 +              mail_storage_set_internal_error(&ctx->map->storage->storage);
 +              mail_index_reset_error(ctx->map->index);
 +              return -1;
 +      }
 +      dbox_map_sync_handle(ctx->map, ctx->sync_ctx);
 +
 +      if (dbox_map_get_next_file_id(ctx->map, ctx->sync_view, &file_id) < 0) {
 +              mail_index_sync_rollback(&ctx->sync_ctx);
 +              return -1;
 +      }
 +
 +      /* assign file_ids for newly created multi-files */
 +      first_file_id = file_id;
 +      files = array_get(&ctx->files, &count);
 +      for (i = 0; i < count; i++) {
 +              if (files[i]->single_mbox != NULL)
 +                      continue;
 +
 +              if (files[i]->output != NULL) {
 +                      if (dbox_file_flush_append(files[i]) < 0) {
 +                              ret = -1;
 +                              break;
 +                      }
 +              }
 +
 +              if (files[i]->file_id == 0) {
 +                      if (dbox_file_assign_id(files[i], file_id++) < 0) {
 +                              ret = -1;
 +                              break;
 +                      }
 +              }
 +      }
 +
 +      if (ret < 0) {
 +              mail_index_sync_rollback(&ctx->sync_ctx);
 +              return -1;
 +      }
 +
 +      ctx->trans = !separate_transaction ? NULL :
 +              mail_index_transaction_begin(ctx->map->view,
 +                                      MAIL_INDEX_TRANSACTION_FLAG_FSYNC);
 +
 +      /* update the highest used file_id */
 +      if (first_file_id != file_id) {
 +              file_id--;
 +              mail_index_update_header_ext(ctx->trans != NULL ? ctx->trans :
 +                                           ctx->sync_trans,
 +                                           ctx->map->map_ext_id,
 +                                           0, &file_id, sizeof(file_id));
 +      }
 +      return 0;
 +}
 +
 +int dbox_map_append_assign_map_uids(struct dbox_map_append_context *ctx,
 +                                  uint32_t *first_map_uid_r,
 +                                  uint32_t *last_map_uid_r)
 +{
 +      const struct dbox_map_append *appends;
 +      const struct mail_index_header *hdr;
 +      struct dbox_mail_index_map_record rec;
 +      unsigned int i, count;
 +      uint32_t seq, first_uid, next_uid;
 +      uint16_t ref16;
 +      int ret = 0;
 +
 +      if (array_count(&ctx->appends) == 0) {
 +              *first_map_uid_r = 0;
 +              *last_map_uid_r = 0;
 +              return 0;
 +      }
 +
 +      if (dbox_map_assign_file_ids(ctx, TRUE) < 0)
 +              return -1;
 +
 +      /* append map records to index */
 +      memset(&rec, 0, sizeof(rec));
 +      ref16 = 1;
 +      appends = array_get(&ctx->appends, &count);
 +      for (i = 0; i < count; i++) {
 +              i_assert(appends[i].offset <= (uint32_t)-1);
 +              i_assert(appends[i].size <= (uint32_t)-1);
 +
 +              rec.file_id = appends[i].file->file_id;
 +              rec.offset = appends[i].offset;
 +              rec.size = appends[i].size;
 +
 +              mail_index_append(ctx->trans, 0, &seq);
 +              mail_index_update_ext(ctx->trans, seq, ctx->map->map_ext_id,
 +                                    &rec, NULL);
 +              mail_index_update_ext(ctx->trans, seq, ctx->map->ref_ext_id,
 +                                    &ref16, NULL);
 +      }
 +
 +      /* assign map UIDs for appended records */
 +      hdr = mail_index_get_header(ctx->sync_view);
 +      first_uid = hdr->next_uid;
 +      mail_index_append_assign_uids(ctx->trans, first_uid, &next_uid);
 +      i_assert(next_uid - first_uid == count);
 +
 +      if (hdr->uid_validity == 0) {
 +              /* we don't really care about uidvalidity, but it can't be 0 */
 +              uint32_t uid_validity = ioloop_time;
 +              mail_index_update_header(ctx->trans,
 +                      offsetof(struct mail_index_header, uid_validity),
 +                      &uid_validity, sizeof(uid_validity), TRUE);
 +      }
 +
 +      if (mail_index_transaction_commit(&ctx->trans) < 0) {
 +              mail_storage_set_internal_error(&ctx->map->storage->storage);
 +              mail_index_reset_error(ctx->map->index);
 +              return -1;
 +      }
 +
 +      *first_map_uid_r = first_uid;
 +      *last_map_uid_r = next_uid - 1;
 +      return ret;
 +}
 +
 +int dbox_map_append_move(struct dbox_map_append_context *ctx,
 +                       const ARRAY_TYPE(uint32_t) *map_uids,
 +                       const ARRAY_TYPE(seq_range) *expunge_map_uids)
 +{
 +      const struct dbox_map_append *appends;
 +      struct dbox_mail_index_map_record rec;
 +      struct seq_range_iter iter;
 +      const uint32_t *uids;
 +      unsigned int i, j, map_uids_count, appends_count;
 +      uint32_t uid, seq;
 +
 +      if (dbox_map_assign_file_ids(ctx, FALSE) < 0)
 +              return -1;
 +
 +      memset(&rec, 0, sizeof(rec));
 +      appends = array_get(&ctx->appends, &appends_count);
 +
 +      uids = array_get(map_uids, &map_uids_count);
 +      for (i = j = 0; i < map_uids_count; i++) {
 +              i_assert(j < appends_count);
 +              rec.file_id = appends[j].file->file_id;
 +              rec.offset = appends[j].offset;
 +              rec.size = appends[j].size;
 +              j++;
 +
 +              if (!mail_index_lookup_seq(ctx->sync_view, uids[i], &seq))
 +                      i_unreached();
 +              mail_index_update_ext(ctx->sync_trans, seq,
 +                                    ctx->map->map_ext_id, &rec, NULL);
 +      }
 +
 +      seq_range_array_iter_init(&iter, expunge_map_uids); i = 0;
 +      while (seq_range_array_iter_nth(&iter, i++, &uid)) {
 +              if (!mail_index_lookup_seq(ctx->sync_view, uid, &seq))
 +                      i_unreached();
 +              mail_index_expunge(ctx->sync_trans, seq);
 +      }
 +      return 0;
 +}
 +
 +int dbox_map_append_assign_uids(struct dbox_map_append_context *ctx,
 +                              uint32_t first_uid, uint32_t last_uid)
 +{
 +      struct dbox_file *const *files;
 +      unsigned int i, count;
 +      uint32_t next_uid = first_uid;
 +
 +      files = array_get(&ctx->files, &count);
 +      for (i = 0; i < count; i++) {
 +              if (files[i]->single_mbox == NULL)
 +                      continue;
 +
 +              if (dbox_file_assign_id(files[i], next_uid++) < 0)
 +                      return -1;
 +      }
 +      i_assert(next_uid == last_uid + 1);
 +      return 0;
 +}
 +
 +int dbox_map_append_commit(struct dbox_map_append_context *ctx)
 +{
 +      struct dbox_map *map = ctx->map;
 +
 +      i_assert(ctx->trans == NULL);
 +
 +      if (ctx->sync_ctx != NULL) {
 +              if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
 +                      mail_storage_set_internal_error(&map->storage->storage);
 +                      mail_index_reset_error(map->index);
 +                      return -1;
 +              }
 +      }
 +      ctx->committed = TRUE;
 +      return 0;
 +}
 +
 +static void dbox_map_append_file_rollback(struct dbox_file *file)
 +{
 +      struct mail_storage *storage = &file->storage->storage;
 +
 +      if (file->output != NULL) {
 +              /* flush before truncating */
 +              (void)o_stream_flush(file->output);
 +      }
 +
 +      if (file->file_id != 0 &&
 +          file->first_append_offset > file->file_header_size) {
 +              if (ftruncate(file->fd, file->first_append_offset) < 0) {
 +                      mail_storage_set_critical(storage,
 +                              "ftruncate(%s, %"PRIuUOFF_T") failed: %m",
 +                              file->current_path, file->first_append_offset);
 +              }
 +      } else {
 +              if (unlink(file->current_path) < 0) {
 +                      mail_storage_set_critical(storage,
 +                              "unlink(%s) failed: %m", file->current_path);
 +              }
 +      }
 +}
 +
 +void dbox_map_append_free(struct dbox_map_append_context **_ctx)
 +{
 +      struct dbox_map_append_context *ctx = *_ctx;
 +      struct dbox_file **files;
 +      unsigned int i, count;
 +
 +      *_ctx = NULL;
 +
 +      if (ctx->trans != NULL)
 +              mail_index_transaction_rollback(&ctx->trans);
 +      if (ctx->sync_ctx != NULL)
 +              mail_index_sync_rollback(&ctx->sync_ctx);
 +
 +      files = array_get_modifiable(&ctx->files, &count);
 +      for (i = 0; i < count; i++) {
 +              if (!ctx->committed)
 +                      dbox_map_append_file_rollback(files[i]);
 +
 +              files[i]->first_append_offset = 0;
 +              dbox_file_unlock(files[i]);
 +              dbox_file_unref(&files[i]);
 +      }
 +      array_free(&ctx->appends);
 +      array_free(&ctx->files);
 +      i_free(ctx);
 +}
 +
 +uint32_t dbox_map_get_uid_validity(struct dbox_map *map)
 +{
 +      const struct mail_index_header *hdr;
 +
 +      i_assert(map->view != NULL);
 +
 +      hdr = mail_index_get_header(map->view);
 +      return hdr->uid_validity != 0 ? hdr->uid_validity :
 +              map->created_uid_validity;
 +}
index 45df36e195860db04c74852656b1621ff5e41289,738f80f72c2d0523841ac184de085dfd69325b0b..724daa6e2be649da94d877671de9b45754de733b
@@@ -432,17 -467,9 +432,17 @@@ void dbox_transaction_save_commit_post(
  {
        ctx->ctx.transaction = NULL; /* transaction is already freed */
  
 -      (void)dbox_sync_finish(&ctx->sync_ctx, TRUE);
 +      /* finish writing the mailbox APPENDs */
 +      if (dbox_sync_finish(&ctx->sync_ctx, TRUE) == 0) {
 +              if (ctx->map_trans != NULL)
 +                      (void)dbox_map_transaction_commit(ctx->map_trans);
 +              /* commit only updates the sync tail offset, everything else
 +                 was already written at this point. */
 +              (void)dbox_map_append_commit(ctx->append_ctx);
 +      }
 +      dbox_map_append_free(&ctx->append_ctx);
  
--      if (!ctx->mbox->ibox.fsync_disable) {
++      if (!ctx->mbox->storage->storage.set->fsync_disable) {
                if (fdatasync_path(ctx->mbox->path) < 0) {
                        i_error("fdatasync_path(%s) failed: %m",
                                ctx->mbox->path);
index 0000000000000000000000000000000000000000,9f87da4edcc180e1e95cfe4ac46c080db9cf1178..f66fe0ec8c2fc98c57bc2e5b28a0440c418091a6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,45 +1,47 @@@
 -      MEMBER(dbox_max_open_files) 64
+ /* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
+ #include "lib.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "dbox-settings.h"
+ #include <stddef.h>
+ #undef DEF
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct dbox_settings, name), NULL }
+ static struct setting_define dbox_setting_defines[] = {
+       DEF(SET_UINT, dbox_rotate_size),
+       DEF(SET_UINT, dbox_rotate_min_size),
+       DEF(SET_UINT, dbox_rotate_days),
+       DEF(SET_UINT, dbox_max_open_files),
++      DEF(SET_UINT, dbox_purge_min_percentage),
+       SETTING_DEFINE_LIST_END
+ };
+ static struct dbox_settings dbox_default_settings = {
+       MEMBER(dbox_rotate_size) 2048*1024,
+       MEMBER(dbox_rotate_min_size) 16*1024,
+       MEMBER(dbox_rotate_days) 0,
++      MEMBER(dbox_max_open_files) 64,
++      MEMBER(dbox_purge_min_percentage) 0
+ };
+ static struct setting_parser_info dbox_setting_parser_info = {
+       MEMBER(defines) dbox_setting_defines,
+       MEMBER(defaults) &dbox_default_settings,
+       MEMBER(parent) &mail_user_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct dbox_settings)
+ };
+ const struct setting_parser_info *dbox_get_setting_parser_info(void)
+ {
+       return &dbox_setting_parser_info;
+ }
index 0000000000000000000000000000000000000000,58a473c8679ee692b415b5072794b8c9d612a609..6a1f3898355588f9ba25657cd0c7cb388b642bb6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,13 +1,14 @@@
+ #ifndef DBOX_SETTINGS_H
+ #define DBOX_SETTINGS_H
+ struct dbox_settings {
+       unsigned int dbox_rotate_size;
+       unsigned int dbox_rotate_min_size;
+       unsigned int dbox_rotate_days;
+       unsigned int dbox_max_open_files;
++      unsigned int dbox_purge_min_percentage;
+ };
+ const struct setting_parser_info *dbox_get_setting_parser_info(void);
+ #endif
index a409ffe22d54efb1fc5296c1566107eaee4584cb,7716a06a7b628770b79ddd0ef5007d3cdc6a89e4..cb0c6e1b4cc04c26fdef0a0e18be66cd37cf0830
@@@ -56,35 -50,26 +56,32 @@@ dbox_get_list_settings(struct mailbox_l
                       const char **layout_r, const char **alt_dir_r,
                       const char **error_r)
  {
-       const char *subs_fname = DBOX_SUBSCRIPTION_FILE_NAME;
-       bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
 -      bool debug = storage->set->mail_debug;
--
        *layout_r = "fs";
  
        memset(list_set, 0, sizeof(*list_set));
-       list_set->subscription_fname = subs_fname;
+       list_set->subscription_fname = DBOX_SUBSCRIPTION_FILE_NAME;
        list_set->maildir_name = DBOX_MAILDIR_NAME;
 +      list_set->mailbox_dir_name = DBOX_MAILBOX_DIR_NAME;
  
        if (data == NULL || *data == '\0' || *data == ':') {
                /* we won't do any guessing for this format. */
--              if (debug)
++              if (storage->set->mail_debug)
                        i_info("dbox: mailbox location not given");
                *error_r = "Root mail directory not given";
                return -1;
        }
  
--      if (debug)
++      if (storage->set->mail_debug)
                i_info("dbox: data=%s", data);
 -      return mailbox_list_settings_parse(data, list_set, storage->ns,
 -                                         layout_r, alt_dir_r, error_r);
 +      if (mailbox_list_settings_parse(data, list_set, storage->ns,
 +                                      layout_r, alt_dir_r, error_r) < 0)
 +              return -1;
 +
 +      if (*list_set->mailbox_dir_name == '\0') {
 +              *error_r = "dbox: MAILBOXDIR must not be empty";
 +              return -1;
 +      }
 +      return 0;
  }
  
  static struct mail_storage *dbox_alloc(void)
@@@ -107,7 -92,7 +104,7 @@@ static int dbox_create(struct mail_stor
        struct dbox_storage *storage = (struct dbox_storage *)_storage;
        struct mailbox_list_settings list_set;
        struct stat st;
-       const char *layout, *alt_dir, *dir, *value;
 -      const char *layout, *alt_dir;
++      const char *layout, *alt_dir, *dir;
  
        if (dbox_get_list_settings(&list_set, data, _storage,
                                   &layout, &alt_dir, error_r) < 0)
                return -1;
        }
  
-       value = getenv("DBOX_ROTATE_SIZE");
-       if (value != NULL)
-               storage->rotate_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
-       else
-               storage->rotate_size = DBOX_DEFAULT_ROTATE_SIZE;
-       value = getenv("DBOX_ROTATE_MIN_SIZE");
-       if (value != NULL)
-               storage->rotate_min_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
-       else
-               storage->rotate_min_size = DBOX_DEFAULT_ROTATE_MIN_SIZE;
-       if (storage->rotate_min_size > storage->rotate_size)
-               storage->rotate_min_size = storage->rotate_size;
-       value = getenv("DBOX_ROTATE_DAYS");
-       if (value != NULL)
-               storage->rotate_days = (unsigned int)strtoul(value, NULL, 10);
-       else
-               storage->rotate_days = DBOX_DEFAULT_ROTATE_DAYS;
-       value = getenv("DBOX_MAX_OPEN_FILES");
-       if (value != NULL)
-               storage->max_open_files = (unsigned int)strtoul(value, NULL, 10);
-       else
-               storage->max_open_files = DBOX_DEFAULT_MAX_OPEN_FILES;
-       value = getenv("DBOX_PURGE_MIN_PERCENTAGE");
-       if (value != NULL)
-               storage->purge_min_percentage = (unsigned int)strtoul(value, NULL, 10);
-       else
-               storage->purge_min_percentage = DBOX_DEFAULT_PURGE_MIN_PERCENTAGE;
-       if (storage->max_open_files <= 1) {
+       storage->set = mail_storage_get_driver_settings(_storage);
++      if (storage->set->dbox_max_open_files <= 1) {
 +              /* we store file offsets in a 32bit integer. */
 +              *error_r = "dbox_max_open_files must be at least 2";
 +              return -1;
 +      }
-       if (storage->rotate_size > (uint32_t)-1) {
-               /* we store file offsets in a 32bit integer. */
-               *error_r = "dbox_rotate_size must be less than 4 GB";
-               return -1;
-       }
  
        if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
                return -1;
                                storage, &storage->list_module_ctx);
  
        /* finish list init after we've overridden vfuncs */
-       mailbox_list_init(_storage->list, _storage->ns, &list_set,
-                         mail_storage_get_list_flags(_storage->flags));
+       mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
 +
 +      dir = mailbox_list_get_path(storage->storage.list, NULL,
 +                                  MAILBOX_LIST_PATH_TYPE_DIR);
 +      storage->storage_dir = p_strconcat(_storage->pool, dir,
 +                                         "/"DBOX_GLOBAL_DIR_NAME, NULL);
 +      storage->alt_storage_dir = p_strconcat(_storage->pool, alt_dir,
 +                                             "/"DBOX_GLOBAL_DIR_NAME, NULL);
-       i_array_init(&storage->open_files, I_MIN(storage->max_open_files, 128));
++      i_array_init(&storage->open_files,
++                   I_MIN(storage->set->dbox_max_open_files, 128));
 +
 +      storage->map = dbox_map_init(storage);
 +      mailbox_list_get_permissions(storage->storage.list, NULL,
 +                                   &storage->create_mode,
 +                                   &storage->create_gid);
        return 0;
  }
  
@@@ -458,68 -342,74 +423,71 @@@ static int dbox_mailbox_create(struct m
  }
  
  static int
 -dbox_delete_nonrecursive(struct mailbox_list *list, const char *path,
 -                       const char *name)
 +dbox_mailbox_unref_mails(struct dbox_storage *storage, const char *path)
  {
 -      DIR *dir;
 -      struct dirent *d;
 -      string_t *full_path;
 -      unsigned int dir_len;
 -      bool unlinked_something = FALSE;
 -
 -      dir = opendir(path);
 -      if (dir == NULL) {
 -              if (errno == ENOENT)
 -                      return 0;
 -              if (!mailbox_list_set_error_from_errno(list)) {
 -                      mailbox_list_set_critical(list,
 -                              "opendir(%s) failed: %m", path);
 -              }
 -              return -1;
 -      }
 -
 -      full_path = t_str_new(256);
 -      str_append(full_path, path);
 -      str_append_c(full_path, '/');
 -      dir_len = str_len(full_path);
 -
 -      errno = 0;
 -      while ((d = readdir(dir)) != NULL) {
 -              if (d->d_name[0] == '.') {
 -                      /* skip . and .. */
 -                      if (d->d_name[1] == '\0')
 -                              continue;
 -                      if (d->d_name[1] == '.' && d->d_name[2] == '\0')
 -                              continue;
 -              }
 +      struct mailbox_list *list = storage->storage.list;
++      const struct mail_storage_settings *old_set;
++      struct mail_storage_settings tmp_set;
 +      struct mailbox *box;
 +      struct dbox_mailbox *mbox;
 +      const struct mail_index_header *hdr;
 +      const struct dbox_mail_index_record *dbox_rec;
 +      struct dbox_map_transaction_context *map_trans;
 +      ARRAY_TYPE(uint32_t) map_uids;
 +      const void *data;
-       bool expunged, old_fs_access;
++      bool expunged;
 +      uint32_t seq;
 +      int ret;
  
-       old_fs_access = (list->flags & MAILBOX_LIST_FLAG_FULL_FS_ACCESS) != 0;
-       list->flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
 -              str_truncate(full_path, dir_len);
 -              str_append(full_path, d->d_name);
 -
 -              /* trying to unlink() a directory gives either EPERM or EISDIR
 -                 (non-POSIX). it doesn't really work anywhere in practise,
 -                 so don't bother stat()ing the file first */
 -              if (unlink(str_c(full_path)) == 0)
 -                      unlinked_something = TRUE;
 -              else if (errno != ENOENT && errno != EISDIR && errno != EPERM) {
 -                      mailbox_list_set_critical(list, "unlink(%s) failed: %m",
 -                                                str_c(full_path));
++      old_set = list->mail_set;
++      tmp_set = *list->mail_set;
++      tmp_set.mail_full_filesystem_access = TRUE;
++      list->mail_set = &tmp_set;
 +      box = dbox_open(storage, path, MAILBOX_OPEN_IGNORE_ACLS |
 +                      MAILBOX_OPEN_KEEP_RECENT);
-       if (!old_fs_access)
-               list->flags &= ~MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
++      list->mail_set = old_set;
 +      if (box == NULL)
 +              return -1;
 +      mbox = (struct dbox_mailbox *)box;
 +
 +      /* get a list of all map_uids in this mailbox */
 +      i_array_init(&map_uids, 128);
 +      hdr = mail_index_get_header(mbox->ibox.view);
-       for (seq = 1; seq <= hdr->messages_count; hdr++) {
++      for (seq = 1; seq <= hdr->messages_count; seq++) {
 +              mail_index_lookup_ext(mbox->ibox.view, seq, mbox->dbox_ext_id,
 +                                    &data, &expunged);
 +              dbox_rec = data;
 +              if (dbox_rec == NULL) {
 +                      /* no multi-mails */
 +                      break;
                }
 +              if (dbox_rec->map_uid != 0)
 +                      array_append(&map_uids, &dbox_rec->map_uid, 1);
        }
  
 -      if (closedir(dir) < 0) {
 -              mailbox_list_set_critical(list, "closedir(%s) failed: %m",
 -                                        path);
 -      }
 +      /* unreference the map_uids */
 +      map_trans = dbox_map_transaction_begin(storage->map, FALSE);
 +      ret = dbox_map_update_refcounts(map_trans, &map_uids, -1);
 +      if (ret == 0)
 +              ret = dbox_map_transaction_commit(map_trans);
 +      dbox_map_transaction_free(&map_trans);
 +      array_free(&map_uids);
 +      mailbox_close(&box);
 +      return ret;
 +}
  
 -      if (rmdir(path) == 0)
 -              unlinked_something = TRUE;
 -      else if (errno != ENOENT && errno != ENOTEMPTY) {
 -              mailbox_list_set_critical(list, "rmdir(%s) failed: %m", path);
 -              return -1;
 -      }
 +static const char *dbox_get_trash_dest(const char *trash_dir)
 +{
 +      const char *path;
 +      unsigned char randbuf[16];
 +      struct stat st;
  
 -      if (!unlinked_something) {
 -              mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND,
 -                      t_strdup_printf("Directory %s isn't empty, "
 -                                      "can't delete it.", name));
 -              return -1;
 -      }
 -      return 1;
 +      do {
 +              random_fill_weak(randbuf, sizeof(randbuf));
 +              path = t_strconcat(trash_dir, "/",
 +                      binary_to_hex(randbuf, sizeof(randbuf)), NULL);
 +      } while (lstat(path, &st) == 0);
 +      return path;
  }
  
  static int
index b5f96c0bb94a7de157339dd6bc9dc2df9322bd97,d224cc85d2e6627183c3a749b15e74d1b280dc40..332f818227bdfc3e213c7290d1dfe39b5acc662d
@@@ -3,17 -3,15 +3,18 @@@
  
  #include "index-storage.h"
  #include "mailbox-list-private.h"
+ #include "dbox-settings.h"
  
  #define DBOX_STORAGE_NAME "dbox"
 -#define DBOX_SUBSCRIPTION_FILE_NAME ".dbox-subscriptions"
 -#define DBOX_UIDVALIDITY_FILE_NAME ".dbox-uidvalidity"
 +#define DBOX_SUBSCRIPTION_FILE_NAME "subscriptions"
 +#define DBOX_UIDVALIDITY_FILE_NAME "dovecot-uidvalidity"
  #define DBOX_INDEX_PREFIX "dovecot.index"
  
 +#define DBOX_MAILBOX_DIR_NAME "mailboxes"
 +#define DBOX_TRASH_DIR_NAME "trash"
  #define DBOX_MAILDIR_NAME "dbox-Mails"
 -#define DBOX_INDEX_NAME "dbox.index"
 +#define DBOX_GLOBAL_INDEX_PREFIX "dovecot.map.index"
 +#define DBOX_GLOBAL_DIR_NAME "storage"
  #define DBOX_MAIL_FILE_MULTI_PREFIX "m."
  #define DBOX_MAIL_FILE_UID_PREFIX "u."
  #define DBOX_MAIL_FILE_MULTI_FORMAT DBOX_MAIL_FILE_MULTI_PREFIX"%u"
@@@ -43,25 -32,8 +37,22 @@@ struct dbox_index_header 
  struct dbox_storage {
        struct mail_storage storage;
        union mailbox_list_module_context list_module_ctx;
+       const struct dbox_settings *set;
 +
 +      /* root path for alt directory */
        const char *alt_dir;
-       uoff_t rotate_size, rotate_min_size;
-       unsigned int rotate_days;
-       unsigned int max_open_files;
-       unsigned int purge_min_percentage;
 +      /* paths for storage directories */
 +      const char *storage_dir, *alt_storage_dir;
 +      struct dbox_map *map;
 +
 +      /* mode/gid to use for new dbox storage files */
 +      mode_t create_mode;
 +      gid_t create_gid;
 +
 +      ARRAY_DEFINE(open_files, struct dbox_file *);
 +
 +      unsigned int sync_rebuild:1;
 +      unsigned int have_multi_msgs:1;
  };
  
  struct dbox_mail_index_record {
index 109a9efb74f94c906fb6d64e9402fa4366e82e3f,0e421ea15be6f48af2d3a4878bb49ed364c72f17..dacc980e5bea8b38311c57739d3af3299f414eca
@@@ -297,155 -336,64 +297,155 @@@ static int dbox_sync_index_rebuild_dir(
        return ret;
  }
  
 -static int dbox_sync_new_maildir(struct dbox_sync_rebuild_context *ctx)
 +static int dbox_sync_maildir_finish(struct dbox_sync_rebuild_context *ctx)
  {
 +      struct dbox_mailbox *mbox = ctx->mbox;
 +      struct maildir_uidlist_iter_ctx *iter;
        struct mail_index_view *trans_view;
 -      char *const *fnames;
 -      unsigned int i, count;
 +      struct dbox_file *file;
 +      const char *fname;
 +      enum maildir_uidlist_rec_flag flags;
 +      uint32_t uid, next_uid;
        int ret = 0;
  
 -      fnames = array_get(&ctx->maildir_new_files, &count);
 -      if (count == 0)
 +      if (ctx->maildir_sync_ctx == NULL)
                return 0;
  
 +      /* we'll need the uidlist to contain the latest filenames.
 +         since there's no easy way to figure out if they changed, just
 +         recreate the uidlist always. */
 +      maildir_uidlist_sync_recreate(ctx->maildir_sync_ctx);
 +
 +      /* update the maildir uidlist's next_uid if we have seen higher
 +         dbox UIDs */
        trans_view = mail_index_transaction_open_updated_view(ctx->trans);
 -      ctx->maildir_new_uid = mail_index_get_header(trans_view)->next_uid;
 +      next_uid = mail_index_get_header(trans_view)->next_uid;
        mail_index_view_close(&trans_view);
 -
 -      for (i = 0; i < count && ret == 0; i++) {
 -              T_BEGIN {
 -                      ret = dbox_sync_index_maildir_file(ctx, fnames[i]);
 -              } T_END;
 +      maildir_uidlist_set_next_uid(mbox->maildir_uidlist, next_uid, FALSE);
 +      maildir_uidlist_set_next_uid(mbox->maildir_uidlist,
 +                                   ctx->highest_uid + 1, FALSE);
 +      /* assign UIDs for new maildir mails before iterating */
 +      maildir_uidlist_sync_finish(ctx->maildir_sync_ctx);
 +
 +      mbox->highest_maildir_uid =
 +              maildir_uidlist_get_next_uid(mbox->maildir_uidlist);
 +
 +      iter = maildir_uidlist_iter_init(mbox->maildir_uidlist);
 +      while (maildir_uidlist_iter_next(iter, &uid, &flags, &fname)) {
 +              file = dbox_file_init_single(mbox, uid);
 +              file->current_path =
 +                      i_strdup_printf("%s/%s", ctx->mbox->path, fname);
 +
 +              ret = dbox_sync_add_file_index(ctx, file);
 +              dbox_file_unref(&file);
 +              if (ret < 0)
 +                      break;
        }
 -      return ret;
 +      maildir_uidlist_iter_deinit(&iter);
 +      return ret < 0 ? -1 : 0;
  }
  
 -static int dbox_sync_index_rebuild_ctx(struct dbox_sync_rebuild_context *ctx)
 +static void dbox_sync_update_header(struct dbox_sync_rebuild_context *ctx)
  {
 -      if (dbox_sync_set_uidvalidity(ctx) < 0)
 -              return -1;
 +      const struct dbox_index_header *hdr;
 +      struct dbox_index_header new_hdr;
 +      const void *data;
 +      size_t data_size;
 +
 +      mail_index_get_header_ext(ctx->mbox->ibox.view,
 +                                ctx->mbox->dbox_hdr_ext_id,
 +                                &data, &data_size);
 +      hdr = data;
 +      if (data_size == sizeof(*hdr))
 +              new_hdr = *hdr;
 +      else
 +              memset(&new_hdr, 0, sizeof(new_hdr));
 +      if (new_hdr.highest_maildir_uid < ctx->mbox->highest_maildir_uid)
 +              new_hdr.highest_maildir_uid = ctx->mbox->highest_maildir_uid;
 +      new_hdr.map_uid_validity = !ctx->storage_rebuild ? 0 :
 +              dbox_map_get_uid_validity(ctx->mbox->storage->map);
 +      mail_index_update_header_ext(ctx->trans, ctx->mbox->dbox_hdr_ext_id, 0,
 +                                   &new_hdr, sizeof(new_hdr));
 +}
  
 -      if (dbox_sync_index_rebuild_dir(ctx, ctx->mbox->path, TRUE) < 0)
 -              return -1;
 +struct dbox_sync_rebuild_context *
 +dbox_sync_index_rebuild_init(struct dbox_mailbox *mbox,
 +                           struct mail_index_view *view,
 +                           struct mail_index_transaction *trans,
 +                           bool storage_rebuild)
 +{
 +      struct mailbox *box = &mbox->ibox.box;
 +      struct dbox_sync_rebuild_context *ctx;
 +      const char *index_dir;
 +      enum mail_index_open_flags open_flags = MAIL_INDEX_OPEN_FLAG_READONLY;
 +
 +      ctx = i_new(struct dbox_sync_rebuild_context, 1);
 +      ctx->mbox = mbox;
 +      ctx->view = view;
 +      ctx->trans = trans;
 +      ctx->storage_rebuild = storage_rebuild;
 +      mail_index_reset(ctx->trans);
 +      index_mailbox_reset_uidvalidity(&mbox->ibox);
 +      mail_index_ext_lookup(mbox->ibox.index, "cache", &ctx->cache_ext_id);
 +
 +      /* if backup index file exists, try to use it */
 +      index_dir = mailbox_list_get_path(box->storage->list, box->name,
 +                                        MAILBOX_LIST_PATH_TYPE_INDEX);
 +      ctx->backup_index =
 +              mail_index_alloc(index_dir, DBOX_INDEX_PREFIX".backup");
 +
 +#ifndef MMAP_CONFLICTS_WRITE
-       if ((box->storage->flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0)
++      if (box->storage->set->mmap_disable)
 +#endif
 +              open_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
 +      if (mail_index_open(ctx->backup_index, open_flags,
 +                          box->storage->lock_method) <= 0)
 +              mail_index_free(&ctx->backup_index);
 +      else
 +              ctx->backup_view = mail_index_view_open(ctx->backup_index);
 +      return ctx;
 +}
 +
 +int dbox_sync_index_rebuild_singles(struct dbox_sync_rebuild_context *ctx)
 +{
 +      int ret = 0;
  
 -      if (ctx->mbox->alt_path != NULL) {
 +      dbox_sync_set_uidvalidity(ctx);
 +      if (dbox_sync_index_rebuild_dir(ctx, ctx->mbox->path, TRUE) < 0)
 +              ret = -1;
 +      else if (ctx->mbox->alt_path != NULL) {
                if (dbox_sync_index_rebuild_dir(ctx, ctx->mbox->alt_path,
                                                FALSE) < 0)
 -                      return -1;
 +                      ret = -1;
 +      }
 +
 +      if (ret == 0) {
 +              if (dbox_sync_maildir_finish(ctx) < 0)
 +                      ret = -1;
        }
  
 -      /* finally give UIDs to newly seen maildir files */
 -      return dbox_sync_new_maildir(ctx);
 +      if (ctx->maildir_sync_ctx != NULL) {
 +              if (maildir_uidlist_sync_deinit(&ctx->maildir_sync_ctx) < 0)
 +                      ret = -1;
 +      }
 +      if (ctx->maildir_sync_keywords != NULL)
 +              maildir_keywords_sync_deinit(&ctx->maildir_sync_keywords);
 +      if (ctx->mk != NULL)
 +              maildir_keywords_deinit(&ctx->mk);
 +      return ret;
  }
  
 -static void dbox_sync_update_maildir_ids(struct dbox_sync_rebuild_context *ctx)
 +void dbox_sync_index_rebuild_deinit(struct dbox_sync_rebuild_context **_ctx)
  {
 -      struct dbox_mail_index_record rec;
 -      struct dbox_file *const *files;
 -      unsigned int i, count;
 -
 -      memset(&rec, 0, sizeof(rec));
 -      files = array_get(&ctx->mbox->open_files, &count);
 -      for (i = 0; i < count; i++) {
 -              if (!files[i]->maildir_file)
 -                      continue;
 -
 -              i_assert(files[i]->file_id != 0);
 -              rec.file_id = files[i]->file_id;
 -              mail_index_update_ext(ctx->trans, files[i]->maildir_append_seq,
 -                                    ctx->mbox->dbox_ext_id, &rec, NULL);
 +      struct dbox_sync_rebuild_context *ctx = *_ctx;
 +
 +      *_ctx = NULL;
 +      if (ctx->backup_index != NULL) {
 +              mail_index_view_close(&ctx->backup_view);
 +              mail_index_free(&ctx->backup_index);
        }
 +      dbox_sync_update_header(ctx);
 +      i_free(ctx);
  }
  
  int dbox_sync_index_rebuild(struct dbox_mailbox *mbox)
Simple merge
index 1f2050be5e15f71c36d47c92a7d411cfb9b50242,6b11fa8e9cbce0ec0e51315abd7bae2ffbcdd310..1b62cada54c0ce1be711847eeeb832e868061816
@@@ -387,15 -387,9 +357,13 @@@ void index_storage_mailbox_open(struct 
  
        i_assert(!ibox->box.opened);
  
-       index_flags = index_storage_get_index_open_flags(storage);
+       index_flags = mail_storage_settings_to_index_flags(storage->set);
        if (!ibox->move_to_memory)
                index_flags |= MAIL_INDEX_OPEN_FLAG_CREATE;
-       if ((index_flags & MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE) != 0)
-               ibox->fsync_disable = TRUE;
 +      if (ibox->keep_index_backups)
 +              index_flags |= MAIL_INDEX_OPEN_FLAG_KEEP_BACKUPS;
 +      if (ibox->index_never_in_memory)
 +              index_flags |= MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY;
  
        ret = mail_index_open(ibox->index, index_flags, storage->lock_method);
        if (ret <= 0 || ibox->move_to_memory) {
index 25c7022cf53aee1f1b3ab10e91585312df794e18,3faf74d4cf1c06e7f6ff9bd030d443e5fd2931bf..1b13583b5e6e7caa21cb8740b04e529ea2d6c666
@@@ -63,9 -62,7 +62,8 @@@ struct index_mailbox 
        unsigned int sent_readonly_flags_warning:1;
        unsigned int notify_pending:1;
        unsigned int move_to_memory:1;
--      unsigned int fsync_disable:1;
 +      unsigned int keep_index_backups:1;
 +      unsigned int index_never_in_memory:1;
  };
  
  struct index_transaction_context {
index 3f18ca8dd331a40df8437d23c3ef5f4599a47bca,050abb81819a8c235bccc6278e5beac0532d0f71..56ac217c063559d895904711171ae6b3afdc335a
@@@ -263,18 -268,17 +263,18 @@@ int maildir_copy(struct mail_save_conte
        struct maildir_mailbox *mbox = (struct maildir_mailbox *)t->ictx.ibox;
        int ret;
  
-       if (mbox->storage->copy_with_hardlinks &&
+       if (mbox->storage->set->maildir_copy_with_hardlinks &&
            maildir_compatible_file_modes(&mbox->ibox.box, mail->box)) {
                T_BEGIN {
 -                      ret = maildir_copy_hardlink(t, mail, flags,
 -                                                  keywords, dest_mail);
 +                      ret = maildir_copy_hardlink(t, mail, ctx->flags,
 +                                                  ctx->keywords,
 +                                                  ctx->dest_mail);
                } T_END;
  
 -              if (ret > 0)
 -                      return 0;
 -              if (ret < 0)
 -                      return -1;
 +              if (ret != 0) {
 +                      index_save_context_free(ctx);
 +                      return ret > 0 ? 0 : -1;
 +              }
  
                /* non-fatal hardlinking failure, try the slow way */
        }
index 1a754f4b619b725bd7d1bcbce144893f41f381a7,98b414b2c80066c953aac3084bf4fa0288d5260d..e5a76bade1787ea7ac26314772d665943baa5c3e
@@@ -490,10 -490,10 +489,10 @@@ static int maildir_save_finish_real(str
                                  &ctx->file_last->vsize) < 0)
                ctx->file_last->vsize = (uoff_t)-1;
  
 -      output_errno = ctx->output->stream_errno;
 -      o_stream_destroy(&ctx->output);
 +      output_errno = _ctx->output->stream_errno;
 +      o_stream_destroy(&_ctx->output);
  
--      if (!ctx->mbox->ibox.fsync_disable && !ctx->failed) {
++      if (!storage->set->fsync_disable && !ctx->failed) {
                if (fsync(ctx->fd) < 0) {
                        if (!mail_storage_set_error_from_errno(storage)) {
                                mail_storage_set_critical(storage,
@@@ -584,7 -584,7 +583,7 @@@ static int maildir_transaction_fsync_di
  {
        struct mail_storage *storage = &ctx->mbox->storage->storage;
  
--      if (ctx->mbox->ibox.fsync_disable)
++      if (storage->set->fsync_disable)
                return 0;
  
        if (new_changed) {
index 0000000000000000000000000000000000000000,01cbe9af80865dcbca705adb9542c4d2e0b00bdc..b08db7db12a85e5db1a685d1dbf31e822fb4f6d6
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,44 +1,46 @@@
 -      MEMBER(maildir_copy_preserve_filename) FALSE
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+ #include "lib.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "maildir-settings.h"
+ #include <stddef.h>
+ #undef DEF
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct maildir_settings, name), NULL }
+ static struct setting_define maildir_setting_defines[] = {
+       DEF(SET_BOOL, maildir_stat_dirs),
+       DEF(SET_BOOL, maildir_copy_with_hardlinks),
+       DEF(SET_BOOL, maildir_copy_preserve_filename),
++      DEF(SET_BOOL, maildir_very_dirty_syncs),
+       SETTING_DEFINE_LIST_END
+ };
+ static struct maildir_settings maildir_default_settings = {
+       MEMBER(maildir_stat_dirs) FALSE,
+       MEMBER(maildir_copy_with_hardlinks) TRUE,
++      MEMBER(maildir_copy_preserve_filename) FALSE,
++      MEMBER(maildir_very_dirty_syncs) FALSE
+ };
+ static struct setting_parser_info maildir_setting_parser_info = {
+       MEMBER(defines) maildir_setting_defines,
+       MEMBER(defaults) &maildir_default_settings,
+       MEMBER(parent) &mail_user_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct maildir_settings)
+ };
+ const struct setting_parser_info *maildir_get_setting_parser_info(void)
+ {
+       return &maildir_setting_parser_info;
+ }
index 0000000000000000000000000000000000000000,37355a7cd064d7dc1b6c3100404969c4a1809f13..e74f0615f5be74e32508207c78477af1738fee98
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,12 +1,13 @@@
+ #ifndef MAILDIR_SETTINGS_H
+ #define MAILDIR_SETTINGS_H
+ struct maildir_settings {
+       bool maildir_stat_dirs;
+       bool maildir_copy_with_hardlinks;
+       bool maildir_copy_preserve_filename;
++      bool maildir_very_dirty_syncs;
+ };
+ const struct setting_parser_info *maildir_get_setting_parser_info(void);
+ #endif
index aa67b8aa14e0a74be9764dfd278f86c0d734d43c,011ae3ef09525d50b46410ac71755c5b90278890..35b7094e2f27f84e97e3f34afc7df8b16d72d17e
@@@ -248,30 -250,15 +250,23 @@@ maildir_create(struct mail_storage *_st
                                storage, &storage->list_module_ctx);
  
        /* finish list init after we've overridden vfuncs */
-       mailbox_list_init(list, _storage->ns, &list_set,
-                         mail_storage_get_list_flags(flags));
-       storage->copy_with_hardlinks =
-               getenv("MAILDIR_COPY_WITH_HARDLINKS") != NULL;
-       storage->copy_preserve_filename =
-               getenv("MAILDIR_COPY_PRESERVE_FILENAME") != NULL;
-       storage->stat_dirs = getenv("MAILDIR_STAT_DIRS") != NULL;
+       mailbox_list_init(list, _storage->ns, &list_set, 0);
  
        storage->temp_prefix = mailbox_list_get_temp_prefix(list);
 -      if (list_set.control_dir == NULL) {
 +      if (list_set.control_dir == NULL && list_set.inbox_path == NULL &&
 +          (_storage->ns->flags & NAMESPACE_FLAG_INBOX) != 0) {
                /* put the temp files into tmp/ directory preferrably */
 -              storage->temp_prefix =
 -                      p_strconcat(_storage->pool,
 -                                  "tmp/", storage->temp_prefix, NULL);
 +              storage->temp_prefix = p_strconcat(_storage->pool, "tmp/",
 +                                                 storage->temp_prefix, NULL);
 +              dir = mailbox_list_get_path(list, NULL,
 +                                          MAILBOX_LIST_PATH_TYPE_DIR);
 +      } else {
 +              /* control dir should also be writable */
 +              dir = mailbox_list_get_path(list, NULL,
 +                                          MAILBOX_LIST_PATH_TYPE_CONTROL);
        }
 +      _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/",
 +                                               storage->temp_prefix, NULL);
        return 0;
  }
  
index 756f31dedcbacde1894877d63e1fd593436d56bf,2999619a38fea913692e6c03577411885796add7..e197050d8fdcb9731446f2c5401c03bf63c06566
@@@ -577,7 -558,7 +577,8 @@@ static int maildir_sync_quick_check(str
  
        /* try to avoid stat()ing by first checking delayed changes */
        if (DIR_DELAYED_REFRESH(hdr, new) ||
-           (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)) {
 -          DIR_DELAYED_REFRESH(hdr, cur)) {
++          (DIR_DELAYED_REFRESH(hdr, cur) &&
++           !mbox->storage->set->maildir_very_dirty_syncs)) {
                /* refresh index and try again */
                if (maildir_sync_header_refresh(mbox) < 0)
                        return -1;
  
                if (DIR_DELAYED_REFRESH(hdr, new))
                        *new_changed_r = TRUE;
-               if (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)
 -              if (DIR_DELAYED_REFRESH(hdr, cur))
++              if (DIR_DELAYED_REFRESH(hdr, cur) &&
++                  !mbox->storage->set->maildir_very_dirty_syncs)
                        *cur_changed_r = TRUE;
                if (*new_changed_r && *cur_changed_r)
                        return 0;
@@@ -914,32 -895,6 +916,32 @@@ maildir_storage_sync_init(struct mailbo
                }
        }
  
-       if (mbox->very_dirty_syncs) {
++      if (mbox->storage->set->maildir_very_dirty_syncs) {
 +              struct mail_index_view_sync_ctx *sync_ctx;
 +              bool b;
 +
 +              if (mbox->flags_view == NULL) {
 +                      mbox->flags_view =
 +                              mail_index_view_open(mbox->ibox.index);
 +              }
 +              sync_ctx = mail_index_view_sync_begin(mbox->flags_view,
 +                              MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
 +              if (mail_index_view_sync_commit(&sync_ctx, &b) < 0) {
 +                      mail_storage_set_index_error(&mbox->ibox);
 +                      ret = -1;
 +              }
 +              /* make sure the map stays in private memory */
 +              if (mbox->flags_view->map->refcount > 1) {
 +                      struct mail_index_map *map;
 +
 +                      map = mail_index_map_clone(mbox->flags_view->map);
 +                      mail_index_unmap(&mbox->flags_view->map);
 +                      mbox->flags_view->map = map;
 +              }
 +              mail_index_record_map_move_to_private(mbox->flags_view->map);
 +              mail_index_map_move_to_memory(mbox->flags_view->map);
 +              maildir_uidlist_set_all_nonsynced(mbox->uidlist);
 +      }
        return index_mailbox_sync_init(box, flags, ret < 0);
  }
  
index eb74a686378412e14b3f924b885f1cad2ea4bcf6,0f1ea95f2f37eb8cde4dfb11b5c43efad64c9091..b8bddef275d5937dddd98f555912f61d3cd5eea3
@@@ -1092,7 -1073,7 +1091,7 @@@ static int maildir_uidlist_write_fd(str
                return -1;
        }
  
--      if (!uidlist->ibox->fsync_disable) {
++      if (!storage->set->fsync_disable) {
                if (fdatasync(fd) < 0) {
                        mail_storage_set_critical(storage,
                                "fdatasync(%s) failed: %m", path);
@@@ -1455,16 -1431,10 +1454,15 @@@ int maildir_uidlist_sync_next(struct ma
  
        if (ctx->failed)
                return -1;
        for (p = filename; *p != '\0'; p++) {
                if (*p == 13 || *p == 10) {
 +                      struct mailbox *box = &uidlist->ibox->box;
 +
 +                      dir = mailbox_list_get_path(box->storage->list,
 +                                              box->name,
 +                                              MAILBOX_LIST_PATH_TYPE_MAILBOX);
                        i_warning("Maildir %s: Ignoring a file with #0x%x: %s",
 -                                uidlist->mbox->path, *p, filename);
 +                                dir, *p, filename);
                        return 1;
                }
        }
index 82a3b69ef0b7875741bebc0b972354b1a5b06480,cceefeeae4fd9e7a12ad656670f1a493b98594e3..f98049c90688e57de368ef93c59793cba16a9778
@@@ -741,7 -739,7 +739,8 @@@ int mbox_transaction_save_commit(struc
        }
  
        if (!ctx->synced && mbox->mbox_fd != -1 &&
--          !mbox->mbox_writeonly && !mbox->ibox.fsync_disable) {
++          !mbox->mbox_writeonly &&
++          !mbox->storage->storage.set->fsync_disable) {
                if (fdatasync(mbox->mbox_fd) < 0) {
                        mbox_set_syscall_error(mbox, "fdatasync()");
                        ret = -1;
index ecd05613b9ac158afb8b1c756fb0ccb5d66e5ff4,9dfd875544d3014fc6930417d991036a2315ec35..ece0237fe7429a986ece09c41d7df47d3a0f4d21
@@@ -471,18 -477,7 +480,17 @@@ static int mbox_create(struct mail_stor
  
        /* finish list init after we've overridden vfuncs */
        mailbox_list_init(_storage->list, _storage->ns, &list_set,
-                         mail_storage_get_list_flags(_storage->flags) |
                          MAILBOX_LIST_FLAG_MAILBOX_FILES);
 +
 +      dir = mailbox_list_get_path(_storage->list, NULL,
 +                                  MAILBOX_LIST_PATH_TYPE_INDEX);
 +      if (*dir == '\0') {
 +              /* no index directory. just fallback to writing to root. */
 +              dir = mailbox_list_get_path(_storage->list, NULL,
 +                                          MAILBOX_LIST_PATH_TYPE_DIR);
 +      }
 +      _storage->temp_path_prefix = p_strconcat(_storage->pool, dir, "/",
 +              mailbox_list_get_temp_prefix(_storage->list), NULL);
        return 0;
  }
  
index 943e21d4088d59f60d2860bb309574df046dd811,2cc8dd800b65cf3d178907a6a4a08c0b42839b9d..612935ec81f2dce599204d9d088b1ec7c8446f51
@@@ -236,18 -238,26 +246,28 @@@ int shared_storage_get_namespace(struc
        ns->prefix = i_strdup(str_c(prefix));
        ns->owner = owner;
        ns->flags = NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN |
 -              NAMESPACE_FLAG_AUTOCREATED;
 +              NAMESPACE_FLAG_AUTOCREATED | NAMESPACE_FLAG_INBOX;
        ns->sep = _storage->ns->sep;
+       ns->mail_set = _storage->set;
  
        location = t_str_new(256);
        if (ret > 0)
                var_expand(location, storage->location, tab);
 -      else
 +      else {
                get_nonexisting_user_location(storage, userdomain, location);
-       if (mail_storage_create(ns, NULL, str_c(location), _storage->flags,
-                               _storage->lock_method, &error) < 0) {
 +              ns->flags |= NAMESPACE_FLAG_UNUSABLE;
 +      }
+       ns_set = p_new(user->pool, struct mail_namespace_settings, 1);
+       ns_set->type = "shared";
+       ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep);
+       ns_set->prefix = ns->prefix;
+       ns_set->location = p_strdup(user->pool, str_c(location));
+       ns_set->hidden = TRUE;
+       ns_set->list = "yes";
+       ns->set = ns_set;
+       if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) {
                mail_storage_set_critical(_storage, "Namespace '%s': %s",
                                          ns->prefix, error);
                mail_namespace_destroy(ns);
index 657f00b66518fb120b96a3128aaac44d33d3ec93,88c7950cfbf85cee04642354517a8f362136dffc..4d0681a89c368ffcb223d40e2b442c4547c8a13b
@@@ -456,19 -456,21 +456,20 @@@ static int index_mailbox_list_open_inde
  {
        struct index_mailbox_list *ilist = INDEX_LIST_CONTEXT(list);
        const char *path;
 -      enum mail_index_open_flags index_flags;
 +      enum mail_index_open_flags index_flags = 0;
-       enum mail_storage_flags storage_flags;
+       enum file_lock_method lock_method;
        int ret;
  
-       /* FIXME: a bit ugly way to get the flags, but this will do for now.. */
-       storage_flags = *list->set.mail_storage_flags;
- #ifndef MMAP_CONFLICTS_WRITE
-       if ((storage_flags & MAIL_STORAGE_FLAG_MMAP_DISABLE) != 0)
- #endif
-               index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
 -      index_flags = MAIL_INDEX_OPEN_FLAG_CREATE |
 -              mail_storage_settings_to_index_flags(list->mail_set);
++      index_flags = mail_storage_settings_to_index_flags(list->mail_set);
+       if (!file_lock_method_parse(list->mail_set->lock_method,
+                                   &lock_method)) {
+               i_error("Unknown lock_method: %s", list->mail_set->lock_method);
+               return -1;
+       }
  
 -      if (mail_index_open(ilist->mail_index, index_flags,
 -                          *list->set.lock_method) < 0) {
 +      if (mail_index_open_or_create(ilist->mail_index, index_flags,
 +                                    *list->set.lock_method) < 0) {
                if (mail_index_move_to_memory(ilist->mail_index) < 0) {
                        /* try opening once more. it should be created
                           directly into memory now. */
index 598ebf79b0f13628fd85149d4e74ef4525098cc1,c7c2c66046e7d9d46794832a1695f171ca98c39f..204c42b3345d793f1d482ac7f70ea0b602c26623
@@@ -121,18 -126,24 +126,23 @@@ namespace_add(struct mail_user *user
                driver = NULL;
        }
  
-       if (mail_storage_create(ns, driver, data, flags, lock_method,
-                               &error) < 0) {
-               i_error("Namespace '%s': %s", ns->prefix, error);
+       if (mail_storage_create(ns, driver, 0, &error) < 0) {
+               *error_r = t_strdup_printf("Namespace '%s': %s",
+                                          ns->prefix, error);
                mail_namespace_free(ns);
-               return NULL;
+               return -1;
        }
-       return ns;
+       if (ns_set->separator != NULL)
+               ns->sep = *ns_set->separator;
+       *ns_p = ns;
+       return 0;
  }
  
- static bool namespaces_check(struct mail_namespace *namespaces)
+ static bool
+ namespaces_check(struct mail_namespace *namespaces, const char **error_r)
  {
 -      struct mail_namespace *ns, *inbox_ns = NULL, *private_ns = NULL;
 -      unsigned int private_ns_count = 0;
 +      struct mail_namespace *ns, *inbox_ns = NULL;
        unsigned int subscriptions_count = 0;
        char list_sep = '\0';
  
        }
  
        if (inbox_ns == NULL) {
-               i_error("namespace configuration error: "
-                       "inbox=yes namespace missing");
 -              if (private_ns_count == 1) {
 -                      /* just one private namespace. we'll assume it's
 -                         the INBOX namespace. */
 -                      private_ns->flags |= NAMESPACE_FLAG_INBOX;
 -              } else {
+                       *error_r = "namespace configuration error: "
+                               "inbox=yes namespace missing";
 -                      return FALSE;
 -              }
 +              return FALSE;
        }
        if (list_sep == '\0') {
-               i_error("namespace configuration error: "
-                       "no list=yes namespaces");
+               *error_r = "namespace configuration error: "
+                       "no list=yes namespaces";
                return FALSE;
        }
        if (subscriptions_count == 0) {
index 8073aa33ceedec53015e427648496815be5e600a,ff2d02c196238588a3ac1b18b5134b4aa5a08de1..f57ba7e727dd1b9e5b1ed39ba4fd6d61c5aacd06
@@@ -62,10 -63,8 +65,10 @@@ struct mail_namespace 
  /* Called after namespaces has been created */
  extern void (*hook_mail_namespaces_created)(struct mail_namespace *namespaces);
  
- int mail_namespaces_init(struct mail_user *user);
+ int mail_namespaces_init(struct mail_user *user, const char **error_r);
  struct mail_namespace *mail_namespaces_init_empty(struct mail_user *user);
 +/* Deinitialize all namespaces. mail_user_deinit() calls this automatically
 +   for user's namespaces. */
  void mail_namespaces_deinit(struct mail_namespace **namespaces);
  
  /* Destroy a single namespace and remove it from user's namespaces list. */
index 50a6a50bb35e3689d6ceb04c75ed047b3327b155,aefd9a913f45297bae08c70bcaf444e4d93dfe6c..221934ab35a5e8f5e2de5de85f5ee6b34d238881
@@@ -61,7 -64,7 +64,8 @@@ struct mail_storage 
          const struct mail_storage *storage_class;
        struct mail_namespace *ns;
        struct mailbox_list *list;
 +      const char *temp_path_prefix;
+       const struct mail_storage_settings *set;
  
        enum mail_storage_flags flags;
        enum file_lock_method lock_method;
index 0000000000000000000000000000000000000000,8ad083592d6cefc9b51a8aa992155e18a40ee05f..34e04e545cecb4c1526f5510b83286a071cc913b
mode 000000,100644..100644
--- /dev/null
@@@ -1,0 -1,272 +1,274 @@@
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+ #include "lib.h"
+ #include "array.h"
+ #include "settings-parser.h"
+ #include "mail-index.h"
+ #include "mail-user.h"
+ #include "mail-namespace.h"
+ #include "mail-storage-private.h"
+ #include "mail-storage-settings.h"
+ #include <stddef.h>
+ static bool mail_storage_settings_check(void *_set, const char **error_r);
+ static bool namespace_settings_check(void *_set, const char **error_r);
+ #undef DEF
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct mail_storage_settings, name), NULL }
+ static struct setting_define mail_storage_setting_defines[] = {
+       DEF(SET_STR_VARS, mail_location),
+       DEF(SET_STR, mail_cache_fields),
+       DEF(SET_STR, mail_never_cache_fields),
+       DEF(SET_UINT, mail_cache_min_mail_count),
+       DEF(SET_UINT, mailbox_idle_check_interval),
+       DEF(SET_UINT, mail_max_keyword_length),
+       DEF(SET_BOOL, mail_save_crlf),
+       DEF(SET_BOOL, fsync_disable),
+       DEF(SET_BOOL, mmap_disable),
+       DEF(SET_BOOL, dotlock_use_excl),
+       DEF(SET_BOOL, mail_nfs_storage),
+       DEF(SET_BOOL, mail_nfs_index),
+       DEF(SET_BOOL, mailbox_list_index_disable),
+       DEF(SET_BOOL, mail_debug),
+       DEF(SET_BOOL, mail_full_filesystem_access),
+       DEF(SET_ENUM, lock_method),
+       DEF(SET_STR, pop3_uidl_format),
+       SETTING_DEFINE_LIST_END
+ };
+ struct mail_storage_settings mail_storage_default_settings = {
+       MEMBER(mail_location) "",
+       MEMBER(mail_cache_fields) "flags",
+       MEMBER(mail_never_cache_fields) "imap.envelope",
+       MEMBER(mail_cache_min_mail_count) 0,
+       MEMBER(mailbox_idle_check_interval) 30,
+       MEMBER(mail_max_keyword_length) 50,
+       MEMBER(mail_save_crlf) FALSE,
+       MEMBER(fsync_disable) FALSE,
+       MEMBER(mmap_disable) FALSE,
+       MEMBER(dotlock_use_excl) FALSE,
+       MEMBER(mail_nfs_storage) FALSE,
+       MEMBER(mail_nfs_index) FALSE,
+       MEMBER(mailbox_list_index_disable) FALSE,
+       MEMBER(mail_debug) FALSE,
+       MEMBER(mail_full_filesystem_access) FALSE,
+       MEMBER(lock_method) "fcntl:flock:dotlock",
+       MEMBER(pop3_uidl_format) "%08Xu%08Xv"
+ };
+ struct setting_parser_info mail_storage_setting_parser_info = {
+       MEMBER(defines) mail_storage_setting_defines,
+       MEMBER(defaults) &mail_storage_default_settings,
+       MEMBER(parent) &mail_user_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct mail_storage_settings),
+       MEMBER(check_func) mail_storage_settings_check
+ };
+ #undef DEF
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct mail_namespace_settings, name), NULL }
+ static struct setting_define mail_namespace_setting_defines[] = {
+       DEF(SET_ENUM, type),
+       DEF(SET_STR, separator),
+       DEF(SET_STR_VARS, prefix),
+       DEF(SET_STR_VARS, location),
+       DEF(SET_STR_VARS, alias_for),
+       DEF(SET_BOOL, inbox),
+       DEF(SET_BOOL, hidden),
+       DEF(SET_ENUM, list),
+       DEF(SET_BOOL, subscriptions),
+       SETTING_DEFINE_LIST_END
+ };
+ struct mail_namespace_settings mail_namespace_default_settings = {
+       MEMBER(type) "private:shared:public",
+       MEMBER(separator) "",
+       MEMBER(prefix) "",
+       MEMBER(location) "",
+       MEMBER(alias_for) NULL,
+       MEMBER(inbox) FALSE,
+       MEMBER(hidden) FALSE,
+       MEMBER(list) "yes:no:children",
+       MEMBER(subscriptions) TRUE
+ };
+ struct setting_parser_info mail_namespace_setting_parser_info = {
+       MEMBER(defines) mail_namespace_setting_defines,
+       MEMBER(defaults) &mail_namespace_default_settings,
+       MEMBER(parent) &mail_user_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) offsetof(struct mail_namespace_settings, user_set),
+       MEMBER(type_offset) offsetof(struct mail_namespace_settings, type),
+       MEMBER(struct_size) sizeof(struct mail_namespace_settings),
+       MEMBER(check_func) namespace_settings_check
+ };
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct mail_user_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+       { SET_DEFLIST, name, \
+         offsetof(struct mail_user_settings, field), defines }
+ static struct setting_define mail_user_setting_defines[] = {
+       DEF(SET_UINT, umask),
+       DEFLIST(namespaces, "namespace", &mail_namespace_setting_parser_info),
+       { SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL },
+       SETTING_DEFINE_LIST_END
+ };
+ static struct mail_user_settings mail_user_default_settings = {
+       MEMBER(umask) 0077,
+       MEMBER(namespaces) ARRAY_INIT,
+       MEMBER(plugin_envs) ARRAY_INIT
+ };
+ struct setting_parser_info mail_user_setting_parser_info = {
+       MEMBER(defines) mail_user_setting_defines,
+       MEMBER(defaults) &mail_user_default_settings,
+       MEMBER(parent) NULL,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct mail_user_settings)
+ };
+ const void *
+ mail_user_set_get_driver_settings(const struct mail_user_settings *set,
+                                 const char *driver)
+ {
+       const void *dset;
+       dset = settings_find_dynamic(&mail_user_setting_parser_info,
+                                    set, driver);
+       if (dset == NULL) {
+               i_panic("Default settings not found for storage driver %s",
+                       driver);
+       }
+       return dset;
+ }
+ const void *mail_storage_get_driver_settings(struct mail_storage *storage)
+ {
+       return mail_user_set_get_driver_settings(storage->ns->user->set,
+                                                storage->name);
+ }
+ enum mail_index_open_flags
+ mail_storage_settings_to_index_flags(const struct mail_storage_settings *set)
+ {
+       enum mail_index_open_flags index_flags = 0;
+       if (set->fsync_disable)
+               index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE;
+ #ifndef MMAP_CONFLICTS_WRITE
+       if (set->mmap_disable)
+ #endif
+               index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
+       if (set->dotlock_use_excl)
+               index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL;
++      if (set->mail_nfs_index)
++              index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH;
+       return index_flags;
+ }
+ void mail_storage_namespace_defines_init(pool_t pool)
+ {
+       struct dynamic_settings_parser *parsers;
+       struct mail_storage *const *storages;
+       unsigned int i, j, count;
+       storages = array_get(&mail_storage_classes, &count);
+       parsers = t_new(struct dynamic_settings_parser, count + 1);
+       parsers[0].name = "MAIL";
+       parsers[0].info = &mail_storage_setting_parser_info;
+       for (i = 0, j = 1; i < count; i++) {
+               if (storages[i]->v.get_setting_parser_info == NULL)
+                       continue;
+               parsers[j].name = storages[i]->name;
+               parsers[j].info = storages[i]->v.get_setting_parser_info();
+               j++;
+       }
+       settings_parser_info_update(pool, parsers[j-1].info->parent, parsers);
+ }
+ /* <settings checks> */
+ static bool mail_storage_settings_check(void *_set, const char **error_r)
+ {
+       const struct mail_storage_settings *set = _set;
+       if (set->mail_nfs_index && !set->mmap_disable) {
+               *error_r = "mail_nfs_index=yes requires mmap_disable=yes";
+               return FALSE;
+       }
+       if (set->mail_nfs_index && set->fsync_disable) {
+               *error_r = "mail_nfs_index=yes requires fsync_disable=no";
+               return FALSE;
+       }
+       return TRUE;
+ }
+ static bool namespace_settings_check(void *_set, const char **error_r)
+ {
+       struct mail_namespace_settings *ns = _set;
+       struct mail_namespace_settings *const *namespaces;
+       const char *name;
+       unsigned int i, count;
+       name = ns->prefix != NULL ? ns->prefix : "";
+       if (ns->separator != NULL &&
+           ns->separator[0] != '\0' && ns->separator[1] != '\0') {
+               *error_r = t_strdup_printf("Namespace '%s': "
+                       "Hierarchy separator must be only one character long",
+                       name);
+               return FALSE;
+       }
+       if (ns->alias_for != NULL) {
+               namespaces = array_get(&ns->user_set->namespaces, &count);
+               for (i = 0; i < count; i++) {
+                       if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0)
+                               break;
+               }
+               if (i == count) {
+                       *error_r = t_strdup_printf(
+                               "Namespace '%s': alias_for points to "
+                               "unknown namespace: %s", name, ns->alias_for);
+                       return FALSE;
+               }
+               if (namespaces[i]->alias_for != NULL) {
+                       *error_r = t_strdup_printf(
+                               "Namespace '%s': alias_for chaining isn't "
+                               "allowed: %s -> %s", name, ns->alias_for,
+                               namespaces[i]->alias_for);
+                       return FALSE;
+               }
+       }
+       return TRUE;
+ }
+ /* </settings checks> */
index b0ebdfffec3e38248830f2f497bb8bdebe6d629b,6d1c741d07a17e0097cb2c70d53b99280a0d727a..121e6358ffc66bcc06292c3c1443d5fa9a84aa5b
@@@ -158,15 -117,30 +117,30 @@@ mail_storage_set_autodetection(const ch
  }
  
  int mail_storage_create(struct mail_namespace *ns, const char *driver,
-                       const char *data, enum mail_storage_flags flags,
-                       enum file_lock_method lock_method,
-                       const char **error_r)
+                       enum mail_storage_flags flags, const char **error_r)
  {
 -      struct mail_storage *storage_class, *storage;
 +      struct mail_storage *storage_class, *storage = NULL;
        struct mail_storage *const *classes;
-       const char *home, *value;
+       const char *data = ns->set->location;
+       const char *home, *p;
        unsigned int i, count;
  
+       if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
+           ns->mail_set->pop3_uidl_format != NULL) {
+               /* if pop3_uidl_format contains %m, we want to keep the
+                  header MD5 sums stored even if we're not running POP3
+                  right now. */
+               p = ns->mail_set->pop3_uidl_format;
+               while ((p = strchr(p, '%')) != NULL) {
+                       if (p[1] == '%')
+                               p += 2;
+                       else if (var_get_key(++p) == 'm') {
+                               flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
+                               break;
+                       }
+               }
+       }
        if (data == NULL)
                data = "";
        else if (driver == NULL)
                return -1;
        }
  
-       value = getenv("MAIL_MAX_KEYWORD_LENGTH");
-       storage->keyword_max_len = value != NULL ?
-               atoi(value) : DEFAULT_MAX_KEYWORD_LENGTH;
 +
        if (hook_mail_storage_created != NULL) {
                T_BEGIN {
                        hook_mail_storage_created(storage);
@@@ -404,36 -385,6 +386,20 @@@ const char *mail_storage_get_mailbox_in
                                     MAILBOX_LIST_PATH_TYPE_INDEX);
  }
  
- enum mailbox_list_flags
- mail_storage_get_list_flags(enum mail_storage_flags storage_flags)
- {
-       enum mailbox_list_flags list_flags = 0;
-       if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
-               list_flags |= MAILBOX_LIST_FLAG_DEBUG;
-       if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
-               list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
-       if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0)
-               list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL;
-       if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
-               list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH;
-       return list_flags;
- }
 +const char *mail_storage_get_temp_prefix(struct mail_storage *storage)
 +{
 +      const char *dir;
 +
 +      if (storage->temp_path_prefix == NULL) {
 +              dir = mailbox_list_get_path(storage->list, NULL,
 +                                          MAILBOX_LIST_PATH_TYPE_DIR);
 +              storage->temp_path_prefix = p_strconcat(storage->pool, dir, "/",
 +                      mailbox_list_get_temp_prefix(storage->list), NULL);
 +      }
 +
 +      return storage->temp_path_prefix;
 +}
 +
  bool mail_storage_set_error_from_errno(struct mail_storage *storage)
  {
        const char *error_string;
Simple merge
index 84831aec5885d57f59711eb90c84752496f6be51,49315534653ed6a274d295d4a8c405ec6e5cfd9c..9a749ae0d94d8b588d3df2b6e5e5e9bf61052f6e
@@@ -3,9 -3,13 +3,14 @@@
  #include "lib.h"
  #include "array.h"
  #include "hostpid.h"
+ #include "network.h"
+ #include "str.h"
+ #include "var-expand.h"
+ #include "settings-parser.h"
  #include "auth-master.h"
+ #include "mail-storage-settings.h"
  #include "mail-namespace.h"
 +#include "mail-storage.h"
  #include "mail-user.h"
  
  #include <stdlib.h>
index 04c15a61b0f565d59d2329f237e318cbe6de52cc,db1eb3ac1db627d619108cfc7b08456f7775e73e..bfd97f72ecebb7825cf2b189e2f892ba957b72c3
@@@ -59,9 -86,8 +86,11 @@@ void mail_user_set_home(struct mail_use
     successfully, 0 if there is no home directory (either user doesn't exist or
     has no home directory) or -1 if lookup failed. */
  int mail_user_get_home(struct mail_user *user, const char **home_r);
 +/* Returns path + file prefix for creating a temporary file. Uses home
 +   directory if possible, fallbacks to mail directory. */
 +const char *mail_user_get_temp_prefix(struct mail_user *user);
+ /* If name exists in plugin_envs, return its value. */
+ const char *mail_user_plugin_getenv(struct mail_user *user, const char *name);
  
  /* Add more namespaces to user's namespaces. The ->next pointers may be
     changed, so the namespaces pointer will be updated to user->namespaces. */
index 252455c6a96165b605f29b140cbf98569784cf27,75d7b0ac9aef9dc3644b97db466ebfba59eb3e6a..aee4c1946e9db6cb399582c9350cef5e8641b5e4
@@@ -201,9 -192,9 +201,10 @@@ void mailbox_list_init(struct mailbox_l
                 *set->subscription_fname != '\0');
  
        list->ns = ns;
+       list->mail_set = ns->mail_set;
        list->flags = flags;
        list->file_create_mode = (mode_t)-1;
 +      list->dir_create_mode = (mode_t)-1;
        list->file_create_gid = (gid_t)-1;
  
        /* copy settings */
@@@ -283,16 -262,13 +284,22 @@@ mailbox_list_get_namespace(const struc
        return list->ns;
  }
  
 -void mailbox_list_get_permissions(struct mailbox_list *list,
 +static mode_t get_dir_mode(mode_t mode)
 +{
 +      /* add the execute bit if either read or write bit is set */
 +      if ((mode & 0600) != 0) mode |= 0100;
 +      if ((mode & 0060) != 0) mode |= 0010;
 +      if ((mode & 0006) != 0) mode |= 0001;
 +      return mode;
 +}
 +
+ struct mail_user *
+ mailbox_list_get_user(const struct mailbox_list *list)
+ {
+       return list->ns->user;
+ }
 +void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
                                  mode_t *mode_r, gid_t *gid_r)
  {
        const char *path;
                list->file_create_gid = st.st_gid;
        }
  
-       if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0 && name == NULL) {
 -      if (list->mail_set->mail_debug) {
++      if (list->mail_set->mail_debug && name == NULL) {
                i_info("Namespace %s: Using permissions from %s: "
                       "mode=0%o gid=%ld", list->ns->prefix, path,
 -                     (int)list->file_create_mode,
 +                     (int)list->dir_create_mode,
                       list->file_create_gid == (gid_t)-1 ? -1L :
                       (long)list->file_create_gid);
        }
index 23b1f348e864989021d2cfdaa61cedeeb4724be4,92a3181b231754d9a4f5ac581a1876ad26036f93..d6cf93a49113c3056c770b455cedbbaab6b4b5e9
@@@ -144,11 -133,13 +136,13 @@@ enum mailbox_list_flag
  mailbox_list_get_flags(const struct mailbox_list *list) ATTR_PURE;
  struct mail_namespace *
  mailbox_list_get_namespace(const struct mailbox_list *list) ATTR_PURE;
+ struct mail_user *
+ mailbox_list_get_user(const struct mailbox_list *list) ATTR_PURE;
  
 -/* Returns the mode and GID that should be used when creating new global files
 -   to the mailbox list root directories. (gid_t)-1 is returned if it's not
 -   necessary to change the default */
 -void mailbox_list_get_permissions(struct mailbox_list *list,
 +/* Returns the mode and GID that should be used when creating new files to
 +   the specified mailbox, or to mailbox list root if name is NULL. (gid_t)-1 is
 +   returned if it's not necessary to change the default gid. */
 +void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
                                  mode_t *mode_r, gid_t *gid_r);
  /* Like mailbox_list_get_permissions(), but add execute-bits for mode
     if either read or write bit is set (e.g. 0640 -> 0750). */
Simple merge
index a2d47b1dbd2aa8fd245952316b06a0866ea95fc4,bdd790d6661d00f5b4b7cdbb95d92b0cb3f8e84f..50dacf9fa18b8834e8385709b125ce131beb31c7
@@@ -602,11 -531,13 +535,9 @@@ static int create_auth_process(struct a
  
        env_put(t_strdup_printf("AUTH_WORKER_PATH=%s/auth-worker.%s",
                                *group->set->chroot != '\0' ? "" :
-                               group->set->parent->defaults->base_dir,
+                               group->master_set->base_dir,
                                dec2str(getpid())));
-       env_put(t_strdup_printf("AUTH_WORKER_MAX_COUNT=%u",
-                               group->set->worker_max_count));
  
 -      /* make sure we don't leak syslog fd, but do it last so that
 -         any errors above will be logged */
 -      closelog();
 -
        executable = group->set->executable;
        client_process_exec(executable, "");
        i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
@@@ -670,9 -597,13 +601,9 @@@ static int create_auth_worker(struct au
                fd_close_on_exec(i, FALSE);
        fd_close_on_exec(4, FALSE);
  
-       child_process_init_env();
-         auth_set_environment(process->group->set);
+       child_process_init_env(process->group->master_set);
+         auth_set_environment(process->group->master_set, process->group->set);
  
 -      /* make sure we don't leak syslog fd, but do it last so that
 -         any errors above will be logged */
 -      closelog();
 -
        executable = t_strconcat(process->group->set->executable, " -w", NULL);
        client_process_exec(executable, "");
        i_fatal_status(FATAL_EXEC, "execv(%s) failed: %m", executable);
index 138edfd628fa39149a5dd1c905ffbdd483f5afe3,e2092a50229bc474d19caecd533092378c71cc89..0851ee7b1a09a35713023d011f1b506b844f4d32
@@@ -2,6 -2,6 +2,7 @@@
  
  #include "common.h"
  #include "lib-signals.h"
++#include "array.h"
  #include "hash.h"
  #include "str.h"
  #include "env-util.h"
@@@ -127,69 -126,7 +127,70 @@@ static const char *get_exit_status_mess
        return NULL;
  }
  
 -static void sigchld_handler(int signo ATTR_UNUSED,
 +static void
 +log_coredump(string_t *str, enum process_type process_type, int status)
 +{
 +#ifdef WCOREDUMP
++      struct master_auth_settings *const *auth_set;
 +      int signum = WTERMSIG(status);
 +
 +      if (WCOREDUMP(status)) {
 +              str_append(str, " (core dumped)");
 +              return;
 +      }
 +
 +      if (signum != SIGABRT && signum != SIGSEGV && signum != SIGBUS)
 +              return;
 +
 +      /* let's try to figure out why we didn't get a core dump */
 +      if (core_dumps_disabled) {
 +              str_printfa(str, " (core dumps disabled)");
 +              return;
 +      }
 +
 +      switch (process_type) {
 +      case PROCESS_TYPE_LOGIN:
 +#ifdef HAVE_PR_SET_DUMPABLE
 +              str_append(str, " (core not dumped - add -D to login_executable)");
 +              return;
 +#else
 +              break;
 +#endif
 +      case PROCESS_TYPE_IMAP:
 +      case PROCESS_TYPE_POP3:
 +#ifndef HAVE_PR_SET_DUMPABLE
-               if (!settings_root->defaults->mail_drop_priv_before_exec) {
++              if (!master_set->defaults->mail_drop_priv_before_exec) {
 +                      str_append(str, " (core not dumped - set mail_drop_priv_before_exec=yes)");
 +                      return;
 +              }
-               if (*settings_root->defaults->mail_privileged_group != '\0') {
++              if (*master_set->defaults->mail_privileged_group != '\0') {
 +                      str_append(str, " (core not dumped - mail_privileged_group prevented it)");
 +                      return;
 +              }
 +#endif
 +              str_append(str, " (core not dumped - is home dir set?)");
 +              return;
 +      case PROCESS_TYPE_AUTH:
 +      case PROCESS_TYPE_AUTH_WORKER:
-               if (settings_root->auths->uid == 0)
++              auth_set = array_idx(&master_set->defaults->auths, 0);
++              if (auth_set[0]->uid == 0)
 +                      break;
 +#ifdef HAVE_PR_SET_DUMPABLE
 +              str_printfa(str, " (core not dumped - "
 +                          "no permissions for auth user %s in %s?)",
-                           settings_root->auths->user,
-                           settings_root->defaults->base_dir);
++                          auth_set[0]->user, master_set->defaults->base_dir);
 +#else
 +              str_append(str, " (core not dumped - auth user is not root)");
 +#endif
 +              return;
 +      default:
 +              break;
 +      }
 +      str_append(str, " (core not dumped)");
 +#endif
 +}
 +
 +static void sigchld_handler(const siginfo_t *si ATTR_UNUSED,
                            void *context ATTR_UNUSED)
  {
        struct child_process *process;
Simple merge
Simple merge
Simple merge
index d21ec38f140bf483d1966ca12877e1cd6f7d86a1,a5d985dab8750d176858811f0f95ea7278ba4e16..ed7e848d1d03337e949d9c9a8f784a882b511214
@@@ -206,239 -206,13 +207,17 @@@ get_var_expand_table(const char *protoc
        return tab;
  }
  
- static bool
- has_missing_used_home(const char *str, const struct var_expand_table *table)
+ static void mail_process_set_environment(struct master_settings *set)
  {
-       i_assert(table[VAR_EXPAND_HOME_IDX].key == 'h');
+       /* we don't know all the settings, so since we can't expand all of
+          them just let the mail process expand all of them internally. */
+       master_settings_export_to_env(set);
  
-       return table[VAR_EXPAND_HOME_IDX].value == NULL &&
-               var_has_key(str, 'h', "home");
- }
- static const char *
- expand_mail_env(const char *env, const struct var_expand_table *table)
- {
-       string_t *str;
-       const char *p;
-       str = t_str_new(256);
-       /* it's either type:data or just data */
-       p = strchr(env, ':');
-       if (p != NULL) {
-               while (env != p) {
-                       str_append_c(str, *env);
-                       env++;
-               }
-               str_append_c(str, *env++);
-       }
-       if (has_missing_used_home(env, table)) {
-               i_fatal("userdb didn't return a home directory, "
-                       "but mail location used it (%%h): %s", env);
-       }
-       /* expand %vars */
-       var_expand(str, env, table);
-       return str_c(str);
- }
- static void
- env_put_namespace(struct namespace_settings *ns, const char *default_location,
-                 const struct var_expand_table *table)
- {
-       const char *location;
-       unsigned int i;
-       string_t *str;
-       if (default_location == NULL)
-               default_location = "";
-       for (i = 1; ns != NULL; i++, ns = ns->next) {
-               location = *ns->location != '\0' ? ns->location :
-                       default_location;
-               location = expand_mail_env(location, table);
-               env_put(t_strdup_printf("NAMESPACE_%u=%s", i, location));
-               if (ns->separator != NULL) {
-                       env_put(t_strdup_printf("NAMESPACE_%u_SEP=%s",
-                                               i, ns->separator));
-               }
-               if (ns->type != NULL) {
-                       env_put(t_strdup_printf("NAMESPACE_%u_TYPE=%s",
-                                               i, ns->type));
-               }
-               if (ns->alias_for != NULL) {
-                       env_put(t_strdup_printf("NAMESPACE_%u_ALIAS=%s",
-                                               i, ns->alias_for));
-               }
-               if (ns->prefix != NULL) {
-                       /* expand variables, eg. ~%u/ can be useful */
-                       str = t_str_new(256);
-                       str_printfa(str, "NAMESPACE_%u_PREFIX=", i);
-                       var_expand(str, ns->prefix, table);
-                       env_put(str_c(str));
-               }
-               if (ns->inbox)
-                       env_put(t_strdup_printf("NAMESPACE_%u_INBOX=1", i));
-               if (ns->hidden)
-                       env_put(t_strdup_printf("NAMESPACE_%u_HIDDEN=1", i));
-               if (strcmp(ns->list, "no") != 0) {
-                       env_put(t_strdup_printf("NAMESPACE_%u_LIST=%s",
-                                               i, ns->list));
-               }
-               if (ns->subscriptions)
-                       env_put(t_strdup_printf("NAMESPACE_%u_SUBSCRIPTIONS=1",
-                                               i));
-       }
- }
- static void
- mail_process_set_environment(struct settings *set, const char *mail,
-                            const struct var_expand_table *var_expand_table,
-                            bool exec_mail)
- {
-       const char *const *envs;
-       string_t *str;
-       unsigned int i, count;
-       env_put(t_strconcat("MAIL_CACHE_FIELDS=",
-                           set->mail_cache_fields, NULL));
-       env_put(t_strconcat("MAIL_NEVER_CACHE_FIELDS=",
-                           set->mail_never_cache_fields, NULL));
-       env_put(t_strdup_printf("MAIL_CACHE_MIN_MAIL_COUNT=%u",
-                               set->mail_cache_min_mail_count));
-       env_put(t_strdup_printf("MAILBOX_IDLE_CHECK_INTERVAL=%u",
-                               set->mailbox_idle_check_interval));
-       env_put(t_strdup_printf("MAIL_MAX_KEYWORD_LENGTH=%u",
-                               set->mail_max_keyword_length));
-       if (set->protocol == MAIL_PROTOCOL_IMAP) {
-               env_put(t_strdup_printf("IMAP_MAX_LINE_LENGTH=%u",
-                                       set->imap_max_line_length));
-               if (*set->imap_capability != '\0') {
-                       env_put(t_strconcat("IMAP_CAPABILITY=",
-                                           set->imap_capability, NULL));
-               }
-               env_put(t_strconcat("IMAP_CLIENT_WORKAROUNDS=",
-                                   set->imap_client_workarounds, NULL));
-               env_put(t_strconcat("IMAP_LOGOUT_FORMAT=",
-                                   set->imap_logout_format, NULL));
-               env_put(t_strconcat("IMAP_ID_SEND=", set->imap_id_send, NULL));
-               env_put(t_strconcat("IMAP_ID_LOG=", set->imap_id_log, NULL));
-       }
-       if (set->protocol == MAIL_PROTOCOL_POP3) {
-               env_put(t_strconcat("POP3_CLIENT_WORKAROUNDS=",
-                                   set->pop3_client_workarounds, NULL));
-               env_put(t_strconcat("POP3_LOGOUT_FORMAT=",
-                                   set->pop3_logout_format, NULL));
-               if (set->pop3_no_flag_updates)
-                       env_put("POP3_NO_FLAG_UPDATES=1");
-               if (set->pop3_reuse_xuidl)
-                       env_put("POP3_REUSE_XUIDL=1");
-               if (set->pop3_enable_last)
-                       env_put("POP3_ENABLE_LAST=1");
-               if (set->pop3_lock_session)
-                       env_put("POP3_LOCK_SESSION=1");
-       }
-       /* We care about POP3 UIDL format in all process types */
-       env_put(t_strconcat("POP3_UIDL_FORMAT=", set->pop3_uidl_format, NULL));
-       if (set->mail_save_crlf)
-               env_put("MAIL_SAVE_CRLF=1");
-       if (set->mmap_disable)
-               env_put("MMAP_DISABLE=1");
-       if (set->dotlock_use_excl)
-               env_put("DOTLOCK_USE_EXCL=1");
-       if (set->fsync_disable)
-               env_put("FSYNC_DISABLE=1");
-       if (set->mail_nfs_storage)
-               env_put("MAIL_NFS_STORAGE=1");
-       if (set->mail_nfs_index)
-               env_put("MAIL_NFS_INDEX=1");
-       if (set->mailbox_list_index_disable)
-               env_put("MAILBOX_LIST_INDEX_DISABLE=1");
-       if (set->maildir_stat_dirs)
-               env_put("MAILDIR_STAT_DIRS=1");
-       if (set->maildir_copy_with_hardlinks)
-               env_put("MAILDIR_COPY_WITH_HARDLINKS=1");
-       if (set->maildir_copy_preserve_filename)
-               env_put("MAILDIR_COPY_PRESERVE_FILENAME=1");
 -      (void)umask(set->umask);
 +      if (set->maildir_very_dirty_syncs)
 +              env_put("MAILDIR_VERY_DIRTY_SYNCS=1");
-       if (set->mail_debug)
-               env_put("DEBUG=1");
-       if (set->mail_full_filesystem_access)
-               env_put("FULL_FILESYSTEM_ACCESS=1");
-       if (set->mbox_dirty_syncs)
-               env_put("MBOX_DIRTY_SYNCS=1");
-       if (set->mbox_very_dirty_syncs)
-               env_put("MBOX_VERY_DIRTY_SYNCS=1");
-       if (set->mbox_lazy_writes)
-               env_put("MBOX_LAZY_WRITES=1");
-       /* when we're not certain that the log fd points to the master
-          process's log pipe (dump-capability, --exec-mail), don't let
-          the imap process listen for stderr since it might break
-          (e.g. epoll_ctl() gives EPERM). */
-       if (set->shutdown_clients && !exec_mail)
-               env_put("STDERR_CLOSE_SHUTDOWN=1");
 +      (void)umask(0077);
-       env_put(t_strconcat("LOCK_METHOD=", set->lock_method, NULL));
-       env_put(t_strconcat("MBOX_READ_LOCKS=", set->mbox_read_locks, NULL));
-       env_put(t_strconcat("MBOX_WRITE_LOCKS=", set->mbox_write_locks, NULL));
-       env_put(t_strdup_printf("MBOX_LOCK_TIMEOUT=%u",
-                               set->mbox_lock_timeout));
-       env_put(t_strdup_printf("MBOX_DOTLOCK_CHANGE_TIMEOUT=%u",
-                               set->mbox_dotlock_change_timeout));
-       env_put(t_strdup_printf("MBOX_MIN_INDEX_SIZE=%u",
-                               set->mbox_min_index_size));
-       env_put(t_strdup_printf("DBOX_ROTATE_SIZE=%u",
-                               set->dbox_rotate_size));
-       env_put(t_strdup_printf("DBOX_ROTATE_MIN_SIZE=%u",
-                               set->dbox_rotate_min_size));
-       env_put(t_strdup_printf("DBOX_ROTATE_DAYS=%u",
-                               set->dbox_rotate_days));
 +      env_put(t_strdup_printf("DBOX_PURGE_MIN_PERCENTAGE=%u",
 +                              set->dbox_purge_min_percentage));
-       if (*set->mail_plugins != '\0') {
-               env_put(t_strconcat("MAIL_PLUGIN_DIR=",
-                                   set->mail_plugin_dir, NULL));
-               env_put(t_strconcat("MAIL_PLUGINS=", set->mail_plugins, NULL));
-       }
-       /* user given environment - may be malicious. virtual_user comes from
-          auth process, but don't trust that too much either. Some auth
-          mechanism might allow leaving extra data there. */
-       if ((mail == NULL || *mail == '\0') && *set->mail_location != '\0')
-               mail = expand_mail_env(set->mail_location, var_expand_table);
-       env_put(t_strconcat("MAIL=", mail, NULL));
-       if (set->server->namespaces != NULL) {
-               env_put_namespace(set->server->namespaces,
-                                 mail, var_expand_table);
-       }
-       str = t_str_new(256);
-       envs = array_get(&set->plugin_envs, &count);
-       i_assert((count % 2) == 0);
-       for (i = 0; i < count; i += 2) {
-               str_truncate(str, 0);
-               var_expand(str, envs[i+1], var_expand_table);
-               if (has_missing_used_home(envs[i+1], var_expand_table)) {
-                       i_error("userdb didn't return a home directory, "
-                               "but it's used in plugin setting %s: %s",
-                               envs[i], envs[i+1]);
-               }
-               env_put(t_strconcat(t_str_ucase(envs[i]), "=",
-                                   str_c(str), NULL));
-       }
  }
  
  void mail_process_exec(const char *protocol, const char **args)
@@@ -554,8 -277,8 +282,8 @@@ create_mail_process(enum process_type p
                    pid_t *pid_r)
  {
        const struct var_expand_table *var_expand_table;
-       const char *p, *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
-       const char *system_groups_user, *master_user;
+       const char *p, *addr, *chroot_dir, *home_dir, *full_home_dir;
 -      const char *system_user, *master_user, *key;
++      const char *system_groups_user, *master_user, *key;
        struct mail_process_group *process_group;
        char title[1024];
        struct log_io *log;
        }
  
        t_array_init(&extra_args, 16);
-       mail = home_dir = chroot_dir = system_groups_user = "";
 -      home_dir = chroot_dir = system_user = ""; master_user = NULL;
++      home_dir = chroot_dir = system_groups_user = "";
 +      master_user = NULL;
        uid = (uid_t)-1; gid = (gid_t)-1; nice_value = 0;
        home_given = FALSE;
        for (; *args != NULL; args++) {
                log_set_prefix(log, str_c(str));
        }
  
-       child_process_init_env();
+       child_process_init_env(set);
  
 -      /* move the client socket into stdin and stdout fds, log to stderr */
 -      if (dup2(dump_capability ? null_fd : request->fd, 0) < 0)
 -              i_fatal("dup2(stdin) failed: %m");
 -      if (dup2(request->fd, 1) < 0)
 -              i_fatal("dup2(stdout) failed: %m");
 -      if (dup2(log_fd, 2) < 0)
 -              i_fatal("dup2(stderr) failed: %m");
 -
 -      for (i = 0; i < 3; i++)
 -              fd_close_on_exec(i, FALSE);
 -
        /* setup environment - set the most important environment first
           (paranoia about filling up environment without noticing) */
 -      restrict_access_set_env(system_user, uid, gid, set->mail_priv_gid_t,
 +      restrict_access_set_env(system_groups_user, uid, gid,
 +                              set->mail_priv_gid_t,
                                dump_capability ? "" : chroot_dir,
                                set->first_valid_gid, set->last_valid_gid,
                                set->mail_access_groups);
                i_snprintf(title, sizeof(title), "[%s %s]", user, addr);
        }
  
 -      /* make sure we don't leak syslog fd, but do it last so that
 -         any errors above will be logged */
 +      /* make sure we don't leak syslog fd. try to do it as late as possible,
 +         but also before dup2()s in case syslog fd is one of them. */
        closelog();
  
-       if (dup2(dump_capability ? null_fd : request->fd, 0) < 0)
 +      /* move the client socket into stdin and stdout fds, log to stderr */
++      if (dup2(request->fd, 0) < 0)
 +              i_fatal("dup2(stdin) failed: %m");
 +      if (dup2(request->fd, 1) < 0)
 +              i_fatal("dup2(stdout) failed: %m");
 +      if (dup2(log_fd, 2) < 0)
 +              i_fatal("dup2(stderr) failed: %m");
 +
 +      for (i = 0; i < 3; i++)
 +              fd_close_on_exec(i, FALSE);
 +
        if (set->mail_drop_priv_before_exec) {
                restrict_access_by_env(TRUE);
                /* privileged GID is now only in saved-GID. if we want to
index f431a7e7cffa38e6415dbf279d6e638ad2bf5085,c2913785d1dc23c896ad92247a18d6fcd7ae78ae..147155044e3ec912ba85583ccb36e3ec9a961294
@@@ -185,13 -186,13 +191,13 @@@ static void sig_reload_settings(const s
        settings_reload();
  }
  
 -static void sig_reopen_logs(int signo ATTR_UNUSED,
 +static void sig_reopen_logs(const siginfo_t *si ATTR_UNUSED,
                            void *context ATTR_UNUSED)
  {
-       set_logfile(settings_root->defaults);
+       set_logfile(master_set->defaults);
  }
  
- static bool have_stderr_set(struct settings *set)
+ static bool have_stderr_set(struct master_settings *set)
  {
        if (*set->log_path != '\0' &&
            strcmp(set->log_path, "/dev/stderr") == 0)
index 009169d3812769b5b5ab8b0314b09e048583618d,a8b3c4215999c82cb0d315d942c6b09ea416b7cc..b3ec73ef6634d47311b8b0ee9505115a5e921419
  #  include <sys/resource.h>
  #endif
  
- enum settings_type {
-       SETTINGS_TYPE_ROOT,
-       SETTINGS_TYPE_SERVER,
-       SETTINGS_TYPE_AUTH,
-       SETTINGS_TYPE_AUTH_SOCKET,
-       SETTINGS_TYPE_AUTH_PASSDB,
-       SETTINGS_TYPE_AUTH_USERDB,
-         SETTINGS_TYPE_NAMESPACE,
-       SETTINGS_TYPE_SOCKET,
-       SETTINGS_TYPE_DICT,
-       SETTINGS_TYPE_PLUGIN
- };
+ extern struct setting_parser_info master_auth_setting_parser_info;
+ extern struct setting_parser_info master_setting_parser_info;
+ extern struct setting_parser_info master_auth_socket_setting_parser_info;
  
- struct settings_parse_ctx {
-       enum settings_type type, parent_type;
-       enum mail_protocol protocol;
+ #undef DEF
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct master_auth_socket_unix_settings, name), NULL }
  
-       struct server_settings *root, *server;
-       struct auth_settings *auth;
-       struct socket_settings *socket;
-       struct auth_socket_settings *auth_socket;
-       struct auth_passdb_settings *auth_passdb;
-       struct auth_userdb_settings *auth_userdb;
-         struct namespace_settings *namespace;
+ static struct setting_define master_auth_socket_master_setting_defines[] = {
+       DEF(SET_STR, path),
  
-       int level;
+       SETTING_DEFINE_LIST_END
  };
  
- #include "master-settings-defs.c"
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, auth_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, auth_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_settings)
- static struct setting_def auth_setting_defs[] = {
-       DEF_STR(mechanisms),
-       DEF_STR(realms),
-       DEF_STR(default_realm),
-       DEF_INT(cache_size),
-       DEF_INT(cache_ttl),
-       DEF_INT(cache_negative_ttl),
-       DEF_STR(executable),
-       DEF_STR(user),
-       DEF_STR(chroot),
-       DEF_STR(username_chars),
-       DEF_STR(username_translation),
-       DEF_STR(username_format),
-       DEF_STR(master_user_separator),
-       DEF_STR(anonymous_username),
-       DEF_STR(krb5_keytab),
-       DEF_STR(gssapi_hostname),
-       DEF_STR(winbind_helper_path),
-       DEF_INT(failure_delay),
-       DEF_BOOL(verbose),
-       DEF_BOOL(debug),
-       DEF_BOOL(debug_passwords),
-       DEF_BOOL(ssl_require_client_cert),
-       DEF_BOOL(ssl_username_from_cert),
-       DEF_BOOL(use_winbind),
-       DEF_INT(count),
-       DEF_INT(worker_max_count),
-       DEF_INT(process_size),
-       { 0, NULL, 0 }
+ static struct master_auth_socket_unix_settings master_auth_socket_master_default_settings = {
+       MEMBER(path) "auth-master"
  };
  
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, socket_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, socket_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, socket_settings)
+ struct setting_parser_info master_auth_socket_master_setting_parser_info = {
+       MEMBER(defines) master_auth_socket_master_setting_defines,
+       MEMBER(defaults) &master_auth_socket_master_default_settings,
+       MEMBER(parent) &master_auth_socket_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
  
- static struct setting_def socket_setting_defs[] = {
-       DEF_STR(path),
-       DEF_INT(mode),
-       DEF_STR(user),
-       DEF_STR(group),
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct master_auth_socket_unix_settings)
+ };
  
-       { 0, NULL, 0 }
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct master_auth_socket_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+       { SET_DEFLIST, name, offsetof(struct master_auth_socket_settings, field), defines }
+ static struct setting_define master_auth_socket_setting_defines[] = {
+       DEF(SET_STR, type),
+       DEFLIST(masters, "master", &master_auth_socket_master_setting_parser_info),
+       SETTING_DEFINE_LIST_END
  };
  
- static struct setting_def auth_socket_setting_defs[] = {
-       DEF_STRUCT_STR(type, auth_socket_settings),
+ struct setting_parser_info master_auth_socket_setting_parser_info = {
+       MEMBER(defines) master_auth_socket_setting_defines,
+       MEMBER(defaults) NULL,
+       MEMBER(parent) &master_auth_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
  
-       { 0, NULL, 0 }
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) offsetof(struct master_auth_socket_settings, type),
+       MEMBER(struct_size) sizeof(struct master_auth_socket_settings)
  };
  
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, auth_passdb_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, auth_passdb_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_passdb_settings)
- static struct setting_def auth_passdb_setting_defs[] = {
-       DEF_STR(driver),
-       DEF_STR(args),
-       DEF_BOOL(deny),
-       DEF_BOOL(pass),
-       DEF_BOOL(master),
-       { 0, NULL, 0 }
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct master_auth_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+       { SET_DEFLIST, name, offsetof(struct master_auth_settings, field), defines }
+ static struct setting_define master_auth_setting_defines[] = {
+       DEF(SET_STR, name),
+       DEF(SET_STR, executable),
+       DEF(SET_STR, user),
+       DEF(SET_STR, chroot),
+       DEF(SET_UINT, count),
+       DEF(SET_UINT, process_size),
+       DEF(SET_STR, mechanisms),
+       DEF(SET_BOOL, debug),
+       DEFLIST(sockets, "socket", &master_auth_socket_setting_parser_info),
+       SETTING_DEFINE_LIST_END
  };
  
- static struct setting_def auth_userdb_setting_defs[] = {
-       DEF_STRUCT_STR(driver, auth_userdb_settings),
-       DEF_STRUCT_STR(args, auth_userdb_settings),
+ static struct master_auth_settings master_auth_default_settings = {
+       MEMBER(name) "default",
+       MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
+       MEMBER(user) "root",
+       MEMBER(chroot) "",
+       MEMBER(count) 1,
+       MEMBER(mechanisms) "plain",
+       MEMBER(debug) FALSE,
+       MEMBER(process_size) 256
  
-       { 0, NULL, 0 }
+       /* .. */
  };
  
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, namespace_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, namespace_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, namespace_settings)
- static struct setting_def namespace_setting_defs[] = {
-       DEF_STR(type),
-       DEF_STR(separator),
-       DEF_STR(prefix),
-       DEF_STR(location),
-       DEF_STR(alias_for),
-       DEF_BOOL(inbox),
-       DEF_BOOL(hidden),
-       DEF_STR(list),
-       DEF_BOOL(subscriptions),
-       { 0, NULL, 0 }
+ struct setting_parser_info master_auth_setting_parser_info = {
+       MEMBER(defines) master_auth_setting_defines,
+       MEMBER(defaults) &master_auth_default_settings,
+       MEMBER(parent) &master_setting_parser_info,
+       MEMBER(dynamic_parsers) NULL,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) offsetof(struct master_auth_settings, name),
+       MEMBER(struct_size) sizeof(struct master_auth_settings)
  };
  
- struct settings default_settings = {
-       MEMBER(server) NULL,
-       MEMBER(protocol) 0,
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+       { type, #name, offsetof(struct master_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+       { SET_DEFLIST, name, offsetof(struct master_settings, field), defines }
+ static struct setting_define master_setting_defines[] = {
+       /* common */
+       DEF(SET_STR, base_dir),
+       DEF(SET_STR, log_path),
+       DEF(SET_STR, info_log_path),
+       DEF(SET_STR, log_timestamp),
+       DEF(SET_STR, syslog_facility),
+       /* general */
+       DEF(SET_STR, protocols),
+       DEF(SET_STR, listen),
+       DEF(SET_STR, ssl_listen),
+       DEF(SET_STR, ssl),
+       DEF(SET_STR, ssl_key_file),
+       DEF(SET_UINT, ssl_parameters_regenerate),
+       DEF(SET_BOOL, version_ignore),
+       /* 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, disable_plaintext_auth),
+       DEF(SET_UINT, login_process_size),
+       DEF(SET_UINT, login_processes_count),
+       DEF(SET_UINT, login_max_processes_count),
+       /* mail */
+       DEF(SET_STR, valid_chroot_dirs),
+       DEF(SET_STR, mail_chroot),
+       DEF(SET_UINT, max_mail_processes),
+       DEF(SET_UINT, mail_max_userip_connections),
+       DEF(SET_BOOL, verbose_proctitle),
+       DEF(SET_UINT, first_valid_uid),
+       DEF(SET_UINT, last_valid_uid),
+       DEF(SET_UINT, first_valid_gid),
+       DEF(SET_UINT, last_valid_gid),
+       DEF(SET_STR, mail_access_groups),
+       DEF(SET_STR, mail_privileged_group),
+       DEF(SET_STR, mail_uid),
+       DEF(SET_STR, mail_gid),
+       DEF(SET_STR, mail_plugins),
+       DEF(SET_STR, imap_capability),
+       DEF(SET_STR_VARS, mail_location),
+       DEF(SET_BOOL, mail_debug),
 -      DEF(SET_UINT, umask),
+       DEF(SET_BOOL, mail_drop_priv_before_exec),
+       DEF(SET_STR, mail_executable),
+       DEF(SET_UINT, mail_process_size),
+       DEF(SET_STR, mail_log_prefix),
+       DEF(SET_UINT, mail_log_max_lines_per_sec),
  
+       /* dict */
+       DEF(SET_STR, dict_db_config),
+       DEF(SET_UINT, dict_process_count),
+       DEFLIST(auths, "auth", &master_auth_setting_parser_info),
+       { SET_STRLIST, "dict", offsetof(struct master_settings, dicts), NULL },
+       SETTING_DEFINE_LIST_END
+ };
+ struct master_settings master_default_settings = {
        /* common */
        MEMBER(base_dir) PKG_RUNDIR,
        MEMBER(log_path) "",
        MEMBER(mail_privileged_group) "",
        MEMBER(mail_uid) "",
        MEMBER(mail_gid) "",
+       MEMBER(mail_plugins) "",
+       MEMBER(imap_capability) "",
  
        MEMBER(mail_location) "",
-       MEMBER(mail_cache_fields) "",
-       MEMBER(mail_never_cache_fields) "imap.envelope",
-       MEMBER(mail_cache_min_mail_count) 0,
-       MEMBER(mailbox_idle_check_interval) 30,
        MEMBER(mail_debug) FALSE,
-       MEMBER(mail_full_filesystem_access) FALSE,
-       MEMBER(mail_max_keyword_length) 50,
-       MEMBER(mail_save_crlf) FALSE,
- #ifdef MMAP_CONFLICTS_WRITE
-       MEMBER(mmap_disable) TRUE,
- #else
-       MEMBER(mmap_disable) FALSE,
- #endif
-       MEMBER(dotlock_use_excl) TRUE,
-       MEMBER(fsync_disable) FALSE,
-       MEMBER(mail_nfs_storage) FALSE,
-       MEMBER(mail_nfs_index) FALSE,
-       MEMBER(mailbox_list_index_disable) TRUE,
-       MEMBER(lock_method) "fcntl",
-       MEMBER(maildir_stat_dirs) FALSE,
-       MEMBER(maildir_copy_with_hardlinks) TRUE,
-       MEMBER(maildir_copy_preserve_filename) FALSE,
 -      MEMBER(umask) 0077,
 +      MEMBER(maildir_very_dirty_syncs) FALSE,
-       MEMBER(mbox_read_locks) "fcntl",
-       MEMBER(mbox_write_locks) "dotlock fcntl",
-       MEMBER(mbox_lock_timeout) 300,
-       MEMBER(mbox_dotlock_change_timeout) 120,
-       MEMBER(mbox_min_index_size) 0,
-       MEMBER(mbox_dirty_syncs) TRUE,
-       MEMBER(mbox_very_dirty_syncs) FALSE,
-       MEMBER(mbox_lazy_writes) TRUE,
-       MEMBER(dbox_rotate_size) 2048,
-       MEMBER(dbox_rotate_min_size) 16,
-       MEMBER(dbox_rotate_days) 1,
 +      MEMBER(dbox_purge_min_percentage) 0,
        MEMBER(mail_drop_priv_before_exec) FALSE,
  
-       MEMBER(mail_executable) PKG_LIBEXECDIR"/imap",
+       MEMBER(mail_executable) NULL,
        MEMBER(mail_process_size) 256,
-       MEMBER(mail_plugins) "",
-       MEMBER(mail_plugin_dir) MODULEDIR"/imap",
        MEMBER(mail_log_prefix) "%Us(%u): ",
        MEMBER(mail_log_max_lines_per_sec) 10,
  
        /* .. */
  };
  
- struct auth_settings default_auth_settings = {
+ struct setting_parser_info master_setting_parser_info = {
+       MEMBER(defines) master_setting_defines,
+       MEMBER(defaults) &master_default_settings,
        MEMBER(parent) NULL,
-       MEMBER(next) NULL,
+       MEMBER(dynamic_parsers) NULL,
  
-       MEMBER(name) NULL,
-       MEMBER(mechanisms) "plain",
-       MEMBER(realms) "",
-       MEMBER(default_realm) "",
-       MEMBER(cache_size) 0,
-       MEMBER(cache_ttl) 3600,
-       MEMBER(cache_negative_ttl) 3600,
-       MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
-       MEMBER(user) "root",
-       MEMBER(chroot) "",
-       MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@",
-       MEMBER(username_translation) "",
-       MEMBER(username_format) "",
-       MEMBER(master_user_separator) "",
-       MEMBER(anonymous_username) "anonymous",
-       MEMBER(krb5_keytab) "",
-       MEMBER(gssapi_hostname) "",
-       MEMBER(winbind_helper_path) "/usr/bin/ntlm_auth",
-       MEMBER(failure_delay) 2,
-       MEMBER(verbose) FALSE,
-       MEMBER(debug) FALSE,
-       MEMBER(debug_passwords) FALSE,
-       MEMBER(ssl_require_client_cert) FALSE,
-       MEMBER(ssl_username_from_cert) FALSE,
-       MEMBER(use_winbind) FALSE,
+       MEMBER(parent_offset) (size_t)-1,
+       MEMBER(type_offset) (size_t)-1,
+       MEMBER(struct_size) sizeof(struct master_settings)
+ };
  
-       MEMBER(count) 1,
-       MEMBER(worker_max_count) 30,
-       MEMBER(process_size) 256,
+ static pool_t settings_pool, settings2_pool;
+ struct master_server_settings *master_set = NULL;
  
-       /* .. */
-       MEMBER(uid) 0,
-       MEMBER(gid) 0,
-       MEMBER(passdbs) NULL,
-       MEMBER(userdbs) NULL,
-       MEMBER(sockets) NULL
- };
+ #ifdef HAVE_MODULES
+ static const char *
+ get_process_capability(enum process_type ptype, struct master_settings *set)
+ {
+       /* FIXME: pretty ugly code just for getting the capability
+          automatically */
+       static const char *args[] = {
+               "uid=65534",
+               "gid=65534",
+               "home=/tmp",
+               NULL
+       };
+       const char *pname = process_names[ptype];
+       enum master_login_status login_status;
+       struct mail_login_request request;
+       char buf[4096];
+       int fd[2], status;
+       ssize_t ret;
+       unsigned int pos;
+       uid_t uid;
+       pid_t pid;
  
- struct socket_settings default_socket_settings = {
- #define DEFAULT_MASTER_SOCKET_PATH "auth-master"
- #define DEFAULT_CLIENT_SOCKET_PATH "auth-client"
-       MEMBER(path) "",
-       MEMBER(mode) 0600,
-       MEMBER(user) "",
-       MEMBER(group) ""
- };
+       uid = geteuid();
+       if (uid != 0) {
+               /* use the current user */
+               args[0] = t_strdup_printf("uid=%s", dec2str(uid));
+               args[1] = t_strdup_printf("gid=%s", dec2str(getegid()));
+       }
  
- struct namespace_settings default_namespace_settings = {
-       MEMBER(parent) NULL,
-       MEMBER(next) NULL,
-       MEMBER(type) NULL,
-       MEMBER(separator) "",
-       MEMBER(prefix) "",
-       MEMBER(location) "",
-       MEMBER(alias_for) NULL,
-       MEMBER(inbox) FALSE,
-       MEMBER(hidden) FALSE,
-       MEMBER(list) "yes",
-       MEMBER(subscriptions) TRUE
- };
 -      if (pipe(fd) < 0) {
 -              i_error("pipe() failed: %m");
++      if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
++              i_error("socketpair() failed: %m");
+               return NULL;
+       }
+       fd_close_on_exec(fd[0], TRUE);
+       fd_close_on_exec(fd[1], TRUE);
  
- static pool_t settings_pool, settings2_pool;
- struct server_settings *settings_root = NULL;
+       memset(&request, 0, sizeof(request));
+       request.fd = fd[1];
+       login_status = create_mail_process(ptype, set, &request,
+                                          "dump-capability", args, NULL, TRUE,
+                                          &pid);
+       if (login_status != MASTER_LOGIN_STATUS_OK) {
+               (void)close(fd[0]);
+               (void)close(fd[1]);
+               return NULL;
+       }
+       (void)close(fd[1]);
+       alarm(5);
+       if (wait(&status) == -1) {
+               i_fatal("%s dump-capability process %d got stuck",
+                       pname, (int)pid);
+       }
+       alarm(0);
+       if (status != 0) {
+               (void)close(fd[0]);
+               if (WIFSIGNALED(status)) {
+                       i_error("%s dump-capability process "
+                               "killed with signal %d",
+                               pname, WTERMSIG(status));
+               } else {
+                       i_error("%s dump-capability process returned %d",
+                               pname, WIFEXITED(status) ? WEXITSTATUS(status) :
+                               status);
+               }
+               return NULL;
+       }
+       pos = 0;
+       while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
+               pos += ret;
+       if (ret < 0) {
+               i_error("read(%s dump-capability process) failed: %m", pname);
+               (void)close(fd[0]);
+               return NULL;
+       }
+       (void)close(fd[0]);
+       if (pos == 0 || buf[pos-1] != '\n') {
+               i_error("%s dump-capability: Couldn't read capability "
+                       "(got %u bytes)", pname, pos);
+               return NULL;
+       }
+       buf[pos-1] = '\0';
+       return i_strdup(buf);
+ }
+ static bool get_imap_capability(struct master_settings *set)
+ {
+       static const char *generated_capability = NULL;
+       if (generated_capability != NULL) {
+               /* Reloading configuration. Don't try to execute the imap
+                  process again. Too risky and the wait() call below will
+                  break it anyway. Just use the previous capability list we
+                  already had generated. */
+               set->imap_generated_capability =
+                       p_strdup(settings_pool, generated_capability);
+               return TRUE;
+       }
+       generated_capability = get_process_capability(PROCESS_TYPE_IMAP, set);
+       if (generated_capability == NULL)
+               return FALSE;
+       set->imap_generated_capability =
+               p_strdup(settings_pool, generated_capability);
+       return TRUE;
+ }
+ #endif
  
- static void fix_base_path(struct settings *set, const char **str)
+ static void fix_base_path(struct master_settings *set, const char **str)
  {
        if (*str != NULL && **str != '\0' && **str != '/') {
                *str = p_strconcat(settings_pool,
@@@ -942,22 -761,7 +761,22 @@@ static bool settings_verify(struct mast
        return TRUE;
  }
  
- static bool login_want_core_dumps(struct settings *set)
++static bool login_want_core_dumps(struct master_server_settings *set)
 +{
 +      const char *p;
 +
-       p = set->server->pop3 == NULL ? NULL :
-               strstr(set->server->pop3->login_executable, " -D");
++      p = set->pop3 == NULL ? NULL :
++              strstr(set->pop3->login_executable, " -D");
 +      if (p != NULL && p[3] == '\0')
 +              return TRUE;
-       p = set->server->imap == NULL ? NULL :
-               strstr(set->server->imap->login_executable, " -D");
++      p = set->imap == NULL ? NULL :
++              strstr(set->imap->login_executable, " -D");
 +      if (p != NULL && p[3] == '\0')
 +              return TRUE;
 +      return FALSE;
 +}
 +
- static bool settings_do_fixes(struct settings *set)
+ static bool settings_do_fixes(struct master_settings *set)
  {
        struct stat st;
  
                return FALSE;
        }
  
--      if (!settings_have_connect_sockets(set)) {
--              /* we are not using external authentication, so make sure the
--                 login directory exists with correct permissions and it's
--                 empty. with external auth we wouldn't want to delete
--                 existing sockets or break the permissions required by the
--                 auth server. */
-               mode_t mode = login_want_core_dumps(set) ? 0770 : 0750;
-               if (safe_mkdir(set->login_dir, mode,
 -              if (safe_mkdir(set->login_dir, 0750,
--                             master_uid, set->server->login_gid) == 0) {
--                      i_warning("Corrected permissions for login directory "
--                                "%s", set->login_dir);
--              }
--
--              unlink_auth_sockets(set->login_dir, "");
--      }
--
  #ifdef HAVE_MODULES
        if (*set->mail_plugins != '\0' && set->protocol == MAIL_PROTOCOL_IMAP &&
            *set->imap_capability == '\0') {
@@@ -1020,601 -824,160 +824,180 @@@ settings_fix(struct master_settings *se
        return nofixes ? TRUE : settings_do_fixes(set);
  }
  
- static void pid_file_check_running(const char *path)
- {
-       char buf[32];
-       int fd;
-       ssize_t ret;
-       fd = open(path, O_RDONLY);
-       if (fd == -1) {
-               if (errno == ENOENT)
-                       return;
-               i_fatal("open(%s) failed: %m", path);
-       }
-       ret = read(fd, buf, sizeof(buf));
-       if (ret <= 0) {
-               if (ret == 0)
-                       i_error("Empty PID file in %s, overriding", path);
-               else
-                       i_fatal("read(%s) failed: %m", path);
-       } else {
-               pid_t pid;
-               if (buf[ret-1] == '\n')
-                       ret--;
-               buf[ret] = '\0';
-               pid = atoi(buf);
-               if (pid == getpid() || (kill(pid, 0) < 0 && errno == ESRCH)) {
-                       /* doesn't exist */
-               } else {
-                       i_fatal("Dovecot is already running with PID %s "
-                               "(read from %s)", buf, path);
-               }
-       }
-       (void)close(fd);
- }
- static struct auth_settings *
- auth_settings_new(struct server_settings *server, const char *name)
+ static void
+ settings_warn_needed_fds(struct master_server_settings *server ATTR_UNUSED)
  {
-       struct auth_settings *auth;
-       auth = p_new(settings_pool, struct auth_settings, 1);
-       /* copy defaults */
-       *auth = server->auth_defaults;
-       auth->parent = server;
-       auth->name = p_strdup(settings_pool, name);
-       auth->next = server->auths;
-       server->auths = auth;
-       return auth;
- }
+ #ifdef HAVE_SETRLIMIT
+       struct rlimit rlim;
+       unsigned int fd_count = 0;
  
- static struct auth_settings *
- parse_new_auth(struct server_settings *server, const char *name,
-              const char **errormsg)
- {
-       struct auth_settings *auth;
+       if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+               return;
  
-       if (strchr(name, '/') != NULL) {
-               *errormsg = "Authentication process name must not contain '/'";
-               return NULL;
-       }
+       /* count only log pipes needed for login and mail processes. we need
+          more, but they're the ones that can use up most of the fds */
+       if (server->imap != NULL)
+               fd_count += server->imap->login_max_processes_count;
+       if (server->pop3 != NULL)
+               fd_count += server->pop3->login_max_processes_count;
+       fd_count += server->defaults->max_mail_processes;
  
-       for (auth = server->auths; auth != NULL; auth = auth->next) {
-               if (strcmp(auth->name, name) == 0) {
-                       *errormsg = "Authentication process already exists "
-                               "with the same name";
-                       return NULL;
-               }
+       if (rlim.rlim_cur < fd_count) {
+               i_warning("fd limit %d is lower than what Dovecot can use under "
+                         "full load (more than %u). Either grow the limit or "
+                         "change login_max_processes_count and "
+                         "max_mail_processes master_settings",
+                         (int)rlim.rlim_cur, fd_count);
        }
-       return auth_settings_new(server, name);
- }
- static struct auth_passdb_settings *
- auth_passdb_settings_new(struct auth_settings *auth, const char *type)
- {
-       struct auth_passdb_settings *as, **as_p;
-       as = p_new(settings_pool, struct auth_passdb_settings, 1);
-       as->parent = auth;
-       as->driver = str_lcase(p_strdup(settings_pool, type));
-       as_p = &auth->passdbs;
-       while (*as_p != NULL)
-               as_p = &(*as_p)->next;
-       *as_p = as;
-       return as;
- }
- static struct auth_userdb_settings *
- auth_userdb_settings_new(struct auth_settings *auth, const char *type)
- {
-       struct auth_userdb_settings *as, **as_p;
-       as = p_new(settings_pool, struct auth_userdb_settings, 1);
-       as->parent = auth;
-       as->driver = str_lcase(p_strdup(settings_pool, type));
-       as_p = &auth->userdbs;
-       while (*as_p != NULL)
-               as_p = &(*as_p)->next;
-       *as_p = as;
-       return as;
- }
- static struct auth_socket_settings *
- auth_socket_settings_new(struct auth_settings *auth, const char *type)
- {
-       struct auth_socket_settings *as, **as_p;
-       as = p_new(settings_pool, struct auth_socket_settings, 1);
-       as->parent = auth;
-       as->type = str_lcase(p_strdup(settings_pool, type));
-       as->master = default_socket_settings;
-       as->client = default_socket_settings;
-       as->master.path = DEFAULT_MASTER_SOCKET_PATH;
-       as->client.path = DEFAULT_CLIENT_SOCKET_PATH;
-       as_p = &auth->sockets;
-       while (*as_p != NULL)
-               as_p = &(*as_p)->next;
-       *as_p = as;
-       return as;
+ #endif
  }
  
- static struct auth_socket_settings *
- parse_new_auth_socket(struct auth_settings *auth, const char *name,
-                     const char **errormsg)
+ static void
+ config_split_all_settings(struct master_settings *set, const char *input)
  {
-       if (strcmp(name, "connect") != 0 && strcmp(name, "listen") != 0) {
-               *errormsg = "Unknown auth socket type";
-               return NULL;
-       }
+       const char *p, *line;
+       string_t *str;
  
-       if (auth->sockets != NULL && strcmp(name, "connect") == 0) {
-               *errormsg = "With connect auth socket no other sockets "
-                       "can be used in same auth section";
-               return NULL;
+       str = t_str_new(256);
+       p_array_init(&set->all_settings, settings_pool, 256);
+       for (p = input; *p != '\n'; p++) {
+               str_truncate(str, 0);
+               for (; *p != '='; p++) {
+                       i_assert(*p != '\n' && *p != '\0');
+                       str_append_c(str, i_toupper(*p));
+               }
+               for (; *p != '\n'; p++) {
+                       i_assert(*p != '\0');
+                       str_append_c(str, *p);
+               }
+               line = p_strdup(settings_pool, str_c(str));
+               array_append(&set->all_settings, &line, 1);
        }
-       return auth_socket_settings_new(auth, name);
  }
  
- static struct namespace_settings *
namespace_settings_new(struct server_settings *server, const char *type)
+ static int config_exec(const char *path, const char *service,
                     struct master_settings **set_r)
  {
-       struct namespace_settings *ns, **ns_p;
-       ns = p_new(settings_pool, struct namespace_settings, 1);
-       *ns = default_namespace_settings;
-       ns->parent = server;
-       ns->type = str_lcase(p_strdup(settings_pool, type));
-       ns_p = &server->namespaces;
-       while (*ns_p != NULL)
-               ns_p = &(*ns_p)->next;
-       *ns_p = ns;
-       return ns;
+       struct setting_parser_context *parser;
+       string_t *all_settings;
+       int ret;
+       env_put("LOG_TO_MASTER=1");
+       all_settings = str_new(default_pool, 10240);
+       parser = settings_parser_init(settings_pool,
+                                     &master_setting_parser_info,
+                                     SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+       settings_parse_save_input(parser, all_settings);
+       if ((ret = settings_parse_exec(parser, DOVECOT_CONFIG_BIN_PATH,
+                                      path, service)) == 0) {
+               *set_r = settings_parser_get(parser);
+               config_split_all_settings(*set_r, str_c(all_settings));
+       }
+       settings_parser_deinit(&parser);
+       str_free(&all_settings);
+       return ret;
  }
  
- static struct namespace_settings *
- parse_new_namespace(struct server_settings *server, const char *name,
-                   const char **errormsg)
+ int master_settings_read(const char *path,
+                        struct master_server_settings **set_r)
  {
-       if (strcasecmp(name, "private") != 0 &&
-           strcasecmp(name, "shared") != 0 &&
-           strcasecmp(name, "public") != 0) {
-               *errormsg = "Unknown namespace type";
-               return NULL;
-       }
+       struct master_server_settings *set;
  
-       return namespace_settings_new(server, name);
+       p_clear(settings_pool);
+       set = p_new(settings_pool, struct master_server_settings, 1);
+       master_default_settings.mail_executable = NULL;
+       master_default_settings.login_executable = NULL;
+       if (config_exec(path, "", &set->defaults) < 0)
+               return -1;
+       set->defaults->protocol = MAIL_PROTOCOL_ANY;
+       set->defaults->server = set;
+       master_default_settings.mail_executable = PKG_LIBEXECDIR"/imap";
+       master_default_settings.login_executable = PKG_LIBEXECDIR"/imap-login";
+       if (config_exec(path, "imap", &set->imap) < 0)
+               return -1;
+       set->imap->protocol = MAIL_PROTOCOL_IMAP;
+       set->imap->server = set;
+       master_default_settings.mail_executable = PKG_LIBEXECDIR"/pop3";
+       master_default_settings.login_executable = PKG_LIBEXECDIR"/pop3-login";
+       if (config_exec(path, "pop3", &set->pop3) < 0)
+               return -1;
+       set->pop3->protocol = MAIL_PROTOCOL_POP3;
+       set->pop3->server = set;
+       *set_r = set;
+       return 0;
  }
  
- static const char *parse_setting(const char *key, const char *value,
-                                struct settings_parse_ctx *ctx)
++static void settings_verify_master(struct master_server_settings *set)
 +{
-       const char *error;
-       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 (!settings_have_connect_sockets(set->defaults)) {
++              /* we are not using external authentication, so make sure the
++                 login directory exists with correct permissions and it's
++                 empty. with external auth we wouldn't want to delete
++                 existing sockets or break the permissions required by the
++                 auth server. */
++              mode_t mode = login_want_core_dumps(set) ? 0770 : 0750;
++              if (safe_mkdir(set->defaults->login_dir, mode,
++                             master_uid, set->login_gid) == 0) {
++                      i_warning("Corrected permissions for login directory "
++                                "%s", set->defaults->login_dir);
 +              }
 +
-               if (error == NULL)
-                       return NULL;
-               if (strncmp(key, "auth_", 5) == 0) {
-                       return parse_setting_from_defs(settings_pool,
-                                                      auth_setting_defs,
-                                                      ctx->auth,
-                                                      key + 5, value);
-               }
-               return error;
-       case SETTINGS_TYPE_AUTH:
-               if (strncmp(key, "auth_", 5) == 0)
-                       key += 5;
-               return parse_setting_from_defs(settings_pool, auth_setting_defs,
-                                              ctx->auth, key, value);
-       case SETTINGS_TYPE_AUTH_SOCKET:
-               return parse_setting_from_defs(settings_pool,
-                                              auth_socket_setting_defs,
-                                              ctx->auth_socket, key, value);
-       case SETTINGS_TYPE_AUTH_PASSDB:
-               return parse_setting_from_defs(settings_pool,
-                                              auth_passdb_setting_defs,
-                                              ctx->auth_passdb, key, value);
-       case SETTINGS_TYPE_AUTH_USERDB:
-               return parse_setting_from_defs(settings_pool,
-                                              auth_userdb_setting_defs,
-                                              ctx->auth_userdb, key, value);
-       case SETTINGS_TYPE_NAMESPACE:
-               return parse_setting_from_defs(settings_pool,
-                                              namespace_setting_defs,
-                                              ctx->namespace, key, value);
-       case SETTINGS_TYPE_SOCKET:
-               return parse_setting_from_defs(settings_pool,
-                                              socket_setting_defs,
-                                              ctx->socket, key, value);
-       case SETTINGS_TYPE_DICT:
-               key = p_strdup(settings_pool, key);
-               value = p_strdup(settings_pool, value);
-               array_append(&ctx->server->dicts, &key, 1);
-               array_append(&ctx->server->dicts, &value, 1);
-               return NULL;
-       case SETTINGS_TYPE_PLUGIN:
-               key = p_strdup(settings_pool, key);
-               value = p_strdup(settings_pool, value);
-               if (ctx->protocol == MAIL_PROTOCOL_ANY ||
-                   ctx->protocol == MAIL_PROTOCOL_IMAP) {
-                       array_append(&ctx->server->imap->plugin_envs, &key, 1);
-                       array_append(&ctx->server->imap->plugin_envs,
-                                    &value, 1);
-               }
-               if (ctx->protocol == MAIL_PROTOCOL_ANY ||
-                   ctx->protocol == MAIL_PROTOCOL_POP3) {
-                       array_append(&ctx->server->pop3->plugin_envs, &key, 1);
-                       array_append(&ctx->server->pop3->plugin_envs,
-                                    &value, 1);
-               }
-               return NULL;
++              unlink_auth_sockets(set->defaults->login_dir, "");
 +      }
-       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->auth_defaults = default_auth_settings;
-       *server->imap = *imap_defaults;
-       *server->pop3 = *pop3_defaults;
-       p_array_init(&server->dicts, settings_pool, 4);
-       p_array_init(&server->imap->plugin_envs, settings_pool, 8);
-       p_array_init(&server->pop3->plugin_envs, settings_pool, 8);
-       server->imap->server = server;
-       server->imap->protocol = MAIL_PROTOCOL_IMAP;
-       server->imap->login_executable = PKG_LIBEXECDIR"/imap-login";
-       server->imap->mail_executable = PKG_LIBEXECDIR"/imap";
-       server->imap->mail_plugin_dir = MODULEDIR"/imap";
-       server->pop3->server = server;
-       server->pop3->protocol = MAIL_PROTOCOL_POP3;
-       server->pop3->login_executable = PKG_LIBEXECDIR"/pop3-login";
-       server->pop3->mail_executable = PKG_LIBEXECDIR"/pop3";
-       server->pop3->mail_plugin_dir = MODULEDIR"/pop3";
-       return server;
 +}
 +
static bool parse_section(const char *type, const char *name,
-                         struct settings_parse_ctx *ctx, const char **errormsg)
bool master_settings_check(struct master_server_settings *set,
+                          bool nochecks, bool nofixes)
  {
-       struct server_settings *server;
-       if (type == NULL) {
-               /* section closing */
-               if (ctx->level-- > 0) {
-                       ctx->type = ctx->parent_type;
-                       ctx->protocol = MAIL_PROTOCOL_ANY;
-                       switch (ctx->type) {
-                       case SETTINGS_TYPE_AUTH_SOCKET:
-                               ctx->parent_type = SETTINGS_TYPE_AUTH;
-                               break;
-                       default:
-                               ctx->parent_type = SETTINGS_TYPE_ROOT;
-                               break;
-                       }
-               } else {
-                       ctx->type = SETTINGS_TYPE_ROOT;
-                       ctx->server = ctx->root;
-                       ctx->auth = &ctx->root->auth_defaults;
-                       ctx->namespace = NULL;
-               }
-               return TRUE;
-       }
-       ctx->level++;
-       ctx->parent_type = ctx->type;
-       if (strcmp(type, "server") == 0) {
-               if (ctx->type != SETTINGS_TYPE_ROOT) {
-                       *errormsg = "Server section not allowed here";
-                       return FALSE;
-               }
-               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 != 1) {
-                       *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 if (strcmp(name, "lda") == 0)
-                       ctx->protocol = MAIL_PROTOCOL_LDA;
-               else {
-                       *errormsg = "Unknown protocol name";
-                       return FALSE;
-               }
-               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;
-       }
-       if (ctx->type == SETTINGS_TYPE_AUTH &&
-           strcmp(type, "socket") == 0) {
-               ctx->type = SETTINGS_TYPE_AUTH_SOCKET;
-               ctx->auth_socket = parse_new_auth_socket(ctx->auth,
-                                                        name, errormsg);
-               return ctx->auth_socket != NULL;
-       }
-       if (ctx->type == SETTINGS_TYPE_AUTH && strcmp(type, "passdb") == 0) {
-               ctx->type = SETTINGS_TYPE_AUTH_PASSDB;
-               ctx->auth_passdb = auth_passdb_settings_new(ctx->auth, name);
-               return TRUE;
-       }
-       if (ctx->type == SETTINGS_TYPE_AUTH && strcmp(type, "userdb") == 0) {
-               ctx->type = SETTINGS_TYPE_AUTH_USERDB;
-               ctx->auth_userdb = auth_userdb_settings_new(ctx->auth, name);
-               return TRUE;
-       }
-       if (ctx->type == SETTINGS_TYPE_AUTH_SOCKET) {
-               ctx->type = SETTINGS_TYPE_SOCKET;
-               if (strcmp(type, "master") == 0) {
-                       ctx->socket = &ctx->auth_socket->master;
-                       ctx->socket->used = TRUE;
-                       return TRUE;
-               }
+       struct master_auth_settings *const *auths;
+       unsigned int i, count;
+       pool_t temp;
  
-               if (strcmp(type, "client") == 0) {
-                       ctx->socket = &ctx->auth_socket->client;
-                       ctx->socket->used = TRUE;
-                       return TRUE;
-               }
+       if ((*set->imap->protocols == '\0' ||
+            *set->pop3->protocols == '\0') && !nochecks) {
+               i_error("protocols: No protocols given in configuration file");
+               return FALSE;
        }
-       if (strcmp(type, "namespace") == 0) {
-               if (ctx->type != SETTINGS_TYPE_ROOT &&
-                   ctx->type != SETTINGS_TYPE_SERVER) {
-                       *errormsg = "Namespace section not allowed here";
-                       return FALSE;
+       /* --exec-mail is used if nochecks=TRUE. Allow it regardless
+          of what's in protocols setting. */
+       if (!settings_is_active(set->imap) && !nochecks) {
+               if (strcmp(set->imap->protocols, "none") == 0) {
+                       set->imap->protocol = MAIL_PROTOCOL_ANY;
+                       if (!settings_fix(set->imap, nochecks, nofixes))
+                               return FALSE;
                }
-               ctx->type = SETTINGS_TYPE_NAMESPACE;
-               ctx->namespace = parse_new_namespace(ctx->server, name,
-                                                    errormsg);
-               return ctx->namespace != NULL;
-       }
-       if (strcmp(type, "dict") == 0) {
-               if (ctx->type != SETTINGS_TYPE_ROOT &&
-                   ctx->type != SETTINGS_TYPE_SERVER) {
-                       *errormsg = "Plugin section not allowed here";
+               set->imap = NULL;
+       } else {
+               if (!settings_fix(set->imap, nochecks, nofixes))
                        return FALSE;
-               }
-               ctx->type = SETTINGS_TYPE_DICT;
-               return TRUE;
        }
  
-       if (strcmp(type, "plugin") == 0) {
-               if (ctx->type != SETTINGS_TYPE_ROOT &&
-                   ctx->type != SETTINGS_TYPE_SERVER) {
-                       *errormsg = "Plugin section not allowed here";
+       if (!settings_is_active(set->pop3) && !nochecks)
+               set->pop3 = NULL;
+       else {
+               if (!settings_fix(set->pop3, nochecks, nofixes))
                        return FALSE;
-               }
-               ctx->type = SETTINGS_TYPE_PLUGIN;
-               return TRUE;
-       }
-       *errormsg = "Unknown section type";
-       return FALSE;
- }
- static void
- settings_warn_needed_fds(struct server_settings *server ATTR_UNUSED)
- {
- #ifdef HAVE_SETRLIMIT
-       struct rlimit rlim;
-       unsigned int fd_count = 0;
-       if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
-               return;
-       /* count only log pipes needed for login and mail processes. we need
-          more, but they're the ones that can use up most of the fds */
-       for (; server != NULL; server = server->next) {
-               if (server->imap != NULL)
-                       fd_count += server->imap->login_max_processes_count;
-               if (server->pop3 != NULL)
-                       fd_count += server->pop3->login_max_processes_count;
-               fd_count += server->defaults->max_mail_processes;
        }
  
-       if (rlim.rlim_cur < fd_count) {
-               i_warning("fd limit %d is lower than what Dovecot can use under "
-                         "full load (more than %u). Either grow the limit or "
-                         "change login_max_processes_count and "
-                         "max_mail_processes settings",
-                         (int)rlim.rlim_cur, fd_count);
-       }
- #endif
- }
- bool master_settings_read(const char *path, bool nochecks, bool nofixes)
- {
-       struct settings_parse_ctx ctx;
-       struct server_settings *server, *prev;
-       struct auth_settings *auth;
-       struct namespace_settings *ns;
-       pool_t temp;
-       memset(&ctx, 0, sizeof(ctx));
-       p_clear(settings_pool);
-       ctx.type = SETTINGS_TYPE_ROOT;
-       ctx.protocol = MAIL_PROTOCOL_ANY;
-       ctx.server = ctx.root =
-               create_new_server("default",
-                                 &default_settings, &default_settings);
-       ctx.auth = &ctx.server->auth_defaults;
-       if (!settings_read(path, NULL, parse_setting, parse_section, &ctx))
-               return FALSE;
-       if (ctx.level != 0) {
-               i_error("Missing '}'");
+       if (!settings_fix(set->defaults, nochecks, nofixes))
                return FALSE;
-       }
-       /* If server sections were defined, skip the root */
-       if (ctx.root->next != NULL)
-               ctx.root = ctx.root->next;
-       if (!nochecks && !nofixes) {
-               ctx.root->defaults = settings_is_active(ctx.root->imap) ?
-                       ctx.root->imap : ctx.root->pop3;
  
-               path = t_strconcat(ctx.root->defaults->base_dir,
-                                  "/master.pid", NULL);
-               pid_file_check_running(path);
-       }
-       prev = NULL;
-       for (server = ctx.root; server != NULL; server = server->next) {
-               if ((*server->imap->protocols == '\0' ||
-                    *server->pop3->protocols == '\0') && !nochecks) {
-                       i_error("protocols: No protocols given "
-                               "in configuration file");
+       if (!nochecks) {
++              settings_verify_master(set);
+               auths = array_get(&set->defaults->auths, &count);
+               if (count == 0) {
+                       i_error("Missing auth section");
                        return FALSE;
                }
-               /* --exec-mail is used if nochecks=TRUE. Allow it regardless
-                  of what's in protocols setting. */
-               if (!settings_is_active(server->imap) && !nochecks) {
-                       if (strcmp(server->imap->protocols, "none") == 0) {
-                               server->imap->protocol = MAIL_PROTOCOL_ANY;
-                               if (!settings_fix(server->imap, nochecks,
-                                                 nofixes))
-                                       return FALSE;
-                               server->defaults = server->imap;
-                       }
-                       server->imap = NULL;
-               } else {
-                       if (!settings_fix(server->imap, nochecks, nofixes))
-                               return FALSE;
-                       server->defaults = server->imap;
-               }
-               if (!settings_is_active(server->pop3) && !nochecks)
-                       server->pop3 = NULL;
-               else {
-                       if (!settings_fix(server->pop3, nochecks, nofixes))
-                               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;
-                       if (auth == NULL) {
-                               i_error("Missing auth section for server %s",
-                                       server->name);
+               for (i = 0; i < count; i++) {
+                       if (!auth_settings_verify(set->defaults, auths[i]))
                                return FALSE;
-                       }
-                       if (!nochecks) {
-                               for (; auth != NULL; auth = auth->next) {
-                                       if (!auth_settings_verify(auth))
-                                               return FALSE;
-                               }
-                               ns = server->namespaces;
-                               for (; ns != NULL; ns = ns->next) {
-                                       if (!namespace_settings_verify(server, ns))
-                                               return FALSE;
-                               }
-                       }
-                       prev = server;
                }
        }
  
index 5a637a94367b21e95d3a595fa92eb8e9f9d59c74,b9f30eb121cff993edbc51eb3c742312260f5f42..03e711f81cf7a171d817fb70194f01a19c8be9f3
@@@ -80,38 -94,12 +94,13 @@@ struct master_settings 
        const char *mail_uid;
        const char *mail_gid;
  
+       const char *mail_plugins;
+       const char *imap_capability;
        const char *mail_location;
-       const char *mail_cache_fields;
-       const char *mail_never_cache_fields;
-       unsigned int mail_cache_min_mail_count;
-       unsigned int mailbox_idle_check_interval;
        bool mail_debug;
-       bool mail_full_filesystem_access;
-       unsigned int mail_max_keyword_length;
-       bool mail_save_crlf;
-       bool mmap_disable;
-       bool dotlock_use_excl;
-       bool fsync_disable;
-       bool mail_nfs_storage;
-       bool mail_nfs_index;
-       bool mailbox_list_index_disable;
-       const char *lock_method;
-       bool maildir_stat_dirs;
-       bool maildir_copy_with_hardlinks;
-       bool maildir_copy_preserve_filename;
 -      unsigned int umask;
 +      bool maildir_very_dirty_syncs;
-       const char *mbox_read_locks;
-       const char *mbox_write_locks;
-       unsigned int mbox_lock_timeout;
-       unsigned int mbox_dotlock_change_timeout;
-       unsigned int mbox_min_index_size;
-       bool mbox_dirty_syncs;
-       bool mbox_very_dirty_syncs;
-       bool mbox_lazy_writes;
-       unsigned int dbox_rotate_size;
-       unsigned int dbox_rotate_min_size;
-       unsigned int dbox_rotate_days;
 +      unsigned int dbox_purge_min_percentage;
        bool mail_drop_priv_before_exec;
  
        const char *mail_executable;
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
Simple merge
index 28b56d9b894535cedca3a788a4045dd7ce897707,5def08488d7f759466eead78a86ba570a92ce461..637324e35c5c4fde6cb06cb038208e8a238611b7
@@@ -176,10 -164,6 +178,10 @@@ struct client *client_create(int fd_in
          client->last_input = ioloop_time;
        client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
                                      client_idle_timeout, client);
-       if (!lock_session) {
++      if (!set->pop3_lock_session) {
 +              client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
 +                                              client_commit_timeout, client);
 +      }
  
        client->user = user;
  
                return NULL;
        }
  
-       if (!no_flag_updates && client->messages_count > 0)
++      if (!set->pop3_no_flag_updates && client->messages_count > 0)
 +              client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
 +
        i_assert(my_client == NULL);
        my_client = client;
  
index da08deeec53706290eb201548f7e931ac854bae7,181f4e3bf350b32a38f7bf75625710b8ea6274ed..ec4dc85cdefd6f6def60841b2d77052125bd52e8
@@@ -45,8 -43,12 +45,13 @@@ struct client 
        uoff_t byte_counter_offset;
  
        unsigned char *deleted_bitmask;
 +      unsigned char *seen_bitmask;
  
+       /* settings: */
+       const struct pop3_settings *set;
+       enum client_workarounds workarounds;
+       enum uidl_keys uidl_keymask;
        unsigned int disconnected:1;
        unsigned int deleted:1;
        unsigned int waiting_input:1;
index 4759b7a431145e01dc3cd5fc61d0f16c955ef875,62b88c34dcb0f7c6249e4ce4f474fc484c2e9cb1..a570ce3da957b404b3df1ef0ad4a4d81b47fa9a9
@@@ -461,12 -464,8 +463,12 @@@ static int cmd_rset(struct client *clie
                client->deleted_count = 0;
                client->deleted_size = 0;
        }
 +      if (client->seen_change_count > 0) {
 +              memset(client->seen_bitmask, 0, MSGS_BITMASK_SIZE(client));
 +              client->seen_change_count = 0;
 +      }
  
-       if (enable_last_command) {
+       if (client->set->pop3_enable_last) {
                /* remove all \Seen flags (as specified by RFC 1460) */
                search_args = pop3_search_build(client, 0);
                search_ctx = mailbox_search_init(client->trans,
diff --cc src/pop3/main.c
index c0bb229fca8ac1cd0e9924c0290676cb49553b2a,67a6de6b0a8656c1c9ffed37641dbe1cb75e146f..f4a261ea1ced4fd7e474c1a23ce1c04c7e8c2b6a
@@@ -44,15 -44,7 +44,7 @@@ static struct module *modules = NULL
  static char log_prefix[128]; /* syslog() needs this to be permanent */
  static struct io *log_io = NULL;
  
- enum client_workarounds client_workarounds = 0;
- bool enable_last_command = FALSE;
- bool no_flag_updates = FALSE;
- bool reuse_xuidl = FALSE;
- bool lock_session = FALSE;
- const char *uidl_format, *logout_format;
- enum uidl_keys uidl_keymask;
 -static void sig_die(int signo, void *context ATTR_UNUSED)
 +static void sig_die(const siginfo_t *si, void *context ATTR_UNUSED)
  {
        /* warn about being killed because of some signal, except SIGINT (^C)
           which is too common at least while testing :) */
@@@ -171,21 -157,24 +164,25 @@@ static void main_preinit(const struct p
        /* Log file or syslog opening probably requires roots */
        open_logfile();
  
-       /* Load the plugins before chrooting. Their init() is called later. */
-       if (getenv("MAIL_PLUGINS") != NULL) {
-               const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+         mail_storage_init();
+       mail_storage_register_all();
+       mailbox_list_register_all();
  
-               if (plugin_dir == NULL)
-                       plugin_dir = MODULEDIR"/pop3";
-               modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
-                                         TRUE, version);
-       }
+       /* read settings after registering storages so they can have their
+          own setting definitions too */
+       pop3_settings_read(set_r, user_set_r);
+       /* Load the plugins before chrooting. Their init() is called later. */
+       modules = *(*set_r)->mail_plugins == '\0' ? NULL :
+               module_dir_load((*set_r)->mail_plugin_dir,
+                               (*set_r)->mail_plugins, TRUE, version);
  
        restrict_access_by_env(!IS_STANDALONE());
 +      restrict_access_allow_coredumps(TRUE);
  }
  
- static bool main_init(void)
+ static bool main_init(const struct pop3_settings *set,
+                     const struct mail_user_settings *user_set)
  {
        struct mail_user *user;
        struct client *client;
        if (getenv("USER") == NULL)
                i_fatal("USER environment missing");
  
-       if (getenv("DEBUG") != NULL) {
+       if (set->mail_debug) {
 -              i_info("Effective uid=%s, gid=%s",
 -                     dec2str(geteuid()), dec2str(getegid()));
 +              const char *home;
 +
 +              home = getenv("HOME");
 +              i_info("Effective uid=%s, gid=%s, home=%s",
 +                     dec2str(geteuid()), dec2str(getegid()),
 +                     home != NULL ? home : "(none)");
        }
  
-       if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
+       if (set->shutdown_clients) {
                /* If master dies, the log fd gets closed and we'll quit */
                log_io = io_add(STDERR_FILENO, IO_ERROR,
                                log_error_callback, NULL);