~ubuntu-branches/ubuntu/wily/dovecot/wily

« back to all changes in this revision

Viewing changes to src/plugins/virtual/virtual-sync.c

  • Committer: Bazaar Package Importer
  • Author(s): CHuck Short, Chuck Short
  • Date: 2009-11-06 00:47:29 UTC
  • mfrom: (4.1.9 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091106004729-i39n7v9e7d4h51f6
Tags: 1:1.2.6-1ubuntu1
* Merge from debian testing, remaining changes:
  Add new binary pkg dovecot-postfix that integrates postfix and dovecot
  automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restrictions
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
   + debian/dovecot-postfix.dirs: create backup directory for postfix's configuration
   + restart postfix and dovecot.
   + debian/dovecot-postfix.postrm:
     - remove all dovecot related configuration from postfix.
     - restart postfix and dovecot.
   + debian/dovecot-common.init:
     - check if /etc/dovecot/dovecot-postfix.conf exists and use it
       as the configuration file if so.
   + debian/patches/warning-ubuntu-postfix.dpatch
     - add warning about dovecot-postfix.conf in dovecot default 
       configuration file
   + debian/patches/dovecot-postfix.conf.diff:
     - Ubuntu server custom changes to the default dovecot configuration for
       better interfation with postfix
     - enable sieve plugin
   + debian/patches/dovecot-postfix.conf.diff:
     + Ubuntu server custom changes to the default dovecot configuration for
       better integration with postfix:
       - enable imap, pop3, imaps, pop3s and managesieve by default.
       - enable dovecot LDA (deliver).
       - enable SASL auth socket in postfix private directory.
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules:
       + install profile
     - debian/control:
       + Suggest ufw
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). LP: #254721
   + debian/control:
     - Update Vcs-* headers.
   + debian/rules:
     - Create emtpy stamp.h.in files in dovecot-sieve/ and dovecot-managesieve/
       if they're not there since empty files are not included in the diff.gz 
       file.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
   + Dropped:
     - debian/patches/security-CVE-2009-3235: Applied upstream.
     - debian/patches/fix-pop3-assertion.dpatch: Applied upstream.
     - dovecot-sieve and dovecot-managesieve: Use the debian patches instead.

  [Chuck Short]
  - Updated dovecot-sieve to 0.1.13.
  - Updated dovecot-managesieve to 0.11.9.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2008-2009 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "array.h"
 
5
#include "bsearch-insert-pos.h"
 
6
#include "ioloop.h"
 
7
#include "str.h"
 
8
#include "mail-index-modseq.h"
 
9
#include "mail-search-build.h"
 
10
#include "mailbox-search-result-private.h"
 
11
#include "index-sync-private.h"
 
12
#include "index-search-result.h"
 
13
#include "virtual-storage.h"
 
14
 
 
15
#include <stdlib.h>
 
16
 
 
17
struct virtual_add_record {
 
18
        struct virtual_mail_index_record rec;
 
19
        time_t received_date;
 
20
};
 
21
 
 
22
struct virtual_sync_mail {
 
23
        uint32_t vseq;
 
24
        struct virtual_mail_index_record vrec;
 
25
};
 
26
 
 
27
struct virtual_sync_context {
 
28
        struct virtual_mailbox *mbox;
 
29
        struct mail_index_sync_ctx *index_sync_ctx;
 
30
        struct mail_index *index;
 
31
        struct mail_index_view *sync_view;
 
32
        struct mail_index_transaction *trans;
 
33
        const char *const *kw_all;
 
34
 
 
35
        /* messages expunged within this sync */
 
36
        ARRAY_TYPE(seq_range) sync_expunges;
 
37
 
 
38
        ARRAY_DEFINE(all_adds, struct virtual_add_record);
 
39
        enum mailbox_sync_flags flags;
 
40
        uint32_t uid_validity;
 
41
 
 
42
        unsigned int ext_header_changed:1;
 
43
        unsigned int ext_header_rewrite:1;
 
44
        unsigned int expunge_removed:1;
 
45
        unsigned int index_broken:1;
 
46
};
 
47
 
 
48
static void virtual_sync_set_uidvalidity(struct virtual_sync_context *ctx)
 
49
{
 
50
        uint32_t uid_validity = ioloop_time;
 
51
 
 
52
        mail_index_update_header(ctx->trans,
 
53
                offsetof(struct mail_index_header, uid_validity),
 
54
                &uid_validity, sizeof(uid_validity), TRUE);
 
55
        ctx->uid_validity = uid_validity;
 
56
}
 
57
 
 
58
static void virtual_sync_external_flags(struct virtual_sync_context *ctx,
 
59
                                        struct virtual_backend_box *bbox,
 
60
                                        uint32_t vseq, uint32_t real_uid)
 
61
{
 
62
        enum mail_flags flags;
 
63
        const char *const *kw_names;
 
64
        struct mail_keywords *keywords;
 
65
 
 
66
        if (!mail_set_uid(bbox->sync_mail, real_uid)) {
 
67
                i_panic("UID %u lost unexpectedly from %s",
 
68
                        real_uid, bbox->box->name);
 
69
        }
 
70
 
 
71
        /* copy flags */
 
72
        flags = mail_get_flags(bbox->sync_mail);
 
73
        mail_index_update_flags(ctx->trans, vseq, MODIFY_REPLACE, flags);
 
74
 
 
75
        /* copy keywords */
 
76
        kw_names = mail_get_keywords(bbox->sync_mail);
 
77
        keywords = mail_index_keywords_create(ctx->index, kw_names);
 
78
        mail_index_update_keywords(ctx->trans, vseq, MODIFY_REPLACE, keywords);
 
79
        mail_index_keywords_free(&keywords);
 
80
}
 
81
 
 
82
static int virtual_sync_mail_cmp(const void *p1, const void *p2)
 
83
{
 
84
        const struct virtual_sync_mail *m1 = p1, *m2 = p2;
 
85
 
 
86
        if (m1->vrec.mailbox_id < m2->vrec.mailbox_id)
 
87
                return -1;
 
88
        if (m1->vrec.mailbox_id > m2->vrec.mailbox_id)
 
89
                return 1;
 
90
 
 
91
        if (m1->vrec.real_uid < m2->vrec.real_uid)
 
92
                return -1;
 
93
        if (m1->vrec.real_uid > m2->vrec.real_uid)
 
94
                return 1;
 
95
        /* broken */
 
96
        return 0;
 
97
}
 
98
 
 
99
static void
 
100
virtual_backend_box_sync_mail_set(struct virtual_backend_box *bbox)
 
101
{
 
102
        struct mailbox_transaction_context *trans;
 
103
 
 
104
        if (bbox->sync_mail == NULL) {
 
105
                trans = mailbox_transaction_begin(bbox->box, 0);
 
106
                bbox->sync_mail = mail_alloc(trans, 0, NULL);
 
107
        }
 
108
}
 
109
 
 
110
static void
 
111
virtual_backend_box_sync_mail_unset(struct virtual_backend_box *bbox)
 
112
{
 
113
        struct mailbox_transaction_context *trans;
 
114
 
 
115
        if (bbox->sync_mail != NULL) {
 
116
                trans = bbox->sync_mail->transaction;
 
117
                mail_free(&bbox->sync_mail);
 
118
                (void)mailbox_transaction_commit(&trans);
 
119
        }
 
120
}
 
121
 
 
122
static int bbox_mailbox_id_cmp(const void *p1, const void *p2)
 
123
{
 
124
        const struct virtual_backend_box *const *b1 = p1, *const *b2 = p2;
 
125
 
 
126
        if ((*b1)->mailbox_id < (*b2)->mailbox_id)
 
127
                return -1;
 
128
        if ((*b1)->mailbox_id > (*b2)->mailbox_id)
 
129
                return 1;
 
130
        return 0;
 
131
}
 
132
 
 
133
static int
 
134
virtual_sync_get_backend_box(struct virtual_sync_context *ctx, const char *name,
 
135
                             struct virtual_backend_box **bbox_r)
 
136
{
 
137
        *bbox_r = virtual_backend_box_lookup_name(ctx->mbox, name);
 
138
        if (*bbox_r != NULL || !ctx->mbox->sync_initialized)
 
139
                return 0;
 
140
 
 
141
        /* another process just added a new mailbox.
 
142
           we can't handle this currently. */
 
143
        ctx->mbox->inconsistent = TRUE;
 
144
        mail_storage_set_error(ctx->mbox->ibox.box.storage, MAIL_ERROR_TEMP,
 
145
                "Backend mailbox added by another session. "
 
146
                "Reopen the virtual mailbox.");
 
147
        return -1;
 
148
}
 
149
 
 
150
static int virtual_sync_ext_header_read(struct virtual_sync_context *ctx)
 
