From: Timo Sirainen Date: Fri, 3 Apr 2009 23:00:52 +0000 (-0400) Subject: Merged configuration rewrite. X-Git-Tag: 2.0.alpha1~1037 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fe813f74aaccb12f38e1bd9cd338c6a37fa646e5;p=thirdparty%2Fdovecot%2Fcore.git Merged configuration rewrite. --HG-- branch : HEAD --- fe813f74aaccb12f38e1bd9cd338c6a37fa646e5 diff --cc TODO index e964e1f6c0,b802b3b03b..c8e6ccb085 --- a/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 src/auth/mech-gssapi.c index a2b8c8aae3,af8eca1ce8..cd2847f76b --- a/src/auth/mech-gssapi.c +++ b/src/auth/mech-gssapi.c @@@ -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 diff --cc src/auth/passdb-ldap.c index e28287780f,08a6c9d428..60bbd8992d --- a/src/auth/passdb-ldap.c +++ b/src/auth/passdb-ldap.c @@@ -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", diff --cc src/auth/passdb.c index 1be5a27483,671b2b0650..e7ea725110 --- a/src/auth/passdb.c +++ b/src/auth/passdb.c @@@ -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); + 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->verbose_debug_passwords) { + if (auth_request->auth->set->debug_passwords) { auth_request_log_info(auth_request, "password", - "Generating %s from user %s password %s", - wanted_scheme, auth_request->original_username, - 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); diff --cc src/auth/passdb.h index 94cc36a7ec,123f0f62fd..7b8b3f46fa --- a/src/auth/passdb.h +++ b/src/auth/passdb.h @@@ -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); diff --cc src/deliver/auth-client.c index d5425e3fab,d5425e3fab..c799ff9ea8 --- a/src/deliver/auth-client.c +++ b/src/deliver/auth-client.c @@@ -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; @@@ -102,7 -102,7 +103,7 @@@ } 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 && @@@ -116,7 -116,7 +117,7 @@@ 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)); @@@ -124,13 -124,13 +125,12 @@@ 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); diff --cc src/deliver/auth-client.h index 2cc3cb0118,2cc3cb0118..1d9dbe1d8e --- a/src/deliver/auth-client.h +++ b/src/deliver/auth-client.h @@@ -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); diff --cc src/deliver/deliver-settings.c index 0000000000,8e1cb20e07..4b82283a4e mode 000000,100644..100644 --- a/src/deliver/deliver-settings.c +++ b/src/deliver/deliver-settings.c @@@ -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 + #include + + #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; + + } diff --cc src/deliver/deliver-settings.h index 0000000000,103fe2fbe1..facd4efd34 mode 000000,100644..100644 --- a/src/deliver/deliver-settings.h +++ b/src/deliver/deliver-settings.h @@@ -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 diff --cc src/deliver/deliver.c index 55adc44d97,658b026300..be5dfbcd60 --- a/src/deliver/deliver.c +++ b/src/deliver/deliver.c @@@ -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; @@@ -1004,18 -663,9 +679,10 @@@ } 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); @@@ -1065,62 -708,40 +725,41 @@@ } 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); @@@ -1185,21 -806,21 +824,18 @@@ } 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 || @@@ -1212,11 -833,11 +848,11 @@@ /* 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 */ diff --cc src/deliver/duplicate.c index f05e7fc616,f05e7fc616..45884a5628 --- a/src/deliver/duplicate.c +++ b/src/deliver/duplicate.c @@@ -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) diff --cc src/deliver/duplicate.h index c31402c71c,c31402c71c..4f220ae47e --- a/src/deliver/duplicate.h +++ b/src/deliver/duplicate.h @@@ -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 diff --cc src/deliver/mail-send.c index 41d89f7cb5,41d89f7cb5..fbdf6f98b7 --- a/src/deliver/mail-send.c +++ b/src/deliver/mail-send.c @@@ -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); } diff --cc src/imap/Makefile.am index 386301c0ec,80b3777df6..e302bece79 --- a/src/imap/Makefile.am +++ b/src/imap/Makefile.am @@@ -72,7 -75,8 +75,8 @@@ imap_SOURCES = imap-fetch.c \ imap-fetch-body.c \ imap-search.c \ + imap-search-args.c \ + imap-settings.c \ - imap-sort.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-search-args.h \ + imap-settings.h \ - imap-sort.h \ imap-status.h \ imap-sync.h diff --cc src/imap/main.c index af4815067d,153d6b1861..db5f8db1a5 --- a/src/imap/main.c +++ b/src/imap/main.c @@@ -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; @@@ -176,6 -169,8 +177,8 @@@ 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) { diff --cc src/lib-storage/index/cydir/cydir-save.c index ca09bd70f4,bb1e8f1a79..0528815b64 --- a/src/lib-storage/index/cydir/cydir-save.c +++ b/src/lib-storage/index/cydir/cydir-save.c @@@ -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); diff --cc src/lib-storage/index/cydir/cydir-storage.c index 26a6d7316b,995505cf70..0ebadc7842 --- a/src/lib-storage/index/cydir/cydir-storage.c +++ b/src/lib-storage/index/cydir/cydir-storage.c @@@ -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)); @@@ -47,13 -47,13 +45,13 @@@ 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); diff --cc src/lib-storage/index/dbox/Makefile.am index 1225bda5bd,05e5d53902..0bbf70349b --- a/src/lib-storage/index/dbox/Makefile.am +++ b/src/lib-storage/index/dbox/Makefile.am @@@ -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 \ @@@ -25,9 -25,9 +27,10 @@@ 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 diff --cc src/lib-storage/index/dbox/dbox-file.c index 7e93895531,8a415880d2..44234d3592 --- a/src/lib-storage/index/dbox/dbox-file.c +++ b/src/lib-storage/index/dbox/dbox-file.c @@@ -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; } } @@@ -964,11 -1299,9 +965,10 @@@ (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; } diff --cc src/lib-storage/index/dbox/dbox-map.c index 82fdbce0ff,0000000000..0ad091c272 mode 100644,000000..100644 --- a/src/lib-storage/index/dbox/dbox-map.c +++ b/src/lib-storage/index/dbox/dbox-map.c @@@ -1,1108 -1,0 +1,1109 @@@ +/* 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 | - index_storage_get_index_open_flags(storage); ++ 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 >= - map->storage->purge_min_percentage) ++ 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 >= 100) { ++ 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); + } + } - if (map->storage->purge_min_percentage > 0 && ++ 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 */ - } else if (append_offset + mail_size > 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 (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]); - if (append_offset + mail_size <= map->storage->rotate_size && ++ 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 */ - stamp = day_begin_stamp(map->storage->rotate_days); ++ 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. */ - if (offset + size + mail_size >= map->storage->rotate_size) ++ 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->rotate_size == 0 ? ++ 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; +} diff --cc src/lib-storage/index/dbox/dbox-save.c index 45df36e195,738f80f72c..724daa6e2b --- a/src/lib-storage/index/dbox/dbox-save.c +++ b/src/lib-storage/index/dbox/dbox-save.c @@@ -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); diff --cc src/lib-storage/index/dbox/dbox-settings.c index 0000000000,9f87da4edc..f66fe0ec8c mode 000000,100644..100644 --- a/src/lib-storage/index/dbox/dbox-settings.c +++ b/src/lib-storage/index/dbox/dbox-settings.c @@@ -1,0 -1,45 +1,47 @@@ + /* 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 + + #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_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; + } diff --cc src/lib-storage/index/dbox/dbox-settings.h index 0000000000,58a473c867..6a1f389835 mode 000000,100644..100644 --- a/src/lib-storage/index/dbox/dbox-settings.h +++ b/src/lib-storage/index/dbox/dbox-settings.h @@@ -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 diff --cc src/lib-storage/index/dbox/dbox-storage.c index a409ffe22d,7716a06a7b..cb0c6e1b4c --- a/src/lib-storage/index/dbox/dbox-storage.c +++ b/src/lib-storage/index/dbox/dbox-storage.c @@@ -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) @@@ -147,44 -132,7 +144,12 @@@ 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; @@@ -199,21 -147,7 +164,21 @@@ 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 diff --cc src/lib-storage/index/dbox/dbox-storage.h index b5f96c0bb9,d224cc85d2..332f818227 --- a/src/lib-storage/index/dbox/dbox-storage.h +++ b/src/lib-storage/index/dbox/dbox-storage.h @@@ -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; + /* 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; + - uoff_t rotate_size, rotate_min_size; - unsigned int rotate_days; - unsigned int max_open_files; - unsigned int purge_min_percentage; + ARRAY_DEFINE(open_files, struct dbox_file *); + + unsigned int sync_rebuild:1; + unsigned int have_multi_msgs:1; }; struct dbox_mail_index_record { diff --cc src/lib-storage/index/dbox/dbox-sync-rebuild.c index 109a9efb74,0e421ea15b..dacc980e5b --- a/src/lib-storage/index/dbox/dbox-sync-rebuild.c +++ b/src/lib-storage/index/dbox/dbox-sync-rebuild.c @@@ -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) diff --cc src/lib-storage/index/index-storage.c index 1f2050be5e,6b11fa8e9c..1b62cada54 --- a/src/lib-storage/index/index-storage.c +++ b/src/lib-storage/index/index-storage.c @@@ -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) { diff --cc src/lib-storage/index/index-storage.h index 25c7022cf5,3faf74d4cf..1b13583b5e --- a/src/lib-storage/index/index-storage.h +++ b/src/lib-storage/index/index-storage.h @@@ -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 { diff --cc src/lib-storage/index/maildir/maildir-copy.c index 3f18ca8dd3,050abb8181..56ac217c06 --- a/src/lib-storage/index/maildir/maildir-copy.c +++ b/src/lib-storage/index/maildir/maildir-copy.c @@@ -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 */ } diff --cc src/lib-storage/index/maildir/maildir-save.c index 1a754f4b61,98b414b2c8..e5a76bade1 --- a/src/lib-storage/index/maildir/maildir-save.c +++ b/src/lib-storage/index/maildir/maildir-save.c @@@ -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) { diff --cc src/lib-storage/index/maildir/maildir-settings.c index 0000000000,01cbe9af80..b08db7db12 mode 000000,100644..100644 --- a/src/lib-storage/index/maildir/maildir-settings.c +++ b/src/lib-storage/index/maildir/maildir-settings.c @@@ -1,0 -1,44 +1,46 @@@ + /* 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 + + #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_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; + } + diff --cc src/lib-storage/index/maildir/maildir-settings.h index 0000000000,37355a7cd0..e74f0615f5 mode 000000,100644..100644 --- a/src/lib-storage/index/maildir/maildir-settings.h +++ b/src/lib-storage/index/maildir/maildir-settings.h @@@ -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 diff --cc src/lib-storage/index/maildir/maildir-storage.c index aa67b8aa14,011ae3ef09..35b7094e2f --- a/src/lib-storage/index/maildir/maildir-storage.c +++ b/src/lib-storage/index/maildir/maildir-storage.c @@@ -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; } diff --cc src/lib-storage/index/maildir/maildir-sync.c index 756f31dedc,2999619a38..e197050d8f --- a/src/lib-storage/index/maildir/maildir-sync.c +++ b/src/lib-storage/index/maildir/maildir-sync.c @@@ -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; @@@ -585,7 -566,7 +586,8 @@@ 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); } diff --cc src/lib-storage/index/maildir/maildir-uidlist.c index eb74a68637,0f1ea95f2f..b8bddef275 --- a/src/lib-storage/index/maildir/maildir-uidlist.c +++ b/src/lib-storage/index/maildir/maildir-uidlist.c @@@ -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; } } diff --cc src/lib-storage/index/mbox/mbox-save.c index 82a3b69ef0,cceefeeae4..f98049c906 --- a/src/lib-storage/index/mbox/mbox-save.c +++ b/src/lib-storage/index/mbox/mbox-save.c @@@ -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; diff --cc src/lib-storage/index/mbox/mbox-storage.c index ecd05613b9,9dfd875544..ece0237fe7 --- a/src/lib-storage/index/mbox/mbox-storage.c +++ b/src/lib-storage/index/mbox/mbox-storage.c @@@ -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; } diff --cc src/lib-storage/index/shared/shared-storage.c index 943e21d408,2cc8dd800b..612935ec81 --- a/src/lib-storage/index/shared/shared-storage.c +++ b/src/lib-storage/index/shared/shared-storage.c @@@ -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); + ns->flags |= NAMESPACE_FLAG_UNUSABLE; + } - if (mail_storage_create(ns, NULL, str_c(location), _storage->flags, - _storage->lock_method, &error) < 0) { + + ns_set = p_new(user->pool, struct mail_namespace_settings, 1); + ns_set->type = "shared"; + ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep); + ns_set->prefix = ns->prefix; + ns_set->location = p_strdup(user->pool, str_c(location)); + ns_set->hidden = TRUE; + ns_set->list = "yes"; + ns->set = ns_set; + + if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) { mail_storage_set_critical(_storage, "Namespace '%s': %s", ns->prefix, error); mail_namespace_destroy(ns); diff --cc src/lib-storage/list/index-mailbox-list.c index 657f00b665,88c7950cfb..4d0681a89c --- a/src/lib-storage/list/index-mailbox-list.c +++ b/src/lib-storage/list/index-mailbox-list.c @@@ -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. */ diff --cc src/lib-storage/mail-namespace.c index 598ebf79b0,c7c2c66046..204c42b334 --- a/src/lib-storage/mail-namespace.c +++ b/src/lib-storage/mail-namespace.c @@@ -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'; @@@ -176,13 -192,19 +187,13 @@@ } 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) { diff --cc src/lib-storage/mail-namespace.h index 8073aa33ce,ff2d02c196..f57ba7e727 --- a/src/lib-storage/mail-namespace.h +++ b/src/lib-storage/mail-namespace.h @@@ -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. */ diff --cc src/lib-storage/mail-storage-private.h index 50a6a50bb3,aefd9a913f..221934ab35 --- a/src/lib-storage/mail-storage-private.h +++ b/src/lib-storage/mail-storage-private.h @@@ -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; diff --cc src/lib-storage/mail-storage-settings.c index 0000000000,8ad083592d..34e04e545c mode 000000,100644..100644 --- a/src/lib-storage/mail-storage-settings.c +++ b/src/lib-storage/mail-storage-settings.c @@@ -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 + + 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); + } + + /* */ + 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; + } + /* */ diff --cc src/lib-storage/mail-storage.c index b0ebdfffec,6d1c741d07..121e6358ff --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@@ -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) @@@ -234,10 -213,6 +213,7 @@@ 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); } +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; +} + - enum mailbox_list_flags - mail_storage_get_list_flags(enum mail_storage_flags storage_flags) - { - enum mailbox_list_flags list_flags = 0; - - if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0) - list_flags |= MAILBOX_LIST_FLAG_DEBUG; - if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0) - list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS; - if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0) - list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL; - if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0) - list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH; - return list_flags; - } - bool mail_storage_set_error_from_errno(struct mail_storage *storage) { const char *error_string; diff --cc src/lib-storage/mail-user.c index 84831aec58,4931553465..9a749ae0d9 --- a/src/lib-storage/mail-user.c +++ b/src/lib-storage/mail-user.c @@@ -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 diff --cc src/lib-storage/mail-user.h index 04c15a61b0,db1eb3ac1d..bfd97f72ec --- a/src/lib-storage/mail-user.h +++ b/src/lib-storage/mail-user.h @@@ -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. */ diff --cc src/lib-storage/mailbox-list.c index 252455c6a9,75d7b0ac9a..aee4c1946e --- a/src/lib-storage/mailbox-list.c +++ b/src/lib-storage/mailbox-list.c @@@ -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; } +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, +void mailbox_list_get_permissions(struct mailbox_list *list, const char *name, mode_t *mode_r, gid_t *gid_r) { const char *path; @@@ -345,10 -310,10 +352,10 @@@ 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); } diff --cc src/lib-storage/mailbox-list.h index 23b1f348e8,92a3181b23..d6cf93a491 --- a/src/lib-storage/mailbox-list.h +++ b/src/lib-storage/mailbox-list.h @@@ -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). */ diff --cc src/master/auth-process.c index a2d47b1dbd,bdd790d666..50dacf9fa1 --- a/src/master/auth-process.c +++ b/src/master/auth-process.c @@@ -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); diff --cc src/master/child-process.c index 138edfd628,e2092a5022..0851ee7b1a --- a/src/master/child-process.c +++ b/src/master/child-process.c @@@ -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; diff --cc src/master/mail-process.c index d21ec38f14,a5d985dab8..ed7e848d1d --- a/src/master/mail-process.c +++ b/src/master/mail-process.c @@@ -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; @@@ -578,8 -301,7 +306,8 @@@ } 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++) { @@@ -758,12 -474,22 +480,12 @@@ 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); @@@ -891,21 -607,10 +608,21 @@@ 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(); + /* move the client socket into stdin and stdout fds, log to stderr */ - if (dup2(dump_capability ? null_fd : request->fd, 0) < 0) ++ 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 diff --cc src/master/main.c index f431a7e7cf,c2913785d1..147155044e --- a/src/master/main.c +++ b/src/master/main.c @@@ -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) diff --cc src/master/master-settings.c index 009169d381,a8b3c42159..b3ec73ef66 --- a/src/master/master-settings.c +++ b/src/master/master-settings.c @@@ -28,148 -30,184 +30,183 @@@ # include #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) "", @@@ -229,49 -253,16 +252,17 @@@ 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, @@@ -299,78 -273,136 +273,136 @@@ /* .. */ }; - 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; @@@ -982,22 -786,21 +801,6 @@@ 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; } } diff --cc src/master/master-settings.h index 5a637a9436,b9f30eb121..03e711f81c --- a/src/master/master-settings.h +++ b/src/master/master-settings.h @@@ -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; diff --cc src/pop3/client.c index 28b56d9b89,5def08488d..637324e35c --- a/src/pop3/client.c +++ b/src/pop3/client.c @@@ -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; @@@ -215,9 -199,6 +217,9 @@@ 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; diff --cc src/pop3/client.h index da08deeec5,181f4e3bf3..ec4dc85cde --- a/src/pop3/client.h +++ b/src/pop3/client.h @@@ -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; diff --cc src/pop3/commands.c index 4759b7a431,62b88c34dc..a570ce3da9 --- a/src/pop3/commands.c +++ b/src/pop3/commands.c @@@ -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 c0bb229fca,67a6de6b0a..f4a261ea1c --- a/src/pop3/main.c +++ b/src/pop3/main.c @@@ -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; @@@ -201,16 -190,12 +198,16 @@@ 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);