~james-page/ubuntu/raring/dovecot/autopkgtest

« back to all changes in this revision

Viewing changes to src/doveadm/dsync/dsync-brain.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-06-11 11:11:54 UTC
  • mfrom: (1.15.2) (4.1.27 sid)
  • Revision ID: package-import@ubuntu.com-20120611111154-678cwbdj6ktgsv1h
Tags: 1:2.1.7-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/{control,rules}: enable PIE hardening.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + d/control: Added Pre-Depends: dpkg (>= 1.15.6) to dovecot-dbg to support
    xz compression in Ubuntu.
  + d/control: Demote dovecot-common Recommends: to Suggests: to prevent
    install of extra packages on upgrade.
  + d/patches/dovecot-drac.patch: Updated with version for dovecot >= 2.0.0.
  + d/control: Drop B-D on systemd.
* Dropped changes:
  + d/patches/fix-racey-restart.patch: part of 2.1.x, no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2009-2012 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "array.h"
 
5
#include "hash.h"
 
6
#include "dsync-worker.h"
 
7
#include "dsync-brain-private.h"
 
8
 
 
9
#include <unistd.h>
 
10
 
 
11
#define DSYNC_WRONG_DIRECTION_ERROR_MSG \
 
12
        "dsync backup: " \
 
13
        "Looks like you're trying to run backup in wrong direction. " \
 
14
        "Source is empty and destination is not."
 
15
 
 
16
static void
 
17
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **list);
 
18
static void
 
19
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **list);
 
20
 
 
21
struct dsync_brain *
 
22
dsync_brain_init(struct dsync_worker *src_worker,
 
23
                 struct dsync_worker *dest_worker,
 
24
                 const char *mailbox, enum dsync_brain_flags flags)
 