151
{
 
152
        const struct virtual_mail_index_header *ext_hdr;
 
153
        const struct mail_index_header *hdr;
 
154
        const struct virtual_mail_index_mailbox_record *mailboxes;
 
155
        struct virtual_backend_box *bbox, **bboxes;
 
156
        const void *ext_data;
 
157
        size_t ext_size;
 
158
        unsigned int i, count, ext_name_offset, ext_mailbox_count;
 
159
        uint32_t prev_mailbox_id;
 
160
        int ret = 1;
 
161
 
 
162
        hdr = mail_index_get_header(ctx->sync_view);
 
163
        mail_index_get_header_ext(ctx->sync_view, ctx->mbox->virtual_ext_id,
 
164
                                  &ext_data, &ext_size);
 
165
        ext_hdr = ext_data;
 
166
        if (ctx->mbox->sync_initialized &&
 
167
            ctx->mbox->prev_uid_validity == hdr->uid_validity &&
 
168
            ext_size >= sizeof(*ext_hdr) &&
 
169
            ctx->mbox->prev_change_counter == ext_hdr->change_counter) {
 
170
                /* fully refreshed */
 
171
                return TRUE;
 
172
        }
 
173
 
 
174
        ctx->mbox->prev_uid_validity = hdr->uid_validity;
 
175
        if (ext_hdr == NULL ||
 
176
            ctx->mbox->search_args_crc32 != ext_hdr->search_args_crc32) {
 
177
                mailboxes = NULL;
 
178
                ext_name_offset = 0;
 
179
                ext_mailbox_count = 0;
 
180
        } else {
 
181
                ctx->mbox->prev_change_counter = ext_hdr->change_counter;
 
182
                mailboxes = (const void *)(ext_hdr + 1);
 
183
                ext_name_offset = sizeof(*ext_hdr) +
 
184
                        ext_hdr->mailbox_count * sizeof(*mailboxes);
 
185
                if (ext_name_offset >= ext_size ||
 
186
                    ext_hdr->mailbox_count > INT_MAX/sizeof(*mailboxes)) {
 
187
                        i_error("virtual index %s: Broken mailbox_count header",
 
188
                                ctx->mbox->path);
 
189
                        ctx->index_broken = TRUE;
 
190
                        ext_mailbox_count = 0;
 
191
                        ret = 0;
 
192
                } else {
 
193
                        ext_mailbox_count = ext_hdr->mailbox_count;
 
194
                }
 
195
        }
 
196
 
 
197
        /* update mailbox backends */
 
198
        prev_mailbox_id = 0;
 
199
        for (i = 0; i < ext_mailbox_count; i++) {
 
200
                if (mailboxes[i].id > ext_hdr->highest_mailbox_id ||
 
201
                    mailboxes[i].id <= prev_mailbox_id) {
 
202
                        i_error("virtual index %s: Broken mailbox id",
 
203
                                ctx->mbox->path);
 
204
                        break;
 
205
                }
 
206
                if (mailboxes[i].name_len == 0 ||
 
207
                    mailboxes[i].name_len > ext_size) {
 
208
                        i_error("virtual index %s: Broken mailbox name_len",
 
209
                                ctx->mbox->path);
 
210
                        break;
 
211
                }
 
212
                if (ext_name_offset + mailboxes[i].name_len > ext_size) {
 
213
                        i_error("virtual index %s: Broken mailbox list",
 
214
                                ctx->mbox->path);
 
215
                        break;
 
216
                }
 
217
                T_BEGIN {
 
218
                        const unsigned char *nameptr;
 
219
                        const char *name;
 
220
 
 
221
                        nameptr = CONST_PTR_OFFSET(ext_data, ext_name_offset);
 
222
                        name = t_strndup(nameptr, mailboxes[i].name_len);
 
223
                        if (virtual_sync_get_backend_box(ctx, name, &bbox) < 0)
 
224
                                ret = -1;
 
225
                } T_END;
 
226
 
 
227
                if (bbox == NULL) {
 
228
                        if (ret < 0)
 
229
                                return -1;
 
230
                        /* mailbox no longer exists. */
 
231
                        ret = 0;
 
232
                } else {
 
233
                        bbox->mailbox_id = mailboxes[i].id;
 
234
                        bbox->sync_uid_validity = mailboxes[i].uid_validity;
 
235
                        bbox->sync_highest_modseq = mailboxes[i].highest_modseq;
 
236
                        bbox->sync_next_uid = mailboxes[i].next_uid;
 
237
                        bbox->sync_mailbox_idx = i;
 
238
                }
 
239
                ext_name_offset += mailboxes[i].name_len;
 
240
                prev_mailbox_id = mailboxes[i].id;
 
241
        }
 
242
        if (i < ext_mailbox_count) {
 
243
                ctx->index_broken = TRUE;
 
244
                ret = 0;
 
245
        }
 
246
        ctx->mbox->highest_mailbox_id = ext_hdr == NULL ? 0 :
 
247
                ext_hdr->highest_mailbox_id;
 
248
        ctx->mbox->sync_initialized = TRUE;
 
249
 
 
250
        /* assign new mailbox IDs if any are missing */
 
251
        bboxes = array_get_modifiable(&ctx->mbox->backend_boxes, &count);
 
252
        for (i = 0; i < count; i++) {
 
253
                if (bboxes[i]->mailbox_id == 0) {
 
254
                        bboxes[i]->mailbox_id = ++ctx->mbox->highest_mailbox_id;
 
255
                        ret = 0;
 
256
                }
 
257
        }
 
258
        /* sort the backend mailboxes by mailbox_id. */
 
259
        qsort(bboxes, count, sizeof(*bboxes), bbox_mailbox_id_cmp);
 
260
        return ret;
 
261
}
 
262
 
 
263
static void virtual_sync_ext_header_rewrite(struct virtual_sync_context *ctx)
 
264
{
 
265
        struct virtual_mail_index_header ext_hdr;
 
266
        struct virtual_mail_index_mailbox_record mailbox;
 
267
        struct virtual_backend_box **bboxes;
 
268
        buffer_t *buf;
 
269
        const void *ext_data;
 
270
        size_t ext_size;
 
271
        unsigned int i, mailbox_pos, name_pos, count;
 
272
 
 
273
        bboxes = array_get_modifiable(&ctx->mbox->backend_boxes, &count);
 
274
        mailbox_pos = sizeof(ext_hdr);
 
275
        name_pos = mailbox_pos + sizeof(mailbox) * count;
 
276
 
 
277
        memset(&ext_hdr, 0, sizeof(ext_hdr));
 
278
        memset(&mailbox, 0, sizeof(mailbox));
 
279
 
 
280
        ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
 
281
        ext_hdr.mailbox_count = count;
 
282
        ext_hdr.highest_mailbox_id = ctx->mbox->highest_mailbox_id;
 
283
        ext_hdr.search_args_crc32 = ctx->mbox->search_args_crc32;
 
284
 
 
285
        buf = buffer_create_dynamic(pool_datastack_create(), name_pos + 256);
 
286
        buffer_append(buf, &ext_hdr, sizeof(ext_hdr));
 
287
 
 
288
        for (i = 0; i < count; i++) {
 
289
                i_assert(i == 0 ||
 
290
                         bboxes[i]->mailbox_id > bboxes[i-1]->mailbox_id);
 
291
 
 
292
                bboxes[i]->sync_mailbox_idx = i;
 
293
                mailbox.id = bboxes[i]->mailbox_id;
 
294
                mailbox.name_len = strlen(bboxes[i]->name);
 
295
                mailbox.uid_validity = bboxes[i]->sync_uid_validity;
 
296
                mailbox.highest_modseq = bboxes[i]->sync_highest_modseq;
 
297
                mailbox.next_uid = bboxes[i]->sync_next_uid;
 
298
                buffer_write(buf, mailbox_pos, &mailbox, sizeof(mailbox));
 
299
                buffer_write(buf, name_pos, bboxes[i]->name, mailbox.name_len);
 
300
 
 
301
                mailbox_pos += sizeof(mailbox);
 
302
                name_pos += mailbox.name_len;
 
303
        }
 
304
        i_assert(buf->used == name_pos);
 
305
 
 
306
        mail_index_get_header_ext(ctx->sync_view, ctx->mbox->virtual_ext_id,
 
307
                                  &ext_data, &ext_size);
 
308
        if (ext_size < name_pos) {
 
309
                mail_index_ext_resize(ctx->trans, ctx->mbox->virtual_ext_id,
 
310
                                      name_pos,
 
311
                                      sizeof(struct virtual_mail_index_record),
 
312
                                      sizeof(uint32_t));
 
313
        }
 
314
        mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
 
315
                                     0, buf->data, name_pos);
 
316
}
 
317
 
 
318
static void virtual_sync_ext_header_update(struct virtual_sync_context *ctx)
 
319
{
 
320
        struct virtual_mail_index_header ext_hdr;
 
321
 
 
322
        if (!ctx->ext_header_changed)
 
323
                return;
 
324
 
 
325
        /* we changed something - update the change counter in header */
 
326
        ext_hdr.change_counter = ++ctx->mbox->prev_change_counter;
 
327
        mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
 
328
                offsetof(struct virtual_mail_index_header, change_counter),
 
329
                &ext_hdr.change_counter, sizeof(ext_hdr.change_counter));
 
330
}
 
331
 
 
332
static void virtual_sync_index_rec(struct virtual_sync_context *ctx,
 
333
                                   const struct mail_index_sync_rec *sync_rec)
 
