1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
8
#include "wildcard-match.h"
9
#include "mailbox-log.h"
10
#include "mail-namespace.h"
11
#include "mail-storage.h"
12
#include "mailbox-list-iter.h"
13
#include "dsync-mailbox-tree-private.h"
16
dsync_mailbox_tree_add_node(struct dsync_mailbox_tree *tree,
17
const struct mailbox_info *info,
18
struct dsync_mailbox_node **node_r)
20
struct dsync_mailbox_node *node;
22
node = dsync_mailbox_tree_get(tree, info->vname);
23
if (node->ns != info->ns) {
24
i_assert(node->ns != NULL);
26
i_error("Mailbox '%s' exists in two namespaces: '%s' and '%s'",
27
info->vname, node->ns->prefix, info->ns->prefix);
35
dsync_mailbox_tree_add_exists_node(struct dsync_mailbox_tree *tree,
36
const struct mailbox_info *info,
37
struct dsync_mailbox_node **node_r)
39
if (dsync_mailbox_tree_add_node(tree, info, node_r) < 0)
41
(*node_r)->existence = DSYNC_MAILBOX_NODE_EXISTS;
46
dsync_mailbox_tree_get_selectable(struct mailbox *box,
47
struct mailbox_metadata *metadata_r,
48
struct mailbox_status *status_r)
50
/* try the fast path */
51
if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, metadata_r) < 0)
53
if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
56
i_assert(!guid_128_is_empty(metadata_r->guid));
57
if (status_r->uidvalidity != 0)
60
/* no UIDVALIDITY assigned yet. syncing a mailbox should add it. */
61
if (mailbox_sync(box, 0) < 0)
63
if (mailbox_get_status(box, STATUS_UIDVALIDITY | STATUS_UIDNEXT, status_r) < 0)
65
i_assert(status_r->uidvalidity != 0);
69
static int dsync_mailbox_tree_add(struct dsync_mailbox_tree *tree,
70
const struct mailbox_info *info,
71
const guid_128_t box_guid)
73
struct dsync_mailbox_node *node;
75
struct mailbox_metadata metadata;
76
struct mailbox_status status;
78
enum mail_error error;
81
if ((info->flags & MAILBOX_NONEXISTENT) != 0)
83
if ((info->flags & MAILBOX_NOSELECT) != 0) {
84
return !guid_128_is_empty(box_guid) ? 0 :
85
dsync_mailbox_tree_add_exists_node(tree, info, &node);
88
/* get GUID and UIDVALIDITY for selectable mailbox */
89
box = mailbox_alloc(info->ns->list, info->vname, 0);
90
if (dsync_mailbox_tree_get_selectable(box, &metadata, &status) < 0) {
91
errstr = mailbox_get_last_error(box, &error);
93
case MAIL_ERROR_NOTFOUND:
94
/* mailbox was just deleted? */
96
case MAIL_ERROR_NOTPOSSIBLE:
97
/* invalid mbox files? ignore */
100
i_error("Failed to access mailbox %s: %s",
101
info->vname, errstr);
109
if (!guid_128_is_empty(box_guid) &&
110
!guid_128_equals(box_guid, metadata.guid)) {
111
/* unwanted mailbox */
114
if (dsync_mailbox_tree_add_exists_node(tree, info, &node) < 0)
116
memcpy(node->mailbox_guid, metadata.guid,
117
sizeof(node->mailbox_guid));
118
node->uid_validity = status.uidvalidity;
119
node->uid_next = status.uidnext;
123
static struct dsync_mailbox_node *
124
dsync_mailbox_tree_find_sha(struct dsync_mailbox_tree *tree,
125
struct mail_namespace *ns, const guid_128_t sha128)
127
struct dsync_mailbox_node *node;
129
if (!hash_table_is_created(tree->name128_hash))
130
dsync_mailbox_tree_build_name128_hash(tree);
132
node = hash_table_lookup(tree->name128_hash, sha128);
133
return node == NULL || node->ns != ns ? NULL : node;
137
dsync_mailbox_tree_add_change_timestamps(struct dsync_mailbox_tree *tree,
138
struct mail_namespace *ns)
140
struct dsync_mailbox_node *node;
141
struct dsync_mailbox_delete *del;
142
struct mailbox_log *log;
143
struct mailbox_log_iter *iter;
144
const struct mailbox_log_record *rec;
145
const uint8_t *guid_p;
148
log = mailbox_list_get_changelog(ns->list);
152
iter = mailbox_log_iter_init(log);
153
while ((rec = mailbox_log_iter_next(iter)) != NULL) {
154
node = rec->type == MAILBOX_LOG_RECORD_DELETE_MAILBOX ? NULL :
155
dsync_mailbox_tree_find_sha(tree, ns, rec->mailbox_guid);
157
timestamp = mailbox_log_record_get_timestamp(rec);
159
case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
160
guid_p = rec->mailbox_guid;
161
if (hash_table_lookup(tree->guid_hash, guid_p) != NULL) {
162
/* mailbox still exists. maybe it was restored
163
from backup or something. */
166
del = array_append_space(&tree->deletes);
167
del->type = DSYNC_MAILBOX_DELETE_TYPE_MAILBOX;
168
del->timestamp = timestamp;
169
memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
171
case MAILBOX_LOG_RECORD_DELETE_DIR:
173
/* directory exists again, skip it */
176
/* we don't know what directory name was deleted,
177
just its hash. if the name still exists on the other
178
dsync side, it can match this deletion to the
180
del = array_append_space(&tree->deletes);
181
del->type = DSYNC_MAILBOX_DELETE_TYPE_DIR;
182
del->timestamp = timestamp;
183
memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
185
case MAILBOX_LOG_RECORD_CREATE_DIR:
187
/* directory has been deleted again, skip it */
190
/* notify the remote that we want to keep this
191
directory created (unless remote has a newer delete
193
node->last_renamed_or_created = timestamp;
195
case MAILBOX_LOG_RECORD_RENAME:
197
node->last_renamed_or_created = timestamp;
199
case MAILBOX_LOG_RECORD_SUBSCRIBE:
201
node->last_subscription_change = timestamp;
203
case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
205
node->last_subscription_change = timestamp;
208
/* The mailbox is already deleted, but it may still
209
exist on the other side (even the subscription
211
del = array_append_space(&tree->deletes);
212
del->type = DSYNC_MAILBOX_DELETE_TYPE_UNSUBSCRIBE;
213
del->timestamp = timestamp;
214
memcpy(del->guid, rec->mailbox_guid, sizeof(del->guid));
218
if (mailbox_log_iter_deinit(&iter) < 0) {
219
i_error("Mailbox log iteration for namespace '%s' failed",
227
dsync_mailbox_tree_fix_guid_duplicate(struct dsync_mailbox_tree *tree,
228
struct dsync_mailbox_node *node1,
229
struct dsync_mailbox_node *node2)
232
struct mailbox_update update;
233
struct dsync_mailbox_node *change_node;
236
memset(&update, 0, sizeof(update));
237
guid_128_generate(update.mailbox_guid);
239
/* just in case the duplication exists in both sides,
240
make them choose the same node */
241
if (strcmp(dsync_mailbox_node_get_full_name(tree, node1),
242
dsync_mailbox_node_get_full_name(tree, node2)) <= 0)
247
i_error("Duplicate mailbox GUID %s for mailboxes %s and %s - "
248
"giving a new GUID %s to %s",
249
guid_128_to_string(node1->mailbox_guid),
250
dsync_mailbox_node_get_full_name(tree, node1),
251
dsync_mailbox_node_get_full_name(tree, node2),
252
guid_128_to_string(update.mailbox_guid),
253
dsync_mailbox_node_get_full_name(tree, change_node));
255
i_assert(node1->ns != NULL && node2->ns != NULL);
256
box = mailbox_alloc(change_node->ns->list, change_node->name, 0);
257
if (mailbox_update(box, &update) < 0) {
258
i_error("Couldn't update mailbox %s GUID: %s",
259
change_node->name, mailbox_get_last_error(box, NULL));
262
memcpy(change_node->mailbox_guid, update.mailbox_guid,
263
sizeof(change_node->mailbox_guid));
270
dsync_mailbox_info_is_excluded(const struct mailbox_info *info,
271
const char *const *exclude_mailboxes)
273
const char *const *info_specialuses;
276
if (exclude_mailboxes == NULL)
279
info_specialuses = info->special_use == NULL ? NULL :
280
t_strsplit(info->special_use, " ");
281
for (i = 0; exclude_mailboxes[i] != NULL; i++) {
282
const char *exclude = exclude_mailboxes[i];
284
if (exclude[0] == '\\') {
286
if (info_specialuses != NULL &&
287
str_array_icase_find(info_specialuses, exclude))
290
/* mailbox with wildcards */
291
if (wildcard_match(info->vname, exclude))
298
int dsync_mailbox_tree_fill(struct dsync_mailbox_tree *tree,
299
struct mail_namespace *ns, const char *box_name,
300
const guid_128_t box_guid,
301
const char *const *exclude_mailboxes)
303
const enum mailbox_list_iter_flags list_flags =
304
/* FIXME: we'll skip symlinks, because we can't handle them
305
currently. in future we could detect them and create them
306
by creating the symlink. */
307
MAILBOX_LIST_ITER_SKIP_ALIASES |
308
MAILBOX_LIST_ITER_NO_AUTO_BOXES;
309
const enum mailbox_list_iter_flags subs_list_flags =
310
MAILBOX_LIST_ITER_NO_AUTO_BOXES |
311
MAILBOX_LIST_ITER_SELECT_SUBSCRIBED |
312
MAILBOX_LIST_ITER_RETURN_NO_FLAGS;
313
struct mailbox_list_iterate_context *iter;
314
struct dsync_mailbox_node *node, *dup_node1, *dup_node2;
315
const struct mailbox_info *info;
316
const char *list_pattern = box_name != NULL ? box_name : "*";
319
i_assert(mail_namespace_get_sep(ns) == tree->sep);
321
/* assign namespace to its root, so it gets copied to children */
322
if (ns->prefix_len > 0) {
323
node = dsync_mailbox_tree_get(tree,
324
t_strndup(ns->prefix, ns->prefix_len-1));
330
/* first add all of the existing mailboxes */
331
iter = mailbox_list_iter_init(ns->list, list_pattern, list_flags);
332
while ((info = mailbox_list_iter_next(iter)) != NULL) T_BEGIN {
333
if (!dsync_mailbox_info_is_excluded(info, exclude_mailboxes)) {
334
if (dsync_mailbox_tree_add(tree, info, box_guid) < 0)
338
if (mailbox_list_iter_deinit(&iter) < 0) {
339
i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
343
/* add subscriptions */
344
iter = mailbox_list_iter_init(ns->list, list_pattern, subs_list_flags);
345
while ((info = mailbox_list_iter_next(iter)) != NULL) {
346
if (dsync_mailbox_tree_add_node(tree, info, &node) < 0)
349
node->subscribed = TRUE;
351
if (mailbox_list_iter_deinit(&iter) < 0) {
352
i_error("Mailbox listing for namespace '%s' failed", ns->prefix);
358
while (dsync_mailbox_tree_build_guid_hash(tree, &dup_node1,
360
if (dsync_mailbox_tree_fix_guid_duplicate(tree, dup_node1, dup_node2) < 0)
365
if (dsync_mailbox_tree_add_change_timestamps(tree, ns) < 0)