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