]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
dsync: Fixed problems with syncing mailbox names that are invalid on other side.
authorTimo Sirainen <tss@iki.fi>
Thu, 19 Nov 2009 22:28:48 +0000 (17:28 -0500)
committerTimo Sirainen <tss@iki.fi>
Thu, 19 Nov 2009 22:28:48 +0000 (17:28 -0500)
--HG--
branch : HEAD

src/dsync/dsync-brain.c
src/dsync/dsync-proxy-client.c
src/dsync/dsync-proxy-server-cmd.c
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-proxy-server-cmd.c
src/dsync/test-dsync-worker.c

index 3af5fba70b0346d7016d52f756be89de32cd563c..df9ead81cc2aba8bb7c4bee90d9744b9d824d84d 100644 (file)
@@ -535,11 +535,11 @@ dsync_brain_sync_rename_mailbox(struct dsync_brain *brain,
        if (mailbox->src->last_renamed > mailbox->dest->last_renamed) {
                dsync_worker_rename_mailbox(brain->dest_worker,
                                            &mailbox->box.mailbox_guid,
-                                           mailbox->src->name);
+                                           mailbox->src);
        } else {
                dsync_worker_rename_mailbox(brain->src_worker,
                                            &mailbox->box.mailbox_guid,
-                                           mailbox->dest->name);
+                                           mailbox->dest);
        }
 }
 
@@ -549,12 +549,12 @@ dsync_brain_sync_update_mailboxes(struct dsync_brain *brain)
        const struct dsync_brain_mailbox *mailbox;
 
        array_foreach(&brain->mailbox_sync->mailboxes, mailbox) {
+               dsync_worker_update_mailbox(brain->src_worker, &mailbox->box);
+               dsync_worker_update_mailbox(brain->dest_worker, &mailbox->box);
+
                if (mailbox->src != NULL && mailbox->dest != NULL &&
                    strcmp(mailbox->src->name, mailbox->dest->name) != 0)
                        dsync_brain_sync_rename_mailbox(brain, mailbox);
-
-               dsync_worker_update_mailbox(brain->src_worker, &mailbox->box);
-               dsync_worker_update_mailbox(brain->dest_worker, &mailbox->box);
        }
 }
 
index b7bc19e2257d87859e38a86466e5390d426681b2..b9012b4dd479703324b0bf720cfc0c9949c10a78 100644 (file)
@@ -698,10 +698,11 @@ proxy_client_worker_delete_mailbox(struct dsync_worker *_worker,
 static void
 proxy_client_worker_rename_mailbox(struct dsync_worker *_worker,
                                   const mailbox_guid_t *mailbox,
-                                  const char *name)
+                                  const struct dsync_mailbox *dsync_box)
 {
        struct proxy_client_dsync_worker *worker =
                (struct proxy_client_dsync_worker *)_worker;
+       char sep[2];
 
        i_assert(worker->save_input == NULL);
 
@@ -711,7 +712,10 @@ proxy_client_worker_rename_mailbox(struct dsync_worker *_worker,
                str_append(str, "BOX-RENAME\t");
                dsync_proxy_mailbox_guid_export(str, mailbox);
                str_append_c(str, '\t');
-               str_tabescape_write(str, name);
+               str_tabescape_write(str, dsync_box->name);
+               str_append_c(str, '\t');
+               sep[0] = dsync_box->name_sep; sep[1] = '\0';
+               str_tabescape_write(str, sep);
                str_append_c(str, '\n');
                o_stream_send(worker->output, str_data(str), str_len(str));
        } T_END;
index e25eec38fa8008fe030a1c728c60d62b4cdda21d..47260ded6a3e8d354c8f958a93eadfa261f92216 100644 (file)
@@ -259,15 +259,19 @@ static int
 cmd_box_rename(struct dsync_proxy_server *server, const char *const *args)
 {
        mailbox_guid_t guid;
+       struct dsync_mailbox dsync_box;
 
-       if (str_array_length(args) < 2)
+       if (str_array_length(args) < 3)
                return -1;
        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]);
+       memset(&dsync_box, 0, sizeof(dsync_box));
+       dsync_box.name = args[1];
+       dsync_box.name_sep = args[2][0];
+       dsync_worker_rename_mailbox(server->worker, &guid, &dsync_box);
        return 1;
 }
 
index e057f4c8830dae4708f847ba6977e6215b3e5562..bd1bc8009c1932d6a81c403488131500423134a8 100644 (file)
@@ -14,6 +14,8 @@
 #include "mail-search-build.h"
 #include "dsync-worker-private.h"
 
