]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dsync: Lots of updates and fixes.
authorTimo Sirainen <tss@iki.fi>
Thu, 6 Aug 2009 00:30:43 +0000 (20:30 -0400)
committerTimo Sirainen <tss@iki.fi>
Thu, 6 Aug 2009 00:30:43 +0000 (20:30 -0400)
--HG--
branch : HEAD

25 files changed:
src/dsync/dsync-brain-msgs-new.c
src/dsync/dsync-brain-msgs.c
src/dsync/dsync-brain-private.h
src/dsync/dsync-brain.c
src/dsync/dsync-brain.h
src/dsync/dsync-data.c
src/dsync/dsync-data.h
src/dsync/dsync-proxy-client.c
src/dsync/dsync-proxy-server-cmd.c
src/dsync/dsync-proxy-server.c
src/dsync/dsync-proxy-server.h
src/dsync/dsync-proxy.c
src/dsync/dsync-proxy.h
src/dsync/dsync-worker-local.c
src/dsync/dsync-worker-private.h
src/dsync/dsync-worker.c
src/dsync/dsync-worker.h
src/dsync/dsync.c
src/dsync/test-dsync-brain-msgs.c
src/dsync/test-dsync-brain.c
src/dsync/test-dsync-common.c
src/dsync/test-dsync-proxy-server-cmd.c
src/dsync/test-dsync-proxy.c
src/dsync/test-dsync-worker.c
src/dsync/test-dsync-worker.h

index 86ac196f6662662b134d33e16b59b07eb9e5f642..c11023ea6f4ea02404f45160c444c9a89319d6fa 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "lib.h"
 #include "array.h"
+#include "istream.h"
 #include "hash.h"
 #include "dsync-worker.h"
 #include "dsync-brain-private.h"
@@ -13,42 +14,36 @@ struct dsync_brain_msg_copy_context {
 
 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)
@@ -57,7 +52,6 @@ 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);
@@ -66,17 +60,15 @@ static void dsync_brain_copy_callback(bool success, void *context)
                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;
@@ -84,10 +76,9 @@ dsync_brain_msg_sync_add_new_msg(struct dsync_brain_msg_iter *dest_iter,
        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);
 
