1
/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
1
/* Copyright (c) 2013 Dovecot authors, see the included COPYING file */
6
#include "dsync-worker.h"
8
#include "process-title.h"
9
#include "settings-parser.h"
10
#include "master-service.h"
11
#include "master-service-settings.h"
12
#include "mail-namespace.h"
13
#include "dsync-mailbox-tree.h"
14
#include "dsync-ibc.h"
7
15
#include "dsync-brain-private.h"
11
#define DSYNC_WRONG_DIRECTION_ERROR_MSG \
13
"Looks like you're trying to run backup in wrong direction. " \
14
"Source is empty and destination is not."
17
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **list);
19
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **list);
22
dsync_brain_init(struct dsync_worker *src_worker,
23
struct dsync_worker *dest_worker,
24
const char *mailbox, enum dsync_brain_flags flags)
26
struct dsync_brain *brain;
28
brain = i_new(struct dsync_brain, 1);
29
brain->src_worker = src_worker;
30
brain->dest_worker = dest_worker;
31
brain->mailbox = i_strdup(mailbox);
33
brain->verbose = (flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0;
34
brain->backup = (flags & DSYNC_BRAIN_FLAG_BACKUP) != 0;
35
brain->stdout_tty = isatty(STDOUT_FILENO) > 0;
37
if ((flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0) {
38
dsync_worker_set_verbose(src_worker);
39
dsync_worker_set_verbose(dest_worker);
44
void dsync_brain_fail(struct dsync_brain *brain)
47
io_loop_stop(current_ioloop);
16
#include "dsync-mailbox-import.h"
17
#include "dsync-mailbox-export.h"
21
static const char *dsync_state_names[] = {
22
"master_recv_handshake",
23
"slave_recv_handshake",
24
"master_send_last_common",
25
"slave_recv_last_common",
27
"send_mailbox_tree_deletes",
29
"recv_mailbox_tree_deletes",
30
"master_send_mailbox",
36
static const char *dsync_brain_get_proctitle(struct dsync_brain *brain)
38
string_t *str = t_str_new(128);
39
const char *import_title, *export_title;
41
str_append_c(str, '[');
42
str_append(str, brain->user->username);
43
if (brain->box == NULL) {
44
str_append_c(str, ' ');
45
str_append(str, dsync_state_names[brain->state]);
47
str_append_c(str, ' ');
48
str_append(str, mailbox_get_vname(brain->box));
49
import_title = brain->box_importer == NULL ? "" :
50
dsync_mailbox_import_get_proctitle(brain->box_importer);
51
export_title = brain->box_exporter == NULL ? "" :
52
dsync_mailbox_export_get_proctitle(brain->box_exporter);
53
if (import_title[0] == '\0' && export_title[0] == '\0') {
54
str_printfa(str, " send:%s recv:%s",
55
dsync_box_state_names[brain->box_send_state],
56
dsync_box_state_names[brain->box_recv_state]);
58
if (import_title[0] != '\0') {
59
str_append(str, " import:");
60
str_append(str, import_title);
62
if (export_title[0] != '\0') {
63
str_append(str, " export:");
64
str_append(str, export_title);
68
str_append_c(str, ']');
72
static void dsync_brain_run_io(void *context)
74
struct dsync_brain *brain = context;
75
bool changed, try_pending;
77
if (dsync_ibc_has_failed(brain->ibc)) {
78
io_loop_stop(current_ioloop);
85
if (!dsync_brain_run(brain, &changed)) {
86
io_loop_stop(current_ioloop);
91
else if (try_pending) {
92
if (dsync_ibc_has_pending_data(brain->ibc))
99
static struct dsync_brain *
100
dsync_brain_common_init(struct mail_user *user, struct dsync_ibc *ibc)
102
struct dsync_brain *brain;
103
const struct master_service_settings *service_set;
106
service_set = master_service_settings_get(master_service);
108
pool = pool_alloconly_create("dsync brain", 10240);
109
brain = p_new(pool, struct dsync_brain, 1);
113
brain->sync_type = DSYNC_BRAIN_SYNC_TYPE_UNKNOWN;
115
brain->verbose_proctitle = service_set->verbose_proctitle;
116
hash_table_create(&brain->mailbox_states, pool, 0,
117
guid_128_hash, guid_128_cmp);
118
p_array_init(&brain->remote_mailbox_states, pool, 64);
123
dsync_brain_set_flags(struct dsync_brain *brain, enum dsync_brain_flags flags)
125
brain->mail_requests =
126
(flags & DSYNC_BRAIN_FLAG_SEND_MAIL_REQUESTS) != 0;
127
brain->backup_send = (flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0;
128
brain->backup_recv = (flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0;
129
brain->debug = (flags & DSYNC_BRAIN_FLAG_DEBUG) != 0;
130
brain->sync_visible_namespaces =
131
(flags & DSYNC_BRAIN_FLAG_SYNC_VISIBLE_NAMESPACES) != 0;
132
brain->no_mail_sync = (flags & DSYNC_BRAIN_FLAG_NO_MAIL_SYNC) != 0;
133
brain->no_backup_overwrite =
134
(flags & DSYNC_BRAIN_FLAG_NO_BACKUP_OVERWRITE) != 0;
138
dsync_brain_master_init(struct mail_user *user, struct dsync_ibc *ibc,
139
enum dsync_brain_sync_type sync_type,
140
enum dsync_brain_flags flags,
141
const struct dsync_brain_settings *set)
143
struct dsync_ibc_settings ibc_set;
144
struct dsync_brain *brain;
147
i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
148
i_assert(sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE ||
149
(set->state != NULL && *set->state != '\0'));
150
i_assert(N_ELEMENTS(dsync_state_names) == DSYNC_STATE_DONE+1);
152
brain = dsync_brain_common_init(user, ibc);
153
brain->sync_type = sync_type;
154
if (set->sync_ns != NULL)
155
brain->sync_ns = set->sync_ns;
156
brain->sync_box = p_strdup(brain->pool, set->sync_box);
157
brain->exclude_mailboxes = set->exclude_mailboxes == NULL ? NULL :
158
p_strarray_dup(brain->pool, set->exclude_mailboxes);
159
memcpy(brain->sync_box_guid, set->sync_box_guid,
160
sizeof(brain->sync_box_guid));
161
brain->lock_timeout = set->lock_timeout_secs;
162
brain->master_brain = TRUE;
163
dsync_brain_set_flags(brain, flags);
165
if (sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE &&
166
dsync_mailbox_states_import(brain->mailbox_states,
167
brain->pool, set->state, &error) < 0) {
168
hash_table_clear(brain->mailbox_states, FALSE);
169
i_error("Saved sync state is invalid, "
170
"falling back to full sync: %s", error);
171
brain->sync_type = sync_type = DSYNC_BRAIN_SYNC_TYPE_FULL;
173
dsync_brain_mailbox_trees_init(brain);
175
memset(&ibc_set, 0, sizeof(ibc_set));
176
ibc_set.hostname = my_hostdomain();
177
ibc_set.sync_ns_prefix = set->sync_ns == NULL ? NULL :
178
set->sync_ns->prefix;
179
ibc_set.sync_box = set->sync_box;
180
ibc_set.exclude_mailboxes = set->exclude_mailboxes;
181
memcpy(ibc_set.sync_box_guid, set->sync_box_guid,
182
sizeof(ibc_set.sync_box_guid));
183
ibc_set.sync_type = sync_type;
184
ibc_set.lock_timeout = set->lock_timeout_secs;
185
/* reverse the backup direction for the slave */
186
ibc_set.brain_flags = flags & ~(DSYNC_BRAIN_FLAG_BACKUP_SEND |
187
DSYNC_BRAIN_FLAG_BACKUP_RECV);
188
if ((flags & DSYNC_BRAIN_FLAG_BACKUP_SEND) != 0)
189
ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_RECV;
190
else if ((flags & DSYNC_BRAIN_FLAG_BACKUP_RECV) != 0)
191
ibc_set.brain_flags |= DSYNC_BRAIN_FLAG_BACKUP_SEND;
192
dsync_ibc_send_handshake(ibc, &ibc_set);
194
dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
195
brain->state = DSYNC_STATE_MASTER_RECV_HANDSHAKE;
200
dsync_brain_slave_init(struct mail_user *user, struct dsync_ibc *ibc,
203
struct dsync_ibc_settings ibc_set;
204
struct dsync_brain *brain;
206
brain = dsync_brain_common_init(user, ibc);
207
brain->state = DSYNC_STATE_SLAVE_RECV_HANDSHAKE;
210
/* both master and slave are running within the same process,
211
update the proctitle only for master. */
212
brain->verbose_proctitle = FALSE;
215
memset(&ibc_set, 0, sizeof(ibc_set));
216
ibc_set.hostname = my_hostdomain();
217
dsync_ibc_send_handshake(ibc, &ibc_set);
219
dsync_ibc_set_io_callback(ibc, dsync_brain_run_io, brain);
50
223
int dsync_brain_deinit(struct dsync_brain **_brain)
52
225
struct dsync_brain *brain = *_brain;
53
int ret = brain->failed ? -1 : 0;
55
if (brain->state != DSYNC_STATE_SYNC_END)
57
if (brain->to != NULL)
58
timeout_remove(&brain->to);
61
/* make sure we unreference save input streams before workers
62
are deinitialized, so they can destroy the streams */
63
dsync_worker_msg_save_cancel(brain->src_worker);
64
dsync_worker_msg_save_cancel(brain->dest_worker);
67
if (brain->mailbox_sync != NULL)
68
dsync_brain_msg_sync_deinit(&brain->mailbox_sync);
70
if (brain->src_mailbox_list != NULL)
71
dsync_brain_mailbox_list_deinit(&brain->src_mailbox_list);
72
if (brain->dest_mailbox_list != NULL)
73
dsync_brain_mailbox_list_deinit(&brain->dest_mailbox_list);
75
if (brain->src_subs_list != NULL)
76
dsync_brain_subs_list_deinit(&brain->src_subs_list);
77
if (brain->dest_subs_list != NULL)
78
dsync_brain_subs_list_deinit(&brain->dest_subs_list);
80
if (dsync_worker_has_failed(brain->src_worker) ||
81
dsync_worker_has_failed(brain->dest_worker))
85
i_free(brain->mailbox);
230
if (dsync_ibc_has_timed_out(brain->ibc)) {
231
i_error("Timeout during state=%s%s",
232
dsync_state_names[brain->state],
233
brain->state != DSYNC_STATE_SYNC_MAILS ? "" :
234
t_strdup_printf(" (send=%s recv=%s)",
235
dsync_box_state_names[brain->box_send_state],
236
dsync_box_state_names[brain->box_recv_state]));
238
if (dsync_ibc_has_failed(brain->ibc) ||
239
brain->state != DSYNC_STATE_DONE)
240
brain->failed = TRUE;
241
dsync_ibc_close_mail_streams(brain->ibc);
243
if (brain->box != NULL)
244
dsync_brain_sync_mailbox_deinit(brain);
245
if (brain->local_tree_iter != NULL)
246
dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
247
if (brain->local_mailbox_tree != NULL)
248
dsync_mailbox_tree_deinit(&brain->local_mailbox_tree);
249
if (brain->remote_mailbox_tree != NULL)
250
dsync_mailbox_tree_deinit(&brain->remote_mailbox_tree);
251
if (brain->mailbox_states_iter != NULL)
252
hash_table_iterate_deinit(&brain->mailbox_states_iter);
253
hash_table_destroy(&brain->mailbox_states);
255
if (brain->lock_fd != -1) {
256
/* unlink the lock file before it gets unlocked */
257
if (unlink(brain->lock_path) < 0)
258
i_error("unlink(%s) failed: %m", brain->lock_path);
259
file_lock_free(&brain->lock);
260
i_close_fd(&brain->lock_fd);
263
ret = brain->failed ? -1 : 0;
264
pool_unref(&brain->pool);
90
static void dsync_brain_mailbox_list_finished(struct dsync_brain *brain)
92
if (brain->src_mailbox_list->iter != NULL ||
93
brain->dest_mailbox_list->iter != NULL)
96
/* both lists are finished */
98
dsync_brain_sync(brain);
101
static void dsync_worker_mailbox_input(void *context)
103
struct dsync_brain_mailbox_list *list = context;
104
struct dsync_mailbox dsync_box, *dup_box;
107
while ((ret = dsync_worker_mailbox_iter_next(list->iter,
109
if (list->brain->mailbox != NULL &&
110
strcmp(list->brain->mailbox, dsync_box.name) != 0)
113
dup_box = dsync_mailbox_dup(list->pool, &dsync_box);
114
if (!dsync_mailbox_is_noselect(dup_box))
115
array_append(&list->mailboxes, &dup_box, 1);
117
array_append(&list->dirs, &dup_box, 1);
120
/* finished listing mailboxes */
121
if (dsync_worker_mailbox_iter_deinit(&list->iter) < 0)
122
dsync_brain_fail(list->brain);
123
array_sort(&list->mailboxes, dsync_mailbox_p_guid_cmp);
124
array_sort(&list->dirs, dsync_mailbox_p_name_sha1_cmp);
125
dsync_brain_mailbox_list_finished(list->brain);
129
static struct dsync_brain_mailbox_list *
130
dsync_brain_mailbox_list_init(struct dsync_brain *brain,
131
struct dsync_worker *worker)
133
struct dsync_brain_mailbox_list *list;
136
pool = pool_alloconly_create("dsync brain mailbox list", 10240);
137
list = p_new(pool, struct dsync_brain_mailbox_list, 1);
140
list->worker = worker;
141
list->iter = dsync_worker_mailbox_iter_init(worker);
142
p_array_init(&list->mailboxes, pool, 128);
143
p_array_init(&list->dirs, pool, 32);
144
dsync_worker_set_input_callback(worker, dsync_worker_mailbox_input,
150
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **_list)
152
struct dsync_brain_mailbox_list *list = *_list;
156
if (list->iter != NULL)
157
(void)dsync_worker_mailbox_iter_deinit(&list->iter);
158
pool_unref(&list->pool);
161
static void dsync_brain_subs_list_finished(struct dsync_brain *brain)
163
if (brain->src_subs_list->iter != NULL ||
164
brain->dest_subs_list->iter != NULL)
167
/* both lists are finished */
169
dsync_brain_sync(brain);
173
dsync_worker_subscription_cmp(const struct dsync_worker_subscription *s1,
174
const struct dsync_worker_subscription *s2)
176
return strcmp(s1->vname, s2->vname);
180
dsync_worker_unsubscription_cmp(const struct dsync_worker_unsubscription *u1,
181
const struct dsync_worker_unsubscription *u2)
185
ret = strcmp(u1->ns_prefix, u2->ns_prefix);
186
return ret != 0 ? ret :
187
dsync_guid_cmp(&u1->name_sha1, &u2->name_sha1);
190
static void dsync_worker_subs_input(void *context)
192
struct dsync_brain_subs_list *list = context;
193
struct dsync_worker_subscription subs;
194
struct dsync_worker_unsubscription unsubs;
197
memset(&subs, 0, sizeof(subs));
198
while ((ret = dsync_worker_subs_iter_next(list->iter, &subs)) > 0) {
199
subs.vname = p_strdup(list->pool, subs.vname);
200
subs.storage_name = p_strdup(list->pool, subs.storage_name);
201
subs.ns_prefix = p_strdup(list->pool, subs.ns_prefix);
202
array_append(&list->subscriptions, &subs, 1);
207
memset(&unsubs, 0, sizeof(unsubs));
208
while ((ret = dsync_worker_subs_iter_next_un(list->iter,
210
unsubs.ns_prefix = p_strdup(list->pool, unsubs.ns_prefix);
211
array_append(&list->unsubscriptions, &unsubs, 1);
215
/* finished listing subscriptions */
216
if (dsync_worker_subs_iter_deinit(&list->iter) < 0)
217
dsync_brain_fail(list->brain);
218
array_sort(&list->subscriptions,
219
dsync_worker_subscription_cmp);
220
array_sort(&list->unsubscriptions,
221
dsync_worker_unsubscription_cmp);
222
dsync_brain_subs_list_finished(list->brain);
226
static struct dsync_brain_subs_list *
227
dsync_brain_subs_list_init(struct dsync_brain *brain,
228
struct dsync_worker *worker)
230
struct dsync_brain_subs_list *list;
233
pool = pool_alloconly_create(MEMPOOL_GROWING"dsync brain subs list",
235
list = p_new(pool, struct dsync_brain_subs_list, 1);
238
list->worker = worker;
239
list->iter = dsync_worker_subs_iter_init(worker);
240
p_array_init(&list->subscriptions, pool, 128);
241
p_array_init(&list->unsubscriptions, pool, 64);
242
dsync_worker_set_input_callback(worker, dsync_worker_subs_input, list);
247
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **_list)
249
struct dsync_brain_subs_list *list = *_list;
253
if (list->iter != NULL)
254
(void)dsync_worker_subs_iter_deinit(&list->iter);
255
pool_unref(&list->pool);
258
enum dsync_brain_mailbox_action {
259
DSYNC_BRAIN_MAILBOX_ACTION_NONE,
260
DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
261
DSYNC_BRAIN_MAILBOX_ACTION_DELETE
265
dsync_brain_mailbox_action(struct dsync_brain *brain,
266
enum dsync_brain_mailbox_action action,
267
struct dsync_worker *action_worker,
268
struct dsync_mailbox *action_box)
270
struct dsync_mailbox new_box;
272
if (brain->backup && action_worker == brain->src_worker) {
273
/* backup mode: switch actions */
274
action_worker = brain->dest_worker;
276
case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
278
case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
279
action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
281
case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
282
action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
288
case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
290
case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
291
new_box = *action_box;
292
new_box.uid_next = action_box->uid_validity == 0 ? 0 : 1;
293
new_box.first_recent_uid = 0;
294
new_box.highest_modseq = 0;
295
dsync_worker_create_mailbox(action_worker, &new_box);
297
case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
298
if (!dsync_mailbox_is_noselect(action_box))
299
dsync_worker_delete_mailbox(action_worker, action_box);
301
dsync_worker_delete_dir(action_worker, action_box);
307
dsync_mailbox_list_is_empty(const ARRAY_TYPE(dsync_mailbox) *boxes_arr)
309
struct dsync_mailbox *const *boxes;
312
boxes = array_get(boxes_arr, &count);
315
if (count == 1 && strcasecmp(boxes[0]->name, "INBOX") == 0 &&
316
boxes[0]->message_count == 0 && boxes[0]->uid_next == 1)
321
static void dsync_brain_sync_mailboxes(struct dsync_brain *brain)
323
struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
324
struct dsync_mailbox *action_box = NULL;
325
struct dsync_worker *action_worker = NULL;
326
unsigned int src, dest, src_count, dest_count;
327
enum dsync_brain_mailbox_action action;
328
bool src_deleted, dest_deleted;
332
dsync_mailbox_list_is_empty(&brain->src_mailbox_list->mailboxes) &&
333
!dsync_mailbox_list_is_empty(&brain->dest_mailbox_list->mailboxes)) {
334
i_fatal(DSYNC_WRONG_DIRECTION_ERROR_MSG);
337
/* create/delete missing mailboxes. the mailboxes are sorted by
338
GUID, so we can do this quickly. */
339
src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
340
dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
341
for (src = dest = 0; src < src_count && dest < dest_count; ) {
342
action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
343
src_deleted = (src_boxes[src]->flags &
344
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
345
dest_deleted = (dest_boxes[dest]->flags &
346
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
347
ret = dsync_mailbox_guid_cmp(src_boxes[src],
350
/* exists only in source */
352
action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
353
action_worker = brain->dest_worker;
354
action_box = src_boxes[src];
357
} else if (ret > 0) {
358
/* exists only in dest */
360
action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
361
action_worker = brain->src_worker;
362
action_box = dest_boxes[dest];
365
} else if (src_deleted) {
366
/* delete from dest too */
368
action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
369
action_worker = brain->dest_worker;
370
action_box = dest_boxes[dest];
373
} else if (dest_deleted) {
374
/* delete from src too */
375
action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
376
action_worker = brain->src_worker;
377
action_box = src_boxes[src];
382
dsync_brain_mailbox_action(brain, action,
383
action_worker, action_box);
385
for (; src < src_count; src++) {
386
if ((src_boxes[src]->flags &
387
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
390
dsync_brain_mailbox_action(brain,
391
DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
392
brain->dest_worker, src_boxes[src]);
394
for (; dest < dest_count; dest++) {
395
if ((dest_boxes[dest]->flags &
396
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
399
dsync_brain_mailbox_action(brain,
400
DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
401
brain->src_worker, dest_boxes[dest]);
405
static void dsync_brain_sync_dirs(struct dsync_brain *brain)
407
struct dsync_mailbox *const *src_boxes, *const *dest_boxes, *action_box;
408
unsigned int src, dest, src_count, dest_count;
409
enum dsync_brain_mailbox_action action;
410
struct dsync_worker *action_worker = NULL;
411
bool src_deleted, dest_deleted;
414
/* create/delete missing directories. */
415
src_boxes = array_get(&brain->src_mailbox_list->dirs, &src_count);
416
dest_boxes = array_get(&brain->dest_mailbox_list->dirs, &dest_count);
417
for (src = dest = 0; src < src_count && dest < dest_count; ) {
418
action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
421
src_deleted = (src_boxes[src]->flags &
422
DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
423
dest_deleted = (dest_boxes[dest]->flags &
424
DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
425
ret = memcmp(src_boxes[src]->name_sha1.guid,
426
dest_boxes[dest]->name_sha1.guid,
427
sizeof(src_boxes[src]->name_sha1.guid));
429
/* exists only in source */
431
action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
432
action_worker = brain->dest_worker;
433
action_box = src_boxes[src];
436
} else if (ret > 0) {
437
/* exists only in dest */
439
action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
440
action_worker = brain->src_worker;
441
action_box = dest_boxes[dest];
444
} else if (src_deleted) {
445
/* delete from dest too */
447
action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
448
action_worker = brain->dest_worker;
449
action_box = dest_boxes[dest];
452
} else if (dest_deleted) {
453
/* delete from src too */
454
action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
455
action_worker = brain->src_worker;
456
action_box = src_boxes[src];
461
i_assert(action_box == NULL ||
462
dsync_mailbox_is_noselect(action_box));
463
dsync_brain_mailbox_action(brain, action,
464
action_worker, action_box);
466
for (; src < src_count; src++) {
467
if ((src_boxes[src]->flags &
468
DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
471
dsync_brain_mailbox_action(brain,
472
DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
473
brain->dest_worker, src_boxes[src]);
475
for (; dest < dest_count; dest++) {
476
if ((dest_boxes[dest]->flags &
477
DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
480
dsync_brain_mailbox_action(brain,
481
DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
482
brain->src_worker, dest_boxes[dest]);
487
dsync_brain_is_unsubscribed(struct dsync_brain_subs_list *list,
488
const struct dsync_worker_subscription *subs,
489
time_t *last_change_r)
491
const struct dsync_worker_unsubscription *unsubs;
492
struct dsync_worker_unsubscription lookup;
494
lookup.ns_prefix = subs->ns_prefix;
495
dsync_str_sha_to_guid(subs->storage_name, &lookup.name_sha1);
496
unsubs = array_bsearch(&list->unsubscriptions, &lookup,
497
dsync_worker_unsubscription_cmp);
498
if (unsubs == NULL) {
501
} else if (unsubs->last_change <= subs->last_change) {
502
*last_change_r = subs->last_change;
505
*last_change_r = unsubs->last_change;
510
static void dsync_brain_sync_subscriptions(struct dsync_brain *brain)
512
const struct dsync_worker_subscription *src_subs, *dest_subs;
513
const struct dsync_worker_subscription *action_subs;
514
struct dsync_worker *action_worker;
515
unsigned int src, dest, src_count, dest_count;
520
/* subscriptions are sorted by name. */
521
src_subs = array_get(&brain->src_subs_list->subscriptions, &src_count);
522
dest_subs = array_get(&brain->dest_subs_list->subscriptions, &dest_count);
523
for (src = dest = 0;; ) {
524
if (src == src_count) {
525
if (dest == dest_count)
528
} else if (dest == dest_count) {
531
ret = strcmp(src_subs[src].vname,
532
dest_subs[dest].vname);
540
/* subscribed only in source */
541
action_subs = &src_subs[src];
542
if (dsync_brain_is_unsubscribed(brain->dest_subs_list,
545
action_worker = brain->src_worker;
548
action_worker = brain->dest_worker;
553
/* subscribed only in dest */
554
action_subs = &dest_subs[dest];
555
if (dsync_brain_is_unsubscribed(brain->src_subs_list,
558
action_worker = brain->dest_worker;
561
action_worker = brain->src_worker;
567
if (brain->backup && action_worker == brain->src_worker) {
568
/* backup mode: switch action */
569
action_worker = brain->dest_worker;
570
subscribe = !subscribe;
571
last_change = ioloop_time;
573
dsync_worker_set_subscribed(action_worker, action_subs->vname,
574
last_change, subscribe);
578
static bool dsync_mailbox_has_changed_msgs(struct dsync_brain *brain,
579
const struct dsync_mailbox *box1,
580
const struct dsync_mailbox *box2)
582
const char *name = *box1->name != '\0' ? box1->name : box2->name;
584
if (box1->uid_validity != box2->uid_validity) {
585
if (brain->verbose) {
586
i_info("%s: uidvalidity changed: %u != %u", name,
587
box1->uid_validity, box2->uid_validity);
591
if (box1->uid_next != box2->uid_next) {
592
if (brain->verbose) {
593
i_info("%s: uidnext changed: %u != %u", name,
594
box1->uid_next, box2->uid_next);
598
if (box1->highest_modseq != box2->highest_modseq) {
599
if (brain->verbose) {
600
i_info("%s: highest_modseq changed: %llu != %llu", name,
601
(unsigned long long)box1->highest_modseq,
602
(unsigned long long)box2->highest_modseq);
606
if (box1->message_count != box2->message_count) {
607
if (brain->verbose) {
608
i_info("%s: message_count changed: %u != %u", name,
609
box1->message_count, box2->message_count);
616
static bool dsync_mailbox_has_changes(struct dsync_brain *brain,
617
const struct dsync_mailbox *box1,
618
const struct dsync_mailbox *box2)
620
if (strcmp(box1->name, box2->name) != 0)
622
return dsync_mailbox_has_changed_msgs(brain, box1, box2);
626
dsync_brain_get_changed_mailboxes(struct dsync_brain *brain,
627
ARRAY_TYPE(dsync_brain_mailbox) *brain_boxes,
630
struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
631
struct dsync_brain_mailbox *brain_box;
632
unsigned int src, dest, src_count, dest_count;
633
bool src_deleted, dest_deleted;
636
src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
637
dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
639
for (src = dest = 0; src < src_count && dest < dest_count; ) {
640
src_deleted = (src_boxes[src]->flags &
641
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
642
dest_deleted = (dest_boxes[dest]->flags &
643
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
645
ret = dsync_mailbox_guid_cmp(src_boxes[src], dest_boxes[dest]);
648
dsync_mailbox_has_changes(brain, src_boxes[src],
649
dest_boxes[dest])) &&
650
!src_deleted && !dest_deleted) {
651
brain_box = array_append_space(brain_boxes);
652
brain_box->box = *src_boxes[src];
654
brain_box->box.highest_modseq =
655
I_MAX(src_boxes[src]->highest_modseq,
656
dest_boxes[dest]->highest_modseq);
657
brain_box->box.uid_next =
658
I_MAX(src_boxes[src]->uid_next,
659
dest_boxes[dest]->uid_next);
660
brain_box->src = src_boxes[src];
661
brain_box->dest = dest_boxes[dest];
664
} else if (ret < 0) {
665
/* exists only in source */
667
brain_box = array_append_space(brain_boxes);
668
brain_box->box = *src_boxes[src];
669
brain_box->src = src_boxes[src];
670
if (brain->verbose) {
671
i_info("%s: only in source (guid=%s)",
673
dsync_guid_to_str(&brain_box->box.mailbox_guid));
678
/* exists only in dest */
680
brain_box = array_append_space(brain_boxes);
681
brain_box->box = *dest_boxes[dest];
682
brain_box->dest = dest_boxes[dest];
683
if (brain->verbose) {
684
i_info("%s: only in dest (guid=%s)",
686
dsync_guid_to_str(&brain_box->box.mailbox_guid));
692
for (; src < src_count; src++) {
693
if ((src_boxes[src]->flags &
694
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
697
brain_box = array_append_space(brain_boxes);
698
brain_box->box = *src_boxes[src];
699
brain_box->src = src_boxes[src];
700
if (brain->verbose) {
701
i_info("%s: only in source (guid=%s)",
703
dsync_guid_to_str(&brain_box->box.mailbox_guid));
706
for (; dest < dest_count; dest++) {
707
if ((dest_boxes[dest]->flags &
708
DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
711
brain_box = array_append_space(brain_boxes);
712
brain_box->box = *dest_boxes[dest];
713
brain_box->dest = dest_boxes[dest];
714
if (brain->verbose) {
715
i_info("%s: only in dest (guid=%s)",
717
dsync_guid_to_str(&brain_box->box.mailbox_guid));
722
static bool dsync_brain_sync_msgs(struct dsync_brain *brain)
724
ARRAY_TYPE(dsync_brain_mailbox) mailboxes;
269
dsync_brain_lock(struct dsync_brain *brain, const char *remote_hostname)
271
struct stat st1, st2;
275
if ((ret = strcmp(remote_hostname, my_hostdomain())) < 0) {
276
/* locking done by remote */
279
if (ret == 0 && !brain->master_brain) {
280
/* running dsync within the same server.
281
locking done by master brain. */
285
if ((ret = mail_user_get_home(brain->user, &home)) < 0) {
286
i_error("Couldn't look up user's home dir");
290
i_error("User has no home directory");
294
brain->lock_path = p_strconcat(brain->pool, home,
295
"/"DSYNC_LOCK_FILENAME, NULL);
297
brain->lock_fd = creat(brain->lock_path, 0600);
298
if (brain->lock_fd == -1) {
299
i_error("Couldn't create lock %s: %m",
304
if (file_wait_lock(brain->lock_fd, brain->lock_path, F_WRLCK,
305
FILE_LOCK_METHOD_FCNTL, brain->lock_timeout,
306
&brain->lock) <= 0) {
307
if (errno == EAGAIN) {
308
i_error("Couldn't lock %s: Timed out after %u seconds",
309
brain->lock_path, brain->lock_timeout);
311
i_error("Couldn't lock %s: %m", brain->lock_path);
315
if (fstat(brain->lock_fd, &st1) < 0) {
316
if (errno != ESTALE) {
317
i_error("fstat(%s) failed: %m", brain->lock_path);
320
} else if (stat(brain->lock_path, &st2) < 0) {
321
if (errno != ENOENT) {
322
i_error("stat(%s) failed: %m", brain->lock_path);
325
} else if (st1.st_ino == st2.st_ino) {
329
/* file was recreated, try again */
330
i_close_fd(&brain->lock_fd);
332
i_close_fd(&brain->lock_fd);
336
static bool dsync_brain_master_recv_handshake(struct dsync_brain *brain)
338
const struct dsync_ibc_settings *ibc_set;
340
i_assert(brain->master_brain);
342
if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0)
345
if (brain->lock_timeout > 0) {
346
if (dsync_brain_lock(brain, ibc_set->hostname) < 0) {
347
brain->failed = TRUE;
352
brain->state = brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE ?
353
DSYNC_STATE_MASTER_SEND_LAST_COMMON :
354
DSYNC_STATE_SEND_MAILBOX_TREE;
358
static bool dsync_brain_slave_recv_handshake(struct dsync_brain *brain)
360
const struct dsync_ibc_settings *ibc_set;
362
i_assert(!brain->master_brain);
364
if (dsync_ibc_recv_handshake(brain->ibc, &ibc_set) == 0)
367
if (ibc_set->lock_timeout > 0) {
368
brain->lock_timeout = ibc_set->lock_timeout;
369
if (dsync_brain_lock(brain, ibc_set->hostname) < 0) {
370
brain->failed = TRUE;
375
if (ibc_set->sync_ns_prefix != NULL) {
376
brain->sync_ns = mail_namespace_find(brain->user->namespaces,
377
ibc_set->sync_ns_prefix);
379
brain->sync_box = p_strdup(brain->pool, ibc_set->sync_box);
380
brain->exclude_mailboxes = ibc_set->exclude_mailboxes == NULL ? NULL :
381
p_strarray_dup(brain->pool, ibc_set->exclude_mailboxes);
382
memcpy(brain->sync_box_guid, ibc_set->sync_box_guid,
383
sizeof(brain->sync_box_guid));
384
i_assert(brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_UNKNOWN);
385
brain->sync_type = ibc_set->sync_type;
386
dsync_brain_set_flags(brain, ibc_set->brain_flags);
388
dsync_brain_mailbox_trees_init(brain);
390
if (brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_STATE)
391
brain->state = DSYNC_STATE_SLAVE_RECV_LAST_COMMON;
393
brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
397
static void dsync_brain_master_send_last_common(struct dsync_brain *brain)
399
struct dsync_mailbox_state *state;
401
enum dsync_ibc_send_ret ret = DSYNC_IBC_SEND_RET_OK;
403
i_assert(brain->master_brain);
405
if (brain->mailbox_states_iter == NULL) {
406
brain->mailbox_states_iter =
407
hash_table_iterate_init(brain->mailbox_states);
411
if (ret == DSYNC_IBC_SEND_RET_FULL)
413
if (!hash_table_iterate(brain->mailbox_states_iter,
414
brain->mailbox_states, &guid, &state))
416
ret = dsync_ibc_send_mailbox_state(brain->ibc, state);
418
hash_table_iterate_deinit(&brain->mailbox_states_iter);
420
dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX_STATE);
421
brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
424
static void dsync_mailbox_state_add(struct dsync_brain *brain,
425
const struct dsync_mailbox_state *state)
427
struct dsync_mailbox_state *dupstate;
430
dupstate = p_new(brain->pool, struct dsync_mailbox_state, 1);
432
guid_p = dupstate->mailbox_guid;
433
hash_table_insert(brain->mailbox_states, guid_p, dupstate);
436
static bool dsync_brain_slave_recv_last_common(struct dsync_brain *brain)
438
struct dsync_mailbox_state state;
439
enum dsync_ibc_recv_ret ret;
440
bool changed = FALSE;
442
i_assert(!brain->master_brain);
444
while ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) > 0) {
445
dsync_mailbox_state_add(brain, &state);
448
if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
449
brain->state = DSYNC_STATE_SEND_MAILBOX_TREE;
455
static bool dsync_brain_run_real(struct dsync_brain *brain, bool *changed_r)
457
enum dsync_state orig_state = brain->state;
458
enum dsync_box_state orig_box_recv_state = brain->box_recv_state;
459
enum dsync_box_state orig_box_send_state = brain->box_send_state;
460
bool changed = FALSE, ret = TRUE;
466
i_debug("brain %c: in state=%s", brain->master_brain ? 'M' : 'S',
467
dsync_state_names[brain->state]);
469
switch (brain->state) {
470
case DSYNC_STATE_MASTER_RECV_HANDSHAKE:
471
changed = dsync_brain_master_recv_handshake(brain);
473
case DSYNC_STATE_SLAVE_RECV_HANDSHAKE:
474
changed = dsync_brain_slave_recv_handshake(brain);
476
case DSYNC_STATE_MASTER_SEND_LAST_COMMON:
477
dsync_brain_master_send_last_common(brain);
480
case DSYNC_STATE_SLAVE_RECV_LAST_COMMON:
481
changed = dsync_brain_slave_recv_last_common(brain);
483
case DSYNC_STATE_SEND_MAILBOX_TREE:
484
dsync_brain_send_mailbox_tree(brain);
487
case DSYNC_STATE_RECV_MAILBOX_TREE:
488
changed = dsync_brain_recv_mailbox_tree(brain);
490
case DSYNC_STATE_SEND_MAILBOX_TREE_DELETES:
491
dsync_brain_send_mailbox_tree_deletes(brain);
494
case DSYNC_STATE_RECV_MAILBOX_TREE_DELETES:
495
changed = dsync_brain_recv_mailbox_tree_deletes(brain);
497
case DSYNC_STATE_MASTER_SEND_MAILBOX:
498
dsync_brain_master_send_mailbox(brain);
501
case DSYNC_STATE_SLAVE_RECV_MAILBOX:
502
changed = dsync_brain_slave_recv_mailbox(brain);
504
case DSYNC_STATE_SYNC_MAILS:
505
changed = dsync_brain_sync_mails(brain);
507
case DSYNC_STATE_DONE:
513
i_debug("brain %c: out state=%s changed=%d",
514
brain->master_brain ? 'M' : 'S',
515
dsync_state_names[brain->state], changed);
517
if (brain->verbose_proctitle) {
518
if (orig_state != brain->state ||
519
orig_box_recv_state != brain->box_recv_state ||
520
orig_box_send_state != brain->box_send_state ||
521
++brain->proctitle_update_counter % 100 == 0)
522
process_title_set(dsync_brain_get_proctitle(brain));
524
*changed_r = changed;
525
return brain->failed ? FALSE : ret;
528
bool dsync_brain_run(struct dsync_brain *brain, bool *changed_r)
728
pool = pool_alloconly_create(MEMPOOL_GROWING"dsync changed mailboxes",
730
p_array_init(&mailboxes, pool, 128);
731
dsync_brain_get_changed_mailboxes(brain, &mailboxes,
732
(brain->flags & DSYNC_BRAIN_FLAG_FULL_SYNC) != 0);
733
if (array_count(&mailboxes) > 0) {
734
brain->mailbox_sync =
735
dsync_brain_msg_sync_init(brain, &mailboxes);
736
dsync_brain_msg_sync_more(brain->mailbox_sync);
534
if (dsync_ibc_has_failed(brain->ibc)) {
535
brain->failed = TRUE;
540
ret = dsync_brain_run_real(brain, changed_r);
746
dsync_brain_sync_rename_mailbox(struct dsync_brain *brain,
747
const struct dsync_brain_mailbox *mailbox)
749
if (mailbox->src->last_change > mailbox->dest->last_change ||
751
dsync_worker_rename_mailbox(brain->dest_worker,
752
&mailbox->box.mailbox_guid,
755
dsync_worker_rename_mailbox(brain->src_worker,
756
&mailbox->box.mailbox_guid,
762
dsync_brain_sync_update_mailboxes(struct dsync_brain *brain)
764
const struct dsync_brain_mailbox *mailbox;
765
bool failed_changes = dsync_brain_has_unexpected_changes(brain) ||
766
dsync_worker_has_failed(brain->src_worker) ||
767
dsync_worker_has_failed(brain->dest_worker);
769
if (brain->mailbox_sync == NULL) {
770
/* no mailboxes changed */
774
array_foreach(&brain->mailbox_sync->mailboxes, mailbox) {
775
/* don't update mailboxes if any changes had failed.
776
for example if some messages couldn't be saved, we don't
777
want to increase the next_uid to jump over them */
778
if (!brain->backup && !failed_changes) {
779
dsync_worker_update_mailbox(brain->src_worker,
782
if (!failed_changes) {
783
dsync_worker_update_mailbox(brain->dest_worker,
787
if (mailbox->src != NULL && mailbox->dest != NULL &&
788
strcmp(mailbox->src->name, mailbox->dest->name) != 0)
789
dsync_brain_sync_rename_mailbox(brain, mailbox);
793
static void dsync_brain_worker_finished(bool success, void *context)
795
struct dsync_brain *brain = context;
797
switch (brain->state) {
798
case DSYNC_STATE_SYNC_MSGS_FLUSH:
799
case DSYNC_STATE_SYNC_MSGS_FLUSH2:
800
case DSYNC_STATE_SYNC_FLUSH:
801
case DSYNC_STATE_SYNC_FLUSH2:
804
i_panic("dsync brain state=%d", brain->state);
808
dsync_brain_fail(brain);
811
if (brain->to == NULL && (brain->flags & DSYNC_BRAIN_FLAG_LOCAL) == 0)
812
brain->to = timeout_add(0, dsync_brain_sync, brain);
815
void dsync_brain_sync(struct dsync_brain *brain)
817
if (dsync_worker_has_failed(brain->src_worker) ||
818
dsync_worker_has_failed(brain->dest_worker)) {
819
/* we can't safely continue, especially with backup */
823
if (brain->to != NULL)
824
timeout_remove(&brain->to);
825
switch (brain->state) {
826
case DSYNC_STATE_GET_MAILBOXES:
827
i_assert(brain->src_mailbox_list == NULL);
828
brain->src_mailbox_list =
829
dsync_brain_mailbox_list_init(brain, brain->src_worker);
830
brain->dest_mailbox_list =
831
dsync_brain_mailbox_list_init(brain, brain->dest_worker);
832
dsync_worker_mailbox_input(brain->src_mailbox_list);
833
dsync_worker_mailbox_input(brain->dest_mailbox_list);
835
case DSYNC_STATE_GET_SUBSCRIPTIONS:
836
i_assert(brain->src_subs_list == NULL);
837
brain->src_subs_list =
838
dsync_brain_subs_list_init(brain, brain->src_worker);
839
brain->dest_subs_list =
840
dsync_brain_subs_list_init(brain, brain->dest_worker);
841
dsync_worker_subs_input(brain->src_subs_list);
842
dsync_worker_subs_input(brain->dest_subs_list);
844
case DSYNC_STATE_SYNC_MAILBOXES:
845
dsync_worker_set_input_callback(brain->src_worker, NULL, NULL);
846
dsync_worker_set_input_callback(brain->dest_worker, NULL, NULL);
848
dsync_brain_sync_mailboxes(brain);
849
dsync_brain_sync_dirs(brain);
852
case DSYNC_STATE_SYNC_SUBSCRIPTIONS:
853
dsync_brain_sync_subscriptions(brain);
856
case DSYNC_STATE_SYNC_MSGS:
857
if (dsync_brain_sync_msgs(brain))
860
/* no mailboxes changed */
861
case DSYNC_STATE_SYNC_MSGS_FLUSH:
862
/* wait until all saves are done, so we don't try to close
863
the mailbox too early */
864
dsync_worker_finish(brain->src_worker,
865
dsync_brain_worker_finished, brain);
866
dsync_worker_finish(brain->dest_worker,
867
dsync_brain_worker_finished, brain);
869
case DSYNC_STATE_SYNC_MSGS_FLUSH2:
871
case DSYNC_STATE_SYNC_UPDATE_MAILBOXES:
872
dsync_brain_sync_update_mailboxes(brain);
875
case DSYNC_STATE_SYNC_FLUSH:
876
dsync_worker_finish(brain->src_worker,
877
dsync_brain_worker_finished, brain);
878
dsync_worker_finish(brain->dest_worker,
879
dsync_brain_worker_finished, brain);
881
case DSYNC_STATE_SYNC_FLUSH2:
883
case DSYNC_STATE_SYNC_END:
884
io_loop_stop(current_ioloop);
891
void dsync_brain_sync_all(struct dsync_brain *brain)
893
enum dsync_state old_state;
895
while (brain->state != DSYNC_STATE_SYNC_END) {
896
old_state = brain->state;
897
dsync_brain_sync(brain);
899
if (dsync_brain_has_failed(brain))
902
i_assert(brain->state != old_state);
545
void dsync_brain_get_state(struct dsync_brain *brain, string_t *output)
547
struct hash_iterate_context *iter;
548
struct dsync_mailbox_node *node;
549
const struct dsync_mailbox_state *new_state;
550
struct dsync_mailbox_state *state;
551
const uint8_t *guid_p;
554
/* update mailbox states */
555
array_foreach(&brain->remote_mailbox_states, new_state) {
556
guid_p = new_state->mailbox_guid;
557
state = hash_table_lookup(brain->mailbox_states, guid_p);
561
dsync_mailbox_state_add(brain, new_state);
564
/* remove nonexistent mailboxes */
565
iter = hash_table_iterate_init(brain->mailbox_states);
566
while (hash_table_iterate(iter, brain->mailbox_states, &guid, &state)) {
567
node = dsync_mailbox_tree_lookup_guid(brain->local_mailbox_tree,
570
node->existence != DSYNC_MAILBOX_NODE_EXISTS)
571
hash_table_remove(brain->mailbox_states, guid);
573
hash_table_iterate_deinit(&iter);
575
dsync_mailbox_states_export(brain->mailbox_states, output);
578
enum dsync_brain_sync_type dsync_brain_get_sync_type(struct dsync_brain *brain)
580
return brain->sync_type;
583
bool dsync_brain_has_failed(struct dsync_brain *brain)
585
return brain->failed;
906
588
bool dsync_brain_has_unexpected_changes(struct dsync_brain *brain)
908
return brain->unexpected_changes ||
909
dsync_worker_has_unexpected_changes(brain->src_worker) ||
910
dsync_worker_has_unexpected_changes(brain->dest_worker);
590
return brain->changes_during_sync;
913
bool dsync_brain_has_failed(struct dsync_brain *brain)
593
bool dsync_brain_want_namespace(struct dsync_brain *brain,
594
struct mail_namespace *ns)
915
return brain->failed ||
916
dsync_worker_has_failed(brain->src_worker) ||
917
dsync_worker_has_failed(brain->dest_worker);
596
if (brain->sync_ns != NULL)
597
return brain->sync_ns == ns;
598
if (ns->alias_for != NULL) {
599
/* always skip aliases */
602
if (brain->sync_visible_namespaces) {
603
if ((ns->flags & NAMESPACE_FLAG_HIDDEN) == 0)
605
if ((ns->flags & (NAMESPACE_FLAG_LIST_PREFIX |
606
NAMESPACE_FLAG_LIST_CHILDREN)) != 0)
610
return strcmp(ns->unexpanded_set->location,
611
SETTING_STRVAR_UNEXPANDED) == 0;