virtual-storage.c revision dd37e2ff291fbebac1b94e8aad50f3bdf7531049
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen/* Copyright (c) 2008-2015 Dovecot authors, see the included COPYING file */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "lib.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "array.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "ioloop.h"
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen#include "str.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "llist.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "mkdir-parents.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "unlink-directory.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "index-mail.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "mail-copy.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "mail-search.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "mailbox-list-private.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "virtual-plugin.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "virtual-transaction.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include "virtual-storage.h"
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <stdio.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <unistd.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <dirent.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#include <sys/stat.h>
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen#define VIRTUAL_DEFAULT_MAX_OPEN_MAILBOXES 64
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenextern struct mail_storage virtual_storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenextern struct mailbox virtual_mailbox;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenextern struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstruct virtual_storage_module virtual_storage_module =
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen MODULE_CONTEXT_INIT(&mail_storage_module_register);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool ns_is_visible(struct mail_namespace *ns)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return (ns->flags & NAMESPACE_FLAG_LIST_PREFIX) != 0 ||
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (ns->flags & NAMESPACE_FLAG_LIST_CHILDREN) != 0 ||
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen (ns->flags & NAMESPACE_FLAG_HIDDEN) == 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainenstatic const char *get_user_visible_mailbox_name(struct mailbox *box)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (ns_is_visible(box->list->ns))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return box->vname;
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainen else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return t_strdup_printf("<hidden>%c%s",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_namespace_get_sep(box->list->ns),
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen box->vname);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid virtual_box_copy_error(struct mailbox *dest, struct mailbox *src)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *name, *str;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum mail_error error;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen name = get_user_visible_mailbox_name(src);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen str = mailbox_get_last_error(src, &error);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen str = t_strdup_printf("%s (for backend mailbox %s)", str, name);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mail_storage_set_error(dest->storage, error, str);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic struct mail_storage *virtual_storage_alloc(void)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_storage *storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pool_t pool;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen pool = pool_alloconly_create("virtual storage", 1024);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen storage = p_new(pool, struct virtual_storage, 1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen storage->storage = virtual_storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen storage->storage.pool = pool;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen p_array_init(&storage->open_stack, pool, 8);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return &storage->storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvirtual_storage_create(struct mail_storage *_storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mail_namespace *ns ATTR_UNUSED,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char **error_r)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_storage *storage = (struct virtual_storage *)_storage;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *value;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen value = mail_user_plugin_getenv(_storage->user, "virtual_max_open_mailboxes");
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (value == NULL)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen storage->max_open_mailboxes = VIRTUAL_DEFAULT_MAX_OPEN_MAILBOXES;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen else if (str_to_uint(value, &storage->max_open_mailboxes) < 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *error_r = "Invalid virtual_max_open_mailboxes setting";
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic void
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvirtual_storage_get_list_settings(const struct mail_namespace *ns ATTR_UNUSED,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct mailbox_list_settings *set)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (set->layout == NULL)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen set->layout = MAILBOX_LIST_NAME_FS;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (set->subscription_fname == NULL)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen set->subscription_fname = VIRTUAL_SUBSCRIPTION_FILE_NAME;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenstruct virtual_backend_box *
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvirtual_backend_box_lookup_name(struct virtual_mailbox *mbox, const char *name)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_backend_box *const *bboxes;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen unsigned int i, count;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen bboxes = array_get(&mbox->backend_boxes, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (strcmp(bboxes[i]->name, name) == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return bboxes[i];
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstruct virtual_backend_box *
44ff75ca53188056ff5a3e50428e3f2078800b3cTimo Sirainenvirtual_backend_box_lookup(struct virtual_mailbox *mbox, uint32_t mailbox_id)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *const *bboxes;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int i, count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mailbox_id == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return NULL;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bboxes = array_get(&mbox->backend_boxes, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bboxes[i]->mailbox_id == mailbox_id)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return bboxes[i];
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return NULL;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool virtual_mailbox_is_in_open_stack(struct virtual_storage *storage,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *name)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *const *names;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen unsigned int i, count;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen names = array_get(&storage->open_stack, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < count; i++) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (strcmp(names[i], name) == 0)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int virtual_backend_box_open_failed(struct virtual_mailbox *mbox,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *bbox)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen enum mail_error error;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen const char *str;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen str = t_strdup_printf(
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "Virtual mailbox open failed because of mailbox %s: %s",
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen get_user_visible_mailbox_name(bbox->box),
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mailbox_get_last_error(bbox->box, &error));
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_storage_set_error(mbox->box.storage, error, str);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mailbox_free(&bbox->box);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (error == MAIL_ERROR_PERM && bbox->wildcard) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* this mailbox wasn't explicitly specified. just skip it. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return -1;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic int virtual_backend_box_alloc(struct virtual_mailbox *mbox,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *bbox,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen enum mailbox_flags flags)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct mail_user *user = mbox->storage->storage.user;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct mail_namespace *ns;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *mailbox;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen enum mailbox_existence existence;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(bbox->box == NULL);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!bbox->clear_recent)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen flags &= ~MAILBOX_FLAG_DROP_RECENT;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mailbox = bbox->name;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ns = mail_namespace_find(user->namespaces, mailbox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bbox->box = mailbox_alloc(ns->list, mailbox, flags);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen MODULE_CONTEXT_SET(bbox->box, virtual_storage_module, bbox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mailbox_exists(bbox->box, TRUE, &existence) < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return virtual_backend_box_open_failed(mbox, bbox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (existence != MAILBOX_EXISTENCE_SELECT) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* ignore this. it could be intentional. */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mbox->storage->storage.user->mail_debug) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_debug("virtual mailbox %s: "
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "Skipping non-existing mailbox %s",
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mbox->box.vname, bbox->box->vname);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mailbox_free(&bbox->box);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 0;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_array_init(&bbox->uids, 64);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_array_init(&bbox->sync_pending_removes, 64);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* we use modseqs for being able to check quickly if backend mailboxes
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen have changed. make sure the backend has them enabled. */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mailbox_enable(bbox->box, MAILBOX_FEATURE_CONDSTORE);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenstatic int virtual_mailboxes_open(struct virtual_mailbox *mbox,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen enum mailbox_flags flags)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_backend_box *const *bboxes;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen unsigned int i, count;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen int ret;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bboxes = array_get(&mbox->backend_boxes, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen for (i = 0; i < count; ) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen ret = virtual_backend_box_alloc(mbox, bboxes[i], flags);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (ret <= 0) {
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen if (ret < 0)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen break;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen array_delete(&mbox->backend_boxes, i, 1);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bboxes = array_get(&mbox->backend_boxes, &count);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen } else {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (i == count)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return 0;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen else {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* failed */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen for (; i > 0; i--) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mailbox_free(&bboxes[i-1]->box);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen array_free(&bboxes[i-1]->uids);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return -1;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenstatic struct mailbox *
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenvirtual_mailbox_alloc(struct mail_storage *_storage, struct mailbox_list *list,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen const char *vname, enum mailbox_flags flags)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_storage *storage = (struct virtual_storage *)_storage;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_mailbox *mbox;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen pool_t pool;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen pool = pool_alloconly_create("virtual mailbox", 2048);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox = p_new(pool, struct virtual_mailbox, 1);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->box = virtual_mailbox;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->box.pool = pool;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->box.storage = _storage;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->box.list = list;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->box.mail_vfuncs = &virtual_mail_vfuncs;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->vfuncs = virtual_mailbox_vfuncs;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen index_storage_mailbox_alloc(&mbox->box, vname, flags, MAIL_INDEX_PREFIX);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->storage = storage;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox->virtual_ext_id = (uint32_t)-1;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen return &mbox->box;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen}
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainenvoid virtual_backend_box_sync_mail_unset(struct virtual_backend_box *bbox)
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen{
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen struct mailbox_transaction_context *trans;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bbox->sync_mail != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen trans = bbox->sync_mail->transaction;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_free(&bbox->sync_mail);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen (void)mailbox_transaction_commit(&trans);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenstatic bool virtual_backend_box_can_close(struct virtual_backend_box *bbox)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bbox->box->notify_callback != NULL) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* FIXME: IMAP IDLE running - we should support closing this
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen also if mailbox_list_index=yes */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return FALSE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (array_count(&bbox->sync_pending_removes) > 0) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* FIXME: we could probably close this by making
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen syncing support it? */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return FALSE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return TRUE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenstatic bool
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenvirtual_backend_box_close_any_except(struct virtual_mailbox *mbox,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_backend_box *except_bbox)
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen{
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen struct virtual_backend_box *bbox;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* first try to close a mailbox without any transactions.
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen we'll also skip any mailbox that has notifications enabled (ideally
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen these would be handled by mailbox list index) */
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen for (bbox = mbox->open_backend_boxes_head; bbox != NULL; bbox = bbox->next_open) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(bbox->box->opened);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (bbox != except_bbox &&
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen bbox->box->transaction_count == 0 &&
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen virtual_backend_box_can_close(bbox)) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(bbox->sync_mail == NULL);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen virtual_backend_box_close(mbox, bbox);
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return TRUE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* next try to close a mailbox that has sync_mail, but no
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen other transactions */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen for (bbox = mbox->open_backend_boxes_head; bbox != NULL; bbox = bbox->next_open) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bbox != except_bbox &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bbox->sync_mail != NULL &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bbox->box->transaction_count == 1 &&
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen virtual_backend_box_can_close(bbox)) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen virtual_backend_box_sync_mail_unset(bbox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(bbox->box->transaction_count == 0);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen virtual_backend_box_close(mbox, bbox);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return TRUE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return FALSE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid virtual_mailbox_opened_hook(struct mailbox *box)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *bbox = VIRTUAL_CONTEXT(box);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_mailbox *mbox;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (bbox == NULL) {
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen /* not a backend for a virtual mailbox */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen return;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen }
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen i_assert(!bbox->open_tracked);
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen mbox = bbox->virtual_mbox;
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen
e96fb85799dc95603bb1a6b4d3685df2d042a2f8Timo Sirainen /* the backend mailbox was already opened. if we didn't get here
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen from virtual_backend_box_open() we may need to close a mailbox */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen while (mbox->backends_open_count > mbox->storage->max_open_mailboxes &&
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen virtual_backend_box_close_any_except(mbox, bbox))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bbox->open_tracked = TRUE;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen mbox->backends_open_count++;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen DLLIST2_APPEND_FULL(&mbox->open_backend_boxes_head,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen &mbox->open_backend_boxes_tail, bbox,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen prev_open, next_open);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainenint virtual_backend_box_open(struct virtual_mailbox *mbox,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *bbox)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(!bbox->box->opened);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen /* try to keep the number of open mailboxes below the threshold
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen before opening the mailbox */
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen while (mbox->backends_open_count >= mbox->storage->max_open_mailboxes &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen virtual_backend_box_close_any_except(mbox, bbox))
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen ;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return mailbox_open(bbox->box);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainenvoid virtual_backend_box_close(struct virtual_mailbox *mbox,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen struct virtual_backend_box *bbox)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen{
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(bbox->box->opened);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen i_assert(bbox->open_tracked);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bbox->search_result != NULL)
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mailbox_search_result_free(&bbox->search_result);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (bbox->search_args != NULL &&
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen bbox->search_args_initialized) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mail_search_args_deinit(bbox->search_args);
bbox->search_args_initialized = FALSE;
}
i_assert(mbox->backends_open_count > 0);
mbox->backends_open_count--;
bbox->open_tracked = FALSE;
DLLIST2_REMOVE_FULL(&mbox->open_backend_boxes_head,
&mbox->open_backend_boxes_tail, bbox,
prev_open, next_open);
mailbox_close(bbox->box);
}
void virtual_backend_box_accessed(struct virtual_mailbox *mbox,
struct virtual_backend_box *bbox)
{
DLLIST2_REMOVE_FULL(&mbox->open_backend_boxes_head,
&mbox->open_backend_boxes_tail, bbox,
prev_open, next_open);
DLLIST2_APPEND_FULL(&mbox->open_backend_boxes_head,
&mbox->open_backend_boxes_tail, bbox,
prev_open, next_open);
}
static void virtual_mailbox_close_internal(struct virtual_mailbox *mbox)
{
struct virtual_backend_box **bboxes;
unsigned int i, count;
bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
for (i = 0; i < count; i++) {
if (bboxes[i]->box == NULL)
continue;
if (bboxes[i]->box->opened)
virtual_backend_box_close(mbox, bboxes[i]);
mailbox_free(&bboxes[i]->box);
if (array_is_created(&bboxes[i]->sync_outside_expunges))
array_free(&bboxes[i]->sync_outside_expunges);
array_free(&bboxes[i]->sync_pending_removes);
array_free(&bboxes[i]->uids);
}
i_assert(mbox->backends_open_count == 0);
}
static int
virtual_mailbox_exists(struct mailbox *box, bool auto_boxes ATTR_UNUSED,
enum mailbox_existence *existence_r)
{
return index_storage_mailbox_exists_full(box, VIRTUAL_CONFIG_FNAME,
existence_r);
}
static int virtual_mailbox_open(struct mailbox *box)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
bool broken;
int ret = 0;
if (virtual_mailbox_is_in_open_stack(mbox->storage, box->name)) {
mail_storage_set_critical(box->storage,
"Virtual mailbox loops: %s", box->name);
return -1;
}
if (!array_is_created(&mbox->backend_boxes))
ret = virtual_config_read(mbox);
if (ret == 0) {
array_append(&mbox->storage->open_stack, &box->name, 1);
ret = virtual_mailboxes_open(mbox, box->flags);
array_delete(&mbox->storage->open_stack,
array_count(&mbox->storage->open_stack)-1, 1);
}
if (ret < 0) {
virtual_mailbox_close_internal(mbox);
return -1;
}
if (index_storage_mailbox_open(box, FALSE) < 0)
return -1;
mbox->virtual_ext_id =
mail_index_ext_register(mbox->box.index, "virtual", 0,
sizeof(struct virtual_mail_index_record),
sizeof(uint32_t));
if (virtual_mailbox_ext_header_read(mbox, box->view, &broken) < 0) {
virtual_mailbox_close_internal(mbox);
index_storage_mailbox_close(box);
return -1;
}
return 0;
}
static void virtual_mailbox_close(struct mailbox *box)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
virtual_mailbox_close_internal(mbox);
index_storage_mailbox_close(box);
}
static void virtual_mailbox_free(struct mailbox *box)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
virtual_config_free(mbox);
index_storage_mailbox_free(box);
}
static int
virtual_mailbox_create(struct mailbox *box,
const struct mailbox_update *update ATTR_UNUSED,
bool directory ATTR_UNUSED)
{
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Can't create virtual mailboxes");
return -1;
}
static int
virtual_mailbox_update(struct mailbox *box,
const struct mailbox_update *update ATTR_UNUSED)
{
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Can't update virtual mailboxes");
return -1;
}
static int virtual_storage_set_have_guid_flags(struct virtual_mailbox *mbox)
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
struct mailbox_status status;
bool opened;
mbox->have_guids = TRUE;
mbox->have_save_guids = TRUE;
bboxes = array_get(&mbox->backend_boxes, &count);
for (i = 0; i < count; i++) {
opened = bboxes[i]->box->opened;
if (mailbox_get_status(bboxes[i]->box, 0, &status) < 0) {
virtual_box_copy_error(&mbox->box, bboxes[i]->box);
return -1;
}
i_assert(bboxes[i]->box->opened == opened);
if (!status.have_guids)
mbox->have_guids = FALSE;
if (!status.have_save_guids)
mbox->have_save_guids = FALSE;
}
return 0;
}
static int
virtual_storage_get_status(struct mailbox *box,
enum mailbox_status_items items,
struct mailbox_status *status_r)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
if ((items & STATUS_LAST_CACHED_SEQ) != 0)
items |= STATUS_MESSAGES;
if (index_storage_get_status(box, items, status_r) < 0)
return -1;
if ((items & STATUS_LAST_CACHED_SEQ) != 0) {
/* Virtual mailboxes have no cached data of their own, so the
current value is always 0. The most important use for this
functionality is for "doveadm index" to do FTS indexing and
it doesn't really matter there if we set this value
correctly or not. So for now just assume that everything is
indexed. */
status_r->last_cached_seq = status_r->messages;
}
if (!mbox->have_guid_flags_set) {
if (virtual_storage_set_have_guid_flags(mbox) < 0)
return -1;
mbox->have_guid_flags_set = TRUE;
}
if (mbox->have_guids)
status_r->have_guids = TRUE;
if (mbox->have_save_guids)
status_r->have_save_guids = TRUE;
return 0;
}
static int
virtual_mailbox_get_metadata(struct mailbox *box,
enum mailbox_metadata_items items,
struct mailbox_metadata *metadata_r)
{
if (index_mailbox_get_metadata(box, items, metadata_r) < 0)
return -1;
if ((items & MAILBOX_METADATA_GUID) != 0) {
mail_storage_set_error(box->storage, MAIL_ERROR_NOTPOSSIBLE,
"Virtual mailboxes have no GUIDs");
return -1;
}
return 0;
}
static void
virtual_notify_callback(struct mailbox *bbox ATTR_UNUSED, struct mailbox *box)
{
box->notify_callback(box, box->notify_context);
}
static void virtual_notify_changes(struct mailbox *box)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
struct virtual_backend_box *const *bboxp;
if (box->notify_callback == NULL) {
array_foreach(&mbox->backend_boxes, bboxp)
mailbox_notify_changes_stop((*bboxp)->box);
return;
}
/* FIXME: if mailbox_list_index=yes, use mailbox-list-notify.h API
to wait for changes and avoid opening all mailboxes here. */
array_foreach(&mbox->backend_boxes, bboxp) {
if (!(*bboxp)->box->opened &&
virtual_backend_box_open(mbox, *bboxp) < 0) {
/* we can't report error in here, so do it later */
(*bboxp)->open_failed = TRUE;
continue;
}
mailbox_notify_changes((*bboxp)->box,
virtual_notify_callback, box);
}
}
static void
virtual_get_virtual_uids(struct mailbox *box,
struct mailbox *backend_mailbox,
const ARRAY_TYPE(seq_range) *backend_uids,
ARRAY_TYPE(seq_range) *virtual_uids_r)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
struct virtual_backend_box *bbox;
const struct virtual_backend_uidmap *uids;
struct seq_range_iter iter;
unsigned int n, i, count;
uint32_t uid;
if (mbox->lookup_prev_bbox != NULL &&
strcmp(mbox->lookup_prev_bbox->box->vname, backend_mailbox->vname) == 0)
bbox = mbox->lookup_prev_bbox;
else {
bbox = virtual_backend_box_lookup_name(mbox, backend_mailbox->vname);
mbox->lookup_prev_bbox = bbox;
}
if (bbox == NULL)
return;
uids = array_get(&bbox->uids, &count); i = 0;
seq_range_array_iter_init(&iter, backend_uids); n = 0;
while (seq_range_array_iter_nth(&iter, n++, &uid)) {
while (i < count && uids[i].real_uid < uid) i++;
if (i < count && uids[i].real_uid == uid) {
i_assert(uids[i].virtual_uid > 0);
seq_range_array_add(virtual_uids_r,
uids[i].virtual_uid);
i++;
}
}
}
static void
virtual_get_virtual_uid_map(struct mailbox *box,
struct mailbox *backend_mailbox,
const ARRAY_TYPE(seq_range) *backend_uids,
ARRAY_TYPE(uint32_t) *virtual_uids_r)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
struct virtual_backend_box *bbox;
const struct virtual_backend_uidmap *uids;
struct seq_range_iter iter;
unsigned int n, i, count;
uint32_t uid;
if (mbox->lookup_prev_bbox != NULL &&
strcmp(mbox->lookup_prev_bbox->box->vname, backend_mailbox->vname) == 0)
bbox = mbox->lookup_prev_bbox;
else {
bbox = virtual_backend_box_lookup_name(mbox, backend_mailbox->vname);
mbox->lookup_prev_bbox = bbox;
}
if (bbox == NULL)
return;
uids = array_get(&bbox->uids, &count); i = 0;
seq_range_array_iter_init(&iter, backend_uids); n = 0;
while (seq_range_array_iter_nth(&iter, n++, &uid)) {
while (i < count && uids[i].real_uid < uid) i++;
if (i == count || uids[i].real_uid > uid) {
uint32_t zero = 0;
array_append(virtual_uids_r, &zero, 1);
} else {
i_assert(uids[i].virtual_uid > 0);
array_append(virtual_uids_r, &uids[i].virtual_uid, 1);
i++;
}
}
}
static void
virtual_get_virtual_backend_boxes(struct mailbox *box,
ARRAY_TYPE(mailboxes) *mailboxes,
bool only_with_msgs)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
bboxes = array_get(&mbox->backend_boxes, &count);
for (i = 0; i < count; i++) {
if (!only_with_msgs || array_count(&bboxes[i]->uids) > 0)
array_append(mailboxes, &bboxes[i]->box, 1);
}
}
static bool virtual_is_inconsistent(struct mailbox *box)
{
struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
if (mbox->inconsistent)
return TRUE;
return index_storage_is_inconsistent(box);
}
static int
virtual_list_index_has_changed(struct mailbox *box ATTR_UNUSED,
struct mail_index_view *list_view ATTR_UNUSED,
uint32_t seq ATTR_UNUSED)
{
/* we don't have any quick and easy optimizations for tracking
virtual folders. ideally we'd completely disable mailbox list
indexes for them, but this is the easiest way to do it for now. */
return 1;
}
static void
virtual_list_index_update_sync(struct mailbox *box ATTR_UNUSED,
struct mail_index_transaction *trans ATTR_UNUSED,
uint32_t seq ATTR_UNUSED)
{
}
struct mail_storage virtual_storage = {
.name = VIRTUAL_STORAGE_NAME,
.class_flags = MAIL_STORAGE_CLASS_FLAG_NOQUOTA,
.v = {
NULL,
virtual_storage_alloc,
virtual_storage_create,
index_storage_destroy,
NULL,
virtual_storage_get_list_settings,
NULL,
virtual_mailbox_alloc,
NULL
}
};
struct mailbox virtual_mailbox = {
.v = {
index_storage_is_readonly,
index_storage_mailbox_enable,
virtual_mailbox_exists,
virtual_mailbox_open,
virtual_mailbox_close,
virtual_mailbox_free,
virtual_mailbox_create,
virtual_mailbox_update,
index_storage_mailbox_delete,
index_storage_mailbox_rename,
virtual_storage_get_status,
virtual_mailbox_get_metadata,
index_storage_set_subscribed,
index_storage_attribute_set,
index_storage_attribute_get,
index_storage_attribute_iter_init,
index_storage_attribute_iter_next,
index_storage_attribute_iter_deinit,
virtual_list_index_has_changed,
virtual_list_index_update_sync,
virtual_storage_sync_init,
index_mailbox_sync_next,
index_mailbox_sync_deinit,
NULL,
virtual_notify_changes,
virtual_transaction_begin,
virtual_transaction_commit,
virtual_transaction_rollback,
NULL,
virtual_mail_alloc,
virtual_search_init,
virtual_search_deinit,
virtual_search_next_nonblock,
virtual_search_next_update_seq,
virtual_save_alloc,
virtual_save_begin,
virtual_save_continue,
virtual_save_finish,
virtual_save_cancel,
mail_storage_copy,
NULL,
NULL,
NULL,
virtual_is_inconsistent
}
};
struct virtual_mailbox_vfuncs virtual_mailbox_vfuncs = {
virtual_get_virtual_uids,
virtual_get_virtual_uid_map,
virtual_get_virtual_backend_boxes
};