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_name_cmp);
+ array_sort(&list->dirs, dsync_mailbox_p_name_sha1_cmp);
dsync_brain_mailbox_list_finished(list->brain);
}
}
}
}
+static void dsync_brain_sync_dirs(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;
+
+ memset(&new_box, 0, sizeof(new_box));
+
+ /* create/delete missing directories. */
+ src_boxes = array_get(&brain->src_mailbox_list->dirs, &src_count);
+ dest_boxes = array_get(&brain->dest_mailbox_list->dirs, &dest_count);
+ for (src = dest = 0; src < src_count && dest < dest_count; ) {
+ src_deleted = (src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
+ dest_deleted = (dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
+ ret = memcmp(src_boxes[src]->name_sha1.guid,
+ dest_boxes[dest]->name_sha1.guid,
+ sizeof(src_boxes[src]->name_sha1.guid));
+ if (ret < 0) {
+ /* exists only in source */
+ if (!src_deleted) {
+ new_box = *src_boxes[src];
+ dsync_worker_create_mailbox(brain->dest_worker,
+ &new_box);
+ }
+ src++;
+ } else if (ret > 0) {
+ /* exists only in dest */
+ if (!dest_deleted) {
+ new_box = *dest_boxes[dest];
+ dsync_worker_create_mailbox(brain->src_worker,
+ &new_box);
+ }
+ dest++;
+ } else if (src_deleted) {
+ /* delete from dest too */
+ if (!dest_deleted) {
+ dsync_worker_delete_dir(brain->dest_worker,
+ dest_boxes[dest]);
+ }
+ src++; dest++;
+ } else if (dest_deleted) {
+ /* delete from src too */
+ dsync_worker_delete_dir(brain->src_worker,
+ src_boxes[src]);
+ src++; dest++;
+ } else {
+ src++; dest++;
+ }
+ }
+ for (; src < src_count; src++) {
+ if ((src_boxes[src]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
+ continue;
+
+ new_box = *src_boxes[src];
+ dsync_worker_create_mailbox(brain->dest_worker, &new_box);
+ }
+ for (; dest < dest_count; dest++) {
+ if ((dest_boxes[dest]->flags &
+ DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
+ continue;
+
+ new_box = *dest_boxes[dest];
+ dsync_worker_create_mailbox(brain->src_worker, &new_box);
+ }
+}
+
static bool
dsync_brain_is_unsubscribed(struct dsync_brain_subs_list *list,
const struct dsync_worker_subscription *subs,
break;
case DSYNC_STATE_SYNC_MAILBOXES:
dsync_brain_sync_mailboxes(brain);
+ dsync_brain_sync_dirs(brain);
brain->state++;
/* fall through */
case DSYNC_STATE_SYNC_SUBSCRIPTIONS:
return dsync_mailbox_guid_cmp(*box1, *box2);
}
-int dsync_mailbox_name_cmp(const struct dsync_mailbox *box1,
- const struct dsync_mailbox *box2)
+int dsync_mailbox_name_sha1_cmp(const struct dsync_mailbox *box1,
+ const struct dsync_mailbox *box2)
{
+ int ret;
+
+ ret = memcmp(box1->name_sha1.guid, box2->name_sha1.guid,
+ sizeof(box1->name_sha1.guid));
+ if (ret != 0)
+ return ret;
+
return strcmp(box1->name, box2->name);
}
-int dsync_mailbox_p_name_cmp(struct dsync_mailbox *const *box1,
- struct dsync_mailbox *const *box2)
+int dsync_mailbox_p_name_sha1_cmp(struct dsync_mailbox *const *box1,
+ struct dsync_mailbox *const *box2)
{
- return dsync_mailbox_name_cmp(*box1, *box2);
+ return dsync_mailbox_name_sha1_cmp(*box1, *box2);
}
bool dsync_keyword_list_equals(const char *const *k1, const char *const *k2)
int dsync_mailbox_p_guid_cmp(struct dsync_mailbox *const *box1,
struct dsync_mailbox *const *box2);
-int dsync_mailbox_name_cmp(const struct dsync_mailbox *box1,
- const struct dsync_mailbox *box2);
-int dsync_mailbox_p_name_cmp(struct dsync_mailbox *const *box1,
- struct dsync_mailbox *const *box2);
+int dsync_mailbox_name_sha1_cmp(const struct dsync_mailbox *box1,
+ const struct dsync_mailbox *box2);
+int dsync_mailbox_p_name_sha1_cmp(struct dsync_mailbox *const *box1,
+ struct dsync_mailbox *const *box2);
bool dsync_keyword_list_equals(const char *const *k1, const char *const *k2);
} T_END;
}
+static void
+proxy_client_worker_delete_dir(struct dsync_worker *_worker,
+ const struct dsync_mailbox *dsync_box)
+{
+ 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, "DIR-DELETE\t");
+ str_tabescape_write(str, dsync_box->name);
+ str_printfa(str, "\t%s\n", dec2str(dsync_box->last_change));
+ 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,
proxy_client_worker_create_mailbox,
proxy_client_worker_delete_mailbox,
+ proxy_client_worker_delete_dir,
proxy_client_worker_rename_mailbox,
proxy_client_worker_update_mailbox,
return 1;
}
+static int
+cmd_dir_delete(struct dsync_proxy_server *server, const char *const *args)
+{
+ struct dsync_mailbox dsync_box;
+
+ if (str_array_length(args) < 2)
+ return -1;
+
+ memset(&dsync_box, 0, sizeof(dsync_box));
+ dsync_box.name = str_tabunescape(t_strdup_noconst(args[0]));
+ dsync_box.last_change = strtoul(args[1], NULL, 10);
+ dsync_worker_delete_dir(server->worker, &dsync_box);
+ return 1;
+}
+
static int
cmd_box_rename(struct dsync_proxy_server *server, const char *const *args)
{
{ "MSG-LIST", cmd_msg_list },
{ "BOX-CREATE", cmd_box_create },
{ "BOX-DELETE", cmd_box_delete },
+ { "DIR-DELETE", cmd_dir_delete },
{ "BOX-RENAME", cmd_box_rename },
{ "BOX-UPDATE", cmd_box_update },
{ "BOX-SELECT", cmd_box_select },
dsync_box_r->name = "";
dsync_box_r->name_sha1 = change->name_sha1;
dsync_box_r->last_change = change->last_delete;
- dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_DELETED_DIR;
+ dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_NOSELECT |
+ DSYNC_MAILBOX_FLAG_DELETED_DIR;
return 1;
}
}
struct mailbox_status status;
uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
struct local_dsync_mailbox_change *change;
- struct local_dsync_dir_change *dir_change;
+ struct local_dsync_dir_change *dir_change, change_lookup;
const char *const *fields;
unsigned int i, field_count;
dsync_box_r->name = info->name;
dsync_box_r->name_sep = info->ns->sep;
+ storage_name = mail_namespace_get_storage_name(info->ns, info->name);
+ dsync_str_sha_to_guid(storage_name, &dsync_box_r->name_sha1);
+
/* get last change timestamp */
- dsync_str_sha_to_guid(info->name, &dsync_box_r->name_sha1);
- dir_change = hash_table_lookup(worker->mailbox_changes_hash,
- dsync_box_r->name_sha1.guid);
+ change_lookup.list = info->ns->list;
+ change_lookup.name_sha1 = dsync_box_r->name_sha1;
+ dir_change = hash_table_lookup(worker->dir_changes_hash,
+ &change_lookup);
if (dir_change != NULL) {
/* it shouldn't be marked as deleted, but drop it to be sure */
dir_change->deleted_dir = FALSE;
dsync_box_r->last_change = dir_change->last_rename;
}
- storage_name = mail_namespace_get_storage_name(info->ns, info->name);
if ((info->flags & MAILBOX_NOSELECT) != 0) {
dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_NOSELECT;
local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
mailbox_list_set_changelog_timestamp(lbox->ns->list, (time_t)-1);
}
+static void
+local_worker_delete_dir(struct dsync_worker *_worker,
+ const struct dsync_mailbox *dsync_box)
+{
+ struct local_dsync_worker *worker =
+ (struct local_dsync_worker *)_worker;
+ struct mail_namespace *ns;
+ const char *storage_name;
+
+ storage_name = dsync_box->name;
+ ns = mail_namespace_find(worker->user->namespaces, &storage_name);
+
+ mailbox_list_set_changelog_timestamp(ns->list, dsync_box->last_change);
+ if (mailbox_list_delete_dir(ns->list, storage_name) < 0) {
+ i_error("Can't delete mailbox directory %s: %s",
+ dsync_box->name,
+ mailbox_list_get_last_error(ns->list, NULL));
+ }
+ mailbox_list_set_changelog_timestamp(ns->list, (time_t)-1);
+}
+
static void
local_worker_rename_mailbox(struct dsync_worker *_worker,
const mailbox_guid_t *mailbox,
local_worker_create_mailbox,
local_worker_delete_mailbox,
+ local_worker_delete_dir,
local_worker_rename_mailbox,
local_worker_update_mailbox,
const struct dsync_mailbox *dsync_box);
void (*delete_mailbox)(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
+ void (*delete_dir)(struct dsync_worker *worker,
+ const struct dsync_mailbox *dsync_box);
void (*rename_mailbox)(struct dsync_worker *worker,
const mailbox_guid_t *mailbox,
const struct dsync_mailbox *dsync_box);
} T_END;
}
+void dsync_worker_delete_dir(struct dsync_worker *worker,
+ const struct dsync_mailbox *dsync_box)
+{
+ if (!worker->readonly) T_BEGIN {
+ worker->v.delete_dir(worker, dsync_box);
+ } T_END;
+}
+
void dsync_worker_rename_mailbox(struct dsync_worker *worker,
const mailbox_guid_t *mailbox,
const struct dsync_mailbox *dsync_box)
/* Delete mailbox/dir with given GUID. */
void dsync_worker_delete_mailbox(struct dsync_worker *worker,
const struct dsync_mailbox *dsync_box);
+/* Delete mailbox's directory. Fail if it would also delete mailbox. */
+void dsync_worker_delete_dir(struct dsync_worker *worker,
+ const struct dsync_mailbox *dsync_box);
/* 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,
array_append(&worker->box_events, &event, 1);
}
+static void
+test_worker_delete_dir(struct dsync_worker *_worker,
+ 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_DELETE_DIR;
+
+ event.box = *dsync_box;
+ array_append(&worker->box_events, &event, 1);
+}
+
static void
test_worker_rename_mailbox(struct dsync_worker *_worker,
const mailbox_guid_t *mailbox,
test_worker_create_mailbox,
test_worker_delete_mailbox,
+ test_worker_delete_dir,
test_worker_rename_mailbox,
test_worker_update_mailbox,
enum test_dsync_last_box_type {
LAST_BOX_TYPE_CREATE,
LAST_BOX_TYPE_DELETE,
+ LAST_BOX_TYPE_DELETE_DIR,
LAST_BOX_TYPE_RENAME,
LAST_BOX_TYPE_UPDATE,
LAST_BOX_TYPE_SUBSCRIBE,