summaryrefslogtreecommitdiffstats
path: root/src/lib-storage/mail-user.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib-storage/mail-user.c')
-rw-r--r--src/lib-storage/mail-user.c848
1 files changed, 848 insertions, 0 deletions
diff --git a/src/lib-storage/mail-user.c b/src/lib-storage/mail-user.c
new file mode 100644
index 0000000..9cf6042
--- /dev/null
+++ b/src/lib-storage/mail-user.c
@@ -0,0 +1,848 @@
+/* Copyright (c) 2008-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "array.h"
+#include "hostpid.h"
+#include "ioloop.h"
+#include "net.h"
+#include "module-dir.h"
+#include "home-expand.h"
+#include "file-create-locked.h"
+#include "mkdir-parents.h"
+#include "safe-mkstemp.h"
+#include "str.h"
+#include "strescape.h"
+#include "var-expand.h"
+#include "settings-parser.h"
+#include "iostream-ssl.h"
+#include "fs-api.h"
+#include "auth-master.h"
+#include "master-service.h"
+#include "master-service-ssl-settings.h"
+#include "dict.h"
+#include "mail-storage-settings.h"
+#include "mail-storage-private.h"
+#include "mail-storage-service.h"
+#include "mail-namespace.h"
+#include "mail-storage.h"
+#include "mailbox-list-private.h"
+#include "mail-autoexpunge.h"
+#include "mail-user.h"
+
+
+struct mail_user_module_register mail_user_module_register = { 0 };
+struct auth_master_connection *mail_user_auth_master_conn;
+
+static void mail_user_deinit_base(struct mail_user *user)
+{
+ if (user->_attr_dict != NULL) {
+ dict_wait(user->_attr_dict);
+ dict_deinit(&user->_attr_dict);
+ }
+ mail_namespaces_deinit(&user->namespaces);
+ if (user->_service_user != NULL)
+ mail_storage_service_user_unref(&user->_service_user);
+}
+
+static void mail_user_deinit_pre_base(struct mail_user *user ATTR_UNUSED)
+{
+}
+
+static void mail_user_stats_fill_base(struct mail_user *user ATTR_UNUSED,
+ struct stats *stats ATTR_UNUSED)
+{
+}
+
+static struct mail_user *
+mail_user_alloc_int(struct event *parent_event,
+ const char *username,
+ const struct setting_parser_info *set_info,
+ const struct mail_user_settings *set, pool_t pool)
+{
+ struct mail_user *user;
+ const char *error;
+
+ i_assert(username != NULL);
+ i_assert(*username != '\0');
+
+ user = p_new(pool, struct mail_user, 1);
+ user->pool = pool;
+ user->refcount = 1;
+ user->username = p_strdup(pool, username);
+ user->set_info = set_info;
+ user->unexpanded_set = set;
+ user->set = settings_dup_with_pointers(set_info, user->unexpanded_set, pool);
+ user->service = master_service_get_name(master_service);
+ user->default_normalizer = uni_utf8_to_decomposed_titlecase;
+ user->session_create_time = ioloop_time;
+ user->event = event_create(parent_event);
+ event_add_category(user->event, &event_category_storage);
+ event_add_str(user->event, "user", username);
+
+ /* check settings so that the duplicated structure will again
+ contain the parsed fields */
+ if (!settings_check(set_info, pool, user->set, &error))
+ i_panic("Settings check unexpectedly failed: %s", error);
+
+ user->v.deinit = mail_user_deinit_base;
+ user->v.deinit_pre = mail_user_deinit_pre_base;
+ user->v.stats_fill = mail_user_stats_fill_base;
+ p_array_init(&user->module_contexts, user->pool, 5);
+ return user;
+}
+
+struct mail_user *
+mail_user_alloc_nodup_set(struct event *parent_event,
+ const char *username,
+ const struct setting_parser_info *set_info,
+ const struct mail_user_settings *set)
+{
+ pool_t pool;
+
+ pool = pool_alloconly_create(MEMPOOL_GROWING"mail user", 16*1024);
+ return mail_user_alloc_int(parent_event, username, set_info, set, pool);
+}
+
+struct mail_user *mail_user_alloc(struct event *parent_event,
+ const char *username,
+ const struct setting_parser_info *set_info,
+ const struct mail_user_settings *set)
+{
+ pool_t pool;
+
+ pool = pool_alloconly_create(MEMPOOL_GROWING"mail user", 16*1024);
+ return mail_user_alloc_int(parent_event, username, set_info,
+ settings_dup(set_info, set, pool), pool);
+}
+
+static void
+mail_user_expand_plugins_envs(struct mail_user *user)
+{
+ const char **envs, *home, *error;
+ string_t *str;
+ unsigned int i, count;
+
+ if (!array_is_created(&user->set->plugin_envs))
+ return;
+
+ str = t_str_new(256);
+ envs = array_get_modifiable(&user->set->plugin_envs, &count);
+ i_assert((count % 2) == 0);
+ for (i = 0; i < count; i += 2) {
+ if (user->_home == NULL &&
+ var_has_key(envs[i+1], 'h', "home") &&
+ mail_user_get_home(user, &home) <= 0) {
+ user->error = p_strdup_printf(user->pool,
+ "userdb didn't return a home directory, "
+ "but plugin setting %s used it (%%h): %s",
+ envs[i], envs[i+1]);
+ return;
+ }
+ str_truncate(str, 0);
+ if (var_expand_with_funcs(str, envs[i+1],
+ mail_user_var_expand_table(user),
+ mail_user_var_expand_func_table, user,
+ &error) <= 0) {
+ user->error = p_strdup_printf(user->pool,
+ "Failed to expand plugin setting %s = '%s': %s",
+ envs[i], envs[i+1], error);
+ return;
+ }
+ envs[i+1] = p_strdup(user->pool, str_c(str));
+ }
+}
+
+int mail_user_init(struct mail_user *user, const char **error_r)
+{
+ const struct mail_storage_settings *mail_set;
+ const char *home, *key, *value, *error;
+ bool need_home_dir;
+
+ need_home_dir = user->_home == NULL &&
+ settings_vars_have_key(user->set_info, user->set,
+ 'h', "home", &key, &value);
+ if (need_home_dir && mail_user_get_home(user, &home) <= 0) {
+ user->error = p_strdup_printf(user->pool,
+ "userdb didn't return a home directory, "
+ "but %s used it (%%h): %s", key, value);
+ }
+
+ /* expand settings after we can expand %h */
+ if (settings_var_expand_with_funcs(user->set_info, user->set,
+ user->pool, mail_user_var_expand_table(user),
+ mail_user_var_expand_func_table, user,
+ &error) <= 0) {
+ user->error = p_strdup_printf(user->pool,
+ "Failed to expand settings: %s", error);
+ }
+ user->settings_expanded = TRUE;
+ mail_user_expand_plugins_envs(user);
+
+ /* autocreated users for shared mailboxes need to be fully initialized
+ if they don't exist, since they're going to be used anyway */
+ if (user->error == NULL || user->nonexistent) {
+ mail_set = mail_user_set_get_storage_set(user);
+ user->mail_debug = mail_set->mail_debug;
+
+ user->initialized = TRUE;
+ hook_mail_user_created(user);
+ }
+
+ if (user->error != NULL) {
+ *error_r = t_strdup(user->error);
+ return -1;
+ }
+ process_stat_read_start(&user->proc_stat, user->event);
+ return 0;
+}
+
+void mail_user_ref(struct mail_user *user)
+{
+ i_assert(user->refcount > 0);
+
+ user->refcount++;
+}
+
+void mail_user_unref(struct mail_user **_user)
+{
+ struct mail_user *user = *_user;
+
+ i_assert(user->refcount > 0);
+
+ *_user = NULL;
+ if (user->refcount > 1) {
+ user->refcount--;
+ return;
+ }
+
+ user->deinitializing = TRUE;
+
+ /* call deinit() and deinit_pre() with refcount=1, otherwise we may
+ assert-crash in mail_user_ref() that is called by some handlers. */
+ T_BEGIN {
+ user->v.deinit_pre(user);
+ user->v.deinit(user);
+ } T_END;
+ event_unref(&user->event);
+ i_assert(user->refcount == 1);
+ pool_unref(&user->pool);
+}
+
+static void mail_user_session_finished(struct mail_user *user)
+{
+ struct event *ev = user->event;
+ struct process_stat *stat = &user->proc_stat;
+
+ process_stat_read_finish(stat, ev);
+
+ struct event_passthrough *e = event_create_passthrough(ev)->
+ set_name("mail_user_session_finished")->
+ add_int_nonzero("utime", stat->utime)->
+ add_int_nonzero("stime", stat->stime)->
+ add_int_nonzero("minor_faults", stat->minor_faults)->
+ add_int_nonzero("major_faults", stat->major_faults)->
+ add_int_nonzero("vol_cs", stat->vol_cs)->
+ add_int_nonzero("invol_cs", stat->invol_cs)->
+ add_int_nonzero("rss", stat->rss)->
+ add_int_nonzero("vsz", stat->vsz)->
+ add_int_nonzero("rchar", stat->rchar)->
+ add_int_nonzero("wchar", stat->wchar)->
+ add_int_nonzero("syscr", stat->syscr)->
+ add_int_nonzero("syscw", stat->syscw);
+ e_debug(e->event(), "User session is finished");
+}
+
+void mail_user_deinit(struct mail_user **user)
+{
+ mail_user_session_finished(*user);
+ i_assert((*user)->refcount == 1);
+ mail_user_unref(user);
+}
+
+struct mail_user *mail_user_find(struct mail_user *user, const char *name)
+{
+ struct mail_namespace *ns;
+
+ for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+ if (ns->owner != NULL && strcmp(ns->owner->username, name) == 0)
+ return ns->owner;
+ }
+ return NULL;
+}
+
+static void
+mail_user_connection_init_from(struct mail_user_connection_data *conn,
+ pool_t pool, const struct mail_user_connection_data *src)
+{
+ *conn = *src;
+
+ if (src->local_ip != NULL && src->local_ip->family != 0) {
+ conn->local_ip = p_new(pool, struct ip_addr, 1);
+ *conn->local_ip = *src->local_ip;
+ }
+ if (src->remote_ip != NULL && src->remote_ip->family != 0) {
+ conn->remote_ip = p_new(pool, struct ip_addr, 1);
+ *conn->remote_ip = *src->remote_ip;
+ }
+}
+
+void mail_user_set_vars(struct mail_user *user, const char *service,
+ const struct mail_user_connection_data *conn)
+{
+ i_assert(service != NULL);
+
+ user->service = p_strdup(user->pool, service);
+ mail_user_connection_init_from(&user->conn, user->pool, conn);
+}
+
+const struct var_expand_table *
+mail_user_var_expand_table(struct mail_user *user)
+{
+ /* use a cached table, unless home directory has been set afterwards */
+ if (user->var_expand_table != NULL &&
+ user->var_expand_table[4].value == user->_home)
+ return user->var_expand_table;
+
+ const char *username =
+ p_strdup(user->pool, t_strcut(user->username, '@'));
+ const char *domain = i_strchr_to_next(user->username, '@');
+ const char *local_ip = user->conn.local_ip == NULL ? NULL :
+ p_strdup(user->pool, net_ip2addr(user->conn.local_ip));
+ const char *remote_ip = user->conn.remote_ip == NULL ? NULL :
+ p_strdup(user->pool, net_ip2addr(user->conn.remote_ip));
+
+ const char *auth_user, *auth_username, *auth_domain;
+ if (user->auth_user == NULL) {
+ auth_user = user->username;
+ auth_username = username;
+ auth_domain = domain;
+ } else {
+ auth_user = user->auth_user;
+ auth_username =
+ p_strdup(user->pool, t_strcut(user->auth_user, '@'));
+ auth_domain = i_strchr_to_next(user->auth_user, '@');
+ }
+
+ const struct var_expand_table stack_tab[] = {
+ { 'u', user->username, "user" },
+ { 'n', username, "username" },
+ { 'd', domain, "domain" },
+ { 's', user->service, "service" },
+ { 'h', user->_home /* don't look it up unless we need it */, "home" },
+ { 'l', local_ip, "lip" },
+ { 'r', remote_ip, "rip" },
+ { 'p', my_pid, "pid" },
+ { 'i', p_strdup(user->pool, dec2str(user->uid)), "uid" },
+ { '\0', p_strdup(user->pool, dec2str(user->gid)), "gid" },
+ { '\0', user->session_id, "session" },
+ { '\0', auth_user, "auth_user" },
+ { '\0', auth_username, "auth_username" },
+ { '\0', auth_domain, "auth_domain" },
+ { '\0', user->set->hostname, "hostname" },
+ /* aliases: */
+ { '\0', local_ip, "local_ip" },
+ { '\0', remote_ip, "remote_ip" },
+ /* NOTE: keep this synced with imap-hibernate's
+ imap_client_var_expand_table() */
+ { '\0', NULL, NULL }
+ };
+ struct var_expand_table *tab;
+
+ tab = p_malloc(user->pool, sizeof(stack_tab));
+ memcpy(tab, stack_tab, sizeof(stack_tab));
+
+ user->var_expand_table = tab;
+ return user->var_expand_table;
+}
+
+static int
+mail_user_var_expand_func_userdb(const char *data, void *context,
+ const char **value_r,
+ const char **error_r ATTR_UNUSED)
+{
+ struct mail_user *user = context;
+
+ *value_r = mail_storage_service_fields_var_expand(data, user->userdb_fields);
+ return 1;
+}
+
+void mail_user_set_home(struct mail_user *user, const char *home)
+{
+ user->_home = p_strdup(user->pool, home);
+ user->home_looked_up = TRUE;
+}
+
+void mail_user_add_namespace(struct mail_user *user,
+ struct mail_namespace **namespaces)
+{
+ struct mail_namespace **tmp, *next, *ns = *namespaces;
+
+ for (; ns != NULL; ns = next) {
+ next = ns->next;
+
+ tmp = &user->namespaces;
+ for (; *tmp != NULL; tmp = &(*tmp)->next) {
+ i_assert(*tmp != ns);
+ if (strlen(ns->prefix) < strlen((*tmp)->prefix))
+ break;
+ }
+ ns->next = *tmp;
+ *tmp = ns;
+ }
+ *namespaces = user->namespaces;
+
+ T_BEGIN {
+ hook_mail_namespaces_added(user->namespaces);
+ } T_END;
+}
+
+void mail_user_drop_useless_namespaces(struct mail_user *user)
+{
+ struct mail_namespace *ns, *next;
+
+ /* drop all autocreated unusable (typically shared) namespaces.
+ don't drop the autocreated prefix="" namespace that we explicitly
+ created for being the fallback namespace. */
+ for (ns = user->namespaces; ns != NULL; ns = next) {
+ next = ns->next;
+
+ if (mail_namespace_is_removable(ns) && ns->prefix_len > 0)
+ mail_namespace_destroy(ns);
+ }
+}
+
+const char *mail_user_home_expand(struct mail_user *user, const char *path)
+{
+ (void)mail_user_try_home_expand(user, &path);
+ return path;
+}
+
+static int mail_user_userdb_lookup_home(struct mail_user *user)
+{
+ struct auth_user_info info;
+ struct auth_user_reply reply;
+ pool_t userdb_pool;
+ const char *username, *const *fields;
+ int ret;
+
+ i_assert(!user->home_looked_up);
+
+ i_zero(&info);
+ info.service = user->service;
+ if (user->conn.local_ip != NULL)
+ info.local_ip = *user->conn.local_ip;
+ if (user->conn.remote_ip != NULL)
+ info.remote_ip = *user->conn.remote_ip;
+
+ userdb_pool = pool_alloconly_create("userdb lookup", 2048);
+ ret = auth_master_user_lookup(mail_user_auth_master_conn,
+ user->username, &info, userdb_pool,
+ &username, &fields);
+ if (ret > 0) {
+ auth_user_fields_parse(fields, userdb_pool, &reply);
+ user->_home = p_strdup(user->pool, reply.home);
+ }
+ pool_unref(&userdb_pool);
+ return ret;
+}
+
+static bool mail_user_get_mail_home(struct mail_user *user)
+{
+ const char *error, *home = user->set->mail_home;
+ string_t *str;
+
+ if (user->settings_expanded) {
+ user->_home = home[0] != '\0' ? home : NULL;
+ return TRUE;
+ }
+ /* we're still initializing user. need to do the expansion ourself. */
+ i_assert(home[0] == SETTING_STRVAR_UNEXPANDED[0]);
+ home++;
+ if (home[0] == '\0')
+ return TRUE;
+
+ str = t_str_new(128);
+ if (var_expand_with_funcs(str, home,
+ mail_user_var_expand_table(user),
+ mail_user_var_expand_func_table, user,
+ &error) <= 0) {
+ e_error(user->event, "Failed to expand mail_home=%s: %s",
+ home, error);
+ return FALSE;
+ }
+ user->_home = p_strdup(user->pool, str_c(str));
+ return TRUE;
+}
+
+int mail_user_get_home(struct mail_user *user, const char **home_r)
+{
+ int ret;
+
+ if (user->home_looked_up) {
+ *home_r = user->_home;
+ return user->_home != NULL ? 1 : 0;
+ }
+
+ if (mail_user_auth_master_conn == NULL) {
+ /* no userdb connection. we can only use mail_home setting. */
+ if (!mail_user_get_mail_home(user))
+ return -1;
+ } else if ((ret = mail_user_userdb_lookup_home(user)) < 0) {
+ /* userdb lookup failed */
+ return -1;
+ } else if (ret == 0) {
+ /* user doesn't exist */
+ user->nonexistent = TRUE;
+ } else if (user->_home == NULL) {
+ /* no home returned by userdb lookup, fallback to
+ mail_home setting. */
+ if (!mail_user_get_mail_home(user))
+ return -1;
+ }
+ user->home_looked_up = TRUE;
+
+ *home_r = user->_home;
+ return user->_home != NULL ? 1 : 0;
+}
+
+bool mail_user_is_plugin_loaded(struct mail_user *user, struct module *module)
+{
+ const char *const *plugins;
+ bool ret;
+
+ T_BEGIN {
+ plugins = t_strsplit_spaces(user->set->mail_plugins, ", ");
+ ret = str_array_find(plugins, module_get_plugin_name(module));
+ } T_END;
+ return ret;
+}
+
+bool mail_user_plugin_getenv_bool(struct mail_user *user, const char *name)
+{
+ return mail_user_set_plugin_getenv_bool(user->set, name);
+}
+
+bool mail_user_set_plugin_getenv_bool(const struct mail_user_settings *set,
+ const char *name)
+{
+ const char *env = mail_user_set_plugin_getenv(set, name);
+
+ if (env == NULL)
+ return FALSE;
+ switch (env[0]) {
+ case 'n':
+ case 'N':
+ case '0':
+ case 'f':
+ case 'F':
+ return FALSE;
+ }
+
+ //any other value including empty string will be treated as TRUE.
+ return TRUE;
+}
+
+const char *mail_user_plugin_getenv(struct mail_user *user, const char *name)
+{
+ return mail_user_set_plugin_getenv(user->set, name);
+}
+
+const char *mail_user_set_plugin_getenv(const struct mail_user_settings *set,
+ const char *name)
+{
+ const char *const *envs;
+ unsigned int i, count;
+
+ if (!array_is_created(&set->plugin_envs))
+ return NULL;
+
+ envs = array_get(&set->plugin_envs, &count);
+ for (i = 0; i < count; i += 2) {
+ if (strcmp(envs[i], name) == 0)
+ return envs[i+1];
+ }
+ return NULL;
+}
+
+int mail_user_try_home_expand(struct mail_user *user, const char **pathp)
+{
+ const char *home, *path = *pathp;
+
+ if (*path != '~') {
+ /* no need to expand home */
+ return 0;
+ }
+
+ if (mail_user_get_home(user, &home) <= 0)
+ return -1;
+
+ path = home_expand_tilde(path, home);
+ if (path == NULL)
+ return -1;
+
+ *pathp = path;
+ return 0;
+}
+
+void mail_user_set_get_temp_prefix(string_t *dest,
+ const struct mail_user_settings *set)
+{
+ str_append(dest, set->mail_temp_dir);
+ str_append(dest, "/dovecot.");
+ str_append(dest, master_service_get_name(master_service));
+ str_append_c(dest, '.');
+}
+
+const char *mail_user_get_volatile_dir(struct mail_user *user)
+{
+ struct mailbox_list *inbox_list =
+ mail_namespace_find_inbox(user->namespaces)->list;
+
+ return inbox_list->set.volatile_dir;
+}
+
+int mail_user_lock_file_create(struct mail_user *user, const char *lock_fname,
+ unsigned int lock_secs,
+ struct file_lock **lock_r, const char **error_r)
+{
+ const char *home, *path;
+ int ret;
+
+ if ((ret = mail_user_get_home(user, &home)) < 0) {
+ /* home lookup failed - shouldn't really happen */
+ *error_r = "Failed to lookup home directory";
+ errno = EINVAL;
+ return -1;
+ }
+ if (ret == 0) {
+ *error_r = "User has no home directory";
+ errno = EINVAL;
+ return -1;
+ }
+
+ const struct mail_storage_settings *mail_set =
+ mail_user_set_get_storage_set(user);
+ struct file_create_settings lock_set = {
+ .lock_timeout_secs = lock_secs,
+ .lock_settings = {
+ .lock_method = mail_set->parsed_lock_method,
+ },
+ };
+ struct mailbox_list *inbox_list =
+ mail_namespace_find_inbox(user->namespaces)->list;
+ if (inbox_list->set.volatile_dir == NULL)
+ path = t_strdup_printf("%s/%s", home, lock_fname);
+ else {
+ path = t_strdup_printf("%s/%s", inbox_list->set.volatile_dir,
+ lock_fname);
+ lock_set.mkdir_mode = 0700;
+ }
+ return mail_storage_lock_create(path, &lock_set, mail_set, lock_r, error_r);
+}
+
+const char *mail_user_get_anvil_userip_ident(struct mail_user *user)
+{
+ if (user->conn.remote_ip == NULL)
+ return NULL;
+ return t_strconcat(net_ip2addr(user->conn.remote_ip), "/",
+ str_tabescape(user->username), NULL);
+}
+
+static void
+mail_user_try_load_class_plugin(struct mail_user *user, const char *name)
+{
+ struct module_dir_load_settings mod_set;
+ struct module *module;
+ size_t name_len = strlen(name);
+
+ i_zero(&mod_set);
+ mod_set.abi_version = DOVECOT_ABI_VERSION;
+ mod_set.binary_name = master_service_get_name(master_service);
+ mod_set.setting_name = "<built-in storage lookup>";
+ mod_set.require_init_funcs = TRUE;
+ mod_set.debug = user->mail_debug;
+
+ mail_storage_service_modules =
+ module_dir_load_missing(mail_storage_service_modules,
+ user->set->mail_plugin_dir,
+ name, &mod_set);
+ /* initialize the module (and only this module!) immediately so that
+ the class gets registered */
+ for (module = mail_storage_service_modules; module != NULL; module = module->next) {
+ if (strncmp(module->name, name, name_len) == 0 &&
+ strcmp(module->name + name_len, "_plugin") == 0) {
+ if (!module->initialized) {
+ module->initialized = TRUE;
+ module->init(module);
+ }
+ break;
+ }
+ }
+}
+
+struct mail_storage *
+mail_user_get_storage_class(struct mail_user *user, const char *name)
+{
+ struct mail_storage *storage;
+
+ storage = mail_storage_find_class(name);
+ if (storage == NULL || storage->v.alloc != NULL)
+ return storage;
+
+ /* it's implemented by a plugin. load it and check again. */
+ mail_user_try_load_class_plugin(user, name);
+
+ storage = mail_storage_find_class(name);
+ if (storage != NULL && storage->v.alloc == NULL) {
+ e_error(user->event, "Storage driver '%s' exists as a stub, "
+ "but its plugin couldn't be loaded", name);
+ return NULL;
+ }
+ return storage;
+}
+
+struct mail_user *mail_user_dup(struct mail_user *user)
+{
+ struct mail_user *user2;
+
+ user2 = mail_user_alloc(event_get_parent(user->event), user->username,
+ user->set_info, user->unexpanded_set);
+ if (user2->_service_user != NULL) {
+ user2->_service_user = user->_service_user;
+ mail_storage_service_user_ref(user2->_service_user);
+ }
+ if (user->_home != NULL)
+ mail_user_set_home(user2, user->_home);
+ mail_user_set_vars(user2, user->service, &user->conn);
+ user2->uid = user->uid;
+ user2->gid = user->gid;
+ user2->anonymous = user->anonymous;
+ user2->admin = user->admin;
+ user2->auth_mech = p_strdup(user2->pool, user->auth_mech);
+ user2->auth_token = p_strdup(user2->pool, user->auth_token);
+ user2->auth_user = p_strdup(user2->pool, user->auth_user);
+ user2->session_id = p_strdup(user2->pool, user->session_id);
+ user2->session_create_time = user->session_create_time;
+ user2->userdb_fields = user->userdb_fields == NULL ? NULL :
+ p_strarray_dup(user2->pool, user->userdb_fields);
+ return user2;
+}
+
+void mail_user_init_ssl_client_settings(struct mail_user *user,
+ struct ssl_iostream_settings *ssl_set_r)
+{
+ if (user->_service_user == NULL) {
+ /* Internal test user that should never actually need any
+ SSL settings. */
+ i_zero(ssl_set_r);
+ return;
+ }
+
+ const struct master_service_ssl_settings *ssl_set =
+ mail_storage_service_user_get_ssl_settings(user->_service_user);
+
+ master_service_ssl_client_settings_to_iostream_set(ssl_set,
+ pool_datastack_create(), ssl_set_r);
+}
+
+void mail_user_init_fs_settings(struct mail_user *user,
+ struct fs_settings *fs_set,
+ struct ssl_iostream_settings *ssl_set_r)
+{
+ fs_set->event_parent = user->event;
+ fs_set->username = user->username;
+ fs_set->session_id = user->session_id;
+ fs_set->base_dir = user->set->base_dir;
+ fs_set->temp_dir = user->set->mail_temp_dir;
+ fs_set->debug = user->mail_debug;
+ fs_set->enable_timing = user->stats_enabled;
+
+ fs_set->ssl_client_set = ssl_set_r;
+ mail_user_init_ssl_client_settings(user, ssl_set_r);
+}
+
+void mail_user_stats_fill(struct mail_user *user, struct stats *stats)
+{
+ user->v.stats_fill(user, stats);
+}
+
+static int
+mail_user_home_mkdir_try_ns(struct mail_namespace *ns, const char *home)
+{
+ const enum mailbox_list_path_type types[] = {
+ MAILBOX_LIST_PATH_TYPE_DIR,
+ MAILBOX_LIST_PATH_TYPE_ALT_DIR,
+ MAILBOX_LIST_PATH_TYPE_CONTROL,
+ MAILBOX_LIST_PATH_TYPE_INDEX,
+ MAILBOX_LIST_PATH_TYPE_INDEX_PRIVATE,
+ MAILBOX_LIST_PATH_TYPE_INDEX_CACHE,
+ MAILBOX_LIST_PATH_TYPE_LIST_INDEX,
+ };
+ size_t home_len = strlen(home);
+ const char *path;
+
+ for (unsigned int i = 0; i < N_ELEMENTS(types); i++) {
+ if (!mailbox_list_get_root_path(ns->list, types[i], &path))
+ continue;
+ if (strncmp(path, home, home_len) == 0 &&
+ (path[home_len] == '\0' || path[home_len] == '/')) {
+ return mailbox_list_mkdir_root(ns->list, path,
+ types[i]) < 0 ? -1 : 1;
+ }
+ }
+ return 0;
+}
+
+int mail_user_home_mkdir(struct mail_user *user)
+{
+ struct mail_namespace *ns;
+ const char *home;
+ int ret;
+
+ if ((ret = mail_user_get_home(user, &home)) <= 0) {
+ /* If user has no home directory, just return success. */
+ return ret;
+ }
+
+ /* Try to create the home directory by creating the root directory for
+ a namespace that exists under the home. This way we end up in the
+ special mkdir() code in mailbox_list_try_mkdir_root_parent().
+ Start from INBOX, since that's usually the correct place. */
+ ns = mail_namespace_find_inbox(user->namespaces);
+ if ((ret = mail_user_home_mkdir_try_ns(ns, home)) != 0)
+ return ret < 0 ? -1 : 0;
+ /* try other namespaces */
+ for (ns = user->namespaces; ns != NULL; ns = ns->next) {
+ if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0) {
+ /* already tried the INBOX namespace */
+ continue;
+ }
+ if ((ret = mail_user_home_mkdir_try_ns(ns, home)) != 0)
+ return ret < 0 ? -1 : 0;
+ }
+ /* fallback to a safe mkdir() with 0700 mode */
+ if (mkdir_parents(home, 0700) < 0 && errno != EEXIST) {
+ e_error(user->event, "mkdir_parents(%s) failed: %m", home);
+ return -1;
+ }
+ return 0;
+}
+
+const struct dict_op_settings *
+mail_user_get_dict_op_settings(struct mail_user *user)
+{
+ if (user->dict_op_set == NULL) {
+ user->dict_op_set = p_new(user->pool, struct dict_op_settings, 1);
+ user->dict_op_set->username = p_strdup(user->pool, user->username);
+ if (mail_user_get_home(user, &user->dict_op_set->home_dir) <= 0)
+ user->dict_op_set->home_dir = NULL;
+ }
+ return user->dict_op_set;
+}
+
+static const struct var_expand_func_table mail_user_var_expand_func_table_arr[] = {
+ { "userdb", mail_user_var_expand_func_userdb },
+ { NULL, NULL }
+};
+const struct var_expand_func_table *mail_user_var_expand_func_table =
+ mail_user_var_expand_func_table_arr;