Mercurial > dovecot > original-hg > dovecot-1.2
changeset 7663:8fc919084252 HEAD
Added initial support for virtual mailboxes.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Fri, 14 Mar 2008 12:00:32 +0200 |
parents | 2d3d1a61f734 |
children | df3728c2093c |
files | configure.in src/plugins/Makefile.am src/plugins/virtual/Makefile.am src/plugins/virtual/virtual-config.c src/plugins/virtual/virtual-mail.c src/plugins/virtual/virtual-plugin.c src/plugins/virtual/virtual-plugin.h src/plugins/virtual/virtual-storage.c src/plugins/virtual/virtual-storage.h src/plugins/virtual/virtual-sync.c src/plugins/virtual/virtual-transaction.c |
diffstat | 11 files changed, 1711 insertions(+), 1 deletions(-) [+] |
line wrap: on
line diff
--- a/configure.in Fri Mar 14 11:59:36 2008 +0200 +++ b/configure.in Fri Mar 14 12:00:32 2008 +0200 @@ -2180,6 +2180,7 @@ src/plugins/quota/Makefile src/plugins/imap-quota/Makefile src/plugins/trash/Makefile +src/plugins/virtual/Makefile src/plugins/zlib/Makefile stamp.h dovecot-config.in])
--- a/src/plugins/Makefile.am Fri Mar 14 11:59:36 2008 +0200 +++ b/src/plugins/Makefile.am Fri Mar 14 12:00:32 2008 +0200 @@ -8,5 +8,5 @@ SUBDIRS = \ acl convert expire fts fts-squat lazy-expunge mail-log mbox-snarf \ - quota imap-quota trash \ + quota imap-quota trash virtual \ $(ZLIB) $(FTS_LUCENE)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/Makefile.am Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,30 @@ +AM_CPPFLAGS = \ + -I$(top_srcdir)/src/lib \ + -I$(top_srcdir)/src/lib-mail \ + -I$(top_srcdir)/src/lib-imap \ + -I$(top_srcdir)/src/lib-index \ + -I$(top_srcdir)/src/lib-storage \ + -I$(top_srcdir)/src/lib-storage/index + +lib20_virtual_plugin_la_LDFLAGS = -module -avoid-version + +module_LTLIBRARIES = \ + lib20_virtual_plugin.la + +lib20_virtual_plugin_la_SOURCES = \ + virtual-config.c \ + virtual-mail.c \ + virtual-plugin.c \ + virtual-storage.c \ + virtual-sync.c \ + virtual-transaction.c + +noinst_HEADERS = \ + virtual-storage.h + +install-exec-local: + for d in imap pop3 lda; do \ + $(mkdir_p) $(DESTDIR)$(moduledir)/$$d; \ + rm -f $(DESTDIR)$(moduledir)/$$d/lib20_virtual_plugin.so; \ + $(LN_S) ../lib20_virtual_plugin.so $(DESTDIR)$(moduledir)/$$d; \ + done
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-config.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,159 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "istream.h" +#include "str.h" +#include "imap-parser.h" +#include "mail-search-build.h" +#include "virtual-storage.h" + +#include <unistd.h> +#include <fcntl.h> + +struct virtual_parse_context { + struct virtual_mailbox *mbox; + struct istream *input; + + pool_t pool; + string_t *rule; + unsigned int mailbox_id; + unsigned int rule_idx; +}; + +static struct mail_search_arg * +virtual_search_args_parse(pool_t pool, const string_t *rule, + const char **error_r) +{ + struct istream *input; + struct imap_parser *parser; + const struct imap_arg *args; + struct mail_search_arg *sargs; + bool fatal; + int ret; + + input = i_stream_create_from_data(str_data(rule), str_len(rule)); + (void)i_stream_read(input); + + parser = imap_parser_create(input, NULL, (size_t)-1); + ret = imap_parser_finish_line(parser, 0, 0, &args); + if (ret < 0) { + sargs = NULL; + *error_r = t_strdup(imap_parser_get_error(parser, &fatal)); + } else { + sargs = mail_search_build_from_imap_args(pool, args, error_r); + } + + imap_parser_destroy(&parser); + i_stream_destroy(&input); + return sargs; +} + +static int +virtual_config_add_rule(struct virtual_parse_context *ctx, const char **error_r) +{ + struct virtual_backend_box *const *bboxes; + struct mail_search_arg *search_args; + unsigned int i, count; + + if (str_len(ctx->rule) == 0) + return 0; + + search_args = virtual_search_args_parse(ctx->pool, ctx->rule, error_r); + str_truncate(ctx->rule, 0); + if (search_args == NULL) { + *error_r = t_strconcat("Previous search rule is invalid: ", + *error_r, NULL); + return -1; + } + + bboxes = array_get(&ctx->mbox->backend_boxes, &count); + i_assert(ctx->rule_idx < count); + for (i = ctx->rule_idx; i < count; i++) + bboxes[i]->search_args = search_args; + + ctx->rule_idx = array_count(&ctx->mbox->backend_boxes); + return 0; +} + +static int +virtual_config_parse_line(struct virtual_parse_context *ctx, const char *line, + const char **error_r) +{ + struct virtual_backend_box *bbox; + + if (*line == ' ') { + /* continues the previous search rule */ + if (ctx->rule_idx == array_count(&ctx->mbox->backend_boxes)) { + *error_r = "Search rule without a mailbox"; + return -1; + } + str_append(ctx->rule, line); + return 0; + } + if (virtual_config_add_rule(ctx, error_r) < 0) + return -1; + + /* new mailbox */ + bbox = p_new(ctx->pool, struct virtual_backend_box, 1); + bbox->mailbox_id = ++ctx->mailbox_id; + bbox->name = p_strdup(ctx->pool, line); + array_append(&ctx->mbox->backend_boxes, &bbox, 1); + return 0; +} + +int virtual_config_read(struct virtual_mailbox *mbox) +{ + struct virtual_parse_context ctx; + const char *path, *line, *error; + unsigned int linenum = 0; + int fd, ret = 0; + + i_array_init(&mbox->backend_boxes, 8); + + path = t_strconcat(mbox->path, "/"VIRTUAL_CONFIG_FNAME, NULL); + fd = open(path, O_RDWR); + if (fd == -1) { + if (errno == ENOENT) { + mail_storage_set_error(mbox->ibox.storage, + MAIL_ERROR_NOTPOSSIBLE, + "Virtual mailbox missing configuration file"); + return -1; + } + mail_storage_set_critical(mbox->ibox.storage, + "open(%s) failed: %m", path); + return -1; + } + + memset(&ctx, 0, sizeof(ctx)); + ctx.mbox = mbox; + ctx.pool = mbox->ibox.box.pool; + ctx.rule = t_str_new(256); + ctx.input = i_stream_create_fd(fd, (size_t)-1, FALSE); + while ((line = i_stream_read_next_line(ctx.input)) != NULL) { + linenum++; + if (*line == '#') + continue; + if (*line == '\0') + ret = virtual_config_add_rule(&ctx, &error); + else + ret = virtual_config_parse_line(&ctx, line, &error); + if (ret < 0) { + mail_storage_set_critical(mbox->ibox.storage, + "%s: Error at line %u: %s", + path, linenum, error); + break; + } + } + if (ret == 0) + ret = virtual_config_add_rule(&ctx, &error); + + if (ret == 0 && array_count(&mbox->backend_boxes) == 0) { + mail_storage_set_critical(mbox->ibox.storage, + "%s: No mailboxes defined", path); + ret = -1; + } + i_stream_unref(&ctx.input); + (void)close(fd); + return ret; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-mail.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,296 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "index-mail.h" +#include "virtual-storage.h" + +struct virtual_mail { + struct index_mail imail; + + enum mail_fetch_field wanted_fields; + struct mailbox_header_lookup_ctx *wanted_headers; + + /* currently active mail */ + struct mail *backend_mail; + /* all allocated mails */ + ARRAY_DEFINE(backend_mails, struct mail *); +}; + +struct mail * +virtual_mail_alloc(struct mailbox_transaction_context *t, + enum mail_fetch_field wanted_fields, + struct mailbox_header_lookup_ctx *wanted_headers) +{ + struct virtual_mailbox *mbox = (struct virtual_mailbox *)t->box; + struct virtual_mail *vmail; + pool_t pool; + + pool = pool_alloconly_create("vmail", 1024); + vmail = p_new(pool, struct virtual_mail, 1); + vmail->imail.mail.pool = pool; + vmail->imail.mail.v = virtual_mail_vfuncs; + vmail->imail.mail.mail.box = t->box; + vmail->imail.mail.mail.transaction = t; + array_create(&vmail->imail.mail.module_contexts, pool, + sizeof(void *), 5); + + vmail->imail.data_pool = + pool_alloconly_create("virtual index_mail", 512); + vmail->imail.ibox = &mbox->ibox; + vmail->imail.trans = (struct index_transaction_context *)t; + + vmail->wanted_fields = wanted_fields; + vmail->wanted_headers = wanted_headers; + i_array_init(&vmail->backend_mails, array_count(&mbox->backend_boxes)); + return &vmail->imail.mail.mail; +} + +static void virtual_mail_free(struct mail *mail) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + struct mail **mails; + unsigned int i, count; + + mails = array_get_modifiable(&vmail->backend_mails, &count); + for (i = 0; i < count; i++) + mail_free(&mails[i]); + array_free(&vmail->backend_mails); + + pool_unref(&vmail->imail.data_pool); + pool_unref(&vmail->imail.mail.pool); +} + +static struct mail * +backend_mail_find(struct virtual_mail *vmail, struct mailbox *box) +{ + struct mail *const *mails; + unsigned int i, count; + + mails = array_get(&vmail->backend_mails, &count); + for (i = 0; i < count; i++) { + if (mails[i]->box == box) + return mails[i]; + } + return NULL; +} + +static void virtual_mail_set_seq(struct mail *mail, uint32_t seq) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; + struct virtual_backend_box *bbox; + struct mailbox_transaction_context *backend_trans; + const struct virtual_mail_index_record *vrec; + const struct mail_index_record *rec; + const void *data; + bool expunged; + + mail_index_lookup_ext(mbox->ibox.view, seq, mbox->virtual_ext_id, + &data, &expunged); + vrec = data; + + bbox = virtual_backend_box_lookup(mbox, vrec->mailbox_id); + vmail->backend_mail = backend_mail_find(vmail, bbox->box); + if (vmail->backend_mail == NULL) { + backend_trans = + virtual_transaction_get(mail->transaction, bbox->box); + vmail->backend_mail = mail_alloc(backend_trans, + vmail->wanted_fields, + vmail->wanted_headers); + array_append(&vmail->backend_mails, &vmail->backend_mail, 1); + } + mail_set_uid(vmail->backend_mail, vrec->real_uid); + memset(&vmail->imail.data, 0, sizeof(vmail->imail.data)); + p_clear(vmail->imail.data_pool); + + rec = mail_index_lookup(mbox->ibox.view, seq); + vmail->imail.data.seq = seq; + vmail->imail.data.flags = rec->flags & MAIL_FLAGS_NONRECENT; + + mail->seq = seq; + mail->uid = rec->uid; + + mail->expunged = vmail->backend_mail->expunged; + mail->has_nuls = vmail->backend_mail->has_nuls; + mail->has_no_nuls = vmail->backend_mail->has_no_nuls; +} + +static bool virtual_mail_set_uid(struct mail *mail, uint32_t uid) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + struct virtual_mailbox *mbox = (struct virtual_mailbox *)mail->box; + uint32_t seq; + + if (!mail_index_lookup_seq(mbox->ibox.view, uid, &seq)) + return FALSE; + + virtual_mail_set_seq(vmail->backend_mail, seq); + return TRUE; +} + +static int +virtual_mail_get_parts(struct mail *mail, const struct message_part **parts_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_parts(vmail->backend_mail, parts_r); +} + +static int +virtual_mail_get_date(struct mail *mail, time_t *date_r, int *timezone_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + int tz; + + if (timezone_r == NULL) + timezone_r = &tz; + + return mail_get_date(vmail->backend_mail, date_r, timezone_r); +} + +static int virtual_mail_get_received_date(struct mail *mail, time_t *date_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_received_date(vmail->backend_mail, date_r); +} + +static int virtual_mail_get_save_date(struct mail *mail, time_t *date_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_save_date(vmail->backend_mail, date_r); +} + +static int virtual_mail_get_virtual_mail_size(struct mail *mail, uoff_t *size_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_virtual_size(vmail->backend_mail, size_r); +} + +static int virtual_mail_get_physical_size(struct mail *mail, uoff_t *size_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_physical_size(vmail->backend_mail, size_r); +} + +static int +virtual_mail_get_first_header(struct mail *mail, const char *field, + bool decode_to_utf8, const char **value_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + struct mail_private *p = (struct mail_private *)vmail->backend_mail; + + return p->v.get_first_header(vmail->backend_mail, field, + decode_to_utf8, value_r); +} + +static int +virtual_mail_get_headers(struct mail *mail, const char *field, + bool decode_to_utf8, const char *const **value_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + struct mail_private *p = (struct mail_private *)vmail->backend_mail; + + return p->v.get_headers(vmail->backend_mail, field, + decode_to_utf8, value_r); +} + +static int +virtual_mail_get_header_stream(struct mail *mail, + struct mailbox_header_lookup_ctx *headers, + struct istream **stream_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_header_stream(vmail->backend_mail, headers, stream_r); +} + +static int +virtual_mail_get_stream(struct mail *mail, struct message_size *hdr_size, + struct message_size *body_size, + struct istream **stream_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_stream(vmail->backend_mail, hdr_size, body_size, stream_r); +} + +static int +virtual_mail_get_special(struct mail *mail, enum mail_fetch_field field, + const char **value_r) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return mail_get_special(vmail->backend_mail, field, value_r); +} + +static void +virtual_mail_update_flags(struct mail *mail, enum modify_type modify_type, + enum mail_flags flags) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + mail_update_flags(vmail->backend_mail, modify_type, flags); +} + +static void +virtual_mail_update_keywords(struct mail *mail, enum modify_type modify_type, + struct mail_keywords *keywords) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + mail_update_keywords(vmail->backend_mail, modify_type, keywords); +} + +static void virtual_mail_expunge(struct mail *mail) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + mail_expunge(vmail->backend_mail); +} + +static void +virtual_mail_set_cache_corrupted(struct mail *mail, enum mail_fetch_field field) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + mail_set_cache_corrupted(vmail->backend_mail, field); +} + +static struct index_mail *virtual_mail_get_index_mail(struct mail *mail) +{ + struct virtual_mail *vmail = (struct virtual_mail *)mail; + + return (struct index_mail *)vmail->backend_mail; +} + +struct mail_vfuncs virtual_mail_vfuncs = { + NULL, + virtual_mail_free, + virtual_mail_set_seq, + virtual_mail_set_uid, + + index_mail_get_flags, + index_mail_get_keywords, + index_mail_get_keyword_indexes, + virtual_mail_get_parts, + virtual_mail_get_date, + virtual_mail_get_received_date, + virtual_mail_get_save_date, + virtual_mail_get_virtual_mail_size, + virtual_mail_get_physical_size, + virtual_mail_get_first_header, + virtual_mail_get_headers, + virtual_mail_get_header_stream, + virtual_mail_get_stream, + virtual_mail_get_special, + virtual_mail_update_flags, + virtual_mail_update_keywords, + virtual_mail_expunge, + virtual_mail_set_cache_corrupted, + virtual_mail_get_index_mail +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-plugin.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,40 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mail-namespace.h" +#include "virtual-storage.h" +#include "virtual-plugin.h" + +static void (*virtual_next_hook_mail_namespaces_created) + (struct mail_namespace *namespaces); + +const char *virtual_plugin_version = PACKAGE_VERSION; +struct mail_namespace *virtual_all_namespaces; + +static void +virtual_hook_mail_namespaces_created(struct mail_namespace *namespaces) +{ + if (virtual_next_hook_mail_namespaces_created != NULL) + virtual_next_hook_mail_namespaces_created(namespaces); + + /* FIXME: some day we should support multiple clients and this + global namespaces list doesn't work */ + virtual_all_namespaces = namespaces; +} + +void virtual_plugin_init(void) +{ + mail_storage_class_register(&virtual_storage); + + virtual_next_hook_mail_namespaces_created = + hook_mail_namespaces_created; + hook_mail_namespaces_created = virtual_hook_mail_namespaces_created; +} + +void virtual_plugin_deinit(void) +{ + mail_storage_class_unregister(&virtual_storage); + + hook_mail_namespaces_created = + virtual_next_hook_mail_namespaces_created; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-plugin.h Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,9 @@ +#ifndef VIRTUAL_PLUGIN_H +#define VIRTUAL_PLUGIN_H + +extern struct mail_namespace *virtual_all_namespaces; + +void virtual_plugin_init(void); +void virtual_plugin_deinit(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-storage.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,517 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ioloop.h" +#include "str.h" +#include "mkdir-parents.h" +#include "unlink-directory.h" +#include "index-mail.h" +#include "mail-copy.h" +#include "mail-search.h" +#include "virtual-plugin.h" +#include "virtual-storage.h" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> + +#define VIRTUAL_LIST_CONTEXT(obj) \ + MODULE_CONTEXT(obj, virtual_mailbox_list_module) + +extern struct mail_storage virtual_storage; +extern struct mailbox virtual_mailbox; + +static MODULE_CONTEXT_DEFINE_INIT(virtual_mailbox_list_module, + &mailbox_list_module_register); + +static int +virtual_list_delete_mailbox(struct mailbox_list *list, const char *name); +static int +virtual_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags); + +static int +virtual_get_list_settings(struct mailbox_list_settings *list_set, + const char *data, enum mail_storage_flags flags, + const char **layout_r, const char **error_r) +{ + bool debug = (flags & MAIL_STORAGE_FLAG_DEBUG) != 0; + + *layout_r = "fs"; + + memset(list_set, 0, sizeof(*list_set)); + list_set->subscription_fname = VIRTUAL_SUBSCRIPTION_FILE_NAME; + list_set->maildir_name = ""; + + if (data == NULL || *data == '\0' || *data == ':') { + /* we won't do any guessing for this format. */ + if (debug) + i_info("virtual: mailbox location not given"); + *error_r = "Root mail directory not given"; + return -1; + } + + if (debug) + i_info("virtual: data=%s", data); + return mailbox_list_settings_parse(data, list_set, layout_r, NULL, + error_r); +} + +static struct mail_storage *virtual_alloc(void) +{ + struct virtual_storage *storage; + pool_t pool; + + pool = pool_alloconly_create("virtual storage", 512+256); + storage = p_new(pool, struct virtual_storage, 1); + storage->storage = virtual_storage; + storage->storage.pool = pool; + + return &storage->storage; +} + +static int virtual_create(struct mail_storage *_storage, const char *data, + const char **error_r) +{ + struct virtual_storage *storage = (struct virtual_storage *)_storage; + struct mailbox_list_settings list_set; + struct stat st; + const char *layout; + + if (virtual_get_list_settings(&list_set, data, _storage->flags, + &layout, error_r) < 0) + return -1; + list_set.mail_storage_flags = &_storage->flags; + list_set.lock_method = &_storage->lock_method; + + if (stat(list_set.root_dir, &st) < 0) { + if (errno == ENOENT) { + *error_r = t_strdup_printf( + "Root mail directory doesn't exist: %s", + list_set.root_dir); + } else if (errno == EACCES) { + *error_r = mail_storage_eacces_msg("stat", + list_set.root_dir); + } else { + *error_r = t_strdup_printf("stat(%s) failed: %m", + list_set.root_dir); + } + return -1; + } + + if (mailbox_list_alloc(layout, &_storage->list, error_r) < 0) + return -1; + storage->list_module_ctx.super = _storage->list->v; + _storage->list->v.iter_is_mailbox = virtual_list_iter_is_mailbox; + _storage->list->v.delete_mailbox = virtual_list_delete_mailbox; + + MODULE_CONTEXT_SET_FULL(_storage->list, virtual_mailbox_list_module, + 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)); + return 0; +} + +struct virtual_backend_box * +virtual_backend_box_lookup(struct virtual_mailbox *mbox, uint32_t mailbox_id) +{ + struct virtual_backend_box *const *bboxes; + unsigned int i, count; + + if (mailbox_id == 0) + return NULL; + + bboxes = array_get(&mbox->backend_boxes, &count); + for (i = mailbox_id-1; i < count; i++) { + if (bboxes[i]->mailbox_id == mailbox_id) + return bboxes[i]; + } + return NULL; +} + +static int virtual_mailboxes_open(struct virtual_mailbox *mbox, + enum mailbox_open_flags open_flags) +{ + struct virtual_backend_box *const *bboxes; + struct mail_namespace *ns; + unsigned int i, count; + enum mail_error error; + const char *str; + + open_flags |= MAILBOX_OPEN_KEEP_RECENT; + + bboxes = array_get(&mbox->backend_boxes, &count); + for (i = 0; i < count; i++) { + ns = mail_namespace_find_inbox(virtual_all_namespaces); + bboxes[i]->box = mailbox_open(ns->storage, bboxes[i]->name, + NULL, open_flags); + if (bboxes[i]->box == NULL) { + str = mail_storage_get_last_error(ns->storage, &error); + mail_storage_set_error(mbox->ibox.box.storage, + error, str); + break; + } + i_array_init(&bboxes[i]->uids, 64); + } + if (i == count) + return 0; + else { + /* failed */ + for (; i > 0; i--) { + mailbox_close(&bboxes[i-1]->box); + array_free(&bboxes[i-1]->uids); + } + return -1; + } +} + +static struct mailbox * +virtual_open(struct virtual_storage *storage, const char *name, + enum mailbox_open_flags flags) +{ + struct mail_storage *_storage = &storage->storage; + struct virtual_mailbox *mbox; + struct mail_index *index; + const char *path; + pool_t pool; + + path = mailbox_list_get_path(_storage->list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + index = index_storage_alloc(_storage, name, flags, + VIRTUAL_INDEX_PREFIX); + mail_index_set_fsync_types(index, MAIL_INDEX_SYNC_TYPE_APPEND | + MAIL_INDEX_SYNC_TYPE_EXPUNGE); + + pool = pool_alloconly_create("virtual mailbox", 1024+512); + mbox = p_new(pool, struct virtual_mailbox, 1); + mbox->ibox.box = virtual_mailbox; + mbox->ibox.box.pool = pool; + mbox->ibox.box.storage = &storage->storage; + mbox->ibox.storage = &storage->storage; + mbox->ibox.mail_vfuncs = &virtual_mail_vfuncs; + mbox->ibox.index = index; + + mbox->storage = storage; + mbox->path = p_strdup(pool, path); + + mbox->virtual_ext_id = + mail_index_ext_register(index, "virtual", 0, + sizeof(struct virtual_mail_index_record), + sizeof(uint32_t)); + + if (virtual_config_read(mbox) < 0 || + virtual_mailboxes_open(mbox, flags) < 0) { + pool_unref(&pool); + return NULL; + } + + index_storage_mailbox_init(&mbox->ibox, name, flags, FALSE); + return &mbox->ibox.box; +} + +static struct mailbox * +virtual_mailbox_open(struct mail_storage *_storage, const char *name, + struct istream *input, enum mailbox_open_flags flags) +{ + struct virtual_storage *storage = (struct virtual_storage *)_storage; + const char *path; + struct stat st; + + if (input != NULL) { + mail_storage_set_critical(_storage, + "virtual doesn't support streamed mailboxes"); + return NULL; + } + + path = mailbox_list_get_path(_storage->list, name, + MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (stat(path, &st) == 0) + return virtual_open(storage, name, flags); + else if (errno == ENOENT) { + mail_storage_set_error(_storage, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + } else if (errno == EACCES) { + mail_storage_set_critical(_storage, "%s", + mail_storage_eacces_msg("stat", path)); + } else { + mail_storage_set_critical(_storage, "stat(%s) failed: %m", + path); + } + return NULL; +} + +static int virtual_storage_mailbox_close(struct mailbox *box) +{ + struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; + struct virtual_backend_box **bboxes; + unsigned int i, count; + int ret = 0; + + bboxes = array_get_modifiable(&mbox->backend_boxes, &count); + for (i = 0; i < count; i++) { + if (mailbox_close(&bboxes[i]->box) < 0) + ret = -1; + array_free(&bboxes[i]->uids); + } + array_free(&mbox->backend_boxes); + return index_storage_mailbox_close(box) < 0 ? -1 : ret; +} + +static int virtual_mailbox_create(struct mail_storage *_storage, + const char *name ATTR_UNUSED, + bool directory ATTR_UNUSED) +{ + mail_storage_set_error(_storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't create virtual mailboxes"); + return -1; +} + +static int +virtual_delete_nonrecursive(struct mailbox_list *list, const char *path, + const char *name) +{ + DIR *dir; + struct dirent *d; + string_t *full_path; + unsigned int dir_len; + bool unlinked_something = FALSE; + + dir = opendir(path); + if (dir == NULL) { + 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; + } + + 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)); + } + } + + if (closedir(dir) < 0) { + mailbox_list_set_critical(list, "closedir(%s) failed: %m", + path); + } + + 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; + } + + if (!unlinked_something) { + mailbox_list_set_error(list, MAIL_ERROR_NOTPOSSIBLE, + t_strdup_printf("Directory %s isn't empty, " + "can't delete it.", name)); + return -1; + } + return 0; +} + +static int +virtual_list_delete_mailbox(struct mailbox_list *list, const char *name) +{ + struct virtual_storage *storage = VIRTUAL_LIST_CONTEXT(list); + struct stat st; + const char *src; + + /* Make sure the indexes are closed before trying to delete the + directory that contains them. It can still fail with some NFS + implementations if indexes are opened by another session, but + that can't really be helped. */ + index_storage_destroy_unrefed(); + + /* delete the index and control directories */ + if (storage->list_module_ctx.super.delete_mailbox(list, name) < 0) + return -1; + + /* check if the mailbox actually exists */ + src = mailbox_list_get_path(list, name, MAILBOX_LIST_PATH_TYPE_MAILBOX); + if (stat(src, &st) != 0 && errno == ENOENT) { + mailbox_list_set_error(list, MAIL_ERROR_NOTFOUND, + T_MAIL_ERR_MAILBOX_NOT_FOUND(name)); + return -1; + } + + return virtual_delete_nonrecursive(list, src, name); +} + +static void virtual_notify_changes(struct mailbox *box) +{ + struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; + + // FIXME +} + +static int +virtual_list_iter_is_mailbox(struct mailbox_list_iterate_context *ctx + ATTR_UNUSED, + const char *dir, const char *fname, + enum mailbox_list_file_type type, + enum mailbox_info_flags *flags) +{ + const char *path, *maildir_path; + struct stat st; + int ret = 1; + + /* try to avoid stat() with these checks */ + if (type != MAILBOX_LIST_FILE_TYPE_DIR && + type != MAILBOX_LIST_FILE_TYPE_SYMLINK && + type != MAILBOX_LIST_FILE_TYPE_UNKNOWN) { + /* it's a file */ + *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + return 0; + } + + /* need to stat() then */ + path = t_strconcat(dir, "/", fname, NULL); + if (stat(path, &st) == 0) { + if (!S_ISDIR(st.st_mode)) { + /* non-directory */ + *flags |= MAILBOX_NOSELECT | MAILBOX_NOINFERIORS; + ret = 0; + } else if (st.st_nlink == 2) { + /* no subdirectories */ + *flags |= MAILBOX_NOCHILDREN; + } else if (*ctx->list->set.maildir_name != '\0') { + /* non-default configuration: we have one directory + containing the mailboxes. if there are 3 links, + either this is a selectable mailbox without children + or non-selectable mailbox with children */ + if (st.st_nlink > 3) + *flags |= MAILBOX_CHILDREN; + } else { + /* default configuration: all subdirectories are + child mailboxes. */ + if (st.st_nlink > 2) + *flags |= MAILBOX_CHILDREN; + } + } else { + /* non-selectable. probably either access denied, or symlink + destination not found. don't bother logging errors. */ + *flags |= MAILBOX_NOSELECT; + } + if ((*flags & MAILBOX_NOSELECT) == 0) { + /* make sure it's a selectable mailbox */ + maildir_path = t_strconcat(path, "/"VIRTUAL_CONFIG_FNAME, NULL); + if (stat(maildir_path, &st) < 0 || !S_ISDIR(st.st_mode)) + *flags |= MAILBOX_NOSELECT; + } + return ret; +} + +static int +virtual_save_init(struct mailbox_transaction_context *_t, + enum mail_flags flags ATTR_UNUSED, + struct mail_keywords *keywords ATTR_UNUSED, + time_t received_date ATTR_UNUSED, + int timezone_offset ATTR_UNUSED, + const char *from_envelope ATTR_UNUSED, + struct istream *input ATTR_UNUSED, + struct mail *dest_mail ATTR_UNUSED, + struct mail_save_context **ctx_r) +{ + mail_storage_set_error(_t->box->storage, MAIL_ERROR_NOTPOSSIBLE, + "Can't save to virtual mailboxes"); + *ctx_r = NULL; + return -1; +} + +static void virtual_class_init(void) +{ + virtual_transaction_class_init(); +} + +static void virtual_class_deinit(void) +{ + virtual_transaction_class_deinit(); +} + +struct mail_storage virtual_storage = { + MEMBER(name) VIRTUAL_STORAGE_NAME, + MEMBER(mailbox_is_file) FALSE, + + { + virtual_class_init, + virtual_class_deinit, + virtual_alloc, + virtual_create, + index_storage_destroy, + NULL, + virtual_mailbox_open, + virtual_mailbox_create + } +}; + +struct mailbox virtual_mailbox = { + MEMBER(name) NULL, + MEMBER(storage) NULL, + + { + index_storage_is_readonly, + index_storage_allow_new_keywords, + virtual_storage_mailbox_close, + index_storage_get_status, + NULL, + NULL, + virtual_storage_sync_init, + index_mailbox_sync_next, + index_mailbox_sync_deinit, + NULL, + virtual_notify_changes, + index_transaction_begin, + index_transaction_commit, + index_transaction_rollback, + index_keywords_create, + index_keywords_free, + index_storage_get_uids, + virtual_mail_alloc, + index_header_lookup_init, + index_header_lookup_deinit, + index_storage_search_init, + index_storage_search_deinit, + index_storage_search_next_nonblock, + index_storage_search_next_update_seq, + virtual_save_init, + NULL, + NULL, + NULL, + mail_storage_copy, + index_storage_is_inconsistent + } +};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-storage.h Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,70 @@ +#ifndef VIRTUAL_STORAGE_H +#define VIRTUAL_STORAGE_H + +#include "seq-range-array.h" +#include "index-storage.h" +#include "mailbox-list-private.h" + +#define VIRTUAL_STORAGE_NAME "virtual" +#define VIRTUAL_SUBSCRIPTION_FILE_NAME ".virtual-subscriptions" +#define VIRTUAL_CONFIG_FNAME "dovecot-virtual" +#define VIRTUAL_INDEX_PREFIX "dovecot.index" + +struct virtual_mail_index_record { + uint32_t mailbox_id; + uint32_t real_uid; +}; + +struct virtual_storage { + struct mail_storage storage; + union mailbox_list_module_context list_module_ctx; +}; + +struct virtual_backend_box { + uint32_t mailbox_id; + const char *name; + struct mail_search_arg *search_args; + + struct mailbox *box; + /* Sorted list of UIDs currently included in the virtual mailbox */ + ARRAY_TYPE(seq_range) uids; + + struct mail *sync_mail; + unsigned int sync_iter_idx; + unsigned int sync_iter_prev_real_uid; +}; + +struct virtual_mailbox { + struct index_mailbox ibox; + struct virtual_storage *storage; + + const char *path; + uint32_t virtual_ext_id; + + /* Mailboxes this virtual mailbox consists of, sorted by mailbox_id */ + ARRAY_DEFINE(backend_boxes, struct virtual_backend_box *); +}; + +extern struct mail_storage virtual_storage; +extern struct mail_vfuncs virtual_mail_vfuncs; + +int virtual_config_read(struct virtual_mailbox *mbox); + +struct virtual_backend_box * +virtual_backend_box_lookup(struct virtual_mailbox *mbox, uint32_t mailbox_id); +struct mailbox_transaction_context * +virtual_transaction_get(struct mailbox_transaction_context *trans, + struct mailbox *backend_box); + +struct mail * +virtual_mail_alloc(struct mailbox_transaction_context *t, + enum mail_fetch_field wanted_fields, + struct mailbox_header_lookup_ctx *wanted_headers); + +struct mailbox_sync_context * +virtual_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags); + +void virtual_transaction_class_init(void); +void virtual_transaction_class_deinit(void); + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-sync.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,474 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "ioloop.h" +#include "str.h" +#include "mail-search-build.h" +#include "virtual-storage.h" + +#include <stdlib.h> + +struct virtual_sync_mail { + uint32_t vseq; + struct virtual_mail_index_record vrec; +}; + +struct virtual_sync_context { + struct virtual_mailbox *mbox; + struct mail_index_sync_ctx *index_sync_ctx; + struct mail_index *index; + struct mail_index_view *sync_view; + struct mail_index_transaction *trans; + const char *const *kw_all; + + enum mailbox_sync_flags flags; + uint32_t uid_validity; + unsigned int expunge_removed:1; +}; + +static void virtual_sync_set_uidvalidity(struct virtual_sync_context *ctx) +{ + 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); + ctx->uid_validity = uid_validity; +} + +static void virtual_sync_external_flags(struct virtual_sync_context *ctx, + struct virtual_backend_box *bbox, + uint32_t vseq, uint32_t real_uid) +{ + enum mail_flags flags; + const char *const *kw_names; + struct mail_keywords *keywords; + + if (!mail_set_uid(bbox->sync_mail, real_uid)) + i_panic("UID lost unexpectedly"); + + /* copy flags */ + flags = mail_get_flags(bbox->sync_mail); + mail_index_update_flags(ctx->trans, vseq, MODIFY_REPLACE, flags); + + /* copy keywords */ + kw_names = mail_get_keywords(bbox->sync_mail); + if (kw_names[0] != NULL) { + keywords = mail_index_keywords_create(ctx->index, kw_names); + mail_index_update_keywords(ctx->trans, vseq, + MODIFY_REPLACE, keywords); + mail_index_keywords_free(&keywords); + } +} + +static void virtual_sync_external_appends(struct virtual_sync_context *ctx, + struct virtual_backend_box *bbox, + uint32_t uid1, uint32_t uid2) +{ + uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; + struct virtual_mail_index_record vrec; + uint32_t uid, vseq; + + vrec.mailbox_id = bbox->mailbox_id; + for (uid = uid1; uid <= uid2; uid++) { + mail_index_append(ctx->trans, 0, &vseq); + vrec.real_uid = uid; + mail_index_update_ext(ctx->trans, vseq, virtual_ext_id, + &vrec, NULL); + virtual_sync_external_flags(ctx, bbox, vseq, uid); + } +} + +static void +virtual_sync_external_appends_finish_box(struct virtual_sync_context *ctx, + struct virtual_backend_box *bbox) +{ + const struct seq_range *seqs; + unsigned int seqs_count; + uint32_t first_ruid, last_ruid; + + seqs = array_get(&bbox->uids, &seqs_count); + while (bbox->sync_iter_idx < seqs_count) { + /* max(seq1,prev_uid+1)..seq2 contain newly seen UIDs */ + first_ruid = I_MAX(seqs[bbox->sync_iter_idx].seq1, + bbox->sync_iter_prev_real_uid + 1); + last_ruid = seqs[bbox->sync_iter_idx].seq2; + + if (first_ruid <= last_ruid) { + virtual_sync_external_appends(ctx, bbox, + first_ruid, last_ruid); + } + bbox->sync_iter_idx++; + } +} + +static void +virtual_sync_external_appends_finish(struct virtual_sync_context *ctx) +{ + struct virtual_backend_box *const *bboxes; + unsigned int i, count; + uint32_t next_uid; + + next_uid = mail_index_get_header(ctx->sync_view)->next_uid; + bboxes = array_get(&ctx->mbox->backend_boxes, &count); + for (i = 0; i < count; i++) { + virtual_sync_external_appends_finish_box(ctx, bboxes[i]); + mail_index_append_assign_uids(ctx->trans, next_uid, &next_uid); + } +} + +static int virtual_sync_mail_cmp(const void *p1, const void *p2) +{ + const struct virtual_sync_mail *m1 = p1, *m2 = p2; + + if (m1->vrec.mailbox_id < m2->vrec.mailbox_id) + return -1; + if (m1->vrec.mailbox_id > m2->vrec.mailbox_id) + return 1; + + if (m1->vrec.real_uid < m2->vrec.real_uid) + return -1; + if (m1->vrec.real_uid > m2->vrec.real_uid) + return 1; + /* broken */ + return 0; +} + +static void virtual_sync_external(struct virtual_sync_context *ctx) +{ + uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; + struct virtual_backend_box *bbox; + struct virtual_sync_mail *vmails; + const struct virtual_mail_index_record *vrec; + const void *data; + const struct seq_range *seqs; + unsigned int i, seqs_count; + uint32_t vseq, first_ruid, last_ruid, messages; + bool expunged; + + messages = mail_index_view_get_messages_count(ctx->sync_view); + + /* sort the messages by their backend mailbox and real UID */ + vmails = messages == 0 ? NULL : + i_new(struct virtual_sync_mail, messages); + for (vseq = 1; vseq <= messages; vseq++) { + mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id, + &data, &expunged); + vrec = data; + vmails[vseq-1].vseq = vseq; + vmails[vseq-1].vrec = *vrec; + } + qsort(vmails, messages, sizeof(*vmails), virtual_sync_mail_cmp); + + bbox = NULL; + for (i = 0; i < messages; i++) { + vseq = vmails[i].vseq; + vrec = &vmails[i].vrec; + + if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) { + bbox = virtual_backend_box_lookup(ctx->mbox, + vrec->mailbox_id); + if (bbox == NULL) { + /* the entire mailbox is lost */ + mail_index_expunge(ctx->trans, vseq); + continue; + } + } + + seqs = array_get(&bbox->uids, &seqs_count); + while (bbox->sync_iter_idx < seqs_count) { + /* max(seq1,prev_uid+1)..min(seq2,uid-1) contain + newly seen UIDs */ + first_ruid = I_MAX(seqs[bbox->sync_iter_idx].seq1, + bbox->sync_iter_prev_real_uid + 1); + last_ruid = I_MIN(seqs[bbox->sync_iter_idx].seq2, + vrec->real_uid - 1); + if (first_ruid <= last_ruid) { + virtual_sync_external_appends(ctx, bbox, + first_ruid, + last_ruid); + } + if (vrec->real_uid <= seqs[bbox->sync_iter_idx].seq2) + break; + bbox->sync_iter_idx++; + } + if (bbox->sync_iter_idx >= seqs_count || + vrec->real_uid < seqs[bbox->sync_iter_idx].seq1) { + if (ctx->expunge_removed) { + mail_index_expunge(ctx->trans, vseq); + continue; + } + } + + /* uid is within seq1..seq2 */ + bbox->sync_iter_prev_real_uid = vrec->real_uid; + virtual_sync_external_flags(ctx, bbox, vseq, vrec->real_uid); + } + i_free(vmails); + + virtual_sync_external_appends_finish(ctx); +} + +static void virtual_sync_index_rec(struct virtual_sync_context *ctx, + const struct mail_index_sync_rec *sync_rec) +{ + uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id; + struct virtual_backend_box *bbox; + const struct virtual_mail_index_record *vrec; + const void *data; + enum mail_flags flags; + struct mail_keywords *keywords; + enum modify_type modify_type; + const char *kw_names[2]; + uint32_t vseq, seq1, seq2; + bool expunged; + + switch (sync_rec->type) { + case MAIL_INDEX_SYNC_TYPE_APPEND: + /* don't care */ + return; + case MAIL_INDEX_SYNC_TYPE_EXPUNGE: + case MAIL_INDEX_SYNC_TYPE_FLAGS: + case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: + case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: + case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET: + break; + } + if (!mail_index_lookup_seq_range(ctx->sync_view, + sync_rec->uid1, sync_rec->uid2, + &seq1, &seq2)) { + /* already expunged, nothing to do. */ + return; + } + + for (vseq = seq1; vseq <= seq2; vseq++) { + mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id, + &data, &expunged); + vrec = data; + + bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id); + if (bbox == NULL) + continue; + + if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) + i_panic("UID lost unexpectedly"); + + switch (sync_rec->type) { + case MAIL_INDEX_SYNC_TYPE_EXPUNGE: + mail_expunge(bbox->sync_mail); + break; + case MAIL_INDEX_SYNC_TYPE_FLAGS: + flags = sync_rec->add_flags & MAIL_FLAGS_NONRECENT; + if (flags != 0) { + mail_update_flags(bbox->sync_mail, + MODIFY_ADD, flags); + } + flags = sync_rec->remove_flags & MAIL_FLAGS_NONRECENT; + if (flags != 0) { + mail_update_flags(bbox->sync_mail, + MODIFY_REMOVE, flags); + } + break; + case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD: + case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE: + kw_names[0] = ctx->kw_all[sync_rec->keyword_idx]; + kw_names[1] = NULL; + keywords = mailbox_keywords_create_valid(bbox->box, + kw_names); + + modify_type = sync_rec->type == + MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD ? + MODIFY_ADD : MODIFY_REMOVE; + mail_update_keywords(bbox->sync_mail, + modify_type, keywords); + mailbox_keywords_free(bbox->box, &keywords); + break; + case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET: + kw_names[0] = NULL; + keywords = mailbox_keywords_create_valid(bbox->box, + kw_names); + mail_update_keywords(bbox->sync_mail, MODIFY_REPLACE, + keywords); + mailbox_keywords_free(bbox->box, &keywords); + break; + case MAIL_INDEX_SYNC_TYPE_APPEND: + i_unreached(); + } + } +} + +static void virtual_sync_index(struct virtual_sync_context *ctx) +{ + struct mailbox *box = &ctx->mbox->ibox.box; + const ARRAY_TYPE(keywords) *keywords; + const struct mail_index_header *hdr; + struct mail_index_sync_rec sync_rec; + uint32_t seq1, seq2; + + hdr = mail_index_get_header(ctx->sync_view); + if (hdr->uid_validity != 0) + ctx->uid_validity = hdr->uid_validity; + else + virtual_sync_set_uidvalidity(ctx); + + /* mark the newly seen messages as recent */ + if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid, + hdr->next_uid, &seq1, &seq2)) { + index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view, + seq1, seq2); + } + + keywords = mail_index_get_keywords(ctx->index); + ctx->kw_all = array_count(keywords) == 0 ? NULL : + array_idx(keywords, 0); + while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) + virtual_sync_index_rec(ctx, &sync_rec); + + if (box->v.sync_notify != NULL) + box->v.sync_notify(box, 0, 0); +} + +static int virtual_sync_backend_box(struct virtual_sync_context *ctx, + struct virtual_backend_box *bbox) +{ + struct mailbox_transaction_context *trans; + struct mail_search_context *search_ctx; + struct mail *mail; + enum mailbox_sync_flags sync_flags; + int ret; + + sync_flags = ctx->flags & (MAILBOX_SYNC_FLAG_FULL_READ | + MAILBOX_SYNC_FLAG_FULL_WRITE | + MAILBOX_SYNC_FLAG_FAST); + if (mailbox_sync(bbox->box, sync_flags, 0, NULL) < 0) + return -1; + + trans = mailbox_transaction_begin(bbox->box, 0); + mail = mail_alloc(trans, 0, NULL); + + mail_search_args_init(bbox->search_args, bbox->box, FALSE); + search_ctx = mailbox_search_init(trans, "UTF-8", + bbox->search_args, NULL); + + array_clear(&bbox->uids); + while (mailbox_search_next(search_ctx, mail) > 0) + seq_range_array_add(&bbox->uids, 0, mail->uid); + ret = mailbox_search_deinit(&search_ctx); + mail_free(&mail); + + mail_search_args_deinit(bbox->search_args, bbox->box); + (void)mailbox_transaction_commit(&trans); + return ret; +} + +static int virtual_sync_backend_boxes(struct virtual_sync_context *ctx) +{ + struct virtual_backend_box *const *bboxes; + struct mailbox_transaction_context *trans; + unsigned int i, count; + + bboxes = array_get(&ctx->mbox->backend_boxes, &count); + for (i = 0; i < count; i++) { + if (virtual_sync_backend_box(ctx, bboxes[i]) < 0) + return -1; + + bboxes[i]->sync_iter_idx = 0; + bboxes[i]->sync_iter_prev_real_uid = 0; + + i_assert(bboxes[i]->sync_mail == NULL); + trans = mailbox_transaction_begin(bboxes[i]->box, 0); + bboxes[i]->sync_mail = mail_alloc(trans, 0, NULL); + } + return 0; +} + +static void virtual_sync_backend_boxes_finish(struct virtual_sync_context *ctx) +{ + struct virtual_backend_box *const *bboxes; + struct mailbox_transaction_context *trans; + unsigned int i, count; + + bboxes = array_get(&ctx->mbox->backend_boxes, &count); + for (i = 0; i < count; i++) { + if (bboxes[i]->sync_mail != NULL) { + trans = bboxes[i]->sync_mail->transaction; + mail_free(&bboxes[i]->sync_mail); + (void)mailbox_transaction_commit(&trans); + } + } +} + +static int virtual_sync_finish(struct virtual_sync_context *ctx, bool success) +{ + int ret = success ? 0 : -1; + + virtual_sync_backend_boxes_finish(ctx); + if (success) { + if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) { + mail_storage_set_index_error(&ctx->mbox->ibox); + ret = -1; + } + } else { + mail_index_sync_rollback(&ctx->index_sync_ctx); + } + i_free(ctx); + return 0; +} + +static int virtual_sync(struct virtual_mailbox *mbox, + enum mailbox_sync_flags flags) +{ + struct virtual_sync_context *ctx; + enum mail_index_sync_flags index_sync_flags; + int ret; + + ctx = i_new(struct virtual_sync_context, 1); + ctx->mbox = mbox; + ctx->flags = flags; + ctx->index = mbox->ibox.index; + /* Removed messages are expunged when + a) EXPUNGE is used + b) Mailbox is being opened (FIX_INCONSISTENT is set) */ + ctx->expunge_removed = + (ctx->flags & (MAILBOX_SYNC_FLAG_EXPUNGE | + MAILBOX_SYNC_FLAG_FIX_INCONSISTENT)) != 0; + + index_sync_flags = MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY | + MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES; + if (!mbox->ibox.keep_recent) + index_sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT; + + ret = mail_index_sync_begin(ctx->index, &ctx->index_sync_ctx, + &ctx->sync_view, &ctx->trans, + index_sync_flags); + if (ret <= 0) { + if (ret < 0) + mail_storage_set_index_error(&mbox->ibox); + i_free(ctx); + return ret; + } + + /* update list of UIDs in mailboxes */ + if (virtual_sync_backend_boxes(ctx) < 0) + return virtual_sync_finish(ctx, FALSE); + + virtual_sync_index(ctx); + virtual_sync_external(ctx); + return virtual_sync_finish(ctx, TRUE); +} + +struct mailbox_sync_context * +virtual_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) +{ + struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; + int ret = 0; + + if (!box->opened) + index_storage_mailbox_open(&mbox->ibox); + + if (index_mailbox_want_full_sync(&mbox->ibox, flags)) + ret = virtual_sync(mbox, flags); + + return index_mailbox_sync_init(box, flags, ret < 0); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/plugins/virtual/virtual-transaction.c Fri Mar 14 12:00:32 2008 +0200 @@ -0,0 +1,114 @@ +/* Copyright (c) 2008 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "array.h" +#include "virtual-storage.h" + +struct virtual_transaction_context { + struct index_transaction_context ictx; + union mail_index_transaction_module_context module_ctx; + + ARRAY_DEFINE(backend_transactions, + struct mailbox_transaction_context *); +}; + +static void (*next_hook_mail_index_transaction_created) + (struct mail_index_transaction *t) = NULL; + +struct mailbox_transaction_context * +virtual_transaction_get(struct mailbox_transaction_context *trans, + struct mailbox *backend_box) +{ + struct virtual_transaction_context *dt = + (struct virtual_transaction_context *)trans; + struct mailbox_transaction_context *const *bt, *new_bt; + unsigned int i, count; + + bt = array_get(&dt->backend_transactions, &count); + for (i = 0; i < count; i++) { + if (bt[i]->box == backend_box) + return bt[i]; + } + + new_bt = mailbox_transaction_begin(backend_box, trans->flags); + array_append(&dt->backend_transactions, &new_bt, 1); + return new_bt; +} + +static int virtual_transaction_commit(struct mail_index_transaction *t, + uint32_t *log_file_seq_r, + uoff_t *log_file_offset_r) +{ + struct virtual_transaction_context *dt = MAIL_STORAGE_CONTEXT(t); + struct mailbox_transaction_context **bt; + unsigned int i, count; + int ret = 0; + + bt = array_get_modifiable(&dt->backend_transactions, &count); + for (i = 0; i < count; i++) { + if (mailbox_transaction_commit(&bt[i]) < 0) + ret = -1; + } + array_free(&dt->backend_transactions); + + if (index_transaction_finish_commit(&dt->ictx, log_file_seq_r, + log_file_offset_r) < 0) + ret = -1; + return ret; +} + +static void virtual_transaction_rollback(struct mail_index_transaction *t) +{ + struct virtual_transaction_context *dt = MAIL_STORAGE_CONTEXT(t); + struct mailbox_transaction_context **bt; + unsigned int i, count; + + bt = array_get_modifiable(&dt->backend_transactions, &count); + for (i = 0; i < count; i++) + mailbox_transaction_rollback(&bt[i]); + array_free(&dt->backend_transactions); + + index_transaction_finish_rollback(&dt->ictx); +} + +static void virtual_transaction_created(struct mail_index_transaction *t) +{ + struct mailbox *box = MAIL_STORAGE_CONTEXT(t->view); + + /* index can be for mailbox list index, in which case box=NULL */ + if (box != NULL && + strcmp(box->storage->name, VIRTUAL_STORAGE_NAME) == 0) { + struct virtual_mailbox *mbox = (struct virtual_mailbox *)box; + struct virtual_transaction_context *mt; + + mt = i_new(struct virtual_transaction_context, 1); + mt->ictx.trans = t; + mt->ictx.super = t->v; + + t->v.commit = virtual_transaction_commit; + t->v.rollback = virtual_transaction_rollback; + MODULE_CONTEXT_SET(t, mail_storage_mail_index_module, mt); + + i_array_init(&mt->backend_transactions, + array_count(&mbox->backend_boxes)); + index_transaction_init(&mt->ictx, &mbox->ibox); + } + + if (next_hook_mail_index_transaction_created != NULL) + next_hook_mail_index_transaction_created(t); +} + +void virtual_transaction_class_init(void) +{ + next_hook_mail_index_transaction_created = + hook_mail_index_transaction_created; + hook_mail_index_transaction_created = virtual_transaction_created; +} + +void virtual_transaction_class_deinit(void) +{ + i_assert(hook_mail_index_transaction_created == + virtual_transaction_created); + hook_mail_index_transaction_created = + next_hook_mail_index_transaction_created; +}