334
{
 
335
        uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
 
336
        struct virtual_backend_box *bbox;
 
337
        const struct virtual_mail_index_record *vrec;
 
338
        const void *data;
 
339
        enum mail_flags flags;
 
340
        struct mail_keywords *keywords;
 
341
        enum modify_type modify_type;
 
342
        const char *kw_names[2];
 
343
        uint32_t vseq, seq1, seq2;
 
344
        bool expunged;
 
345
 
 
346
        switch (sync_rec->type) {
 
347
        case MAIL_INDEX_SYNC_TYPE_APPEND:
 
348
                /* don't care */
 
349
                return;
 
350
        case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
 
351
        case MAIL_INDEX_SYNC_TYPE_FLAGS:
 
352
        case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
 
353
        case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
 
354
        case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
 
355
                break;
 
356
        }
 
357
        if (!mail_index_lookup_seq_range(ctx->sync_view,
 
358
                                         sync_rec->uid1, sync_rec->uid2,
 
359
                                         &seq1, &seq2)) {
 
360
                /* already expunged, nothing to do. */
 
361
                return;
 
362
        }
 
363
 
 
364
        for (vseq = seq1; vseq <= seq2; vseq++) {
 
365
                mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id,
 
366
                                      &data, &expunged);
 
367
                vrec = data;
 
368
 
 
369
                bbox = virtual_backend_box_lookup(ctx->mbox, vrec->mailbox_id);
 
370
                if (bbox == NULL)
 
371
                        continue;
 
372
 
 
373
                virtual_backend_box_sync_mail_set(bbox);
 
374
                if (!mail_set_uid(bbox->sync_mail, vrec->real_uid)) {
 
375
                        /* message is already expunged from backend mailbox. */
 
376
                        continue;
 
377
                }
 
378
 
 
379
                switch (sync_rec->type) {
 
380
                case MAIL_INDEX_SYNC_TYPE_EXPUNGE:
 
381
                        mail_expunge(bbox->sync_mail);
 
382
                        break;
 
383
                case MAIL_INDEX_SYNC_TYPE_FLAGS:
 
384
                        flags = sync_rec->add_flags & MAIL_FLAGS_NONRECENT;
 
385
                        if (flags != 0) {
 
386
                                mail_update_flags(bbox->sync_mail,
 
387
                                                  MODIFY_ADD, flags);
 
388
                        }
 
389
                        flags = sync_rec->remove_flags & MAIL_FLAGS_NONRECENT;
 
390
                        if (flags != 0) {
 
391
                                mail_update_flags(bbox->sync_mail,
 
392
                                                  MODIFY_REMOVE, flags);
 
393
                        }
 
394
                        break;
 
395
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
 
396
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
 
397
                        kw_names[0] = ctx->kw_all[sync_rec->keyword_idx];
 
398
                        kw_names[1] = NULL;
 
399
                        keywords = mailbox_keywords_create_valid(bbox->box,
 
400
                                                                 kw_names);
 
401
 
 
402
                        modify_type = sync_rec->type ==
 
403
                                MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD ?
 
404
                                MODIFY_ADD : MODIFY_REMOVE;
 
405
                        mail_update_keywords(bbox->sync_mail,
 
406
                                             modify_type, keywords);
 
407
                        mailbox_keywords_free(bbox->box, &keywords);
 
408
                        break;
 
409
                case MAIL_INDEX_SYNC_TYPE_KEYWORD_RESET:
 
410
                        kw_names[0] = NULL;
 
411
                        keywords = mailbox_keywords_create_valid(bbox->box,
 
412
                                                                 kw_names);
 
413
                        mail_update_keywords(bbox->sync_mail, MODIFY_REPLACE,
 
414
                                             keywords);
 
415
                        mailbox_keywords_free(bbox->box, &keywords);
 
416
                        break;
 
417
                case MAIL_INDEX_SYNC_TYPE_APPEND:
 
418
                        i_unreached();
 
419
                }
 
420
        }
 
421
}
 
422
 
 
423
static void virtual_sync_index_changes(struct virtual_sync_context *ctx)
 
424
{
 
425
        const ARRAY_TYPE(keywords) *keywords;
 
426
        struct mail_index_sync_rec sync_rec;
 
427
 
 
428
        keywords = mail_index_get_keywords(ctx->index);
 
429
        ctx->kw_all = array_count(keywords) == 0 ? NULL :
 
430
                array_idx(keywords, 0);
 
431
        while (mail_index_sync_next(ctx->index_sync_ctx, &sync_rec))
 
432
                virtual_sync_index_rec(ctx, &sync_rec);
 
433
}
 
434
 
 
435
static void virtual_sync_index_finish(struct virtual_sync_context *ctx)
 
436
{
 
437
        struct mailbox *box = &ctx->mbox->ibox.box;
 
438
        const struct mail_index_header *hdr;
 
439
        uint32_t seq1, seq2;
 
440
 
 
441
        hdr = mail_index_get_header(ctx->sync_view);
 
442
        if (hdr->uid_validity != 0)
 
443
                ctx->uid_validity = hdr->uid_validity;
 
444
        else
 
445
                virtual_sync_set_uidvalidity(ctx);
 
446
 
 
447
        /* mark the newly seen messages as recent */
 
448
        if (mail_index_lookup_seq_range(ctx->sync_view, hdr->first_recent_uid,
 
449
                                        hdr->next_uid, &seq1, &seq2)) {
 
450
                index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view,
 
451
                                             seq1, seq2);
 
452
        }
 
453
        if (ctx->ext_header_rewrite) {
 
454
                /* entire mailbox list needs to be rewritten */
 
455
                virtual_sync_ext_header_rewrite(ctx);
 
456
        } else {
 
457
                /* update only changed parts in the header */
 
458
                virtual_sync_ext_header_update(ctx);
 
459
        }
 
460
 
 
461
        if (box->v.sync_notify != NULL)
 
462
                box->v.sync_notify(box, 0, 0);
 
463
}
 
464
 
 
465
static int virtual_sync_backend_box_init(struct virtual_backend_box *bbox)
 
466
{
 
467
        struct mailbox_transaction_context *trans;
 
468
        struct mail_search_context *search_ctx;
 
469
        struct mail *mail;
 
470
        struct virtual_backend_uidmap uidmap;
 
471
        enum mailbox_search_result_flags result_flags;
 
472
        int ret;
 
473
 
 
474
        trans = mailbox_transaction_begin(bbox->box, 0);
 
475
        mail = mail_alloc(trans, 0, NULL);
 
476
 
 
477
        search_ctx = mailbox_search_init(trans, bbox->search_args, NULL);
 
478
 
 
479
        /* save the result and keep it updated */
 
480
        result_flags = MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
 
481
                MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC;
 
482
        bbox->search_result =
 
483
                mailbox_search_result_save(search_ctx, result_flags);
 
484
 
 
485
        /* add the found UIDs to uidmap. virtual_uid gets assigned later. */
 
486
        memset(&uidmap, 0, sizeof(uidmap));
 
487
        array_clear(&bbox->uids);
 
488
        while (mailbox_search_next(search_ctx, mail) > 0) {
 
489
                uidmap.real_uid = mail->uid;
 
490
                array_append(&bbox->uids, &uidmap, 1);
 
491
        }
 
492
 
 
493
        ret = mailbox_search_deinit(&search_ctx);
 
494
        mail_free(&mail);
 
495
 
 
496
        (void)mailbox_transaction_commit(&trans);
 
497
        return ret;
 
498
}
 
499
 
 
500
static int virtual_backend_uidmap_bsearch_cmp(const void *key, const void *data)
 
501
{
 
502
        const uint32_t *uidp = key;
 
503
        const struct virtual_backend_uidmap *uidmap = data;
 
504
 
 
505
        return *uidp < uidmap->real_uid ? -1 :
 
506
                (*uidp > uidmap->real_uid ? 1 : 0);
 
507
}
 
508
 
 
509
static void
 
510
virtual_sync_mailbox_box_remove(struct virtual_sync_context *ctx,
 
511
                                struct virtual_backend_box *bbox,
 
512
                                const ARRAY_TYPE(seq_range) *removed_uids)
 
513
{
 
514
        const struct seq_range *uids;
 
515
        struct virtual_backend_uidmap *uidmap;
 
516
        unsigned int i, src, dest, uid_count, rec_count;
 
517
        uint32_t uid, vseq;
 
518
 
 
519
        uids = array_get(removed_uids, &uid_count);
 
520
        if (uid_count == 0)
 
521
                return;
 
522
 
 
523
        /* everything in removed_uids should exist in bbox->uids */
 
524
        uidmap = array_get_modifiable(&bbox->uids, &rec_count);
 
525
        i_assert(rec_count >= uid_count);
 
526
 
 
527
        /* find the first uidmap record to be removed */
 
528
        if (!bsearch_insert_pos(&uids[0].seq1, uidmap, rec_count,
 
529
                                sizeof(*uidmap),
 
530
                                virtual_backend_uidmap_bsearch_cmp, &src))
 
531
                i_unreached();
 
532
 
 
533
        /* remove the unwanted messages */
 
534
        dest = src;
 
535
        for (i = 0; i < uid_count; i++) {
 
536
                uid = uids[i].seq1;
 
537
                while (uidmap[src].real_uid != uid) {
 
538
                        uidmap[dest++] = uidmap[src++];
 
539
                        i_assert(src < rec_count);
 
540
                }
 
541
 
 
542
                for (; uid <= uids[i].seq2; uid++, src++) {
 
543
                        i_assert(src < rec_count);
 
544
                        i_assert(uidmap[src].real_uid == uid);
 
545
                        if (mail_index_lookup_seq(ctx->sync_view,
 
546
                                                  uidmap[src].virtual_uid,
 
547
                                                  &vseq))
 
548
                                mail_index_expunge(ctx->trans, vseq);
 
549
                }
 
550
        }
 
551
        array_delete(&bbox->uids, dest, src - dest);
 
552
}
 
553
 
 
554
static void
 
555
virtual_sync_mailbox_box_add(struct virtual_sync_context *ctx,
 
556
                             struct virtual_backend_box *bbox,
 
557
                             const ARRAY_TYPE(seq_range) *added_uids_arr)
 
