virtual-sync.c revision 1ce59b0bd89ab1b90d38fa9b9d51a92bf99bb73a
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen/* Copyright (c) 2008-2016 Dovecot authors, see the included COPYING file */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "lib.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "array.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "bsearch-insert-pos.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "ioloop.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "str.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mail-index-modseq.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mail-search-build.h"
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen#include "mailbox-search-result-private.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "index-sync-private.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "index-search-result.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen#include "virtual-storage.h"
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct virtual_add_record {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mail_index_record rec;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen time_t received_date;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen};
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct virtual_sync_mail {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t vseq;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mail_index_record vrec;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen};
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstruct virtual_sync_context {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mailbox *mbox;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index_sync_ctx *index_sync_ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index *index;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index_view *sync_view;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index_transaction *trans;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const char *const *kw_all;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* messages expunged within this sync */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ARRAY_TYPE(seq_range) sync_expunges;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ARRAY(struct virtual_add_record) all_adds;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* all messages in this sync, sorted by mailbox_id
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (but unsorted inside it for now, since it doesn't matter) */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ARRAY(struct virtual_sync_mail) all_mails;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen uint32_t all_mails_idx, all_mails_prev_mailbox_id;
2eb2cf8eeb763bd5ca9b6848dce32f0303e88ec1Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen enum mailbox_sync_flags flags;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int ext_header_changed:1;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen unsigned int ext_header_rewrite:1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int expunge_removed:1;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen unsigned int index_broken:1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen};
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_set_uidvalidity(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t uid_validity = ioloop_time;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_header(ctx->trans,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen offsetof(struct mail_index_header, uid_validity),
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen &uid_validity, sizeof(uid_validity), TRUE);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->uid_validity = uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_external_flags(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *bbox,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t vseq, uint32_t real_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen enum mail_flags flags;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *const *kw_names;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_keywords *keywords;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!mail_set_uid(bbox->sync_mail, real_uid)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* we may have reopened the mailbox, which could have
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen caused the mail to be expunged already. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* copy flags */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen flags = mail_get_flags(bbox->sync_mail);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_flags(ctx->trans, vseq, MODIFY_REPLACE, flags);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen /* copy keywords */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen kw_names = mail_get_keywords(bbox->sync_mail);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen keywords = mail_index_keywords_create(ctx->index, kw_names);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_keywords(ctx->trans, vseq, MODIFY_REPLACE, keywords);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_keywords_unref(&keywords);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_mail_uid_cmp(const void *p1, const void *p2)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_sync_mail *m1 = p1, *m2 = p2;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (m1->vrec.mailbox_id < m2->vrec.mailbox_id)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (m1->vrec.mailbox_id > m2->vrec.mailbox_id)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen return 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (m1->vrec.real_uid < m2->vrec.real_uid)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (m1->vrec.real_uid > m2->vrec.real_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* broken */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_backend_box_sync_mail_set(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen struct mailbox_transaction_context *trans;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (bbox->sync_mail == NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen trans = mailbox_transaction_begin(bbox->box, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_mail = mail_alloc(trans, 0, NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int bbox_mailbox_id_cmp(struct virtual_backend_box *const *b1,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *const *b2)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((*b1)->mailbox_id < (*b2)->mailbox_id)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if ((*b1)->mailbox_id > (*b2)->mailbox_id)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen}
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenstatic int
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainenvirtual_sync_get_backend_box(struct virtual_mailbox *mbox, const char *name,
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen struct virtual_backend_box **bbox_r)
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen{
b34fdb68d376d85b4880da4a4bdf67ae726a381bTimo Sirainen *bbox_r = virtual_backend_box_lookup_name(mbox, name);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (*bbox_r != NULL || !mbox->sync_initialized)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* another process just added a new mailbox.
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen we can't handle this currently. */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mbox->inconsistent = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_storage_set_error(mbox->box.storage, MAIL_ERROR_TEMP, t_strdup_printf(
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Backend mailbox '%s' added by another session. "
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen "Reopen the virtual mailbox.", name));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenint virtual_mailbox_ext_header_read(struct virtual_mailbox *mbox,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index_view *view,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bool *broken_r)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *box_path = mailbox_get_path(&mbox->box);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const struct virtual_mail_index_header *ext_hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct mail_index_header *hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_mail_index_mailbox_record *mailboxes;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *bbox, **bboxes;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const void *ext_data;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen size_t ext_size;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i, count, ext_name_offset, ext_mailbox_count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t prev_mailbox_id;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen int ret = 1;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen *broken_r = FALSE;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen hdr = mail_index_get_header(view);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_get_header_ext(view, mbox->virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen &ext_data, &ext_size);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr = ext_data;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mbox->sync_initialized &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_uid_validity == hdr->uid_validity &&
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ext_size >= sizeof(*ext_hdr) &&
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_change_counter == ext_hdr->change_counter) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* fully refreshed */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_uid_validity = hdr->uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ext_hdr == NULL ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->search_args_crc32 != ext_hdr->search_args_crc32) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailboxes = NULL;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_name_offset = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_mailbox_count = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } else {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->prev_change_counter = ext_hdr->change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailboxes = (const void *)(ext_hdr + 1);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen ext_name_offset = sizeof(*ext_hdr) +
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr->mailbox_count * sizeof(*mailboxes);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ext_name_offset >= ext_size ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr->mailbox_count > INT_MAX/sizeof(*mailboxes)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox_count header",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen box_path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen *broken_r = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_mailbox_count = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } else {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_mailbox_count = ext_hdr->mailbox_count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* update mailbox backends */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen prev_mailbox_id = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < ext_mailbox_count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mailboxes[i].id > ext_hdr->highest_mailbox_id ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailboxes[i].id <= prev_mailbox_id) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox id",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen box_path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mailboxes[i].name_len == 0 ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailboxes[i].name_len > ext_size) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox name_len",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen box_path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ext_name_offset + mailboxes[i].name_len > ext_size) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_error("virtual index %s: Broken mailbox list",
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen box_path);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen T_BEGIN {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const unsigned char *nameptr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const char *name;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen nameptr = CONST_PTR_OFFSET(ext_data, ext_name_offset);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen name = t_strndup(nameptr, mailboxes[i].name_len);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_sync_get_backend_box(mbox, name, &bbox) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } T_END;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (bbox == NULL) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ret < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* mailbox no longer exists. */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen ret = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen } else {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->mailbox_id = mailboxes[i].id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_uid_validity = mailboxes[i].uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->ondisk_highest_modseq =
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_highest_modseq =
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailboxes[i].highest_modseq;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_next_uid = mailboxes[i].next_uid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->sync_mailbox_idx = i;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_name_offset += mailboxes[i].name_len;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen prev_mailbox_id = mailboxes[i].id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (i < ext_mailbox_count) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen *broken_r = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->highest_mailbox_id = ext_hdr == NULL ? 0 :
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr->highest_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mbox->sync_initialized = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* assign new mailbox IDs if any are missing */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes = array_get_modifiable(&mbox->backend_boxes, &count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (bboxes[i]->mailbox_id == 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes[i]->mailbox_id = ++mbox->highest_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* sort the backend mailboxes by mailbox_id. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_sort(&mbox->backend_boxes, bbox_mailbox_id_cmp);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_ext_header_rewrite(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mail_index_header ext_hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_mail_index_mailbox_record mailbox;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box **bboxes;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_t *buf;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const void *ext_data;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen size_t ext_size;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i, mailbox_pos, name_pos, count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes = array_get_modifiable(&ctx->mbox->backend_boxes, &count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox_pos = sizeof(ext_hdr);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen name_pos = mailbox_pos + sizeof(mailbox) * count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen memset(&ext_hdr, 0, sizeof(ext_hdr));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen memset(&mailbox, 0, sizeof(mailbox));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.mailbox_count = count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.highest_mailbox_id = ctx->mbox->highest_mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.search_args_crc32 = ctx->mbox->search_args_crc32;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), name_pos + 256);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_append(buf, &ext_hdr, sizeof(ext_hdr));
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(i == 0 ||
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes[i]->mailbox_id > bboxes[i-1]->mailbox_id);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bboxes[i]->sync_mailbox_idx = i;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox.id = bboxes[i]->mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox.name_len = strlen(bboxes[i]->name);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox.uid_validity = bboxes[i]->sync_uid_validity;
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen mailbox.highest_modseq = bboxes[i]->ondisk_highest_modseq;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox.next_uid = bboxes[i]->sync_next_uid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen buffer_write(buf, mailbox_pos, &mailbox, sizeof(mailbox));
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen buffer_write(buf, name_pos, bboxes[i]->name, mailbox.name_len);
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox_pos += sizeof(mailbox);
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen name_pos += mailbox.name_len;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(buf->used == name_pos);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_get_header_ext(ctx->sync_view, ctx->mbox->virtual_ext_id,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen &ext_data, &ext_size);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ext_size < name_pos) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_ext_resize(ctx->trans, ctx->mbox->virtual_ext_id,
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen name_pos,
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen sizeof(struct virtual_mail_index_record),
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen sizeof(uint32_t));
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen 0, buf->data, name_pos);
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_ext_header_update(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen struct virtual_mail_index_header ext_hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!ctx->ext_header_changed)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* we changed something - update the change counter in header */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen offsetof(struct virtual_mail_index_header, change_counter),
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen &ext_hdr.change_counter, sizeof(ext_hdr.change_counter));
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_index_rec(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct mail_index_sync_rec *sync_rec)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *bbox;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_mail_index_record *vrec;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const void *data;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen enum mail_flags flags;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen struct mail_keywords *keywords;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen enum modify_type modify_type;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen const char *kw_names[2];
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen uint32_t vseq, seq1, seq2;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen switch (sync_rec->type) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen case MAIL_INDEX_SYNC_TYPE_FLAGS:
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen break;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!mail_index_lookup_seq_range(ctx->sync_view,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen sync_rec->uid1, sync_rec->uid2,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen &seq1, &seq2)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* already expunged, nothing to do. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
d896b20f276a4fe2b291b238c2ba580c58c8d186Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen for (vseq = seq1; vseq <= seq2; vseq++) {
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id,
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen &data, NULL);
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen vrec = data;
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (bbox == NULL)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen continue;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!bbox->box->opened) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_backend_box_open(ctx->mbox, bbox) < 0) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen virtual_box_copy_error(&ctx->mbox->box,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->box);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen return -1;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen } else {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen virtual_backend_box_accessed(ctx->mbox, bbox);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen virtual_backend_box_sync_mail_set(bbox);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* message is already expunged from backend mailbox. */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen continue;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen switch (sync_rec->type) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_expunge(bbox->sync_mail);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen break;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen case MAIL_INDEX_SYNC_TYPE_FLAGS:
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen flags = sync_rec->add_flags & MAIL_FLAGS_NONRECENT;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (flags != 0) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_update_flags(bbox->sync_mail,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen MODIFY_ADD, flags);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen flags = sync_rec->remove_flags & MAIL_FLAGS_NONRECENT;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (flags != 0) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_update_flags(bbox->sync_mail,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen MODIFY_REMOVE, flags);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen break;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen kw_names[0] = ctx->kw_all[sync_rec->keyword_idx];
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen kw_names[1] = NULL;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen keywords = mailbox_keywords_create_valid(bbox->box,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen kw_names);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen modify_type = sync_rec->type ==
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD ?
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen MODIFY_ADD : MODIFY_REMOVE;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mail_update_keywords(bbox->sync_mail,
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen modify_type, keywords);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen mailbox_keywords_unref(&keywords);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen break;
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_sync_index_changes(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const ARRAY_TYPE(keywords) *keywords;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_index_sync_rec sync_rec;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen keywords = mail_index_get_keywords(ctx->index);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->kw_all = array_count(keywords) == 0 ? NULL :
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_idx(keywords, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (virtual_sync_index_rec(ctx, &sync_rec) < 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainen}
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
3e564425db51f3921ce4de11859777135fdedd15Timo Sirainenstatic void virtual_sync_index_finish(struct virtual_sync_context *ctx)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mailbox *box = &ctx->mbox->box;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct mail_index_header *hdr;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t seq1, seq2;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen hdr = mail_index_get_header(ctx->sync_view);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (hdr->uid_validity != 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ctx->uid_validity = hdr->uid_validity;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen else
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen virtual_sync_set_uidvalidity(ctx);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* mark the newly seen messages as recent */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen hdr->next_uid, &seq1, &seq2)) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mailbox_recent_flags_set_seqs(&ctx->mbox->box, ctx->sync_view,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen seq1, seq2);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (ctx->ext_header_rewrite) {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* entire mailbox list needs to be rewritten */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen virtual_sync_ext_header_rewrite(ctx);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen } else {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen /* update only changed parts in the header */
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen virtual_sync_ext_header_update(ctx);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (box->v.sync_notify != NULL)
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen box->v.sync_notify(box, 0, 0);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen}
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainenstatic int virtual_sync_backend_box_init(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mailbox_transaction_context *trans;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail_search_context *search_ctx;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct mail *mail;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_uidmap uidmap;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen enum mailbox_search_result_flags result_flags;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen int ret;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen trans = mailbox_transaction_begin(bbox->box, 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!bbox->search_args_initialized) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mail_search_args_init(bbox->search_args, bbox->box, FALSE, NULL);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->search_args_initialized = TRUE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen search_ctx = mailbox_search_init(trans, bbox->search_args, NULL,
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen 0, NULL);
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen /* save the result and keep it updated */
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen result_flags = MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->search_result =
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen mailbox_search_result_save(search_ctx, result_flags);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* add the found UIDs to uidmap. virtual_uid gets assigned later. */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen memset(&uidmap, 0, sizeof(uidmap));
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen array_clear(&bbox->uids);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (mailbox_search_next(search_ctx, &mail)) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap.real_uid = mail->uid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_append(&bbox->uids, &uidmap, 1);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen ret = mailbox_search_deinit(&search_ctx);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen (void)mailbox_transaction_commit(&trans);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return ret;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_backend_uidmap_bsearch_cmp(const uint32_t *uidp,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct virtual_backend_uidmap *uidmap)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return *uidp < uidmap->real_uid ? -1 :
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen (*uidp > uidmap->real_uid ? 1 : 0);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenvirtual_sync_mailbox_box_remove(struct virtual_sync_context *ctx,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *bbox,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const ARRAY_TYPE(seq_range) *removed_uids)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen const struct seq_range *uids;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_uidmap *uidmap;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen unsigned int i, src, dest, uid_count, rec_count;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uint32_t uid, vseq;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
812883e7758a86f615b9508ef10df1339123da83Timo Sirainen uids = array_get(removed_uids, &uid_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (uid_count == 0)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* everything in removed_uids should exist in bbox->uids */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(rec_count >= uid_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* find the first uidmap record to be removed */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (!array_bsearch_insert_pos(&bbox->uids, &uids[0].seq1,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen virtual_backend_uidmap_bsearch_cmp, &src))
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_unreached();
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* remove the unwanted messages */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen dest = src;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < uid_count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uid = uids[i].seq1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (uidmap[src].real_uid != uid) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap[dest++] = uidmap[src++];
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(src < rec_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen for (; uid <= uids[i].seq2; uid++, src++) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen i_assert(src < rec_count);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen i_assert(uidmap[src].real_uid == uid);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (mail_index_lookup_seq(ctx->sync_view,
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap[src].virtual_uid,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen &vseq))
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen mail_index_expunge(ctx->trans, vseq);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_delete(&bbox->uids, dest, src - dest);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen}
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenstatic void
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainenvirtual_sync_mailbox_box_add(struct virtual_sync_context *ctx,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen struct virtual_backend_box *bbox,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const ARRAY_TYPE(seq_range) *added_uids_arr)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen{
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const struct seq_range *added_uids;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_uidmap *uidmap;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_add_record rec;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen unsigned int i, src, dest, uid_count, add_count, rec_count;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen uint32_t add_uid;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen added_uids = array_get(added_uids_arr, &uid_count);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (uid_count == 0)
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen return;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen add_count = seq_range_count(added_uids_arr);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* none of added_uids should exist in bbox->uids. find the position
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen of the first inserted index. */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (rec_count == 0 ||
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen added_uids[0].seq1 > uidmap[rec_count-1].real_uid) {
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* fast path: usually messages are appended */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen dest = rec_count;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen } else if (array_bsearch_insert_pos(&bbox->uids, &added_uids[0].seq1,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen virtual_backend_uidmap_bsearch_cmp,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen &dest))
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen i_unreached();
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen /* make space for all added UIDs. */
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen if (rec_count == dest)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_idx_clear(&bbox->uids, dest + add_count-1);
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen else {
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen array_copy(&bbox->uids.arr, dest + add_count,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen &bbox->uids.arr, dest, rec_count - dest);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen uidmap = array_get_modifiable(&bbox->uids, &rec_count);
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen src = dest + add_count;
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen /* add/move the UIDs to their correct positions */
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen memset(&rec, 0, sizeof(rec));
1b04762685272a53643ac2179939537a44c7c044Timo Sirainen rec.rec.mailbox_id = bbox->mailbox_id;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (i = 0; i < uid_count; i++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen add_uid = added_uids[i].seq1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen while (src < rec_count && uidmap[src].real_uid < add_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap[dest++] = uidmap[src++];
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen for (; add_uid <= added_uids[i].seq2; add_uid++, dest++) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen i_assert(dest < rec_count);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap[dest].real_uid = add_uid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen uidmap[dest].virtual_uid = 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen if (ctx->mbox->uids_mapped) {
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen rec.rec.real_uid = add_uid;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_append(&ctx->all_adds, &rec, 1);
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen }
fa2a11210f20fb8998ed656f75e163191c8047e6Timo Sirainen }
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen }
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen}
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic int virtual_backend_uidmap_cmp(const struct virtual_backend_uidmap *u1,
367e28a16854ee9f7247b2518f36f5e9163fcc10Timo Sirainen const struct virtual_backend_uidmap *u2)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (u1->real_uid < u2->real_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return -1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen if (u1->real_uid > u2->real_uid)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 1;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen return 0;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_bbox_uids_sort(struct virtual_backend_box *bbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen /* the uidmap must be sorted by real_uids */
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen array_sort(&bbox->uids, virtual_backend_uidmap_cmp);
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen bbox->uids_nonsorted = FALSE;
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen}
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainenstatic void virtual_sync_backend_boxes_sort_uids(struct virtual_mailbox *mbox)
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen{
8d3278a82b964217d95c340ec6f82037cdc59d19Timo Sirainen struct virtual_backend_box *const *bboxes;
unsigned int i, count;
bboxes = array_get(&mbox->backend_boxes, &count);
for (i = 0; i < count; i++) {
if (bboxes[i]->uids_nonsorted)
virtual_sync_bbox_uids_sort(bboxes[i]);
}
}
static void
virtual_sync_backend_add_vmsgs_results(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox,
uint32_t real_uid,
struct mail_search_result *result,
const uint32_t vseq)
{
struct virtual_backend_uidmap uidmap;
uint32_t vuid, seq;
mail_index_lookup_uid(ctx->sync_view, vseq, &vuid);
memset(&uidmap, 0, sizeof(uidmap));
uidmap.real_uid = real_uid;
uidmap.virtual_uid = vuid;
array_append(&bbox->uids, &uidmap, 1);
if (result == NULL)
;
else if (mail_index_lookup_seq(bbox->box->view, real_uid, &seq))
seq_range_array_add(&result->uids, real_uid);
else
seq_range_array_add(&result->removed_uids, real_uid);
}
static void
virtual_sync_backend_handle_old_vmsgs(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox,
struct mail_search_result *result)
{
const struct virtual_mail_index_record *vrec;
const struct virtual_sync_mail *sync_mail, *sync_mails;
const void *data;
uint32_t i, vseq, messages;
/* find the messages that currently exist in virtual index and add them
to the backend mailbox's list of uids. */
array_clear(&bbox->uids);
if (array_is_created(&ctx->all_mails)) {
i_assert(ctx->all_mails_prev_mailbox_id < bbox->mailbox_id);
sync_mails = array_get(&ctx->all_mails, &messages);
for (i = ctx->all_mails_idx; i < messages; i++) {
sync_mail = &sync_mails[i];
if (sync_mail->vrec.mailbox_id != bbox->mailbox_id) {
if (sync_mail->vrec.mailbox_id < bbox->mailbox_id) {
/* stale mailbox_id, ignore */
continue;
}
/* Should be in mailbox_id order,
so skip to next box */
break;
}
virtual_sync_backend_add_vmsgs_results(ctx, bbox,
sync_mail->vrec.real_uid, result, sync_mail->vseq);
}
ctx->all_mails_idx = i;
ctx->all_mails_prev_mailbox_id = bbox->mailbox_id;
} else {
/* there should be only a single backend mailbox, but in the
existing index there may be stale mailbox_ids that we'll
just skip over. */
messages = mail_index_view_get_messages_count(ctx->sync_view);
for (vseq = 1; vseq <= messages; vseq++) {
mail_index_lookup_ext(ctx->sync_view, vseq,
ctx->mbox->virtual_ext_id, &data, NULL);
vrec = data;
if (vrec->mailbox_id == bbox->mailbox_id) {
virtual_sync_backend_add_vmsgs_results(ctx,
bbox, vrec->real_uid, result, vseq);
}
}
}
virtual_sync_bbox_uids_sort(bbox);
}
static int virtual_sync_backend_box_continue(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox)
{
const enum mailbox_search_result_flags result_flags =
MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC;
struct mail_index_view *view = bbox->box->view;
struct mail_search_result *result;
ARRAY_TYPE(seq_range) expunged_uids = ARRAY_INIT, removed_uids;
ARRAY_TYPE(seq_range) added_uids, flag_update_uids;
uint64_t modseq, old_highest_modseq;
uint32_t seq, uid, old_msg_count;
/* initialize the search result from all the existing messages in
virtual index. */
if (!bbox->search_args_initialized) {
mail_search_args_init(bbox->search_args, bbox->box, FALSE, NULL);
bbox->search_args_initialized = TRUE;
}
result = mailbox_search_result_alloc(bbox->box, bbox->search_args,
result_flags);
mailbox_search_result_initial_done(result);
i_assert(array_count(&result->removed_uids) == 0);
virtual_sync_backend_handle_old_vmsgs(ctx, bbox, result);
if (array_count(&result->removed_uids) > 0) {
/* these are all expunged messages. treat them separately from
"no longer matching messages" (=removed_uids) */
t_array_init(&expunged_uids, array_count(&result->removed_uids));
array_append_array(&expunged_uids, &result->removed_uids);
array_clear(&result->removed_uids);
}
/* get list of changed old messages (messages already once seen by
virtual index), based on modseq changes. (we'll assume all modseq
changes are due to flag changes, which may not be true in future) */
if (bbox->sync_next_uid <= 1 ||
!mail_index_lookup_seq_range(view, 1, bbox->sync_next_uid-1,
&seq, &old_msg_count))
old_msg_count = 0;
old_highest_modseq = mail_index_modseq_get_highest(view);
t_array_init(&flag_update_uids, I_MIN(128, old_msg_count));
if (bbox->sync_highest_modseq < old_highest_modseq) {
for (seq = 1; seq <= old_msg_count; seq++) {
modseq = mail_index_modseq_lookup(view, seq);
if (modseq > bbox->sync_highest_modseq) {
mail_index_lookup_uid(view, seq, &uid);
seq_range_array_add(&flag_update_uids, uid);
}
}
}
/* update the search result based on the flag changes and
new messages */
if (index_search_result_update_flags(result, &flag_update_uids) < 0 ||
index_search_result_update_appends(result, old_msg_count) < 0) {
mailbox_search_result_free(&result);
return -1;
}
t_array_init(&removed_uids, 128);
t_array_init(&added_uids, 128);
mailbox_search_result_sync(result, &removed_uids, &added_uids);
if (array_is_created(&expunged_uids)) {
seq_range_array_remove_seq_range(&removed_uids, &expunged_uids);
virtual_sync_mailbox_box_remove(ctx, bbox, &expunged_uids);
}
if (ctx->expunge_removed)
virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids);
else {
/* delayed remove */
seq_range_array_merge(&bbox->sync_pending_removes,
&removed_uids);
}
virtual_sync_mailbox_box_add(ctx, bbox, &added_uids);
bbox->search_result = result;
return 0;
}
static void virtual_sync_drop_existing(struct virtual_backend_box *bbox,
ARRAY_TYPE(seq_range) *added_uids)
{
ARRAY_TYPE(seq_range) drop_uids;
const struct virtual_backend_uidmap *uidmap;
struct seq_range_iter iter;
unsigned int i, n = 0, count;
uint32_t add_uid;
seq_range_array_iter_init(&iter, added_uids);
if (!seq_range_array_iter_nth(&iter, n++, &add_uid))
return;
(void)array_bsearch_insert_pos(&bbox->uids, &add_uid,
virtual_backend_uidmap_bsearch_cmp, &i);
uidmap = array_get_modifiable(&bbox->uids, &count);
if (i == count)
return;
t_array_init(&drop_uids, array_count(added_uids));
for (; i < count; ) {
if (uidmap[i].real_uid < add_uid) {
i++;
continue;
}
if (uidmap[i].real_uid == add_uid) {
seq_range_array_add(&drop_uids, add_uid);
i++;
}
if (!seq_range_array_iter_nth(&iter, n++, &add_uid))
break;
}
seq_range_array_remove_seq_range(added_uids, &drop_uids);
}
static void virtual_sync_drop_nonexistent(struct virtual_backend_box *bbox,
ARRAY_TYPE(seq_range) *removed_uids)
{
ARRAY_TYPE(seq_range) drop_uids;
const struct virtual_backend_uidmap *uidmap;
struct seq_range_iter iter;
unsigned int i, n = 0, count;
uint32_t remove_uid;
bool iter_done = FALSE;
seq_range_array_iter_init(&iter, removed_uids);
if (!seq_range_array_iter_nth(&iter, n++, &remove_uid))
return;
(void)array_bsearch_insert_pos(&bbox->uids, &remove_uid,
virtual_backend_uidmap_bsearch_cmp, &i);
t_array_init(&drop_uids, array_count(removed_uids)); iter_done = FALSE;
uidmap = array_get_modifiable(&bbox->uids, &count);
for (; i < count; ) {
if (uidmap[i].real_uid < remove_uid) {
i++;
continue;
}
if (uidmap[i].real_uid != remove_uid)
seq_range_array_add(&drop_uids, remove_uid);
else
i++;
if (!seq_range_array_iter_nth(&iter, n++, &remove_uid)) {
iter_done = TRUE;
break;
}
}
if (!iter_done) {
do {
seq_range_array_add(&drop_uids, remove_uid);
} while (seq_range_array_iter_nth(&iter, n++, &remove_uid));
}
seq_range_array_remove_seq_range(removed_uids, &drop_uids);
}
static void virtual_sync_mailbox_box_update(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox)
{
ARRAY_TYPE(seq_range) removed_uids, added_uids, temp_uids;
unsigned int count1, count2;
t_array_init(&removed_uids, 128);
t_array_init(&added_uids, 128);
mailbox_search_result_sync(bbox->search_result,
&removed_uids, &added_uids);
if (array_is_created(&bbox->sync_outside_expunges)) {
seq_range_array_remove_seq_range(&bbox->sync_outside_expunges,
&added_uids);
seq_range_array_merge(&removed_uids,
&bbox->sync_outside_expunges);
array_clear(&bbox->sync_outside_expunges);
}
virtual_sync_drop_existing(bbox, &added_uids);
virtual_sync_drop_nonexistent(bbox, &removed_uids);
/* if any of the pending removes came back, we don't want to expunge
them anymore. also since they already exist, remove them from
added_uids. */
count1 = array_count(&bbox->sync_pending_removes);
count2 = array_count(&added_uids);
if (count1 > 0 && count2 > 0) {
t_array_init(&temp_uids, count1);
array_append_array(&temp_uids, &bbox->sync_pending_removes);
if (seq_range_array_remove_seq_range(
&bbox->sync_pending_removes, &added_uids) > 0) {
seq_range_array_remove_seq_range(&added_uids,
&temp_uids);
}
}
if (!ctx->expunge_removed) {
/* delay removing messages that don't match the search
criteria, but don't delay removing expunged messages */
if (array_count(&ctx->sync_expunges) > 0) {
seq_range_array_remove_seq_range(&bbox->sync_pending_removes,
&ctx->sync_expunges);
seq_range_array_remove_seq_range(&removed_uids,
&ctx->sync_expunges);
virtual_sync_mailbox_box_remove(ctx, bbox,
&ctx->sync_expunges);
}
seq_range_array_merge(&bbox->sync_pending_removes,
&removed_uids);
} else if (array_count(&bbox->sync_pending_removes) > 0) {
/* remove all current and old */
seq_range_array_merge(&bbox->sync_pending_removes,
&removed_uids);
virtual_sync_mailbox_box_remove(ctx, bbox,
&bbox->sync_pending_removes);
array_clear(&bbox->sync_pending_removes);
} else {
virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids);
}
virtual_sync_mailbox_box_add(ctx, bbox, &added_uids);
}
static bool virtual_sync_find_seqs(struct virtual_backend_box *bbox,
const struct mailbox_sync_rec *sync_rec,
unsigned int *idx1_r,
unsigned int *idx2_r)
{
const struct virtual_backend_uidmap *uidmap;
unsigned int idx, count;
uint32_t uid1, uid2;
mail_index_lookup_uid(bbox->box->view, sync_rec->seq1, &uid1);
mail_index_lookup_uid(bbox->box->view, sync_rec->seq2, &uid2);
(void)array_bsearch_insert_pos(&bbox->uids, &uid1,
virtual_backend_uidmap_bsearch_cmp,
&idx);
uidmap = array_get_modifiable(&bbox->uids, &count);
if (idx == count || uidmap[idx].real_uid > uid2)
return FALSE;
*idx1_r = idx;
while (idx < count && uidmap[idx].real_uid <= uid2) idx++;
*idx2_r = idx - 1;
return TRUE;
}
static void virtual_sync_expunge_add(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox,
const struct mailbox_sync_rec *sync_rec)
{
struct virtual_backend_uidmap *uidmap;
uint32_t uid1, uid2;
unsigned int i, idx1, count;
mail_index_lookup_uid(bbox->box->view, sync_rec->seq1, &uid1);
mail_index_lookup_uid(bbox->box->view, sync_rec->seq2, &uid2);
/* remember only the expunges for messages that
already exist for this mailbox */
(void)array_bsearch_insert_pos(&bbox->uids, &uid1,
virtual_backend_uidmap_bsearch_cmp,
&idx1);
uidmap = array_get_modifiable(&bbox->uids, &count);
for (i = idx1; i < count; i++) {
if (uidmap[i].real_uid > uid2)
break;
seq_range_array_add(&ctx->sync_expunges, uidmap[i].real_uid);
}
}
static int virtual_sync_backend_box_sync(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox,
enum mailbox_sync_flags sync_flags)
{
struct mailbox_sync_context *sync_ctx;
const struct virtual_backend_uidmap *uidmap;
struct mailbox_sync_rec sync_rec;
struct mailbox_sync_status sync_status;
unsigned int idx1, idx2;
uint32_t vseq, vuid;
sync_ctx = mailbox_sync_init(bbox->box, sync_flags);
virtual_backend_box_sync_mail_set(bbox);
while (mailbox_sync_next(sync_ctx, &sync_rec)) {
switch (sync_rec.type) {
case MAILBOX_SYNC_TYPE_EXPUNGE:
if (ctx->expunge_removed) {
/* no need to keep track of expunges */
break;
}
virtual_sync_expunge_add(ctx, bbox, &sync_rec);
break;
case MAILBOX_SYNC_TYPE_FLAGS:
if (!virtual_sync_find_seqs(bbox, &sync_rec,
&idx1, &idx2))
break;
uidmap = array_idx(&bbox->uids, 0);
for (; idx1 <= idx2; idx1++) {
vuid = uidmap[idx1].virtual_uid;
if (!mail_index_lookup_seq(ctx->sync_view,
vuid, &vseq)) {
/* expunged by another session,
but we haven't yet updated
bbox->uids. */
continue;
}
virtual_sync_external_flags(ctx, bbox, vseq,
uidmap[idx1].real_uid);
}
break;
case MAILBOX_SYNC_TYPE_MODSEQ:
break;
}
}
return mailbox_sync_deinit(&sync_ctx, &sync_status);
}
static void virtual_sync_backend_ext_header(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox)
{
const unsigned int uidval_pos =
offsetof(struct virtual_mail_index_mailbox_record,
uid_validity);
struct mailbox_status status;
struct virtual_mail_index_mailbox_record mailbox;
unsigned int mailbox_offset;
uint64_t wanted_ondisk_highest_modseq;
mailbox_get_open_status(bbox->box, STATUS_UIDVALIDITY |
STATUS_HIGHESTMODSEQ, &status);
wanted_ondisk_highest_modseq =
array_count(&bbox->sync_pending_removes) > 0 ? 0 :
status.highest_modseq;
if (bbox->sync_uid_validity == status.uidvalidity &&
bbox->sync_next_uid == status.uidnext &&
bbox->sync_highest_modseq == status.highest_modseq &&
bbox->ondisk_highest_modseq == wanted_ondisk_highest_modseq)
return;
/* mailbox changed - update extension header */
bbox->sync_uid_validity = status.uidvalidity;
bbox->sync_highest_modseq = status.highest_modseq;
bbox->ondisk_highest_modseq = wanted_ondisk_highest_modseq;
bbox->sync_next_uid = status.uidnext;
if (ctx->ext_header_rewrite) {
/* we'll rewrite the entire header later */
return;
}
memset(&mailbox, 0, sizeof(mailbox));
mailbox.uid_validity = bbox->sync_uid_validity;
mailbox.highest_modseq = bbox->ondisk_highest_modseq;
mailbox.next_uid = bbox->sync_next_uid;
mailbox_offset = sizeof(struct virtual_mail_index_header) +
bbox->sync_mailbox_idx * sizeof(mailbox);
mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
mailbox_offset + uidval_pos,
CONST_PTR_OFFSET(&mailbox, uidval_pos),
sizeof(mailbox) - uidval_pos);
ctx->ext_header_changed = TRUE;
}
static void virtual_sync_backend_box_deleted(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox)
{
ARRAY_TYPE(seq_range) removed_uids;
const struct virtual_backend_uidmap *uidmap;
/* delay its full removal until the next time we open the virtual
mailbox. for now just treat it as if it was empty. */
t_array_init(&removed_uids, 128);
array_foreach(&bbox->uids, uidmap)
seq_range_array_add(&removed_uids, uidmap->real_uid);
virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids);
bbox->deleted = TRUE;
}
static int virtual_sync_backend_box(struct virtual_sync_context *ctx,
struct virtual_backend_box *bbox)
{
enum mailbox_sync_flags sync_flags;
struct mailbox_status status;
int ret;
if (bbox->deleted)
return 0;
/* if we already did some changes to index, commit them before
syncing starts. */
virtual_backend_box_sync_mail_unset(bbox);
sync_flags = ctx->flags & (MAILBOX_SYNC_FLAG_FULL_READ |
MAILBOX_SYNC_FLAG_FULL_WRITE |
MAILBOX_SYNC_FLAG_FAST);
if (bbox->search_result == NULL) {
/* a) first sync in this process.
b) we had auto-closed this backend mailbox.
first try to quickly check if the mailbox has changed.
if we can do that check from mailbox list index, we don't
even need to open the mailbox. */
i_assert(array_count(&bbox->sync_pending_removes) == 0);
if (bbox->box->opened || bbox->open_failed) {
/* a) index already opened, refresh it
b) delayed error handling for mailbox_open()
that failed in virtual_notify_changes() */
if (!bbox->box->opened) {
if (virtual_backend_box_open(ctx->mbox, bbox) < 0)
return -1;
}
if (mailbox_sync(bbox->box, sync_flags) < 0)
return -1;
bbox->open_failed = FALSE;
}
if (mailbox_get_status(bbox->box, STATUS_UIDVALIDITY |
STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ,
&status) < 0) {
if (mailbox_get_last_mail_error(bbox->box) != MAIL_ERROR_NOTFOUND)
return -1;
/* mailbox was deleted */
virtual_sync_backend_box_deleted(ctx, bbox);
return 0;
}
if (status.uidvalidity == bbox->sync_uid_validity &&
status.uidnext == bbox->sync_next_uid &&
status.highest_modseq == bbox->sync_highest_modseq) {
/* mailbox hasn't changed since we last opened it,
skip it for now.
we'll still need to create the bbox->uids mapping
using the current index. */
if (array_count(&bbox->uids) == 0)
virtual_sync_backend_handle_old_vmsgs(ctx, bbox, NULL);
return 0;
}
if (!bbox->box->opened) {
/* first time we're opening the index */
if (virtual_backend_box_open(ctx->mbox, bbox) < 0)
return -1;
if (mailbox_sync(bbox->box, sync_flags) < 0)
return -1;
}
virtual_backend_box_sync_mail_set(bbox);
if (status.uidvalidity != bbox->sync_uid_validity) {
/* UID validity changed since last sync (or this is
the first sync), do a full search */
i_assert(ctx->expunge_removed);
ret = virtual_sync_backend_box_init(bbox);
} else {
/* build the initial search using the saved modseq. */
ret = virtual_sync_backend_box_continue(ctx, bbox);
}
i_assert(bbox->search_result != NULL || ret < 0);
} else {
/* sync using the existing search result */
i_assert(bbox->box->opened);
i_array_init(&ctx->sync_expunges, 32);
ret = virtual_sync_backend_box_sync(ctx, bbox, sync_flags);
if (ret == 0) T_BEGIN {
virtual_sync_mailbox_box_update(ctx, bbox);
} T_END;
array_free(&ctx->sync_expunges);
}
virtual_sync_backend_ext_header(ctx, bbox);
return ret;
}
static void virtual_sync_backend_map_uids(struct virtual_sync_context *ctx)
{
uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
struct virtual_sync_mail *vmails;
struct virtual_backend_box *bbox;
struct virtual_backend_uidmap *uidmap = NULL;
struct virtual_add_record add_rec;
const struct virtual_mail_index_record *vrec;
const void *data;
uint32_t i, vseq, vuid, messages;
unsigned int j = 0, uidmap_count = 0;
messages = mail_index_view_get_messages_count(ctx->sync_view);
if (messages == 0)
return;
/* sort the messages in current view by their backend mailbox and
real UID */
vmails = 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, NULL);
vrec = data;
vmails[vseq-1].vseq = vseq;
vmails[vseq-1].vrec = *vrec;
}
qsort(vmails, messages, sizeof(*vmails), virtual_sync_mail_uid_cmp);
/* create real mailbox uid -> virtual uid mapping and expunge
messages no longer matching the search rule */
memset(&add_rec, 0, sizeof(add_rec));
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) {
/* add the rest of the newly seen messages */
for (; j < uidmap_count; j++) {
add_rec.rec.real_uid = uidmap[j].real_uid;
array_append(&ctx->all_adds, &add_rec, 1);
}
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;
}
uidmap = array_get_modifiable(&bbox->uids,
&uidmap_count);
j = 0;
add_rec.rec.mailbox_id = bbox->mailbox_id;
bbox->sync_seen = TRUE;
}
mail_index_lookup_uid(ctx->sync_view, vseq, &vuid);
/* if virtual record doesn't exist in uidmap, it's expunged */
for (; j < uidmap_count; j++) {
if (uidmap[j].real_uid >= vrec->real_uid)
break;
/* newly seen message */
add_rec.rec.real_uid = uidmap[j].real_uid;
array_append(&ctx->all_adds, &add_rec, 1);
}
if (j == uidmap_count || uidmap[j].real_uid != vrec->real_uid)
mail_index_expunge(ctx->trans, vseq);
else {
/* exists - update uidmap and flags */
uidmap[j++].virtual_uid = vuid;
if (bbox->search_result == NULL) {
/* mailbox is completely unchanged since last
sync - no need to sync flags */
} else {
virtual_sync_external_flags(ctx, bbox, vseq,
vrec->real_uid);
}
}
}
i_free(vmails);
/* finish adding messages to the last mailbox */
for (; j < uidmap_count; j++) {
add_rec.rec.real_uid = uidmap[j].real_uid;
array_append(&ctx->all_adds, &add_rec, 1);
}
}
static void virtual_sync_new_backend_boxes(struct virtual_sync_context *ctx)
{
struct virtual_backend_box *const *bboxes;
struct virtual_add_record add_rec;
struct virtual_backend_uidmap *uidmap;
unsigned int i, j, count, uidmap_count;
/* if there are any mailboxes we didn't yet sync, add new messages in
them */
memset(&add_rec, 0, sizeof(add_rec));
bboxes = array_get(&ctx->mbox->backend_boxes, &count);
for (i = 0; i < count; i++) {
if (bboxes[i]->sync_seen)
continue;
add_rec.rec.mailbox_id = bboxes[i]->mailbox_id;
uidmap = array_get_modifiable(&bboxes[i]->uids, &uidmap_count);
for (j = 0; j < uidmap_count; j++) {
add_rec.rec.real_uid = uidmap[j].real_uid;
array_append(&ctx->all_adds, &add_rec, 1);
}
}
}
static int virtual_add_record_cmp(const struct virtual_add_record *add1,
const struct virtual_add_record *add2)
{
if (add1->received_date < add2->received_date)
return -1;
if (add1->received_date > add2->received_date)
return 1;
/* if they're in same mailbox, we can order them correctly by the UID.
if they're in different mailboxes, ordering by UID doesn't really
help but it doesn't really harm either. */
if (add1->rec.real_uid < add2->rec.real_uid)
return -1;
if (add1->rec.real_uid > add2->rec.real_uid)
return 1;
/* two messages in different mailboxes have the same received date
and UID. */
return 0;
}
static int virtual_sync_backend_sort_new(struct virtual_sync_context *ctx)
{
struct virtual_backend_box *bbox;
struct virtual_add_record *adds;
const struct virtual_mail_index_record *vrec;
unsigned int i, count;
/* get all messages' received dates */
adds = array_get_modifiable(&ctx->all_adds, &count);
for (bbox = NULL, i = 0; i < count; i++) {
vrec = &adds[i].rec;
if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
bbox = virtual_backend_box_lookup(ctx->mbox,
vrec->mailbox_id);
if (!bbox->box->opened &&
virtual_backend_box_open(ctx->mbox, bbox) < 0)
return -1;
virtual_backend_box_sync_mail_set(bbox);
}
if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) {
/* we may have reopened the mailbox, which could have
caused the mail to be expunged already. */
adds[i].received_date = 0;
} else if (mail_get_received_date(bbox->sync_mail,
&adds[i].received_date) < 0) {
if (!bbox->sync_mail->expunged)
return -1;
/* expunged already, just add it somewhere */
adds[i].received_date = 0;
}
}
array_sort(&ctx->all_adds, virtual_add_record_cmp);
return 0;
}
static int virtual_sync_backend_add_new(struct virtual_sync_context *ctx)
{
uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
struct virtual_add_record *adds;
struct virtual_backend_box *bbox;
struct virtual_backend_uidmap *uidmap;
const struct mail_index_header *hdr;
const struct virtual_mail_index_record *vrec;
unsigned int i, count, idx;
ARRAY_TYPE(seq_range) saved_uids;
uint32_t vseq, first_uid;
hdr = mail_index_get_header(ctx->sync_view);
adds = array_get_modifiable(&ctx->all_adds, &count);
if (count == 0) {
ctx->mbox->sync_virtual_next_uid = hdr->next_uid;
return 0;
}
if (adds[0].rec.mailbox_id == adds[count-1].rec.mailbox_id) {
/* all messages are from a single mailbox. add them in
the same order. */
} else {
/* sort new messages by received date to get the add order */
if (virtual_sync_backend_sort_new(ctx) < 0)
return -1;
}
for (bbox = NULL, i = 0; i < count; i++) {
vrec = &adds[i].rec;
if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
bbox = virtual_backend_box_lookup(ctx->mbox,
vrec->mailbox_id);
if (!bbox->box->opened &&
virtual_backend_box_open(ctx->mbox, bbox) < 0)
return -1;
virtual_backend_box_sync_mail_set(bbox);
}
mail_index_append(ctx->trans, 0, &vseq);
mail_index_update_ext(ctx->trans, vseq, virtual_ext_id,
vrec, NULL);
virtual_sync_external_flags(ctx, bbox, vseq, vrec->real_uid);
}
/* assign UIDs to new messages */
first_uid = hdr->next_uid;
t_array_init(&saved_uids, 1);
mail_index_append_finish_uids(ctx->trans, first_uid, &saved_uids);
i_assert(seq_range_count(&saved_uids) == count);
/* update virtual UIDs in uidmap */
for (bbox = NULL, i = 0; i < count; i++) {
vrec = &adds[i].rec;
if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
bbox = virtual_backend_box_lookup(ctx->mbox,
vrec->mailbox_id);
}
if (!array_bsearch_insert_pos(&bbox->uids, &vrec->real_uid,
virtual_backend_uidmap_bsearch_cmp,
&idx))
i_unreached();
uidmap = array_idx_modifiable(&bbox->uids, idx);
i_assert(uidmap->virtual_uid == 0);
uidmap->virtual_uid = first_uid + i;
}
ctx->mbox->sync_virtual_next_uid = first_uid + i;
return 0;
}
static int
virtual_sync_apply_existing_appends(struct virtual_sync_context *ctx)
{
uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
struct virtual_backend_box *bbox = NULL;
const struct mail_index_header *hdr;
const struct virtual_mail_index_record *vrec;
struct virtual_backend_uidmap uidmap;
const void *data;
uint32_t seq, seq2;
if (!ctx->mbox->uids_mapped)
return 0;
hdr = mail_index_get_header(ctx->sync_view);
if (ctx->mbox->sync_virtual_next_uid >= hdr->next_uid)
return 0;
/* another process added messages to virtual index. get backend boxes'
uid lists up-to-date by adding the new messages there. */
if (!mail_index_lookup_seq_range(ctx->sync_view,
ctx->mbox->sync_virtual_next_uid,
(uint32_t)-1, &seq, &seq2))
return 0;
memset(&uidmap, 0, sizeof(uidmap));
for (; seq <= seq2; seq++) {
mail_index_lookup_ext(ctx->sync_view, seq, virtual_ext_id,
&data, NULL);
vrec = data;
uidmap.real_uid = vrec->real_uid;
mail_index_lookup_uid(ctx->sync_view, seq, &uidmap.virtual_uid);
if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
bbox = virtual_backend_box_lookup(ctx->mbox,
vrec->mailbox_id);
if (bbox == NULL) {
mail_storage_set_critical(
ctx->mbox->box.storage,
"Mailbox ID %u unexpectedly lost",
vrec->mailbox_id);
return -1;
}
}
array_append(&bbox->uids, &uidmap, 1);
bbox->uids_nonsorted = TRUE;
}
virtual_sync_backend_boxes_sort_uids(ctx->mbox);
return 0;
}
static void
virtual_sync_apply_existing_expunges(struct virtual_mailbox *mbox,
struct mailbox_sync_context *sync_ctx)
{
struct index_mailbox_sync_context *isync_ctx =
(struct index_mailbox_sync_context *)sync_ctx;
struct virtual_backend_box *bbox = NULL;
struct seq_range_iter iter;
const struct virtual_mail_index_record *vrec;
const void *data;
unsigned int n = 0;
uint32_t seq;
if (isync_ctx->expunges == NULL)
return;
seq_range_array_iter_init(&iter, isync_ctx->expunges);
while (seq_range_array_iter_nth(&iter, n++, &seq)) {
mail_index_lookup_ext(mbox->box.view, seq,
mbox->virtual_ext_id, &data, NULL);
vrec = data;
if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
bbox = virtual_backend_box_lookup(mbox,
vrec->mailbox_id);
if (!array_is_created(&bbox->sync_outside_expunges))
i_array_init(&bbox->sync_outside_expunges, 32);
}
seq_range_array_add(&bbox->sync_outside_expunges,
vrec->real_uid);
}
}
static int virtual_sync_mail_mailbox_cmp(const struct virtual_sync_mail *m1,
const struct virtual_sync_mail *m2)
{
if (m1->vrec.mailbox_id < m2->vrec.mailbox_id)
return -1;
if (m1->vrec.mailbox_id > m2->vrec.mailbox_id)
return 1;
return 0;
}
static void virtual_sync_bboxes_get_mails(struct virtual_sync_context *ctx)
{
uint32_t messages, vseq;
const void *mail_data;
const struct virtual_mail_index_record *vrec;
struct virtual_sync_mail *sync_mail;
messages = mail_index_view_get_messages_count(ctx->sync_view);
i_array_init(&ctx->all_mails, messages);
for (vseq = 1; vseq <= messages; vseq++) {
mail_index_lookup_ext(ctx->sync_view, vseq,
ctx->mbox->virtual_ext_id, &mail_data, NULL);
vrec = mail_data;
sync_mail = array_append_space(&ctx->all_mails);
sync_mail->vseq = vseq;
sync_mail->vrec = *vrec;
}
array_sort(&ctx->all_mails, virtual_sync_mail_mailbox_cmp);
}
static int virtual_sync_backend_boxes(struct virtual_sync_context *ctx)
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
int ret;
if (virtual_sync_apply_existing_appends(ctx) < 0)
return -1;
i_array_init(&ctx->all_adds, 128);
bboxes = array_get(&ctx->mbox->backend_boxes, &count);
/* we have different optimizations depending on whether the virtual
mailbox consists of multiple backend boxes or just one */
if (count > 1)
virtual_sync_bboxes_get_mails(ctx);
for (i = 0; i < count; i++) {
if (virtual_sync_backend_box(ctx, bboxes[i]) < 0) {
/* backend failed, copy the error */
virtual_box_copy_error(&ctx->mbox->box,
bboxes[i]->box);
return -1;
}
}
if (!ctx->mbox->uids_mapped) {
/* initial sync: assign virtual UIDs to existing messages and
sync all flags */
ctx->mbox->uids_mapped = TRUE;
virtual_sync_backend_map_uids(ctx);
virtual_sync_new_backend_boxes(ctx);
}
ret = virtual_sync_backend_add_new(ctx);
#ifdef DEBUG
for (i = 0; i < count; i++) {
const struct virtual_backend_uidmap *uidmap;
array_foreach(&bboxes[i]->uids, uidmap)
i_assert(uidmap->virtual_uid > 0);
}
#endif
array_free(&ctx->all_adds);
if (array_is_created(&ctx->all_mails))
array_free(&ctx->all_mails);
return ret;
}
static void virtual_sync_backend_boxes_finish(struct virtual_sync_context *ctx)
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
bboxes = array_get(&ctx->mbox->backend_boxes, &count);
for (i = 0; i < count; i++)
virtual_backend_box_sync_mail_unset(bboxes[i]);
}
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) {
mailbox_set_index_error(&ctx->mbox->box);
ret = -1;
}
} else {
if (ctx->index_broken) {
/* make sure we don't complain about the same errors
over and over again. */
if (mail_index_unlink(ctx->index) < 0) {
i_error("virtual index %s: Failed to unlink() "
"broken indexes: %m",
mailbox_get_path(&ctx->mbox->box));
}
}
mail_index_sync_rollback(&ctx->index_sync_ctx);
}
i_free(ctx);
return ret;
}
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;
bool broken;
int ret;
ctx = i_new(struct virtual_sync_context, 1);
ctx->mbox = mbox;
ctx->flags = flags;
ctx->index = mbox->box.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->box.flags & MAILBOX_FLAG_DROP_RECENT) != 0)
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)
mailbox_set_index_error(&mbox->box);
i_free(ctx);
return ret;
}
ret = virtual_mailbox_ext_header_read(mbox, ctx->sync_view, &broken);
if (ret < 0)
return virtual_sync_finish(ctx, FALSE);
if (ret == 0)
ctx->ext_header_rewrite = TRUE;
if (broken)
ctx->index_broken = TRUE;
/* apply changes from virtual index to backend mailboxes */
if (virtual_sync_index_changes(ctx) < 0)
return virtual_sync_finish(ctx, FALSE);
/* update list of UIDs in backend mailboxes */
if (virtual_sync_backend_boxes(ctx) < 0)
return virtual_sync_finish(ctx, FALSE);
virtual_sync_index_finish(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;
struct mailbox_sync_context *sync_ctx;
int ret = 0;
if (!box->opened) {
if (mailbox_open(box) < 0)
ret = -1;
}
if (index_mailbox_want_full_sync(&mbox->box, flags) && ret == 0)
ret = virtual_sync(mbox, flags);
sync_ctx = index_mailbox_sync_init(box, flags, ret < 0);
virtual_sync_apply_existing_expunges(mbox, sync_ctx);
return sync_ctx;
}