/* Copyright (c) 2008-2018 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 "mailbox-recent-flags.h"
#include "index-sync-private.h"
#include "index-search-result.h"
#include "virtual-storage.h"
struct virtual_add_record {
};
struct virtual_sync_mail {
};
struct virtual_sync_context {
const char *const *kw_all;
/* messages expunged within this sync */
/* all messages in this sync, sorted by mailbox_id
(but unsorted inside it for now, since it doesn't matter) */
};
struct virtual_backend_box *bbox);
{
}
struct virtual_backend_box *bbox,
{
const char *const *kw_names;
/* we may have reopened the mailbox, which could have
caused the mail to be expunged already. */
return;
}
/* copy flags */
/* we don't need to keep recent flags here */
/* copy keywords */
}
{
return -1;
return 1;
return -1;
return 1;
/* broken */
return 0;
}
static void
{
}
}
struct virtual_backend_box *const *b2)
{
return -1;
return 1;
return 0;
}
static int
struct virtual_backend_box **bbox_r)
{
return 0;
/* another process just added a new mailbox.
we can't handle this currently. */
"Backend mailbox '%s' added by another session. "
"Reopen the virtual mailbox.", name));
return -1;
}
struct mail_index_view *view,
bool *broken_r)
{
const void *ext_data;
if (mbox->sync_initialized &&
/* fully refreshed */
return 1;
}
ext_name_offset = 0;
ext_mailbox_count = 0;
ret = 0;
} else {
const void *guid_data;
if (guid_size >= GUID_128_SIZE)
ext_name_offset = sizeof(*ext_hdr) +
if (ext_name_offset >= ext_size ||
i_error("virtual index %s: Broken mailbox_count header",
box_path);
ext_mailbox_count = 0;
ret = 0;
} else {
}
}
/* update mailbox backends */
prev_mailbox_id = 0;
for (i = 0; i < ext_mailbox_count; i++) {
i_error("virtual index %s: Broken mailbox id",
box_path);
break;
}
i_error("virtual index %s: Broken mailbox name_len",
box_path);
break;
}
i_error("virtual index %s: Broken mailbox list",
box_path);
break;
}
T_BEGIN {
const unsigned char *nameptr;
const char *name;
ret = -1;
} T_END;
if (ret < 0)
return -1;
/* mailbox no longer exists. */
ret = 0;
} else {
}
}
if (i < ext_mailbox_count) {
ret = 0;
}
/* do not mark it initialized if it's broken */
/* assign new mailbox IDs if any are missing */
for (i = 0; i < count; i++) {
if (bboxes[i]->mailbox_id == 0) {
ret = 0;
}
}
/* sort the backend mailboxes by mailbox_id. */
if (ret == 0)
return ret;
}
{
const void *ext_data;
mailbox_pos = sizeof(ext_hdr);
for (i = 0; i < count; i++) {
i_assert(i == 0 ||
mailbox_pos += sizeof(mailbox);
/* repair the value */
}
}
sizeof(struct virtual_mail_index_record),
sizeof(uint32_t));
}
}
{
if (!ctx->ext_header_changed)
return;
/* we changed something - update the change counter in header */
}
const struct mail_index_sync_rec *sync_rec)
{
const void *data;
break;
}
/* already expunged, nothing to do. */
return 0;
}
continue;
return -1;
}
} else {
}
/* message is already expunged from backend mailbox. */
continue;
}
break;
if (flags != 0) {
MODIFY_ADD, flags);
}
if (flags != 0) {
}
break;
kw_names);
break;
}
}
return 0;
}
{
return -1;
}
return 0;
}
{
if (hdr->uid_validity != 0)
else
/* mark the newly seen messages as recent */
}
/* entire mailbox list needs to be rewritten */
} else {
/* update only changed parts in the header */
}
}
{
int ret;
if (!bbox->search_args_initialized) {
}
0, NULL);
/* 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 int
const struct virtual_backend_uidmap *uidmap)
{
}
static void
struct virtual_backend_box *bbox,
{
if (uid_count == 0)
return;
/* everything in removed_uids should exist in bbox->uids */
/* find the first uidmap record to be removed */
i_unreached();
/* remove the unwanted messages */
for (i = 0; i < uid_count; i++) {
}
/* has not been assigned yet */
continue;
}
&vseq))
}
}
}
static void
struct virtual_backend_box *bbox,
{
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 */
&dest))
i_unreached();
/* make space for all added UIDs. */
else {
}
for (i = 0; i < uid_count; i++) {
}
}
}
}
static void
struct virtual_backend_box *bbox,
{
i = 0;
vuid->virtual_uid == 0 ||
/* the entry has been already removed either by
us or some other session. doesn't matter,
we don't need to update the flags.
it might also have not yet been assigned a uid
so we don't want to update the flags then either.
*/
continue;
}
}
}
const struct virtual_backend_uidmap *u2)
{
return -1;
return 1;
return 0;
}
{
/* the uidmap must be sorted by real_uids */
}
{
unsigned int i, count;
for (i = 0; i < count; i++) {
if (bboxes[i]->uids_nonsorted)
}
}
static void
struct virtual_backend_box *bbox,
struct mail_search_result *result,
{
;
else
}
static void
struct virtual_backend_box *bbox,
struct mail_search_result *result)
{
const void *data;
/* find the messages that currently exist in virtual index and add them
to the backend mailbox's list of uids. */
sync_mail = &sync_mails[i];
/* stale mailbox_id, ignore */
continue;
}
/* Should be in mailbox_id order,
so skip to next box */
break;
}
}
ctx->all_mails_idx = i;
} 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. */
}
}
}
}
struct virtual_backend_box *bbox)
{
/* initialize the search result from all the existing messages in
virtual index. */
if (!bbox->search_args_initialized) {
}
/* these are all expunged messages. treat them separately from
"no longer matching messages" (=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) */
&seq, &old_msg_count))
old_msg_count = 0;
}
}
}
/* update the search result based on the flag changes and
new messages */
return -1;
}
if (array_is_created(&expunged_uids)) {
}
if (ctx->expunge_removed)
else {
/* delayed remove */
&removed_uids);
}
return 0;
}
{
unsigned int i, n = 0, count;
return;
if (i == count)
return;
for (; i < count; ) {
i++;
continue;
}
i++;
}
break;
}
}
{
unsigned int i, n = 0, count;
return;
for (; i < count; ) {
i++;
continue;
}
else
i++;
break;
}
}
if (!iter_done) {
do {
}
}
struct virtual_backend_box *bbox)
{
&removed_uids, &added_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);
&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)
{
&idx);
return FALSE;
return TRUE;
}
struct virtual_backend_box *bbox,
const struct mailbox_sync_rec *sync_rec)
{
/* remember only the expunges for messages that
already exist for this mailbox */
&idx1);
break;
}
}
struct virtual_backend_box *bbox,
enum mailbox_sync_flags sync_flags)
{
if (ctx->expunge_removed) {
/* no need to keep track of expunges */
break;
}
break;
case MAILBOX_SYNC_TYPE_FLAGS:
break;
if (vuid == 0) {
/* has not been even assigned yet */
continue;
}
/* expunged by another session,
but we haven't yet updated
bbox->uids. */
continue;
}
}
break;
case MAILBOX_SYNC_TYPE_MODSEQ:
break;
}
}
return -1;
/* mailbox was deleted */
return 0;
}
return 0;
}
struct virtual_backend_box *bbox)
{
const unsigned int uidval_pos =
unsigned int mailbox_offset;
return;
/* mailbox changed - update extension header */
/* 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)
{
/* delay its full removal until the next time we open the virtual
mailbox. for now just treat it as if it was empty. */
}
static int
struct virtual_backend_box *bbox,
enum mailbox_sync_flags sync_flags)
{
int ret = 0;
if (ret == 0)
if (ret < 0) {
return -1;
/* mailbox was deleted */
return 0;
}
return 1;
}
struct virtual_backend_box *bbox)
{
int ret;
return 0;
/* if we already did some changes to index, commit them before
syncing starts. */
/* 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. */
/* a) index already opened, refresh it
b) delayed error handling for mailbox_open()
that failed in virtual_notify_changes() */
return ret;
}
&status) < 0) {
return -1;
/* mailbox was deleted */
return 0;
}
/* 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. */
return 0;
}
/* first time we're opening the index */
return ret;
}
/* 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;
}
{
const void *data;
unsigned int j = 0, uidmap_count = 0;
if (messages == 0)
return;
/* 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 */
/* mailbox is completely unchanged since last
sync - no need to sync flags */
} else {
}
}
}
/* 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++) {
}
}
}
const struct virtual_add_record *add2)
{
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;
}
{
unsigned int i, count;
/* get all messages' received dates */
vrec->mailbox_id);
return -1;
}
/* we may have reopened the mailbox, which could have
caused the mail to be expunged already. */
adds[i].received_date = 0;
&adds[i].received_date) < 0) {
return -1;
/* expunged already, just add it somewhere */
adds[i].received_date = 0;
}
}
return 0;
}
{
if (count == 0) {
return 0;
}
/* 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;
}
vrec->mailbox_id);
return -1;
}
}
/* assign UIDs to new messages */
/* update virtual UIDs in uidmap */
vrec->mailbox_id);
}
&idx))
i_unreached();
}
return 0;
}
static int
{
const void *data;
return 0;
return 0;
/* another process added messages to virtual index. get backend boxes'
uid lists up-to-date by adding the new messages there. */
return 0;
vrec->mailbox_id);
continue;
}
}
}
return 0;
}
static void
struct mailbox_sync_context *sync_ctx)
{
(struct index_mailbox_sync_context *)sync_ctx;
const void *data;
unsigned int n = 0;
return;
vrec->mailbox_id);
}
}
}
const struct virtual_sync_mail *m2)
{
return -1;
return 1;
return 0;
}
{
const void *mail_data;
}
}
{
unsigned int i, count;
int ret;
if (virtual_sync_apply_existing_appends(ctx) < 0)
return -1;
/* we have different optimizations depending on whether the virtual
mailbox consists of multiple backend boxes or just one */
if (count > 1)
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 */
}
#ifdef DEBUG
for (i = 0; i < count; i++) {
}
#endif
return ret;
}
{
unsigned int i, count;
for (i = 0; i < count; i++)
}
{
if (success) {
ret = -1;
}
} else {
if (ctx->index_broken) {
/* make sure we don't complain about the same errors
over and over again. */
i_error("virtual index %s: Failed to unlink() "
"broken indexes: %m",
}
}
}
return ret;
}
enum mailbox_sync_flags flags)
{
bool broken;
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 (ret < 0)
if (broken)
/* apply changes from virtual index to backend mailboxes */
if (virtual_sync_index_changes(ctx) < 0)
/* update list of UIDs in backend mailboxes */
if (virtual_sync_backend_boxes(ctx) < 0)
}
struct mailbox_sync_context *
{
int ret = 0;
if (mailbox_open(box) < 0)
ret = -1;
}
return sync_ctx;
}