1 /* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "istream.h"
5 #include "dsync-ibc.h"
6 #include "dsync-mail.h"
7 #include "dsync-mailbox-import.h"
8 #include "dsync-mailbox-export.h"
9 #include "dsync-brain-private.h"
10 
11 const char *dsync_box_state_names[DSYNC_BOX_STATE_DONE+1] = {
12 	"mailbox",
13 	"changes",
14 	"attributes",
15 	"mail_requests",
16 	"mails",
17 	"recv_last_common",
18 	"done"
19 };
20 
dsync_brain_master_sync_recv_mailbox(struct dsync_brain * brain)21 static bool dsync_brain_master_sync_recv_mailbox(struct dsync_brain *brain)
22 {
23 	const struct dsync_mailbox *dsync_box;
24 	const char *resync_reason;
25 	enum dsync_ibc_recv_ret ret;
26 	bool resync;
27 
28 	i_assert(brain->master_brain);
29 
30 	if ((ret = dsync_ibc_recv_mailbox(brain->ibc, &dsync_box)) == 0)
31 		return FALSE;
32 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
33 		i_error("Remote sent end-of-list instead of a mailbox");
34 		brain->failed = TRUE;
35 		return TRUE;
36 	}
37 	if (memcmp(dsync_box->mailbox_guid, brain->local_dsync_box.mailbox_guid,
38 		   sizeof(dsync_box->mailbox_guid)) != 0) {
39 		i_error("Remote sent mailbox with a wrong GUID");
40 		brain->failed = TRUE;
41 		return TRUE;
42 	}
43 
44 	if (dsync_box->mailbox_ignore) {
45 		/* ignore this box */
46 		if (brain->debug)
47 			i_debug("brain %c: Ignoring missing remote box GUID %s",
48 				brain->master_brain ? 'M' : 'S',
49 			        guid_128_to_string(dsync_box->mailbox_guid));
50 		dsync_brain_sync_mailbox_deinit(brain);
51 		return TRUE;
52 	}
53 	if (dsync_box->mailbox_lost) {
54 		/* remote lost the mailbox. it's probably already deleted, but
55 		   verify it on next sync just to be sure */
56 		dsync_brain_set_changes_during_sync(brain, t_strdup_printf(
57 			"Remote lost mailbox GUID %s (maybe it was just deleted?)",
58 			guid_128_to_string(dsync_box->mailbox_guid)));
59 		brain->require_full_resync = TRUE;
60 		dsync_brain_sync_mailbox_deinit(brain);
61 		return TRUE;
62 	}
63 	resync = !dsync_brain_mailbox_update_pre(brain, brain->box,
64 						 &brain->local_dsync_box,
65 						 dsync_box, &resync_reason);
66 
67 	if (!dsync_boxes_need_sync(brain, &brain->local_dsync_box, dsync_box)) {
68 		/* no fields appear to have changed, skip this mailbox */
69 		dsync_brain_sync_mailbox_deinit(brain);
70 		return TRUE;
71 	}
72 	if ((ret = dsync_brain_sync_mailbox_open(brain, dsync_box)) < 0)
73 		return TRUE;
74 	if (resync)
75 		dsync_brain_set_changes_during_sync(brain, resync_reason);
76 	if (ret == 0 || resync) {
77 		brain->require_full_resync = TRUE;
78 		brain->failed = TRUE;
79 		dsync_brain_sync_mailbox_deinit(brain);
80 		return TRUE;
81 	}
82 	dsync_brain_sync_init_box_states(brain);
83 	return TRUE;
84 }
85 
dsync_brain_recv_mailbox_attribute(struct dsync_brain * brain)86 static bool dsync_brain_recv_mailbox_attribute(struct dsync_brain *brain)
87 {
88 	const struct dsync_mailbox_attribute *attr;
89 	struct istream *input;
90 	enum dsync_ibc_recv_ret ret;
91 
92 	if ((ret = dsync_ibc_recv_mailbox_attribute(brain->ibc, &attr)) == 0)
93 		return FALSE;
94 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
95 		brain->box_recv_state = DSYNC_BOX_STATE_CHANGES;
96 		return TRUE;
97 	}
98 	if (dsync_mailbox_import_attribute(brain->box_importer, attr) < 0)
99 		brain->failed = TRUE;
100 	input = attr->value_stream;
101 	i_stream_unref(&input);
102 	return TRUE;
103 }
104 
dsync_brain_send_end_of_list(struct dsync_brain * brain,enum dsync_ibc_eol_type type)105 static void dsync_brain_send_end_of_list(struct dsync_brain *brain,
106 					 enum dsync_ibc_eol_type type)
107 {
108 	i_assert(!brain->failed);
109 	dsync_ibc_send_end_of_list(brain->ibc, type);
110 }
111 
dsync_brain_export_deinit(struct dsync_brain * brain)112 static int dsync_brain_export_deinit(struct dsync_brain *brain)
113 {
114 	const char *errstr;
115 	enum mail_error error;
116 
117 	if (dsync_mailbox_export_deinit(&brain->box_exporter,
118 					&errstr, &error) < 0) {
119 		i_error("Exporting mailbox %s failed: %s",
120 			mailbox_get_vname(brain->box), errstr);
121 		brain->mail_error = error;
122 		brain->failed = TRUE;
123 		return -1;
124 	}
125 	return 0;
126 }
127 
dsync_brain_send_mailbox_attribute(struct dsync_brain * brain)128 static void dsync_brain_send_mailbox_attribute(struct dsync_brain *brain)
129 {
130 	const struct dsync_mailbox_attribute *attr;
131 	int ret;
132 
133 	while ((ret = dsync_mailbox_export_next_attr(brain->box_exporter, &attr)) > 0) {
134 		if (dsync_ibc_send_mailbox_attribute(brain->ibc, attr) == 0)
135 			return;
136 	}
137 	if (ret < 0) {
138 		if (dsync_brain_export_deinit(brain) == 0)
139 			i_unreached();
140 		return;
141 	}
142 	dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAILBOX_ATTRIBUTE);
143 	brain->box_send_state = DSYNC_BOX_STATE_CHANGES;
144 }
145 
dsync_brain_recv_mail_change(struct dsync_brain * brain)146 static bool dsync_brain_recv_mail_change(struct dsync_brain *brain)
147 {
148 	const struct dsync_mail_change *change;
149 	enum dsync_ibc_recv_ret ret;
150 
151 	if ((ret = dsync_ibc_recv_change(brain->ibc, &change)) == 0)
152 		return FALSE;
153 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
154 		if (dsync_mailbox_import_changes_finish(brain->box_importer) < 0)
155 			brain->failed = TRUE;
156 		if (brain->mail_requests && brain->box_exporter != NULL)
157 			brain->box_recv_state = DSYNC_BOX_STATE_MAIL_REQUESTS;
158 		else
159 			brain->box_recv_state = DSYNC_BOX_STATE_MAILS;
160 		return TRUE;
161 	}
162 	if (dsync_mailbox_import_change(brain->box_importer, change) < 0)
163 		brain->failed = TRUE;
164 	return TRUE;
165 }
166 
dsync_brain_send_mail_change(struct dsync_brain * brain)167 static void dsync_brain_send_mail_change(struct dsync_brain *brain)
168 {
169 	const struct dsync_mail_change *change;
170 	int ret;
171 
172 	while ((ret = dsync_mailbox_export_next(brain->box_exporter, &change)) > 0) {
173 		if (dsync_ibc_send_change(brain->ibc, change) == 0)
174 			return;
175 	}
176 	if (ret < 0) {
177 		if (dsync_brain_export_deinit(brain) == 0)
178 			i_unreached();
179 		return;
180 	}
181 	dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAIL_CHANGES);
182 	if (brain->mail_requests && brain->box_importer != NULL)
183 		brain->box_send_state = DSYNC_BOX_STATE_MAIL_REQUESTS;
184 	else
185 		brain->box_send_state = DSYNC_BOX_STATE_MAILS;
186 }
187 
dsync_brain_recv_mail_request(struct dsync_brain * brain)188 static bool dsync_brain_recv_mail_request(struct dsync_brain *brain)
189 {
190 	const struct dsync_mail_request *request;
191 	enum dsync_ibc_recv_ret ret;
192 
193 	i_assert(brain->mail_requests);
194 	i_assert(brain->box_exporter != NULL);
195 
196 	if ((ret = dsync_ibc_recv_mail_request(brain->ibc, &request)) == 0)
197 		return FALSE;
198 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
199 		brain->box_recv_state = brain->box_importer != NULL ?
200 			DSYNC_BOX_STATE_MAILS :
201 			DSYNC_BOX_STATE_RECV_LAST_COMMON;
202 		return TRUE;
203 	}
204 	dsync_mailbox_export_want_mail(brain->box_exporter, request);
205 	return TRUE;
206 }
207 
dsync_brain_send_mail_request(struct dsync_brain * brain)208 static bool dsync_brain_send_mail_request(struct dsync_brain *brain)
209 {
210 	const struct dsync_mail_request *request;
211 
212 	i_assert(brain->mail_requests);
213 
214 	while ((request = dsync_mailbox_import_next_request(brain->box_importer)) != NULL) {
215 		if (dsync_ibc_send_mail_request(brain->ibc, request) == 0)
216 			return TRUE;
217 	}
218 	if (brain->box_recv_state < DSYNC_BOX_STATE_MAIL_REQUESTS)
219 		return FALSE;
220 
221 	dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAIL_REQUESTS);
222 	if (brain->box_exporter != NULL)
223 		brain->box_send_state = DSYNC_BOX_STATE_MAILS;
224 	else {
225 		i_assert(brain->box_recv_state != DSYNC_BOX_STATE_DONE);
226 		brain->box_send_state = DSYNC_BOX_STATE_DONE;
227 	}
228 	return TRUE;
229 }
230 
dsync_brain_sync_half_finished(struct dsync_brain * brain)231 static void dsync_brain_sync_half_finished(struct dsync_brain *brain)
232 {
233 	struct dsync_mailbox_state state;
234 	const char *changes_during_sync;
235 	bool require_full_resync;
236 
237 	if (brain->box_recv_state < DSYNC_BOX_STATE_RECV_LAST_COMMON ||
238 	    brain->box_send_state < DSYNC_BOX_STATE_RECV_LAST_COMMON)
239 		return;
240 
241 	/* finished with this mailbox */
242 	i_zero(&state);
243 	memcpy(state.mailbox_guid, brain->local_dsync_box.mailbox_guid,
244 	       sizeof(state.mailbox_guid));
245 	state.last_uidvalidity = brain->local_dsync_box.uid_validity;
246 	if (brain->box_importer == NULL) {
247 		/* this mailbox didn't exist on remote */
248 		state.last_common_uid = brain->local_dsync_box.uid_next-1;
249 		state.last_common_modseq =
250 			brain->local_dsync_box.highest_modseq;
251 		state.last_common_pvt_modseq =
252 			brain->local_dsync_box.highest_pvt_modseq;
253 		state.last_messages_count =
254 			brain->local_dsync_box.messages_count;
255 	} else {
256 		if (dsync_mailbox_import_deinit(&brain->box_importer,
257 						!brain->failed,
258 						&state.last_common_uid,
259 						&state.last_common_modseq,
260 						&state.last_common_pvt_modseq,
261 						&state.last_messages_count,
262 						&changes_during_sync,
263 						&require_full_resync,
264 						&brain->mail_error) < 0) {
265 			if (require_full_resync) {
266 				/* don't treat this as brain failure or the
267 				   state won't be sent to the other brain.
268 				   this also means we'll continue syncing the
269 				   following mailboxes. */
270 				brain->require_full_resync = TRUE;
271 			} else {
272 				brain->failed = TRUE;
273 			}
274 		}
275 		if (changes_during_sync != NULL) {
276 			state.changes_during_sync = TRUE;
277 			dsync_brain_set_changes_during_sync(brain, changes_during_sync);
278 		}
279 	}
280 	if (brain->require_full_resync) {
281 		state.last_uidvalidity = 0;
282 		state.changes_during_sync = TRUE;
283 	}
284 	brain->mailbox_state = state;
285 	dsync_ibc_send_mailbox_state(brain->ibc, &state);
286 }
287 
dsync_brain_recv_mail(struct dsync_brain * brain)288 static bool dsync_brain_recv_mail(struct dsync_brain *brain)
289 {
290 	struct dsync_mail *mail;
291 	enum dsync_ibc_recv_ret ret;
292 
293 	if ((ret = dsync_ibc_recv_mail(brain->ibc, &mail)) == 0)
294 		return FALSE;
295 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
296 		brain->box_recv_state = DSYNC_BOX_STATE_RECV_LAST_COMMON;
297 		if (brain->box_exporter != NULL &&
298 		    brain->box_send_state >= DSYNC_BOX_STATE_RECV_LAST_COMMON) {
299 			if (dsync_brain_export_deinit(brain) < 0)
300 				return TRUE;
301 		}
302 		dsync_brain_sync_half_finished(brain);
303 		return TRUE;
304 	}
305 	if (brain->debug) {
306 		i_debug("brain %c: import mail uid %u guid %s",
307 			brain->master_brain ? 'M' : 'S', mail->uid, mail->guid);
308 	}
309 	if (dsync_mailbox_import_mail(brain->box_importer, mail) < 0)
310 		brain->failed = TRUE;
311 	i_stream_unref(&mail->input);
312 	return TRUE;
313 }
314 
dsync_brain_send_mail(struct dsync_brain * brain)315 static bool dsync_brain_send_mail(struct dsync_brain *brain)
316 {
317 	const struct dsync_mail *mail;
318 
319 	if (brain->mail_requests &&
320 	    brain->box_recv_state < DSYNC_BOX_STATE_MAILS) {
321 		/* wait for mail requests to finish. we could already start
322 		   exporting, but then we're going to do quite a lot of
323 		   separate searches. especially with pipe backend we'd do
324 		   a separate search for each mail. */
325 		return FALSE;
326 	}
327 
328 	while (dsync_mailbox_export_next_mail(brain->box_exporter, &mail) > 0) {
329 		if (dsync_ibc_send_mail(brain->ibc, mail) == 0)
330 			return TRUE;
331 	}
332 
333 	if (dsync_brain_export_deinit(brain) < 0)
334 		return TRUE;
335 
336 	brain->box_send_state = DSYNC_BOX_STATE_DONE;
337 	dsync_brain_send_end_of_list(brain, DSYNC_IBC_EOL_MAILS);
338 
339 	dsync_brain_sync_half_finished(brain);
340 	return TRUE;
341 }
342 
dsync_brain_recv_last_common(struct dsync_brain * brain)343 static bool dsync_brain_recv_last_common(struct dsync_brain *brain)
344 {
345 	enum dsync_ibc_recv_ret ret;
346 	struct dsync_mailbox_state state;
347 
348 	if ((ret = dsync_ibc_recv_mailbox_state(brain->ibc, &state)) == 0)
349 		return FALSE;
350 	if (ret == DSYNC_IBC_RECV_RET_FINISHED) {
351 		i_error("Remote sent end-of-list instead of a mailbox state");
352 		brain->failed = TRUE;
353 		return TRUE;
354 	}
355 	i_assert(brain->box_send_state == DSYNC_BOX_STATE_DONE);
356 	i_assert(memcmp(state.mailbox_guid, brain->local_dsync_box.mailbox_guid,
357 			sizeof(state.mailbox_guid)) == 0);
358 
359 	/* normally the last_common_* values should be the same in local and
360 	   remote, but during unexpected changes they may differ. use the
361 	   values that are lower as the final state. */
362 	if (brain->mailbox_state.last_common_uid > state.last_common_uid)
363 		brain->mailbox_state.last_common_uid = state.last_common_uid;
364 	if (brain->mailbox_state.last_common_modseq > state.last_common_modseq)
365 		brain->mailbox_state.last_common_modseq = state.last_common_modseq;
366 	if (brain->mailbox_state.last_common_pvt_modseq > state.last_common_pvt_modseq)
367 		brain->mailbox_state.last_common_pvt_modseq = state.last_common_pvt_modseq;
368 	if (state.changes_during_sync)
369 		brain->changes_during_remote_sync = TRUE;
370 
371 	dsync_brain_sync_mailbox_deinit(brain);
372 	return TRUE;
373 }
374 
dsync_brain_sync_mails(struct dsync_brain * brain)375 bool dsync_brain_sync_mails(struct dsync_brain *brain)
376 {
377 	bool changed = FALSE;
378 
379 	i_assert(brain->box != NULL);
380 
381 	switch (brain->box_recv_state) {
382 	case DSYNC_BOX_STATE_MAILBOX:
383 		changed = dsync_brain_master_sync_recv_mailbox(brain);
384 		break;
385 	case DSYNC_BOX_STATE_ATTRIBUTES:
386 		changed = dsync_brain_recv_mailbox_attribute(brain);
387 		break;
388 	case DSYNC_BOX_STATE_CHANGES:
389 		changed = dsync_brain_recv_mail_change(brain);
390 		break;
391 	case DSYNC_BOX_STATE_MAIL_REQUESTS:
392 		changed = dsync_brain_recv_mail_request(brain);
393 		break;
394 	case DSYNC_BOX_STATE_MAILS:
395 		changed = dsync_brain_recv_mail(brain);
396 		break;
397 	case DSYNC_BOX_STATE_RECV_LAST_COMMON:
398 		changed = dsync_brain_recv_last_common(brain);
399 		break;
400 	case DSYNC_BOX_STATE_DONE:
401 		break;
402 	}
403 
404 	if (!dsync_ibc_is_send_queue_full(brain->ibc) && !brain->failed) {
405 		switch (brain->box_send_state) {
406 		case DSYNC_BOX_STATE_MAILBOX:
407 			/* wait for mailbox to be received first */
408 			break;
409 		case DSYNC_BOX_STATE_ATTRIBUTES:
410 			dsync_brain_send_mailbox_attribute(brain);
411 			changed = TRUE;
412 			break;
413 		case DSYNC_BOX_STATE_CHANGES:
414 			dsync_brain_send_mail_change(brain);
415 			changed = TRUE;
416 			break;
417 		case DSYNC_BOX_STATE_MAIL_REQUESTS:
418 			if (dsync_brain_send_mail_request(brain))
419 				changed = TRUE;
420 			break;
421 		case DSYNC_BOX_STATE_MAILS:
422 			if (dsync_brain_send_mail(brain))
423 				changed = TRUE;
424 			break;
425 		case DSYNC_BOX_STATE_RECV_LAST_COMMON:
426 			i_unreached();
427 		case DSYNC_BOX_STATE_DONE:
428 			break;
429 		}
430 	}
431 	return changed;
432 }
433