static void test_dsync_mailbox_tree_sync_renames1(void)
{
	static const char *common_nodes[] = { "1", "2", "3", "2/s1", "2/s2", "x/y", "3/s3", NULL };
	struct dsync_mailbox_tree *tree1, *tree2;
	struct dsync_mailbox_node *node;
	unsigned int counter = 0;

	test_begin("dsync mailbox tree sync renames 1");
	tree1 = dsync_mailbox_tree_init('/', '_');
	nodes_create(tree1, &counter, common_nodes);
	tree2 = dsync_mailbox_tree_dup(tree1);

	node = dsync_mailbox_tree_get(tree1, "1");
	node->name = "a";
	node->last_renamed_or_created = 1000;
	node = dsync_mailbox_tree_get(tree2, "2");
	node->name = "b";
	node->last_renamed_or_created = 1000;

	node = dsync_mailbox_tree_get(tree1, "3/s3");
	node->name = "z";
	node->last_renamed_or_created = 1000;
	dsync_mailbox_tree_node_detach(node);
	dsync_mailbox_tree_node_attach(node, &tree1->root);

	test_trees(tree1, tree2);
	test_end();
}
static struct dsync_mailbox_node *
node_create(struct dsync_mailbox_tree *tree, unsigned int counter,
	    const char *name, unsigned int last_renamed_or_created)
{
	struct dsync_mailbox_node *node;

	node = dsync_mailbox_tree_get(tree, name);
	memcpy(node->mailbox_guid, &counter, sizeof(counter));
	node->uid_validity = counter;
	node->existence = DSYNC_MAILBOX_NODE_EXISTS;
	node->last_renamed_or_created = last_renamed_or_created;
	return node;
}
bool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain)
{
	const struct dsync_mailbox_node *remote_node;
	struct dsync_mailbox_node *node, *dup_node1, *dup_node2;
	const char *const *parts, *name;
	struct mail_namespace *ns;
	enum dsync_ibc_recv_ret ret;
	char sep[2];
	bool changed = FALSE;

	sep[0] = brain->hierarchy_sep; sep[1] = '\0';
	while ((ret = dsync_ibc_recv_mailbox_tree_node(brain->ibc, &parts,
						       &remote_node)) > 0) {
		if (dsync_get_mailbox_name(brain, parts, &name, &ns) < 0) {
			i_error("Couldn't find namespace for mailbox %s",
				t_strarray_join(parts, sep));
			brain->failed = TRUE;
			return TRUE;
		}
		if (brain->debug) {
			i_debug("brain %c: Remote mailbox tree: %s %s",
				brain->master_brain ? 'M' : 'S',
				t_strarray_join(parts, sep),
				dsync_mailbox_node_to_string(remote_node));
		}
		node = dsync_mailbox_tree_get(brain->remote_mailbox_tree, name);
		node->ns = ns;
		dsync_mailbox_node_copy_data(node, remote_node);
	}
	if (ret != DSYNC_IBC_RECV_RET_FINISHED)
		return changed;

	if (dsync_mailbox_tree_build_guid_hash(brain->remote_mailbox_tree,
					       &dup_node1, &dup_node2) < 0) {
		i_error("Remote sent duplicate mailbox GUID %s for mailboxes %s and %s",
			guid_128_to_string(dup_node1->mailbox_guid),
			dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
							 dup_node1),
			dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
							 dup_node2));
		brain->failed = TRUE;
	}

	brain->state = DSYNC_STATE_RECV_MAILBOX_TREE_DELETES;
	return TRUE;
}
static int
dsync_mailbox_tree_add_node(struct dsync_mailbox_tree *tree,
			    const struct mailbox_info *info,
			    struct dsync_mailbox_node **node_r)
{
	struct dsync_mailbox_node *node;