558
{
 
559
        const struct seq_range *added_uids;
 
560
        struct virtual_backend_uidmap *uidmap;
 
561
        struct virtual_add_record rec;
 
562
        unsigned int i, src, dest, uid_count, add_count, rec_count;
 
563
        uint32_t add_uid;
 
564
 
 
565
        added_uids = array_get(added_uids_arr, &uid_count);
 
566
        if (uid_count == 0)
 
567
                return;
 
568
        add_count = seq_range_count(added_uids_arr);
 
569
 
 
570
        /* none of added_uids should exist in bbox->uids. find the position
 
571
           of the first inserted index. */
 
572
        uidmap = array_get_modifiable(&bbox->uids, &rec_count);
 
573
        if (rec_count == 0 ||
 
574
            added_uids[0].seq1 > uidmap[rec_count-1].real_uid) {
 
575
                /* fast path: usually messages are appended */
 
576
                dest = rec_count;
 
577
        } else if (bsearch_insert_pos(&added_uids[0].seq1, uidmap, rec_count,
 
578
                                      sizeof(*uidmap),
 
579
                                      virtual_backend_uidmap_bsearch_cmp,
 
580
                                      &dest))
 
581
                i_unreached();
 
582
 
 
583
        /* make space for all added UIDs. */
 
584
        if (rec_count == dest)
 
585
                array_idx_clear(&bbox->uids, dest + add_count-1);
 
586
        else {
 
587
                array_copy(&bbox->uids.arr, dest + add_count,
 
588
                           &bbox->uids.arr, dest, rec_count - dest);
 
589
        }
 
590
        uidmap = array_get_modifiable(&bbox->uids, &rec_count);
 
591
        src = dest + add_count;
 
592
 
 
593
        /* add/move the UIDs to their correct positions */
 
594
        memset(&rec, 0, sizeof(rec));
 
595
        rec.rec.mailbox_id = bbox->mailbox_id;
 
596
        for (i = 0; i < uid_count; i++) {
 
597
                add_uid = added_uids[i].seq1;
 
598
                while (src < rec_count && uidmap[src].real_uid < add_uid)
 
599
                        uidmap[dest++] = uidmap[src++];
 
600
 
 
601
                for (; add_uid <= added_uids[i].seq2; add_uid++, dest++) {
 
602
                        i_assert(dest < rec_count);
 
603
 
 
604
                        uidmap[dest].real_uid = add_uid;
 
605
                        uidmap[dest].virtual_uid = 0;
 
606
 
 
607
                        if (ctx->mbox->uids_mapped) {
 
608
                                rec.rec.real_uid = add_uid;
 
609
                                array_append(&ctx->all_adds, &rec, 1);
 
610
                        }
 
611
                }
 
612
        }
 
613
}
 
614
 
 
615
static int virtual_backend_uidmap_cmp(const void *p1, const void *p2)
 
616
{
 
617
        const struct virtual_backend_uidmap *u1 = p1, *u2 = p2;
 
618
 
 
619
        if (u1->real_uid < u2->real_uid)
 
620
                return -1;
 
621
        if (u1->real_uid > u2->real_uid)
 
622
                return 1;
 
623
        return 0;
 
624
}
 
625
 
 
626
static void virtual_sync_bbox_uids_sort(struct virtual_backend_box *bbox)
 
627
{
 
628
        struct virtual_backend_uidmap *uids;
 
629
        unsigned int uid_count;
 
630
 
 
631
        /* the uidmap must be sorted by real_uids */
 
632
        uids = array_get_modifiable(&bbox->uids, &uid_count);
 
633
        qsort(uids, uid_count, sizeof(*uids), virtual_backend_uidmap_cmp);
 
634
        bbox->uids_nonsorted = FALSE;
 
635
}
 
636
 
 
637
static void virtual_sync_backend_boxes_sort_uids(struct virtual_mailbox *mbox)
 
638
{
 
639
        struct virtual_backend_box *const *bboxes;
 
640
        unsigned int i, count;
 
641
 
 
642
        bboxes = array_get(&mbox->backend_boxes, &count);
 
643
        for (i = 0; i < count; i++) {
 
644
                if (bboxes[i]->uids_nonsorted)
 
645
                        virtual_sync_bbox_uids_sort(bboxes[i]);
 
646
        }
 
647
}
 
648
 
 
649
static void
 
650
virtual_sync_backend_handle_old_vmsgs(struct virtual_sync_context *ctx,
 
651
                                      struct virtual_backend_box *bbox,
 
652
                                      struct mail_search_result *result)
 
653
{
 
654
        struct index_mailbox *ibox = (struct index_mailbox *)bbox->box;
 
655
        const struct virtual_mail_index_record *vrec;
 
656
        struct virtual_backend_uidmap uidmap;
 
657
        const void *data;
 
658
        uint32_t seq, vseq, vuid, messages;
 
659
        bool expunged;
 
660
 
 
661
        /* add the currently existing UIDs to uidmap. remember the messages
 
662
           that were already expunged */
 
663
        memset(&uidmap, 0, sizeof(uidmap));
 
664
        array_clear(&bbox->uids);
 
665
 
 
666
        messages = mail_index_view_get_messages_count(ctx->sync_view);
 
667
        for (vseq = 1; vseq <= messages; vseq++) {
 
668
                mail_index_lookup_uid(ctx->sync_view, vseq, &vuid);
 
669
                mail_index_lookup_ext(ctx->sync_view, vseq,
 
670
                                      ctx->mbox->virtual_ext_id,
 
671
                                      &data, &expunged);
 
672
                vrec = data;
 
673
                if (vrec->mailbox_id == bbox->mailbox_id) {
 
674
                        uidmap.real_uid = vrec->real_uid;
 
675
                        uidmap.virtual_uid = vuid;
 
676
                        array_append(&bbox->uids, &uidmap, 1);
 
677
 
 
678
                        if (mail_index_lookup_seq(ibox->view, vrec->real_uid,
 
679
                                                  &seq)) {
 
680
                                seq_range_array_add(&result->uids, 0,
 
681
                                                    vrec->real_uid);
 
682
                        } else {
 
683
                                seq_range_array_add(&result->removed_uids, 0,
 
684
                                                    vrec->real_uid);
 
685
                        }
 
686
                }
 
687
        }
 
688
 
 
689
        virtual_sync_bbox_uids_sort(bbox);
 
690
}
 
691
 
 
692
static int virtual_sync_backend_box_continue(struct virtual_sync_context *ctx,
 
693
                                             struct virtual_backend_box *bbox)
 
694
{
 
695
        const enum mailbox_search_result_flags result_flags =
 
696
                MAILBOX_SEARCH_RESULT_FLAG_UPDATE |
 
697
                MAILBOX_SEARCH_RESULT_FLAG_QUEUE_SYNC;
 
698
        struct index_mailbox *ibox = (struct index_mailbox *)bbox->box;
 
699
        struct mail_search_result *result;
 
700
        ARRAY_TYPE(seq_range) removed_uids, added_uids, flag_updates;
 
701
        uint64_t modseq;
 
702
        uint32_t seq, old_msg_count;
 
703
 
 
704
        /* initialize the search result from all the existing messages in
 
705
           virtual index. */
 
706
        result = mailbox_search_result_alloc(bbox->box, bbox->search_args,
 
707
                                             result_flags);
 
708
        mailbox_search_result_initial_done(result);
 
709
        virtual_sync_backend_handle_old_vmsgs(ctx, bbox, result);
 
710
 
 
711
        /* get list of changed old messages, based on modseq changes.
 
712
           (we'll assume all modseq changes are due to flag changes, which
 
713
           may not be true in future.) */
 
714
        if (bbox->sync_next_uid <= 1 ||
 
715
            !mail_index_lookup_seq_range(ibox->view, 1, bbox->sync_next_uid-1,
 
716
                                         &seq, &old_msg_count))
 
717
                old_msg_count = 0;
 
718
        t_array_init(&flag_updates, I_MIN(128, old_msg_count));
 
719
        for (seq = 1; seq <= old_msg_count; seq++) {
 
720
                modseq = mail_index_modseq_lookup(ibox->view, seq);
 
721
                if (modseq > bbox->sync_highest_modseq)
 
722
                        seq_range_array_add(&flag_updates, 0, seq);
 
723
        }
 
724
 
 
725
        /* update the search result based on the flag changes and
 
726
           new messages */
 
727
        if (index_search_result_update_flags(result, &flag_updates) < 0 ||
 
728
            index_search_result_update_appends(result, old_msg_count) < 0) {
 
729
                mailbox_search_result_free(&result);
 
730
                return -1;
 
731
        }
 
732
 
 
733
        t_array_init(&removed_uids, 128);
 
734
        t_array_init(&added_uids, 128);
 
735
        mailbox_search_result_sync(result, &removed_uids, &added_uids);
 
736
        virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids);
 
737
        virtual_sync_mailbox_box_add(ctx, bbox, &added_uids);
 
738
 
 
739
        bbox->search_result = result;
 
740
        return 0;
 
741
}
 
742
 
 
743
static void virtual_sync_drop_existing(struct virtual_backend_box *bbox,
 
744
                                       ARRAY_TYPE(seq_range) *added_uids)
 
