bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstatic void dsync_brain_check_namespaces(struct dsync_brain *brain)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen i_fatal("Synced namespaces have conflicting separators "
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen "('%c' for prefix=\"%s\", '%c' for prefix=\"%s\")",
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen i_fatal("All your namespaces have a location setting. "
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen "Only namespaces with empty location settings are converted. "
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen "(One namespace should default to mail_location setting)");
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenvoid dsync_brain_mailbox_trees_init(struct dsync_brain *brain)
0219a05495ca78d0ccc2a4d5dcfcf17aa859481fTimo Sirainen dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* we'll convert remote mailbox names to use our own separator */
0219a05495ca78d0ccc2a4d5dcfcf17aa859481fTimo Sirainen dsync_mailbox_tree_init(brain->hierarchy_sep, brain->alt_char);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* fill the local mailbox tree */
fd32c46c360e61de2c957c3d2241eaacab7b3eecTimo Sirainen for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
fd32c46c360e61de2c957c3d2241eaacab7b3eecTimo Sirainen if (dsync_mailbox_tree_fill(brain->local_mailbox_tree, ns,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_mailbox_tree_iter_init(brain->local_mailbox_tree);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenvoid dsync_brain_send_mailbox_tree(struct dsync_brain *brain)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen while (dsync_mailbox_tree_iter_next(brain->local_tree_iter,
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen i_debug("brain %c: Local mailbox tree: %s %s",
a85473f7c11c8734bdee9c2cbe4b767f144a18aaTimo Sirainen ret = dsync_ibc_send_mailbox_tree_node(brain->ibc,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
b8e6e314eb2f9f1fc8ce2999034321bfeb7a2269Timo Sirainen dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_TREE);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen brain->state = DSYNC_STATE_SEND_MAILBOX_TREE_DELETES;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenvoid dsync_brain_send_mailbox_tree_deletes(struct dsync_brain *brain)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree,
a85473f7c11c8734bdee9c2cbe4b767f144a18aaTimo Sirainen dsync_ibc_send_mailbox_deletes(brain->ibc, deletes, count,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainendsync_namespace_match_parts(struct mail_namespace *ns,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char *const *name_parts)
ec66a68735096e81df73176231b49179222ad9ceTimo Sirainen if ((ns->flags & NAMESPACE_FLAG_INBOX_USER) != 0 &&
ec66a68735096e81df73176231b49179222ad9ceTimo Sirainen strcmp(name_parts[0], "INBOX") == 0 && name_parts[1] == NULL)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (; *name_parts != NULL && *prefix != '\0'; name_parts++) {
e16052742970235960af359ce62515b9127c16fdTimo Sirainen /* namespace prefix found with a mailbox */
e16052742970235960af359ce62515b9127c16fdTimo Sirainen /* namespace prefix itself matched */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainendsync_find_namespace(struct dsync_brain *brain, const char *const *name_parts)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* prefix="" is the fallback namespace */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen } else if (dsync_namespace_match_parts(ns, name_parts)) {
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainendsync_is_valid_name(struct mail_namespace *ns, const char *vname)
f771d4d1fd4a4df3271d86f9be00c2fabaa99348Timo Sirainen box = mailbox_alloc(ns->list, vname, MAILBOX_FLAG_READONLY);
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainendsync_fix_mailbox_name(struct mail_namespace *ns, string_t *vname_str,
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainen char *vname, list_sep = mailbox_list_get_hierarchy_sep(ns->list);
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainen if (strncmp(vname, ns->prefix, ns->prefix_len) == 0)
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* replace control chars */
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* make it valid UTF8 */
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen if (uni_utf8_get_valid_data((const void *)old_vname,
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* 1) change any real separators to alt separators (this wouldn't
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen be necessary with listescape, but don't bother detecting it) */
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* 2) '/' characters aren't valid without listescape */
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen if (mail_namespace_get_sep(ns) != '/' && list_sep != '/') {
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* 3) probably some reserved name (e.g. dbox-Mails) */
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainen if (dsync_is_valid_name(ns, str_c(vname_str)))
e9df0f285429d2b896ecdb4cd873d25e5e895620Timo Sirainen /* 4) name is too long? just give up and generate a unique name */
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainen str_append(vname_str, guid_128_to_string(guid));
a46162e640e660638596a91032905ab9cb27bf04Timo Sirainen i_assert(dsync_is_valid_name(ns, str_c(vname_str)));
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainendsync_get_mailbox_name(struct dsync_brain *brain, const char *const *name_parts,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char **name_r, struct mail_namespace **ns_r)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const char *p;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* build the mailbox name */
0219a05495ca78d0ccc2a4d5dcfcf17aa859481fTimo Sirainen dsync_fix_mailbox_name(ns, vname, brain->alt_char);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenstatic void dsync_brain_mailbox_trees_sync(struct dsync_brain *brain)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen const struct dsync_mailbox_tree_sync_change *change;
be2bc5677a16a3547d5cbfd21c9deedc96a759adTimo Sirainen enum dsync_mailbox_trees_sync_flags sync_flags =
be2bc5677a16a3547d5cbfd21c9deedc96a759adTimo Sirainen (brain->debug ? DSYNC_MAILBOX_TREES_SYNC_FLAG_DEBUG : 0) |
1a1d00fd04bfcf8436b00b58d527e46b23523c9dTimo Sirainen (brain->master_brain ? DSYNC_MAILBOX_TREES_SYNC_FLAG_MASTER_BRAIN : 0) |
1a1d00fd04bfcf8436b00b58d527e46b23523c9dTimo Sirainen (brain->no_mailbox_renames ? DSYNC_MAILBOX_TREES_SYNC_FLAG_NO_RENAMES : 0);
36723cf206a7b64b9d972ab0719bbfaacc9316faTimo Sirainen sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY;
e83126866761632b437e532dfdc30be01d14039dTimo Sirainen sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_LOCAL;
e83126866761632b437e532dfdc30be01d14039dTimo Sirainen sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_PRESERVE_REMOTE;
e83126866761632b437e532dfdc30be01d14039dTimo Sirainen sync_type = DSYNC_MAILBOX_TREES_SYNC_TYPE_TWOWAY;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen ctx = dsync_mailbox_trees_sync_init(brain->local_mailbox_tree,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen while ((change = dsync_mailbox_trees_sync_next(ctx)) != NULL) {
ce0e25f26d6e67480ee39b5ca0ad634fa60c4605Timo Sirainen if (dsync_brain_mailbox_tree_sync_change(brain, change,
34b0579f1542471947564bf56cfcf51946f57313Timo Sirainen if (dsync_mailbox_trees_sync_deinit(&ctx) < 0)
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenbool dsync_brain_recv_mailbox_tree(struct dsync_brain *brain)
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen struct dsync_mailbox_node *node, *dup_node1, *dup_node2;
a85473f7c11c8734bdee9c2cbe4b767f144a18aaTimo Sirainen while ((ret = dsync_ibc_recv_mailbox_tree_node(brain->ibc, &parts,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen if (dsync_get_mailbox_name(brain, parts, &name, &ns) < 0) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen i_error("Couldn't find namespace for mailbox %s",
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen i_debug("brain %c: Remote mailbox tree: %s %s",
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen node = dsync_mailbox_tree_get(brain->remote_mailbox_tree, name);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_mailbox_node_copy_data(node, remote_node);
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen if (dsync_mailbox_tree_build_guid_hash(brain->remote_mailbox_tree,
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen i_error("Remote sent duplicate mailbox GUID %s for mailboxes %s and %s",
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen dsync_mailbox_node_get_full_name(brain->remote_mailbox_tree,
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen brain->state = DSYNC_STATE_RECV_MAILBOX_TREE_DELETES;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainendsync_brain_mailbox_tree_add_delete(struct dsync_mailbox_tree *tree,
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen struct dsync_mailbox_node *other_node, *old_node;
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* see if we can find the deletion based on mailbox tree that should
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen still have the mailbox */
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen node = *node_r = dsync_mailbox_tree_find_delete(tree, other_del);
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen /* mailbox is always deleted */
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen if (other_del->timestamp <= node->last_renamed_or_created) {
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen /* we don't want to delete this directory, we already
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen have a newer timestamp for it */
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen *status_r = "keep directory, we have a newer timestamp";
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen if (other_del->timestamp <= node->last_subscription_change) {
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen /* we don't want to unsubscribe, since we already have
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen a newer subscription timestamp */
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen *status_r = "keep subscription, we have a newer timestamp";
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* make a node for it in the other mailbox tree */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen name = dsync_mailbox_node_get_full_name(tree, node);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen other_node = dsync_mailbox_tree_get(other_tree, name);
f5d3670e7e1c0da30a106ee8adef74afec657907Timo Sirainen if (other_node->existence == DSYNC_MAILBOX_NODE_EXISTS &&
f5d3670e7e1c0da30a106ee8adef74afec657907Timo Sirainen (!guid_128_is_empty(other_node->mailbox_guid) ||
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen other_del->type != DSYNC_MAILBOX_DELETE_TYPE_MAILBOX)) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* other side has already created a new mailbox or
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen directory with this name, we can't delete it */
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen *status_r = "name has already been recreated";
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* ok, mark the other node deleted */
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen if (other_del->type == DSYNC_MAILBOX_DELETE_TYPE_MAILBOX) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen memcpy(other_node->mailbox_guid, node->mailbox_guid,
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen if (other_node->ns != node->ns && other_node->ns != NULL) {
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen /* namespace mismatch for this node. this shouldn't happen
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen normally, but especially during some misconfigurations it's
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen possible that one side has created mailboxes that conflict
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen with another namespace's prefix. since we're here because
22c646fc87e87f33008f55220d60961f98c9eb3eTimo Sirainen one of the mailboxes was deleted, we'll just ignore this. */
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen if (other_del->type != DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE) {
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen other_node->existence = DSYNC_MAILBOX_NODE_DELETED;
985acc0cfd9184b3f4f4cfd6b9e5686a65226147Timo Sirainen other_node->last_subscription_change = other_del->timestamp;
e3df4d9063a06e0cd228a1713677ec105b0a4aa2Timo Sirainen if (dsync_mailbox_tree_guid_hash_add(other_tree, other_node,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainenbool dsync_brain_recv_mailbox_tree_deletes(struct dsync_brain *brain)
a85473f7c11c8734bdee9c2cbe4b767f144a18aaTimo Sirainen if (dsync_ibc_recv_mailbox_deletes(brain->ibc, &deletes, &count,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* apply remote's mailbox deletions based on our local tree */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_mailbox_tree_set_remote_sep(brain->local_mailbox_tree, sep);
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (i = 0; i < count; i++) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_brain_mailbox_tree_add_delete(brain->local_mailbox_tree,
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen dsync_mailbox_node_get_full_name(brain->local_mailbox_tree, node);
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen i_debug("brain %c: Remote mailbox tree deletion: guid=%s type=%s timestamp=%ld name=%s local update=%s",
299451d50b891c83f4f5d921bc22715f24094236Timo Sirainen dsync_mailbox_delete_type_to_string(deletes[i].type),
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen /* apply local mailbox deletions based on remote tree */
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen deletes = dsync_mailbox_tree_get_deletes(brain->local_mailbox_tree,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_mailbox_tree_set_remote_sep(brain->remote_mailbox_tree,
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen for (i = 0; i < count; i++) {
7bd5b1c64cc987715bdaf8cc4907c3c37d5d7b29Timo Sirainen dsync_brain_mailbox_tree_add_delete(brain->remote_mailbox_tree,