	node = dsync_mailbox_tree_get(tree, info->vname);
	if (node->ns != info->ns) {
		i_assert(node->ns != NULL);

		i_error("Mailbox '%s' exists in two namespaces: '%s' and '%s'",
			info->vname, node->ns->prefix, info->ns->prefix);
		return -1;
	}
	*node_r = node;
	return 0;
}
Esempio n. 5
0
int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
			    struct mail_namespace *ns, const char *box_name,
			    const guid_128_t box_guid,
			    const char *const *exclude_mailboxes,
			    enum mail_error *error_r)
{
	const enum mailbox_list_iter_flags list_flags =
		/* FIXME: we'll skip symlinks, because we can't handle them
		   currently. in future we could detect them and create them
		   by creating the symlink. */
		MAILBOX_LIST_ITER_SKIP_ALIASES |
		MAILBOX_LIST_ITER_NO_AUTO_BOXES;
	const enum mailbox_list_iter_flags subs_list_flags =
		MAILBOX_LIST_ITER_NO_AUTO_BOXES |
		MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
		MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
	struct mailbox_list_iterate_context *iter;
	struct dsync_mailbox_node *node, *dup_node1, *dup_node2;
	const struct mailbox_info *info;
	const char *list_pattern =
		box_name != NULL && box_name[0] != '\\' ? box_name : "*";
	int ret = 0;

	i_assert(mail_namespace_get_sep(ns) == tree->sep);

	/* assign namespace to its root, so it gets copied to children */
	if (ns->prefix_len > 0) {
		node = dsync_mailbox_tree_get(tree,
			t_strndup(ns->prefix, ns->prefix_len-1));
		node->ns = ns;
	} else {
		tree->root.ns = ns;
	}