745
{
 
746
        ARRAY_TYPE(seq_range) drop_uids;
 
747
        const struct virtual_backend_uidmap *uidmap;
 
748
        struct seq_range_iter iter;
 
749
        unsigned int i, n = 0, count;
 
750
        uint32_t add_uid;
 
751
 
 
752
        seq_range_array_iter_init(&iter, added_uids);
 
753
        if (!seq_range_array_iter_nth(&iter, n++, &add_uid))
 
754
                return;
 
755
 
 
756
        uidmap = array_get_modifiable(&bbox->uids, &count);
 
757
        (void)bsearch_insert_pos(&add_uid, uidmap, count, sizeof(*uidmap),
 
758
                                 virtual_backend_uidmap_bsearch_cmp, &i);
 
759
        if (i == count)
 
760
                return;
 
761
 
 
762
        t_array_init(&drop_uids, array_count(added_uids));
 
763
        for (; i < count; ) {
 
764
                if (uidmap[i].real_uid < add_uid) {
 
765
                        i++;
 
766
                        continue;
 
767
                }
 
768
                if (uidmap[i].real_uid == add_uid) {
 
769
                        seq_range_array_add(&drop_uids, 0, add_uid);
 
770
                        i++;
 
771
                }
 
772
                if (!seq_range_array_iter_nth(&iter, n++, &add_uid))
 
773
                        break;
 
774
        }
 
775
        seq_range_array_remove_seq_range(added_uids, &drop_uids);
 
776
}
 
777
 
 
778
static void virtual_sync_drop_nonexisting(struct virtual_backend_box *bbox,
 
779
                                          ARRAY_TYPE(seq_range) *removed_uids)
 
780
{
 
781
        ARRAY_TYPE(seq_range) drop_uids;
 
782
        const struct virtual_backend_uidmap *uidmap;
 
783
        struct seq_range_iter iter;
 
784
        unsigned int i, n = 0, count;
 
785
        uint32_t remove_uid;
 
786
        bool iter_done = FALSE;
 
787
 
 
788
        seq_range_array_iter_init(&iter, removed_uids);
 
789
        if (!seq_range_array_iter_nth(&iter, n++, &remove_uid))
 
790
                return;
 
791
 
 
792
        uidmap = array_get_modifiable(&bbox->uids, &count);
 
793
        (void)bsearch_insert_pos(&remove_uid, uidmap, count, sizeof(*uidmap),
 
794
                                 virtual_backend_uidmap_bsearch_cmp, &i);
 
795
 
 
796
        t_array_init(&drop_uids, array_count(removed_uids)); iter_done = FALSE;
 
797
        for (; i < count; ) {
 
798
                if (uidmap[i].real_uid < remove_uid) {
 
799
                        i++;
 
800
                        continue;
 
801
                }
 
802
                if (uidmap[i].real_uid != remove_uid)
 
803
                        seq_range_array_add(&drop_uids, 0, remove_uid);
 
804
                else
 
805
                        i++;
 
806
                if (!seq_range_array_iter_nth(&iter, n++, &remove_uid)) {
 
807
                        iter_done = TRUE;
 
808
                        break;
 
809
                }
 
810
        }
 
811
        if (!iter_done) {
 
812
                do {
 
813
                        seq_range_array_add(&drop_uids, 0, remove_uid);
 
814
                } while (seq_range_array_iter_nth(&iter, n++, &remove_uid));
 
815
        }
 
816
        seq_range_array_remove_seq_range(removed_uids, &drop_uids);
 
817
}
 
818
 
 
819
static void virtual_sync_mailbox_box_update(struct virtual_sync_context *ctx,
 
820
                                            struct virtual_backend_box *bbox)
 
821
{
 
822
        ARRAY_TYPE(seq_range) removed_uids, added_uids, temp_uids;
 
823
        unsigned int count1, count2;
 
824
 
 
825
        t_array_init(&removed_uids, 128);
 
826
        t_array_init(&added_uids, 128);
 
827
 
 
828
        mailbox_search_result_sync(bbox->search_result,
 
829
                                   &removed_uids, &added_uids);
 
830
        if (array_is_created(&bbox->sync_outside_expunges)) {
 
831
                seq_range_array_remove_seq_range(&bbox->sync_outside_expunges,
 
832
                                                 &added_uids);
 
833
                seq_range_array_merge(&removed_uids,
 
834
                                      &bbox->sync_outside_expunges);
 
835
                array_clear(&bbox->sync_outside_expunges);
 
836
        }
 
837
 
 
838
        virtual_sync_drop_existing(bbox, &added_uids);
 
839
        virtual_sync_drop_nonexisting(bbox, &removed_uids);
 
840
 
 
841
        /* if any of the pending removes came back, we don't want to expunge
 
842
           them anymore. also since they already exist, remove them from
 
843
           added_uids. */
 
844
        count1 = array_count(&bbox->sync_pending_removes);
 
845
        count2 = array_count(&added_uids);
 
846
        if (count1 > 0 && count2 > 0) {
 
847
                t_array_init(&temp_uids, count1);
 
848
                array_append_array(&temp_uids, &bbox->sync_pending_removes);
 
849
                if (seq_range_array_remove_seq_range(
 
850
                                &bbox->sync_pending_removes, &added_uids) > 0) {
 
851
                        seq_range_array_remove_seq_range(&added_uids,
 
852
                                                         &temp_uids);
 
853
                }
 
854
        }
 
855
 
 
856
        if (!ctx->expunge_removed) {
 
857
                /* delay removing messages that don't match the search
 
858
                   criteria, but don't delay removing expunged messages */
 
859
                if (array_count(&ctx->sync_expunges) > 0) {
 
860
                        seq_range_array_remove_seq_range(&bbox->sync_pending_removes,
 
861
                                                         &ctx->sync_expunges);
 
862
                        seq_range_array_remove_seq_range(&removed_uids,
 
863
                                                         &ctx->sync_expunges);
 
864
                        virtual_sync_mailbox_box_remove(ctx, bbox,
 
865
                                                        &ctx->sync_expunges);
 
866
                }
 
867
                seq_range_array_merge(&bbox->sync_pending_removes,
 
868
                                      &removed_uids);
 
869
        } else if (array_count(&bbox->sync_pending_removes) > 0) {
 
870
                /* remove all current and old */
 
871
                seq_range_array_merge(&bbox->sync_pending_removes,
 
872
                                      &removed_uids);
 
873
                virtual_sync_mailbox_box_remove(ctx, bbox,
 
874
                                                &bbox->sync_pending_removes);
 
875
                array_clear(&bbox->sync_pending_removes);
 
876
        } else {
 
877
                virtual_sync_mailbox_box_remove(ctx, bbox, &removed_uids);
 
878
        }
 
879
        virtual_sync_mailbox_box_add(ctx, bbox, &added_uids);
 
880
}
 
881
 
 
882
static bool virtual_sync_find_seqs(struct virtual_backend_box *bbox,
 
883
                                   const struct mailbox_sync_rec *sync_rec,
 
884
                                   unsigned int *idx1_r,
 
885
                                   unsigned int *idx2_r)
 
886
{
 
887
        struct index_mailbox *ibox = (struct index_mailbox *)bbox->box;
 
888
        const struct virtual_backend_uidmap *uidmap;
 
889
        unsigned int idx, count;
 
890
        uint32_t uid1, uid2;
 
891
 
 
892
        mail_index_lookup_uid(ibox->view, sync_rec->seq1, &uid1);
 
893
        mail_index_lookup_uid(ibox->view, sync_rec->seq2, &uid2);
 
894
        uidmap = array_get_modifiable(&bbox->uids, &count);
 
895
        (void)bsearch_insert_pos(&uid1, uidmap, count, sizeof(*uidmap),
 
896
                                 virtual_backend_uidmap_bsearch_cmp, &idx);
 
897
        if (idx == count || uidmap[idx].real_uid > uid2)
 
898
                return FALSE;
 
899
 
 
900
        *idx1_r = idx;
 
901
        while (idx < count && uidmap[idx].real_uid <= uid2) idx++;
 
902
        *idx2_r = idx - 1;
 
903
        return TRUE;
 
904
}
 
905
 
 
906
static void virtual_sync_expunge_add(struct virtual_sync_context *ctx,
 
907
                                     struct virtual_backend_box *bbox,
 
908
                                     const struct mailbox_sync_rec *sync_rec)
 
909
{
 
910
        struct index_mailbox *ibox = (struct index_mailbox *)bbox->box;
 
911
        struct virtual_backend_uidmap *uidmap;
 
912
        uint32_t uid1, uid2;
 
913
        unsigned int i, idx1, count;
 
914
 
 
915
        mail_index_lookup_uid(ibox->view, sync_rec->seq1, &uid1);
 
916
        mail_index_lookup_uid(ibox->view, sync_rec->seq2, &uid2);
 
917
 
 
918
        /* remember only the expunges for messages that
 
919
           already exist for this mailbox */
 
920
        uidmap = array_get_modifiable(&bbox->uids, &count);
 
921
        (void)bsearch_insert_pos(&uid1, uidmap, count, sizeof(*uidmap),
 
922
                                 virtual_backend_uidmap_bsearch_cmp, &idx1);
 
923
        for (i = idx1; i < count; i++) {
 
924
                if (uidmap[i].real_uid > uid2)
 
925
                        break;
 
926
                seq_range_array_add(&ctx->sync_expunges, 0, uidmap[i].real_uid);
 
927
        }
 
928
}
 
929
 
 
930
static int virtual_sync_backend_box_sync(struct virtual_sync_context *ctx,
 
931
                                         struct virtual_backend_box *bbox,
 
932
                                         enum mailbox_sync_flags sync_flags)
 
