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