1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "hash.h"
6 #include "mail-cache-private.h"
7 #include "mail-namespace.h"
8 #include "mail-storage-private.h"
9 #include "dsync-ibc.h"
10 #include "dsync-mailbox-tree.h"
11 #include "dsync-mailbox-import.h"
12 #include "dsync-mailbox-export.h"
13 #include "dsync-transaction-log-scan.h"
14 #include "dsync-brain-private.h"
15
16 static int
ns_mailbox_try_alloc(struct dsync_brain * brain,struct mail_namespace * ns,const guid_128_t guid,struct mailbox ** box_r,const char ** errstr_r,enum mail_error * error_r)17 ns_mailbox_try_alloc(struct dsync_brain *brain, struct mail_namespace *ns,
18 const guid_128_t guid, struct mailbox **box_r,
19 const char **errstr_r, enum mail_error *error_r)
20 {
21 enum mailbox_flags flags = 0;
22 struct mailbox *box;
23 enum mailbox_existence existence;
24 int ret;
25
26 if (brain->backup_send) {
27 /* make sure mailbox isn't modified */
28 flags |= MAILBOX_FLAG_READONLY;
29 }
30
31 box = mailbox_alloc_guid(ns->list, guid, flags);
32 ret = mailbox_exists(box, FALSE, &existence);
33 if (ret < 0) {
34 *errstr_r = mailbox_get_last_internal_error(box, error_r);
35 mailbox_free(&box);
36 return -1;
37 }
38 if (existence != MAILBOX_EXISTENCE_SELECT) {
39 mailbox_free(&box);
40 *errstr_r = existence == MAILBOX_EXISTENCE_NONE ?
41 "Mailbox was already deleted" :
42 "Mailbox is no longer selectable";
43 return 0;
44 }
45 *box_r = box;
46 return 1;
47 }
48
dsync_brain_mailbox_alloc(struct dsync_brain * brain,const guid_128_t guid,struct mailbox ** box_r,const char ** errstr_r,enum mail_error * error_r)49 int dsync_brain_mailbox_alloc(struct dsync_brain *brain, const guid_128_t guid,
50 struct mailbox **box_r, const char **errstr_r,
51 enum mail_error *error_r)
52 {
53 struct mail_namespace *ns;
54 int ret;
55
56 *box_r = NULL;
57
58 for (ns = brain->user->namespaces; ns != NULL; ns = ns->next) {
59 if (!dsync_brain_want_namespace(brain, ns))
60 continue;
61 if ((ret = ns_mailbox_try_alloc(brain, ns, guid, box_r,
62 errstr_r, error_r)) != 0)
63 return ret;
64 }
65 return 0;
66 }
67
68 static void
dsync_mailbox_cache_field_dup(ARRAY_TYPE (mailbox_cache_field)* dest,const ARRAY_TYPE (mailbox_cache_field)* src,pool_t pool)69 dsync_mailbox_cache_field_dup(ARRAY_TYPE(mailbox_cache_field) *dest,
70 const ARRAY_TYPE(mailbox_cache_field) *src,
71 pool_t pool)
72 {
73 const struct mailbox_cache_field *src_field;
74 struct mailbox_cache_field *dest_field;
75
76 p_array_init(dest, pool, array_count(src));
77 array_foreach(src, src_field) {
78 dest_field = array_append_space(dest);
79 dest_field->name = p_strdup(pool, src_field->name);
80 dest_field->decision = src_field->decision;
81 dest_field->last_used = src_field->last_used;
82 }
83 }
84
85
86 static const struct dsync_mailbox_state *
dsync_mailbox_state_find(struct dsync_brain * brain,const guid_128_t mailbox_guid)87 dsync_mailbox_state_find(struct dsync_brain *brain,
88 const guid_128_t mailbox_guid)
89 {
90 const uint8_t *guid_p;
91
92 guid_p = mailbox_guid;
93 return hash_table_lookup(brain->mailbox_states, guid_p);
94 }
95
96 static void
dsync_mailbox_state_remove(struct dsync_brain * brain,const guid_128_t mailbox_guid)97 dsync_mailbox_state_remove(struct dsync_brain *brain,
98 const guid_128_t mailbox_guid)
99 {
100 const uint8_t *guid_p;
101
102 guid_p = mailbox_guid;
103 if (hash_table_lookup(brain->mailbox_states, guid_p) != NULL)
104 hash_table_remove(brain->mailbox_states, guid_p);
105 }
106
dsync_brain_sync_init_box_states(struct dsync_brain * brain)107 void dsync_brain_sync_init_box_states(struct dsync_brain *brain)
108 {
109 if (brain->backup_send) {
110 /* we have an exporter, but no importer. */
111 brain->box_send_state = DSYNC_BOX_STATE_ATTRIBUTES;
112 brain->box_recv_state = brain->mail_requests ?
113 DSYNC_BOX_STATE_MAIL_REQUESTS :
114 DSYNC_BOX_STATE_RECV_LAST_COMMON;
115 } else if (brain->backup_recv) {
116 /* we have an importer, but no exporter */
117 brain->box_send_state = brain->mail_requests ?
118 DSYNC_BOX_STATE_MAIL_REQUESTS :
119 DSYNC_BOX_STATE_DONE;
120 brain->box_recv_state = DSYNC_BOX_STATE_ATTRIBUTES;
121 } else {
122 brain->box_send_state = DSYNC_BOX_STATE_ATTRIBUTES;
123 brain->box_recv_state = DSYNC_BOX_STATE_ATTRIBUTES;
124 }
125 }
126
127 static void
dsync_brain_sync_mailbox_init(struct dsync_brain * brain,struct mailbox * box,struct file_lock * lock,const struct dsync_mailbox * local_dsync_box,bool wait_for_remote_box)128 dsync_brain_sync_mailbox_init(struct dsync_brain *brain,
129 struct mailbox *box,
130 struct file_lock *lock,
131 const struct dsync_mailbox *local_dsync_box,
132 bool wait_for_remote_box)
133 {
134 const struct dsync_mailbox_state *state;
135
136 i_assert(brain->box_importer == NULL);
137 i_assert(brain->box_exporter == NULL);
138 i_assert(box->synced);
139
140 brain->box = box;
141 brain->box_lock = lock;
142 brain->pre_box_state = brain->state;
143 if (wait_for_remote_box) {
144 brain->box_send_state = DSYNC_BOX_STATE_MAILBOX;
145 brain->box_recv_state = DSYNC_BOX_STATE_MAILBOX;
146 } else {
147 dsync_brain_sync_init_box_states(brain);
148 }
149 brain->local_dsync_box = *local_dsync_box;
150 if (brain->dsync_box_pool != NULL)
151 p_clear(brain->dsync_box_pool);
152 else {
153 brain->dsync_box_pool =
154 pool_alloconly_create(MEMPOOL_GROWING"dsync brain box pool", 2048);
155 }
156 dsync_mailbox_cache_field_dup(&brain->local_dsync_box.cache_fields,
157 &local_dsync_box->cache_fields,
158 brain->dsync_box_pool);
159 i_zero(&brain->remote_dsync_box);
160
161 state = dsync_mailbox_state_find(brain, local_dsync_box->mailbox_guid);
162 if (state != NULL)
163 brain->mailbox_state = *state;
164 else {
165 i_zero(&brain->mailbox_state);
166 memcpy(brain->mailbox_state.mailbox_guid,
167 local_dsync_box->mailbox_guid,
168 sizeof(brain->mailbox_state.mailbox_guid));
169 brain->mailbox_state.last_uidvalidity =
170 local_dsync_box->uid_validity;
171 }
172 }
173
174 static void
dsync_brain_sync_mailbox_init_remote(struct dsync_brain * brain,const struct dsync_mailbox * remote_dsync_box)175 dsync_brain_sync_mailbox_init_remote(struct dsync_brain *brain,
176 const struct dsync_mailbox *remote_dsync_box)
177 {
178 enum dsync_mailbox_import_flags import_flags = 0;
179 const struct dsync_mailbox_state *state;
180 uint32_t last_common_uid;
181 uint64_t last_common_modseq, last_common_pvt_modseq;
182
183 i_assert(brain->box_importer == NULL);
184 i_assert(brain->log_scan != NULL);
185
186 i_assert(memcmp(brain->local_dsync_box.mailbox_guid,
187 remote_dsync_box->mailbox_guid,
188 sizeof(remote_dsync_box->mailbox_guid)) == 0);
189
190 brain->remote_dsync_box = *remote_dsync_box;
191 dsync_mailbox_cache_field_dup(&brain->remote_dsync_box.cache_fields,
192 &remote_dsync_box->cache_fields,
193 brain->dsync_box_pool);
194
195 state = dsync_mailbox_state_find(brain, remote_dsync_box->mailbox_guid);
196 if (state != NULL) {
197 last_common_uid = state->last_common_uid;
198 last_common_modseq = state->last_common_modseq;
199 last_common_pvt_modseq = state->last_common_pvt_modseq;
200 } else {
201 last_common_uid = 0;
202 last_common_modseq = 0;
203 last_common_pvt_modseq = 0;
204 }
205
206 if (brain->mail_requests)
207 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_WANT_MAIL_REQUESTS;
208 if (brain->master_brain)
209 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MASTER_BRAIN;
210 if (brain->backup_recv && !brain->no_backup_overwrite)
211 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_REVERT_LOCAL_CHANGES;
212 if (brain->debug)
213 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_DEBUG;
214 if (brain->local_dsync_box.have_save_guids &&
215 (remote_dsync_box->have_save_guids ||
216 (brain->backup_recv && remote_dsync_box->have_guids)))
217 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MAILS_HAVE_GUIDS;
218 if (brain->local_dsync_box.have_only_guid128 ||
219 remote_dsync_box->have_only_guid128)
220 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_MAILS_USE_GUID128;
221 if (brain->no_notify)
222 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_NO_NOTIFY;
223 if (brain->empty_hdr_workaround)
224 import_flags |= DSYNC_MAILBOX_IMPORT_FLAG_EMPTY_HDR_WORKAROUND;
225
226 brain->box_importer = brain->backup_send ? NULL :
227 dsync_mailbox_import_init(brain->box, brain->virtual_all_box,
228 brain->log_scan,
229 last_common_uid, last_common_modseq,
230 last_common_pvt_modseq,
231 remote_dsync_box->uid_next,
232 remote_dsync_box->first_recent_uid,
233 remote_dsync_box->highest_modseq,
234 remote_dsync_box->highest_pvt_modseq,
235 brain->sync_since_timestamp,
236 brain->sync_until_timestamp,
237 brain->sync_max_size,
238 brain->sync_flag,
239 brain->import_commit_msgs_interval,
240 import_flags, brain->hdr_hash_version,
241 brain->hashed_headers);
242 }
243
dsync_brain_sync_mailbox_open(struct dsync_brain * brain,const struct dsync_mailbox * remote_dsync_box)244 int dsync_brain_sync_mailbox_open(struct dsync_brain *brain,
245 const struct dsync_mailbox *remote_dsync_box)
246 {
247 struct mailbox_status status;
248 enum dsync_mailbox_exporter_flags exporter_flags = 0;
249 uint32_t last_common_uid, highest_wanted_uid;
250 uint64_t last_common_modseq, last_common_pvt_modseq;
251 const char *desync_reason = "";
252 bool pvt_too_old;
253 int ret;
254
255 i_assert(brain->log_scan == NULL);
256 i_assert(brain->box_exporter == NULL);
257
258 last_common_uid = brain->mailbox_state.last_common_uid;
259 last_common_modseq = brain->mailbox_state.last_common_modseq;
260 last_common_pvt_modseq = brain->mailbox_state.last_common_pvt_modseq;
261 highest_wanted_uid = last_common_uid == 0 ?
262 (uint32_t)-1 : last_common_uid;
263 ret = dsync_transaction_log_scan_init(brain->box->view,
264 brain->box->view_pvt,
265 highest_wanted_uid,
266 last_common_modseq,
267 last_common_pvt_modseq,
268 &brain->log_scan, &pvt_too_old);
269 if (ret < 0) {
270 i_error("Failed to read transaction log for mailbox %s",
271 mailbox_get_vname(brain->box));
272 brain->failed = TRUE;
273 return -1;
274 }
275
276 mailbox_get_open_status(brain->box, STATUS_UIDNEXT |
277 STATUS_HIGHESTMODSEQ |
278 STATUS_HIGHESTPVTMODSEQ, &status);
279 if (ret == 0) {
280 if (pvt_too_old) {
281 desync_reason = t_strdup_printf(
282 "Private modseq %"PRIu64" no longer in transaction log "
283 "(highest=%"PRIu64", last_common_uid=%u, nextuid=%u)",
284 last_common_pvt_modseq,
285 status.highest_pvt_modseq, last_common_uid,
286 status.uidnext);
287 } else {
288 desync_reason = t_strdup_printf(
289 "Modseq %"PRIu64" no longer in transaction log "
290 "(highest=%"PRIu64", last_common_uid=%u, nextuid=%u)",
291 last_common_modseq,
292 status.highest_modseq, last_common_uid,
293 status.uidnext);
294 }
295 }
296
297 if (last_common_uid != 0) {
298 /* if last_common_* is higher than our current ones it means
299 that the incremental sync state is stale and we need to do
300 a full resync */
301 if (status.uidnext < last_common_uid) {
302 desync_reason = t_strdup_printf("uidnext %u < %u",
303 status.uidnext, last_common_uid);
304 ret = 0;
305 } else if (status.highest_modseq < last_common_modseq) {
306 desync_reason = t_strdup_printf("highest_modseq %"PRIu64" < %"PRIu64,
307 status.highest_modseq, last_common_modseq);
308 ret = 0;
309 } else if (status.highest_pvt_modseq < last_common_pvt_modseq) {
310 desync_reason = t_strdup_printf("highest_pvt_modseq %"PRIu64" < %"PRIu64,
311 status.highest_pvt_modseq, last_common_pvt_modseq);
312 ret = 0;
313 }
314 }
315 if (ret == 0) {
316 i_warning("Failed to do incremental sync for mailbox %s, "
317 "retry with a full sync (%s)",
318 mailbox_get_vname(brain->box), desync_reason);
319 dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
320 "Incremental sync failed: %s", desync_reason));
321 brain->require_full_resync = TRUE;
322 return 0;
323 }
324
325 if (!brain->mail_requests)
326 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_AUTO_EXPORT_MAILS;
327 if (remote_dsync_box->have_save_guids &&
328 (brain->local_dsync_box.have_save_guids ||
329 (brain->backup_send && brain->local_dsync_box.have_guids)))
330 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_MAILS_HAVE_GUIDS;
331 if (brain->no_mail_prefetch)
332 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_MINIMAL_DMAIL_FILL;
333 if (brain->sync_since_timestamp > 0 ||
334 brain->sync_until_timestamp > 0)
335 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_TIMESTAMPS;
336 if (brain->sync_max_size > 0)
337 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_VSIZES;
338 if (remote_dsync_box->messages_count == 0) {
339 /* remote mailbox is empty - we don't really need to export
340 header hashes since they're not going to match anything
341 anyway. */
342 exporter_flags |= DSYNC_MAILBOX_EXPORTER_FLAG_NO_HDR_HASHES;
343 }
344
345 brain->box_exporter = brain->backup_recv ? NULL :
346 dsync_mailbox_export_init(brain->box, brain->log_scan,
347 last_common_uid,
348 exporter_flags,
349 brain->hdr_hash_version,
350 brain->hashed_headers);
351 dsync_brain_sync_mailbox_init_remote(brain, remote_dsync_box);
352 return 1;
353 }
354
dsync_brain_sync_mailbox_deinit(struct dsync_brain * brain)355 void dsync_brain_sync_mailbox_deinit(struct dsync_brain *brain)
356 {
357 enum mail_error error;
358
359 i_assert(brain->box != NULL);
360
361 array_push_back(&brain->remote_mailbox_states, &brain->mailbox_state);
362 if (brain->box_exporter != NULL) {
363 const char *errstr;
364
365 i_assert(brain->failed || brain->require_full_resync ||
366 brain->sync_type == DSYNC_BRAIN_SYNC_TYPE_CHANGED);
367 if (dsync_mailbox_export_deinit(&brain->box_exporter,
368 &errstr, &error) < 0)
369 i_error("Mailbox export failed: %s", errstr);
370 }
371 if (brain->box_importer != NULL) {
372 uint32_t last_common_uid, last_messages_count;
373 uint64_t last_common_modseq, last_common_pvt_modseq;
374 const char *changes_during_sync;
375 bool require_full_resync;
376
377 i_assert(brain->failed);
378 (void)dsync_mailbox_import_deinit(&brain->box_importer,
379 FALSE,
380 &last_common_uid,
381 &last_common_modseq,
382 &last_common_pvt_modseq,
383 &last_messages_count,
384 &changes_during_sync,
385 &require_full_resync,
386 &brain->mail_error);
387 if (require_full_resync)
388 brain->require_full_resync = TRUE;
389 }
390 if (brain->log_scan != NULL)
391 dsync_transaction_log_scan_deinit(&brain->log_scan);
392 file_lock_free(&brain->box_lock);
393 mailbox_free(&brain->box);
394
395 brain->state = brain->pre_box_state;
396 }
397
dsync_box_get(struct mailbox * box,struct dsync_mailbox * dsync_box_r,enum mail_error * error_r)398 static int dsync_box_get(struct mailbox *box, struct dsync_mailbox *dsync_box_r,
399 enum mail_error *error_r)
400 {
401 const enum mailbox_status_items status_items =
402 STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES |
403 STATUS_FIRST_RECENT_UID | STATUS_HIGHESTMODSEQ |
404 STATUS_HIGHESTPVTMODSEQ;
405 const enum mailbox_metadata_items metadata_items =
406 MAILBOX_METADATA_CACHE_FIELDS | MAILBOX_METADATA_GUID;
407 struct mailbox_status status;
408 struct mailbox_metadata metadata;
409 const char *errstr;
410 enum mail_error error;
411
412 /* get metadata first, since it may autocreate the mailbox */
413 if (mailbox_get_metadata(box, metadata_items, &metadata) < 0 ||
414 mailbox_get_status(box, status_items, &status) < 0) {
415 errstr = mailbox_get_last_internal_error(box, &error);
416 if (error == MAIL_ERROR_NOTFOUND ||
417 error == MAIL_ERROR_NOTPOSSIBLE) {
418 /* Mailbox isn't selectable, try the next one. We
419 should have already caught \Noselect mailboxes, but
420 check them anyway here. The NOTPOSSIBLE check is
421 mainly for invalid mbox files. */
422 return 0;
423 }
424 i_error("Failed to access mailbox %s: %s",
425 mailbox_get_vname(box), errstr);
426 *error_r = error;
427 return -1;
428 }
429
430 i_assert(status.uidvalidity != 0 || status.messages == 0);
431
432 i_zero(dsync_box_r);
433 memcpy(dsync_box_r->mailbox_guid, metadata.guid,
434 sizeof(dsync_box_r->mailbox_guid));
435 dsync_box_r->uid_validity = status.uidvalidity;
436 dsync_box_r->uid_next = status.uidnext;
437 dsync_box_r->messages_count = status.messages;
438 dsync_box_r->first_recent_uid = status.first_recent_uid;
439 dsync_box_r->highest_modseq = status.highest_modseq;
440 dsync_box_r->highest_pvt_modseq = status.highest_pvt_modseq;
441 dsync_mailbox_cache_field_dup(&dsync_box_r->cache_fields,
442 metadata.cache_fields,
443 pool_datastack_create());
444 dsync_box_r->have_guids = status.have_guids;
445 dsync_box_r->have_save_guids = status.have_save_guids;
446 dsync_box_r->have_only_guid128 = status.have_only_guid128;
447 return 1;
448 }
449
450 static bool
dsync_brain_has_mailbox_state_changed(struct dsync_brain * brain,const struct dsync_mailbox * dsync_box)451 dsync_brain_has_mailbox_state_changed(struct dsync_brain *brain,
452 const struct dsync_mailbox *dsync_box)
453 {
454 const struct dsync_mailbox_state *state;
455
456 if (brain->sync_type != DSYNC_BRAIN_SYNC_TYPE_STATE)
457 return TRUE;
458
459 state = dsync_mailbox_state_find(brain, dsync_box->mailbox_guid);
460 return state == NULL ||
461 state->last_uidvalidity != dsync_box->uid_validity ||
462 state->last_common_uid+1 != dsync_box->uid_next ||
463 state->last_common_modseq != dsync_box->highest_modseq ||
464 state->last_common_pvt_modseq != dsync_box->highest_pvt_modseq ||
465 state->last_messages_count != dsync_box->messages_count;
466 }
467
468 static int
dsync_brain_try_next_mailbox(struct dsync_brain * brain,struct mailbox ** box_r,struct file_lock ** lock_r,struct dsync_mailbox * dsync_box_r)469 dsync_brain_try_next_mailbox(struct dsync_brain *brain, struct mailbox **box_r,
470 struct file_lock **lock_r,
471 struct dsync_mailbox *dsync_box_r)
472 {
473 enum mailbox_flags flags = 0;
474 struct dsync_mailbox dsync_box;
475 struct mailbox *box;
476 struct file_lock *lock = NULL;
477 struct dsync_mailbox_node *node;
478 const char *vname = NULL;
479 enum mail_error error;
480 bool synced = FALSE;
481 int ret;
482
483 *box_r = NULL;
484
485 while (dsync_mailbox_tree_iter_next(brain->local_tree_iter, &vname, &node)) {
486 if (node->existence == DSYNC_MAILBOX_NODE_EXISTS &&
487 !guid_128_is_empty(node->mailbox_guid))
488 break;
489 vname = NULL;
490 }
491 if (vname == NULL) {
492 /* no more mailboxes */
493 dsync_mailbox_tree_iter_deinit(&brain->local_tree_iter);
494 return -1;
495 }
496
497 if (brain->backup_send) {
498 /* make sure mailbox isn't modified */
499 flags |= MAILBOX_FLAG_READONLY;
500 }
501 box = mailbox_alloc(node->ns->list, vname, flags);
502 for (;;) {
503 if ((ret = dsync_box_get(box, &dsync_box, &error)) <= 0) {
504 if (ret < 0) {
505 brain->mail_error = error;
506 brain->failed = TRUE;
507 }
508 mailbox_free(&box);
509 file_lock_free(&lock);
510 return ret;
511 }
512
513 /* if mailbox's last_common_* state equals the current state,
514 we can skip the mailbox */
515 if (!dsync_brain_has_mailbox_state_changed(brain, &dsync_box)) {
516 if (brain->debug) {
517 i_debug("brain %c: Skipping mailbox %s with unchanged state "
518 "uidvalidity=%u uidnext=%u highestmodseq=%"PRIu64" highestpvtmodseq=%"PRIu64" messages=%u",
519 brain->master_brain ? 'M' : 'S',
520 guid_128_to_string(dsync_box.mailbox_guid),
521 dsync_box.uid_validity,
522 dsync_box.uid_next,
523 dsync_box.highest_modseq,
524 dsync_box.highest_pvt_modseq,
525 dsync_box.messages_count);
526 }
527 mailbox_free(&box);
528 file_lock_free(&lock);
529 return 0;
530 }
531 if (synced) {
532 /* ok, the mailbox really changed */
533 break;
534 }
535
536 /* mailbox appears to have changed. do a full sync here and get the
537 state again. Lock before syncing. */
538 if (dsync_mailbox_lock(brain, box, &lock) < 0) {
539 brain->failed = TRUE;
540 mailbox_free(&box);
541 return -1;
542 }
543 if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
544 i_error("Can't sync mailbox %s: %s",
545 mailbox_get_vname(box),
546 mailbox_get_last_internal_error(box, &brain->mail_error));
547 brain->failed = TRUE;
548 mailbox_free(&box);
549 file_lock_free(&lock);
550 return -1;
551 }
552 synced = TRUE;
553 }
554
555 *box_r = box;
556 *lock_r = lock;
557 *dsync_box_r = dsync_box;
558 return 1;
559 }
560
561 static bool
dsync_brain_next_mailbox(struct dsync_brain * brain,struct mailbox ** box_r,struct file_lock ** lock_r,struct dsync_mailbox * dsync_box_r)562 dsync_brain_next_mailbox(struct dsync_brain *brain, struct mailbox **box_r,
563 struct file_lock **lock_r,
564 struct dsync_mailbox *dsync_box_r)
565 {
566 int ret;
567
568 if (brain->no_mail_sync)
569 return FALSE;
570
571 while ((ret = dsync_brain_try_next_mailbox(brain, box_r, lock_r, dsync_box_r)) == 0)
572 ;
573 return ret > 0;
574 }
575
dsync_brain_master_send_mailbox(struct dsync_brain * brain)576 void dsync_brain_master_send_mailbox(struct dsync_brain *brain)
577 {
578 struct dsync_mailbox dsync_box;
579 struct mailbox *box;
580 struct file_lock *lock;
581
582 i_assert(brain->master_brain);
583 i_assert(brain->box == NULL);
584
585 if (!dsync_brain_next_mailbox(brain, &box, &lock, &dsync_box)) {
586 brain->state = DSYNC_STATE_FINISH;
587 dsync_ibc_send_end_of_list(brain->ibc, DSYNC_IBC_EOL_MAILBOX);
588 return;
589 }
590
591 /* start exporting this mailbox (wait for remote to start importing) */
592 dsync_ibc_send_mailbox(brain->ibc, &dsync_box);
593 dsync_brain_sync_mailbox_init(brain, box, lock, &dsync_box, TRUE);
594 brain->state = DSYNC_STATE_SYNC_MAILS;
595 }
596
dsync_boxes_need_sync(struct dsync_brain * brain,const struct dsync_mailbox * box1,const struct dsync_mailbox * box2)597 bool dsync_boxes_need_sync(struct dsync_brain *brain,
598 const struct dsync_mailbox *box1,
599 const struct dsync_mailbox *box2)
600 {
601 if (brain->no_mail_sync)
602 return FALSE;
603 if (brain->sync_type != DSYNC_BRAIN_SYNC_TYPE_CHANGED)
604 return TRUE;
605 return box1->highest_modseq != box2->highest_modseq ||
606 box1->highest_pvt_modseq != box2->highest_pvt_modseq ||
607 box1->messages_count != box2->messages_count ||
608 box1->uid_next != box2->uid_next ||
609 box1->uid_validity != box2->uid_validity ||
610 box1->first_recent_uid != box2->first_recent_uid;
611 }
612
613 static int
mailbox_cache_field_name_cmp(const struct mailbox_cache_field * f1,const struct mailbox_cache_field * f2)614 mailbox_cache_field_name_cmp(const struct mailbox_cache_field *f1,
615 const struct mailbox_cache_field *f2)
616 {
617 return strcmp(f1->name, f2->name);
618 }
619
620 static void
dsync_cache_fields_update(const struct dsync_mailbox * local_box,const struct dsync_mailbox * remote_box,struct mailbox * box,struct mailbox_update * update)621 dsync_cache_fields_update(const struct dsync_mailbox *local_box,
622 const struct dsync_mailbox *remote_box,
623 struct mailbox *box,
624 struct mailbox_update *update)
625 {
626 ARRAY_TYPE(mailbox_cache_field) local_sorted, remote_sorted, changes;
627 const struct mailbox_cache_field *local_fields, *remote_fields;
628 unsigned int li, ri, local_count, remote_count;
629 time_t drop_older_timestamp;
630 int ret;
631
632 if (array_count(&remote_box->cache_fields) == 0) {
633 /* remote has no cached fields. there's nothing to update. */
634 return;
635 }
636
637 t_array_init(&local_sorted, array_count(&local_box->cache_fields));
638 t_array_init(&remote_sorted, array_count(&remote_box->cache_fields));
639 array_append_array(&local_sorted, &local_box->cache_fields);
640 array_append_array(&remote_sorted, &remote_box->cache_fields);
641 array_sort(&local_sorted, mailbox_cache_field_name_cmp);
642 array_sort(&remote_sorted, mailbox_cache_field_name_cmp);
643
644 if (array_count(&local_sorted) == 0) {
645 /* local has no cached fields. set them to same as remote. */
646 array_append_zero(&remote_sorted);
647 update->cache_updates = array_front(&remote_sorted);
648 return;
649 }
650
651 /* figure out what to change */
652 local_fields = array_get(&local_sorted, &local_count);
653 remote_fields = array_get(&remote_sorted, &remote_count);
654 t_array_init(&changes, local_count + remote_count);
655 drop_older_timestamp = ioloop_time -
656 box->index->optimization_set.cache.unaccessed_field_drop_secs;
657
658 for (li = ri = 0; li < local_count || ri < remote_count; ) {
659 ret = li == local_count ? 1 :
660 ri == remote_count ? -1 :
661 strcmp(local_fields[li].name, remote_fields[ri].name);
662 if (ret == 0) {
663 /* field exists in both local and remote */
664 const struct mailbox_cache_field *lf = &local_fields[li];
665 const struct mailbox_cache_field *rf = &remote_fields[ri];
666
667 if (lf->last_used > rf->last_used ||
668 (lf->last_used == rf->last_used &&
669 lf->decision > rf->decision)) {
670 /* use local decision and timestamp */
671 } else {
672 array_push_back(&changes, rf);
673 }
674 li++; ri++;
675 } else if (ret < 0) {
676 /* remote field doesn't exist */
677 li++;
678 } else {
679 /* local field doesn't exist */
680 if (remote_fields[ri].last_used < drop_older_timestamp) {
681 /* field hasn't be used for a long time, remote
682 will probably drop this soon as well */
683 } else {
684 array_push_back(&changes, &remote_fields[ri]);
685 }
686 ri++;
687 }
688 }
689 i_assert(li == local_count && ri == remote_count);
690 if (array_count(&changes) > 0) {
691 array_append_zero(&changes);
692 update->cache_updates = array_front(&changes);
693 }
694 }
695
dsync_brain_mailbox_update_pre(struct dsync_brain * brain,struct mailbox * box,const struct dsync_mailbox * local_box,const struct dsync_mailbox * remote_box,const char ** reason_r)696 bool dsync_brain_mailbox_update_pre(struct dsync_brain *brain,
697 struct mailbox *box,
698 const struct dsync_mailbox *local_box,
699 const struct dsync_mailbox *remote_box,
700 const char **reason_r)
701 {
702 struct mailbox_update update;
703 const struct dsync_mailbox_state *state;
704 bool ret = TRUE;
705
706 *reason_r = NULL;
707 i_zero(&update);
708
709 if (local_box->uid_validity != remote_box->uid_validity) {
710 /* Keep the UIDVALIDITY for the mailbox that has more
711 messages. If they equal, use the higher UIDVALIDITY. */
712 if (remote_box->messages_count > local_box->messages_count ||
713 (remote_box->messages_count == local_box->messages_count &&
714 remote_box->uid_validity > local_box->uid_validity))
715 update.uid_validity = remote_box->uid_validity;
716
717 state = dsync_mailbox_state_find(brain, local_box->mailbox_guid);
718 if (state != NULL && state->last_common_uid > 0) {
719 /* we can't continue syncing this mailbox in this
720 session, because the other side already started
721 sending mailbox changes, but not for all mails. */
722 dsync_mailbox_state_remove(brain, local_box->mailbox_guid);
723 *reason_r = "UIDVALIDITY changed during a stateful sync, need to restart";
724 brain->failed = TRUE;
725 ret = FALSE;
726 }
727 }
728
729 dsync_cache_fields_update(local_box, remote_box, box, &update);
730
731 if (update.uid_validity == 0 &&
732 update.cache_updates == NULL) {
733 /* no changes */
734 return ret;
735 }
736
737 if (mailbox_update(box, &update) < 0) {
738 i_error("Couldn't update mailbox %s metadata: %s",
739 mailbox_get_vname(box),
740 mailbox_get_last_internal_error(box, &brain->mail_error));
741 brain->failed = TRUE;
742 }
743 return ret;
744 }
745
746 static void
dsync_brain_slave_send_mailbox_lost(struct dsync_brain * brain,const struct dsync_mailbox * dsync_box,bool ignore)747 dsync_brain_slave_send_mailbox_lost(struct dsync_brain *brain,
748 const struct dsync_mailbox *dsync_box,
749 bool ignore)
750 {
751 struct dsync_mailbox delete_box;
752
753 if (brain->debug) {
754 i_debug("brain %c: We don't have mailbox %s",
755 brain->master_brain ? 'M' : 'S',
756 guid_128_to_string(dsync_box->mailbox_guid));
757 }
758 i_zero(&delete_box);
759 memcpy(delete_box.mailbox_guid, dsync_box->mailbox_guid,
760 sizeof(delete_box.mailbox_guid));
761 t_array_init(&delete_box.cache_fields, 0);
762 if (ignore)
763 delete_box.mailbox_ignore = TRUE;
764 else
765 delete_box.mailbox_lost = TRUE;
766 dsync_ibc_send_mailbox(brain->ibc, &delete_box);
767 }
768
dsync_brain_slave_recv_mailbox(struct dsync_brain * brain)769 bool dsync_brain_slave_recv_mailbox(struct dsync_brain *brain)
770 {
771 const struct dsync_mailbox *dsync_box;
772 struct dsync_mailbox local_dsync_box;
773 struct mailbox *box;
774 struct file_lock *lock;
775 const char *errstr, *resync_reason;
776 enum mail_error error;
777 int ret;
778 bool resync;
779
780 i_assert(!brain->master_brain);
781 i_assert(brain->box == NULL);
782
783 if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0)
784 return FALSE;
785 if (ret < 0) {
786 brain->state = DSYNC_STATE_FINISH;
787 return TRUE;
788 }
789
790 if (dsync_brain_mailbox_alloc(brain, dsync_box->mailbox_guid,
791 &box, &errstr, &error) < 0) {
792 i_error("Couldn't allocate mailbox GUID %s: %s",
793 guid_128_to_string(dsync_box->mailbox_guid), errstr);
794 brain->mail_error = error;
795 brain->failed = TRUE;
796 return TRUE;
797 }
798 if (box == NULL) {
799 /* mailbox was probably deleted/renamed during sync */
800 if (brain->backup_send && brain->no_backup_overwrite) {
801 if (brain->debug) {
802 i_debug("brain %c: Ignore nonexistent "
803 "mailbox GUID %s with -1 sync",
804 brain->master_brain ? 'M' : 'S',
805 guid_128_to_string(dsync_box->mailbox_guid));
806 }
807 dsync_brain_slave_send_mailbox_lost(brain, dsync_box, TRUE);
808 return TRUE;
809 }
810 //FIXME: verify this from log, and if not log an error.
811 dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
812 "Mailbox GUID %s was lost",
813 guid_128_to_string(dsync_box->mailbox_guid)));
814 dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE);
815 return TRUE;
816 }
817 /* Lock before syncing */
818 if (dsync_mailbox_lock(brain, box, &lock) < 0) {
819 mailbox_free(&box);
820 brain->failed = TRUE;
821 return TRUE;
822 }
823 if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ) < 0) {
824 i_error("Can't sync mailbox %s: %s",
825 mailbox_get_vname(box),
826 mailbox_get_last_internal_error(box, &brain->mail_error));
827 file_lock_free(&lock);
828 mailbox_free(&box);
829 brain->failed = TRUE;
830 return TRUE;
831 }
832
833 if ((ret = dsync_box_get(box, &local_dsync_box, &error)) <= 0) {
834 file_lock_free(&lock);
835 mailbox_free(&box);
836 if (ret < 0) {
837 brain->mail_error = error;
838 brain->failed = TRUE;
839 return TRUE;
840 }
841 /* another process just deleted this mailbox? */
842 if (brain->debug) {
843 i_debug("brain %c: Skipping lost mailbox %s",
844 brain->master_brain ? 'M' : 'S',
845 guid_128_to_string(dsync_box->mailbox_guid));
846 }
847 dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE);
848 return TRUE;
849 }
850 i_assert(local_dsync_box.uid_validity != 0);
851 i_assert(memcmp(dsync_box->mailbox_guid, local_dsync_box.mailbox_guid,
852 sizeof(dsync_box->mailbox_guid)) == 0);
853
854 resync = !dsync_brain_mailbox_update_pre(brain, box, &local_dsync_box,
855 dsync_box, &resync_reason);
856
857 if (!dsync_boxes_need_sync(brain, &local_dsync_box, dsync_box)) {
858 /* no fields appear to have changed, skip this mailbox */
859 if (brain->debug) {
860 i_debug("brain %c: Skipping unchanged mailbox %s",
861 brain->master_brain ? 'M' : 'S',
862 guid_128_to_string(dsync_box->mailbox_guid));
863 }
864 dsync_ibc_send_mailbox(brain->ibc, &local_dsync_box);
865 file_lock_free(&lock);
866 mailbox_free(&box);
867 return TRUE;
868 }
869
870 /* start export/import */
871 dsync_brain_sync_mailbox_init(brain, box, lock, &local_dsync_box, FALSE);
872 if ((ret = dsync_brain_sync_mailbox_open(brain, dsync_box)) < 0)
873 return TRUE;
874 if (resync)
875 dsync_brain_set_changes_during_sync(brain, resync_reason);
876 if (ret == 0 || resync) {
877 brain->require_full_resync = TRUE;
878 dsync_brain_sync_mailbox_deinit(brain);
879 dsync_brain_slave_send_mailbox_lost(brain, dsync_box, FALSE);
880 return TRUE;
881 }
882
883 dsync_ibc_send_mailbox(brain->ibc, &local_dsync_box);
884 brain->state = DSYNC_STATE_SYNC_MAILS;
885 return TRUE;
886 }
887