933
{
 
934
        struct mailbox_sync_context *sync_ctx;
 
935
        const struct virtual_backend_uidmap *uidmap;
 
936
        struct mailbox_sync_rec sync_rec;
 
937
        unsigned int idx1, idx2;
 
938
        uint32_t vseq, vuid;
 
939
 
 
940
        sync_ctx = mailbox_sync_init(bbox->box, sync_flags);
 
941
        virtual_backend_box_sync_mail_set(bbox);
 
942
        while (mailbox_sync_next(sync_ctx, &sync_rec)) {
 
943
                switch (sync_rec.type) {
 
944
                case MAILBOX_SYNC_TYPE_EXPUNGE:
 
945
                        if (ctx->expunge_removed) {
 
946
                                /* no need to keep track of expunges */
 
947
                                break;
 
948
                        }
 
949
                        virtual_sync_expunge_add(ctx, bbox, &sync_rec);
 
950
                        break;
 
951
                case MAILBOX_SYNC_TYPE_FLAGS:
 
952
                        if (!virtual_sync_find_seqs(bbox, &sync_rec,
 
953
                                                    &idx1, &idx2))
 
954
                                break;
 
955
                        uidmap = array_idx(&bbox->uids, 0);
 
956
                        for (; idx1 <= idx2; idx1++) {
 
957
                                vuid = uidmap[idx1].virtual_uid;
 
958
                                if (!mail_index_lookup_seq(ctx->sync_view,
 
959
                                                           vuid, &vseq)) {
 
960
                                        /* expunged by another session,
 
961
                                           but we haven't yet updated
 
962
                                           bbox->uids. */
 
963
                                        continue;
 
964
                                }
 
965
                                virtual_sync_external_flags(ctx, bbox, vseq,
 
966
                                                        uidmap[idx1].real_uid);
 
967
                        }
 
968
                        break;
 
969
                case MAILBOX_SYNC_TYPE_MODSEQ:
 
970
                        break;
 
971
                }
 
972
        }
 
973
        return mailbox_sync_deinit(&sync_ctx, 0, NULL);
 
974
}
 
975
 
 
976
static void virtual_sync_backend_ext_header(struct virtual_sync_context *ctx,
 
977
                                            struct virtual_backend_box *bbox)
 
978
{
 
979
        const unsigned int uidval_pos =
 
980
                offsetof(struct virtual_mail_index_mailbox_record,
 
981
                         uid_validity);
 
982
        struct mailbox_status status;
 
983
        struct virtual_mail_index_mailbox_record mailbox;
 
984
        unsigned int mailbox_offset;
 
985
 
 
986
        mailbox_get_status(bbox->box, STATUS_UIDVALIDITY |
 
987
                           STATUS_HIGHESTMODSEQ, &status);
 
988
        if (bbox->sync_uid_validity == status.uidvalidity &&
 
989
            bbox->sync_next_uid == status.uidnext &&
 
990
            bbox->sync_highest_modseq == status.highest_modseq)
 
991
                return;
 
992
 
 
993
        /* mailbox changed - update extension header */
 
994
        bbox->sync_uid_validity = status.uidvalidity;
 
995
        bbox->sync_highest_modseq = status.highest_modseq;
 
996
        bbox->sync_next_uid = status.uidnext;
 
997
 
 
998
        if (ctx->ext_header_rewrite) {
 
999
                /* we'll rewrite the entire header later */
 
1000
                return;
 
1001
        }
 
1002
 
 
1003
        memset(&mailbox, 0, sizeof(mailbox));
 
1004
        mailbox.uid_validity = bbox->sync_uid_validity;
 
1005
        mailbox.highest_modseq = bbox->sync_highest_modseq;
 
1006
        mailbox.next_uid = bbox->sync_next_uid;
 
1007
 
 
1008
        mailbox_offset = sizeof(struct virtual_mail_index_header) +
 
1009
                bbox->sync_mailbox_idx * sizeof(mailbox);
 
1010
        mail_index_update_header_ext(ctx->trans, ctx->mbox->virtual_ext_id,
 
1011
                                     mailbox_offset + uidval_pos,
 
1012
                                     CONST_PTR_OFFSET(&mailbox, uidval_pos),
 
1013
                                     sizeof(mailbox) - uidval_pos);
 
1014
        ctx->ext_header_changed = TRUE;
 
1015
}
 
1016
 
 
1017
static int virtual_sync_backend_box(struct virtual_sync_context *ctx,
 
1018
                                    struct virtual_backend_box *bbox)
 
1019
{
 
1020
        struct index_mailbox *ibox = (struct index_mailbox *)bbox->box;
 
1021
        enum mailbox_sync_flags sync_flags;
 
1022
        struct mailbox_status status;
 
1023
        int ret;
 
1024
 
 
1025
        if (!bbox->box->opened)
 
1026
                index_storage_mailbox_open(ibox);
 
1027
 
 
1028
        /* if we already did some changes to index, commit them before
 
1029
           syncing starts. */
 
1030
        virtual_backend_box_sync_mail_unset(bbox);
 
1031
        /* we use modseqs for speeding up initial search result build.
 
1032
           make sure the backend has them enabled. */
 
1033
        mail_index_modseq_enable(ibox->index);
 
1034
 
 
1035
        sync_flags = ctx->flags & (MAILBOX_SYNC_FLAG_FULL_READ |
 
1036
                                   MAILBOX_SYNC_FLAG_FULL_WRITE |
 
1037
                                   MAILBOX_SYNC_FLAG_FAST);
 
1038
 
 
1039
        if (bbox->search_result == NULL) {
 
1040
                /* first sync in this process */
 
1041
                i_assert(ctx->expunge_removed);
 
1042
 
 
1043
                if (mailbox_sync(bbox->box, sync_flags, STATUS_UIDVALIDITY,
 
1044
                                 &status) < 0)
 
1045
                        return -1;
 
1046
 
 
1047
                virtual_backend_box_sync_mail_set(bbox);
 
1048
                if (status.uidvalidity != bbox->sync_uid_validity) {
 
1049
                        /* UID validity changed since last sync (or this is
 
1050
                           the first sync), do a full search */
 
1051
                        ret = virtual_sync_backend_box_init(bbox);
 
1052
                } else {
 
1053
                        /* build the initial search using the saved modseq. */
 
1054
                        ret = virtual_sync_backend_box_continue(ctx, bbox);
 
1055
                }
 
1056
        } else {
 
1057
                /* sync using the existing search result */
 
1058
                i_array_init(&ctx->sync_expunges, 32);
 
1059
                ret = virtual_sync_backend_box_sync(ctx, bbox, sync_flags);
 
1060
                if (ret == 0) T_BEGIN {
 
1061
                        virtual_sync_mailbox_box_update(ctx, bbox);
 
1062
                } T_END;
 
1063
                array_free(&ctx->sync_expunges);
 
1064
        }
 
1065
 
 
1066
        virtual_sync_backend_ext_header(ctx, bbox);
 
1067
        return ret;
 
1068
}
 
1069
 
 
1070
static void virtual_sync_backend_map_uids(struct virtual_sync_context *ctx)
 
1071
{
 
1072
        uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
 
1073
        struct virtual_sync_mail *vmails;
 
1074
        struct virtual_backend_box *bbox, *const *bboxes;
 
1075
        struct virtual_backend_uidmap *uidmap = NULL;
 
1076
        struct virtual_add_record add_rec;
 
1077
        const struct virtual_mail_index_record *vrec;
 
1078
        const void *data;
 
1079
        bool expunged;
 
1080
        uint32_t i, vseq, vuid, messages, count;
 
1081
        unsigned int j = 0, uidmap_count = 0;
 
1082
 
 
1083
        messages = mail_index_view_get_messages_count(ctx->sync_view);
 
1084
 
 
1085
        /* sort the messages in current view by their backend mailbox and
 
1086
           real UID */
 
1087
        vmails = messages == 0 ? NULL :
 
1088
                i_new(struct virtual_sync_mail, messages);
 
1089
        for (vseq = 1; vseq <= messages; vseq++) {
 
1090
                mail_index_lookup_ext(ctx->sync_view, vseq, virtual_ext_id,
 
1091
                                      &data, &expunged);
 
1092
                vrec = data;
 
1093
                vmails[vseq-1].vseq = vseq;
 
1094
                vmails[vseq-1].vrec = *vrec;
 
1095
        }
 
1096
        qsort(vmails, messages, sizeof(*vmails), virtual_sync_mail_cmp);
 
1097
 
 
1098
        /* create real mailbox uid -> virtual uid mapping and expunge
 
1099
           messages no longer matching the search rule */
 
1100
        memset(&add_rec, 0, sizeof(add_rec));
 
1101
        bbox = NULL;
 
1102
        for (i = 0; i < messages; i++) {
 
1103
                vseq = vmails[i].vseq;
 
1104
                vrec = &vmails[i].vrec;
 
1105
 
 
1106
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1107
                        /* add the rest of the newly seen messages */
 
1108
                        for (; j < uidmap_count; j++) {
 
1109
                                add_rec.rec.real_uid = uidmap[j].real_uid;
 
1110
                                array_append(&ctx->all_adds, &add_rec, 1);
 
1111
                        }
 
1112
                        bbox = virtual_backend_box_lookup(ctx->mbox,
 
1113
                                                          vrec->mailbox_id);
 
1114
                        if (bbox == NULL) {
 
1115
                                /* the entire mailbox is lost */
 
1116
                                mail_index_expunge(ctx->trans, vseq);
 
1117
                                continue;
 
1118
                        }
 
1119
                        uidmap = array_get_modifiable(&bbox->uids,
 
1120
                                                      &uidmap_count);
 
1121
                        j = 0;
 
1122
                        add_rec.rec.mailbox_id = bbox->mailbox_id;
 
1123
                        bbox->sync_seen = TRUE;
 
1124
                }
 
1125
                mail_index_lookup_uid(ctx->sync_view, vseq, &vuid);
 
1126
 
 
1127
                /* if virtual record doesn't exist in uidmap, it's expunged */
 
1128
                for (; j < uidmap_count; j++) {
 
1129
                        if (uidmap[j].real_uid >= vrec->real_uid)
 
1130
                                break;
 
1131
 
 
1132
                        /* newly seen message */
 
1133
                        add_rec.rec.real_uid = uidmap[j].real_uid;
 
1134
                        array_append(&ctx->all_adds, &add_rec, 1);
 
1135
                }
 
1136
                if (j == uidmap_count || uidmap[j].real_uid != vrec->real_uid)
 
1137
                        mail_index_expunge(ctx->trans, vseq);
 
1138
                else {
 
1139
                        /* exists - update uidmap and flags */
 
1140
                        uidmap[j++].virtual_uid = vuid;
 
1141
                        virtual_sync_external_flags(ctx, bbox, vseq,
 
1142
                                                    vrec->real_uid);
 
1143
                }
 
1144
        }
 