	/* first add all of the existing mailboxes */
	iter = mailbox_list_iter_init(ns->list, list_pattern, list_flags);
	while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
		if (dsync_mailbox_info_is_wanted(info, box_name,
						 exclude_mailboxes)) {
			if (dsync_mailbox_tree_add(tree, info, box_guid, error_r) < 0)
				ret = -1;
		}
	} T_END;
	if (mailbox_list_iter_deinit(&iter) < 0) {
		i_error("Mailbox listing for namespace '%s' failed: %s",
			ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r));
		ret = -1;
	}

	/* add subscriptions */
	iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags);
	while ((info = mailbox_list_iter_next(iter)) != NULL) {
		if (dsync_mailbox_tree_add_node(tree, info, &node) == 0)
			node->subscribed = TRUE;
		else {
			*error_r = MAIL_ERROR_TEMP;
			ret = -1;
		}
	}
	if (mailbox_list_iter_deinit(&iter) < 0) {
		i_error("Mailbox listing for namespace '%s' failed: %s",
			ns->prefix, mailbox_list_get_last_internal_error(ns->list, error_r));
		ret = -1;
	}
	if (ret < 0)
		return -1;

	while (dsync_mailbox_tree_build_guid_hash(tree, &dup_node1,
						  &dup_node2) < 0) {
		if (dsync_mailbox_tree_fix_guid_duplicate(tree, dup_node1, dup_node2) < 0)
			return -1;
	}

	/* add timestamps */
	if (dsync_mailbox_tree_add_change_timestamps(tree, ns) < 0)
		return -1;
	return 0;
}
static void
dsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree *tree,
				    struct dsync_mailbox_tree *other_tree,
				    const struct dsync_mailbox_delete *other_del,
				    const struct dsync_mailbox_node **node_r,
				    const char **status_r)
{
	const struct dsync_mailbox_node *node;
	struct dsync_mailbox_node *other_node, *old_node;
	const char *name;

	/* see if we can find the deletion based on mailbox tree that should
	   still have the mailbox */
	node = *node_r = dsync_mailbox_tree_find_delete(tree, other_del);
	if (node == NULL) {
		*status_r = "not found";
		return;
	}

	switch (other_del->type) {
	case DSYNC_MAILBOX_DELETE_TYPE_MAILBOX:
		/* mailbox is always deleted */
		break;
	case DSYNC_MAILBOX_DELETE_TYPE_DIR:
		if (other_del->timestamp <= node->last_renamed_or_created) {
			/* we don't want to delete this directory, we already
			   have a newer timestamp for it */
			*status_r = "keep directory, we have a newer timestamp";
			return;
		}
		break;
	case DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE:
		if (other_del->timestamp <= node->last_subscription_change) {
			/* we don't want to unsubscribe, since we already have
			   a newer subscription timestamp */
			*status_r = "keep subscription, we have a newer timestamp";
			return;
		}
		break;
	}

	/* make a node for it in the other mailbox tree */
	name = dsync_mailbox_node_get_full_name(tree, node);
	other_node = dsync_mailbox_tree_get(other_tree, name);

	if (other_node->existence == DSYNC_MAILBOX_NODE_EXISTS &&
	    (!guid_128_is_empty(other_node->mailbox_guid) ||
	     other_del->type != DSYNC_MAILBOX_DELETE_TYPE_MAILBOX)) {
		/* other side has already created a new mailbox or
		   directory with this name, we can't delete it */
		*status_r = "name has already been recreated";
		return;
	}

	/* ok, mark the other node deleted */
	if (other_del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) {
		memcpy(other_node->mailbox_guid, node->mailbox_guid,
		       sizeof(other_node->mailbox_guid));
	}
	if (other_node->ns != node->ns && other_node->ns != NULL) {
		/* namespace mismatch for this node. this shouldn't happen
		   normally, but especially during some misconfigurations it's
		   possible that one side has created mailboxes that conflict
		   with another namespace's prefix. since we're here because
		   one of the mailboxes was deleted, we'll just ignore this. */
		*status_r = "namespace mismatch";
		return;
	}
	other_node->ns = node->ns;
	if (other_del->type != DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE) {
		other_node->existence = DSYNC_MAILBOX_NODE_DELETED;
		*status_r = "marked as deleted";
	} else {
		other_node->last_subscription_change = other_del->timestamp;
		other_node->subscribed = FALSE;
		*status_r = "marked as unsubscribed";
	}

	if (dsync_mailbox_tree_guid_hash_add(other_tree, other_node,
					     &old_node) < 0)
		i_unreached();
}
Esempio n. 7
0
static void
dsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree *tree,
				    struct dsync_mailbox_tree *other_tree,
				    const struct dsync_mailbox_delete *other_del)
{
	const struct dsync_mailbox_node *node;
	struct dsync_mailbox_node *other_node, *old_node;
	const char *name;

	/* see if we can find the deletion based on mailbox tree that should
	   still have the mailbox */
	node = dsync_mailbox_tree_find_delete(tree, other_del);
	if (node == NULL)
		return;

	switch (other_del->type) {
	case DSYNC_MAILBOX_DELETE_TYPE_MAILBOX:
		/* mailbox is always deleted */
		break;
	case DSYNC_MAILBOX_DELETE_TYPE_DIR:
		if (other_del->timestamp <= node->last_renamed_or_created) {
			/* we don't want to delete this directory, we already
			   have a newer timestamp for it */
			return;
		}
		break;
	case DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE:
		if (other_del->timestamp <= node->last_subscription_change) {
			/* we don't want to unsubscribe, since we already have
			   a newer subscription timestamp */
			return;
		}
		break;
	}

	/* make a node for it in the other mailbox tree */
	name = dsync_mailbox_node_get_full_name(tree, node);
	other_node = dsync_mailbox_tree_get(other_tree, name);

	if (other_node->existence == DSYNC_MAILBOX_NODE_EXISTS &&
	    (!guid_128_is_empty(other_node->mailbox_guid) ||
	     other_del->type != DSYNC_MAILBOX_DELETE_TYPE_MAILBOX)) {
		/* other side has already created a new mailbox or
		   directory with this name, we can't delete it */
		return;
	}

	/* ok, mark the other node deleted */
	if (other_del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) {
		memcpy(other_node->mailbox_guid, node->mailbox_guid,
		       sizeof(other_node->mailbox_guid));
	}
	i_assert(other_node->ns == NULL || other_node->ns == node->ns);
	other_node->ns = node->ns;
	if (other_del->type != DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE)
		other_node->existence = DSYNC_MAILBOX_NODE_DELETED;
	else {
		other_node->last_subscription_change = other_del->timestamp;
		other_node->subscribed = FALSE;
	}

	if (dsync_mailbox_tree_guid_hash_add(other_tree, other_node,
					     &old_node) < 0)
		i_unreached();
}