@@ -96,9 +87,10 @@ dsync_brain_msg_sync_add_new_msg(struct dsync_brain_msg_iter *dest_iter,
                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 ?
@@ -108,79 +100,78 @@ dsync_brain_msg_sync_add_new_msg(struct dsync_brain_msg_iter *dest_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;
@@ -208,37 +199,120 @@ dsync_brain_msg_iter_sync_retry_copies(struct dsync_brain_msg_iter *iter)
 
        /* 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
@@ -252,7 +326,8 @@ sync_iter_resolve_uid_conflicts(struct dsync_brain_msg_iter *iter)
        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);
        }
index 0495b48c1d7065042f5199623bd789358d1e691e..0d0f672cd5a945943c2d969ab81a115c42f48e1f 100644 (file)
@@ -63,6 +63,7 @@ dsync_brain_msg_sync_save(struct dsync_brain_msg_iter *iter,
 
        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);
 }
 
@@ -87,6 +88,7 @@ dsync_brain_msg_sync_conflict(struct dsync_brain_msg_iter *conflict_iter,
 
        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;
 }
@@ -210,11 +212,8 @@ static int dsync_brain_msg_sync_pair(struct dsync_brain_mailbox_sync *sync)
 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);
@@ -262,7 +261,7 @@ static void dsync_brain_msg_sync_finish(struct dsync_brain_mailbox_sync *sync)
        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;
@@ -272,14 +271,14 @@ static void dsync_brain_msg_sync_more(struct dsync_brain_mailbox_sync *sync)
                /* 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);
        }
 }
 
@@ -314,7 +313,8 @@ dsync_brain_msg_iter_init(struct dsync_brain_mailbox_sync *sync,
                                        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;
 }
 
@@ -339,7 +339,7 @@ get_mailbox_guids(const ARRAY_TYPE(dsync_brain_mailbox) *mailboxes,
        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 *
@@ -347,8 +347,6 @@ dsync_brain_msg_sync_init(struct dsync_brain *brain,
                          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);
@@ -360,17 +358,22 @@ dsync_brain_msg_sync_init(struct dsync_brain *brain,
 
        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;
 }
 
index 828abaa16c952791c2f0334ddc45a327e1fef26f..248e2256e8924a8e7277185bfdccc62c836205ab 100644 (file)
@@ -6,11 +6,11 @@
 
 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
 };
 
@@ -20,6 +20,7 @@ struct dsync_brain_mailbox_list {
        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 {
@@ -42,9 +43,9 @@ struct dsync_brain_msg_iter {
        /* 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;
@@ -52,6 +53,7 @@ struct dsync_brain_msg_iter {
        unsigned int save_results_left;
 
        unsigned int msgs_sent:1;
+       unsigned int adding_msgs:1;
 };
 
 struct dsync_brain_uid_conflict {
@@ -61,6 +63,7 @@ struct dsync_brain_uid_conflict {
 
 struct dsync_brain_new_msg {
        uint32_t mailbox_idx;
+       uint32_t orig_uid;
        struct dsync_message *msg;
 };
 
@@ -90,6 +93,7 @@ struct dsync_brain_mailbox_sync {
 struct dsync_brain {
        struct dsync_worker *src_worker;
        struct dsync_worker *dest_worker;
+       char *mailbox;
        enum dsync_brain_flags flags;
 
        enum dsync_state state;
@@ -100,6 +104,7 @@ struct dsync_brain {
        struct dsync_brain_mailbox_sync *mailbox_sync;
 
        unsigned int failed:1;
+       unsigned int verbose:1;
 };
 
 void dsync_brain_fail(struct dsync_brain *brain);
@@ -107,6 +112,7 @@ 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);
index 4fbdab2a5ae38ff3f2200528cc3f1fc08093d953..583642088d5c666e16371c4bb79e5b3298b2aa1c 100644 (file)
 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;
 }
 
@@ -34,6 +37,16 @@ int dsync_brain_deinit(struct dsync_brain **_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)
@@ -42,6 +55,7 @@ int dsync_brain_deinit(struct dsync_brain **_brain)
                dsync_brain_mailbox_list_deinit(&brain->dest_mailbox_list);
 
        *_brain = NULL;
+       i_free(brain->mailbox);
        i_free(brain);
        return ret;
 }
@@ -65,14 +79,22 @@ static void dsync_worker_mailbox_input(void *context)
 
        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);
        }
 }
@@ -91,6 +113,7 @@ dsync_brain_mailbox_list_init(struct dsync_brain *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;
@@ -108,49 +131,77 @@ dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **_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;
@@ -158,12 +209,34 @@ static void dsync_brain_create_missing_mailboxes(struct dsync_brain *brain)
        }
 }
 
-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
@@ -174,17 +247,25 @@ dsync_brain_get_changed_mailboxes(struct dsync_brain *brain,
        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];
 
@@ -200,27 +281,51 @@ dsync_brain_get_changed_mailboxes(struct dsync_brain *brain,
                        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);
        }
 }
 
@@ -232,6 +337,7 @@ static void dsync_brain_sync_msgs(struct dsync_brain *brain)
        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
@@ -240,6 +346,8 @@ dsync_brain_msg_sync_update_mailbox(struct dsync_brain *brain)
        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,
@@ -249,16 +357,13 @@ dsync_brain_msg_sync_update_mailbox(struct dsync_brain *brain)
        }
 }
 
-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);
 }
@@ -275,17 +380,8 @@ void dsync_brain_sync(struct dsync_brain *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:
@@ -295,21 +391,18 @@ void dsync_brain_sync(struct dsync_brain *brain)
                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();
        }
 }
index 896979e03a6f0475a61a106a2f15bad740255fbd..5dc153721f6f426aca04b70f0ccdf2fc035fb7b1 100644 (file)
@@ -2,14 +2,16 @@
 #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);
index 39ff6fe7ca382dae365731c30cfd3bca4800994b..fc32ead3a207e2fc8e7e9c2409a13e094b6862ff 100644 (file)
@@ -39,19 +39,8 @@ dsync_message_dup(pool_t pool, const struct dsync_message *msg)
 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,
@@ -60,6 +49,19 @@ 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;
index 64bbdd9b35bbe837c6b595495ce31b8c8454130c..dd69acece77626326b0cf093a6b3d3cfdb44c3f2 100644 (file)
@@ -8,12 +8,23 @@ typedef struct {
 } 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 *);
 
@@ -49,6 +60,11 @@ int dsync_mailbox_guid_cmp(const struct dsync_mailbox *box1,
 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,
index 67492a6ece9cb5cc0b0b480da9acfd7fd5ad610f..38c7b9e802e7e9644ddefd64547d24717401710d 100644 (file)
@@ -20,7 +20,8 @@
 
 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 {
@@ -28,6 +29,7 @@ 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;
 };
@@ -54,6 +56,8 @@ struct proxy_client_dsync_worker {
        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;
@@ -77,7 +81,8 @@ proxy_client_worker_read_line(struct proxy_client_dsync_worker *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;
                }
@@ -88,6 +93,8 @@ proxy_client_worker_read_line(struct proxy_client_dsync_worker *worker,
 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);
@@ -126,6 +133,7 @@ proxy_client_worker_next_msg_get(struct proxy_client_dsync_worker *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':
@@ -138,11 +146,17 @@ proxy_client_worker_next_msg_get(struct proxy_client_dsync_worker *worker,
                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)
@@ -162,6 +176,10 @@ proxy_client_worker_next_reply(struct proxy_client_dsync_worker *worker,
        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;
 }
@@ -169,16 +187,21 @@ proxy_client_worker_next_reply(struct proxy_client_dsync_worker *worker,
 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)
@@ -231,7 +254,8 @@ static void proxy_client_worker_deinit(struct 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)
@@ -251,6 +275,11 @@ static bool proxy_client_worker_is_output_full(struct dsync_worker *_worker)
        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;
 }
@@ -433,16 +462,55 @@ proxy_client_worker_create_mailbox(struct dsync_worker *_worker,
        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;
 }
@@ -454,16 +522,14 @@ proxy_client_worker_update_mailbox(struct dsync_worker *_worker,
        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;
 }
@@ -475,6 +541,8 @@ proxy_client_worker_select_mailbox(struct dsync_worker *_worker,
        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;
@@ -496,6 +564,8 @@ proxy_client_worker_msg_update_metadata(struct dsync_worker *_worker,
        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);
 
@@ -514,6 +584,8 @@ proxy_client_worker_msg_update_uid(struct dsync_worker *_worker,
        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",
@@ -527,6 +599,8 @@ proxy_client_worker_msg_expunge(struct dsync_worker *_worker, uint32_t uid)
        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));
@@ -545,6 +619,8 @@ proxy_client_worker_msg_copy(struct dsync_worker *_worker,
                (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);
 
@@ -616,6 +692,8 @@ proxy_client_worker_msg_save(struct dsync_worker *_worker,
        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);
 
@@ -628,7 +706,6 @@ proxy_client_worker_msg_save(struct dsync_worker *_worker,
        } 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);
@@ -636,7 +713,20 @@ proxy_client_worker_msg_save(struct dsync_worker *_worker,
 }
 
 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)
 {
@@ -644,10 +734,14 @@ proxy_client_worker_msg_get(struct dsync_worker *_worker, uint32_t 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);
 
-               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;
 
@@ -658,6 +752,27 @@ proxy_client_worker_msg_get(struct dsync_worker *_worker, uint32_t uid,
        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,
 
@@ -673,6 +788,8 @@ struct dsync_worker_vfuncs proxy_client_dsync_worker = {
        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,
@@ -681,5 +798,7 @@ struct dsync_worker_vfuncs proxy_client_dsync_worker = {
        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
 };
index 7ebe19ee3fb92c948231a8620782783afd59643a..debb1279e266a5dda596639b11a77514774118ec 100644 (file)
@@ -67,7 +67,7 @@ cmd_msg_list_init(struct dsync_proxy_server *server, const char *const *args)
        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) {
@@ -118,46 +118,49 @@ cmd_msg_list(struct dsync_proxy_server *server, const char *const *args)
 }
 
 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;
 }
 
@@ -165,9 +168,14 @@ static int
 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;
 }
@@ -302,6 +310,7 @@ static void cmd_msg_get_send_more(struct dsync_proxy_server *server)
                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 {
@@ -318,7 +327,7 @@ static void cmd_msg_get_send_more(struct dsync_proxy_server *server)
 
 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;
@@ -327,15 +336,15 @@ cmd_msg_get_callback(enum dsync_msg_get_result result,
        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));
@@ -348,22 +357,54 @@ cmd_msg_get_callback(enum dsync_msg_get_result result,
 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 },
@@ -372,6 +413,7 @@ static struct dsync_proxy_server_command commands[] = {
        { "MSG-COPY", cmd_msg_copy },
        { "MSG-SAVE", cmd_msg_save },
        { "MSG-GET", cmd_msg_get },
+       { "FINISH", cmd_finish },
        { NULL, NULL }
 };
 
index 3ba58b7b80fd237d0ea50f6940bb4a6ac690fd54..069b2f629b10845215b100405b206c1632c4a037 100644 (file)
@@ -25,6 +25,8 @@ proxy_server_read_line(struct dsync_proxy_server *server,
                        return -1;
                }
                if (server->input->eof) {
+                       if (!server->finished)
+                               i_error("read() from proxy client failed: EOF");
                        master_service_stop(master_service);
                        return -1;
                }
@@ -154,6 +156,8 @@ void dsync_proxy_server_deinit(struct dsync_proxy_server **_server)
 
        *_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);
index 0dee220a46162f97bcc6506feb7392b9bfe0e33d..d18ea0e35a39df311e63db598ae096461a9c5a43 100644 (file)
@@ -26,6 +26,8 @@ struct dsync_proxy_server {
 
        struct istream *get_input;
        bool get_input_last_lf;
+
+       unsigned int finished:1;
 };
 
 struct dsync_proxy_server *
index 7043978c77b79d8a4741bf05b58c18b80f8ad2db..2847feb71230116950bdb2da98dba7ed831d4a9b 100644 (file)
@@ -140,65 +140,79 @@ void dsync_proxy_mailbox_export(string_t *str,
                                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;
@@ -210,12 +224,16 @@ int dsync_proxy_mailbox_import(pool_t pool, const char *str,
                               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;
 }
index cbdb5f2711b7b24dff37c120a73bb7c9bd04ae89..a367c79aa56f213436dc6afc11e56e75f3d51a48 100644 (file)
@@ -29,6 +29,9 @@ void dsync_proxy_mailbox_export(string_t *str, const struct dsync_mailbox *box);
 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);
index 814da419e2dfaf9d70592bac665b9d282b49d0db..fcb08390a8e6d46763e0477a546c406c308c1e82 100644 (file)
@@ -5,7 +5,9 @@
 #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"
@@ -15,6 +17,7 @@
 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 {
@@ -37,6 +40,14 @@ struct local_dsync_mailbox {
        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;
@@ -44,15 +55,30 @@ struct local_dsync_worker {
        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)
 {
@@ -98,8 +124,13 @@ static void local_worker_deinit(struct dsync_worker *_worker)
        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);
 }
 
@@ -113,6 +144,92 @@ static int local_worker_output_flush(struct dsync_worker *worker ATTR_UNUSED)
        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)
 {
@@ -130,6 +247,7 @@ 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;
 }
 
@@ -149,6 +267,40 @@ local_dsync_worker_add_mailbox(struct local_dsync_worker *worker,
        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)
@@ -163,18 +315,39 @@ local_worker_mailbox_iter_next(struct dsync_worker_mailbox_iter *_iter,
        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);
@@ -189,14 +362,21 @@ local_worker_mailbox_iter_next(struct dsync_worker_mailbox_iter *_iter,
        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;
 }
@@ -315,7 +495,8 @@ local_worker_msg_iter_init(struct dsync_worker *worker,
 
        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],
@@ -449,7 +630,7 @@ local_worker_copy_mailbox_update(const struct dsync_mailbox *dsync_box,
                                 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;
@@ -505,22 +686,113 @@ local_worker_create_mailbox(struct dsync_worker *_worker,
                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
@@ -531,10 +803,15 @@ local_worker_update_mailbox(struct dsync_worker *_worker,
                (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) {
@@ -550,6 +827,9 @@ local_worker_update_mailbox(struct dsync_worker *_worker,
                                                    NULL));
        }
        mailbox_close(&box);
+
+       if (selected)
+               dsync_worker_select_mailbox(_worker, &dsync_box->mailbox_guid);
 }
 
 static void
@@ -558,22 +838,24 @@ local_worker_select_mailbox(struct dsync_worker *_worker,
 {
        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);
        }
 }
 
@@ -605,10 +887,10 @@ local_worker_msg_update_uid(struct dsync_worker *_worker,
        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)
@@ -661,7 +943,7 @@ local_worker_msg_copy(struct dsync_worker *_worker,
        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);
@@ -674,30 +956,52 @@ local_worker_msg_copy(struct dsync_worker *_worker,
        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
@@ -709,7 +1013,9 @@ local_worker_msg_save(struct dsync_worker *_worker,
                (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);
@@ -720,42 +1026,109 @@ local_worker_msg_save(struct dsync_worker *_worker,
                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,
 
@@ -771,6 +1144,8 @@ struct dsync_worker_vfuncs local_dsync_worker = {
        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,
@@ -779,5 +1154,7 @@ struct dsync_worker_vfuncs local_dsync_worker = {
        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
 };
index aea6055504a16899570e926c3b07bdae55076eab..3de5ca30605fc1d8e4bd5729c458a67695598e19 100644 (file)
@@ -28,6 +28,10 @@ struct dsync_worker_vfuncs {
 
        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);
 
@@ -45,8 +49,12 @@ struct dsync_worker_vfuncs {
        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 {
index 997ef2b56f61c63b02a2ee887a986392059b6f89..42292bedca9ef511fc8634cc99f662804beea2fb 100644 (file)
@@ -67,7 +67,12 @@ int dsync_worker_msg_iter_next(struct dsync_worker_msg_iter *iter,
                               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)
@@ -81,9 +86,24 @@ 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)
 {
@@ -136,11 +156,26 @@ void dsync_worker_msg_save(struct dsync_worker *worker,
                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)
index 4e1b441754ccbd94a5589e733a3a15be8985e257..5cbfcaa74bc47bacf37b5ca76cef5c230f35fb9e 100644 (file)
@@ -12,8 +12,9 @@ enum dsync_msg_get_result {
 
 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);
@@ -64,7 +65,14 @@ int dsync_worker_msg_iter_deinit(struct dsync_worker_msg_iter **iter);
 /* 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);
@@ -91,11 +99,18 @@ void dsync_worker_msg_copy(struct dsync_worker *worker,
 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
index 83b7ad967ef9464605cfee251ac9d53d57a6d18a..597b590b45d755e82eb23da56cac2752f1c29f93 100644 (file)
@@ -3,6 +3,7 @@
 #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"
@@ -11,6 +12,7 @@
 #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)
 {
@@ -53,7 +55,7 @@ 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
@@ -66,12 +68,12 @@ int main(int argc, char *argv[])
 {
        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",
@@ -80,20 +82,27 @@ int main(int argc, char *argv[])
                                             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,
@@ -101,14 +110,6 @@ int main(int argc, char *argv[])
                                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();
 
@@ -121,6 +122,7 @@ int main(int argc, char *argv[])
                /* 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);
@@ -133,7 +135,9 @@ int main(int argc, char *argv[])
                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);
        }
 
@@ -143,12 +147,14 @@ int main(int argc, char *argv[])
                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;
index 8dc520a2c0bbd1b40a02c28d220a7ade2cf42c95..ee60fe877f98c8036abc3d861e9a623ffdc465f5 100644 (file)
@@ -47,8 +47,8 @@ test_box_has_guid(const char *name, const mailbox_guid_t *guid)
 
        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 *
@@ -57,6 +57,7 @@ test_box_add(enum test_box_add_type type, const char *name)
        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) {
@@ -65,11 +66,16 @@ test_box_add(enum test_box_add_type type, const char *name)
                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;
 
@@ -86,7 +92,8 @@ test_box_add(enum test_box_add_type type, const char *name)
                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;
 }
@@ -297,6 +304,7 @@ test_dsync_brain_sync_init(void)
                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;
index c20ec73f4b7797478dda5cdbdf0f89ea9d19c1a8..127c704881867c1c428129add67e1f0913a50d45 100644 (file)
@@ -27,6 +27,7 @@ dsync_brain_msg_sync_init(struct dsync_brain *brain,
        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)
 {
@@ -39,10 +40,17 @@ void dsync_brain_msg_sync_resolve_uid_conflicts(struct dsync_brain_mailbox_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));
        }
 }
 
@@ -64,12 +72,22 @@ test_dsync_mailbox_create_equals(const struct dsync_mailbox *cbox,
                                 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)
@@ -96,24 +114,28 @@ dsync_brain_mailbox_name_cmp(const struct dsync_brain_mailbox *box1,
 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;
@@ -131,18 +153,26 @@ static void test_dsync_brain(void)
        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]));
@@ -189,8 +219,8 @@ static void test_dsync_brain(void)
 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;
@@ -207,7 +237,7 @@ static void test_dsync_brain_full(void)
        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);
 
index 92f6660b8af3798b5872095d707793e5982364e5..4cd3b409875a167f1cda8ab7e1c5354a985dcc47 100644 (file)
@@ -43,7 +43,10 @@ bool dsync_mailboxes_equal(const struct dsync_mailbox *box1,
                           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)
@@ -60,7 +63,9 @@ void mail_generate_guid_128_hash(const char *guid,
        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;
 }
index 46ec132fd30f5aaeeda9138adc198a0921a503a6..e213c0350764fb896c2160f4e4ef37baace6c083 100644 (file)
@@ -70,15 +70,20 @@ static void test_dsync_proxy_box_list(void)
        /* \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;
@@ -87,6 +92,7 @@ static void test_dsync_proxy_box_list(void)
        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"
@@ -159,21 +165,69 @@ static void test_dsync_proxy_box_create(void)
 
        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();
 }
@@ -185,14 +239,18 @@ static void test_dsync_proxy_box_update(void)
        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();
 }
@@ -339,6 +397,8 @@ int main(void)
                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,
index dd673df2dd9d3c2065e5f23f5bf503a819e24696..cbdcf65d84db2daa3f7febab56d2dc503de05732 100644 (file)
@@ -94,19 +94,23 @@ static void test_dsync_proxy_mailbox(void)
 
        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;
index 6d684717059766b2e8dc6aadaa424ddadec37d21..1fd0ef238f52c85b6832bce4000a1d82085fa82f 100644 (file)
@@ -166,6 +166,36 @@ test_worker_create_mailbox(struct dsync_worker *_worker,
        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)
@@ -183,7 +213,7 @@ test_worker_select_mailbox(struct dsync_worker *_worker,
        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 *
@@ -300,7 +330,14 @@ test_worker_msg_save(struct dsync_worker *_worker,
 }
 
 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;
@@ -314,6 +351,13 @@ test_worker_msg_get(struct dsync_worker *_worker, uint32_t uid ATTR_UNUSED,
        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,
 
@@ -329,6 +373,8 @@ struct dsync_worker_vfuncs test_dsync_worker = {
        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,
@@ -337,5 +383,7 @@ struct dsync_worker_vfuncs test_dsync_worker = {
        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
 };
index bf285758e2304f6d8042ed261b5be6f205020aad..ebd29c7c8e9a2708837747eb1cfb2609fb8a1f95 100644 (file)
@@ -5,6 +5,8 @@
 
 enum test_dsync_last_box_type {
        LAST_BOX_TYPE_CREATE,
+       LAST_BOX_TYPE_DELETE,
+       LAST_BOX_TYPE_RENAME,
        LAST_BOX_TYPE_UPDATE
 };