1145
        i_free(vmails);
 
1146
 
 
1147
        /* finish adding messages to the last mailbox */
 
1148
        for (; j < uidmap_count; j++) {
 
1149
                add_rec.rec.real_uid = uidmap[j].real_uid;
 
1150
                array_append(&ctx->all_adds, &add_rec, 1);
 
1151
        }
 
1152
 
 
1153
        /* if there are any mailboxes we didn't yet sync, add new messages in
 
1154
           them */
 
1155
        bboxes = array_get(&ctx->mbox->backend_boxes, &count);
 
1156
        for (i = 0; i < count; i++) {
 
1157
                if (bboxes[i]->sync_seen)
 
1158
                        continue;
 
1159
 
 
1160
                add_rec.rec.mailbox_id = bboxes[i]->mailbox_id;
 
1161
                uidmap = array_get_modifiable(&bboxes[i]->uids, &uidmap_count);
 
1162
                for (j = 0; j < uidmap_count; j++) {
 
1163
                        add_rec.rec.real_uid = uidmap[j].real_uid;
 
1164
                        array_append(&ctx->all_adds, &add_rec, 1);
 
1165
                }
 
1166
        }
 
1167
}
 
1168
 
 
1169
static int virtual_add_record_cmp(const void *p1, const void *p2)
 
1170
{
 
1171
        const struct virtual_add_record *add1 = p1, *add2 = p2;
 
1172
 
 
1173
        if (add1->received_date < add2->received_date)
 
1174
                return -1;
 
1175
        if (add1->received_date > add2->received_date)
 
1176
                return 1;
 
1177
 
 
1178
        /* if they're in same mailbox, we can order them correctly by the UID.
 
1179
           if they're in different mailboxes, ordering by UID doesn't really
 
1180
           help but it doesn't really harm either. */
 
1181
        if (add1->rec.real_uid < add2->rec.real_uid)
 
1182
                return -1;
 
1183
        if (add1->rec.real_uid > add2->rec.real_uid)
 
1184
                return 1;
 
1185
 
 
1186
        /* two messages in different mailboxes have the same received date
 
1187
           and UID. */
 
1188
        return 0;
 
1189
}
 
1190
 
 
1191
static void virtual_sync_backend_sort_new(struct virtual_sync_context *ctx)
 
1192
{
 
1193
        struct virtual_backend_box *bbox;
 
1194
        struct virtual_add_record *adds;
 
1195
        const struct virtual_mail_index_record *vrec;
 
1196
        unsigned int i, count;
 
1197
 
 
1198
        /* get all messages' received dates */
 
1199
        adds = array_get_modifiable(&ctx->all_adds, &count);
 
1200
        for (bbox = NULL, i = 0; i < count; i++) {
 
1201
                vrec = &adds[i].rec;
 
1202
 
 
1203
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1204
                        bbox = virtual_backend_box_lookup(ctx->mbox,
 
1205
                                                          vrec->mailbox_id);
 
1206
                }
 
1207
                if (!mail_set_uid(bbox->sync_mail, vrec->real_uid))
 
1208
                        i_unreached();
 
1209
                if (mail_get_received_date(bbox->sync_mail,
 
1210
                                           &adds[i].received_date) < 0) {
 
1211
                        /* probably expunged already, just add it somewhere */
 
1212
                        adds[i].received_date = 0;
 
1213
                }
 
1214
        }
 
1215
 
 
1216
        qsort(adds, count, sizeof(*adds), virtual_add_record_cmp);
 
1217
}
 
1218
 
 
1219
static void virtual_sync_backend_add_new(struct virtual_sync_context *ctx)
 
1220
{
 
1221
        uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
 
1222
        struct virtual_add_record *adds;
 
1223
        struct virtual_backend_box *bbox;
 
1224
        struct virtual_backend_uidmap *uidmap;
 
1225
        const struct mail_index_header *hdr;
 
1226
        const struct virtual_mail_index_record *vrec;
 
1227
        unsigned int i, count, idx, uid_count;
 
1228
        uint32_t vseq, first_uid, next_uid;
 
1229
 
 
1230
        hdr = mail_index_get_header(ctx->sync_view);
 
1231
        adds = array_get_modifiable(&ctx->all_adds, &count);
 
1232
        if (count == 0) {
 
1233
                ctx->mbox->sync_virtual_next_uid = hdr->next_uid;
 
1234
                return;
 
1235
        }
 
1236
 
 
1237
        if (adds[0].rec.mailbox_id == adds[count-1].rec.mailbox_id) {
 
1238
                /* all messages are from a single mailbox. add them in
 
1239
                   the same order. */
 
1240
        } else {
 
1241
                /* sort new messages by received date to get the add order */
 
1242
                virtual_sync_backend_sort_new(ctx);
 
1243
        }
 
1244
 
 
1245
        for (bbox = NULL, i = 0; i < count; i++) {
 
1246
                vrec = &adds[i].rec;
 
1247
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1248
                        bbox = virtual_backend_box_lookup(ctx->mbox,
 
1249
                                                          vrec->mailbox_id);
 
1250
                }
 
1251
 
 
1252
                mail_index_append(ctx->trans, 0, &vseq);
 
1253
                mail_index_update_ext(ctx->trans, vseq, virtual_ext_id,
 
1254
                                      vrec, NULL);
 
1255
                virtual_sync_external_flags(ctx, bbox, vseq, vrec->real_uid);
 
1256
        }
 
1257
 
 
1258
        /* assign UIDs to new messages */
 
1259
        first_uid = hdr->next_uid;
 
1260
        mail_index_append_assign_uids(ctx->trans, first_uid, &next_uid);
 
1261
 
 
1262
        /* update virtual UIDs in uidmap */
 
1263
        for (bbox = NULL, i = 0; i < count; i++) {
 
1264
                vrec = &adds[i].rec;
 
1265
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1266
                        bbox = virtual_backend_box_lookup(ctx->mbox,
 
1267
                                                          vrec->mailbox_id);
 
1268
                }
 
1269
 
 
1270
                uidmap = array_get_modifiable(&bbox->uids, &uid_count);
 
1271
                if (!bsearch_insert_pos(&vrec->real_uid, uidmap, uid_count,
 
1272
                                        sizeof(*uidmap),
 
1273
                                        virtual_backend_uidmap_bsearch_cmp,
 
1274
                                        &idx))
 
1275
                        i_unreached();
 
1276
                i_assert(uidmap[idx].virtual_uid == 0);
 
1277
                uidmap[idx].virtual_uid = first_uid + i;
 
1278
        }
 
1279
        ctx->mbox->sync_virtual_next_uid = first_uid + i;
 
1280
}
 
1281
 
 
1282
static int
 
1283
virtual_sync_apply_existing_appends(struct virtual_sync_context *ctx)
 
1284
{
 
1285
        uint32_t virtual_ext_id = ctx->mbox->virtual_ext_id;
 
1286
        struct virtual_backend_box *bbox = NULL;
 
1287
        const struct mail_index_header *hdr;
 
1288
        const struct virtual_mail_index_record *vrec;
 
1289
        struct virtual_backend_uidmap uidmap;
 
1290
        const void *data;
 
1291
        bool expunged;
 
1292
        uint32_t seq, seq2;
 
1293
 
 
1294
        if (!ctx->mbox->uids_mapped)
 
1295
                return 0;
 
1296
 
 
1297
        hdr = mail_index_get_header(ctx->sync_view);
 
1298
        if (ctx->mbox->sync_virtual_next_uid >= hdr->next_uid)
 
1299
                return 0;
 
1300
 
 
1301
        /* another process added messages to virtual index. get backend boxes'
 
1302
           uid lists up-to-date by adding the new messages there. */
 
1303
        if (!mail_index_lookup_seq_range(ctx->sync_view,
 
1304
                                         ctx->mbox->sync_virtual_next_uid,
 
1305
                                         (uint32_t)-1, &seq, &seq2))
 
1306
                return 0;
 
1307
 
 
1308
        memset(&uidmap, 0, sizeof(uidmap));
 
1309
        for (; seq <= seq2; seq++) {
 
1310
                mail_index_lookup_ext(ctx->sync_view, seq, virtual_ext_id,
 
1311
                                      &data, &expunged);
 
1312
                vrec = data;
 
1313
                uidmap.real_uid = vrec->real_uid;
 
1314
                mail_index_lookup_uid(ctx->sync_view, seq, &uidmap.virtual_uid);
 
1315
 
 
1316
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1317
                        bbox = virtual_backend_box_lookup(ctx->mbox,
 
1318
                                                          vrec->mailbox_id);
 
1319
                        if (bbox == NULL) {
 
1320
                                mail_storage_set_critical(
 
1321
                                        ctx->mbox->ibox.box.storage,
 
1322
                                        "Mailbox ID %u unexpectedly lost",
 
1323
                                        vrec->mailbox_id);
 
1324
                                return -1;
 
1325
                        }
 
1326
                }
 
1327
                array_append(&bbox->uids, &uidmap, 1);
 
1328
                bbox->uids_nonsorted = TRUE;
 
1329
        }
 
