virtual-sync.c revision fb7dd075cf883e5e7defbc0c8fb8326e30bdccde
/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "array.h"
#include "bsearch-insert-pos.h"
#include "ioloop.h"
#include "str.h"
#include "mail-index-modseq.h"
#include "mail-search-build.h"
#include "mailbox-search-result-private.h"
#include "index-search-result.h"
#include "virtual-storage.h"
#include <stdlib.h>
struct virtual_add_record {
struct virtual_mail_index_record rec;
};
struct virtual_sync_mail {
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;
/* messages expunged within this sync */
enum mailbox_sync_flags flags;
unsigned int ext_header_changed:1;
unsigned int ext_header_rewrite:1;
unsigned int expunge_removed:1;
};
{
}
struct virtual_backend_box *bbox,
{
enum mail_flags flags;
const char *const *kw_names;
struct mail_keywords *keywords;
i_panic("UID %u lost unexpectedly from %s",
}
/* copy flags */
/* copy keywords */
}
{
return -1;
return 1;
return -1;
return 1;
/* broken */
return 0;
}
static void
{
struct mailbox_transaction_context *trans;
}
}
static void
{
struct mailbox_transaction_context *trans;
(void)mailbox_transaction_commit(&trans);
}
}
{
return -1;
return 1;
return 0;
}
{
const struct virtual_mail_index_header *ext_hdr;
const struct mail_index_header *hdr;
const struct virtual_mail_index_mailbox_record *mailboxes;
const void *ext_data;
bool ret;
/* fully refreshed */
return TRUE;
}
ext_name_offset = 0;
ext_mailbox_count = 0;
} else {
ext_name_offset = sizeof(*ext_hdr) +
if (ext_name_offset >= ext_size ||
i_error("virtual %s: Broken mailbox_count header",
ext_mailbox_count = 0;
} else {
}
}
/* update mailbox backends */
prev_mailbox_id = 0;
for (i = 0; i < ext_mailbox_count; i++) {
i_error("virtual %s: Broken mailbox id",
break;
}
i_error("virtual %s: Broken mailbox name_len",
break;
}
i_error("virtual %s: Broken mailbox list",
break;
}
T_BEGIN {
const unsigned char *nameptr;
const char *name;
} T_END;
/* mailbox no longer exists */
} else {
bbox->sync_mailbox_idx = i;
}
}
} else {
}
/* assign new mailbox IDs if any are missing */
for (i = 0; i < count; i++) {
if (bboxes[i]->mailbox_id == 0) {
}
}
/* sort the backend mailboxes by mailbox_id. */
return ret;
}
{
struct virtual_mail_index_header ext_hdr;
struct virtual_backend_box **bboxes;
const void *ext_data;
mailbox_pos = sizeof(ext_hdr);
for (i = 0; i < count; i++) {
i_assert(i == 0 ||
bboxes[i]->sync_mailbox_idx = i;
mailbox_pos += sizeof(mailbox);
}
sizeof(struct virtual_mail_index_record),
sizeof(uint32_t));
}
}
{
struct virtual_mail_index_header ext_hdr;
if (!ctx->ext_header_changed)
return;
/* we changed something - update the change counter in header */
}
const struct mail_index_sync_rec *sync_rec)
{
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];
bool expunged;
/* don't care */
return;
break;
}
/* already expunged, nothing to do. */
return;
}
continue;
i_panic("UID lost unexpectedly");
break;
if (flags != 0) {
MODIFY_ADD, flags);
}
if (flags != 0) {
}
break;
kw_names);
break;
kw_names);
keywords);
break;
i_unreached();
}
}
}
{
struct mail_index_sync_rec sync_rec;
}
{
const struct mail_index_header *hdr;
if (hdr->uid_validity != 0)
else
/* mark the newly seen messages as recent */
}
if (ctx->ext_header_rewrite) {
/* entire mailbox list needs to be rewritten */
} else {
/* update only changed parts in the header */
}
}
{
struct mailbox_transaction_context *trans;
struct mail_search_context *search_ctx;
struct virtual_backend_uidmap uidmap;
int ret;
/* save the result and keep it updated */
/* add the found UIDs to uidmap. virtual_uid gets assigned later. */
}
(void)mailbox_transaction_commit(&trans);
return ret;
}
{
}
static void
struct virtual_backend_box *bbox,
{
struct virtual_backend_uidmap *uidmap;
if (uid_count == 0)
return;
/* everything in removed_uids should exist in bbox->uids */
/* find the first uidmap record to be removed */
sizeof(*uidmap),
i_unreached();
/* remove the unwanted messages */
for (i = 0; i < uid_count; i++) {
}
&vseq))
i_unreached();
}
}
}
static void
struct virtual_backend_box *bbox,
{
const struct seq_range *added_uids;
struct virtual_backend_uidmap *uidmap;
struct virtual_add_record rec;
if (uid_count == 0)
return;
/* none of added_uids should exist in bbox->uids. find the position
of the first inserted index. */
if (rec_count == 0 ||
/* fast path: usually messages are appended */
sizeof(*uidmap),
&dest))
i_unreached();
/* make space for all added UIDs. */
else {
}
for (i = 0; i < uid_count; i++) {
}
}
}
}
}
{
return -1;
return 1;
return 0;
}
static void
struct virtual_backend_box *bbox,
struct mail_search_result *result)
{
const struct virtual_mail_index_record *vrec;
const void *data;
unsigned int uid_count;
bool expunged;
/* add the currently existing UIDs to uidmap. remember the messages
that were already expunged */
&seq)) {
} else {
}
}
}
/* the uidmap must be sorted by real_uids */
}
struct virtual_backend_box *bbox)
{
const enum mailbox_search_result_flags result_flags =
struct mail_search_result *result;
/* initialize the search result from all the existing messages in
virtual index. */
/* get list of changed old messages, based on modseq changes.
(we'll assume all modseq changes are due to flag changes, which
may not be true in future.) */
&seq, &old_msg_count))
old_msg_count = 0;
}
/* update the search result based on the flag changes and
new messages */
return -1;
}
return 0;
}
struct virtual_backend_box *bbox)
{
&removed_uids, &added_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. */
&temp_uids);
}
}
if (!ctx->expunge_removed) {
/* delay removing messages that don't match the search
criteria, but don't delay removing expunged messages */
&ctx->sync_expunges);
&ctx->sync_expunges);
}
&removed_uids);
/* remove all current and old */
&removed_uids);
} else {
}
}
const struct mailbox_sync_rec *sync_rec,
unsigned int *idx1_r,
unsigned int *idx2_r)
{
const struct virtual_backend_uidmap *uidmap;
return FALSE;
return TRUE;
}
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;
if (ctx->expunge_removed) {
/* no need to keep track of expunges */
break;
}
break;
case MAILBOX_SYNC_TYPE_FLAGS:
break;
i_unreached();
}
break;
case MAILBOX_SYNC_TYPE_MODSEQ:
break;
}
}
}
struct virtual_backend_box *bbox)
{
const unsigned int uidval_pos =
struct mailbox_status status;
unsigned int mailbox_offset;
return;
/* mailbox changed - update extension header */
if (ctx->ext_header_rewrite) {
/* we'll rewrite the entire header later */
return;
}
mailbox_offset = sizeof(struct virtual_mail_index_header) +
sizeof(mailbox) - uidval_pos);
}
struct virtual_backend_box *bbox)
{
enum mailbox_sync_flags sync_flags;
struct mailbox_status status;
int ret;
/* if we already did some changes to index, commit them before
syncing starts. */
/* we use modseqs for speeding up initial search result build.
make sure the backend has them enabled. */
/* first sync in this process */
&status) < 0)
return -1;
/* UID validity changed since last sync (or this is
the first sync), do a full search */
} else {
/* build the initial search using the saved modseq. */
}
} else {
/* sync using the existing search result */
} T_END;
}
return ret;
}
{
struct virtual_sync_mail *vmails;
struct virtual_add_record add_rec;
const struct virtual_mail_index_record *vrec;
const void *data;
bool expunged;
unsigned int j = 0, uidmap_count = 0;
/* sort the messages in current view by their backend mailbox and
real UID */
}
/* create real mailbox uid -> virtual uid mapping and expunge
messages no longer matching the search rule */
for (i = 0; i < messages; i++) {
/* add the rest of the newly seen messages */
for (; j < uidmap_count; j++) {
}
vrec->mailbox_id);
/* the entire mailbox is lost */
continue;
}
&uidmap_count);
j = 0;
}
/* if virtual record doesn't exist in uidmap, it's expunged */
for (; j < uidmap_count; j++) {
break;
/* newly seen message */
}
else {
/* exists - update uidmap and flags */
}
}
/* finish adding messages to the last mailbox */
for (; j < uidmap_count; j++) {
}
/* if there are any mailboxes we didn't yet sync, add new messages in
them */
for (i = 0; i < count; i++) {
continue;
for (j = 0; j < uidmap_count; j++) {
}
}
}
{
return -1;
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. */
return -1;
return 1;
/* two messages in different mailboxes have the same received date
and UID. */
return 0;
}
{
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 */
vrec->mailbox_id);
}
i_unreached();
&adds[i].received_date) < 0) {
/* probably expunged already, just add it somewhere */
adds[i].received_date = 0;
}
}
}
{
struct virtual_add_record *adds;
struct virtual_backend_box *bbox;
struct virtual_backend_uidmap *uidmap;
const struct virtual_mail_index_record *vrec;
if (count == 0)
return;
/* 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 */
}
vrec->mailbox_id);
}
}
/* assign UIDs to new messages */
/* update virtual UIDs in uidmap */
vrec->mailbox_id);
}
sizeof(*uidmap),
&idx))
i_unreached();
}
}
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
for (i = 0; i < count; i++) {
/* backend failed, copy the error */
return -1;
}
}
/* initial sync: assign virtual UIDs to existing messages and
sync all flags */
}
return 0;
}
{
struct virtual_backend_box *const *bboxes;
unsigned int i, count;
for (i = 0; i < count; i++)
}
{
if (success) {
ret = -1;
}
} else {
}
return ret;
}
enum mailbox_sync_flags flags)
{
struct virtual_sync_context *ctx;
int ret;
/* Removed messages are expunged when
a) EXPUNGE is used
b) Mailbox is being opened (FIX_INCONSISTENT is set) */
if (ret <= 0) {
if (ret < 0)
return ret;
}
if (!virtual_sync_ext_header_read(ctx))
/* apply changes from virtual index to backend mailboxes */
/* update list of UIDs in backend mailboxes */
if (virtual_sync_backend_boxes(ctx) < 0)
}
struct mailbox_sync_context *
{
int ret = 0;
}