#include "lib.h"
#include "array.h"
+#include "istream.h"
#include "hash.h"
#include "dsync-worker.h"
#include "dsync-brain-private.h"
struct dsync_brain_msg_save_context {
struct dsync_brain_msg_iter *iter;
-
- mailbox_guid_t mailbox;
const struct dsync_message *msg;
};
static void
-dsync_brain_msg_sync_retry_copies(struct dsync_brain_mailbox_sync *sync);
-
-static bool
-dsync_brain_msg_sync_is_save_done(struct dsync_brain_mailbox_sync *sync)
-{
- return sync->src_msg_iter->copy_results_left == 0 &&
- sync->dest_msg_iter->copy_results_left == 0 &&
- sync->src_msg_iter->save_results_left == 0 &&
- sync->dest_msg_iter->save_results_left == 0;
-}
+dsync_brain_msg_sync_add_new_msgs(struct dsync_brain_msg_iter *iter);
static void msg_get_callback(enum dsync_msg_get_result result,
- struct dsync_msg_static_data *data,
+ const struct dsync_msg_static_data *data,
void *context)
{
struct dsync_brain_msg_save_context *ctx = context;
+ struct istream *input;
switch (result) {
case DSYNC_MSG_GET_RESULT_SUCCESS:
- dsync_worker_select_mailbox(ctx->iter->worker, &ctx->mailbox);
+ input = data->input;
dsync_worker_msg_save(ctx->iter->worker, ctx->msg, data);
+ i_stream_unref(&input);
break;
case DSYNC_MSG_GET_RESULT_EXPUNGED:
/* mail got expunged during sync. just skip this. */
break;
case DSYNC_MSG_GET_RESULT_FAILED:
+ i_error("msg-get failed: uid=%u guid=%s",
+ ctx->msg->uid, ctx->msg->guid);
dsync_brain_fail(ctx->iter->sync->brain);
break;
}
- ctx->iter->save_results_left--;
+ if (--ctx->iter->save_results_left == 0 && !ctx->iter->adding_msgs)
+ dsync_brain_msg_sync_add_new_msgs(ctx->iter);
}
static void dsync_brain_copy_callback(bool success, void *context)
const struct dsync_brain_new_msg *msg;
struct dsync_brain_guid_instance *inst;
- ctx->iter->copy_results_left--;
if (!success) {
/* mark the guid instance invalid and try again later */
msg = array_idx(&ctx->iter->new_msgs, ctx->msg_idx);
array_append(&ctx->iter->copy_retry_indexes, &ctx->msg_idx, 1);
}
- if (dsync_brain_msg_sync_is_save_done(ctx->iter->sync)) {
- ctx->iter->sync->brain->state++;
- dsync_brain_sync(ctx->iter->sync->brain);
- }
+ if (--ctx->iter->copy_results_left == 0 && !ctx->iter->adding_msgs)
+ dsync_brain_msg_sync_add_new_msgs(ctx->iter);
}
static int
dsync_brain_msg_sync_add_new_msg(struct dsync_brain_msg_iter *dest_iter,
const mailbox_guid_t *src_mailbox,
unsigned int msg_idx,
- const struct dsync_message *msg)
+ const struct dsync_brain_new_msg *msg)
{
struct dsync_brain_msg_save_context *save_ctx;
struct dsync_brain_msg_copy_context *copy_ctx;
const struct dsync_brain_guid_instance *inst;
const struct dsync_brain_mailbox *inst_box;
- inst = hash_table_lookup(dest_iter->guid_hash, msg->guid);
+ inst = hash_table_lookup(dest_iter->guid_hash, msg->msg->guid);
if (inst != NULL) {
/* we can save this by copying an existing message */
- dsync_worker_select_mailbox(dest_iter->worker, src_mailbox);
inst_box = array_idx(&dest_iter->sync->mailboxes,
inst->mailbox_idx);
copy_ctx->iter = dest_iter;
copy_ctx->msg_idx = msg_idx;
- dsync_worker_msg_copy(dest_iter->worker, &inst_box->box.guid,
- inst->uid, msg, dsync_brain_copy_callback,
- copy_ctx);
+ dsync_worker_msg_copy(dest_iter->worker,
+ &inst_box->box.mailbox_guid,
+ inst->uid, msg->msg,
+ dsync_brain_copy_callback, copy_ctx);
dest_iter->copy_results_left++;
} else {
src_iter = dest_iter == dest_iter->sync->dest_msg_iter ?
save_ctx = p_new(src_iter->sync->pool,
struct dsync_brain_msg_save_context, 1);
save_ctx->iter = dest_iter;
- save_ctx->mailbox = *src_mailbox;
- save_ctx->msg = dsync_message_dup(src_iter->sync->pool, msg);
+ save_ctx->msg = dsync_message_dup(src_iter->sync->pool,
+ msg->msg);
- dsync_worker_select_mailbox(src_iter->worker, src_mailbox);
- dsync_worker_msg_get(src_iter->worker, msg->uid,
- msg_get_callback, save_ctx);
+ dest_iter->adding_msgs = TRUE;
dest_iter->save_results_left++;
+ dsync_worker_msg_get(src_iter->worker, src_mailbox,
+ msg->orig_uid, msg_get_callback, save_ctx);
+ dest_iter->adding_msgs = FALSE;
+ if (dsync_worker_output_flush(src_iter->worker) < 0)
+ return -1;
}
return dsync_worker_is_output_full(dest_iter->worker) ? 0 : 1;
}
-static void
-dsync_brain_msg_iter_add_new_msgs(struct dsync_brain_msg_iter *dest_iter)
+static bool
+dsync_brain_mailbox_add_new_msgs(struct dsync_brain_msg_iter *iter,
+ const mailbox_guid_t *mailbox_guid)
{
- const struct dsync_brain_mailbox *mailboxes, *mailbox;
const struct dsync_brain_new_msg *msgs;
- unsigned int i, mailbox_count, msg_count;
-
- mailboxes = array_get(&dest_iter->sync->mailboxes, &mailbox_count);
- msgs = array_get(&dest_iter->new_msgs, &msg_count);
- for (i = dest_iter->next_new_msg; i < msg_count; i++) {
- mailbox = &mailboxes[msgs[i].mailbox_idx];
- if (dsync_brain_msg_sync_add_new_msg(dest_iter,
- &mailbox->box.guid, i,
- msgs[i].msg) <= 0) {
+ unsigned int i, msg_count;
+ bool ret = TRUE;
+
+ msgs = array_get(&iter->new_msgs, &msg_count);
+ for (i = iter->next_new_msg; i < msg_count; i++) {
+ if (msgs[i].mailbox_idx != iter->mailbox_idx) {
+ ret = FALSE;
+ break;
+ }
+ if (dsync_brain_msg_sync_add_new_msg(iter, mailbox_guid,
+ i, &msgs[i]) <= 0) {
/* failed / continue later */
- dest_iter->next_new_msg = i + 1;
+ i++;
break;
}
}
- dest_iter->msgs_sent = TRUE;
+ iter->next_new_msg = i;
+ if (i == msg_count)
+ ret = FALSE;
+
+ /* flush copy commands */
+ if (dsync_worker_output_flush(iter->worker) > 0 && ret) {
+ /* we have more space again, continue */
+ return dsync_brain_mailbox_add_new_msgs(iter, mailbox_guid);
+ } else {
+ return ret;
+ }
}
static void
-dsync_brain_msg_sync_add_new_msgs(struct dsync_brain_msg_iter *iter)
+dsync_brain_mailbox_save_conflicts(struct dsync_brain_msg_iter *iter)
{
- dsync_brain_msg_iter_add_new_msgs(iter);
-
- if (iter->sync->dest_msg_iter->msgs_sent &&
- iter->sync->src_msg_iter->msgs_sent &&
- dsync_brain_msg_sync_is_save_done(iter->sync))
- dsync_brain_msg_sync_retry_copies(iter->sync);
-}
-
-static void dsync_worker_new_msg_output(void *context)
-{
- struct dsync_brain_msg_iter *iter = context;
-
- dsync_brain_msg_sync_add_new_msgs(iter);
-}
+ const struct dsync_brain_uid_conflict *conflicts;
+ unsigned int i, count;
-static void
-dsync_brain_msg_iter_sync_new_msgs(struct dsync_brain_msg_iter *iter)
-{
- dsync_worker_set_input_callback(iter->worker, NULL, iter);
- dsync_worker_set_output_callback(iter->worker,
- dsync_worker_new_msg_output, iter);
- dsync_brain_msg_sync_add_new_msgs(iter);
-}
+ conflicts = array_get(&iter->uid_conflicts, &count);
+ for (i = iter->next_conflict; i < count; i++) {
+ if (conflicts[i].mailbox_idx != iter->mailbox_idx)
+ break;
-void dsync_brain_msg_sync_new_msgs(struct dsync_brain_mailbox_sync *sync)
-{
- dsync_brain_msg_iter_sync_new_msgs(sync->src_msg_iter);
- dsync_brain_msg_iter_sync_new_msgs(sync->dest_msg_iter);
+ dsync_worker_msg_update_uid(iter->worker, conflicts[i].old_uid,
+ conflicts[i].new_uid);
+ }
+ iter->next_conflict = i;
}
static void
-dsync_brain_msg_iter_sync_retry_copies(struct dsync_brain_msg_iter *iter)
+dsync_brain_mailbox_retry_copies(struct dsync_brain_msg_iter *iter,
+ const mailbox_guid_t *mailbox_guid)
{
const uint32_t *indexes;
- const struct dsync_brain_mailbox *mailboxes, *mailbox;
const struct dsync_brain_new_msg *msgs;
- unsigned int i, msg_idx, idx_count, msg_count, mailbox_count;
+ unsigned int i, msg_idx, idx_count, msg_count;
struct dsync_brain_guid_instance *inst;
const char *guid_str;
void *orig_key, *orig_value;
/* try saving again. there probably weren't many of them, so don't
worry about filling output buffer. */
- mailboxes = array_get(&iter->sync->mailboxes, &mailbox_count);
for (i = 0; i < idx_count; i++) {
msg_idx = indexes[i];
- mailbox = &mailboxes[msgs[msg_idx].mailbox_idx];
- (void)dsync_brain_msg_sync_add_new_msg(iter, &mailbox->box.guid,
- msg_idx,
- msgs[msg_idx].msg);
+ (void)dsync_brain_msg_sync_add_new_msg(iter, mailbox_guid,
+ msg_idx, &msgs[msg_idx]);
}
/* if we copied anything, we'll again have to wait for the results */
array_clear(&iter->copy_retry_indexes);
- dsync_worker_set_output_callback(iter->worker, NULL, NULL);
}
static void
-dsync_brain_msg_sync_retry_copies(struct dsync_brain_mailbox_sync *sync)
+dsync_brain_msg_sync_add_new_msgs(struct dsync_brain_msg_iter *iter)
{
- dsync_brain_msg_iter_sync_retry_copies(sync->dest_msg_iter);
- dsync_brain_msg_iter_sync_retry_copies(sync->src_msg_iter);
-
- if (dsync_brain_msg_sync_is_save_done(sync)) {
- dsync_worker_set_input_callback(sync->src_worker, NULL, NULL);
- dsync_worker_set_input_callback(sync->dest_worker, NULL, NULL);
- sync->brain->state++;
- dsync_brain_sync(sync->brain);
- } else {
- /* temporarily move back the state. once copies have returned
- success/failures, we'll get back to this function and see
- if we need to retry again */
- sync->brain->state--;
+ const struct dsync_brain_mailbox *mailbox;
+ const mailbox_guid_t *mailbox_guid;
+
+ while (iter->mailbox_idx < array_count(&iter->sync->mailboxes)) {
+ mailbox = array_idx(&iter->sync->mailboxes, iter->mailbox_idx);
+ mailbox_guid = &mailbox->box.mailbox_guid;
+ dsync_worker_select_mailbox(iter->worker, mailbox_guid);
+
+ if (dsync_brain_mailbox_add_new_msgs(iter, mailbox_guid)) {
+ /* continue later */
+ return;
+ }
+
+ /* all messages saved for this mailbox. continue with saving
+ its conflicts and waiting for copies to finish. */
+ dsync_brain_mailbox_save_conflicts(iter);
+
+ while (iter->copy_results_left == 0 &&
+ array_count(&iter->copy_retry_indexes) > 0)
+ dsync_brain_mailbox_retry_copies(iter, mailbox_guid);
+
+ if (iter->copy_results_left > 0) {
+ /* wait for copies to finish */
+ return;
+ }
+
+ /* done with this mailbox, try the next one */
+ iter->mailbox_idx++;
}
+ iter->msgs_sent = TRUE;
+
+ /* done with all mailboxes from this iter */
+ dsync_worker_set_input_callback(iter->worker, NULL, NULL);
+
+ if (iter->sync->src_msg_iter->msgs_sent &&
+ iter->sync->dest_msg_iter->msgs_sent &&
+ iter->sync->src_msg_iter->save_results_left == 0 &&
+ iter->sync->dest_msg_iter->save_results_left == 0 &&
+ dsync_worker_output_flush(iter->sync->dest_worker) > 0 &&
+ dsync_worker_output_flush(iter->sync->src_worker) > 0) {
+ iter->sync->brain->state++;
+ dsync_brain_sync(iter->sync->brain);
+ }
+}
+
+static void dsync_worker_new_msg_output(void *context)
+{
+ struct dsync_brain_msg_iter *iter = context;
+
+ dsync_brain_msg_sync_add_new_msgs(iter);
+}
+
+static int dsync_brain_new_msg_cmp(const struct dsync_brain_new_msg *m1,
+ const struct dsync_brain_new_msg *m2)
+{
+ if (m1->mailbox_idx < m2->mailbox_idx)
+ return -1;
+ if (m1->mailbox_idx < m2->mailbox_idx)
+ return 1;
+
+ if (m1->msg->uid < m2->msg->uid)
+ return -1;
+ if (m1->msg->uid > m2->msg->uid)
+ return 1;
+ return 0;
+}
+
+static int
+dsync_brain_uid_conflict_cmp(const struct dsync_brain_uid_conflict *c1,
+ const struct dsync_brain_uid_conflict *c2)
+{
+ if (c1->mailbox_idx < c2->mailbox_idx)
+ return -1;
+ if (c1->mailbox_idx < c2->mailbox_idx)
+ return 1;
+
+ if (c1->new_uid < c2->new_uid)
+ return -1;
+ if (c1->new_uid > c2->new_uid)
+ return 1;
+ return 0;
+}
+
+static void
+dsync_brain_msg_iter_sync_new_msgs(struct dsync_brain_msg_iter *iter)
+{
+ iter->mailbox_idx = 0;
+
+ array_sort(&iter->new_msgs, dsync_brain_new_msg_cmp);
+ array_sort(&iter->uid_conflicts, dsync_brain_uid_conflict_cmp);
+
+ dsync_worker_set_input_callback(iter->worker, NULL, iter);
+ dsync_worker_set_output_callback(iter->worker,
+ dsync_worker_new_msg_output, iter);
+ dsync_brain_msg_sync_add_new_msgs(iter);
+}
+
+void dsync_brain_msg_sync_new_msgs(struct dsync_brain_mailbox_sync *sync)
+{
+ dsync_brain_msg_iter_sync_new_msgs(sync->src_msg_iter);
+ dsync_brain_msg_iter_sync_new_msgs(sync->dest_msg_iter);
}
static void
conflicts = array_get(&iter->uid_conflicts, &count);
for (i = 0; i < count; i++) {
mailbox = &mailboxes[conflicts[i].mailbox_idx];
- dsync_worker_select_mailbox(iter->worker, &mailbox->box.guid);
+ dsync_worker_select_mailbox(iter->worker,
+ &mailbox->box.mailbox_guid);
dsync_worker_msg_update_uid(iter->worker, conflicts[i].old_uid,
conflicts[i].new_uid);
}
new_msg = array_append_space(&iter->new_msgs);
new_msg->mailbox_idx = mailbox_idx;
+ new_msg->orig_uid = msg->uid;
new_msg->msg = dsync_message_dup(iter->sync->pool, msg);
}
new_msg = array_append_space(&save_iter->new_msgs);
new_msg->mailbox_idx = save_iter->mailbox_idx;
+ new_msg->orig_uid = msg->uid;
new_msg->msg = dsync_message_dup(save_iter->sync->pool, msg);
new_msg->msg->uid = new_uid;
}
static bool dsync_brain_msg_sync_mailbox_end(struct dsync_brain_msg_iter *iter1,
struct dsync_brain_msg_iter *iter2)
{
- const struct dsync_brain_mailbox *brain_box;
int ret;
- brain_box = array_idx(&iter1->sync->mailboxes,
- iter1->sync->wanted_mailbox_idx);
while ((ret = dsync_brain_msg_iter_next(iter1)) > 0) {
dsync_brain_msg_sync_save(iter2, iter1->mailbox_idx,
&iter1->msg);
dsync_brain_msg_sync_new_msgs(sync);
}
-static void dsync_brain_msg_sync_more(struct dsync_brain_mailbox_sync *sync)
+void dsync_brain_msg_sync_more(struct dsync_brain_mailbox_sync *sync)
{
const struct dsync_brain_mailbox *mailboxes;
unsigned int count, mailbox_idx;
/* sync the next mailbox */
sync->uid_conflict = FALSE;
mailbox_idx = ++sync->wanted_mailbox_idx;
- if (mailbox_idx == count) {
+ if (mailbox_idx >= count) {
dsync_brain_msg_sync_finish(sync);
return;
}
dsync_worker_select_mailbox(sync->src_worker,
- &mailboxes[mailbox_idx].box.guid);
+ &mailboxes[mailbox_idx].box.mailbox_guid);
dsync_worker_select_mailbox(sync->dest_worker,
- &mailboxes[mailbox_idx].box.guid);
+ &mailboxes[mailbox_idx].box.mailbox_guid);
}
}
dsync_worker_msg_callback, sync);
dsync_worker_set_output_callback(worker,
dsync_worker_msg_callback, sync);
- dsync_worker_select_mailbox(worker, &mailboxes[0]);
+ if (mailbox_count > 0)
+ dsync_worker_select_mailbox(worker, &mailboxes[0]);
return iter;
}
t_array_init(guids, array_count(mailboxes));
brain_boxes = array_get(mailboxes, &count);
for (i = 0; i < count; i++)
- array_append(guids, &brain_boxes[i].box.guid, 1);
+ array_append(guids, &brain_boxes[i].box.mailbox_guid, 1);
}
struct dsync_brain_mailbox_sync *
const ARRAY_TYPE(dsync_brain_mailbox) *mailboxes)
{
struct dsync_brain_mailbox_sync *sync;
- ARRAY_TYPE(mailbox_guid) guids;
- unsigned int count;
pool_t pool;
pool = pool_alloconly_create("dsync brain mailbox sync", 1024*256);
p_array_init(&sync->mailboxes, pool, array_count(mailboxes));
array_append_array(&sync->mailboxes, mailboxes);
- get_mailbox_guids(mailboxes, &guids);
-
- /* initialize message iteration on both workers */
- count = array_count(&guids);
- sync->src_msg_iter =
- dsync_brain_msg_iter_init(sync, brain->src_worker,
- array_idx(&guids, 0), count);
- sync->dest_msg_iter =
- dsync_brain_msg_iter_init(sync, brain->dest_worker,
- array_idx(&guids, 0), count);
- dsync_brain_msg_sync_more(sync);
+ T_BEGIN {
+ ARRAY_TYPE(mailbox_guid) guids_arr;
+ const mailbox_guid_t *guids;
+ unsigned int count;
+
+ get_mailbox_guids(mailboxes, &guids_arr);
+
+ /* initialize message iteration on both workers */
+ guids = array_get(&guids_arr, &count);
+ sync->src_msg_iter =
+ dsync_brain_msg_iter_init(sync, brain->src_worker,
+ guids, count);
+ sync->dest_msg_iter =
+ dsync_brain_msg_iter_init(sync, brain->dest_worker,
+ guids, count);
+ } T_END;
return sync;
}
enum dsync_state {
DSYNC_STATE_GET_MAILBOXES = 0,
- DSYNC_STATE_CREATE_MAILBOXES,
+ DSYNC_STATE_SYNC_MAILBOXES,
DSYNC_STATE_SYNC_MSGS,
DSYNC_STATE_SYNC_UPDATE_MAILBOX,
- DSYNC_STATE_SYNC_RESOLVE_UID_CONFLICTS,
DSYNC_STATE_SYNC_FLUSH,
+ DSYNC_STATE_SYNC_FLUSH2,
DSYNC_STATE_SYNC_END
};
struct dsync_worker *worker;
struct dsync_worker_mailbox_iter *iter;
ARRAY_TYPE(dsync_mailbox) mailboxes;
+ ARRAY_TYPE(dsync_mailbox) dirs;
};
struct dsync_brain_guid_instance {
/* char *guid -> struct dsync_brain_guid_instance* */
struct hash_table *guid_hash;
- ARRAY_DEFINE(uid_conflicts, struct dsync_brain_uid_conflict);
ARRAY_DEFINE(new_msgs, struct dsync_brain_new_msg);
- unsigned int next_new_msg;
+ ARRAY_DEFINE(uid_conflicts, struct dsync_brain_uid_conflict);
+ unsigned int next_new_msg, next_conflict;
/* copy operations that failed. indexes point to new_msgs array */
ARRAY_TYPE(uint32_t) copy_retry_indexes;
unsigned int save_results_left;
unsigned int msgs_sent:1;
+ unsigned int adding_msgs:1;
};
struct dsync_brain_uid_conflict {
struct dsync_brain_new_msg {
uint32_t mailbox_idx;
+ uint32_t orig_uid;
struct dsync_message *msg;
};
struct dsync_brain {
struct dsync_worker *src_worker;
struct dsync_worker *dest_worker;
+ char *mailbox;
enum dsync_brain_flags flags;
enum dsync_state state;
struct dsync_brain_mailbox_sync *mailbox_sync;
unsigned int failed:1;
+ unsigned int verbose:1;
};
void dsync_brain_fail(struct dsync_brain *brain);
struct dsync_brain_mailbox_sync *
dsync_brain_msg_sync_init(struct dsync_brain *brain,
const ARRAY_TYPE(dsync_brain_mailbox) *mailboxes);
+void dsync_brain_msg_sync_more(struct dsync_brain_mailbox_sync *sync);
void dsync_brain_msg_sync_deinit(struct dsync_brain_mailbox_sync **_sync);
void dsync_brain_msg_sync_new_msgs(struct dsync_brain_mailbox_sync *sync);
static void
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **list);
-struct dsync_brain *dsync_brain_init(struct dsync_worker *src_worker,
- struct dsync_worker *dest_worker,
- enum dsync_brain_flags flags)
+struct dsync_brain *
+dsync_brain_init(struct dsync_worker *src_worker,
+ struct dsync_worker *dest_worker,
+ const char *mailbox, enum dsync_brain_flags flags)
{
struct dsync_brain *brain;
brain = i_new(struct dsync_brain, 1);
brain->src_worker = src_worker;
brain->dest_worker = dest_worker;
+ brain->mailbox = i_strdup(mailbox);
brain->flags = flags;
+ brain->verbose = (flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0;
return brain;
}
struct dsync_brain *brain = *_brain;
int ret = brain->failed ? -1 : 0;
+ if (brain->state != DSYNC_STATE_SYNC_END)
+ ret = -1;
+
+ if (ret < 0) {
+ /* make sure we unreference save input streams before workers
+ are deinitialized, so they can destroy the streams */
+ dsync_worker_msg_save_cancel(brain->src_worker);
+ dsync_worker_msg_save_cancel(brain->dest_worker);
+ }
+
if (brain->mailbox_sync != NULL)
dsync_brain_msg_sync_deinit(&brain->mailbox_sync);
if (brain->src_mailbox_list != NULL)
dsync_brain_mailbox_list_deinit(&brain->dest_mailbox_list);
*_brain = NULL;
+ i_free(brain->mailbox);
i_free(brain);
return ret;
}
while ((ret = dsync_worker_mailbox_iter_next(list->iter,
&dsync_box)) > 0) {
+ if (list->brain->mailbox != NULL &&
+ strcmp(list->brain->mailbox, dsync_box.name) != 0)
+ continue;
+
dup_box = dsync_mailbox_dup(list->pool, &dsync_box);
- array_append(&list->mailboxes, &dup_box, 1);
+ if (!mail_guid_128_is_empty(dup_box->mailbox_guid.guid))
+ array_append(&list->mailboxes, &dup_box, 1);
+ else
+ array_append(&list->dirs, &dup_box, 1);
}
if (ret < 0) {
/* finished listing mailboxes */
if (dsync_worker_mailbox_iter_deinit(&list->iter) < 0)
dsync_brain_fail(list->brain);
array_sort(&list->mailboxes, dsync_mailbox_p_guid_cmp);
+ array_sort(&list->dirs, dsync_mailbox_p_dir_guid_cmp);
dsync_brain_mailbox_list_finished(list->brain);
}
}
list->worker = worker;
list->iter = dsync_worker_mailbox_iter_init(worker);
p_array_init(&list->mailboxes, pool, 128);
+ p_array_init(&list->dirs, pool, 32);
dsync_worker_set_input_callback(worker, dsync_worker_mailbox_input,
list);
return list;
pool_unref(&list->pool);
}
-static void dsync_brain_create_missing_mailboxes(struct dsync_brain *brain)
+static void dsync_brain_sync_mailboxes(struct dsync_brain *brain)
{
struct dsync_mailbox *const *src_boxes, *const *dest_boxes, new_box;
unsigned int src, dest, src_count, dest_count;
+ bool src_deleted, dest_deleted;
int ret;
- /* FIXME: handle different hierarchy separators? */
-
memset(&new_box, 0, sizeof(new_box));
- /* find mailboxes from whose GUIDs don't exist.
- the mailboxes are sorted by GUID, so we can do this quickly. */
+ /* create/delete missing mailboxes. the mailboxes are sorted by
+ GUID, so we can do this quickly. */
src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
for (src = dest = 0; src < src_count && dest < dest_count; ) {
- ret = dsync_mailbox_guid_cmp(src_boxes[src], dest_boxes[dest]);
- if (ret == 0) {
- src++; dest++;
- } else if (ret < 0) {
+ src_deleted = (src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
+ dest_deleted = (dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
+ ret = dsync_mailbox_guid_cmp(src_boxes[src],
+ dest_boxes[dest]);
+ if (ret < 0) {
/* exists only in source */
- new_box = *src_boxes[src];
- new_box.uid_next = 0;
- new_box.highest_modseq = 0;
- dsync_worker_create_mailbox(brain->dest_worker,
- &new_box);
+ if (!src_deleted) {
+ new_box = *src_boxes[src];
+ new_box.uid_next = 0;
+ new_box.highest_modseq = 0;
+ dsync_worker_create_mailbox(brain->dest_worker,
+ &new_box);
+ }
src++;
- } else {
+ } else if (ret > 0) {
/* exists only in dest */
- new_box = *dest_boxes[dest];
- new_box.uid_next = 0;
- new_box.highest_modseq = 0;
- dsync_worker_create_mailbox(brain->src_worker,
- &new_box);
+ if (!dest_deleted) {
+ new_box = *dest_boxes[dest];
+ new_box.uid_next = 0;
+ new_box.highest_modseq = 0;
+ dsync_worker_create_mailbox(brain->src_worker,
+ &new_box);
+ }
dest++;
+ } else if (src_deleted) {
+ /* delete from dest too */
+ if (!dest_deleted) {
+ dsync_worker_delete_mailbox(brain->dest_worker,
+ &dest_boxes[dest]->mailbox_guid);
+ }
+ src++; dest++;
+ } else if (dest_deleted) {
+ /* delete from src too */
+ dsync_worker_delete_mailbox(brain->src_worker,
+ &src_boxes[src]->mailbox_guid);
+ src++; dest++;
+ } else {
+ src++; dest++;
}
}
for (; src < src_count; src++) {
+ if ((src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
+ continue;
+
new_box = *src_boxes[src];
new_box.uid_next = 0;
new_box.highest_modseq = 0;
dsync_worker_create_mailbox(brain->dest_worker, &new_box);
}
for (; dest < dest_count; dest++) {
+ if ((dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
+ continue;
+
new_box = *dest_boxes[dest];
new_box.uid_next = 0;
new_box.highest_modseq = 0;
}
}
-static bool dsync_mailbox_has_changed_msgs(const struct dsync_mailbox *box1,
+static bool dsync_mailbox_has_changed_msgs(struct dsync_brain *brain,
+ const struct dsync_mailbox *box1,
const struct dsync_mailbox *box2)
{
- return box1->uid_validity != box2->uid_validity ||
- box1->uid_next != box2->uid_next ||
- box1->highest_modseq != box2->highest_modseq;
+ if (box1->uid_validity != box2->uid_validity) {
+ if (brain->verbose) {
+ i_info("%s: uidvalidity changed %u -> %u", box1->name,
+ box1->uid_validity, box2->uid_validity);
+ }
+ return TRUE;
+ }
+ if (box1->uid_next != box2->uid_next) {
+ if (brain->verbose) {
+ i_info("%s: uidnext changed %u -> %u", box1->name,
+ box1->uid_next, box2->uid_next);
+ }
+ return TRUE;
+ }
+ if (box1->highest_modseq != box2->highest_modseq) {
+ if (brain->verbose) {
+ i_info("%s: highest_modseq changed %llu -> %llu",
+ box1->name,
+ (unsigned long long)box1->highest_modseq,
+ (unsigned long long)box2->highest_modseq);
+ }
+ return TRUE;
+ }
+ return FALSE;
}
static void
struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
struct dsync_brain_mailbox *brain_box;
unsigned int src, dest, src_count, dest_count;
+ bool src_deleted, dest_deleted;
int ret;
src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
for (src = dest = 0; src < src_count && dest < dest_count; ) {
+ src_deleted = (src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
+ dest_deleted = (dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
+
ret = dsync_mailbox_guid_cmp(src_boxes[src], dest_boxes[dest]);
if (ret == 0) {
- if (full_sync ||
- dsync_mailbox_has_changed_msgs(src_boxes[src],
- dest_boxes[dest])) {
+ if ((full_sync ||
+ dsync_mailbox_has_changed_msgs(brain,
+ src_boxes[src],
+ dest_boxes[dest])) &&
+ !src_deleted && !dest_deleted) {
brain_box = array_append_space(brain_boxes);
brain_box->box = *src_boxes[src];
src++; dest++;
} else if (ret < 0) {
/* exists only in source */
- brain_box = array_append_space(brain_boxes);
- brain_box->box = *src_boxes[src];
- brain_box->src = src_boxes[src];
- src++;
+ if (!src_deleted) {
+ brain_box = array_append_space(brain_boxes);
+ brain_box->box = *src_boxes[src];
+ brain_box->src = src_boxes[src];
+ if (brain->verbose) {
+ i_info("%s: only in source",
+ brain_box->box.name);
+ }
+ }
+ src++;
} else {
/* exists only in dest */
- brain_box = array_append_space(brain_boxes);
- brain_box->box = *dest_boxes[dest];
- brain_box->dest = dest_boxes[dest];
+ if (!dest_deleted) {
+ brain_box = array_append_space(brain_boxes);
+ brain_box->box = *dest_boxes[dest];
+ brain_box->dest = dest_boxes[dest];
+ if (brain->verbose) {
+ i_info("%s: only in dest",
+ brain_box->box.name);
+ }
+ }
dest++;
}
}
for (; src < src_count; src++) {
+ if ((src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
+ continue;
+
brain_box = array_append_space(brain_boxes);
brain_box->box = *src_boxes[src];
brain_box->src = src_boxes[src];
+ if (brain->verbose)
+ i_info("%s: only in source", brain_box->box.name);
}
for (; dest < dest_count; dest++) {
+ if ((dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
+ continue;
+
brain_box = array_append_space(brain_boxes);
brain_box->box = *dest_boxes[dest];
brain_box->dest = dest_boxes[dest];
+ if (brain->verbose)
+ i_info("%s: only in dest", brain_box->box.name);
}
}
dsync_brain_get_changed_mailboxes(brain, &mailboxes,
(brain->flags & DSYNC_BRAIN_FLAG_FULL_SYNC) != 0);
brain->mailbox_sync = dsync_brain_msg_sync_init(brain, &mailboxes);
+ dsync_brain_msg_sync_more(brain->mailbox_sync);
}
static void
const struct dsync_brain_mailbox *mailboxes;
unsigned int i, count;
+ /* FIXME: handle different hierarchy separators? */
+
mailboxes = array_get(&brain->mailbox_sync->mailboxes, &count);
for (i = 0; i < count; i++) {
dsync_worker_update_mailbox(brain->src_worker,
}
}
-static void dsync_worker_flush_callback(void *context)
+static void dsync_brain_worker_finished(bool success, void *context)
{
struct dsync_brain *brain = context;
- int ret;
- if ((ret = dsync_worker_output_flush(brain->dest_worker)) <= 0) {
- if (ret < 0)
- dsync_brain_fail(brain);
- return;
- }
+ if (!success)
+ dsync_brain_fail(brain);
+
brain->state++;
dsync_brain_sync(brain);
}
dsync_worker_mailbox_input(brain->src_mailbox_list);
dsync_worker_mailbox_input(brain->dest_mailbox_list);
break;
- case DSYNC_STATE_CREATE_MAILBOXES:
- if (array_count(&brain->src_mailbox_list->mailboxes) == 0 &&
- array_count(&brain->dest_mailbox_list->mailboxes) == 0) {
- /* no mailboxes */
- i_error("No mailboxes");
- dsync_brain_fail(brain);
- }
-
- /* FIXME: maybe wait and verify that all mailboxes are
- created successfully? */
- dsync_brain_create_missing_mailboxes(brain);
+ case DSYNC_STATE_SYNC_MAILBOXES:
+ dsync_brain_sync_mailboxes(brain);
brain->state++;
/* fall through */
case DSYNC_STATE_SYNC_MSGS:
dsync_brain_msg_sync_update_mailbox(brain);
brain->state++;
/* fall through */
- case DSYNC_STATE_SYNC_RESOLVE_UID_CONFLICTS:
- /* resolve uid conflicts after uid_nexts have been updated,
- so that it won't again collide uids */
- dsync_brain_msg_sync_resolve_uid_conflicts(brain->mailbox_sync);
- brain->state++;
- /* fall through */
case DSYNC_STATE_SYNC_FLUSH:
- /* FIXME: retrieve worker failures and set brain failure */
- dsync_worker_set_output_callback(brain->dest_worker,
- dsync_worker_flush_callback,
- brain);
- dsync_worker_flush_callback(brain);
+ dsync_worker_finish(brain->src_worker,
+ dsync_brain_worker_finished, brain);
+ dsync_worker_finish(brain->dest_worker,
+ dsync_brain_worker_finished, brain);
+ break;
+ case DSYNC_STATE_SYNC_FLUSH2:
break;
case DSYNC_STATE_SYNC_END:
master_service_stop(master_service);
break;
+ default:
+ i_unreached();
}
}
#define DSYNC_BRAIN_H
enum dsync_brain_flags {
- DSYNC_BRAIN_FLAG_FULL_SYNC = 0x01
+ DSYNC_BRAIN_FLAG_FULL_SYNC = 0x01,
+ DSYNC_BRAIN_FLAG_VERBOSE = 0x02
};
struct dsync_worker;
-struct dsync_brain *dsync_brain_init(struct dsync_worker *src_worker,
- struct dsync_worker *dest_worker,
- enum dsync_brain_flags flags);
+struct dsync_brain *
+dsync_brain_init(struct dsync_worker *src_worker,
+ struct dsync_worker *dest_worker,
+ const char *mailbox, enum dsync_brain_flags flags);
int dsync_brain_deinit(struct dsync_brain **brain);
void dsync_brain_sync(struct dsync_brain *brain);
int dsync_mailbox_guid_cmp(const struct dsync_mailbox *box1,
const struct dsync_mailbox *box2)
{
- int ret;
-
- ret = memcmp(box1->guid.guid, box2->guid.guid,
- sizeof(box1->guid.guid));
- if (ret != 0)
- return ret;
-
- if (box1->uid_validity != 0)
- return ret;
-
- /* \noselect mailboxes */
- i_assert(box2->uid_validity == 0);
- return strcmp(box1->name, box2->name);
+ return memcmp(box1->mailbox_guid.guid, box2->mailbox_guid.guid,
+ sizeof(box1->mailbox_guid.guid));
}
int dsync_mailbox_p_guid_cmp(struct dsync_mailbox *const *box1,
return dsync_mailbox_guid_cmp(*box1, *box2);
}
+int dsync_mailbox_dir_guid_cmp(const struct dsync_mailbox *box1,
+ const struct dsync_mailbox *box2)
+{
+ return memcmp(box1->dir_guid.guid, box2->dir_guid.guid,
+ sizeof(box1->dir_guid.guid));
+}
+
+int dsync_mailbox_p_dir_guid_cmp(struct dsync_mailbox *const *box1,
+ struct dsync_mailbox *const *box2)
+{
+ return dsync_mailbox_dir_guid_cmp(*box1, *box2);
+}
+
bool dsync_keyword_list_equals(const char *const *k1, const char *const *k2)
{
unsigned int i;
} mailbox_guid_t;
ARRAY_DEFINE_TYPE(mailbox_guid, mailbox_guid_t);
+enum dsync_mailbox_flags {
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX = 0x01,
+ DSYNC_MAILBOX_FLAG_DELETED_DIR = 0x02
+};
+
struct dsync_mailbox {
const char *name;
- mailbox_guid_t guid;
- /* uid_validity=0 for \noselect mailbox */
+ /* Mailbox directory's GUID. Not necessarily set if mailbox is
+ deleted. */
+ mailbox_guid_t dir_guid;
+ /* Mailbox's GUID. Full of zero with \Noselect mailboxes. */
+ mailbox_guid_t mailbox_guid;
+
uint32_t uid_validity, uid_next;
uint64_t highest_modseq;
+ time_t last_renamed;
+ enum dsync_mailbox_flags flags;
};
ARRAY_DEFINE_TYPE(dsync_mailbox, struct dsync_mailbox *);
int dsync_mailbox_p_guid_cmp(struct dsync_mailbox *const *box1,
struct dsync_mailbox *const *box2);
+int dsync_mailbox_dir_guid_cmp(const struct dsync_mailbox *box1,
+ const struct dsync_mailbox *box2);
+int dsync_mailbox_p_dir_guid_cmp(struct dsync_mailbox *const *box1,
+ struct dsync_mailbox *const *box2);
+
bool dsync_keyword_list_equals(const char *const *k1, const char *const *k2);
bool dsync_guid_equals(const mailbox_guid_t *guid1,
enum proxy_client_request_type {
PROXY_CLIENT_REQUEST_TYPE_COPY,
- PROXY_CLIENT_REQUEST_TYPE_GET
+ PROXY_CLIENT_REQUEST_TYPE_GET,
+ PROXY_CLIENT_REQUEST_TYPE_FINISH
};
struct proxy_client_request {
union {
dsync_worker_msg_callback_t *get;
dsync_worker_copy_callback_t *copy;
+ dsync_worker_finish_callback_t *finish;
} callback;
void *context;
};
struct dsync_msg_static_data msg_get_data;
ARRAY_DEFINE(request_array, struct proxy_client_request);
struct aqueue *request_queue;
+
+ unsigned int finished:1;
};
extern struct dsync_worker_vfuncs proxy_client_dsync_worker;
return -1;
}
if (worker->input->eof) {
- i_error("worker server disconnected unexpectedly");
+ if (!worker->finished)
+ i_error("read() from worker server failed: EOF");
dsync_worker_set_failure(&worker->worker);
return -1;
}
static void
proxy_client_worker_msg_get_done(struct proxy_client_dsync_worker *worker)
{
+ i_assert(worker->io == NULL);
+
worker->msg_get_data.input = NULL;
worker->io = io_add(worker->fd_in, IO_READ,
proxy_client_worker_input, worker);
i_stream_set_destroy_callback(worker->msg_get_data.input,
proxy_client_worker_msg_get_done,
worker);
+ io_remove(&worker->io);
result = DSYNC_MSG_GET_RESULT_SUCCESS;
break;
case '0':
break;
}
- io_remove(&worker->io);
request->callback.get(result, &worker->msg_get_data, request->context);
return worker->io != NULL;
}
+static void
+proxy_client_worker_next_finish(const struct proxy_client_request *request,
+ const char *line)
+{
+ request->callback.finish(line[0] == '1', request->context);
+}
+
static bool
proxy_client_worker_next_reply(struct proxy_client_dsync_worker *worker,
const char *line)
case PROXY_CLIENT_REQUEST_TYPE_GET:
ret = proxy_client_worker_next_msg_get(worker, &request, line);
break;
+ case PROXY_CLIENT_REQUEST_TYPE_FINISH:
+ worker->finished = TRUE;
+ proxy_client_worker_next_finish(&request, line);
+ break;
}
return ret;
}
static void proxy_client_worker_input(struct proxy_client_dsync_worker *worker)
{
const char *line;
+ int ret;
if (worker->worker.input_callback != NULL) {
worker->worker.input_callback(worker->worker.input_context);
return;
}
- while (proxy_client_worker_read_line(worker, &line) > 0) {
+ while ((ret = proxy_client_worker_read_line(worker, &line)) > 0) {
if (!proxy_client_worker_next_reply(worker, line))
break;
}
+ if (ret < 0) {
+ /* try to continue */
+ proxy_client_worker_next_reply(worker, "");
+ }
}
static int proxy_client_worker_output(struct proxy_client_dsync_worker *worker)
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
- io_remove(&worker->io);
+ if (worker->io != NULL)
+ io_remove(&worker->io);
i_stream_destroy(&worker->input);
o_stream_destroy(&worker->output);
if (close(worker->fd_in) < 0)
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ if (worker->save_io != NULL) {
+ /* we haven't finished sending a message save, so we're full. */
+ return TRUE;
+ }
+
return o_stream_get_buffer_used_size(worker->output) >=
OUTBUF_THROTTLE_SIZE;
}
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
str_append(str, "BOX-CREATE\t");
- str_tabescape_write(str, dsync_box->name);
- if (dsync_box->uid_validity != 0) {
- str_append_c(str, '\t');
- dsync_proxy_mailbox_guid_export(str, &dsync_box->guid);
- str_printfa(str, "\t%u\n", dsync_box->uid_validity);
- }
+ dsync_proxy_mailbox_export(str, dsync_box);
+ str_append_c(str, '\n');
+ o_stream_send(worker->output, str_data(str), str_len(str));
+ } T_END;
+}
+
+static void
+proxy_client_worker_delete_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox)
+{
+ struct proxy_client_dsync_worker *worker =
+ (struct proxy_client_dsync_worker *)_worker;
+
+ i_assert(worker->save_input == NULL);
+
+ T_BEGIN {
+ string_t *str = t_str_new(128);
+
+ str_append(str, "BOX-DELETE\t");
+ dsync_proxy_mailbox_guid_export(str, mailbox);
+ str_append_c(str, '\n');
+ o_stream_send(worker->output, str_data(str), str_len(str));
+ } T_END;
+}
+
+static void
+proxy_client_worker_rename_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox,
+ const char *name)
+{
+ struct proxy_client_dsync_worker *worker =
+ (struct proxy_client_dsync_worker *)_worker;
+
+ i_assert(worker->save_input == NULL);
+
+ T_BEGIN {
+ string_t *str = t_str_new(128);
+
+ str_append(str, "BOX-RENAME\t");
+ dsync_proxy_mailbox_guid_export(str, mailbox);
+ str_append_c(str, '\t');
+ str_tabescape_write(str, name);
+ str_append_c(str, '\n');
o_stream_send(worker->output, str_data(str), str_len(str));
} T_END;
}
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
str_append(str, "BOX-UPDATE\t");
- str_tabescape_write(str, dsync_box->name);
- str_append_c(str, '\t');
- dsync_proxy_mailbox_guid_export(str, &dsync_box->guid);
- str_printfa(str, "\t%u\t%u\t%llu\n",
- dsync_box->uid_validity, dsync_box->uid_next,
- (unsigned long long)dsync_box->highest_modseq);
+ dsync_proxy_mailbox_export(str, dsync_box);
+ str_append_c(str, '\n');
o_stream_send(worker->output, str_data(str), str_len(str));
} T_END;
}
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
if (dsync_guid_equals(&worker->selected_box_guid, mailbox))
return;
worker->selected_box_guid = *mailbox;
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
o_stream_send_str(worker->output,
t_strdup_printf("MSG-UID-CHANGE\t%u\t%u\n",
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
o_stream_send_str(worker->output,
t_strdup_printf("MSG-EXPUNGE\t%u\n", uid));
(struct proxy_client_dsync_worker *)_worker;
struct proxy_client_request request;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
struct proxy_client_dsync_worker *worker =
(struct proxy_client_dsync_worker *)_worker;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
} T_END;
i_assert(worker->save_io == NULL);
- i_assert(worker->save_input == NULL);
worker->save_input = data->input;
worker->save_input_last_lf = TRUE;
i_stream_ref(worker->save_input);
}
static void
-proxy_client_worker_msg_get(struct dsync_worker *_worker, uint32_t uid,
+proxy_client_worker_msg_save_cancel(struct dsync_worker *_worker)
+{
+ struct proxy_client_dsync_worker *worker =
+ (struct proxy_client_dsync_worker *)_worker;
+
+ if (worker->save_io != NULL)
+ io_remove(&worker->save_io);
+ if (worker->save_input != NULL)
+ i_stream_unref(&worker->save_input);
+}
+
+static void
+proxy_client_worker_msg_get(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox, uint32_t uid,
dsync_worker_msg_callback_t *callback,
void *context)
{
(struct proxy_client_dsync_worker *)_worker;
struct proxy_client_request request;
+ i_assert(worker->save_input == NULL);
+
T_BEGIN {
string_t *str = t_str_new(128);
- str_printfa(str, "MSG-GET\t%u\n", uid);
+ str_append(str, "MSG-GET\t");
+ dsync_proxy_mailbox_guid_export(str, mailbox);
+ str_printfa(str, "\t%u\n", uid);
o_stream_send(worker->output, str_data(str), str_len(str));
} T_END;
aqueue_append(worker->request_queue, &request);
}
+static void
+proxy_client_worker_finish(struct dsync_worker *_worker,
+ dsync_worker_finish_callback_t *callback,
+ void *context)
+{
+ struct proxy_client_dsync_worker *worker =
+ (struct proxy_client_dsync_worker *)_worker;
+ struct proxy_client_request request;
+
+ i_assert(worker->save_input == NULL);
+
+ o_stream_send_str(worker->output, "FINISH\n");
+ o_stream_uncork(worker->output);
+
+ memset(&request, 0, sizeof(request));
+ request.type = PROXY_CLIENT_REQUEST_TYPE_FINISH;
+ request.callback.finish = callback;
+ request.context = context;
+ aqueue_append(worker->request_queue, &request);
+}
+
struct dsync_worker_vfuncs proxy_client_dsync_worker = {
proxy_client_worker_deinit,
proxy_client_worker_msg_iter_deinit,
proxy_client_worker_create_mailbox,
+ proxy_client_worker_delete_mailbox,
+ proxy_client_worker_rename_mailbox,
proxy_client_worker_update_mailbox,
proxy_client_worker_select_mailbox,
proxy_client_worker_msg_expunge,
proxy_client_worker_msg_copy,
proxy_client_worker_msg_save,
- proxy_client_worker_msg_get
+ proxy_client_worker_msg_save_cancel,
+ proxy_client_worker_msg_get,
+ proxy_client_worker_finish
};
unsigned int i, count;
count = str_array_length(args);
- mailboxes = t_new(mailbox_guid_t, count);
+ mailboxes = count == 0 ? NULL : t_new(mailbox_guid_t, count);
for (i = 0; i < count; i++) {
if (dsync_proxy_mailbox_guid_import(args[i],
&mailboxes[i]) < 0) {
}
static int
-parse_box_args(const char *const *args, struct dsync_mailbox *dsync_box_r)
+cmd_box_create(struct dsync_proxy_server *server, const char *const *args)
{
- if (args[0] == NULL)
- return -1;
-
- memset(dsync_box_r, 0, sizeof(*dsync_box_r));
- dsync_box_r->name = args[0];
- if (args[1] == NULL) {
- /* \noselect box */
- return 0;
- }
+ struct dsync_mailbox dsync_box;
+ const char *error;
- /* guid uid_validity [uid_next highest_modseq] */
- if (dsync_proxy_mailbox_guid_import(args[1], &dsync_box_r->guid) < 0) {
- i_error("Invalid mailbox GUID '%s' (name: %s)",
- args[1], dsync_box_r->name);
+ if (dsync_proxy_mailbox_import_unescaped(pool_datastack_create(),
+ args, &dsync_box,
+ &error) < 0) {
+ i_error("Invalid mailbox input: %s", error);
return -1;
}
+ dsync_worker_create_mailbox(server->worker, &dsync_box);
+ return 1;
+}
- if (args[2] == NULL)
- return -1;
- dsync_box_r->uid_validity = strtoul(args[2], NULL, 10);
+static int
+cmd_box_delete(struct dsync_proxy_server *server, const char *const *args)
+{
+ mailbox_guid_t guid;
- if (args[3] == NULL)
- return 0;
- dsync_box_r->uid_next = strtoul(args[3], NULL, 10);
- if (args[4] == NULL)
+ if (args[0] == NULL ||
+ dsync_proxy_mailbox_guid_import(args[0], &guid) < 0) {
+ i_error("box-delete: Invalid mailbox GUID '%s'", args[0]);
return -1;
- dsync_box_r->highest_modseq = strtoull(args[4], NULL, 10);
- return 0;
+ }
+
+ dsync_worker_delete_mailbox(server->worker, &guid);
+ return 1;
}
static int
-cmd_box_create(struct dsync_proxy_server *server, const char *const *args)
+cmd_box_rename(struct dsync_proxy_server *server, const char *const *args)
{
- struct dsync_mailbox dsync_box;
+ mailbox_guid_t guid;
- if (parse_box_args(args, &dsync_box) < 0)
+ if (str_array_length(args) < 2)
return -1;
- dsync_worker_create_mailbox(server->worker, &dsync_box);
+ if (dsync_proxy_mailbox_guid_import(args[0], &guid) < 0) {
+ i_error("box-delete: Invalid mailbox GUID '%s'", args[0]);
+ return -1;
+ }
+
+ dsync_worker_rename_mailbox(server->worker, &guid, args[1]);
return 1;
}
cmd_box_update(struct dsync_proxy_server *server, const char *const *args)
{
struct dsync_mailbox dsync_box;
+ const char *error;
- if (parse_box_args(args, &dsync_box) < 0)
+ if (dsync_proxy_mailbox_import_unescaped(pool_datastack_create(),
+ args, &dsync_box,
+ &error) < 0) {
+ i_error("Invalid mailbox input: %s", error);
return -1;
+ }
dsync_worker_update_mailbox(server->worker, &dsync_box);
return 1;
}
ret = i_stream_read_data(server->get_input, &data, &size, 0);
if (ret == -1) {
/* done */
+ o_stream_send(server->output, "\n.\n", 3);
i_stream_unref(&server->get_input);
break;
} else {
static void
cmd_msg_get_callback(enum dsync_msg_get_result result,
- struct dsync_msg_static_data *data, void *context)
+ const struct dsync_msg_static_data *data, void *context)
{
struct dsync_proxy_server *server = context;
string_t *str;
case DSYNC_MSG_GET_RESULT_SUCCESS:
break;
case DSYNC_MSG_GET_RESULT_EXPUNGED:
- o_stream_send(server->output, "*0\n", 3);
+ o_stream_send(server->output, "0\n", 3);
return;
case DSYNC_MSG_GET_RESULT_FAILED:
- o_stream_send(server->output, "*-\n", 3);
+ o_stream_send(server->output, "-\n", 3);
return;
}
str = t_str_new(128);
- str_append(str, "*1\t");
+ str_append(str, "1\t");
dsync_proxy_msg_static_export(str, data);
str_append_c(str, '\n');
o_stream_send(server->output, str_data(str), str_len(str));
static int
cmd_msg_get(struct dsync_proxy_server *server, const char *const *args)
{
- if (args[0] == NULL)
+ mailbox_guid_t mailbox_guid;
+ uint32_t uid;
+
+ if (str_array_length(args) < 2)
+ return -1;
+
+ if (dsync_proxy_mailbox_guid_import(args[0], &mailbox_guid) < 0) {
+ i_error("msg-get: Invalid mailbox GUID '%s'", args[0]);
+ return -1;
+ }
+
+ uid = strtoul(args[1], NULL, 10);
+ if (uid == 0)
return -1;
if (server->get_input != NULL)
cmd_msg_get_send_more(server);
else {
- dsync_worker_msg_get(server->worker, strtoul(args[0], NULL, 10),
+ dsync_worker_msg_get(server->worker, &mailbox_guid, uid,
cmd_msg_get_callback, server);
+ /* FIXME: why? this shouldn't be needed.. */
+ o_stream_uncork(server->output);
}
return server->get_input == NULL ? 1 : 0;
}
+static void cmd_finish_callback(bool success, void *context)
+{
+ struct dsync_proxy_server *server = context;
+
+ server->finished = TRUE;
+ o_stream_send_str(server->output, success ? "1\n" : "0\n");
+}
+
+static int
+cmd_finish(struct dsync_proxy_server *server,
+ const char *const *args ATTR_UNUSED)
+{
+ dsync_worker_finish(server->worker, cmd_finish_callback, server);
+ return 1;
+}
+
static struct dsync_proxy_server_command commands[] = {
{ "BOX-LIST", cmd_box_list },
{ "MSG-LIST", cmd_msg_list },
{ "BOX-CREATE", cmd_box_create },
+ { "BOX-DELETE", cmd_box_delete },
+ { "BOX-RENAME", cmd_box_rename },
{ "BOX-UPDATE", cmd_box_update },
{ "BOX-SELECT", cmd_box_select },
{ "MSG-UPDATE", cmd_msg_update },
{ "MSG-COPY", cmd_msg_copy },
{ "MSG-SAVE", cmd_msg_save },
{ "MSG-GET", cmd_msg_get },
+ { "FINISH", cmd_finish },
{ NULL, NULL }
};
return -1;
}
if (server->input->eof) {
+ if (!server->finished)
+ i_error("read() from proxy client failed: EOF");
master_service_stop(master_service);
return -1;
}
*_server = NULL;
+ if (server->get_input != NULL)
+ i_stream_unref(&server->get_input);
pool_unref(&server->cmd_pool);
io_remove(&server->io);
i_stream_destroy(&server->input);
struct istream *get_input;
bool get_input_last_lf;
+
+ unsigned int finished:1;
};
struct dsync_proxy_server *
const struct dsync_mailbox *box)
{
str_tabescape_write(str, box->name);
- if (box->uid_validity == 0) {
+ str_append_c(str, '\t');
+ dsync_proxy_mailbox_guid_export(str, &box->dir_guid);
+ str_printfa(str, "\t%lu\t%u", (unsigned long)box->last_renamed,
+ box->flags);
+
+ if (mail_guid_128_is_empty(box->mailbox_guid.guid)) {
/* \noselect mailbox */
return;
}
- str_printfa(str, "\t%s\t%u\t%u\t%llu",
- binary_to_hex(box->guid.guid, sizeof(box->guid.guid)),
+ str_append_c(str, '\t');
+ dsync_proxy_mailbox_guid_export(str, &box->mailbox_guid);
+ str_printfa(str, "\t%u\t%u\t%llu",
box->uid_validity, box->uid_next,
(unsigned long long)box->highest_modseq);
}
-static int
-mailbox_parse_args(pool_t pool, struct dsync_mailbox *box,
- const char *const *args, const char **error_r)
+int dsync_proxy_mailbox_import_unescaped(pool_t pool, const char *const *args,
+ struct dsync_mailbox *box_r,
+ const char **error_r)
{
- string_t *str;
unsigned int count;
char *p;
+ memset(box_r, 0, sizeof(*box_r));
+
count = str_array_length(args);
- if (count != 1 && count != 5) {
+ if (count != 4 && count != 8) {
*error_r = "Mailbox missing parameters";
return -1;
}
- /* name guid uid_validity uid_next highest_modseq */
- str = t_str_new(128);
- str_append_tabunescaped(str, args[0], strlen(args[0]));
- box->name = p_strdup(pool, str_c(str));
+ /* name dir_guid mailbox_guid uid_validity uid_next highest_modseq */
+ box_r->name = p_strdup(pool, args[0]);
- if (args[1] == NULL) {
+ if (dsync_proxy_mailbox_guid_import(args[1], &box_r->dir_guid) < 0) {
+ *error_r = "Invalid dir GUID";
+ return -1;
+ }
+ box_r->last_renamed = strtoul(args[2], &p, 10);
+ if (*p != '\0') {
+ *error_r = "Invalid mailbox last_renamed";
+ return -1;
+ }
+ box_r->flags = strtoul(args[3], &p, 10);
+ if (*p != '\0') {
+ *error_r = "Invalid mailbox flags";
+ return -1;
+ }
+
+ if (args[4] == NULL) {
/* \noselect mailbox */
return 0;
}
- str_truncate(str, 0);
- if (hex_to_binary(args[1], str) < 0) {
- *error_r = "Invalid hex in mailbox GUID";
- return -1;
- } else if (str_len(str) != sizeof(box->guid)) {
- *error_r = "Invalid mailbox GUID size";
+ if (dsync_proxy_mailbox_guid_import(args[4], &box_r->mailbox_guid) < 0) {
+ *error_r = "Invalid mailbox GUID";
return -1;
- } else {
- memcpy(box->guid.guid, str_data(str), sizeof(box->guid.guid));
}
- box->uid_validity = strtoul(args[2], &p, 10);
- if (box->uid_validity == 0 || *p != '\0') {
+ box_r->uid_validity = strtoul(args[5], &p, 10);
+ if (box_r->uid_validity == 0 || *p != '\0') {
*error_r = "Invalid mailbox uid_validity";
return -1;
}
- box->uid_next = strtoul(args[3], &p, 10);
- if (box->uid_validity == 0 || *p != '\0') {
+ box_r->uid_next = strtoul(args[6], &p, 10);
+ if (box_r->uid_validity == 0 || *p != '\0') {
*error_r = "Invalid mailbox uid_next";
return -1;
}
- box->highest_modseq = strtoull(args[4], &p, 10);
+ box_r->highest_modseq = strtoull(args[7], &p, 10);
if (*p != '\0') {
*error_r = "Invalid mailbox highest_modseq";
return -1;
struct dsync_mailbox *box_r,
const char **error_r)
{
+ char **args;
int ret;
- memset(box_r, 0, sizeof(*box_r));
T_BEGIN {
- ret = mailbox_parse_args(pool, box_r,
- t_strsplit(str, "\t"), error_r);
+ args = p_strsplit(pool_datastack_create(), str, "\t");
+ if (args[0] != NULL)
+ args[0] = str_tabunescape(args[0]);
+ ret = dsync_proxy_mailbox_import_unescaped(pool,
+ (const char *const *)args,
+ box_r, error_r);
} T_END;
return ret;
}
int dsync_proxy_mailbox_import(pool_t pool, const char *str,
struct dsync_mailbox *box_r,
const char **error_r);
+int dsync_proxy_mailbox_import_unescaped(pool_t pool, const char *const *args,
+ struct dsync_mailbox *box_r,
+ const char **error_r);
void dsync_proxy_mailbox_guid_export(string_t *str,
const mailbox_guid_t *mailbox);
#include "hash.h"
#include "str.h"
#include "hex-binary.h"
+#include "network.h"
#include "istream.h"
+#include "mailbox-log.h"
#include "mail-user.h"
#include "mail-namespace.h"
#include "mail-storage.h"
struct local_dsync_worker_mailbox_iter {
struct dsync_worker_mailbox_iter iter;
struct mailbox_list_iterate_context *list_iter;
+ struct hash_iterate_context *deleted_iter;
};
struct local_dsync_worker_msg_iter {
const char *storage_name;
};
+struct local_dsync_mailbox_change {
+ mailbox_guid_t guid;
+ time_t last_renamed;
+ unsigned int deleted_mailbox:1;
+ unsigned int deleted_dir:1;
+ unsigned int unsubscribed:1;
+};
+
struct local_dsync_worker {
struct dsync_worker worker;
struct mail_user *user;
pool_t pool;
/* mailbox_guid_t -> struct local_dsync_mailbox* */
struct hash_table *mailbox_hash;
+ /* mailbox_guid_t -> struct local_dsync_mailbox_change* */
+ struct hash_table *mailbox_changes_hash;
mailbox_guid_t selected_box_guid;
struct mailbox *selected_box;
- struct mail *mail;
+ struct mail *mail, *ext_mail;
+
+ mailbox_guid_t get_mailbox;
+ struct mail *get_mail;
+
+ struct io *save_io;
+ struct mail_save_context *save_ctx;
+ struct istream *save_input;
+
+ dsync_worker_finish_callback_t *finish_callback;
+ void *finish_context;
+
+ unsigned int reading_mail:1;
};
extern struct dsync_worker_vfuncs local_dsync_worker;
-static void worker_mailbox_close(struct local_dsync_worker *worker);
+static void local_worker_mailbox_close(struct local_dsync_worker *worker);
+static void local_worker_msg_box_close(struct local_dsync_worker *worker);
static int mailbox_guid_cmp(const void *p1, const void *p2)
{
struct local_dsync_worker *worker =
(struct local_dsync_worker *)_worker;
- worker_mailbox_close(worker);
+ i_assert(worker->save_input == NULL);
+
+ local_worker_msg_box_close(worker);
+ local_worker_mailbox_close(worker);
hash_table_destroy(&worker->mailbox_hash);
+ if (worker->mailbox_changes_hash != NULL)
+ hash_table_destroy(&worker->mailbox_changes_hash);
pool_unref(&worker->pool);
}
return 1;
}
+static int
+dsync_worker_get_list_mailbox_log(struct local_dsync_worker *worker,
+ struct mailbox_list *list)
+{
+ struct mailbox_log *log;
+ struct mailbox_log_iter *iter;
+ const struct mailbox_log_record *rec;
+ struct local_dsync_mailbox_change *change;
+
+ log = mailbox_list_get_changelog(list);
+ iter = mailbox_log_iter_init(log);
+ while ((rec = mailbox_log_iter_next(iter)) != NULL) {
+ change = hash_table_lookup(worker->mailbox_changes_hash,
+ rec->mailbox_guid);
+ if (change == NULL) {
+ change = i_new(struct local_dsync_mailbox_change, 1);
+ memcpy(change->guid.guid, rec->mailbox_guid,
+ sizeof(change->guid.guid));
+ hash_table_insert(worker->mailbox_changes_hash,
+ change->guid.guid, change);
+ }
+ switch (rec->type) {
+ case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
+ change->deleted_mailbox = TRUE;
+ break;
+ case MAILBOX_LOG_RECORD_DELETE_DIR:
+ change->deleted_dir = TRUE;
+ break;
+ case MAILBOX_LOG_RECORD_RENAME:
+ change->last_renamed =
+ mailbox_log_record_get_timestamp(rec);
+ break;
+ case MAILBOX_LOG_RECORD_SUBSCRIBE:
+ change->unsubscribed = FALSE;
+ break;
+ case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
+ change->unsubscribed = TRUE;
+ break;
+ }
+ if (change->deleted_dir && change->deleted_mailbox) {
+ /* same GUID shouldn't be both. something's already
+ broken, but change this so we don't get into more
+ problems later. */
+ change->deleted_dir = FALSE;
+ }
+ }
+ return mailbox_log_iter_deinit(&iter);
+}
+
+static unsigned int mailbox_log_record_hash(const void *p)
+{
+ const uint8_t *guid = p;
+
+ return ((unsigned int)guid[0] << 24) |
+ ((unsigned int)guid[1] << 16) |
+ ((unsigned int)guid[2] << 8) |
+ (unsigned int)guid[3];
+}
+
+static int mailbox_log_record_cmp(const void *p1, const void *p2)
+{
+ return memcmp(p1, p2, MAIL_GUID_128_SIZE);
+}
+
+static int dsync_worker_get_mailbox_log(struct local_dsync_worker *worker)
+{
+ struct mail_namespace *ns;
+ int ret = 0;
+
+ if (worker->mailbox_changes_hash != NULL)
+ return 0;
+
+ worker->mailbox_changes_hash =
+ hash_table_create(default_pool, worker->pool, 0,
+ mailbox_log_record_hash,
+ mailbox_log_record_cmp);
+ for (ns = worker->user->namespaces; ns != NULL; ns = ns->next) {
+ if (ns->alias_for != NULL)
+ continue;
+
+ if (dsync_worker_get_list_mailbox_log(worker, ns->list) < 0)
+ ret = -1;
+ }
+ return ret;
+}
+
static struct dsync_worker_mailbox_iter *
local_worker_mailbox_iter_init(struct dsync_worker *_worker)
{
iter->list_iter =
mailbox_list_iter_init_namespaces(worker->user->namespaces,
patterns, list_flags);
+ (void)dsync_worker_get_mailbox_log(worker);
return &iter->iter;
}
hash_table_insert(worker->mailbox_hash, &lbox->guid, lbox);
}
+static int
+iter_next_deleted(struct local_dsync_worker_mailbox_iter *iter,
+ struct local_dsync_worker *worker,
+ struct dsync_mailbox *dsync_box_r)
+{
+ const struct local_dsync_mailbox_change *change;
+ void *key, *value;
+
+ if (iter->deleted_iter == NULL) {
+ iter->deleted_iter =
+ hash_table_iterate_init(worker->mailbox_changes_hash);
+ }
+ while (hash_table_iterate(iter->deleted_iter, &key, &value)) {
+ change = value;
+ if (change->deleted_mailbox) {
+ /* the name doesn't matter */
+ dsync_box_r->name = "";
+ dsync_box_r->mailbox_guid = change->guid;
+ dsync_box_r->flags |=
+ DSYNC_MAILBOX_FLAG_DELETED_MAILBOX;
+ return 1;
+ }
+ if (change->deleted_dir) {
+ /* the name doesn't matter */
+ dsync_box_r->name = "";
+ dsync_box_r->dir_guid = change->guid;
+ dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_DELETED_DIR;
+ return 1;
+ }
+ }
+ hash_table_iterate_deinit(&iter->deleted_iter);
+ return -1;
+}
+
static int
local_worker_mailbox_iter_next(struct dsync_worker_mailbox_iter *_iter,
struct dsync_mailbox *dsync_box_r)
const char *storage_name;
struct mailbox *box;
struct mailbox_status status;
+ struct local_dsync_mailbox_change *change;
+
+ memset(dsync_box_r, 0, sizeof(*dsync_box_r));
info = mailbox_list_iter_next(iter->list_iter);
if (info == NULL)
- return -1;
+ return iter_next_deleted(iter, worker, dsync_box_r);
- memset(dsync_box_r, 0, sizeof(*dsync_box_r));
dsync_box_r->name = info->name;
+ if (mailbox_list_get_guid(info->ns->list, info->name,
+ dsync_box_r->dir_guid.guid) < 0) {
+ i_error("Failed to get dir GUID for mailbox %s: %s", info->name,
+ mailbox_list_get_last_error(info->ns->list, NULL));
+ _iter->failed = TRUE;
+ return -1;
+ }
- if ((info->flags & MAILBOX_NOSELECT) != 0)
- return 1;
+ /* get last rename timestamp */
+ change = hash_table_lookup(worker->mailbox_changes_hash,
+ dsync_box_r->dir_guid.guid);
+ if (change != NULL) {
+ /* it shouldn't be marked as deleted, but drop it to be sure */
+ change->deleted_dir = FALSE;
+ dsync_box_r->last_renamed = change->last_renamed;
+ }
storage_name = mail_namespace_get_storage_name(info->ns, info->name);
+ if ((info->flags & MAILBOX_NOSELECT) != 0) {
+ local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
+ &dsync_box_r->dir_guid);
+ return 1;
+ }
+
box = mailbox_alloc(info->ns->list, storage_name, NULL, flags);
if (mailbox_sync(box, 0, 0, NULL) < 0) {
struct mail_storage *storage = mailbox_get_storage(box);
mailbox_get_status(box, STATUS_UIDNEXT | STATUS_UIDVALIDITY |
STATUS_HIGHESTMODSEQ | STATUS_GUID, &status);
- memcpy(dsync_box_r->guid.guid, status.mailbox_guid,
- sizeof(dsync_box_r->guid.guid));
+ change = hash_table_lookup(worker->mailbox_changes_hash,
+ status.mailbox_guid);
+ if (change != NULL) {
+ /* it shouldn't be marked as deleted, but drop it to be sure */
+ change->deleted_mailbox = FALSE;
+ }
+
+ memcpy(dsync_box_r->mailbox_guid.guid, status.mailbox_guid,
+ sizeof(dsync_box_r->mailbox_guid.guid));
dsync_box_r->uid_validity = status.uidvalidity;
dsync_box_r->uid_next = status.uidnext;
dsync_box_r->highest_modseq = status.highest_modseq;
local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
- &dsync_box_r->guid);
+ &dsync_box_r->mailbox_guid);
mailbox_close(&box);
return 1;
}
iter = i_new(struct local_dsync_worker_msg_iter, 1);
iter->iter.worker = worker;
- iter->mailboxes = i_new(mailbox_guid_t, mailbox_count);
+ iter->mailboxes = mailbox_count == 0 ? NULL :
+ i_new(mailbox_guid_t, mailbox_count);
iter->mailbox_count = mailbox_count;
for (i = 0; i < mailbox_count; i++) {
memcpy(iter->mailboxes[i].guid, &mailboxes[i],
struct mailbox_update *update_r)
{
memset(update_r, 0, sizeof(*update_r));
- memcpy(update_r->mailbox_guid, dsync_box->guid.guid,
+ memcpy(update_r->mailbox_guid, dsync_box->mailbox_guid.guid,
sizeof(update_r->mailbox_guid));
update_r->uid_validity = dsync_box->uid_validity;
update_r->min_next_uid = dsync_box->uid_next;
local_dsync_worker_add_mailbox(worker,
mailbox_get_namespace(box),
mailbox_get_name(box),
- &dsync_box->guid);
+ &dsync_box->mailbox_guid);
}
mailbox_close(&box);
}
-static void worker_mailbox_close(struct local_dsync_worker *worker)
+static void
+local_worker_delete_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox)
{
- struct mailbox_transaction_context *trans;
+ struct local_dsync_worker *worker =
+ (struct local_dsync_worker *)_worker;
+ struct local_dsync_mailbox *lbox;
+
+ lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
+ if (lbox == NULL) {
+ i_error("Trying to delete a non-listed mailbox with guid=%s",
+ binary_to_hex(mailbox->guid, sizeof(mailbox->guid)));
+ dsync_worker_set_failure(_worker);
+ return;
+ }
+
+ if (mailbox_list_delete_mailbox(lbox->ns->list,
+ lbox->storage_name) < 0) {
+ i_error("Can't delete mailbox %s: %s", lbox->storage_name,
+ mailbox_list_get_last_error(lbox->ns->list, NULL));
+ dsync_worker_set_failure(_worker);
+ }
+}
+
+static void
+local_worker_rename_children(struct local_dsync_worker *worker,
+ const char *oldname, const char *newname, char sep)
+{
+ struct hash_iterate_context *iter;
+ const char *oldprefix;
+ void *key, *value;
+ unsigned int oldprefix_len;
+
+ oldprefix = t_strdup_printf("%s%c", oldname, sep);
+ oldprefix_len = strlen(oldprefix);
+
+ iter = hash_table_iterate_init(worker->mailbox_hash);
+ while (hash_table_iterate(iter, &key, &value)) {
+ struct local_dsync_mailbox *lbox = value;
+
+ if (strncmp(lbox->storage_name, oldprefix, oldprefix_len) != 0)
+ continue;
+
+ lbox->storage_name =
+ p_strdup_printf(worker->pool, "%s%c%s", newname, sep,
+ lbox->storage_name + oldprefix_len);
+ }
+ hash_table_iterate_deinit(&iter);
+}
+
+static void
+local_worker_rename_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox, const char *name)
+{
+ struct local_dsync_worker *worker =
+ (struct local_dsync_worker *)_worker;
+ struct local_dsync_mailbox *lbox;
+ const char *oldname;
+
+ lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
+ if (lbox == NULL) {
+ i_error("Trying to rename a non-listed mailbox with guid=%s",
+ binary_to_hex(mailbox->guid, sizeof(mailbox->guid)));
+ dsync_worker_set_failure(_worker);
+ return;
+ }
+
+ if (mailbox_list_rename_mailbox(lbox->ns->list, lbox->storage_name,
+ lbox->ns->list, name, TRUE) < 0) {
+ i_error("Can't rename mailbox %s to %s: %s", lbox->storage_name,
+ name, mailbox_list_get_last_error(lbox->ns->list, NULL));
+ dsync_worker_set_failure(_worker);
+ } else {
+ oldname = lbox->storage_name;
+ lbox->storage_name = p_strdup(worker->pool, name);
+ local_worker_rename_children(worker, oldname, name,
+ lbox->ns->sep);
+ }
+}
+
+static void local_worker_mailbox_close(struct local_dsync_worker *worker)
+{
+ struct mailbox_transaction_context *trans, *ext_trans;
+
+ i_assert(worker->save_input == NULL);
if (worker->selected_box != NULL) {
trans = worker->mail->transaction;
+ ext_trans = worker->ext_mail->transaction;
mail_free(&worker->mail);
- if (mailbox_transaction_commit(&trans) < 0)
+ mail_free(&worker->ext_mail);
+ if (mailbox_transaction_commit(&ext_trans) < 0)
+ dsync_worker_set_failure(&worker->worker);
+ if (mailbox_transaction_commit(&trans) < 0 ||
+ mailbox_sync(worker->selected_box,
+ MAILBOX_SYNC_FLAG_FULL_WRITE, 0, NULL) < 0)
dsync_worker_set_failure(&worker->worker);
+
mailbox_close(&worker->selected_box);
}
+ memset(&worker->selected_box_guid, 0,
+ sizeof(worker->selected_box_guid));
}
static void
(struct local_dsync_worker *)_worker;
struct mailbox *box;
struct mailbox_update update;
+ bool selected = FALSE;
- if (worker->selected_box != NULL &&
- dsync_guid_equals(&dsync_box->guid, &worker->selected_box_guid))
- worker_mailbox_close(worker);
+ /* if we're updating a selected mailbox, close it first so that all
+ pending changes get committed. */
+ selected = worker->selected_box != NULL &&
+ dsync_guid_equals(&dsync_box->mailbox_guid,
+ &worker->selected_box_guid);
+ if (selected)
+ local_worker_mailbox_close(worker);
box = local_worker_mailbox_alloc(worker, dsync_box);
if (box == NULL) {
NULL));
}
mailbox_close(&box);
+
+ if (selected)
+ dsync_worker_select_mailbox(_worker, &dsync_box->mailbox_guid);
}
static void
{
struct local_dsync_worker *worker =
(struct local_dsync_worker *)_worker;
- struct mailbox_transaction_context *trans;
+ struct mailbox_transaction_context *trans, *ext_trans;
if (worker->selected_box != NULL) {
if (dsync_guid_equals(&worker->selected_box_guid, mailbox))
return;
- worker_mailbox_close(worker);
+ local_worker_mailbox_close(worker);
}
worker->selected_box_guid = *mailbox;
if (local_mailbox_open(worker, mailbox, &worker->selected_box) < 0)
dsync_worker_set_failure(_worker);
else {
- trans = mailbox_transaction_begin(worker->selected_box,
+ ext_trans = mailbox_transaction_begin(worker->selected_box,
MAILBOX_TRANSACTION_FLAG_EXTERNAL |
MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS);
+ trans = mailbox_transaction_begin(worker->selected_box, 0);
worker->mail = mail_alloc(trans, 0, NULL);
+ worker->ext_mail = mail_alloc(ext_trans, 0, NULL);
}
}
struct local_dsync_worker *worker =
(struct local_dsync_worker *)_worker;
- if (!mail_set_uid(worker->mail, old_uid))
+ if (!mail_set_uid(worker->ext_mail, old_uid))
dsync_worker_set_failure(_worker);
else
- mail_update_uid(worker->mail, new_uid);
+ mail_update_uid(worker->ext_mail, new_uid);
}
static void local_worker_msg_expunge(struct dsync_worker *_worker, uint32_t uid)
if (!mail_set_uid(src_mail, src_uid))
ret = -1;
else {
- save_ctx = mailbox_save_alloc(worker->mail->transaction);
+ save_ctx = mailbox_save_alloc(worker->ext_mail->transaction);
local_worker_msg_save_set_metadata(worker->mail->box,
save_ctx, dest_msg);
ret = mailbox_copy(&save_ctx, src_mail);
callback(ret == 0, context);
}
+static void dsync_worker_try_finish(struct local_dsync_worker *worker)
+{
+ if (worker->finish_callback == NULL)
+ return;
+ if (worker->save_io != NULL || worker->reading_mail)
+ return;
+
+ worker->finish_callback(!worker->worker.failed, worker->finish_context);
+}
+
static void
-local_worker_save_msg_continue(struct local_dsync_worker *worker,
- struct mail_save_context *save_ctx,
- struct istream *input)
+local_worker_save_msg_continue(struct local_dsync_worker *worker)
{
int ret;
- while ((ret = i_stream_read(input)) > 0) {
- if (mailbox_save_continue(save_ctx) < 0)
+ while ((ret = i_stream_read(worker->save_input)) > 0) {
+ if (mailbox_save_continue(worker->save_ctx) < 0)
break;
}
+ if (ret == 0) {
+ if (worker->save_io != NULL)
+ return;
+ worker->save_io =
+ io_add(i_stream_get_fd(worker->save_input), IO_READ,
+ local_worker_save_msg_continue, worker);
+ return;
+ }
i_assert(ret == -1);
- if (input->stream_errno != 0) {
- errno = input->stream_errno;
+ /* drop save_io before destroying save_input, so that save_input's
+ destroy callback can add io back to its fd. */
+ if (worker->save_io != NULL)
+ io_remove(&worker->save_io);
+ if (worker->save_input->stream_errno != 0) {
+ errno = worker->save_input->stream_errno;
i_error("read(msg input) failed: %m");
- mailbox_save_cancel(&save_ctx);
+ mailbox_save_cancel(&worker->save_ctx);
ret = -1;
} else {
- i_assert(input->eof);
- ret = mailbox_save_finish(&save_ctx);
+ i_assert(worker->save_input->eof);
+ ret = mailbox_save_finish(&worker->save_ctx);
}
if (ret < 0)
dsync_worker_set_failure(&worker->worker);
+ i_stream_unref(&worker->save_input);
+ dsync_worker_try_finish(worker);
}
static void
(struct local_dsync_worker *)_worker;
struct mail_save_context *save_ctx;
- save_ctx = mailbox_save_alloc(worker->mail->transaction);
+ i_assert(worker->save_input == NULL);
+
+ save_ctx = mailbox_save_alloc(worker->ext_mail->transaction);
mailbox_save_set_guid(save_ctx, msg->guid);
local_worker_msg_save_set_metadata(worker->mail->box, save_ctx, msg);
mailbox_save_set_pop3_uidl(save_ctx, data->pop3_uidl);
dsync_worker_set_failure(_worker);
return;
}
- local_worker_save_msg_continue(worker, save_ctx, data->input);
+
+ worker->save_input = data->input;
+ worker->save_ctx = save_ctx;
+ i_stream_ref(worker->save_input);
+ local_worker_save_msg_continue(worker);
+}
+
+static void local_worker_msg_save_cancel(struct dsync_worker *_worker)
+{
+ struct local_dsync_worker *worker =
+ (struct local_dsync_worker *)_worker;
+
+ if (worker->save_input == NULL)
+ return;
+
+ if (worker->save_io != NULL)
+ io_remove(&worker->save_io);
+ mailbox_save_cancel(&worker->save_ctx);
+ i_stream_unref(&worker->save_input);
+}
+
+static void local_worker_msg_get_done(struct local_dsync_worker *worker)
+{
+ worker->reading_mail = FALSE;
+ dsync_worker_try_finish(worker);
+}
+
+static void local_worker_msg_box_close(struct local_dsync_worker *worker)
+{
+ struct mailbox_transaction_context *trans;
+ struct mailbox *box;
+
+ if (worker->get_mail == NULL)
+ return;
+
+ box = worker->get_mail->box;
+ trans = worker->get_mail->transaction;
+
+ mail_free(&worker->get_mail);
+ (void)mailbox_transaction_commit(&trans);
+ mailbox_close(&box);
}
static void
-local_worker_msg_get(struct dsync_worker *_worker, uint32_t uid,
+local_worker_msg_get(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox, uint32_t uid,
dsync_worker_msg_callback_t *callback, void *context)
{
struct local_dsync_worker *worker =
(struct local_dsync_worker *)_worker;
struct dsync_msg_static_data data;
+ struct mailbox_transaction_context *trans;
+ struct mailbox *box;
- if (worker->mail == NULL) {
- /* no mailbox is selected */
- callback(DSYNC_MSG_GET_RESULT_FAILED, NULL, context);
- return;
+ i_assert(!worker->reading_mail);
+
+ if (!dsync_guid_equals(&worker->get_mailbox, mailbox)) {
+ local_worker_msg_box_close(worker);
+ if (local_mailbox_open(worker, mailbox, &box) < 0) {
+ callback(DSYNC_MSG_GET_RESULT_FAILED, NULL, context);
+ return;
+ }
+ trans = mailbox_transaction_begin(box, 0);
+ worker->get_mail = mail_alloc(trans, 0, NULL);
}
- if (!mail_set_uid(worker->mail, uid)) {
+ if (!mail_set_uid(worker->get_mail, uid)) {
callback(DSYNC_MSG_GET_RESULT_EXPUNGED, NULL, context);
return;
}
memset(&data, 0, sizeof(data));
- if (mail_get_special(worker->mail, MAIL_FETCH_UIDL_BACKEND,
+ if (mail_get_special(worker->get_mail, MAIL_FETCH_UIDL_BACKEND,
&data.pop3_uidl) < 0 ||
- mail_get_received_date(worker->mail, &data.received_date) < 0 ||
- mail_get_stream(worker->mail, NULL, NULL, &data.input) < 0) {
- if (worker->mail->expunged)
+ mail_get_received_date(worker->get_mail, &data.received_date) < 0 ||
+ mail_get_stream(worker->get_mail, NULL, NULL, &data.input) < 0) {
+ if (worker->get_mail->expunged)
callback(DSYNC_MSG_GET_RESULT_EXPUNGED, NULL, context);
else
callback(DSYNC_MSG_GET_RESULT_FAILED, NULL, context);
} else {
+ worker->reading_mail = TRUE;
+ data.input = i_stream_create_limit(data.input, (uoff_t)-1);
+ i_stream_set_destroy_callback(data.input,
+ local_worker_msg_get_done,
+ worker);
callback(DSYNC_MSG_GET_RESULT_SUCCESS, &data, context);
}
}
+static void
+local_worker_finish(struct dsync_worker *_worker,
+ dsync_worker_finish_callback_t *callback, void *context)
+{
+ struct local_dsync_worker *worker =
+ (struct local_dsync_worker *)_worker;
+
+ worker->finish_callback = callback;
+ worker->finish_context = context;
+
+ dsync_worker_try_finish(worker);
+}
+
struct dsync_worker_vfuncs local_dsync_worker = {
local_worker_deinit,
local_worker_msg_iter_deinit,
local_worker_create_mailbox,
+ local_worker_delete_mailbox,
+ local_worker_rename_mailbox,
local_worker_update_mailbox,
local_worker_select_mailbox,
local_worker_msg_expunge,
local_worker_msg_copy,
local_worker_msg_save,
- local_worker_msg_get
+ local_worker_msg_save_cancel,
+ local_worker_msg_get,
+ local_worker_finish
};
void (*create_mailbox)(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
+ void (*delete_mailbox)(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox);
+ void (*rename_mailbox)(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox, const char *name);
void (*update_mailbox)(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
void (*msg_save)(struct dsync_worker *worker,
const struct dsync_message *msg,
const struct dsync_msg_static_data *data);
- void (*msg_get)(struct dsync_worker *worker, uint32_t uid,
+ void (*msg_save_cancel)(struct dsync_worker *worker);
+ void (*msg_get)(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox, uint32_t uid,
dsync_worker_msg_callback_t *callback, void *context);
+ void (*finish)(struct dsync_worker *worker,
+ dsync_worker_finish_callback_t *callback, void *context);
};
struct dsync_worker {
unsigned int *mailbox_idx_r,
struct dsync_message *msg_r)
{
- return iter->worker->v.msg_iter_next(iter, mailbox_idx_r, msg_r);
+ int ret;
+
+ T_BEGIN {
+ ret = iter->worker->v.msg_iter_next(iter, mailbox_idx_r, msg_r);
+ } T_END;
+ return ret;
}
int dsync_worker_msg_iter_deinit(struct dsync_worker_msg_iter **_iter)
void dsync_worker_create_mailbox(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box)
{
+ i_assert(dsync_box->uid_validity != 0 ||
+ mail_guid_128_is_empty(dsync_box->mailbox_guid.guid));
worker->v.create_mailbox(worker, dsync_box);
}
+void dsync_worker_delete_mailbox(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox)
+{
+ worker->v.delete_mailbox(worker, mailbox);
+}
+
+void dsync_worker_rename_mailbox(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox,
+ const char *name)
+{
+ worker->v.rename_mailbox(worker, mailbox, name);
+}
+
void dsync_worker_update_mailbox(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box)
{
worker->v.msg_save(worker, msg, data);
}
-void dsync_worker_msg_get(struct dsync_worker *worker, uint32_t uid,
+void dsync_worker_msg_save_cancel(struct dsync_worker *worker)
+{
+ worker->v.msg_save_cancel(worker);
+}
+
+void dsync_worker_msg_get(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox, uint32_t uid,
dsync_worker_msg_callback_t *callback, void *context)
{
+ i_assert(uid != 0);
+
if (!worker->failed)
- worker->v.msg_get(worker, uid, callback, context);
+ worker->v.msg_get(worker, mailbox, uid, callback, context);
+}
+
+void dsync_worker_finish(struct dsync_worker *worker,
+ dsync_worker_finish_callback_t *callback,
+ void *context)
+{
+ worker->v.finish(worker, callback, context);
}
void dsync_worker_set_failure(struct dsync_worker *worker)
typedef void dsync_worker_copy_callback_t(bool success, void *context);
typedef void dsync_worker_msg_callback_t(enum dsync_msg_get_result result,
- struct dsync_msg_static_data *data,
+ const struct dsync_msg_static_data *data,
void *context);
+typedef void dsync_worker_finish_callback_t(bool success, void *context);
struct dsync_worker *dsync_worker_init_local(struct mail_user *user);
struct dsync_worker *dsync_worker_init_proxy_client(int fd_in, int fd_out);
/* Create mailbox with given name, GUID and UIDVALIDITY. */
void dsync_worker_create_mailbox(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
-/* Find mailbox with given GUID and make sure its name, uid_next and
+/* Delete mailbox/dir with given GUID. */
+void dsync_worker_delete_mailbox(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox);
+/* Change a mailbox and its childrens' name */
+void dsync_worker_rename_mailbox(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox,
+ const char *name);
+/* Find mailbox with given GUID and make sure its uid_next and
highest_modseq are up to date (but don't shrink them). */
void dsync_worker_update_mailbox(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
void dsync_worker_msg_save(struct dsync_worker *worker,
const struct dsync_message *msg,
const struct dsync_msg_static_data *data);
+/* Cancel any pending saves */
+void dsync_worker_msg_save_cancel(struct dsync_worker *worker);
/* Get message data for saving. The callback is called once when the static
data has been received. The whole message may not have been downloaded yet,
so the caller must read the input stream until it returns EOF and then
unreference it. */
-void dsync_worker_msg_get(struct dsync_worker *worker, uint32_t uid,
+void dsync_worker_msg_get(struct dsync_worker *worker,
+ const mailbox_guid_t *mailbox, uint32_t uid,
dsync_worker_msg_callback_t *callback, void *context);
+/* Call the callback once all the pending commands are finished. */
+void dsync_worker_finish(struct dsync_worker *worker,
+ dsync_worker_finish_callback_t *callback,
+ void *context);
#endif
#include "lib.h"
#include "master-service.h"
#include "mail-storage-service.h"
+#include "mail-user.h"
#include "dsync-brain.h"
#include "dsync-worker.h"
#include "dsync-proxy-server.h"
#include <unistd.h>
static struct dsync_brain *brain;
+static struct dsync_proxy_server *server;
static void run_cmd(const char *cmd, int *fd_in_r, int *fd_out_r)
{
static void ATTR_NORETURN
usage(void)
{
- i_fatal("usage: dsync [-v] [-u <user>] [-e <cmd>] source|dest");
+ i_fatal("usage: dsync [-v] [-u <user>] [-e <cmd>]");
}
static void
{
enum mail_storage_service_flags ssflags =
MAIL_STORAGE_SERVICE_FLAG_NO_CHDIR;
+ enum dsync_brain_flags brain_flags = 0;
struct mail_storage_service_input input;
struct mail_user *mail_user;
struct dsync_worker *worker1, *worker2;
- struct dsync_proxy_server *server;
- const char *getopt_str, *username, *cmd = NULL;
- bool dest = FALSE;
+ const char *getopt_str, *username, *mailbox = NULL, *cmd = NULL;
+ bool dest = TRUE;
int c, ret, fd_in = STDIN_FILENO, fd_out = STDOUT_FILENO;
master_service = master_service_init("dsync",
argc, argv);
username = getenv("USER");
- getopt_str = t_strconcat("e:u:v", master_service_getopt_string(), NULL);
+ getopt_str = t_strconcat("b:e:fu:v",
+ master_service_getopt_string(), NULL);
while ((c = getopt(argc, argv, getopt_str)) > 0) {
if (c == '-')
break;
switch (c) {
+ case 'b':
+ mailbox = optarg;
+ break;
case 'e':
cmd = optarg;
break;
+ case 'f':
+ brain_flags |= DSYNC_BRAIN_FLAG_FULL_SYNC;
+ break;
case 'u':
username = optarg;
ssflags |= MAIL_STORAGE_SERVICE_FLAG_USERDB_LOOKUP;
break;
case 'v':
- ssflags |= MAIL_STORAGE_SERVICE_FLAG_DEBUG;
+ brain_flags |= DSYNC_BRAIN_FLAG_VERBOSE;
break;
default:
if (!master_service_parse_option(master_service,
usage();
}
}
- if (optind == argc)
- usage();
- if (strcmp(argv[optind], "dest") == 0) {
- dest = TRUE;
- optind++;
- } else if (strcmp(argv[optind], "source") == 0) {
- optind++;
- }
if (optind != argc)
usage();
/* user initialization may exec doveconf, so do our forking
after that */
run_cmd(cmd, &fd_in, &fd_out);
+ dest = FALSE;
}
worker1 = dsync_worker_init_local(mail_user);
i_set_failure_prefix(t_strdup_printf("dsync-src(%s): ",
username));
worker2 = dsync_worker_init_proxy_client(fd_in, fd_out);
- brain = dsync_brain_init(worker1, worker2, TRUE);
+ brain = dsync_brain_init(worker1, worker2,
+ mailbox, brain_flags);
+ server = NULL;
dsync_brain_sync(brain);
}
ret = dsync_brain_deinit(&brain);
else
ret = 0;
+ if (server != NULL)
+ dsync_proxy_server_deinit(&server);
+
dsync_worker_deinit(&worker1);
if (worker2 != NULL)
dsync_worker_deinit(&worker2);
- if (server != NULL)
- dsync_proxy_server_deinit(&server);
+ mail_user_unref(&mail_user);
mail_storage_service_deinit_user();
master_service_deinit(&master_service);
return ret < 0 ? 1 : 0;
box = test_box_find(name);
return box != NULL &&
- memcmp(box->box.box.guid.guid, guid->guid,
- sizeof(box->box.box.guid.guid)) == 0;
+ memcmp(box->box.box.mailbox_guid.guid, guid->guid,
+ sizeof(box->box.box.mailbox_guid.guid)) == 0;
}
static struct test_dsync_mailbox *
unsigned char sha[SHA1_RESULTLEN];
struct test_dsync_mailbox *tbox;
struct dsync_mailbox *box;
+ const char *dir_name;
tbox = test_box_find(name);
if (tbox == NULL) {
i_array_init(&tbox->dest_msgs, 16);
}
- sha1_get_digest(name, strlen(name), sha);
-
box = i_new(struct dsync_mailbox, 1);
box->name = i_strdup(name);
- memcpy(box->guid.guid, sha, sizeof(box->guid.guid));
+
+ sha1_get_digest(name, strlen(name), sha);
+ memcpy(box->mailbox_guid.guid, sha, sizeof(box->mailbox_guid.guid));
+
+ dir_name = t_strconcat("dir-", name, NULL);
+ sha1_get_digest(dir_name, strlen(dir_name), sha);
+ memcpy(box->dir_guid.guid, sha, sizeof(box->dir_guid.guid));
+
box->uid_validity = crc32_str(name);
box->highest_modseq = 1;
break;
}
tbox->box.box.name = box->name;
- tbox->box.box.guid = box->guid;
+ tbox->box.box.mailbox_guid = box->mailbox_guid;
+ tbox->box.box.dir_guid = box->dir_guid;
tbox->box.box.uid_validity = box->uid_validity;
return tbox;
}
array_append(&brain_boxes, &tboxes[i].box, 1);
sync = dsync_brain_msg_sync_init(test_dsync_brain_init(), &brain_boxes);
+ dsync_brain_msg_sync_more(sync);
test_dsync_sync_msgs(test_dest_worker, TRUE);
test_dsync_sync_msgs(test_src_worker, FALSE);
return sync;
array_append_array(&sync->mailboxes, mailboxes);
return sync;
}
+void dsync_brain_msg_sync_more(struct dsync_brain_mailbox_sync *sync ATTR_UNUSED) {}
void dsync_brain_msg_sync_deinit(struct dsync_brain_mailbox_sync **_sync)
{
static void mailboxes_set_guids(struct dsync_mailbox *boxes)
{
unsigned char sha[SHA1_RESULTLEN];
+ const char *dir_name;
for (; boxes->name != NULL; boxes++) {
sha1_get_digest(boxes->name, strlen(boxes->name), sha);
- memcpy(boxes->guid.guid, sha, sizeof(boxes->guid.guid));
+ memcpy(boxes->mailbox_guid.guid, sha,
+ sizeof(boxes->mailbox_guid.guid));
+
+ dir_name = t_strconcat("dir-", boxes->name, NULL);
+ sha1_get_digest(dir_name, strlen(dir_name), sha);
+ memcpy(boxes->dir_guid.guid, sha,
+ sizeof(boxes->dir_guid.guid));
}
}
const struct dsync_mailbox *obox)
{
return strcmp(cbox->name, obox->name) == 0 &&
- memcmp(cbox->guid.guid, obox->guid.guid,
- sizeof(cbox->guid.guid)) == 0 &&
+ memcmp(cbox->mailbox_guid.guid, obox->mailbox_guid.guid,
+ sizeof(cbox->mailbox_guid.guid)) == 0 &&
+ memcmp(cbox->dir_guid.guid, obox->dir_guid.guid,
+ sizeof(cbox->dir_guid.guid)) == 0 &&
cbox->uid_validity == obox->uid_validity &&
cbox->uid_next == 0 && cbox->highest_modseq == 0;
}
+static bool
+test_dsync_mailbox_delete_equals(const struct dsync_mailbox *dbox,
+ const struct dsync_mailbox *obox)
+{
+ return memcmp(dbox->mailbox_guid.guid, obox->mailbox_guid.guid,
+ sizeof(dbox->mailbox_guid.guid)) == 0;
+}
+
static void
test_dsync_mailbox_update(const struct dsync_mailbox *bbox,
const struct dsync_mailbox *box)
static void test_dsync_brain(void)
{
static struct dsync_mailbox src_boxes[] = {
- { "box1", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box2", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box3", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box4", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box5", { { 0, } }, 1234567890, 5433, 123123123123ULL },
- { "box6", { { 0, } }, 1234567890, 5432, 123123123124ULL },
- { "boxx", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { NULL, { { 0, } }, 0, 0, 0 }
+ { "box1", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box2", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box3", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box4", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box5", { { 0, } }, { { 0, } }, 1234567890, 5433, 123123123123ULL, 3636, 0 },
+ { "box6", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123124ULL, 3636, 0 },
+ { "boxx", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "boxd1", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "boxd2", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, DSYNC_MAILBOX_FLAG_DELETED_MAILBOX },
+ { NULL, { { 0, } }, { { 0, } }, 0, 0, 0, 0, 0 }
};
static struct dsync_mailbox dest_boxes[] = {
- { "box1", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box2", { { 0, } }, 1234567891, 5432, 123123123123ULL },
- { "box3", { { 0, } }, 1234567890, 5433, 123123123123ULL },
- { "box4", { { 0, } }, 1234567890, 5432, 123123123124ULL },
- { "box5", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "box6", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { "boxy", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { NULL, { { 0, } }, 0, 0, 0 }
+ { "box1", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box2", { { 0, } }, { { 0, } }, 1234567891, 5432, 123123123123ULL, 3636, 0 },
+ { "box3", { { 0, } }, { { 0, } }, 1234567890, 5433, 123123123123ULL, 3636, 0 },
+ { "box4", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123124ULL, 3636, 0 },
+ { "box5", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "box6", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "boxy", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { "boxd1", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, DSYNC_MAILBOX_FLAG_DELETED_MAILBOX },
+ { "boxd2", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 3636, 0 },
+ { NULL, { { 0, } }, { { 0, } }, 0, 0, 0, 0, 0 }
};
struct dsync_brain *brain;
struct dsync_worker *src_worker, *dest_worker;
src_test_worker = (struct test_dsync_worker *)src_worker;
dest_test_worker = (struct test_dsync_worker *)dest_worker;
- brain = dsync_brain_init(src_worker, dest_worker, 0);
+ brain = dsync_brain_init(src_worker, dest_worker, NULL, 0);
dsync_brain_sync(brain);
/* have brain read the mailboxes */
mailboxes_send_to_worker(src_test_worker, src_boxes);
mailboxes_send_to_worker(dest_test_worker, dest_boxes);
- /* check that it created missing mailboxes */
+ /* check that it created/deleted missing mailboxes */
test_assert(test_dsync_worker_next_box_event(dest_test_worker, &box_event));
test_assert(box_event.type == LAST_BOX_TYPE_CREATE);
test_assert(test_dsync_mailbox_create_equals(&box_event.box, &src_boxes[6]));
+ test_assert(test_dsync_worker_next_box_event(dest_test_worker, &box_event));
+ test_assert(box_event.type == LAST_BOX_TYPE_DELETE);
+ test_assert(test_dsync_mailbox_delete_equals(&box_event.box, &dest_boxes[8]));
+
+ test_assert(test_dsync_worker_next_box_event(src_test_worker, &box_event));
+ test_assert(box_event.type == LAST_BOX_TYPE_DELETE);
+ test_assert(test_dsync_mailbox_delete_equals(&box_event.box, &src_boxes[7]));
+
test_assert(test_dsync_worker_next_box_event(src_test_worker, &box_event));
test_assert(box_event.type == LAST_BOX_TYPE_CREATE);
test_assert(test_dsync_mailbox_create_equals(&box_event.box, &dest_boxes[6]));
static void test_dsync_brain_full(void)
{
static struct dsync_mailbox boxes[] = {
- { "box1", { { 0, } }, 1234567890, 5432, 123123123123ULL },
- { NULL, { { 0, } }, 0, 0, 0 }
+ { "box1", { { 0, } }, { { 0, } }, 1234567890, 5432, 123123123123ULL, 2352, 0 },
+ { NULL, { { 0, } }, { { 0, } }, 0, 0, 0, 0, 0 }
};
struct dsync_brain *brain;
struct dsync_worker *src_worker, *dest_worker;
src_test_worker = (struct test_dsync_worker *)src_worker;
dest_test_worker = (struct test_dsync_worker *)dest_worker;
- brain = dsync_brain_init(src_worker, dest_worker,
+ brain = dsync_brain_init(src_worker, dest_worker, NULL,
DSYNC_BRAIN_FLAG_FULL_SYNC);
dsync_brain_sync(brain);
const struct dsync_mailbox *box2)
{
if (strcmp(box1->name, box2->name) != 0 ||
- memcmp(box1->guid.guid, box2->guid.guid, sizeof(box1->guid.guid)) != 0 ||
+ memcmp(box1->dir_guid.guid, box2->dir_guid.guid,
+ sizeof(box1->dir_guid.guid)) != 0 ||
+ memcmp(box1->mailbox_guid.guid, box2->mailbox_guid.guid,
+ sizeof(box1->mailbox_guid.guid)) != 0 ||
box1->uid_validity != box2->uid_validity ||
box1->uid_next != box2->uid_next ||
box1->highest_modseq != box2->highest_modseq)
memcpy(guid_128, sha1_sum, MAIL_GUID_128_SIZE);
}
-bool mail_guid_128_is_empty(const uint8_t guid_128[MAIL_GUID_128_SIZE] ATTR_UNUSED)
+bool mail_guid_128_is_empty(const uint8_t guid_128[MAIL_GUID_128_SIZE])
{
- return FALSE;
+ static uint8_t empty_guid[MAIL_GUID_128_SIZE] = { 0, };
+
+ return memcmp(empty_guid, guid_128, sizeof(empty_guid)) == 0;
}
/* \noselect mailbox */
memset(&box, 0, sizeof(box));
box.name = "\t\001\r\nname\t\001\n\r";
+ box.last_renamed = 992;
+ box.flags = 123;
+ memcpy(box.dir_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE);
test_worker->box_iter.next_box = &box;
test_assert(run_more() == 0);
- test_assert(strcmp(str_c(out), t_strconcat(str_tabescape(box.name), "\n", NULL)) == 0);
+ test_assert(strcmp(str_c(out), t_strconcat(str_tabescape(box.name),
+ "\t"TEST_MAILBOX_GUID1"\t992\t123\n", NULL)) == 0);
out_clear();
/* selectable mailbox */
memset(&box, 0, sizeof(box));
box.name = "foo/bar";
- memcpy(box.guid.guid, test_mailbox_guid1, sizeof(box.guid.guid));
+ memcpy(box.dir_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE);
+ memcpy(box.mailbox_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE);
box.uid_validity = 4275878552;
box.uid_next = 4023233417;
box.highest_modseq = 18080787909545915012ULL;
test_assert(run_more() == 0);
test_assert(strcmp(str_c(out), "foo/bar\t"
+ TEST_MAILBOX_GUID2"\t0\t0\t"
TEST_MAILBOX_GUID1"\t"
"4275878552\t"
"4023233417\t"
test_begin("proxy server box create");
- test_assert(run_cmd("BOX-CREATE", "noselect", NULL) == 1);
+ test_assert(run_cmd("BOX-CREATE", "noselect",
+ TEST_MAILBOX_GUID2, "553", "99", NULL) == 1);
test_assert(test_dsync_worker_next_box_event(test_worker, &event));
test_assert(event.type == LAST_BOX_TYPE_CREATE);
test_assert(strcmp(event.box.name, "noselect") == 0);
+ test_assert(memcmp(event.box.dir_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+ test_assert(event.box.last_renamed == 553);
+ test_assert(event.box.flags == 99);
test_assert(event.box.uid_validity == 0);
test_assert(run_cmd("BOX-CREATE", "selectable", TEST_MAILBOX_GUID1,
- "1234567890", "9876", "28427847284728", NULL) == 1);
+ "61", "2", TEST_MAILBOX_GUID2, "1234567890", "9876",
+ "28427847284728", NULL) == 1);
test_assert(test_dsync_worker_next_box_event(test_worker, &event));
test_assert(event.type == LAST_BOX_TYPE_CREATE);
test_assert(strcmp(event.box.name, "selectable") == 0);
- test_assert(memcmp(event.box.guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE) == 0);
+ test_assert(memcmp(event.box.dir_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE) == 0);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+ test_assert(event.box.flags == 2);
test_assert(event.box.uid_validity == 1234567890);
test_assert(event.box.uid_next == 9876);
test_assert(event.box.highest_modseq == 28427847284728);
+ test_assert(event.box.last_renamed == 61);
+
+ test_end();
+}
+
+static void test_dsync_proxy_box_delete(void)
+{
+ struct test_dsync_box_event event;
+
+ test_begin("proxy server box delete");
+
+ test_assert(run_cmd("BOX-DELETE", TEST_MAILBOX_GUID1, NULL) == 1);
+ test_assert(test_dsync_worker_next_box_event(test_worker, &event));
+ test_assert(event.type == LAST_BOX_TYPE_DELETE);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE) == 0);
+
+ test_assert(run_cmd("BOX-DELETE", TEST_MAILBOX_GUID2, NULL) == 1);
+ test_assert(test_dsync_worker_next_box_event(test_worker, &event));
+ test_assert(event.type == LAST_BOX_TYPE_DELETE);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+
+ test_end();
+}
+
+static void test_dsync_proxy_box_rename(void)
+{
+ struct test_dsync_box_event event;
+
+ test_begin("proxy server box rename");
+
+ test_assert(run_cmd("BOX-RENAME", TEST_MAILBOX_GUID1, "name\t1", NULL) == 1);
+ test_assert(test_dsync_worker_next_box_event(test_worker, &event));
+ test_assert(event.type == LAST_BOX_TYPE_RENAME);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE) == 0);
+ test_assert(strcmp(event.box.name, "name\t1") == 0);
+
+ test_assert(run_cmd("BOX-RENAME", TEST_MAILBOX_GUID2, "", NULL) == 1);
+ test_assert(test_dsync_worker_next_box_event(test_worker, &event));
+ test_assert(event.type == LAST_BOX_TYPE_RENAME);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+ test_assert(strcmp(event.box.name, "") == 0);
test_end();
}
test_begin("proxy server box update");
test_assert(run_cmd("BOX-UPDATE", "updated", TEST_MAILBOX_GUID2,
- "34343", "22", "2238427847284728", NULL) == 1);
+ "53", "9", TEST_MAILBOX_GUID1, "34343", "22",
+ "2238427847284728", NULL) == 1);
test_assert(test_dsync_worker_next_box_event(test_worker, &event));
test_assert(event.type == LAST_BOX_TYPE_UPDATE);
test_assert(strcmp(event.box.name, "updated") == 0);
- test_assert(memcmp(event.box.guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+ test_assert(memcmp(event.box.dir_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE) == 0);
+ test_assert(memcmp(event.box.mailbox_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE) == 0);
+ test_assert(event.box.flags == 9);
test_assert(event.box.uid_validity == 34343);
test_assert(event.box.uid_next == 22);
test_assert(event.box.highest_modseq == 2238427847284728);
+ test_assert(event.box.last_renamed == 53);
test_end();
}
test_dsync_proxy_box_list,
test_dsync_proxy_msg_list,
test_dsync_proxy_box_create,
+ test_dsync_proxy_box_delete,
+ test_dsync_proxy_box_rename,
test_dsync_proxy_box_update,
test_dsync_proxy_box_select,
test_dsync_proxy_msg_update,
pool = pool_alloconly_create("mailbox pool", 1024);
str = t_str_new(256);
- box_in.name = "\t\001\r\nname\t\001\n\r";
test_begin("dsync proxy mailbox");
/* test \noselect mailbox */
+ box_in.name = "\t\001\r\nname\t\001\n\r";
+ box_in.flags = 1234567890;
+ memcpy(box_in.dir_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE);
dsync_proxy_mailbox_export(str, &box_in);
test_assert(dsync_proxy_mailbox_import(pool, str_c(str),
&box_out, &error) == 0);
test_assert(dsync_mailboxes_equal(&box_in, &box_out));
/* real mailbox */
- i_assert(sizeof(box_in.guid.guid) == sizeof(test_mailbox_guid1));
- memcpy(box_in.guid.guid, test_mailbox_guid1, sizeof(box_in.guid.guid));
+ i_assert(sizeof(box_in.mailbox_guid.guid) == sizeof(test_mailbox_guid1));
+ memcpy(box_in.mailbox_guid.guid, test_mailbox_guid2, MAIL_GUID_128_SIZE);
+ memcpy(box_in.dir_guid.guid, test_mailbox_guid1, MAIL_GUID_128_SIZE);
+ box_in.flags = 24242;
box_in.uid_validity = 0xf74d921b;
box_in.uid_next = 73529472;
box_in.highest_modseq = 0x123456789abcdef0ULL;
test_worker_set_last_box(_worker, dsync_box, LAST_BOX_TYPE_CREATE);
}
+static void
+test_worker_delete_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox)
+{
+ struct test_dsync_worker *worker = (struct test_dsync_worker *)_worker;
+ struct test_dsync_box_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = LAST_BOX_TYPE_DELETE;
+
+ event.box.mailbox_guid = *mailbox;
+ event.box.name = "";
+ array_append(&worker->box_events, &event, 1);
+}
+
+static void
+test_worker_rename_mailbox(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox, const char *name)
+{
+ struct test_dsync_worker *worker = (struct test_dsync_worker *)_worker;
+ struct test_dsync_box_event event;
+
+ memset(&event, 0, sizeof(event));
+ event.type = LAST_BOX_TYPE_RENAME;
+
+ event.box.mailbox_guid = *mailbox;
+ event.box.name = name;
+ array_append(&worker->box_events, &event, 1);
+}
+
static void
test_worker_update_mailbox(struct dsync_worker *_worker,
const struct dsync_mailbox *dsync_box)
worker->selected_mailbox = *mailbox;
memset(&box, 0, sizeof(box));
- memcpy(box.guid.guid, mailbox, sizeof(box.guid.guid));
+ memcpy(box.mailbox_guid.guid, mailbox, sizeof(box.mailbox_guid.guid));
}
static struct test_dsync_msg_event *
}
static void
-test_worker_msg_get(struct dsync_worker *_worker, uint32_t uid ATTR_UNUSED,
+test_worker_msg_save_cancel(struct dsync_worker *_worker ATTR_UNUSED)
+{
+}
+
+static void
+test_worker_msg_get(struct dsync_worker *_worker,
+ const mailbox_guid_t *mailbox ATTR_UNUSED,
+ uint32_t uid ATTR_UNUSED,
dsync_worker_msg_callback_t *callback, void *context)
{
struct test_dsync_worker *worker = (struct test_dsync_worker *)_worker;
callback(DSYNC_MSG_GET_RESULT_SUCCESS, &data, context);
}
+static void
+test_worker_finish(struct dsync_worker *_worker ATTR_UNUSED,
+ dsync_worker_finish_callback_t *callback, void *context)
+{
+ callback(TRUE, context);
+}
+
struct dsync_worker_vfuncs test_dsync_worker = {
test_worker_deinit,
test_worker_msg_iter_deinit,
test_worker_create_mailbox,
+ test_worker_delete_mailbox,
+ test_worker_rename_mailbox,
test_worker_update_mailbox,
test_worker_select_mailbox,
test_worker_msg_expunge,
test_worker_msg_copy,
test_worker_msg_save,
- test_worker_msg_get
+ test_worker_msg_save_cancel,
+ test_worker_msg_get,
+ test_worker_finish
};
enum test_dsync_last_box_type {
LAST_BOX_TYPE_CREATE,
+ LAST_BOX_TYPE_DELETE,
+ LAST_BOX_TYPE_RENAME,
LAST_BOX_TYPE_UPDATE
};