+#include <ctype.h>
+
 struct local_dsync_worker_mailbox_iter {
        struct dsync_worker_mailbox_iter iter;
        struct mailbox_list_iterate_context *list_iter;
@@ -71,7 +73,7 @@ struct local_dsync_worker {
        /* mailbox_guid_t -> struct local_dsync_subscription_change */
        struct hash_table *subscription_changes_hash;
 
-       char alt_hierarchy_char;
+       char alt_char;
 
        mailbox_guid_t selected_box_guid;
        struct mailbox *selected_box;
@@ -121,17 +123,24 @@ static unsigned int mailbox_guid_hash(const void *p)
 }
 
 struct dsync_worker *
-dsync_worker_init_local(struct mail_user *user, char alt_hierarchy_char)
+dsync_worker_init_local(struct mail_user *user, char alt_char)
 {
        struct local_dsync_worker *worker;
+       struct mail_namespace *ns;
        pool_t pool;
 
+       /* whatever we do, we do it because we're trying to sync,
+          not because of a user action. don't log these mailbox list changes
+          so we don't do wrong decisions on future syncs. */
+       for (ns = user->namespaces; ns != NULL; ns = ns->next)
+               mailbox_list_set_changelog_writable(ns->list, FALSE);
+
        pool = pool_alloconly_create("local dsync worker", 10240);
        worker = p_new(pool, struct local_dsync_worker, 1);
        worker->worker.v = local_dsync_worker;
        worker->user = user;
        worker->pool = pool;
-       worker->alt_hierarchy_char = alt_hierarchy_char;
+       worker->alt_char = alt_char;
        worker->mailbox_hash =
                hash_table_create(default_pool, pool, 0,
                                  mailbox_guid_hash, mailbox_guid_cmp);
@@ -873,21 +882,84 @@ mailbox_name_convert(struct local_dsync_worker *worker,
 
        dest_name = t_strdup_noconst(name);
        for (p = dest_name; *p != '\0'; p++) {
-               if (*p == dest_sep && worker->alt_hierarchy_char != '\0')
-                       *p = worker->alt_hierarchy_char;
+               if (*p == dest_sep && worker->alt_char != '\0')
+                       *p = worker->alt_char;
                else if (*p == src_sep)
                        *p = dest_sep;
        }
        return dest_name;
 }
 
+static const char *
+mailbox_name_cleanup(const char *input, char real_sep, char alt_char)
+{
+       char *output, *p;
+
+       output = t_strdup_noconst(input);
+       for (p = output; *p != '\0'; p++) {
+               if (*p == real_sep || (uint8_t)*input < 32 ||
+                   (uint8_t)*input >= 0x80)
+                       *p = alt_char;
+       }
+       return output;
+}
+
+static const char *mailbox_name_force_cleanup(const char *input, char alt_char)
+{
+       char *output, *p;
+
+       output = t_strdup_noconst(input);
+       for (p = output; *p != '\0'; p++) {
+               if (!i_isalnum(*p))
+                       *p = alt_char;
+       }
+       return output;
+}
+
+static const char *
+local_worker_convert_mailbox_name(struct local_dsync_worker *worker,
+                                 const char *name, struct mail_namespace *ns,
+                                 const struct dsync_mailbox *dsync_box,
+                                 bool creating)
+{
+       if (dsync_box->name_sep != ns->sep) {
+               /* mailbox names use different separators. convert them. */
+               name = mailbox_name_convert(worker, name,
+                                           dsync_box->name_sep, ns->sep);
+       }
+       if (creating) {
+               if (!mailbox_list_is_valid_create_name(ns->list, name)) {
+                       /* change any real separators to alt separators,
+                          drop any potentially invalid characters */
+                       name = mailbox_name_cleanup(name, ns->real_sep,
+                                                   worker->alt_char);
+               }
+               if (!mailbox_list_is_valid_create_name(ns->list, name)) {
+                       /* still not working, apparently it's not valid mUTF-7.
+                          just drop all non-alphanumeric characters. */
+                       name = mailbox_name_force_cleanup(name,
+                                                         worker->alt_char);
+               }
+       }
+       return name;
+}
+
 static struct mailbox *
 local_worker_mailbox_alloc(struct local_dsync_worker *worker,
-                          const struct dsync_mailbox *dsync_box)
+                          const struct dsync_mailbox *dsync_box, bool creating)
 {
        struct mail_namespace *ns;
+       struct local_dsync_mailbox *lbox;
        const char *name;
 
+       lbox = hash_table_lookup(worker->mailbox_hash,
+                                &dsync_box->mailbox_guid);
+       if (lbox != NULL) {
+               /* use the existing known mailbox name */
+               return mailbox_alloc(lbox->ns->list, lbox->storage_name,
+                                    NULL, 0);
+       }
+
        name = dsync_box->name;
        ns = mail_namespace_find(worker->user->namespaces, &name);
        if (ns == NULL) {
@@ -895,11 +967,10 @@ local_worker_mailbox_alloc(struct local_dsync_worker *worker,
                return NULL;
        }
 
-       if (dsync_box->name_sep != ns->sep) {
-               /* mailbox names use different separators. convert them. */
-               name = mailbox_name_convert(worker, name,
-                                           dsync_box->name_sep, ns->sep);
-       }
+       name = local_worker_convert_mailbox_name(worker, name, ns,
+                                                dsync_box, creating);
+       local_dsync_worker_add_mailbox(worker, ns, name,
+                                      &dsync_box->mailbox_guid);
        return mailbox_alloc(ns->list, name, NULL, 0);
 }
 
@@ -913,7 +984,7 @@ local_worker_create_mailbox(struct dsync_worker *_worker,
        struct mailbox_update update;
        int ret;
 
-       box = local_worker_mailbox_alloc(worker, dsync_box);
+       box = local_worker_mailbox_alloc(worker, dsync_box, TRUE);
        if (box == NULL) {
                dsync_worker_set_failure(_worker);
                return;
@@ -989,12 +1060,14 @@ local_worker_rename_children(struct local_dsync_worker *worker,
 
 static void
 local_worker_rename_mailbox(struct dsync_worker *_worker,
-                           const mailbox_guid_t *mailbox, const char *name)
+                           const mailbox_guid_t *mailbox,
+                           const struct dsync_mailbox *dsync_box)
 {
        struct local_dsync_worker *worker =
                (struct local_dsync_worker *)_worker;
        struct local_dsync_mailbox *lbox;
-       const char *oldname;
+       struct mailbox_list *list;
+       const char *oldname, *newname;
 
        lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
        if (lbox == NULL) {
@@ -1004,15 +1077,24 @@ local_worker_rename_mailbox(struct dsync_worker *_worker,
                return;
        }
 
-       if (mailbox_list_rename_mailbox(lbox->ns->list, lbox->storage_name,
-                                       lbox->ns->list, name, TRUE) < 0) {
+       list = lbox->ns->list;
+       newname = local_worker_convert_mailbox_name(worker, dsync_box->name,
+                                                   lbox->ns, dsync_box, TRUE);
+       if (strcmp(lbox->storage_name, newname) == 0) {
+               /* nothing changed after all. probably because some characters
+                  in mailbox name weren't valid. */
+               return;
+       }
+
+       if (mailbox_list_rename_mailbox(list, lbox->storage_name,
+                                       list, newname, 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));
+                       newname, mailbox_list_get_last_error(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->storage_name = p_strdup(worker->pool, newname);
+               local_worker_rename_children(worker, oldname, newname,
                                             lbox->ns->sep);
        }
 }
@@ -1091,7 +1173,7 @@ local_worker_update_mailbox(struct dsync_worker *_worker,
        if (selected)
                local_worker_mailbox_close(worker);
 
-       box = local_worker_mailbox_alloc(worker, dsync_box);
+       box = local_worker_mailbox_alloc(worker, dsync_box, FALSE);
        if (box == NULL) {
                dsync_worker_set_failure(_worker);
                return;
index c190e1dab70ab8d7ecbfceea1dd97a770b6d87d3..caa58fb874cbc40787fe832d0a0f94b8ab5c804a 100644 (file)
@@ -41,7 +41,8 @@ struct dsync_worker_vfuncs {
        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);
+                              const mailbox_guid_t *mailbox,
+                              const struct dsync_mailbox *dsync_box);
        void (*update_mailbox)(struct dsync_worker *worker,
                               const struct dsync_mailbox *dsync_box);
 
index a27946460ea3e76b8803782a8c0b30864f0c8cd7..90dea7968f3cbac0f899f0eb08501a7b6f001b63 100644 (file)
@@ -140,10 +140,10 @@ void dsync_worker_delete_mailbox(struct dsync_worker *worker,
 
 void dsync_worker_rename_mailbox(struct dsync_worker *worker,
                                 const mailbox_guid_t *mailbox,
-                                const char *name)
+                                const struct dsync_mailbox *dsync_box)
 {
        if (!worker->readonly)
-               worker->v.rename_mailbox(worker, mailbox, name);
+               worker->v.rename_mailbox(worker, mailbox, dsync_box);
 }
 
 void dsync_worker_update_mailbox(struct dsync_worker *worker,
index 7fa2989b70bef63037cff5f2b6f17f4cb4051eec..8add5e953bb822be790e3c38467150bce529d536 100644 (file)
@@ -29,7 +29,7 @@ typedef void dsync_worker_msg_callback_t(enum dsync_msg_get_result result,
 typedef void dsync_worker_finish_callback_t(bool success, void *context);
 
 struct dsync_worker *
-dsync_worker_init_local(struct mail_user *user, char alt_hierarchy_char);
+dsync_worker_init_local(struct mail_user *user, char alt_char);
 struct dsync_worker *dsync_worker_init_proxy_client(int fd_in, int fd_out);
 void dsync_worker_deinit(struct dsync_worker **worker);
 
@@ -100,10 +100,11 @@ void dsync_worker_create_mailbox(struct dsync_worker *worker,
 /* 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 */
+/* Change a mailbox and its childrens' name. The name is taken from the given
+   dsync_box (applying name_sep if necessary). */
 void dsync_worker_rename_mailbox(struct dsync_worker *worker,
                                 const mailbox_guid_t *mailbox,
-                                const char *name);
+                                const struct dsync_mailbox *dsync_box);
 /* 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,
index f383ca6896136122614e87b408ddfe0b8482a95c..a9523873215686bc43bbc4a112c7f533887efa5d 100644 (file)
@@ -85,7 +85,7 @@ int main(int argc, char *argv[])
        const char *error, *username, *mailbox = NULL, *mirror_cmd = NULL;
        const char *convert_location = NULL;
        bool dsync_server = FALSE, readonly = FALSE, unexpected_changes = FALSE;
-       char alt_hierarchy_char = '_';
+       char alt_char = '_';
        int c, ret, fd_in = STDIN_FILENO, fd_out = STDOUT_FILENO;
 
        master_service = master_service_init("dsync",
@@ -99,7 +99,7 @@ int main(int argc, char *argv[])
                        break;
                switch (c) {
                case 'A':
-                       alt_hierarchy_char = optarg[0];
+                       alt_char = optarg[0];
                        break;
                case 'b':
                        mailbox = optarg;
@@ -159,7 +159,7 @@ int main(int argc, char *argv[])
        }
 
        /* create the first local worker */
-       worker1 = dsync_worker_init_local(mail_user, alt_hierarchy_char);
+       worker1 = dsync_worker_init_local(mail_user, alt_char);
        if (convert_location != NULL) {
                /* update mail_location and create another user for the
                   second location. */
@@ -174,8 +174,7 @@ int main(int argc, char *argv[])
                                              &mail_user2, &error) < 0)
                        i_fatal("User init failed: %s", error);
 
-               worker2 = dsync_worker_init_local(mail_user2,
-                                                 alt_hierarchy_char);
+               worker2 = dsync_worker_init_local(mail_user2, alt_char);
 
                i_set_failure_prefix(t_strdup_printf("dsync(%s): ", username));
                brain = dsync_brain_init(worker1, worker2,
index 11ed2f83a6d2fd796f00e6ea676cebddc036a952..a46624550a5980e65a6866252bc4f9957ec848a7 100644 (file)
@@ -274,17 +274,19 @@ static void test_dsync_proxy_box_rename(void)
 
        test_begin("proxy server box rename");
 
-       test_assert(run_cmd("BOX-RENAME", TEST_MAILBOX_GUID1, "name\t1", NULL) == 1);
+       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(event.box.name_sep == '/');
 
-       test_assert(run_cmd("BOX-RENAME", TEST_MAILBOX_GUID2, "", NULL) == 1);
+       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_assert(event.box.name_sep == '?');
 
        test_end();
 }
index d83cd0689c93c31258117c8c356f3140a4646f3f..a0f1dbfbc7cb77ecd2b030ab0d28590f0908dcaf 100644 (file)
@@ -247,7 +247,8 @@ test_worker_delete_mailbox(struct dsync_worker *_worker,
 
 static void
 test_worker_rename_mailbox(struct dsync_worker *_worker,
-                          const mailbox_guid_t *mailbox, const char *name)
+                          const mailbox_guid_t *mailbox,
+                          const struct dsync_mailbox *dsync_box)
 {
        struct test_dsync_worker *worker = (struct test_dsync_worker *)_worker;
        struct test_dsync_box_event event;
@@ -255,8 +256,8 @@ test_worker_rename_mailbox(struct dsync_worker *_worker,
        memset(&event, 0, sizeof(event));
        event.type = LAST_BOX_TYPE_RENAME;
 
+       event.box = *dsync_box;
        event.box.mailbox_guid = *mailbox;
-       event.box.name = name;
        array_append(&worker->box_events, &event, 1);
 }