1330
 
 
1331
        virtual_sync_backend_boxes_sort_uids(ctx->mbox);
 
1332
        return 0;
 
1333
}
 
1334
 
 
1335
static void
 
1336
virtual_sync_apply_existing_expunges(struct virtual_mailbox *mbox,
 
1337
                                     struct mailbox_sync_context *sync_ctx)
 
1338
{
 
1339
        struct index_mailbox_sync_context *isync_ctx =
 
1340
                (struct index_mailbox_sync_context *)sync_ctx;
 
1341
        struct virtual_backend_box *bbox = NULL;
 
1342
        struct seq_range_iter iter;
 
1343
        const struct virtual_mail_index_record *vrec;
 
1344
        const void *data;
 
1345
        bool expunged;
 
1346
        unsigned int n = 0;
 
1347
        uint32_t seq;
 
1348
 
 
1349
        if (isync_ctx->expunges == NULL)
 
1350
                return;
 
1351
 
 
1352
        seq_range_array_iter_init(&iter, isync_ctx->expunges);
 
1353
        while (seq_range_array_iter_nth(&iter, n++, &seq)) {
 
1354
                mail_index_lookup_ext(mbox->ibox.view, seq,
 
1355
                                      mbox->virtual_ext_id, &data, &expunged);
 
1356
                vrec = data;
 
1357
 
 
1358
                if (bbox == NULL || bbox->mailbox_id != vrec->mailbox_id) {
 
1359
                        bbox = virtual_backend_box_lookup(mbox,
 
1360
                                                          vrec->mailbox_id);
 
1361
                        if (!array_is_created(&bbox->sync_outside_expunges))
 
1362
                                i_array_init(&bbox->sync_outside_expunges, 32);
 
1363
                }
 
1364
                seq_range_array_add(&bbox->sync_outside_expunges, 0,
 
1365
                                    vrec->real_uid);
 
1366
        }
 
1367
}
 
1368
 
 
1369
static int virtual_sync_backend_boxes(struct virtual_sync_context *ctx)
 
1370
{
 
1371
        struct virtual_backend_box *const *bboxes;
 
1372
        unsigned int i, count;
 
1373
        int ret;
 
1374
 
 
1375
        if (virtual_sync_apply_existing_appends(ctx) < 0)
 
1376
                return -1;
 
1377
 
 
1378
        i_array_init(&ctx->all_adds, 128);
 
1379
        bboxes = array_get(&ctx->mbox->backend_boxes, &count);
 
1380
        for (i = 0; i < count; i++) {
 
1381
                T_BEGIN {
 
1382
                        ret = virtual_sync_backend_box(ctx, bboxes[i]);
 
1383
                } T_END;
 
1384
                if (ret < 0) {
 
1385
                        /* backend failed, copy the error */
 
1386
                        virtual_box_copy_error(&ctx->mbox->ibox.box,
 
1387
                                               bboxes[i]->box);
 
1388
                        return -1;
 
1389
                }
 
1390
        }
 
1391
 
 
1392
        if (!ctx->mbox->uids_mapped) {
 
1393
                /* initial sync: assign virtual UIDs to existing messages and
 
1394
                   sync all flags */
 
1395
                ctx->mbox->uids_mapped = TRUE;
 
1396
                virtual_sync_backend_map_uids(ctx);
 
1397
        }
 
1398
        virtual_sync_backend_add_new(ctx);
 
1399
        array_free(&ctx->all_adds);
 
1400
        return 0;
 
1401
}
 
1402
 
 
1403
static void virtual_sync_backend_boxes_finish(struct virtual_sync_context *ctx)
 
1404
{
 
1405
        struct virtual_backend_box *const *bboxes;
 
1406
        unsigned int i, count;
 
1407
 
 
1408
        bboxes = array_get(&ctx->mbox->backend_boxes, &count);
 
1409
        for (i = 0; i < count; i++)
 
1410
                virtual_backend_box_sync_mail_unset(bboxes[i]);
 
1411
}
 
1412
 
 
1413
static int virtual_sync_finish(struct virtual_sync_context *ctx, bool success)
 
1414
{
 
1415
        int ret = success ? 0 : -1;
 
1416
 
 
1417
        virtual_sync_backend_boxes_finish(ctx);
 
1418
        if (success) {
 
1419
                if (mail_index_sync_commit(&ctx->index_sync_ctx) < 0) {
 
1420
                        mail_storage_set_index_error(&ctx->mbox->ibox);
 
1421
                        ret = -1;
 
1422
                }
 
1423
        } else {
 
1424
                if (ctx->index_broken) {
 
1425
                        /* make sure we don't complain about the same errors
 
1426
                           over and over again. */
 
1427
                        if (mail_index_unlink(ctx->index) < 0) {
 
1428
                                i_error("virtual index %s: Failed to unlink() "
 
1429
                                        "broken indexes: %m",
 
1430
                                        ctx->mbox->path);
 
1431
                        }
 
1432
                }
 
1433
                mail_index_sync_rollback(&ctx->index_sync_ctx);
 
1434
        }
 
1435
        i_free(ctx);
 
1436
        return ret;
 
1437
}
 
1438
 
 
1439
static int virtual_sync(struct virtual_mailbox *mbox,
 
1440
                        enum mailbox_sync_flags flags)
 
1441
{
 
1442
        struct virtual_sync_context *ctx;
 
1443
        enum mail_index_sync_flags index_sync_flags;
 
1444
        int ret;
 
1445
 
 
1446
        ctx = i_new(struct virtual_sync_context, 1);
 
1447
        ctx->mbox = mbox;
 
1448
        ctx->flags = flags;
 
1449
        ctx->index = mbox->ibox.index;
 
1450
        /* Removed messages are expunged when
 
1451
           a) EXPUNGE is used
 
1452
           b) Mailbox is being opened (FIX_INCONSISTENT is set) */
 
1453
        ctx->expunge_removed =
 
1454
                (ctx->flags & (MAILBOX_SYNC_FLAG_EXPUNGE |
 
1455
                               MAILBOX_SYNC_FLAG_FIX_INCONSISTENT)) != 0;
 
1456
 
 
1457
        index_sync_flags = MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY |
 
1458
                MAIL_INDEX_SYNC_FLAG_AVOID_FLAG_UPDATES;
 
1459
        if (!mbox->ibox.keep_recent)
 
1460
                index_sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
 
1461
 
 
1462
        ret = mail_index_sync_begin(ctx->index, &ctx->index_sync_ctx,
 
1463
                                    &ctx->sync_view, &ctx->trans,
 
1464
                                    index_sync_flags);
 
1465
        if (ret <= 0) {
 
1466
                if (ret < 0)
 
1467
                        mail_storage_set_index_error(&mbox->ibox);
 
1468
                i_free(ctx);
 
1469
                return ret;
 
1470
        }
 
1471
 
 
1472
        ret = virtual_sync_ext_header_read(ctx);
 
1473
        if (ret < 0)
 
1474
                return virtual_sync_finish(ctx, FALSE);
 
1475
        if (ret == 0)
 
1476
                ctx->ext_header_rewrite = TRUE;
 
1477
        /* apply changes from virtual index to backend mailboxes */
 
1478
        virtual_sync_index_changes(ctx);
 
1479
        /* update list of UIDs in backend mailboxes */
 
1480
        if (virtual_sync_backend_boxes(ctx) < 0)
 
1481
                return virtual_sync_finish(ctx, FALSE);
 
1482
 
 
1483
        virtual_sync_index_finish(ctx);
 
1484
        return virtual_sync_finish(ctx, TRUE);
 
1485
}
 
1486
 
 
1487
struct mailbox_sync_context *
 
1488
virtual_storage_sync_init(struct mailbox *box, enum mailbox_sync_flags flags)
 
1489
{
 
1490
        struct virtual_mailbox *mbox = (struct virtual_mailbox *)box;
 
1491
        struct mailbox_sync_context *sync_ctx;
 
1492
        int ret = 0;
 
1493
 
 
1494
        if (!box->opened)
 
1495
                index_storage_mailbox_open(&mbox->ibox);
 
1496
 
 
1497
        if (index_mailbox_want_full_sync(&mbox->ibox, flags))
 
1498
                ret = virtual_sync(mbox, flags);
 
1499
 
 
1500
        sync_ctx = index_mailbox_sync_init(box, flags, ret < 0);
 
1501
        virtual_sync_apply_existing_expunges(mbox, sync_ctx);
 
1502
        return sync_ctx;
 
1503
}