+ - 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):
{
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
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",
/* we can generate anything out of plaintext passwords */
plaintext = t_strndup(*credentials_r, *size_r);
- if (auth_request->auth->verbose_debug_passwords) {
+ username = auth_request->original_username;
+ if (!auth_request->domain_is_realm &&
+ strchr(username, '@') != NULL) {
+ /* domain must not be used as realm. add the @realm. */
+ username = t_strconcat(username, "@",
+ auth_request->realm, NULL);
+ }
+ if (auth_request->auth->set->debug_passwords) {
auth_request_log_info(auth_request, "password",
- "Generating %s from user %s password %s",
- wanted_scheme, auth_request->original_username,
- plaintext);
+ "Generating %s from user '%s', password '%s'",
+ wanted_scheme, username, plaintext);
}
- if (!password_generate(plaintext,
- auth_request->original_username,
+ if (!password_generate(plaintext, username,
wanted_scheme, credentials_r, size_r)) {
auth_request_log_error(auth_request, "password",
"Requested unknown scheme %s", wanted_scheme);
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);
#include "ostream.h"
#include "env-util.h"
#include "restrict-access.h"
++#include "deliver.h"
#include "auth-client.h"
#include "auth-master.h"
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;
i_error("userdb(%s) returned 0 as gid", user);
return -1;
} else if (reply->gid == (gid_t)-1) {
-- if (getenv("MAIL_GID") != NULL) {
-- if (!parse_gid(getenv("MAIL_GID"), &reply->gid) ||
++ if (*deliver_set->mail_gid != '\0') {
++ if (!parse_gid(deliver_set->mail_gid, &reply->gid) ||
reply->gid == 0) {
i_error("mail_gid setting is invalid");
return -1;
}
if (reply->chroot == NULL)
-- reply->chroot = getenv("MAIL_CHROOT");
++ reply->chroot = deliver_set->mail_chroot;
if (reply->chroot != NULL) {
len = strlen(reply->chroot);
if (len > 2 && strcmp(reply->chroot + len - 2, "/.") == 0 &&
if (reply->home != NULL)
env_put(t_strconcat("HOME=", reply->home, NULL));
-- extra_groups = getenv("MAIL_EXTRA_GROUPS");
++ extra_groups = deliver_set->mail_access_groups;
if (extra_groups != NULL) {
env_put(t_strconcat("RESTRICT_SETEXTRAGROUPS=",
extra_groups, NULL));
return 0;
}
--int auth_client_lookup_and_restrict(const char *auth_socket,
++int auth_client_lookup_and_restrict(const char *auth_socket, bool debug,
const char **user, uid_t euid, pool_t pool,
ARRAY_TYPE(const_string) *extra_fields_r)
{
struct auth_master_connection *conn;
struct auth_user_reply reply;
-- bool debug = getenv("DEBUG") != NULL;
int ret = EX_TEMPFAIL;
conn = auth_master_init(auth_socket, debug);
#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);
--- /dev/null
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+ #include "deliver.h"
+ #include "array.h"
+ #include "hostpid.h"
+ #include "istream.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "deliver-settings.h"
+
+ #include <stddef.h>
+ #include <stdlib.h>
+
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct deliver_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+ { SET_DEFLIST, name, offsetof(struct deliver_settings, field), defines }
+
+ static struct setting_define deliver_setting_defines[] = {
+ DEF(SET_STR, base_dir),
+ DEF(SET_STR, log_path),
+ DEF(SET_STR, info_log_path),
+ DEF(SET_STR, log_timestamp),
+ DEF(SET_STR, syslog_facility),
+ DEF(SET_BOOL, version_ignore),
+ DEF(SET_UINT, umask),
+
+ DEF(SET_STR, mail_plugins),
+ DEF(SET_STR, mail_plugin_dir),
+
++ DEF(SET_STR, mail_uid),
++ DEF(SET_STR, mail_gid),
++ DEF(SET_STR, mail_chroot),
++ DEF(SET_STR, mail_access_groups),
++
+ DEF(SET_STR, postmaster_address),
+ DEF(SET_STR, hostname),
+ DEF(SET_STR, sendmail_path),
+ DEF(SET_STR, rejection_subject),
+ DEF(SET_STR, rejection_reason),
+ DEF(SET_STR, auth_socket_path),
+ DEF(SET_STR, deliver_log_format),
+ DEF(SET_BOOL, quota_full_tempfail),
+
+ { SET_STRLIST, "plugin", offsetof(struct deliver_settings, plugin_envs), NULL },
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ static struct deliver_settings deliver_default_settings = {
+ MEMBER(base_dir) PKG_RUNDIR,
+ MEMBER(log_path) "",
+ MEMBER(info_log_path) "",
+ MEMBER(log_timestamp) DEFAULT_FAILURE_STAMP_FORMAT,
+ MEMBER(syslog_facility) "mail",
+ MEMBER(version_ignore) FALSE,
+ MEMBER(umask) 0077,
+
+ MEMBER(mail_plugins) "",
+ MEMBER(mail_plugin_dir) MODULEDIR"/lda",
+
++ MEMBER(mail_uid) "",
++ MEMBER(mail_gid) "",
++ MEMBER(mail_chroot) "",
++ MEMBER(mail_access_groups) "",
++
+ MEMBER(postmaster_address) "",
+ MEMBER(hostname) "",
+ MEMBER(sendmail_path) "/usr/lib/sendmail",
+ MEMBER(rejection_subject) "Rejected: %s",
+ MEMBER(rejection_reason)
+ "Your message to <%t> was automatically rejected:%n%r",
+ MEMBER(auth_socket_path) "auth-master",
+ MEMBER(deliver_log_format) "msgid=%m: %$",
+ MEMBER(quota_full_tempfail) FALSE
+ };
+
+ struct setting_parser_info deliver_setting_parser_info = {
+ MEMBER(defines) deliver_setting_defines,
+ MEMBER(defaults) &deliver_default_settings,
+
+ MEMBER(parent) NULL,
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct deliver_settings)
+ };
+
+ static pool_t settings_pool = NULL;
+
+ static void fix_base_path(struct deliver_settings *set, const char **str)
+ {
+ if (*str != NULL && **str != '\0' && **str != '/') {
+ *str = p_strconcat(settings_pool,
+ set->base_dir, "/", *str, NULL);
+ }
+ }
+
+ struct setting_parser_context *
+ deliver_settings_read(struct deliver_settings **set_r,
+ struct mail_user_settings **user_set_r)
+ {
+ static const struct setting_parser_info *roots[] = {
+ &deliver_setting_parser_info,
+ &mail_user_setting_parser_info
+ };
+ void **sets;
+ struct deliver_settings *deliver_set;
+ struct setting_parser_context *parser;
+
+ if (settings_pool == NULL)
+ settings_pool = pool_alloconly_create("deliver settings", 1024);
+ else
+ p_clear(settings_pool);
+
+ mail_storage_namespace_defines_init(settings_pool);
+
+ parser = settings_parser_init_list(settings_pool,
+ roots, N_ELEMENTS(roots),
+ SETTINGS_PARSER_FLAG_IGNORE_UNKNOWN_KEYS);
+
+ if (settings_parse_environ(parser) < 0) {
+ i_fatal_status(EX_CONFIG, "Error reading configuration: %s",
+ settings_parser_get_error(parser));
+ }
+
+ sets = settings_parser_get_list(parser);
+
+ deliver_set = sets[0];
+ if (*deliver_set->hostname == '\0')
+ deliver_set->hostname = my_hostname;
+ fix_base_path(deliver_set, &deliver_set->auth_socket_path);
+
+ if (*deliver_set->postmaster_address == '\0') {
+ i_fatal_status(EX_CONFIG,
+ "postmaster_address setting not given");
+ }
+
+ *set_r = deliver_set;
+ *user_set_r = sets[1];
+ return parser;
+ }
+
+ void deliver_settings_add(struct setting_parser_context *parser,
+ const ARRAY_TYPE(const_string) *extra_fields)
+ {
+ const char *const *str, *p, *line;
+ unsigned int i, count;
+
+ str = array_get(extra_fields, &count);
+ for (i = 0; i < count; i++) T_BEGIN {
+ p = strchr(str[i], '=');
+ if (p != NULL)
+ line = str[i];
+ else
+ line = t_strconcat(str[i], "=yes", NULL);
+ if (settings_parse_line(parser, str[i]) < 0) {
+ i_fatal_status(EX_CONFIG,
+ "Invalid userdb input '%s': %s", str[i],
+ settings_parser_get_error(parser));
+ }
+ } T_END;
+
+ }
--- /dev/null
+ #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
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 :) */
{
const char *config_path = DEFAULT_CONFIG_FILE;
const char *mailbox = "INBOX";
- const char *auth_socket;
- const char *home, *destaddr, *user, *value, *errstr, *path, *orig_user;
- const char *home, *destaddr, *user, *error, *path, *orig_user;
++ const char *home, *destaddr, *user, *errstr, *path, *orig_user;
ARRAY_TYPE(const_string) extra_fields = ARRAY_INIT;
+ struct setting_parser_context *parser;
struct mail_user *mail_user, *raw_mail_user;
struct mail_namespace *raw_ns;
+ struct mail_namespace_settings raw_ns_set;
struct mail_storage *storage;
struct mailbox *box;
struct raw_mailbox *raw_box;
struct istream *input;
struct mailbox_transaction_context *t;
struct mailbox_header_lookup_ctx *headers_ctx;
+ struct mail_user_settings *user_set;
+ const struct mail_storage_settings *mail_set;
struct mail *mail;
+ char cwd[PATH_MAX];
uid_t process_euid;
bool stderr_rejection = FALSE;
bool keep_environment = FALSE;
}
if (user_auth) {
- auth_socket = getenv("AUTH_SOCKET_PATH");
- if (auth_socket == NULL) {
- const char *base_dir = getenv("BASE_DIR");
- if (base_dir == NULL)
- base_dir = PKG_RUNDIR;
- auth_socket = t_strconcat(base_dir, "/auth-master",
- NULL);
- }
-
userdb_pool = pool_alloconly_create("userdb lookup replys", 512);
orig_user = user;
- ret = auth_client_lookup_and_restrict(auth_socket,
+ ret = auth_client_lookup_and_restrict(deliver_set->auth_socket_path,
++ mail_set->mail_debug,
&user, process_euid,
userdb_pool,
&extra_fields);
}
env_put(t_strconcat("USER=", user, NULL));
-
- value = getenv("UMASK");
- if (value == NULL || sscanf(value, "%i", &i) != 1 || i < 0)
- i = 0077;
- (void)umask(i);
-
- deliver_set->hostname = getenv("HOSTNAME");
- if (deliver_set->hostname == NULL)
- deliver_set->hostname = my_hostname;
- deliver_set->postmaster_address = getenv("POSTMASTER_ADDRESS");
- if (deliver_set->postmaster_address == NULL) {
- i_fatal_status(EX_CONFIG,
- "postmaster_address setting not given");
- }
- deliver_set->sendmail_path = getenv("SENDMAIL_PATH");
- if (deliver_set->sendmail_path == NULL)
- deliver_set->sendmail_path = DEFAULT_SENDMAIL_PATH;
- deliver_set->rejection_subject = getenv("REJECTION_SUBJECT");
- if (deliver_set->rejection_subject == NULL)
- deliver_set->rejection_subject = DEFAULT_MAIL_REJECTION_SUBJECT;
- deliver_set->rejection_reason = getenv("REJECTION_REASON");
- if (deliver_set->rejection_reason == NULL) {
- deliver_set->rejection_reason =
- DEFAULT_MAIL_REJECTION_HUMAN_REASON;
- }
- deliver_set->log_format = getenv("DELIVER_LOG_FORMAT");
- if (deliver_set->log_format == NULL)
- deliver_set->log_format = DEFAULT_LOG_FORMAT;
+ (void)umask(deliver_set->umask);
dict_drivers_register_builtin();
-- duplicate_init();
- mail_users_init(getenv("AUTH_SOCKET_PATH"), getenv("DEBUG") != NULL);
- mail_storage_init();
- mail_storage_register_all();
- mailbox_list_register_all();
++ duplicate_init(mail_set);
+ mail_users_init(deliver_set->auth_socket_path, mail_set->mail_debug);
module_dir_init(modules);
- mail_user = mail_user_init(user);
+ mail_user = mail_user_alloc(user, user_set);
mail_user_set_home(mail_user, home);
- if (mail_namespaces_init(mail_user) < 0)
- i_fatal("Namespace initialization failed");
+ mail_user_set_vars(mail_user, geteuid(), "deliver", NULL, NULL);
- if (mail_user_init(mail_user, &error) < 0)
- i_fatal("Mail user initialization failed: %s", error);
- if (mail_namespaces_init(mail_user, &error) < 0)
- i_fatal("Namespace initialization failed: %s", error);
++ if (mail_user_init(mail_user, &errstr) < 0)
++ i_fatal("Mail user initialization failed: %s", errstr);
++ if (mail_namespaces_init(mail_user, &errstr) < 0)
++ i_fatal("Namespace initialization failed: %s", errstr);
/* create a separate mail user for the internal namespace */
- raw_mail_user = mail_user_init(user);
- mail_user_set_home(raw_mail_user, NULL);
+ raw_mail_user = mail_user_alloc(user, user_set);
+ mail_user_set_home(raw_mail_user, "/");
- if (mail_user_init(raw_mail_user, &error) < 0)
- i_fatal("Raw user initialization failed: %s", error);
++ if (mail_user_init(raw_mail_user, &errstr) < 0)
++ i_fatal("Raw user initialization failed: %s", errstr);
+
+ settings_parser_deinit(&parser);
+
+ memset(&raw_ns_set, 0, sizeof(raw_ns_set));
+ raw_ns_set.location = "/tmp";
+
raw_ns = mail_namespaces_init_empty(raw_mail_user);
raw_ns->flags |= NAMESPACE_FLAG_INTERNAL;
-
- if (mail_storage_create(raw_ns, "raw", "/tmp",
- MAIL_STORAGE_FLAG_FULL_FS_ACCESS,
- FILE_LOCK_METHOD_FCNTL, &errstr) < 0)
+ raw_ns->set = &raw_ns_set;
- if (mail_storage_create(raw_ns, "raw", 0, &error) < 0)
- i_fatal("Couldn't create internal raw storage: %s", error);
++ if (mail_storage_create(raw_ns, "raw", 0, &errstr) < 0)
+ i_fatal("Couldn't create internal raw storage: %s", errstr);
if (path == NULL) {
- input = create_raw_stream(0, &mtime);
+ const char *prefix = mail_user_get_temp_prefix(mail_user);
+ input = create_raw_stream(prefix, 0, &mtime);
box = mailbox_open(&raw_ns->storage, "Dovecot Delivery Mail",
input, MAILBOX_OPEN_NO_INDEX_FILES);
i_stream_unref(&input);
}
if (ret < 0 ) {
-- const char *error_string;
-- enum mail_error error;
--
if (storage == NULL) {
/* This shouldn't happen */
i_error("BUG: Saving failed for unknown storage");
return EX_TEMPFAIL;
}
-- error_string = mail_storage_get_last_error(storage, &error);
++ errstr = mail_storage_get_last_error(storage, &error);
if (stderr_rejection) {
/* write to stderr also for tempfails so that MTA
can log the reason if it wants to. */
-- fprintf(stderr, "%s\n", error_string);
++ fprintf(stderr, "%s\n", errstr);
}
if (error != MAIL_ERROR_NOSPACE ||
/* we'll have to reply with permanent failure */
deliver_log(mail, "rejected: %s",
-- str_sanitize(error_string, 512));
++ str_sanitize(errstr, 512));
if (stderr_rejection)
return EX_NOPERM;
-- ret = mail_send_rejection(mail, user, error_string);
++ ret = mail_send_rejection(mail, user, errstr);
if (ret != 0)
return ret < 0 ? EX_TEMPFAIL : ret;
/* ok, rejection sent */
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)
#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);
void duplicate_flush(void);
--void duplicate_init(void);
++void duplicate_init(const struct mail_storage_settings *set);
void duplicate_deinit(void);
#endif
#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"
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));
}
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);
}
imap-fetch.c \
imap-fetch-body.c \
imap-search.c \
- imap-sort.c \
+ imap-search-args.c \
+ imap-settings.c \
imap-status.c \
imap-sync.c \
mail-storage-callbacks.c \
imap-expunge.h \
imap-fetch.h \
imap-search.h \
- imap-sort.h \
+ imap-search-args.h \
+ imap-settings.h \
imap-status.h \
imap-sync.h
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 :) */
/* Log file or syslog opening probably requires roots */
open_logfile();
- /* Load the plugins before chrooting. Their init() is called later. */
- if (getenv("MAIL_PLUGINS") != NULL) {
- const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+ mail_storage_init();
+ mail_storage_register_all();
+ mailbox_list_register_all();
- if (plugin_dir == NULL)
- plugin_dir = MODULEDIR"/imap";
- modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
- TRUE, version);
- }
+ /* read settings after registering storages so they can have their
+ own setting definitions too */
+ imap_settings_read(set_r, user_set_r);
+
+ /* Load the plugins before chrooting. Their init() is called later. */
+ modules = *(*set_r)->mail_plugins == '\0' ? NULL :
+ module_dir_load((*set_r)->mail_plugin_dir,
+ (*set_r)->mail_plugins, TRUE, version);
restrict_access_by_env(!IS_STANDALONE());
+ restrict_access_allow_coredumps(TRUE);
}
- static void main_init(void)
+ static void main_init(const struct imap_settings *set,
+ const struct mail_user_settings *user_set)
{
struct client *client;
struct ostream *output;
lib_signals_set_handler(SIGTERM, TRUE, sig_die, NULL);
lib_signals_ignore(SIGPIPE, TRUE);
lib_signals_ignore(SIGALRM, FALSE);
-
++
+ dump_capability = getenv("DUMP_CAPABILITY") != NULL;
username = getenv("USER");
if (username == NULL) {
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);
const char *data, struct mail_storage *storage,
const char **layout_r, const char **error_r)
{
- bool debug = (storage->flags & MAIL_STORAGE_FLAG_DEBUG) != 0;
- bool debug = storage->set->mail_debug;
--
*layout_r = "fs";
memset(list_set, 0, sizeof(*list_set));
if (data == NULL || *data == '\0' || *data == ':') {
/* we won't do any guessing for this format. */
-- if (debug)
++ if (storage->set->mail_debug)
i_info("cydir: mailbox location not given");
*error_r = "Root mail directory not given";
return -1;
}
-- if (debug)
++ if (storage->set->mail_debug)
i_info("cydir: data=%s", data);
return mailbox_list_settings_parse(data, list_set, storage->ns,
layout_r, NULL, error_r);
libstorage_dbox_a_SOURCES = \
dbox-file.c \
+ dbox-file-fix.c \
dbox-file-maildir.c \
- dbox-index.c \
dbox-mail.c \
+ dbox-map.c \
dbox-save.c \
+ dbox-settings.c \
dbox-sync.c \
dbox-sync-file.c \
dbox-sync-rebuild.c \
headers = \
dbox-file.h \
dbox-file-maildir.h \
- dbox-index.h \
+ dbox-map.h \
+ dbox-settings.h \
dbox-storage.h \
+ dbox-storage-rebuild.h \
dbox-sync.h
if INSTALL_HEADERS
}
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;
}
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;
}
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;
}
o_stream_unref(&output);
- if ((file->storage->storage.flags &
- MAIL_STORAGE_FLAG_FSYNC_DISABLE) == 0 && ret == 0) {
- if (!file->mbox->ibox.fsync_disable && ret == 0) {
++ if (!file->storage->storage.set->fsync_disable && ret == 0) {
if (fsync(out_fd) < 0) {
- i_error("fsync(%s) failed: %m", temp_path);
+ mail_storage_set_critical(&file->storage->storage,
+ "fsync(%s) failed: %m", temp_path);
ret = -1;
}
}
(void)unlink(temp_path);
return -1;
}
- if ((file->storage->storage.flags &
- MAIL_STORAGE_FLAG_FSYNC_DISABLE) == 0) {
- if (!file->mbox->ibox.fsync_disable) {
++ if (!file->storage->storage.set->fsync_disable) {
if (fdatasync_path(dest_dir) < 0) {
- i_error("fdatasync(%s) failed: %m", dest_dir);
+ mail_storage_set_critical(&file->storage->storage,
+ "fdatasync(%s) failed: %m", dest_dir);
(void)unlink(dest_path);
return -1;
}
--- /dev/null
- index_storage_get_index_open_flags(storage);
+/* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "hash.h"
+#include "ostream.h"
+#include "mkdir-parents.h"
+#include "dbox-storage.h"
+#include "dbox-file.h"
+#include "dbox-map-private.h"
+
+#define MAX_BACKWARDS_LOOKUPS 10
+
+struct dbox_map_transaction_context {
+ struct dbox_map *map;
+ struct mail_index_transaction *trans;
+ struct mail_index_sync_ctx *sync_ctx;
+
+ unsigned int changed:1;
+ unsigned int success:1;
+};
+
+void dbox_map_set_corrupted(struct dbox_map *map, const char *format, ...)
+{
+ va_list args;
+
+ va_start(args, format);
+ mail_storage_set_critical(&map->storage->storage,
+ "dbox map %s corrupted: %s",
+ map->index->filepath,
+ t_strdup_vprintf(format, args));
+ va_end(args);
+}
+
+struct dbox_map *dbox_map_init(struct dbox_storage *storage)
+{
+ struct dbox_map *map;
+
+ map = i_new(struct dbox_map, 1);
+ map->storage = storage;
+ map->index = mail_index_alloc(storage->storage_dir,
+ DBOX_GLOBAL_INDEX_PREFIX);
+ map->map_ext_id = mail_index_ext_register(map->index, "map",
+ sizeof(struct dbox_mail_index_map_header),
+ sizeof(struct dbox_mail_index_map_record),
+ sizeof(uint32_t));
+ map->ref_ext_id = mail_index_ext_register(map->index, "ref", 0,
+ sizeof(uint16_t), sizeof(uint16_t));
+ map->created_uid_validity = ioloop_time;
+ return map;
+}
+
+void dbox_map_deinit(struct dbox_map **_map)
+{
+ struct dbox_map *map = *_map;
+
+ *_map = NULL;
+
+ if (array_is_created(&map->ref0_file_ids))
+ array_free(&map->ref0_file_ids);
+ if (map->view != NULL)
+ mail_index_view_close(&map->view);
+ mail_index_free(&map->index);
+ i_free(map);
+}
+
+static int dbox_map_mkdir_storage(struct dbox_storage *storage)
+{
+ mode_t mode;
+ gid_t gid;
+
+ mailbox_list_get_dir_permissions(storage->storage.list, NULL,
+ &mode, &gid);
+ if (mkdir_parents_chown(storage->storage_dir, mode,
+ (uid_t)-1, gid) < 0 && errno != EEXIST) {
+ mail_storage_set_critical(&storage->storage,
+ "mkdir(%s) failed: %m", storage->storage_dir);
+ return -1;
+ }
+ return 0;
+}
+
+int dbox_map_open(struct dbox_map *map, bool create_missing)
+{
+ struct mail_storage *storage = &map->storage->storage;
+ enum mail_index_open_flags open_flags;
+ int ret;
+
+ if (map->view != NULL) {
+ /* already opened */
+ return 0;
+ }
+
+ open_flags = MAIL_INDEX_OPEN_FLAG_NEVER_IN_MEMORY |
- map->storage->purge_min_percentage)
++ mail_storage_settings_to_index_flags(storage->set);
+ if (create_missing) {
+ open_flags |= MAIL_INDEX_OPEN_FLAG_CREATE;
+ if (dbox_map_mkdir_storage(map->storage) < 0)
+ return -1;
+ }
+ ret = mail_index_open(map->index, open_flags, storage->lock_method);
+ if (ret < 0) {
+ mail_storage_set_internal_error(storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
+ if (ret == 0) {
+ /* index not found - for now just return failure */
+ return -1;
+ }
+
+ map->view = mail_index_view_open(map->index);
+ return 0;
+}
+
+int dbox_map_refresh(struct dbox_map *map)
+{
+ struct mail_index_view_sync_ctx *ctx;
+ bool delayed_expunges;
+
+ if (mail_index_refresh(map->view->index) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
+ ctx = mail_index_view_sync_begin(map->view,
+ MAIL_INDEX_VIEW_SYNC_FLAG_FIX_INCONSISTENT);
+ if (mail_index_view_sync_commit(&ctx, &delayed_expunges) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
+ return 0;
+}
+
+static int dbox_map_lookup_seq(struct dbox_map *map, uint32_t seq,
+ uint32_t *file_id_r, uoff_t *offset_r,
+ uoff_t *size_r)
+{
+ const struct dbox_mail_index_map_record *rec;
+ const void *data;
+ uint32_t uid;
+ bool expunged;
+
+ mail_index_lookup_ext(map->view, seq, map->map_ext_id,
+ &data, &expunged);
+ rec = data;
+
+ if (rec == NULL || rec->file_id == 0) {
+ mail_index_lookup_uid(map->view, seq, &uid);
+ dbox_map_set_corrupted(map, "file_id=0 for map_uid=%u", uid);
+ return -1;
+ }
+
+ *file_id_r = rec->file_id;
+ *offset_r = rec->offset;
+ *size_r = rec->size;
+ return 0;
+}
+
+static int
+dbox_map_get_seq(struct dbox_map *map, uint32_t map_uid, uint32_t *seq_r)
+{
+ if (!mail_index_lookup_seq(map->view, map_uid, seq_r)) {
+ /* not found - try again after a refresh */
+ if (dbox_map_refresh(map) < 0)
+ return -1;
+ if (!mail_index_lookup_seq(map->view, map_uid, seq_r))
+ return 0;
+ }
+ return 1;
+}
+
+int dbox_map_lookup(struct dbox_map *map, uint32_t map_uid,
+ uint32_t *file_id_r, uoff_t *offset_r)
+{
+ uint32_t seq;
+ uoff_t size;
+ int ret;
+
+ if (dbox_map_open(map, TRUE) < 0)
+ return -1;
+
+ if ((ret = dbox_map_get_seq(map, map_uid, &seq)) <= 0)
+ return ret;
+
+ if (dbox_map_lookup_seq(map, seq, file_id_r, offset_r, &size) < 0)
+ return -1;
+ return 1;
+}
+
+int dbox_map_view_lookup_rec(struct dbox_map *map, struct mail_index_view *view,
+ uint32_t seq, struct dbox_mail_lookup_rec *rec_r)
+{
+ const uint16_t *ref16_p;
+ const void *data;
+ bool expunged;
+
+ memset(rec_r, 0, sizeof(*rec_r));
+ mail_index_lookup_uid(view, seq, &rec_r->map_uid);
+
+ mail_index_lookup_ext(view, seq, map->map_ext_id, &data, &expunged);
+ if (data == NULL) {
+ dbox_map_set_corrupted(map, "missing map extension");
+ return -1;
+ }
+ memcpy(&rec_r->rec, data, sizeof(rec_r->rec));
+
+ mail_index_lookup_ext(view, seq, map->ref_ext_id, &data, &expunged);
+ if (data == NULL) {
+ dbox_map_set_corrupted(map, "missing ref extension");
+ return -1;
+ }
+ ref16_p = data;
+ rec_r->refcount = *ref16_p;
+ return 0;
+}
+
+int dbox_map_get_file_msgs(struct dbox_map *map, uint32_t file_id,
+ ARRAY_TYPE(dbox_map_file_msg) *recs)
+{
+ const struct mail_index_header *hdr;
+ struct dbox_mail_lookup_rec rec;
+ struct dbox_map_file_msg msg;
+ uint32_t seq;
+
+ if (dbox_map_refresh(map) < 0)
+ return -1;
+ hdr = mail_index_get_header(map->view);
+
+ memset(&msg, 0, sizeof(msg));
+ for (seq = 1; seq <= hdr->messages_count; seq++) {
+ if (dbox_map_view_lookup_rec(map, map->view, seq, &rec) < 0)
+ return -1;
+
+ if (rec.rec.file_id == file_id) {
+ msg.map_uid = rec.map_uid;
+ msg.offset = rec.rec.offset;
+ msg.refcount = rec.refcount;
+ array_append(recs, &msg, 1);
+ }
+ }
+ return 0;
+}
+
+struct dbox_file_size {
+ uoff_t file_size;
+ uoff_t ref0_size;
+};
+
+static void dbox_map_filter_zero_refs(struct dbox_map *map)
+{
+ ARRAY_TYPE(seq_range) new_ref0_file_ids;
+ struct hash_table *hash;
+ struct dbox_file_size *size;
+ struct seq_range_iter iter;
+ const struct mail_index_header *hdr;
+ const struct dbox_mail_index_map_record *rec;
+ const uint16_t *ref16_p;
+ const void *data;
+ uint32_t seq, file_id;
+ unsigned int i;
+ bool expunged;
+ pool_t pool;
+
+ pool = pool_alloconly_create("dbox zero ref count", 8*1024);
+ hash = hash_table_create(default_pool, pool, 0, NULL, NULL);
+
+ /* count file sizes */
+ hdr = mail_index_get_header(map->view);
+ for (seq = 1; seq <= hdr->messages_count; seq++) {
+ mail_index_lookup_ext(map->view, seq, map->map_ext_id,
+ &data, &expunged);
+ if (data == NULL || expunged)
+ continue;
+ rec = data;
+
+ if (!seq_range_exists(&map->ref0_file_ids, rec->file_id))
+ continue;
+
+ /* this file has at least some zero references. count how many
+ bytes it has in total and how much of it has refcount=0. */
+ mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
+ &data, &expunged);
+ if (data == NULL || expunged)
+ continue;
+ ref16_p = data;
+
+ size = hash_table_lookup(hash, POINTER_CAST(rec->file_id));
+ if (size == NULL) {
+ size = p_new(pool, struct dbox_file_size, 1);
+ hash_table_insert(hash, POINTER_CAST(rec->file_id),
+ size);
+ }
+ if (*ref16_p == 0)
+ size->ref0_size += rec->size;
+ if (size->file_size < rec->offset + rec->size)
+ size->file_size = rec->offset + rec->size;
+ }
+
+ /* now drop the files that don't have enough deleted space */
+ seq_range_array_iter_init(&iter, &map->ref0_file_ids); i = 0;
+ p_array_init(&new_ref0_file_ids, pool,
+ array_count(&map->ref0_file_ids));
+ while (seq_range_array_iter_nth(&iter, i++, &file_id)) {
+ size = hash_table_lookup(hash, POINTER_CAST(file_id));
+ if (size->ref0_size*100 / size->file_size >=
- if (map->storage->purge_min_percentage >= 100) {
++ map->storage->set->dbox_purge_min_percentage)
+ seq_range_array_add(&new_ref0_file_ids, 0, file_id);
+ }
+ seq_range_array_intersect(&map->ref0_file_ids, &new_ref0_file_ids);
+
+ hash_table_destroy(&hash);
+ pool_unref(&pool);
+}
+
+const ARRAY_TYPE(seq_range) *dbox_map_get_zero_ref_files(struct dbox_map *map)
+{
+ const struct mail_index_header *hdr;
+ const struct dbox_mail_index_map_record *rec;
+ const uint16_t *ref16_p;
+ const void *data;
+ uint32_t seq;
+ bool expunged;
+
+ if (array_is_created(&map->ref0_file_ids))
+ array_clear(&map->ref0_file_ids);
+ else
+ i_array_init(&map->ref0_file_ids, 64);
+
- if (map->storage->purge_min_percentage > 0 &&
++ if (map->storage->set->dbox_purge_min_percentage >= 100) {
+ /* we're never purging anything */
+ return &map->ref0_file_ids;
+ }
+
+ if (dbox_map_open(map, FALSE) < 0) {
+ /* some internal error */
+ return &map->ref0_file_ids;
+ }
+ (void)dbox_map_refresh(map);
+
+ hdr = mail_index_get_header(map->view);
+ for (seq = 1; seq <= hdr->messages_count; seq++) {
+ mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
+ &data, &expunged);
+ if (data != NULL && !expunged) {
+ ref16_p = data;
+ if (*ref16_p != 0)
+ continue;
+ }
+
+ mail_index_lookup_ext(map->view, seq, map->map_ext_id,
+ &data, &expunged);
+ if (data != NULL && !expunged) {
+ rec = data;
+ seq_range_array_add(&map->ref0_file_ids, 0,
+ rec->file_id);
+ }
+ }
- } else if (append_offset + mail_size > storage->rotate_size) {
++ if (map->storage->set->dbox_purge_min_percentage > 0 &&
+ array_count(&map->ref0_file_ids) > 0)
+ dbox_map_filter_zero_refs(map);
+ return &map->ref0_file_ids;
+}
+
+struct dbox_map_transaction_context *
+dbox_map_transaction_begin(struct dbox_map *map, bool external)
+{
+ struct dbox_map_transaction_context *ctx;
+ enum mail_index_transaction_flags flags =
+ MAIL_INDEX_TRANSACTION_FLAG_FSYNC;
+
+ if (external)
+ flags |= MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL;
+
+ ctx = i_new(struct dbox_map_transaction_context, 1);
+ ctx->map = map;
+ if (dbox_map_open(map, FALSE) == 0 &&
+ dbox_map_refresh(map) == 0)
+ ctx->trans = mail_index_transaction_begin(map->view, flags);
+ return ctx;
+}
+
+static void
+dbox_map_sync_handle(struct dbox_map *map, struct mail_index_sync_ctx *sync_ctx)
+{
+ struct mail_index_sync_rec sync_rec;
+ uint32_t seq1, seq2;
+ uoff_t offset1, offset2;
+
+ mail_index_sync_get_offsets(sync_ctx, &seq1, &offset1, &seq2, &offset2);
+ if (offset1 != offset2 || seq1 != seq2) {
+ /* something had crashed. need a full resync. */
+ i_warning("dbox %s: Inconsistency in map index "
+ "(%u,%"PRIuUOFF_T" != %u,%"PRIuUOFF_T")",
+ map->storage->storage_dir,
+ seq1, offset1, seq2, offset2);
+ map->storage->sync_rebuild = TRUE;
+ } else {
+ while (mail_index_sync_next(sync_ctx, &sync_rec)) ;
+ }
+}
+
+int dbox_map_transaction_commit(struct dbox_map_transaction_context *ctx)
+{
+ struct dbox_map *map = ctx->map;
+ struct mail_index_view *view;
+ struct mail_index_transaction *sync_trans;
+ int ret;
+
+ if (!ctx->changed)
+ return 0;
+
+ /* use syncing to lock the transaction log, so that we always see
+ log's head_offset = tail_offset */
+ ret = mail_index_sync_begin(map->index, &ctx->sync_ctx,
+ &view, &sync_trans, 0);
+ if (ret <= 0) {
+ i_assert(ret != 0);
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ mail_index_transaction_rollback(&ctx->trans);
+ return -1;
+ }
+ dbox_map_sync_handle(map, ctx->sync_ctx);
+
+ if (mail_index_transaction_commit(&ctx->trans) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
+ ctx->success = TRUE;
+ return 0;
+}
+
+void dbox_map_transaction_free(struct dbox_map_transaction_context **_ctx)
+{
+ struct dbox_map_transaction_context *ctx = *_ctx;
+ struct dbox_map *map = ctx->map;
+
+ *_ctx = NULL;
+ if (ctx->success) {
+ if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ }
+ } else if (ctx->sync_ctx != NULL) {
+ mail_index_sync_rollback(&ctx->sync_ctx);
+ }
+ if (ctx->trans != NULL)
+ mail_index_transaction_rollback(&ctx->trans);
+ i_free(ctx);
+}
+
+int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx,
+ const ARRAY_TYPE(uint32_t) *map_uids, int diff)
+{
+ struct dbox_map *map = ctx->map;
+ const uint32_t *uids;
+ unsigned int i, count;
+ const void *data;
+ uint32_t seq;
+ bool expunged;
+ int cur_diff;
+
+ if (ctx->trans == NULL)
+ return -1;
+
+ uids = array_get(map_uids, &count);
+ for (i = 0; i < count; i++) {
+ if (!mail_index_lookup_seq(map->view, uids[i], &seq)) {
+ /* we can't refresh map here since view has a
+ transaction open. */
+ dbox_map_set_corrupted(map,
+ "refcount update lost map_uid=%u", uids[i]);
+ return -1;
+ }
+ mail_index_lookup_ext(map->view, seq, map->ref_ext_id,
+ &data, &expunged);
+ cur_diff = data == NULL ? 0 : *((const uint16_t *)data);
+ ctx->changed = TRUE;
+ cur_diff += mail_index_atomic_inc_ext(ctx->trans, seq,
+ map->ref_ext_id, diff);
+ if (cur_diff >= 32768) {
+ /* we're getting close to the 64k limit. fail early
+ to make it less likely that two processes increase
+ the refcount enough times to cross the limit */
+ mail_storage_set_error(&map->storage->storage,
+ MAIL_ERROR_NOTPOSSIBLE,
+ "Message has been copied too many times");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int dbox_map_remove_file_id(struct dbox_map *map, uint32_t file_id)
+{
+ struct dbox_map_transaction_context *map_trans;
+ const struct mail_index_header *hdr;
+ const struct dbox_mail_index_map_record *rec;
+ const void *data;
+ bool expunged;
+ uint32_t seq;
+ int ret = 0;
+
+ /* make sure the map is refreshed, otherwise we might be expunging
+ messages that have already been moved to other files. */
+
+ /* we need a per-file transaction, otherwise we can't refresh the map */
+ map_trans = dbox_map_transaction_begin(map, TRUE);
+
+ hdr = mail_index_get_header(map->view);
+ for (seq = 1; seq <= hdr->messages_count; seq++) {
+ mail_index_lookup_ext(map->view, seq, map->map_ext_id,
+ &data, &expunged);
+ if (data == NULL) {
+ dbox_map_set_corrupted(map, "missing map extension");
+ ret = -1;
+ break;
+ }
+
+ rec = data;
+ if (rec->file_id == file_id) {
+ map_trans->changed = TRUE;
+ mail_index_expunge(map_trans->trans, seq);
+ }
+ }
+ if (ret == 0)
+ (void)dbox_map_transaction_commit(map_trans);
+ dbox_map_transaction_free(&map_trans);
+ return ret;
+}
+
+struct dbox_map_append_context *
+dbox_map_append_begin_storage(struct dbox_storage *storage)
+{
+ struct dbox_map_append_context *ctx;
+
+ ctx = i_new(struct dbox_map_append_context, 1);
+ ctx->map = storage->map;
+ ctx->first_new_file_id = (uint32_t)-1;
+ i_array_init(&ctx->files, 64);
+ i_array_init(&ctx->appends, 128);
+
+ if (dbox_map_open(ctx->map, TRUE) < 0)
+ ctx->failed = TRUE;
+ else {
+ /* refresh the map so we can try appending to the
+ latest files */
+ (void)dbox_map_refresh(ctx->map);
+ }
+ return ctx;
+}
+
+struct dbox_map_append_context *
+dbox_map_append_begin(struct dbox_mailbox *mbox)
+{
+ struct dbox_map_append_context *ctx;
+
+ ctx = dbox_map_append_begin_storage(mbox->storage);
+ ctx->mbox = mbox;
+ return ctx;
+}
+
+static time_t day_begin_stamp(unsigned int days)
+{
+ struct tm tm;
+ time_t stamp;
+
+ if (days == 0)
+ return 0;
+
+ /* get beginning of today */
+ tm = *localtime(&ioloop_time);
+ tm.tm_hour = 0;
+ tm.tm_min = 0;
+ tm.tm_sec = 0;
+ stamp = mktime(&tm);
+ if (stamp == (time_t)-1)
+ i_panic("mktime(today) failed");
+
+ return stamp - (3600*24 * (days-1));
+}
+
+static bool
+dbox_map_file_try_append(struct dbox_map_append_context *ctx,
+ uint32_t file_id, time_t stamp, uoff_t mail_size,
+ struct dbox_file **file_r, struct ostream **output_r,
+ bool *retry_later_r)
+{
+ struct dbox_map *map = ctx->map;
+ struct dbox_storage *storage = map->storage;
+ struct dbox_file *file;
+ struct stat st;
+ uoff_t append_offset;
+ bool deleted, file_too_old = FALSE;
+ int ret;
+
+ *file_r = NULL;
+ *retry_later_r = FALSE;
+
+ file = dbox_file_init_multi(storage, file_id);
+ if (dbox_file_open_or_create(file, &deleted) <= 0 || deleted) {
+ dbox_file_unref(&file);
+ return TRUE;
+ }
+ if (file->lock != NULL) {
+ /* already locked, we're possibly in the middle of purging it
+ in which case we really don't want to write there. */
+ dbox_file_unref(&file);
+ return TRUE;
+ }
+
+ if (file->create_time < stamp)
+ file_too_old = TRUE;
+ else if ((ret = dbox_file_try_lock(file)) <= 0) {
+ /* locking failed */
+ *retry_later_r = ret == 0;
+ } else if (stat(file->current_path, &st) < 0) {
+ if (errno != ENOENT)
+ i_error("stat(%s) failed: %m", file->current_path);
+ /* the file was unlinked between opening and locking it. */
+ } else if (dbox_file_get_append_stream(file, &append_offset,
+ output_r) <= 0) {
+ /* couldn't append to this file */
- if (mail_size >= map->storage->rotate_size)
++ } else if (append_offset + mail_size > storage->set->dbox_rotate_size) {
+ /* file was too large after all */
+ dbox_file_cancel_append(file, append_offset);
+ } else {
+ /* success */
+ *file_r = file;
+ return TRUE;
+ }
+
+ /* failure */
+ dbox_file_unlock(file);
+ dbox_file_unref(&file);
+ return !file_too_old;
+}
+
+static bool
+dbox_map_is_appending(struct dbox_map_append_context *ctx, uint32_t file_id)
+{
+ struct dbox_file *const *files;
+ unsigned int i, count;
+
+ /* there shouldn't be many files open, don't bother with anything
+ faster. */
+ files = array_get(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->file_id == file_id)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+dbox_map_find_appendable_file(struct dbox_map_append_context *ctx,
+ uoff_t mail_size, struct dbox_file **file_r,
+ struct ostream **output_r, bool *existing_r)
+{
+ struct dbox_map *map = ctx->map;
++ const struct dbox_settings *set = map->storage->set;
+ struct dbox_file *const *files;
+ const struct mail_index_header *hdr;
+ unsigned int i, count, backwards_lookup_count;
+ uint32_t seq, seq1, uid, file_id, min_seen_file_id;
+ uoff_t offset, append_offset, size;
+ time_t stamp;
+ bool retry_later;
+
+ *existing_r = FALSE;
+
- if (append_offset + mail_size <= map->storage->rotate_size &&
++ if (mail_size >= set->dbox_rotate_size)
+ return 0;
+
+ /* first try to use files already used in this append */
+ files = array_get(&ctx->files, &count);
+ for (i = count; i > ctx->files_nonappendable_count; i--) {
+ if (files[i-1]->output == NULL) {
+ /* we already decided we can't append to this */
+ continue;
+ }
+
+ append_offset = dbox_file_get_next_append_offset(files[i-1]);
- stamp = day_begin_stamp(map->storage->rotate_days);
++ if (append_offset + mail_size <= set->dbox_rotate_size &&
+ dbox_file_get_append_stream(files[i-1], &append_offset,
+ output_r) > 0) {
+ *file_r = files[i-1];
+ *existing_r = TRUE;
+ return 1;
+ }
+ /* can't append to this file anymore */
+#if 0 /* FIXME: we can't close files, otherwise we lose the lock too early */
+ if (files[i-1]->fd != -1) {
+ /* avoid wasting fds by closing the file, but not if
+ we're also reading from it. */
+ if (dbox_file_flush_append(files[i-1]) < 0)
+ return -1;
+ dbox_file_unlock(files[i-1]);
+ if (files[i-1]->refcount == 1)
+ dbox_file_close(files[i-1]);
+ }
+#endif
+ }
+ ctx->files_nonappendable_count = count;
+
+ /* try to find an existing appendable file */
- if (offset + size + mail_size >= map->storage->rotate_size)
++ stamp = day_begin_stamp(set->dbox_rotate_days);
+ hdr = mail_index_get_header(map->view);
+ min_seen_file_id = (uint32_t)-1;
+
+ ctx->orig_next_uid = hdr->next_uid;
+ backwards_lookup_count = 0;
+ for (seq = hdr->messages_count; seq > 0; seq--) {
+ if (dbox_map_lookup_seq(map, seq, &file_id, &offset, &size) < 0)
+ return -1;
+ if (file_id >= min_seen_file_id)
+ continue;
+ min_seen_file_id = file_id;
+
+ if (++backwards_lookup_count > MAX_BACKWARDS_LOOKUPS) {
+ /* we've wasted enough time here */
+ break;
+ }
+
+ /* first lookup: this should be enough usually, but we can't
+ be sure until after locking. also if messages were recently
+ moved, this message might not be the last one in the file. */
- file = ctx->map->storage->rotate_size == 0 ?
++ if (offset + size + mail_size >= set->dbox_rotate_size)
+ continue;
+
+ if (dbox_map_is_appending(ctx, file_id)) {
+ /* already checked this */
+ continue;
+ }
+
+ mail_index_lookup_uid(map->view, seq, &uid);
+ if (!dbox_map_file_try_append(ctx, file_id, stamp, mail_size,
+ file_r, output_r, &retry_later)) {
+ /* file is too old. the rest of the files are too. */
+ break;
+ }
+ /* NOTE: we've now refreshed map view. there are no guarantees
+ about sequences anymore. */
+ if (*file_r != NULL)
+ return 1;
+ /* FIXME: use retry_later somehow */
+ if (uid == 1 ||
+ !mail_index_lookup_seq_range(map->view, 1, uid-1,
+ &seq1, &seq))
+ break;
+ seq++;
+ }
+ return 0;
+}
+
+int dbox_map_append_next(struct dbox_map_append_context *ctx, uoff_t mail_size,
+ struct dbox_file **file_r, struct ostream **output_r)
+{
+ struct dbox_file *file = NULL;
+ struct dbox_map_append *append;
+ uoff_t append_offset;
+ bool existing;
+ int ret;
+
+ if (ctx->failed)
+ return -1;
+
+ ret = dbox_map_find_appendable_file(ctx, mail_size, &file,
+ output_r, &existing);
+ if (ret < 0)
+ return -1;
+
+ if (ret == 0) {
+ /* create a new file */
++ file = ctx->map->storage->set->dbox_rotate_size == 0 ?
+ dbox_file_init_single(ctx->mbox, 0) :
+ dbox_file_init_multi(ctx->map->storage, 0);
+ ret = dbox_file_get_append_stream(file, &append_offset,
+ output_r);
+ if (ret <= 0) {
+ i_assert(ret < 0);
+ (void)unlink(file->current_path);
+ dbox_file_unref(&file);
+ return -1;
+ }
+ }
+
+ if (file->single_mbox == NULL) {
+ append = array_append_space(&ctx->appends);
+ append->file = file;
+ append->offset = (*output_r)->offset;
+ append->size = (uint32_t)-1;
+ }
+ if (!existing) {
+ i_assert(file->first_append_offset == 0);
+ i_assert(file->output != NULL);
+ file->first_append_offset = file->output->offset;
+ array_append(&ctx->files, &file, 1);
+ }
+ *file_r = file;
+ return 0;
+}
+
+void dbox_map_append_finish_multi_mail(struct dbox_map_append_context *ctx)
+{
+ struct dbox_map_append *appends;
+ unsigned int count;
+
+ appends = array_get_modifiable(&ctx->appends, &count);
+ i_assert(count > 0 && appends[count-1].size == (uint32_t)-1);
+ appends[count-1].size = appends[count-1].file->output->offset -
+ appends[count-1].offset;
+}
+
+static int
+dbox_map_get_next_file_id(struct dbox_map *map, struct mail_index_view *view,
+ uint32_t *file_id_r)
+{
+ const struct dbox_mail_index_map_header *hdr;
+ const void *data;
+ size_t data_size;
+
+ mail_index_get_header_ext(view, map->map_ext_id, &data, &data_size);
+ if (data_size != sizeof(*hdr)) {
+ if (data_size != 0) {
+ dbox_map_set_corrupted(map, "hdr size=%u", data_size);
+ return -1;
+ }
+ /* first file */
+ *file_id_r = 1;
+ } else {
+ hdr = data;
+ *file_id_r = hdr->highest_file_id + 1;
+ }
+ return 0;
+}
+
+static int dbox_map_assign_file_ids(struct dbox_map_append_context *ctx,
+ bool separate_transaction)
+{
+ struct dbox_file *const *files;
+ unsigned int i, count;
+ uint32_t first_file_id, file_id;
+ int ret;
+
+ /* start the syncing. we'll need it even if there are no file ids to
+ be assigned. */
+ ret = mail_index_sync_begin(ctx->map->index, &ctx->sync_ctx,
+ &ctx->sync_view, &ctx->sync_trans, 0);
+ if (ret <= 0) {
+ i_assert(ret != 0);
+ mail_storage_set_internal_error(&ctx->map->storage->storage);
+ mail_index_reset_error(ctx->map->index);
+ return -1;
+ }
+ dbox_map_sync_handle(ctx->map, ctx->sync_ctx);
+
+ if (dbox_map_get_next_file_id(ctx->map, ctx->sync_view, &file_id) < 0) {
+ mail_index_sync_rollback(&ctx->sync_ctx);
+ return -1;
+ }
+
+ /* assign file_ids for newly created multi-files */
+ first_file_id = file_id;
+ files = array_get(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->single_mbox != NULL)
+ continue;
+
+ if (files[i]->output != NULL) {
+ if (dbox_file_flush_append(files[i]) < 0) {
+ ret = -1;
+ break;
+ }
+ }
+
+ if (files[i]->file_id == 0) {
+ if (dbox_file_assign_id(files[i], file_id++) < 0) {
+ ret = -1;
+ break;
+ }
+ }
+ }
+
+ if (ret < 0) {
+ mail_index_sync_rollback(&ctx->sync_ctx);
+ return -1;
+ }
+
+ ctx->trans = !separate_transaction ? NULL :
+ mail_index_transaction_begin(ctx->map->view,
+ MAIL_INDEX_TRANSACTION_FLAG_FSYNC);
+
+ /* update the highest used file_id */
+ if (first_file_id != file_id) {
+ file_id--;
+ mail_index_update_header_ext(ctx->trans != NULL ? ctx->trans :
+ ctx->sync_trans,
+ ctx->map->map_ext_id,
+ 0, &file_id, sizeof(file_id));
+ }
+ return 0;
+}
+
+int dbox_map_append_assign_map_uids(struct dbox_map_append_context *ctx,
+ uint32_t *first_map_uid_r,
+ uint32_t *last_map_uid_r)
+{
+ const struct dbox_map_append *appends;
+ const struct mail_index_header *hdr;
+ struct dbox_mail_index_map_record rec;
+ unsigned int i, count;
+ uint32_t seq, first_uid, next_uid;
+ uint16_t ref16;
+ int ret = 0;
+
+ if (array_count(&ctx->appends) == 0) {
+ *first_map_uid_r = 0;
+ *last_map_uid_r = 0;
+ return 0;
+ }
+
+ if (dbox_map_assign_file_ids(ctx, TRUE) < 0)
+ return -1;
+
+ /* append map records to index */
+ memset(&rec, 0, sizeof(rec));
+ ref16 = 1;
+ appends = array_get(&ctx->appends, &count);
+ for (i = 0; i < count; i++) {
+ i_assert(appends[i].offset <= (uint32_t)-1);
+ i_assert(appends[i].size <= (uint32_t)-1);
+
+ rec.file_id = appends[i].file->file_id;
+ rec.offset = appends[i].offset;
+ rec.size = appends[i].size;
+
+ mail_index_append(ctx->trans, 0, &seq);
+ mail_index_update_ext(ctx->trans, seq, ctx->map->map_ext_id,
+ &rec, NULL);
+ mail_index_update_ext(ctx->trans, seq, ctx->map->ref_ext_id,
+ &ref16, NULL);
+ }
+
+ /* assign map UIDs for appended records */
+ hdr = mail_index_get_header(ctx->sync_view);
+ first_uid = hdr->next_uid;
+ mail_index_append_assign_uids(ctx->trans, first_uid, &next_uid);
+ i_assert(next_uid - first_uid == count);
+
+ if (hdr->uid_validity == 0) {
+ /* we don't really care about uidvalidity, but it can't be 0 */
+ uint32_t uid_validity = ioloop_time;
+ mail_index_update_header(ctx->trans,
+ offsetof(struct mail_index_header, uid_validity),
+ &uid_validity, sizeof(uid_validity), TRUE);
+ }
+
+ if (mail_index_transaction_commit(&ctx->trans) < 0) {
+ mail_storage_set_internal_error(&ctx->map->storage->storage);
+ mail_index_reset_error(ctx->map->index);
+ return -1;
+ }
+
+ *first_map_uid_r = first_uid;
+ *last_map_uid_r = next_uid - 1;
+ return ret;
+}
+
+int dbox_map_append_move(struct dbox_map_append_context *ctx,
+ const ARRAY_TYPE(uint32_t) *map_uids,
+ const ARRAY_TYPE(seq_range) *expunge_map_uids)
+{
+ const struct dbox_map_append *appends;
+ struct dbox_mail_index_map_record rec;
+ struct seq_range_iter iter;
+ const uint32_t *uids;
+ unsigned int i, j, map_uids_count, appends_count;
+ uint32_t uid, seq;
+
+ if (dbox_map_assign_file_ids(ctx, FALSE) < 0)
+ return -1;
+
+ memset(&rec, 0, sizeof(rec));
+ appends = array_get(&ctx->appends, &appends_count);
+
+ uids = array_get(map_uids, &map_uids_count);
+ for (i = j = 0; i < map_uids_count; i++) {
+ i_assert(j < appends_count);
+ rec.file_id = appends[j].file->file_id;
+ rec.offset = appends[j].offset;
+ rec.size = appends[j].size;
+ j++;
+
+ if (!mail_index_lookup_seq(ctx->sync_view, uids[i], &seq))
+ i_unreached();
+ mail_index_update_ext(ctx->sync_trans, seq,
+ ctx->map->map_ext_id, &rec, NULL);
+ }
+
+ seq_range_array_iter_init(&iter, expunge_map_uids); i = 0;
+ while (seq_range_array_iter_nth(&iter, i++, &uid)) {
+ if (!mail_index_lookup_seq(ctx->sync_view, uid, &seq))
+ i_unreached();
+ mail_index_expunge(ctx->sync_trans, seq);
+ }
+ return 0;
+}
+
+int dbox_map_append_assign_uids(struct dbox_map_append_context *ctx,
+ uint32_t first_uid, uint32_t last_uid)
+{
+ struct dbox_file *const *files;
+ unsigned int i, count;
+ uint32_t next_uid = first_uid;
+
+ files = array_get(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ if (files[i]->single_mbox == NULL)
+ continue;
+
+ if (dbox_file_assign_id(files[i], next_uid++) < 0)
+ return -1;
+ }
+ i_assert(next_uid == last_uid + 1);
+ return 0;
+}
+
+int dbox_map_append_commit(struct dbox_map_append_context *ctx)
+{
+ struct dbox_map *map = ctx->map;
+
+ i_assert(ctx->trans == NULL);
+
+ if (ctx->sync_ctx != NULL) {
+ if (mail_index_sync_commit(&ctx->sync_ctx) < 0) {
+ mail_storage_set_internal_error(&map->storage->storage);
+ mail_index_reset_error(map->index);
+ return -1;
+ }
+ }
+ ctx->committed = TRUE;
+ return 0;
+}
+
+static void dbox_map_append_file_rollback(struct dbox_file *file)
+{
+ struct mail_storage *storage = &file->storage->storage;
+
+ if (file->output != NULL) {
+ /* flush before truncating */
+ (void)o_stream_flush(file->output);
+ }
+
+ if (file->file_id != 0 &&
+ file->first_append_offset > file->file_header_size) {
+ if (ftruncate(file->fd, file->first_append_offset) < 0) {
+ mail_storage_set_critical(storage,
+ "ftruncate(%s, %"PRIuUOFF_T") failed: %m",
+ file->current_path, file->first_append_offset);
+ }
+ } else {
+ if (unlink(file->current_path) < 0) {
+ mail_storage_set_critical(storage,
+ "unlink(%s) failed: %m", file->current_path);
+ }
+ }
+}
+
+void dbox_map_append_free(struct dbox_map_append_context **_ctx)
+{
+ struct dbox_map_append_context *ctx = *_ctx;
+ struct dbox_file **files;
+ unsigned int i, count;
+
+ *_ctx = NULL;
+
+ if (ctx->trans != NULL)
+ mail_index_transaction_rollback(&ctx->trans);
+ if (ctx->sync_ctx != NULL)
+ mail_index_sync_rollback(&ctx->sync_ctx);
+
+ files = array_get_modifiable(&ctx->files, &count);
+ for (i = 0; i < count; i++) {
+ if (!ctx->committed)
+ dbox_map_append_file_rollback(files[i]);
+
+ files[i]->first_append_offset = 0;
+ dbox_file_unlock(files[i]);
+ dbox_file_unref(&files[i]);
+ }
+ array_free(&ctx->appends);
+ array_free(&ctx->files);
+ i_free(ctx);
+}
+
+uint32_t dbox_map_get_uid_validity(struct dbox_map *map)
+{
+ const struct mail_index_header *hdr;
+
+ i_assert(map->view != NULL);
+
+ hdr = mail_index_get_header(map->view);
+ return hdr->uid_validity != 0 ? hdr->uid_validity :
+ map->created_uid_validity;
+}
{
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);
--- /dev/null
- MEMBER(dbox_max_open_files) 64
+ /* Copyright (c) 2006-2008 Dovecot authors, see the included COPYING file */
+
+ #include "lib.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "dbox-settings.h"
+
+ #include <stddef.h>
+
+ #undef DEF
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct dbox_settings, name), NULL }
+
+ static struct setting_define dbox_setting_defines[] = {
+ DEF(SET_UINT, dbox_rotate_size),
+ DEF(SET_UINT, dbox_rotate_min_size),
+ DEF(SET_UINT, dbox_rotate_days),
+ DEF(SET_UINT, dbox_max_open_files),
++ DEF(SET_UINT, dbox_purge_min_percentage),
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ static struct dbox_settings dbox_default_settings = {
+ MEMBER(dbox_rotate_size) 2048*1024,
+ MEMBER(dbox_rotate_min_size) 16*1024,
+ MEMBER(dbox_rotate_days) 0,
++ MEMBER(dbox_max_open_files) 64,
++ MEMBER(dbox_purge_min_percentage) 0
+ };
+
+ static struct setting_parser_info dbox_setting_parser_info = {
+ MEMBER(defines) dbox_setting_defines,
+ MEMBER(defaults) &dbox_default_settings,
+
+ MEMBER(parent) &mail_user_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct dbox_settings)
+ };
+
+ const struct setting_parser_info *dbox_get_setting_parser_info(void)
+ {
+ return &dbox_setting_parser_info;
+ }
--- /dev/null
+ #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
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)
struct dbox_storage *storage = (struct dbox_storage *)_storage;
struct mailbox_list_settings list_set;
struct stat st;
- const char *layout, *alt_dir, *dir, *value;
- const char *layout, *alt_dir;
++ const char *layout, *alt_dir, *dir;
if (dbox_get_list_settings(&list_set, data, _storage,
&layout, &alt_dir, error_r) < 0)
return -1;
}
- value = getenv("DBOX_ROTATE_SIZE");
- if (value != NULL)
- storage->rotate_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
- else
- storage->rotate_size = DBOX_DEFAULT_ROTATE_SIZE;
- value = getenv("DBOX_ROTATE_MIN_SIZE");
- if (value != NULL)
- storage->rotate_min_size = (uoff_t)strtoul(value, NULL, 10) * 1024;
- else
- storage->rotate_min_size = DBOX_DEFAULT_ROTATE_MIN_SIZE;
- if (storage->rotate_min_size > storage->rotate_size)
- storage->rotate_min_size = storage->rotate_size;
- value = getenv("DBOX_ROTATE_DAYS");
- if (value != NULL)
- storage->rotate_days = (unsigned int)strtoul(value, NULL, 10);
- else
- storage->rotate_days = DBOX_DEFAULT_ROTATE_DAYS;
- value = getenv("DBOX_MAX_OPEN_FILES");
- if (value != NULL)
- storage->max_open_files = (unsigned int)strtoul(value, NULL, 10);
- else
- storage->max_open_files = DBOX_DEFAULT_MAX_OPEN_FILES;
- value = getenv("DBOX_PURGE_MIN_PERCENTAGE");
- if (value != NULL)
- storage->purge_min_percentage = (unsigned int)strtoul(value, NULL, 10);
- else
- storage->purge_min_percentage = DBOX_DEFAULT_PURGE_MIN_PERCENTAGE;
-
- if (storage->max_open_files <= 1) {
+ storage->set = mail_storage_get_driver_settings(_storage);
++ if (storage->set->dbox_max_open_files <= 1) {
+ /* we store file offsets in a 32bit integer. */
+ *error_r = "dbox_max_open_files must be at least 2";
+ return -1;
+ }
- if (storage->rotate_size > (uint32_t)-1) {
- /* we store file offsets in a 32bit integer. */
- *error_r = "dbox_rotate_size must be less than 4 GB";
- return -1;
- }
if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0)
return -1;
storage, &storage->list_module_ctx);
/* finish list init after we've overridden vfuncs */
- mailbox_list_init(_storage->list, _storage->ns, &list_set,
- mail_storage_get_list_flags(_storage->flags));
+ mailbox_list_init(_storage->list, _storage->ns, &list_set, 0);
+
+ dir = mailbox_list_get_path(storage->storage.list, NULL,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ storage->storage_dir = p_strconcat(_storage->pool, dir,
+ "/"DBOX_GLOBAL_DIR_NAME, NULL);
+ storage->alt_storage_dir = p_strconcat(_storage->pool, alt_dir,
+ "/"DBOX_GLOBAL_DIR_NAME, NULL);
- i_array_init(&storage->open_files, I_MIN(storage->max_open_files, 128));
++ i_array_init(&storage->open_files,
++ I_MIN(storage->set->dbox_max_open_files, 128));
+
+ storage->map = dbox_map_init(storage);
+ mailbox_list_get_permissions(storage->storage.list, NULL,
+ &storage->create_mode,
+ &storage->create_gid);
return 0;
}
}
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
#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"
struct dbox_storage {
struct mail_storage storage;
union mailbox_list_module_context list_module_ctx;
+ const struct dbox_settings *set;
+
+ /* root path for alt directory */
const char *alt_dir;
- uoff_t rotate_size, rotate_min_size;
- unsigned int rotate_days;
- unsigned int max_open_files;
- unsigned int purge_min_percentage;
+ /* paths for storage directories */
+ const char *storage_dir, *alt_storage_dir;
+ struct dbox_map *map;
+
+ /* mode/gid to use for new dbox storage files */
+ mode_t create_mode;
+ gid_t create_gid;
+
+ ARRAY_DEFINE(open_files, struct dbox_file *);
+
+ unsigned int sync_rebuild:1;
+ unsigned int have_multi_msgs:1;
};
struct dbox_mail_index_record {
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)
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) {
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 {
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 */
}
&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,
{
struct mail_storage *storage = &ctx->mbox->storage->storage;
-- if (ctx->mbox->ibox.fsync_disable)
++ if (storage->set->fsync_disable)
return 0;
if (new_changed) {
--- /dev/null
- MEMBER(maildir_copy_preserve_filename) FALSE
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+ #include "lib.h"
+ #include "settings-parser.h"
+ #include "mail-storage-settings.h"
+ #include "maildir-settings.h"
+
+ #include <stddef.h>
+
+ #undef DEF
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct maildir_settings, name), NULL }
+
+ static struct setting_define maildir_setting_defines[] = {
+ DEF(SET_BOOL, maildir_stat_dirs),
+ DEF(SET_BOOL, maildir_copy_with_hardlinks),
+ DEF(SET_BOOL, maildir_copy_preserve_filename),
++ DEF(SET_BOOL, maildir_very_dirty_syncs),
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ static struct maildir_settings maildir_default_settings = {
+ MEMBER(maildir_stat_dirs) FALSE,
+ MEMBER(maildir_copy_with_hardlinks) TRUE,
++ MEMBER(maildir_copy_preserve_filename) FALSE,
++ MEMBER(maildir_very_dirty_syncs) FALSE
+ };
+
+ static struct setting_parser_info maildir_setting_parser_info = {
+ MEMBER(defines) maildir_setting_defines,
+ MEMBER(defaults) &maildir_default_settings,
+
+ MEMBER(parent) &mail_user_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct maildir_settings)
+ };
+
+ const struct setting_parser_info *maildir_get_setting_parser_info(void)
+ {
+ return &maildir_setting_parser_info;
+ }
+
--- /dev/null
+ #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
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;
}
/* try to avoid stat()ing by first checking delayed changes */
if (DIR_DELAYED_REFRESH(hdr, new) ||
- (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)) {
- DIR_DELAYED_REFRESH(hdr, cur)) {
++ (DIR_DELAYED_REFRESH(hdr, cur) &&
++ !mbox->storage->set->maildir_very_dirty_syncs)) {
/* refresh index and try again */
if (maildir_sync_header_refresh(mbox) < 0)
return -1;
if (DIR_DELAYED_REFRESH(hdr, new))
*new_changed_r = TRUE;
- if (DIR_DELAYED_REFRESH(hdr, cur) && !mbox->very_dirty_syncs)
- if (DIR_DELAYED_REFRESH(hdr, cur))
++ if (DIR_DELAYED_REFRESH(hdr, cur) &&
++ !mbox->storage->set->maildir_very_dirty_syncs)
*cur_changed_r = TRUE;
if (*new_changed_r && *cur_changed_r)
return 0;
}
}
- 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);
}
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);
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;
}
}
}
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;
/* 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;
}
ns->prefix = i_strdup(str_c(prefix));
ns->owner = owner;
ns->flags = NAMESPACE_FLAG_LIST_PREFIX | NAMESPACE_FLAG_HIDDEN |
- NAMESPACE_FLAG_AUTOCREATED;
+ NAMESPACE_FLAG_AUTOCREATED | NAMESPACE_FLAG_INBOX;
ns->sep = _storage->ns->sep;
+ ns->mail_set = _storage->set;
location = t_str_new(256);
if (ret > 0)
var_expand(location, storage->location, tab);
- else
+ else {
get_nonexisting_user_location(storage, userdomain, location);
- if (mail_storage_create(ns, NULL, str_c(location), _storage->flags,
- _storage->lock_method, &error) < 0) {
+ ns->flags |= NAMESPACE_FLAG_UNUSABLE;
+ }
+
+ ns_set = p_new(user->pool, struct mail_namespace_settings, 1);
+ ns_set->type = "shared";
+ ns_set->separator = p_strdup_printf(user->pool, "%c", ns->sep);
+ ns_set->prefix = ns->prefix;
+ ns_set->location = p_strdup(user->pool, str_c(location));
+ ns_set->hidden = TRUE;
+ ns_set->list = "yes";
+ ns->set = ns_set;
+
+ if (mail_storage_create(ns, NULL, _storage->flags, &error) < 0) {
mail_storage_set_critical(_storage, "Namespace '%s': %s",
ns->prefix, error);
mail_namespace_destroy(ns);
{
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. */
driver = NULL;
}
- if (mail_storage_create(ns, driver, data, flags, lock_method,
- &error) < 0) {
- i_error("Namespace '%s': %s", ns->prefix, error);
+ if (mail_storage_create(ns, driver, 0, &error) < 0) {
+ *error_r = t_strdup_printf("Namespace '%s': %s",
+ ns->prefix, error);
mail_namespace_free(ns);
- return NULL;
+ return -1;
}
- return ns;
+ if (ns_set->separator != NULL)
+ ns->sep = *ns_set->separator;
+
+ *ns_p = ns;
+ return 0;
}
- static bool namespaces_check(struct mail_namespace *namespaces)
+ static bool
+ namespaces_check(struct mail_namespace *namespaces, const char **error_r)
{
- struct mail_namespace *ns, *inbox_ns = NULL, *private_ns = NULL;
- unsigned int private_ns_count = 0;
+ struct mail_namespace *ns, *inbox_ns = NULL;
unsigned int subscriptions_count = 0;
char list_sep = '\0';
}
if (inbox_ns == NULL) {
- i_error("namespace configuration error: "
- "inbox=yes namespace missing");
- if (private_ns_count == 1) {
- /* just one private namespace. we'll assume it's
- the INBOX namespace. */
- private_ns->flags |= NAMESPACE_FLAG_INBOX;
- } else {
+ *error_r = "namespace configuration error: "
+ "inbox=yes namespace missing";
- return FALSE;
- }
+ return FALSE;
}
if (list_sep == '\0') {
- i_error("namespace configuration error: "
- "no list=yes namespaces");
+ *error_r = "namespace configuration error: "
+ "no list=yes namespaces";
return FALSE;
}
if (subscriptions_count == 0) {
/* 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. */
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;
--- /dev/null
+ /* Copyright (c) 2005-2008 Dovecot authors, see the included COPYING file */
+
+ #include "lib.h"
+ #include "array.h"
+ #include "settings-parser.h"
+ #include "mail-index.h"
+ #include "mail-user.h"
+ #include "mail-namespace.h"
+ #include "mail-storage-private.h"
+ #include "mail-storage-settings.h"
+
+ #include <stddef.h>
+
+ static bool mail_storage_settings_check(void *_set, const char **error_r);
+ static bool namespace_settings_check(void *_set, const char **error_r);
+
+ #undef DEF
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct mail_storage_settings, name), NULL }
+
+ static struct setting_define mail_storage_setting_defines[] = {
+ DEF(SET_STR_VARS, mail_location),
+ DEF(SET_STR, mail_cache_fields),
+ DEF(SET_STR, mail_never_cache_fields),
+ DEF(SET_UINT, mail_cache_min_mail_count),
+ DEF(SET_UINT, mailbox_idle_check_interval),
+ DEF(SET_UINT, mail_max_keyword_length),
+ DEF(SET_BOOL, mail_save_crlf),
+ DEF(SET_BOOL, fsync_disable),
+ DEF(SET_BOOL, mmap_disable),
+ DEF(SET_BOOL, dotlock_use_excl),
+ DEF(SET_BOOL, mail_nfs_storage),
+ DEF(SET_BOOL, mail_nfs_index),
+ DEF(SET_BOOL, mailbox_list_index_disable),
+ DEF(SET_BOOL, mail_debug),
+ DEF(SET_BOOL, mail_full_filesystem_access),
+ DEF(SET_ENUM, lock_method),
+ DEF(SET_STR, pop3_uidl_format),
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ struct mail_storage_settings mail_storage_default_settings = {
+ MEMBER(mail_location) "",
+ MEMBER(mail_cache_fields) "flags",
+ MEMBER(mail_never_cache_fields) "imap.envelope",
+ MEMBER(mail_cache_min_mail_count) 0,
+ MEMBER(mailbox_idle_check_interval) 30,
+ MEMBER(mail_max_keyword_length) 50,
+ MEMBER(mail_save_crlf) FALSE,
+ MEMBER(fsync_disable) FALSE,
+ MEMBER(mmap_disable) FALSE,
+ MEMBER(dotlock_use_excl) FALSE,
+ MEMBER(mail_nfs_storage) FALSE,
+ MEMBER(mail_nfs_index) FALSE,
+ MEMBER(mailbox_list_index_disable) FALSE,
+ MEMBER(mail_debug) FALSE,
+ MEMBER(mail_full_filesystem_access) FALSE,
+ MEMBER(lock_method) "fcntl:flock:dotlock",
+ MEMBER(pop3_uidl_format) "%08Xu%08Xv"
+ };
+
+ struct setting_parser_info mail_storage_setting_parser_info = {
+ MEMBER(defines) mail_storage_setting_defines,
+ MEMBER(defaults) &mail_storage_default_settings,
+
+ MEMBER(parent) &mail_user_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct mail_storage_settings),
+ MEMBER(check_func) mail_storage_settings_check
+ };
+
+ #undef DEF
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct mail_namespace_settings, name), NULL }
+
+ static struct setting_define mail_namespace_setting_defines[] = {
+ DEF(SET_ENUM, type),
+ DEF(SET_STR, separator),
+ DEF(SET_STR_VARS, prefix),
+ DEF(SET_STR_VARS, location),
+ DEF(SET_STR_VARS, alias_for),
+
+ DEF(SET_BOOL, inbox),
+ DEF(SET_BOOL, hidden),
+ DEF(SET_ENUM, list),
+ DEF(SET_BOOL, subscriptions),
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ struct mail_namespace_settings mail_namespace_default_settings = {
+ MEMBER(type) "private:shared:public",
+ MEMBER(separator) "",
+ MEMBER(prefix) "",
+ MEMBER(location) "",
+ MEMBER(alias_for) NULL,
+
+ MEMBER(inbox) FALSE,
+ MEMBER(hidden) FALSE,
+ MEMBER(list) "yes:no:children",
+ MEMBER(subscriptions) TRUE
+ };
+
+ struct setting_parser_info mail_namespace_setting_parser_info = {
+ MEMBER(defines) mail_namespace_setting_defines,
+ MEMBER(defaults) &mail_namespace_default_settings,
+
+ MEMBER(parent) &mail_user_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) offsetof(struct mail_namespace_settings, user_set),
+ MEMBER(type_offset) offsetof(struct mail_namespace_settings, type),
+ MEMBER(struct_size) sizeof(struct mail_namespace_settings),
+ MEMBER(check_func) namespace_settings_check
+ };
+
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct mail_user_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+ { SET_DEFLIST, name, \
+ offsetof(struct mail_user_settings, field), defines }
+
+ static struct setting_define mail_user_setting_defines[] = {
+ DEF(SET_UINT, umask),
+
+ DEFLIST(namespaces, "namespace", &mail_namespace_setting_parser_info),
+ { SET_STRLIST, "plugin", offsetof(struct mail_user_settings, plugin_envs), NULL },
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ static struct mail_user_settings mail_user_default_settings = {
+ MEMBER(umask) 0077,
+
+ MEMBER(namespaces) ARRAY_INIT,
+ MEMBER(plugin_envs) ARRAY_INIT
+ };
+
+ struct setting_parser_info mail_user_setting_parser_info = {
+ MEMBER(defines) mail_user_setting_defines,
+ MEMBER(defaults) &mail_user_default_settings,
+
+ MEMBER(parent) NULL,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct mail_user_settings)
+ };
+
+ const void *
+ mail_user_set_get_driver_settings(const struct mail_user_settings *set,
+ const char *driver)
+ {
+ const void *dset;
+
+ dset = settings_find_dynamic(&mail_user_setting_parser_info,
+ set, driver);
+ if (dset == NULL) {
+ i_panic("Default settings not found for storage driver %s",
+ driver);
+ }
+ return dset;
+ }
+
+ const void *mail_storage_get_driver_settings(struct mail_storage *storage)
+ {
+ return mail_user_set_get_driver_settings(storage->ns->user->set,
+ storage->name);
+ }
+
+ enum mail_index_open_flags
+ mail_storage_settings_to_index_flags(const struct mail_storage_settings *set)
+ {
+ enum mail_index_open_flags index_flags = 0;
+
+ if (set->fsync_disable)
+ index_flags |= MAIL_INDEX_OPEN_FLAG_FSYNC_DISABLE;
+ #ifndef MMAP_CONFLICTS_WRITE
+ if (set->mmap_disable)
+ #endif
+ index_flags |= MAIL_INDEX_OPEN_FLAG_MMAP_DISABLE;
+ if (set->dotlock_use_excl)
+ index_flags |= MAIL_INDEX_OPEN_FLAG_DOTLOCK_USE_EXCL;
++ if (set->mail_nfs_index)
++ index_flags |= MAIL_INDEX_OPEN_FLAG_NFS_FLUSH;
+ return index_flags;
+ }
+
+ void mail_storage_namespace_defines_init(pool_t pool)
+ {
+ struct dynamic_settings_parser *parsers;
+ struct mail_storage *const *storages;
+ unsigned int i, j, count;
+
+ storages = array_get(&mail_storage_classes, &count);
+ parsers = t_new(struct dynamic_settings_parser, count + 1);
+ parsers[0].name = "MAIL";
+ parsers[0].info = &mail_storage_setting_parser_info;
+
+ for (i = 0, j = 1; i < count; i++) {
+ if (storages[i]->v.get_setting_parser_info == NULL)
+ continue;
+
+ parsers[j].name = storages[i]->name;
+ parsers[j].info = storages[i]->v.get_setting_parser_info();
+ j++;
+ }
+
+ settings_parser_info_update(pool, parsers[j-1].info->parent, parsers);
+ }
+
+ /* <settings checks> */
+ static bool mail_storage_settings_check(void *_set, const char **error_r)
+ {
+ const struct mail_storage_settings *set = _set;
+
+ if (set->mail_nfs_index && !set->mmap_disable) {
+ *error_r = "mail_nfs_index=yes requires mmap_disable=yes";
+ return FALSE;
+ }
+ if (set->mail_nfs_index && set->fsync_disable) {
+ *error_r = "mail_nfs_index=yes requires fsync_disable=no";
+ return FALSE;
+ }
+ return TRUE;
+ }
+
+ static bool namespace_settings_check(void *_set, const char **error_r)
+ {
+ struct mail_namespace_settings *ns = _set;
+ struct mail_namespace_settings *const *namespaces;
+ const char *name;
+ unsigned int i, count;
+
+ name = ns->prefix != NULL ? ns->prefix : "";
+
+ if (ns->separator != NULL &&
+ ns->separator[0] != '\0' && ns->separator[1] != '\0') {
+ *error_r = t_strdup_printf("Namespace '%s': "
+ "Hierarchy separator must be only one character long",
+ name);
+ return FALSE;
+ }
+
+ if (ns->alias_for != NULL) {
+ namespaces = array_get(&ns->user_set->namespaces, &count);
+ for (i = 0; i < count; i++) {
+ if (strcmp(namespaces[i]->prefix, ns->alias_for) == 0)
+ break;
+ }
+ if (i == count) {
+ *error_r = t_strdup_printf(
+ "Namespace '%s': alias_for points to "
+ "unknown namespace: %s", name, ns->alias_for);
+ return FALSE;
+ }
+ if (namespaces[i]->alias_for != NULL) {
+ *error_r = t_strdup_printf(
+ "Namespace '%s': alias_for chaining isn't "
+ "allowed: %s -> %s", name, ns->alias_for,
+ namespaces[i]->alias_for);
+ return FALSE;
+ }
+ }
+ return TRUE;
+ }
+ /* </settings checks> */
}
int mail_storage_create(struct mail_namespace *ns, const char *driver,
- const char *data, enum mail_storage_flags flags,
- enum file_lock_method lock_method,
- const char **error_r)
+ enum mail_storage_flags flags, const char **error_r)
{
- struct mail_storage *storage_class, *storage;
+ struct mail_storage *storage_class, *storage = NULL;
struct mail_storage *const *classes;
- const char *home, *value;
+ const char *data = ns->set->location;
+ const char *home, *p;
unsigned int i, count;
+ if ((flags & MAIL_STORAGE_FLAG_KEEP_HEADER_MD5) == 0 &&
+ ns->mail_set->pop3_uidl_format != NULL) {
+ /* if pop3_uidl_format contains %m, we want to keep the
+ header MD5 sums stored even if we're not running POP3
+ right now. */
+ p = ns->mail_set->pop3_uidl_format;
+ while ((p = strchr(p, '%')) != NULL) {
+ if (p[1] == '%')
+ p += 2;
+ else if (var_get_key(++p) == 'm') {
+ flags |= MAIL_STORAGE_FLAG_KEEP_HEADER_MD5;
+ break;
+ }
+ }
+ }
+
if (data == NULL)
data = "";
else if (driver == NULL)
return -1;
}
- value = getenv("MAIL_MAX_KEYWORD_LENGTH");
- storage->keyword_max_len = value != NULL ?
- atoi(value) : DEFAULT_MAX_KEYWORD_LENGTH;
+
if (hook_mail_storage_created != NULL) {
T_BEGIN {
hook_mail_storage_created(storage);
MAILBOX_LIST_PATH_TYPE_INDEX);
}
- enum mailbox_list_flags
- mail_storage_get_list_flags(enum mail_storage_flags storage_flags)
- {
- enum mailbox_list_flags list_flags = 0;
-
- if ((storage_flags & MAIL_STORAGE_FLAG_DEBUG) != 0)
- list_flags |= MAILBOX_LIST_FLAG_DEBUG;
- if ((storage_flags & MAIL_STORAGE_FLAG_FULL_FS_ACCESS) != 0)
- list_flags |= MAILBOX_LIST_FLAG_FULL_FS_ACCESS;
- if ((storage_flags & MAIL_STORAGE_FLAG_DOTLOCK_USE_EXCL) != 0)
- list_flags |= MAILBOX_LIST_FLAG_DOTLOCK_USE_EXCL;
- if ((storage_flags & MAIL_STORAGE_FLAG_NFS_FLUSH_STORAGE) != 0)
- list_flags |= MAILBOX_LIST_FLAG_NFS_FLUSH;
- return list_flags;
- }
-
+const char *mail_storage_get_temp_prefix(struct mail_storage *storage)
+{
+ const char *dir;
+
+ if (storage->temp_path_prefix == NULL) {
+ dir = mailbox_list_get_path(storage->list, NULL,
+ MAILBOX_LIST_PATH_TYPE_DIR);
+ storage->temp_path_prefix = p_strconcat(storage->pool, dir, "/",
+ mailbox_list_get_temp_prefix(storage->list), NULL);
+ }
+
+ return storage->temp_path_prefix;
+}
+
bool mail_storage_set_error_from_errno(struct mail_storage *storage)
{
const char *error_string;
#include "lib.h"
#include "array.h"
#include "hostpid.h"
+ #include "network.h"
+ #include "str.h"
+ #include "var-expand.h"
+ #include "settings-parser.h"
#include "auth-master.h"
+ #include "mail-storage-settings.h"
#include "mail-namespace.h"
+#include "mail-storage.h"
#include "mail-user.h"
#include <stdlib.h>
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. */
*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 */
return list->ns;
}
-void mailbox_list_get_permissions(struct mailbox_list *list,
+static mode_t get_dir_mode(mode_t mode)
+{
+ /* add the execute bit if either read or write bit is set */
+ if ((mode & 0600) != 0) mode |= 0100;
+ if ((mode & 0060) != 0) mode |= 0010;
+ if ((mode & 0006) != 0) mode |= 0001;
+ return mode;
+}
+
+ struct mail_user *
+ mailbox_list_get_user(const struct mailbox_list *list)
+ {
+ return list->ns->user;
+ }
+
+void mailbox_list_get_permissions(struct mailbox_list *list, const char *name,
mode_t *mode_r, gid_t *gid_r)
{
const char *path;
list->file_create_gid = st.st_gid;
}
- if ((list->flags & MAILBOX_LIST_FLAG_DEBUG) != 0 && name == NULL) {
- if (list->mail_set->mail_debug) {
++ if (list->mail_set->mail_debug && name == NULL) {
i_info("Namespace %s: Using permissions from %s: "
"mode=0%o gid=%ld", list->ns->prefix, path,
- (int)list->file_create_mode,
+ (int)list->dir_create_mode,
list->file_create_gid == (gid_t)-1 ? -1L :
(long)list->file_create_gid);
}
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). */
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);
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);
#include "common.h"
#include "lib-signals.h"
++#include "array.h"
#include "hash.h"
#include "str.h"
#include "env-util.h"
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;
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)
pid_t *pid_r)
{
const struct var_expand_table *var_expand_table;
- const char *p, *addr, *mail, *chroot_dir, *home_dir, *full_home_dir;
- const char *system_groups_user, *master_user;
+ const char *p, *addr, *chroot_dir, *home_dir, *full_home_dir;
- const char *system_user, *master_user, *key;
++ const char *system_groups_user, *master_user, *key;
struct mail_process_group *process_group;
char title[1024];
struct log_io *log;
}
t_array_init(&extra_args, 16);
- mail = home_dir = chroot_dir = system_groups_user = "";
- home_dir = chroot_dir = system_user = ""; master_user = NULL;
++ home_dir = chroot_dir = system_groups_user = "";
+ master_user = NULL;
uid = (uid_t)-1; gid = (gid_t)-1; nice_value = 0;
home_given = FALSE;
for (; *args != NULL; args++) {
log_set_prefix(log, str_c(str));
}
- child_process_init_env();
+ child_process_init_env(set);
- /* move the client socket into stdin and stdout fds, log to stderr */
- if (dup2(dump_capability ? null_fd : request->fd, 0) < 0)
- i_fatal("dup2(stdin) failed: %m");
- if (dup2(request->fd, 1) < 0)
- i_fatal("dup2(stdout) failed: %m");
- if (dup2(log_fd, 2) < 0)
- i_fatal("dup2(stderr) failed: %m");
-
- for (i = 0; i < 3; i++)
- fd_close_on_exec(i, FALSE);
-
/* setup environment - set the most important environment first
(paranoia about filling up environment without noticing) */
- restrict_access_set_env(system_user, uid, gid, set->mail_priv_gid_t,
+ restrict_access_set_env(system_groups_user, uid, gid,
+ set->mail_priv_gid_t,
dump_capability ? "" : chroot_dir,
set->first_valid_gid, set->last_valid_gid,
set->mail_access_groups);
i_snprintf(title, sizeof(title), "[%s %s]", user, addr);
}
- /* make sure we don't leak syslog fd, but do it last so that
- any errors above will be logged */
+ /* make sure we don't leak syslog fd. try to do it as late as possible,
+ but also before dup2()s in case syslog fd is one of them. */
closelog();
- if (dup2(dump_capability ? null_fd : request->fd, 0) < 0)
+ /* move the client socket into stdin and stdout fds, log to stderr */
++ if (dup2(request->fd, 0) < 0)
+ i_fatal("dup2(stdin) failed: %m");
+ if (dup2(request->fd, 1) < 0)
+ i_fatal("dup2(stdout) failed: %m");
+ if (dup2(log_fd, 2) < 0)
+ i_fatal("dup2(stderr) failed: %m");
+
+ for (i = 0; i < 3; i++)
+ fd_close_on_exec(i, FALSE);
+
if (set->mail_drop_priv_before_exec) {
restrict_access_by_env(TRUE);
/* privileged GID is now only in saved-GID. if we want to
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)
# include <sys/resource.h>
#endif
- enum settings_type {
- SETTINGS_TYPE_ROOT,
- SETTINGS_TYPE_SERVER,
- SETTINGS_TYPE_AUTH,
- SETTINGS_TYPE_AUTH_SOCKET,
- SETTINGS_TYPE_AUTH_PASSDB,
- SETTINGS_TYPE_AUTH_USERDB,
- SETTINGS_TYPE_NAMESPACE,
- SETTINGS_TYPE_SOCKET,
- SETTINGS_TYPE_DICT,
- SETTINGS_TYPE_PLUGIN
- };
+ extern struct setting_parser_info master_auth_setting_parser_info;
+ extern struct setting_parser_info master_setting_parser_info;
+ extern struct setting_parser_info master_auth_socket_setting_parser_info;
- struct settings_parse_ctx {
- enum settings_type type, parent_type;
- enum mail_protocol protocol;
+ #undef DEF
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct master_auth_socket_unix_settings, name), NULL }
- struct server_settings *root, *server;
- struct auth_settings *auth;
- struct socket_settings *socket;
- struct auth_socket_settings *auth_socket;
- struct auth_passdb_settings *auth_passdb;
- struct auth_userdb_settings *auth_userdb;
- struct namespace_settings *namespace;
+ static struct setting_define master_auth_socket_master_setting_defines[] = {
+ DEF(SET_STR, path),
- int level;
+ SETTING_DEFINE_LIST_END
};
- #include "master-settings-defs.c"
-
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, auth_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, auth_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_settings)
-
- static struct setting_def auth_setting_defs[] = {
- DEF_STR(mechanisms),
- DEF_STR(realms),
- DEF_STR(default_realm),
- DEF_INT(cache_size),
- DEF_INT(cache_ttl),
- DEF_INT(cache_negative_ttl),
- DEF_STR(executable),
- DEF_STR(user),
- DEF_STR(chroot),
- DEF_STR(username_chars),
- DEF_STR(username_translation),
- DEF_STR(username_format),
- DEF_STR(master_user_separator),
- DEF_STR(anonymous_username),
- DEF_STR(krb5_keytab),
- DEF_STR(gssapi_hostname),
- DEF_STR(winbind_helper_path),
- DEF_INT(failure_delay),
-
- DEF_BOOL(verbose),
- DEF_BOOL(debug),
- DEF_BOOL(debug_passwords),
- DEF_BOOL(ssl_require_client_cert),
- DEF_BOOL(ssl_username_from_cert),
- DEF_BOOL(use_winbind),
-
- DEF_INT(count),
- DEF_INT(worker_max_count),
- DEF_INT(process_size),
-
- { 0, NULL, 0 }
+ static struct master_auth_socket_unix_settings master_auth_socket_master_default_settings = {
+ MEMBER(path) "auth-master"
};
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, socket_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, socket_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, socket_settings)
+ struct setting_parser_info master_auth_socket_master_setting_parser_info = {
+ MEMBER(defines) master_auth_socket_master_setting_defines,
+ MEMBER(defaults) &master_auth_socket_master_default_settings,
+
+ MEMBER(parent) &master_auth_socket_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
- static struct setting_def socket_setting_defs[] = {
- DEF_STR(path),
- DEF_INT(mode),
- DEF_STR(user),
- DEF_STR(group),
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct master_auth_socket_unix_settings)
+ };
- { 0, NULL, 0 }
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct master_auth_socket_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+ { SET_DEFLIST, name, offsetof(struct master_auth_socket_settings, field), defines }
+ static struct setting_define master_auth_socket_setting_defines[] = {
+ DEF(SET_STR, type),
+ DEFLIST(masters, "master", &master_auth_socket_master_setting_parser_info),
+
+ SETTING_DEFINE_LIST_END
};
- static struct setting_def auth_socket_setting_defs[] = {
- DEF_STRUCT_STR(type, auth_socket_settings),
+ struct setting_parser_info master_auth_socket_setting_parser_info = {
+ MEMBER(defines) master_auth_socket_setting_defines,
+ MEMBER(defaults) NULL,
+
+ MEMBER(parent) &master_auth_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
- { 0, NULL, 0 }
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) offsetof(struct master_auth_socket_settings, type),
+ MEMBER(struct_size) sizeof(struct master_auth_socket_settings)
};
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, auth_passdb_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, auth_passdb_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, auth_passdb_settings)
-
- static struct setting_def auth_passdb_setting_defs[] = {
- DEF_STR(driver),
- DEF_STR(args),
- DEF_BOOL(deny),
- DEF_BOOL(pass),
- DEF_BOOL(master),
-
- { 0, NULL, 0 }
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct master_auth_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+ { SET_DEFLIST, name, offsetof(struct master_auth_settings, field), defines }
+
+ static struct setting_define master_auth_setting_defines[] = {
+ DEF(SET_STR, name),
+ DEF(SET_STR, executable),
+ DEF(SET_STR, user),
+ DEF(SET_STR, chroot),
+ DEF(SET_UINT, count),
+ DEF(SET_UINT, process_size),
+ DEF(SET_STR, mechanisms),
+ DEF(SET_BOOL, debug),
+ DEFLIST(sockets, "socket", &master_auth_socket_setting_parser_info),
+
+ SETTING_DEFINE_LIST_END
};
- static struct setting_def auth_userdb_setting_defs[] = {
- DEF_STRUCT_STR(driver, auth_userdb_settings),
- DEF_STRUCT_STR(args, auth_userdb_settings),
+ static struct master_auth_settings master_auth_default_settings = {
+ MEMBER(name) "default",
+ MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
+ MEMBER(user) "root",
+ MEMBER(chroot) "",
+ MEMBER(count) 1,
+ MEMBER(mechanisms) "plain",
+ MEMBER(debug) FALSE,
+ MEMBER(process_size) 256
- { 0, NULL, 0 }
+ /* .. */
};
- #undef DEF_STR
- #undef DEF_INT
- #undef DEF_BOOL
- #define DEF_STR(name) DEF_STRUCT_STR(name, namespace_settings)
- #define DEF_INT(name) DEF_STRUCT_INT(name, namespace_settings)
- #define DEF_BOOL(name) DEF_STRUCT_BOOL(name, namespace_settings)
-
- static struct setting_def namespace_setting_defs[] = {
- DEF_STR(type),
- DEF_STR(separator),
- DEF_STR(prefix),
- DEF_STR(location),
- DEF_STR(alias_for),
- DEF_BOOL(inbox),
- DEF_BOOL(hidden),
- DEF_STR(list),
- DEF_BOOL(subscriptions),
-
- { 0, NULL, 0 }
+ struct setting_parser_info master_auth_setting_parser_info = {
+ MEMBER(defines) master_auth_setting_defines,
+ MEMBER(defaults) &master_auth_default_settings,
+
+ MEMBER(parent) &master_setting_parser_info,
+ MEMBER(dynamic_parsers) NULL,
+
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) offsetof(struct master_auth_settings, name),
+ MEMBER(struct_size) sizeof(struct master_auth_settings)
};
- struct settings default_settings = {
- MEMBER(server) NULL,
- MEMBER(protocol) 0,
+ #undef DEF
+ #undef DEFLIST
+ #define DEF(type, name) \
+ { type, #name, offsetof(struct master_settings, name), NULL }
+ #define DEFLIST(field, name, defines) \
+ { SET_DEFLIST, name, offsetof(struct master_settings, field), defines }
+
+ static struct setting_define master_setting_defines[] = {
+ /* common */
+ DEF(SET_STR, base_dir),
+ DEF(SET_STR, log_path),
+ DEF(SET_STR, info_log_path),
+ DEF(SET_STR, log_timestamp),
+ DEF(SET_STR, syslog_facility),
+
+ /* general */
+ DEF(SET_STR, protocols),
+ DEF(SET_STR, listen),
+ DEF(SET_STR, ssl_listen),
+
+ DEF(SET_STR, ssl),
+ DEF(SET_STR, ssl_key_file),
+ DEF(SET_UINT, ssl_parameters_regenerate),
+ DEF(SET_BOOL, version_ignore),
+
+ /* login */
+ DEF(SET_STR, login_dir),
+ DEF(SET_STR, login_executable),
+ DEF(SET_STR, login_user),
+
+ DEF(SET_BOOL, login_process_per_connection),
+ DEF(SET_BOOL, login_chroot),
+ DEF(SET_BOOL, disable_plaintext_auth),
+
+ DEF(SET_UINT, login_process_size),
+ DEF(SET_UINT, login_processes_count),
+ DEF(SET_UINT, login_max_processes_count),
+
+ /* mail */
+ DEF(SET_STR, valid_chroot_dirs),
+ DEF(SET_STR, mail_chroot),
+ DEF(SET_UINT, max_mail_processes),
+ DEF(SET_UINT, mail_max_userip_connections),
+ DEF(SET_BOOL, verbose_proctitle),
+
+ DEF(SET_UINT, first_valid_uid),
+ DEF(SET_UINT, last_valid_uid),
+ DEF(SET_UINT, first_valid_gid),
+ DEF(SET_UINT, last_valid_gid),
+ DEF(SET_STR, mail_access_groups),
+ DEF(SET_STR, mail_privileged_group),
+ DEF(SET_STR, mail_uid),
+ DEF(SET_STR, mail_gid),
+
+ DEF(SET_STR, mail_plugins),
+ DEF(SET_STR, imap_capability),
+
+ DEF(SET_STR_VARS, mail_location),
+ DEF(SET_BOOL, mail_debug),
- DEF(SET_UINT, umask),
+ DEF(SET_BOOL, mail_drop_priv_before_exec),
+
+ DEF(SET_STR, mail_executable),
+ DEF(SET_UINT, mail_process_size),
+ DEF(SET_STR, mail_log_prefix),
+ DEF(SET_UINT, mail_log_max_lines_per_sec),
+ /* dict */
+ DEF(SET_STR, dict_db_config),
+ DEF(SET_UINT, dict_process_count),
+ DEFLIST(auths, "auth", &master_auth_setting_parser_info),
+ { SET_STRLIST, "dict", offsetof(struct master_settings, dicts), NULL },
+
+ SETTING_DEFINE_LIST_END
+ };
+
+ struct master_settings master_default_settings = {
/* common */
MEMBER(base_dir) PKG_RUNDIR,
MEMBER(log_path) "",
MEMBER(mail_privileged_group) "",
MEMBER(mail_uid) "",
MEMBER(mail_gid) "",
+ MEMBER(mail_plugins) "",
+ MEMBER(imap_capability) "",
MEMBER(mail_location) "",
- MEMBER(mail_cache_fields) "",
- MEMBER(mail_never_cache_fields) "imap.envelope",
- MEMBER(mail_cache_min_mail_count) 0,
- MEMBER(mailbox_idle_check_interval) 30,
MEMBER(mail_debug) FALSE,
- MEMBER(mail_full_filesystem_access) FALSE,
- MEMBER(mail_max_keyword_length) 50,
- MEMBER(mail_save_crlf) FALSE,
- #ifdef MMAP_CONFLICTS_WRITE
- MEMBER(mmap_disable) TRUE,
- #else
- MEMBER(mmap_disable) FALSE,
- #endif
- MEMBER(dotlock_use_excl) TRUE,
- MEMBER(fsync_disable) FALSE,
- MEMBER(mail_nfs_storage) FALSE,
- MEMBER(mail_nfs_index) FALSE,
- MEMBER(mailbox_list_index_disable) TRUE,
- MEMBER(lock_method) "fcntl",
- MEMBER(maildir_stat_dirs) FALSE,
- MEMBER(maildir_copy_with_hardlinks) TRUE,
- MEMBER(maildir_copy_preserve_filename) FALSE,
- MEMBER(umask) 0077,
+ MEMBER(maildir_very_dirty_syncs) FALSE,
- MEMBER(mbox_read_locks) "fcntl",
- MEMBER(mbox_write_locks) "dotlock fcntl",
- MEMBER(mbox_lock_timeout) 300,
- MEMBER(mbox_dotlock_change_timeout) 120,
- MEMBER(mbox_min_index_size) 0,
- MEMBER(mbox_dirty_syncs) TRUE,
- MEMBER(mbox_very_dirty_syncs) FALSE,
- MEMBER(mbox_lazy_writes) TRUE,
- MEMBER(dbox_rotate_size) 2048,
- MEMBER(dbox_rotate_min_size) 16,
- MEMBER(dbox_rotate_days) 1,
+ MEMBER(dbox_purge_min_percentage) 0,
MEMBER(mail_drop_priv_before_exec) FALSE,
- MEMBER(mail_executable) PKG_LIBEXECDIR"/imap",
+ MEMBER(mail_executable) NULL,
MEMBER(mail_process_size) 256,
- MEMBER(mail_plugins) "",
- MEMBER(mail_plugin_dir) MODULEDIR"/imap",
MEMBER(mail_log_prefix) "%Us(%u): ",
MEMBER(mail_log_max_lines_per_sec) 10,
/* .. */
};
- struct auth_settings default_auth_settings = {
+ struct setting_parser_info master_setting_parser_info = {
+ MEMBER(defines) master_setting_defines,
+ MEMBER(defaults) &master_default_settings,
+
MEMBER(parent) NULL,
- MEMBER(next) NULL,
+ MEMBER(dynamic_parsers) NULL,
- MEMBER(name) NULL,
- MEMBER(mechanisms) "plain",
- MEMBER(realms) "",
- MEMBER(default_realm) "",
- MEMBER(cache_size) 0,
- MEMBER(cache_ttl) 3600,
- MEMBER(cache_negative_ttl) 3600,
- MEMBER(executable) PKG_LIBEXECDIR"/dovecot-auth",
- MEMBER(user) "root",
- MEMBER(chroot) "",
- MEMBER(username_chars) "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890.-_@",
- MEMBER(username_translation) "",
- MEMBER(username_format) "",
- MEMBER(master_user_separator) "",
- MEMBER(anonymous_username) "anonymous",
- MEMBER(krb5_keytab) "",
- MEMBER(gssapi_hostname) "",
- MEMBER(winbind_helper_path) "/usr/bin/ntlm_auth",
- MEMBER(failure_delay) 2,
-
- MEMBER(verbose) FALSE,
- MEMBER(debug) FALSE,
- MEMBER(debug_passwords) FALSE,
- MEMBER(ssl_require_client_cert) FALSE,
- MEMBER(ssl_username_from_cert) FALSE,
- MEMBER(use_winbind) FALSE,
+ MEMBER(parent_offset) (size_t)-1,
+ MEMBER(type_offset) (size_t)-1,
+ MEMBER(struct_size) sizeof(struct master_settings)
+ };
- MEMBER(count) 1,
- MEMBER(worker_max_count) 30,
- MEMBER(process_size) 256,
+ static pool_t settings_pool, settings2_pool;
+ struct master_server_settings *master_set = NULL;
- /* .. */
- MEMBER(uid) 0,
- MEMBER(gid) 0,
- MEMBER(passdbs) NULL,
- MEMBER(userdbs) NULL,
- MEMBER(sockets) NULL
- };
+ #ifdef HAVE_MODULES
+ static const char *
+ get_process_capability(enum process_type ptype, struct master_settings *set)
+ {
+ /* FIXME: pretty ugly code just for getting the capability
+ automatically */
+ static const char *args[] = {
+ "uid=65534",
+ "gid=65534",
+ "home=/tmp",
+ NULL
+ };
+ const char *pname = process_names[ptype];
+ enum master_login_status login_status;
+ struct mail_login_request request;
+ char buf[4096];
+ int fd[2], status;
+ ssize_t ret;
+ unsigned int pos;
+ uid_t uid;
+ pid_t pid;
- struct socket_settings default_socket_settings = {
- #define DEFAULT_MASTER_SOCKET_PATH "auth-master"
- #define DEFAULT_CLIENT_SOCKET_PATH "auth-client"
- MEMBER(path) "",
- MEMBER(mode) 0600,
- MEMBER(user) "",
- MEMBER(group) ""
- };
+ uid = geteuid();
+ if (uid != 0) {
+ /* use the current user */
+ args[0] = t_strdup_printf("uid=%s", dec2str(uid));
+ args[1] = t_strdup_printf("gid=%s", dec2str(getegid()));
+ }
- struct namespace_settings default_namespace_settings = {
- MEMBER(parent) NULL,
- MEMBER(next) NULL,
- MEMBER(type) NULL,
-
- MEMBER(separator) "",
- MEMBER(prefix) "",
- MEMBER(location) "",
- MEMBER(alias_for) NULL,
-
- MEMBER(inbox) FALSE,
- MEMBER(hidden) FALSE,
- MEMBER(list) "yes",
- MEMBER(subscriptions) TRUE
- };
- if (pipe(fd) < 0) {
- i_error("pipe() failed: %m");
++ if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) {
++ i_error("socketpair() failed: %m");
+ return NULL;
+ }
+ fd_close_on_exec(fd[0], TRUE);
+ fd_close_on_exec(fd[1], TRUE);
- static pool_t settings_pool, settings2_pool;
- struct server_settings *settings_root = NULL;
+ memset(&request, 0, sizeof(request));
+ request.fd = fd[1];
+ login_status = create_mail_process(ptype, set, &request,
+ "dump-capability", args, NULL, TRUE,
+ &pid);
+ if (login_status != MASTER_LOGIN_STATUS_OK) {
+ (void)close(fd[0]);
+ (void)close(fd[1]);
+ return NULL;
+ }
+ (void)close(fd[1]);
+
+ alarm(5);
+ if (wait(&status) == -1) {
+ i_fatal("%s dump-capability process %d got stuck",
+ pname, (int)pid);
+ }
+ alarm(0);
+
+ if (status != 0) {
+ (void)close(fd[0]);
+ if (WIFSIGNALED(status)) {
+ i_error("%s dump-capability process "
+ "killed with signal %d",
+ pname, WTERMSIG(status));
+ } else {
+ i_error("%s dump-capability process returned %d",
+ pname, WIFEXITED(status) ? WEXITSTATUS(status) :
+ status);
+ }
+ return NULL;
+ }
+
+ pos = 0;
+ while ((ret = read(fd[0], buf + pos, sizeof(buf) - pos)) > 0)
+ pos += ret;
+
+ if (ret < 0) {
+ i_error("read(%s dump-capability process) failed: %m", pname);
+ (void)close(fd[0]);
+ return NULL;
+ }
+ (void)close(fd[0]);
+
+ if (pos == 0 || buf[pos-1] != '\n') {
+ i_error("%s dump-capability: Couldn't read capability "
+ "(got %u bytes)", pname, pos);
+ return NULL;
+ }
+ buf[pos-1] = '\0';
+
+ return i_strdup(buf);
+ }
+
+ static bool get_imap_capability(struct master_settings *set)
+ {
+ static const char *generated_capability = NULL;
+
+ if (generated_capability != NULL) {
+ /* Reloading configuration. Don't try to execute the imap
+ process again. Too risky and the wait() call below will
+ break it anyway. Just use the previous capability list we
+ already had generated. */
+ set->imap_generated_capability =
+ p_strdup(settings_pool, generated_capability);
+ return TRUE;
+ }
+
+ generated_capability = get_process_capability(PROCESS_TYPE_IMAP, set);
+ if (generated_capability == NULL)
+ return FALSE;
+
+ set->imap_generated_capability =
+ p_strdup(settings_pool, generated_capability);
+ return TRUE;
+ }
+ #endif
- static void fix_base_path(struct settings *set, const char **str)
+ static void fix_base_path(struct master_settings *set, const char **str)
{
if (*str != NULL && **str != '\0' && **str != '/') {
*str = p_strconcat(settings_pool,
return TRUE;
}
- static bool login_want_core_dumps(struct settings *set)
++static bool login_want_core_dumps(struct master_server_settings *set)
+{
+ const char *p;
+
- p = set->server->pop3 == NULL ? NULL :
- strstr(set->server->pop3->login_executable, " -D");
++ p = set->pop3 == NULL ? NULL :
++ strstr(set->pop3->login_executable, " -D");
+ if (p != NULL && p[3] == '\0')
+ return TRUE;
- p = set->server->imap == NULL ? NULL :
- strstr(set->server->imap->login_executable, " -D");
++ p = set->imap == NULL ? NULL :
++ strstr(set->imap->login_executable, " -D");
+ if (p != NULL && p[3] == '\0')
+ return TRUE;
+ return FALSE;
+}
+
- static bool settings_do_fixes(struct settings *set)
+ static bool settings_do_fixes(struct master_settings *set)
{
struct stat st;
return FALSE;
}
-- if (!settings_have_connect_sockets(set)) {
-- /* we are not using external authentication, so make sure the
-- login directory exists with correct permissions and it's
-- empty. with external auth we wouldn't want to delete
-- existing sockets or break the permissions required by the
-- auth server. */
- mode_t mode = login_want_core_dumps(set) ? 0770 : 0750;
- if (safe_mkdir(set->login_dir, mode,
- if (safe_mkdir(set->login_dir, 0750,
-- master_uid, set->server->login_gid) == 0) {
-- i_warning("Corrected permissions for login directory "
-- "%s", set->login_dir);
-- }
--
-- unlink_auth_sockets(set->login_dir, "");
-- }
--
#ifdef HAVE_MODULES
if (*set->mail_plugins != '\0' && set->protocol == MAIL_PROTOCOL_IMAP &&
*set->imap_capability == '\0') {
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;
}
}
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;
client->last_input = ioloop_time;
client->to_idle = timeout_add(CLIENT_IDLE_TIMEOUT_MSECS,
client_idle_timeout, client);
- if (!lock_session) {
++ if (!set->pop3_lock_session) {
+ client->to_commit = timeout_add(CLIENT_COMMIT_TIMEOUT_MSECS,
+ client_commit_timeout, client);
+ }
client->user = user;
return NULL;
}
- if (!no_flag_updates && client->messages_count > 0)
++ if (!set->pop3_no_flag_updates && client->messages_count > 0)
+ client->seen_bitmask = i_malloc(MSGS_BITMASK_SIZE(client));
+
i_assert(my_client == NULL);
my_client = client;
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;
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,
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 :) */
/* Log file or syslog opening probably requires roots */
open_logfile();
- /* Load the plugins before chrooting. Their init() is called later. */
- if (getenv("MAIL_PLUGINS") != NULL) {
- const char *plugin_dir = getenv("MAIL_PLUGIN_DIR");
+ mail_storage_init();
+ mail_storage_register_all();
+ mailbox_list_register_all();
- if (plugin_dir == NULL)
- plugin_dir = MODULEDIR"/pop3";
- modules = module_dir_load(plugin_dir, getenv("MAIL_PLUGINS"),
- TRUE, version);
- }
+ /* read settings after registering storages so they can have their
+ own setting definitions too */
+ pop3_settings_read(set_r, user_set_r);
+
+ /* Load the plugins before chrooting. Their init() is called later. */
+ modules = *(*set_r)->mail_plugins == '\0' ? NULL :
+ module_dir_load((*set_r)->mail_plugin_dir,
+ (*set_r)->mail_plugins, TRUE, version);
restrict_access_by_env(!IS_STANDALONE());
+ restrict_access_allow_coredumps(TRUE);
}
- static bool main_init(void)
+ static bool main_init(const struct pop3_settings *set,
+ const struct mail_user_settings *user_set)
{
struct mail_user *user;
struct client *client;
if (getenv("USER") == NULL)
i_fatal("USER environment missing");
- if (getenv("DEBUG") != NULL) {
+ if (set->mail_debug) {
- i_info("Effective uid=%s, gid=%s",
- dec2str(geteuid()), dec2str(getegid()));
+ const char *home;
+
+ home = getenv("HOME");
+ i_info("Effective uid=%s, gid=%s, home=%s",
+ dec2str(geteuid()), dec2str(getegid()),
+ home != NULL ? home : "(none)");
}
- if (getenv("STDERR_CLOSE_SHUTDOWN") != NULL) {
+ if (set->shutdown_clients) {
/* If master dies, the log fd gets closed and we'll quit */
log_io = io_add(STDERR_FILENO, IO_ERROR,
log_error_callback, NULL);