25
{
 
26
        struct dsync_brain *brain;
 
27
 
 
28
        brain = i_new(struct dsync_brain, 1);
 
29
        brain->src_worker = src_worker;
 
30
        brain->dest_worker = dest_worker;
 
31
        brain->mailbox = i_strdup(mailbox);
 
32
        brain->flags = flags;
 
33
        brain->verbose = (flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0;
 
34
        brain->backup = (flags & DSYNC_BRAIN_FLAG_BACKUP) != 0;
 
35
        brain->stdout_tty = isatty(STDOUT_FILENO) > 0;
 
36
 
 
37
        if ((flags & DSYNC_BRAIN_FLAG_VERBOSE) != 0) {
 
38
                dsync_worker_set_verbose(src_worker);
 
39
                dsync_worker_set_verbose(dest_worker);
 
40
        }
 
41
        return brain;
 
42
}
 
43
 
 
44
void dsync_brain_fail(struct dsync_brain *brain)
 
45
{
 
46
        brain->failed = TRUE;
 
47
        io_loop_stop(current_ioloop);
 
48
}
 
49
 
 
50
int dsync_brain_deinit(struct dsync_brain **_brain)
 
51
{
 
52
        struct dsync_brain *brain = *_brain;
 
53
        int ret = brain->failed ? -1 : 0;
 
54
 
 
55
        if (brain->state != DSYNC_STATE_SYNC_END)
 
56
                ret = -1;
 
57
        if (brain->to != NULL)
 
58
                timeout_remove(&brain->to);
 
59
 
 
60
        if (ret < 0) {
 
61
                /* make sure we unreference save input streams before workers
 
62
                   are deinitialized, so they can destroy the streams */
 
63
                dsync_worker_msg_save_cancel(brain->src_worker);
 
64
                dsync_worker_msg_save_cancel(brain->dest_worker);
 
65
        }
 
66
 
 
67
        if (brain->mailbox_sync != NULL)
 
68
                dsync_brain_msg_sync_deinit(&brain->mailbox_sync);
 
69
 
 
70
        if (brain->src_mailbox_list != NULL)
 
71
                dsync_brain_mailbox_list_deinit(&brain->src_mailbox_list);
 
72
        if (brain->dest_mailbox_list != NULL)
 
73
                dsync_brain_mailbox_list_deinit(&brain->dest_mailbox_list);
 
74
 
 
75
        if (brain->src_subs_list != NULL)
 
76
                dsync_brain_subs_list_deinit(&brain->src_subs_list);
 
77
        if (brain->dest_subs_list != NULL)
 
78
                dsync_brain_subs_list_deinit(&brain->dest_subs_list);
 
79
 
 
80
        if (dsync_worker_has_failed(brain->src_worker) ||
 
81
            dsync_worker_has_failed(brain->dest_worker))
 
82
                ret = -1;
 
83
 
 
84
        *_brain = NULL;
 
85
        i_free(brain->mailbox);
 
86
        i_free(brain);
 
87
        return ret;
 
88
}
 
89
 
 
90
static void dsync_brain_mailbox_list_finished(struct dsync_brain *brain)
 
91
{
 
92
        if (brain->src_mailbox_list->iter != NULL ||
 
93
            brain->dest_mailbox_list->iter != NULL)
 
94
                return;
 
95
 
 
96
        /* both lists are finished */
 
97
        brain->state++;
 
98
        dsync_brain_sync(brain);
 
99
}
 
100
 
 
101
static void dsync_worker_mailbox_input(void *context)
 
102
{
 
103
        struct dsync_brain_mailbox_list *list = context;
 
104
        struct dsync_mailbox dsync_box, *dup_box;
 
105
        int ret;
 
106
 
 
107
        while ((ret = dsync_worker_mailbox_iter_next(list->iter,
 
108
                                                     &dsync_box)) > 0) {
 
109
                if (list->brain->mailbox != NULL &&
 
110
                    strcmp(list->brain->mailbox, dsync_box.name) != 0)
 
111
                        continue;
 
112
 
 
113
                dup_box = dsync_mailbox_dup(list->pool, &dsync_box);
 
114
                if (!dsync_mailbox_is_noselect(dup_box))
 
115
                        array_append(&list->mailboxes, &dup_box, 1);
 
116
                else
 
117
                        array_append(&list->dirs, &dup_box, 1);
 
118
        }
 
119
        if (ret < 0) {
 
120
                /* finished listing mailboxes */
 
121
                if (dsync_worker_mailbox_iter_deinit(&list->iter) < 0)
 
122
                        dsync_brain_fail(list->brain);
 
123
                array_sort(&list->mailboxes, dsync_mailbox_p_guid_cmp);
 
124
                array_sort(&list->dirs, dsync_mailbox_p_name_sha1_cmp);
 
125
                dsync_brain_mailbox_list_finished(list->brain);
 
126
        }
 
127
}
 
128
 
 
129
static struct dsync_brain_mailbox_list *
 
130
dsync_brain_mailbox_list_init(struct dsync_brain *brain,
 
131
                              struct dsync_worker *worker)
 
132
{
 
133
        struct dsync_brain_mailbox_list *list;
 
134
        pool_t pool;
 
135
 
 
136
        pool = pool_alloconly_create("dsync brain mailbox list", 10240);
 
137
        list = p_new(pool, struct dsync_brain_mailbox_list, 1);
 
138
        list->pool = pool;
 
139
        list->brain = brain;
 
140
        list->worker = worker;
 
141
        list->iter = dsync_worker_mailbox_iter_init(worker);
 
142
        p_array_init(&list->mailboxes, pool, 128);
 
143
        p_array_init(&list->dirs, pool, 32);
 
144
        dsync_worker_set_input_callback(worker, dsync_worker_mailbox_input,
 
145
                                        list);
 
146
        return list;
 
147
}
 
148
 
 
149
static void
 
150
dsync_brain_mailbox_list_deinit(struct dsync_brain_mailbox_list **_list)
 
151
{
 
152
        struct dsync_brain_mailbox_list *list = *_list;
 
153
 
 
154
        *_list = NULL;
 
155
 
 
156
        if (list->iter != NULL)
 
157
                (void)dsync_worker_mailbox_iter_deinit(&list->iter);
 
158
        pool_unref(&list->pool);
 
159
}
 
160
 
 
161
static void dsync_brain_subs_list_finished(struct dsync_brain *brain)
 
162
{
 
163
        if (brain->src_subs_list->iter != NULL ||
 
164
            brain->dest_subs_list->iter != NULL)
 
165
                return;
 
166
 
 
167
        /* both lists are finished */
 
168
        brain->state++;
 
169
        dsync_brain_sync(brain);
 
170
}
 
171
 
 
172
static int
 
173
dsync_worker_subscription_cmp(const struct dsync_worker_subscription *s1,
 
174
                              const struct dsync_worker_subscription *s2)
 
175
{
 
176
        return strcmp(s1->vname, s2->vname);
 
177
}
 
178
 
 
179
static int
 
180
dsync_worker_unsubscription_cmp(const struct dsync_worker_unsubscription *u1,
 
181
                                const struct dsync_worker_unsubscription *u2)
 
182
{
 
183
        int ret;
 
184
 
 
185
        ret = strcmp(u1->ns_prefix, u2->ns_prefix);
 
186
        return ret != 0 ? ret :
 
187
                dsync_guid_cmp(&u1->name_sha1, &u2->name_sha1);
 
188
}
 
189
 
 
190
static void dsync_worker_subs_input(void *context)
 
191
{
 
192
        struct dsync_brain_subs_list *list = context;
 
193
        struct dsync_worker_subscription subs;
 
194
        struct dsync_worker_unsubscription unsubs;
 
195
        int ret;
 
196
 
 
197
        memset(&subs, 0, sizeof(subs));
 
198
        while ((ret = dsync_worker_subs_iter_next(list->iter, &subs)) > 0) {
 
199
                subs.vname = p_strdup(list->pool, subs.vname);
 
200
                subs.storage_name = p_strdup(list->pool, subs.storage_name);
 
201
                subs.ns_prefix = p_strdup(list->pool, subs.ns_prefix);
 
202
                array_append(&list->subscriptions, &subs, 1);
 
203
        }
 
204
        if (ret == 0)
 
205
                return;
 
206
 
 
207
        memset(&unsubs, 0, sizeof(unsubs));
 
208
        while ((ret = dsync_worker_subs_iter_next_un(list->iter,
 
209
                                                     &unsubs)) > 0) {
 
210
                unsubs.ns_prefix = p_strdup(list->pool, unsubs.ns_prefix);
 
211
                array_append(&list->unsubscriptions, &unsubs, 1);
 
212
        }
 
213
 
 
214
        if (ret < 0) {
 
215
                /* finished listing subscriptions */
 
216
                if (dsync_worker_subs_iter_deinit(&list->iter) < 0)
 
217
                        dsync_brain_fail(list->brain);
 
218
                array_sort(&list->subscriptions,
 
219
                           dsync_worker_subscription_cmp);
 
220
                array_sort(&list->unsubscriptions,
 
221
                           dsync_worker_unsubscription_cmp);
 
222
                dsync_brain_subs_list_finished(list->brain);
 
223
        }
 
224
}
 
225
 
 
226
static struct dsync_brain_subs_list *
 
227
dsync_brain_subs_list_init(struct dsync_brain *brain,
 
228
                              struct dsync_worker *worker)
 
229
{
 
230
        struct dsync_brain_subs_list *list;
 
231
        pool_t pool;
 
232
 
 
233
        pool = pool_alloconly_create(MEMPOOL_GROWING"dsync brain subs list",
 
234
                                     1024*4);
 
235
        list = p_new(pool, struct dsync_brain_subs_list, 1);
 
236
        list->pool = pool;
 
237
        list->brain = brain;
 
238
        list->worker = worker;
 
239
        list->iter = dsync_worker_subs_iter_init(worker);
 
240
        p_array_init(&list->subscriptions, pool, 128);
 
241
        p_array_init(&list->unsubscriptions, pool, 64);
 
242
        dsync_worker_set_input_callback(worker, dsync_worker_subs_input, list);
 
243
        return list;
 
244
}
 
245
 
 
246
static void
 
247
dsync_brain_subs_list_deinit(struct dsync_brain_subs_list **_list)
 
248
{
 
249
        struct dsync_brain_subs_list *list = *_list;
 
250
 
 
251
        *_list = NULL;
 
252
 
 
253
        if (list->iter != NULL)
 
254
                (void)dsync_worker_subs_iter_deinit(&list->iter);
 
255
        pool_unref(&list->pool);
 
256
}
 
257
 
 
258
enum dsync_brain_mailbox_action {
 
259
        DSYNC_BRAIN_MAILBOX_ACTION_NONE,
 
260
        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
 
261
        DSYNC_BRAIN_MAILBOX_ACTION_DELETE
 
262
};
 
263
 
 
264
static void
 
265
dsync_brain_mailbox_action(struct dsync_brain *brain,
 
266
                           enum dsync_brain_mailbox_action action,
 
267
                           struct dsync_worker *action_worker,
 
268
                           struct dsync_mailbox *action_box)
 
269
{
 
270
        struct dsync_mailbox new_box;
 
271
 
 
272
        if (brain->backup && action_worker == brain->src_worker) {
 
273
                /* backup mode: switch actions */
 
274
                action_worker = brain->dest_worker;
 
275
                switch (action) {
 
276
                case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
 
277
                        break;
 
278
                case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
 
279
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
 
280
                        break;
 
281
                case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
 
282
                        action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
 
283
                        break;
 
284
                }
 
285
        }
 
286
 
 
287
        switch (action) {
 
288
        case DSYNC_BRAIN_MAILBOX_ACTION_NONE:
 
289
                break;
 
290
        case DSYNC_BRAIN_MAILBOX_ACTION_CREATE:
 
291
                new_box = *action_box;
 
292
                new_box.uid_next = action_box->uid_validity == 0 ? 0 : 1;
 
293
                new_box.first_recent_uid = 0;
 
294
                new_box.highest_modseq = 0;
 
295
                dsync_worker_create_mailbox(action_worker, &new_box);
 
296
                break;
 
297
        case DSYNC_BRAIN_MAILBOX_ACTION_DELETE:
 
298
                if (!dsync_mailbox_is_noselect(action_box))
 
299
                        dsync_worker_delete_mailbox(action_worker, action_box);
 
300
                else
 
301
                        dsync_worker_delete_dir(action_worker, action_box);
 
302
                break;
 
303
        }
 
304
}
 
305
 
 
306
static bool
 
307
dsync_mailbox_list_is_empty(const ARRAY_TYPE(dsync_mailbox) *boxes_arr)
 
308
{
 
309
        struct dsync_mailbox *const *boxes;
 
310
        unsigned int count;
 
311
 
 
312
        boxes = array_get(boxes_arr, &count);
 
313
        if (count == 0)
 
314
                return TRUE;
 
315
        if (count == 1 && strcasecmp(boxes[0]->name, "INBOX") == 0 &&
 
316
            boxes[0]->message_count == 0)
 
317
                return TRUE;
 
318
        return FALSE;
 
319
}
 
320
 
 
321
static void dsync_brain_sync_mailboxes(struct dsync_brain *brain)
 
322
{
 
323
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
 
324
        struct dsync_mailbox *action_box = NULL;
 
325
        struct dsync_worker *action_worker = NULL;
 
326
        unsigned int src, dest, src_count, dest_count;
 
327
        enum dsync_brain_mailbox_action action;
 
328
        bool src_deleted, dest_deleted;
 
329
        int ret;
 
330
 
 
331
        if (brain->backup &&
 
332
            dsync_mailbox_list_is_empty(&brain->src_mailbox_list->mailboxes) &&
 
333
            !dsync_mailbox_list_is_empty(&brain->dest_mailbox_list->mailboxes)) {
 
334
                i_fatal(DSYNC_WRONG_DIRECTION_ERROR_MSG);
 
335
        }
 
336
 
 
337
        /* create/delete missing mailboxes. the mailboxes are sorted by
 
338
           GUID, so we can do this quickly. */
 
339
        src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
 
340
        dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
 
341
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
 
342
                action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
 
343
                src_deleted = (src_boxes[src]->flags &
 
344
                               DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
 
345
                dest_deleted = (dest_boxes[dest]->flags &
 
346
                                DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
 
347
                ret = dsync_mailbox_guid_cmp(src_boxes[src],
 
348
                                             dest_boxes[dest]);
 
349
                if (ret < 0) {
 
350
                        /* exists only in source */
 
351
                        if (!src_deleted) {
 
352
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
 
353
                                action_worker = brain->dest_worker;
 
354
                                action_box = src_boxes[src];
 
355
                        }
 
356
                        src++;
 
357
                } else if (ret > 0) {
 
358
                        /* exists only in dest */
 
359
                        if (!dest_deleted) {
 
360
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
 
361
                                action_worker = brain->src_worker;
 
362
                                action_box = dest_boxes[dest];
 
363
                        }
 
364
                        dest++;
 
365
                } else if (src_deleted) {
 
366
                        /* delete from dest too */
 
367
                        if (!dest_deleted) {
 
368
                                action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
 
369
                                action_worker = brain->dest_worker;
 
370
                                action_box = dest_boxes[dest];
 
371
                        }
 
372
                        src++; dest++;
 
373
                } else if (dest_deleted) {
 
374
                        /* delete from src too */
 
375
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
 
376
                        action_worker = brain->src_worker;
 
377
                        action_box = src_boxes[src];
 
378
                        src++; dest++;
 
379
                } else {
 
380
                        src++; dest++;
 
381
                }
 
382
                dsync_brain_mailbox_action(brain, action,
 
383
                                           action_worker, action_box);
 
384
        }
 
385
        for (; src < src_count; src++) {
 
386
                if ((src_boxes[src]->flags &
 
387
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
 
388
                        continue;
 
389
 
 
390
                dsync_brain_mailbox_action(brain,
 
391
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
 
392
                        brain->dest_worker, src_boxes[src]);
 
393
        }
 
394
        for (; dest < dest_count; dest++) {
 
395
                if ((dest_boxes[dest]->flags &
 
396
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
 
397
                        continue;
 
398
 
 
399
                dsync_brain_mailbox_action(brain,
 
400
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
 
401
                        brain->src_worker, dest_boxes[dest]);
 
402
        }
 
403
}
 
404
 
 
405
static void dsync_brain_sync_dirs(struct dsync_brain *brain)
 
406
{
 
407
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes, *action_box;
 
408
        unsigned int src, dest, src_count, dest_count;
 
409
        enum dsync_brain_mailbox_action action;
 
410
        struct dsync_worker *action_worker = NULL;
 
411
        bool src_deleted, dest_deleted;
 
412
        int ret;
 
413
 
 
414
        /* create/delete missing directories. */
 
415
        src_boxes = array_get(&brain->src_mailbox_list->dirs, &src_count);
 
416
        dest_boxes = array_get(&brain->dest_mailbox_list->dirs, &dest_count);
 
417
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
 
418
                action = DSYNC_BRAIN_MAILBOX_ACTION_NONE;
 
419
                action_box = NULL;
 
420
 
 
421
                src_deleted = (src_boxes[src]->flags &
 
422
                               DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
 
423
                dest_deleted = (dest_boxes[dest]->flags &
 
424
                                DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0;
 
425
                ret = memcmp(src_boxes[src]->name_sha1.guid,
 
426
                             dest_boxes[dest]->name_sha1.guid,
 
427
                             sizeof(src_boxes[src]->name_sha1.guid));
 
428
                if (ret < 0) {
 
429
                        /* exists only in source */
 
430
                        if (!src_deleted) {
 
431
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
 
432
                                action_worker = brain->dest_worker;
 
433
                                action_box = src_boxes[src];
 
434
                        }
 
435
                        src++;
 
436
                } else if (ret > 0) {
 
437
                        /* exists only in dest */
 
438
                        if (!dest_deleted) {
 
439
                                action = DSYNC_BRAIN_MAILBOX_ACTION_CREATE;
 
440
                                action_worker = brain->src_worker;
 
441
                                action_box = dest_boxes[dest];
 
442
                        }
 
443
                        dest++;
 
444
                } else if (src_deleted) {
 
445
                        /* delete from dest too */
 
446
                        if (!dest_deleted) {
 
447
                                action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
 
448
                                action_worker = brain->dest_worker;
 
449
                                action_box = dest_boxes[dest];
 
450
                        }
 
451
                        src++; dest++;
 
452
                } else if (dest_deleted) {
 
453
                        /* delete from src too */
 
454
                        action = DSYNC_BRAIN_MAILBOX_ACTION_DELETE;
 
455
                        action_worker = brain->src_worker;
 
456
                        action_box = src_boxes[src];
 
457
                        src++; dest++;
 
458
                } else {
 
459
                        src++; dest++;
 
460
                }
 
461
                i_assert(action_box == NULL ||
 
462
                         dsync_mailbox_is_noselect(action_box));
 
463
                dsync_brain_mailbox_action(brain, action,
 
464
                                           action_worker, action_box);
 
465
        }
 
466
        for (; src < src_count; src++) {
 
467
                if ((src_boxes[src]->flags &
 
468
                     DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
 
469
                        continue;
 
470
 
 
471
                dsync_brain_mailbox_action(brain,
 
472
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
 
473
                        brain->dest_worker, src_boxes[src]);
 
474
        }
 
475
        for (; dest < dest_count; dest++) {
 
476
                if ((dest_boxes[dest]->flags &
 
477
                     DSYNC_MAILBOX_FLAG_DELETED_DIR) != 0)
 
478
                        continue;
 
479
 
 
480
                dsync_brain_mailbox_action(brain,
 
481
                        DSYNC_BRAIN_MAILBOX_ACTION_CREATE,
 
482
                        brain->src_worker, dest_boxes[dest]);
 
483
        }
 
484
}
 
485
 
 
486
static bool
 
487
dsync_brain_is_unsubscribed(struct dsync_brain_subs_list *list,
 
488
                            const struct dsync_worker_subscription *subs,
 
489
                            time_t *last_change_r)
 
490
{
 
491
        const struct dsync_worker_unsubscription *unsubs;
 
492
        struct dsync_worker_unsubscription lookup;
 
493
 
 
494
        lookup.ns_prefix = subs->ns_prefix;
 
495
        dsync_str_sha_to_guid(subs->storage_name, &lookup.name_sha1);
 
496
        unsubs = array_bsearch(&list->unsubscriptions, &lookup,
 
497
                               dsync_worker_unsubscription_cmp);
 
498
        if (unsubs == NULL) {
 
499
                *last_change_r = 0;
 
500
                return FALSE;
 
501
        } else if (unsubs->last_change <= subs->last_change) {
 
502
                *last_change_r = subs->last_change;
 
503
                return FALSE;
 
504
        } else {
 
505
                *last_change_r = unsubs->last_change;
 
506
                return TRUE;
 
507
        }
 
508
}
 
509
 
 
510
static void dsync_brain_sync_subscriptions(struct dsync_brain *brain)
 
511
{
 
512
        const struct dsync_worker_subscription *src_subs, *dest_subs;
 
513
        const struct dsync_worker_subscription *action_subs;
 
514
        struct dsync_worker *action_worker;
 
515
        unsigned int src, dest, src_count, dest_count;
 
516
        time_t last_change;
 
517
        bool subscribe;
 
518
        int ret;
 
519
 
 
520
        /* subscriptions are sorted by name. */
 
521
        src_subs = array_get(&brain->src_subs_list->subscriptions, &src_count);
 
522
        dest_subs = array_get(&brain->dest_subs_list->subscriptions, &dest_count);
 
523
        for (src = dest = 0;; ) {
 
524
                if (src == src_count) {
 
525
                        if (dest == dest_count)
 
526
                                break;
 
527
                        ret = 1;
 
528
                } else if (dest == dest_count) {
 
529
                        ret = -1;
 
530
                } else {
 
531
                        ret = strcmp(src_subs[src].vname,
 
532
                                     dest_subs[dest].vname);
 
533
                        if (ret == 0) {
 
534
                                src++; dest++;
 
535
                                continue;
 
536
                        }
 
537
                }
 
538
 
 
539
                if (ret < 0) {
 
540
                        /* subscribed only in source */
 
541
                        action_subs = &src_subs[src];
 
542
                        if (dsync_brain_is_unsubscribed(brain->dest_subs_list,
 
543
                                                        &src_subs[src],
 
544
                                                        &last_change)) {
 
545
                                action_worker = brain->src_worker;
 
546
                                subscribe = FALSE;
 
547
                        } else {
 
548
                                action_worker = brain->dest_worker;
 
549
                                subscribe = TRUE;
 
550
                        }
 
551
                        src++;
 
552
                } else {
 
553
                        /* subscribed only in dest */
 
554
                        action_subs = &dest_subs[dest];
 
555
                        if (dsync_brain_is_unsubscribed(brain->src_subs_list,
 
556
                                                        &dest_subs[dest],
 
557
                                                        &last_change)) {
 
558
                                action_worker = brain->dest_worker;
 
559
                                subscribe = FALSE;
 
560
                        } else {
 
561
                                action_worker = brain->src_worker;
 
562
                                subscribe = TRUE;
 
563
                        }
 
564
                        dest++;
 
565
                }
 
566
 
 
567
                if (brain->backup && action_worker == brain->src_worker) {
 
568
                        /* backup mode: switch action */
 
569
                        action_worker = brain->dest_worker;
 
570
                        subscribe = !subscribe;
 
571
                        last_change = ioloop_time;
 
572
                }
 
573
                dsync_worker_set_subscribed(action_worker, action_subs->vname,
 
574
                                            last_change, subscribe);
 
575
        }
 
576
}
 
577
 
 
578
static bool dsync_mailbox_has_changed_msgs(struct dsync_brain *brain,
 
579
                                           const struct dsync_mailbox *box1,
 
580
                                           const struct dsync_mailbox *box2)
 
581
{
 
582
        const char *name = *box1->name != '\0' ? box1->name : box2->name;
 
583
 
 
584
        if (box1->uid_validity != box2->uid_validity) {
 
585
                if (brain->verbose) {
 
586
                        i_info("%s: uidvalidity changed: %u != %u", name,
 
587
                               box1->uid_validity, box2->uid_validity);
 
588
                }
 
589
                return TRUE;
 
590
        }
 
591
        if (box1->uid_next != box2->uid_next) {
 
592
                if (brain->verbose) {
 
593
                        i_info("%s: uidnext changed: %u != %u", name,
 
594
                               box1->uid_next, box2->uid_next);
 
595
                }
 
596
                return TRUE;
 
597
        }
 
598
        if (box1->highest_modseq != box2->highest_modseq) {
 
599
                if (brain->verbose) {
 
600
                        i_info("%s: highest_modseq changed: %llu != %llu", name,
 
601
                               (unsigned long long)box1->highest_modseq,
 
602
                               (unsigned long long)box2->highest_modseq);
 
603
                }
 
604
                return TRUE;
 
605
        }
 
606
        if (box1->message_count != box2->message_count) {
 
607
                if (brain->verbose) {
 
608
                        i_info("%s: message_count changed: %u != %u", name,
 
609
                               box1->message_count, box2->message_count);
 
610
                }
 
611
                return TRUE;
 
612
        }
 
613
        return FALSE;
 
614
}
 
615
 
 
616
static bool dsync_mailbox_has_changes(struct dsync_brain *brain,
 
617
                                      const struct dsync_mailbox *box1,
 
618
                                      const struct dsync_mailbox *box2)
 
619
{
 
620
        if (strcmp(box1->name, box2->name) != 0)
 
621
                return TRUE;
 
622
        return dsync_mailbox_has_changed_msgs(brain, box1, box2);
 
623
}
 
624
 
 
625
static void
 
626
dsync_brain_get_changed_mailboxes(struct dsync_brain *brain,
 
627
                                  ARRAY_TYPE(dsync_brain_mailbox) *brain_boxes,
 
628
                                  bool full_sync)
 
629
{
 
630
        struct dsync_mailbox *const *src_boxes, *const *dest_boxes;
 
631
        struct dsync_brain_mailbox *brain_box;
 
632
        unsigned int src, dest, src_count, dest_count;
 
633
        bool src_deleted, dest_deleted;
 
634
        int ret;
 
635
 
 
636
        src_boxes = array_get(&brain->src_mailbox_list->mailboxes, &src_count);
 
637
        dest_boxes = array_get(&brain->dest_mailbox_list->mailboxes, &dest_count);
 
638
 
 
639
        for (src = dest = 0; src < src_count && dest < dest_count; ) {
 
640
                src_deleted = (src_boxes[src]->flags &
 
641
                               DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
 
642
                dest_deleted = (dest_boxes[dest]->flags &
 
643
                                DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0;
 
644
 
 
645
                ret = dsync_mailbox_guid_cmp(src_boxes[src], dest_boxes[dest]);
 
646
                if (ret == 0) {
 
647
                        if ((full_sync ||
 
648
                             dsync_mailbox_has_changes(brain, src_boxes[src],
 
649
                                                       dest_boxes[dest])) &&
 
650
                            !src_deleted && !dest_deleted) {
 
651
                                brain_box = array_append_space(brain_boxes);
 
652
                                brain_box->box = *src_boxes[src];
 
653
 
 
654
                                brain_box->box.highest_modseq =
 
655
                                        I_MAX(src_boxes[src]->highest_modseq,
 
656
                                              dest_boxes[dest]->highest_modseq);
 
657
                                brain_box->box.uid_next =
 
658
                                        I_MAX(src_boxes[src]->uid_next,
 
659
                                              dest_boxes[dest]->uid_next);
 
660
                                brain_box->src = src_boxes[src];
 
661
                                brain_box->dest = dest_boxes[dest];
 
662
                        }
 
663
                        src++; dest++;
 
664
                } else if (ret < 0) {
 
665
                        /* exists only in source */
 
666
                        if (!src_deleted) {
 
667
                                brain_box = array_append_space(brain_boxes);
 
668
                                brain_box->box = *src_boxes[src];
 
669
                                brain_box->src = src_boxes[src];
 
670
                                if (brain->verbose) {
 
671
                                        i_info("%s: only in source (guid=%s)",
 
672
                                               brain_box->box.name,
 
673
                                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
 
674
                                }
 
675
                        }
 
676
                        src++;
 
677
                } else {
 
678
                        /* exists only in dest */
 
679
                        if (!dest_deleted) {
 
680
                                brain_box = array_append_space(brain_boxes);
 
681
                                brain_box->box = *dest_boxes[dest];
 
682
                                brain_box->dest = dest_boxes[dest];
 
683
                                if (brain->verbose) {
 
684
                                        i_info("%s: only in dest (guid=%s)",
 
685
                                               brain_box->box.name,
 
686
                                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
 
687
                                }
 
688
                        }
 
689
                        dest++;
 
690
                }
 
691
        }
 
692
        for (; src < src_count; src++) {
 
693
                if ((src_boxes[src]->flags &
 
694
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
 
695
                        continue;
 
696
 
 
697
                brain_box = array_append_space(brain_boxes);
 
698
                brain_box->box = *src_boxes[src];
 
699
                brain_box->src = src_boxes[src];
 
700
                if (brain->verbose) {
 
701
                        i_info("%s: only in source (guid=%s)",
 
702
                               brain_box->box.name,
 
703
                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
 
704
                }
 
705
        }
 
706
        for (; dest < dest_count; dest++) {
 
707
                if ((dest_boxes[dest]->flags &
 
708
                     DSYNC_MAILBOX_FLAG_DELETED_MAILBOX) != 0)
 
709
                        continue;
 
710
 
 
711
                brain_box = array_append_space(brain_boxes);
 
712
                brain_box->box = *dest_boxes[dest];
 
713
                brain_box->dest = dest_boxes[dest];
 
714
                if (brain->verbose) {
 
715
                        i_info("%s: only in dest (guid=%s)",
 
716
                               brain_box->box.name,
 
717
                               dsync_guid_to_str(&brain_box->box.mailbox_guid));
 
718
                }
 
719
        }
 
720
}
 
721
 
 
722
static bool dsync_brain_sync_msgs(struct dsync_brain *brain)
 
723
{
 
724
        ARRAY_TYPE(dsync_brain_mailbox) mailboxes;
 
725
        pool_t pool;
 
726
        bool ret;
 
727
 
 
728
        pool = pool_alloconly_create(MEMPOOL_GROWING"dsync changed mailboxes",
 
729
                                     10240);
 
730
        p_array_init(&mailboxes, pool, 128);
 
731
        dsync_brain_get_changed_mailboxes(brain, &mailboxes,
 
732
                (brain->flags & DSYNC_BRAIN_FLAG_FULL_SYNC) != 0);
 
733
        if (array_count(&mailboxes) > 0) {
 
734
                brain->mailbox_sync =
 
735
                        dsync_brain_msg_sync_init(brain, &mailboxes);
 
736
                dsync_brain_msg_sync_more(brain->mailbox_sync);
 
737
                ret = TRUE;
 
738
        } else {
 
739
                ret = FALSE;
 
740
        }
 
741
        pool_unref(&pool);
 
742
        return ret;
 
743
}
 
744
 
 
745
static void
 
746
dsync_brain_sync_rename_mailbox(struct dsync_brain *brain,
 
747
                                const struct dsync_brain_mailbox *mailbox)
 
748
{
 
749
        if (mailbox->src->last_change > mailbox->dest->last_change ||
 
750
            brain->backup) {
 
751
                dsync_worker_rename_mailbox(brain->dest_worker,
 
752
                                            &mailbox->box.mailbox_guid,
 
753
                                            mailbox->src);
 
754
        } else {
 
755
                dsync_worker_rename_mailbox(brain->src_worker,
 
756
                                            &mailbox->box.mailbox_guid,
 
757
                                            mailbox->dest);
 
758
        }
 
759
}
 
760
 
 
761
static void
 
762
dsync_brain_sync_update_mailboxes(struct dsync_brain *brain)
 
763
{
 
764
        const struct dsync_brain_mailbox *mailbox;
 
765
        bool failed_changes = dsync_brain_has_unexpected_changes(brain) ||
 
766
                dsync_worker_has_failed(brain->src_worker) ||
 
767
                dsync_worker_has_failed(brain->dest_worker);
 
768
 
 
769
        if (brain->mailbox_sync == NULL) {
 
770
                /* no mailboxes changed */
 
771
                return;
 
772
        }
 
773
 
 
774
        array_foreach(&brain->mailbox_sync->mailboxes, mailbox) {
 
775
                /* don't update mailboxes if any changes had failed.
 
776
                   for example if some messages couldn't be saved, we don't
 
777
                   want to increase the next_uid to jump over them */
 
778
                if (!brain->backup && !failed_changes) {
 
779
                        dsync_worker_update_mailbox(brain->src_worker,
 
780
                                                    &mailbox->box);
 
781
                }
 
782
                if (!failed_changes) {
 
783
                        dsync_worker_update_mailbox(brain->dest_worker,
 
784
                                                    &mailbox->box);
 
785
                }
 
786
 
 
787
                if (mailbox->src != NULL && mailbox->dest != NULL &&
 
788
                    strcmp(mailbox->src->name, mailbox->dest->name) != 0)
 
789
                        dsync_brain_sync_rename_mailbox(brain, mailbox);
 
790
        }
 
791
}
 
792
 
 
793
static void dsync_brain_worker_finished(bool success, void *context)
 
794
{
 
795
        struct dsync_brain *brain = context;
 
796
 
 
797
        switch (brain->state) {
 
798
        case DSYNC_STATE_SYNC_MSGS_FLUSH:
 
799
        case DSYNC_STATE_SYNC_MSGS_FLUSH2:
 
800
        case DSYNC_STATE_SYNC_FLUSH:
 
801
        case DSYNC_STATE_SYNC_FLUSH2:
 
802
                break;
 
803
        default:
 
804
                i_panic("dsync brain state=%d", brain->state);
 
805
        }
 
806
 
 
807
        if (!success)
 
808
                dsync_brain_fail(brain);
 
809
 
 
810
        brain->state++;
 
811
        if (brain->to == NULL && (brain->flags & DSYNC_BRAIN_FLAG_LOCAL) == 0)
 
812
                brain->to = timeout_add(0, dsync_brain_sync, brain);
 
813
}
 
814
 
 
815
void dsync_brain_sync(struct dsync_brain *brain)
 
816
{
 
817
        if (dsync_worker_has_failed(brain->src_worker) ||
 
818
            dsync_worker_has_failed(brain->dest_worker)) {
 
819
                /* we can't safely continue, especially with backup */
 
820
                return;
 
821
        }
 
822
 
 
823
        if (brain->to != NULL)
 
824
                timeout_remove(&brain->to);
 
825
        switch (brain->state) {
 
826
        case DSYNC_STATE_GET_MAILBOXES:
 
827
                i_assert(brain->src_mailbox_list == NULL);
 
828
                brain->src_mailbox_list =
 
829
                        dsync_brain_mailbox_list_init(brain, brain->src_worker);
 
830
                brain->dest_mailbox_list =
 
831
                        dsync_brain_mailbox_list_init(brain, brain->dest_worker);
 
832
                dsync_worker_mailbox_input(brain->src_mailbox_list);
 
833
                dsync_worker_mailbox_input(brain->dest_mailbox_list);
 
834
                break;
 
835
        case DSYNC_STATE_GET_SUBSCRIPTIONS:
 
836
                i_assert(brain->src_subs_list == NULL);
 
837
                brain->src_subs_list =
 
838
                        dsync_brain_subs_list_init(brain, brain->src_worker);
 
839
                brain->dest_subs_list =
 
840
                        dsync_brain_subs_list_init(brain, brain->dest_worker);
 
841
                dsync_worker_subs_input(brain->src_subs_list);
 
842
                dsync_worker_subs_input(brain->dest_subs_list);
 
843
                break;
 
844
        case DSYNC_STATE_SYNC_MAILBOXES:
 
845
                dsync_worker_set_input_callback(brain->src_worker, NULL, NULL);
 
846
                dsync_worker_set_input_callback(brain->dest_worker, NULL, NULL);
 
847
 
 
848
                dsync_brain_sync_mailboxes(brain);
 
849
                dsync_brain_sync_dirs(brain);
 
850
                brain->state++;
 
851
                /* fall through */
 
852
        case DSYNC_STATE_SYNC_SUBSCRIPTIONS:
 
853
                dsync_brain_sync_subscriptions(brain);
 
854
                brain->state++;
 
855
                /* fall through */
 
856
        case DSYNC_STATE_SYNC_MSGS:
 
857
                if (dsync_brain_sync_msgs(brain))
 
858
                        break;
 
859
                brain->state++;
 
860
                /* no mailboxes changed */
 
861
        case DSYNC_STATE_SYNC_MSGS_FLUSH:
 
862
                /* wait until all saves are done, so we don't try to close
 
863
                   the mailbox too early */
 
864
                dsync_worker_finish(brain->src_worker,
 
865
                                    dsync_brain_worker_finished, brain);
 
866
                dsync_worker_finish(brain->dest_worker,
 
867
                                    dsync_brain_worker_finished, brain);
 
868
                break;
 
869
        case DSYNC_STATE_SYNC_MSGS_FLUSH2:
 
870
                break;
 
871
        case DSYNC_STATE_SYNC_UPDATE_MAILBOXES:
 
872
                dsync_brain_sync_update_mailboxes(brain);
 
873
                brain->state++;
 
874
                /* fall through */
 
875
        case DSYNC_STATE_SYNC_FLUSH:
 
876
                dsync_worker_finish(brain->src_worker,
 
877
                                    dsync_brain_worker_finished, brain);
 
878
                dsync_worker_finish(brain->dest_worker,
 
879
                                    dsync_brain_worker_finished, brain);
 
880
                break;
 
881
        case DSYNC_STATE_SYNC_FLUSH2:
 
882
                break;
 
883
        case DSYNC_STATE_SYNC_END:
 
884
                io_loop_stop(current_ioloop);
 
885
                break;
 
886
        default:
 
887
                i_unreached();
 
888
        }
 
889
}
 
890
 
 
891
void dsync_brain_sync_all(struct dsync_brain *brain)
 
892
{
 
893
        enum dsync_state old_state;
 
894
 
 
895
        while (brain->state != DSYNC_STATE_SYNC_END) {
 
896
                old_state = brain->state;
 
897
                dsync_brain_sync(brain);
 
898
 
 
899
                if (dsync_brain_has_failed(brain))
 
900
                        break;
 
901
 
 
902
                i_assert(brain->state != old_state);
 
903
        }
 
904
}
 
905
 
 
906
bool dsync_brain_has_unexpected_changes(struct dsync_brain *brain)
 
907
{
 
908
        return brain->unexpected_changes ||
 
909
                dsync_worker_has_unexpected_changes(brain->src_worker) ||
 
910
                dsync_worker_has_unexpected_changes(brain->dest_worker);
 
911
}
 
912
 
 
913
bool dsync_brain_has_failed(struct dsync_brain *brain)
 
914
{
 
915
        return brain->failed ||
 
916
                dsync_worker_has_failed(brain->src_worker) ||
 
917
                dsync_worker_has_failed(brain->dest_worker);
 
918
}