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);
}
}
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);
}
}
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);
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;
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;
}
#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;
/* 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;
}
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);
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) {
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);
}
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;
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) {
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);
}
}
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;
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);
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,
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);
/* 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,
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",
break;
switch (c) {
case 'A':
- alt_hierarchy_char = optarg[0];
+ alt_char = optarg[0];
break;
case 'b':
mailbox = optarg;
}
/* 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. */
&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,
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();
}
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;
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);
}