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

« back to all changes in this revision

Viewing changes to src/dsync/dsync-worker-local.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-2011 Dovecot authors, see the included COPYING file */
2
 
 
3
 
#include "lib.h"
4
 
#include "array.h"
5
 
#include "hash.h"
6
 
#include "str.h"
7
 
#include "hex-binary.h"
8
 
#include "network.h"
9
 
#include "istream.h"
10
 
#include "settings-parser.h"
11
 
#include "mailbox-log.h"
12
 
#include "mail-user.h"
13
 
#include "mail-namespace.h"
14
 
#include "mail-storage.h"
15
 
#include "mail-search-build.h"
16
 
#include "dsync-worker-private.h"
17
 
 
18
 
#include <ctype.h>
19
 
 
20
 
struct local_dsync_worker_mailbox_iter {
21
 
        struct dsync_worker_mailbox_iter iter;
22
 
        pool_t ret_pool;
23
 
        struct mailbox_list_iterate_context *list_iter;
24
 
        struct hash_iterate_context *deleted_iter;
25
 
        struct hash_iterate_context *deleted_dir_iter;
26
 
};
27
 
 
28
 
struct local_dsync_worker_subs_iter {
29
 
        struct dsync_worker_subs_iter iter;
30
 
        struct mailbox_list_iterate_context *list_iter;
31
 
        struct hash_iterate_context *deleted_iter;
32
 
};
33
 
 
34
 
struct local_dsync_worker_msg_iter {
35
 
        struct dsync_worker_msg_iter iter;
36
 
        mailbox_guid_t *mailboxes;
37
 
        unsigned int mailbox_idx, mailbox_count;
38
 
 
39
 
        struct mail_search_context *search_ctx;
40
 
        struct mail *mail;
41
 
 
42
 
        string_t *tmp_guid_str;
43
 
        ARRAY_TYPE(mailbox_expunge_rec) expunges;
44
 
        unsigned int expunge_idx;
45
 
        unsigned int expunges_set:1;
46
 
};
47
 
 
48
 
struct local_dsync_mailbox {
49
 
        struct mail_namespace *ns;
50
 
        mailbox_guid_t guid;
51
 
        const char *storage_name;
52
 
        bool deleted;
53
 
};
54
 
 
55
 
struct local_dsync_mailbox_change {
56
 
        mailbox_guid_t guid;
57
 
        time_t last_delete;
58
 
 
59
 
        unsigned int deleted_mailbox:1;
60
 
};
61
 
 
62
 
struct local_dsync_dir_change {
63
 
        mailbox_guid_t name_sha1;
64
 
        struct mailbox_list *list;
65
 
 
66
 
        time_t last_rename;
67
 
        time_t last_delete;
68
 
        time_t last_subs_change;
69
 
 
70
 
        unsigned int unsubscribed:1;
71
 
        unsigned int deleted_dir:1;
72
 
};
73
 
 
74
 
struct local_dsync_worker_msg_get {
75
 
        mailbox_guid_t mailbox;
76
 
        uint32_t uid;
77
 
        dsync_worker_msg_callback_t *callback;
78
 
        void *context;
79
 
};
80
 
 
81
 
struct local_dsync_worker {
82
 
        struct dsync_worker worker;
83
 
        struct mail_user *user;
84
 
 
85
 
        pool_t pool;
86
 
        /* mailbox_guid_t -> struct local_dsync_mailbox* */
87
 
        struct hash_table *mailbox_hash;
88
 
        /* mailbox_guid_t -> struct local_dsync_mailbox_change* */
89
 
        struct hash_table *mailbox_changes_hash;
90
 
        /* <-> struct local_dsync_dir_change */
91
 
        struct hash_table *dir_changes_hash;
92
 
 
93
 
        char alt_char;
94
 
        ARRAY_DEFINE(subs_namespaces, struct mail_namespace *);
95
 
 
96
 
        mailbox_guid_t selected_box_guid;
97
 
        struct mailbox *selected_box;
98
 
        struct mail *mail, *ext_mail;
99
 
 
100
 
        ARRAY_TYPE(uint32_t) saved_uids;
101
 
 
102
 
        mailbox_guid_t get_mailbox;
103
 
        struct mail *get_mail;
104
 
        ARRAY_DEFINE(msg_get_queue, struct local_dsync_worker_msg_get);
105
 
 
106
 
        struct io *save_io;
107
 
        struct mail_save_context *save_ctx;
108
 
        struct istream *save_input;
109
 
        dsync_worker_save_callback_t *save_callback;
110
 
        void *save_context;
111
 
 
112
 
        dsync_worker_finish_callback_t *finish_callback;
113
 
        void *finish_context;
114
 
 
115
 
        unsigned int reading_mail:1;
116
 
        unsigned int finishing:1;
117
 
        unsigned int finished:1;
118
 
};
119
 
 
120
 
extern struct dsync_worker_vfuncs local_dsync_worker;
121
 
 
122
 
static void local_worker_mailbox_close(struct local_dsync_worker *worker);
123
 
static void local_worker_msg_box_close(struct local_dsync_worker *worker);
124
 
static void
125
 
local_worker_msg_get_next(struct local_dsync_worker *worker,
126
 
                          const struct local_dsync_worker_msg_get *get);
127
 
 
128
 
static int mailbox_guid_cmp(const void *p1, const void *p2)
129
 
{
130
 
        const mailbox_guid_t *g1 = p1, *g2 = p2;
131
 
 
132
 
        return memcmp(g1->guid, g2->guid, sizeof(g1->guid));
133
 
}
134
 
 
135
 
static unsigned int mailbox_guid_hash(const void *p)
136
 
{
137
 
        const mailbox_guid_t *guid = p;
138
 
        const uint8_t *s = guid->guid;
139
 
        unsigned int i, g, h = 0;
140
 
 
141
 
        for (i = 0; i < sizeof(guid->guid); i++) {
142
 
                h = (h << 4) + s[i];
143
 
                if ((g = h & 0xf0000000UL)) {
144
 
                        h = h ^ (g >> 24);
145
 
                        h = h ^ g;
146
 
                }
147
 
        }
148
 
        return h;
149
 
}
150
 
 
151
 
static struct mail_namespace *
152
 
namespace_find_set(struct mail_user *user,
153
 
                   const struct mail_namespace_settings *set)
154
 
{
155
 
        struct mail_namespace *ns;
156
 
 
157
 
        for (ns = user->namespaces; ns != NULL; ns = ns->next) {
158
 
                /* compare settings pointers so that it'll work
159
 
                   for shared namespaces */
160
 
                if (ns->set == set)
161
 
                        return ns;
162
 
        }
163
 
        return NULL;
164
 
}
165
 
 
166
 
static void dsync_drop_extra_namespaces(struct local_dsync_worker *worker)
167
 
{
168
 
        struct mail_user *user = worker->user;
169
 
        struct mail_namespace_settings *const *ns_unset, *const *ns_set;
170
 
        struct mail_namespace *ns;
171
 
        unsigned int i, count, count2;
172
 
 
173
 
        if (!array_is_created(&user->unexpanded_set->namespaces))
174
 
                return;
175
 
 
176
 
        /* drop all namespaces that have a location defined internally */
177
 
        ns_unset = array_get(&user->unexpanded_set->namespaces, &count);
178
 
        ns_set = array_get(&user->set->namespaces, &count2);
179
 
        i_assert(count == count2);
180
 
        for (i = 0; i < count; i++) {
181
 
                if (strcmp(ns_unset[i]->location,
182
 
                           SETTING_STRVAR_UNEXPANDED) == 0)
183
 
                        continue;
184
 
 
185
 
                ns = namespace_find_set(user, ns_set[i]);
186
 
                i_assert(ns != NULL);
187
 
                if ((ns->flags & NAMESPACE_FLAG_SUBSCRIPTIONS) == 0) {
188
 
                        /* remember the subscriptions=no namespaces so we can
189
 
                           handle their subscriptions in parent namespaces
190
 
                           properly */
191
 
                        mail_namespace_ref(ns);
192
 
                        array_append(&worker->subs_namespaces, &ns, 1);
193
 
                }
194
 
                mail_namespace_destroy(ns);
195
 
        }
196
 
        if (user->namespaces == NULL) {
197
 
                i_fatal("All your namespaces have a location setting. "
198
 
                        "It should be empty (default mail_location) in the "
199
 
                        "namespace to be converted.");
200
 
        }
201
 
}
202
 
 
203
 
struct dsync_worker *
204
 
dsync_worker_init_local(struct mail_user *user, char alt_char)
205
 
{
206
 
        struct local_dsync_worker *worker;
207
 
        pool_t pool;
208
 
 
209
 
        pool = pool_alloconly_create("local dsync worker", 10240);
210
 
        worker = p_new(pool, struct local_dsync_worker, 1);
211
 
        worker->worker.v = local_dsync_worker;
212
 
        worker->user = user;
213
 
        worker->pool = pool;
214
 
        worker->alt_char = alt_char;
215
 
        worker->mailbox_hash =
216
 
                hash_table_create(default_pool, pool, 0,
217
 
                                  mailbox_guid_hash, mailbox_guid_cmp);
218
 
        i_array_init(&worker->saved_uids, 128);
219
 
        i_array_init(&worker->msg_get_queue, 32);
220
 
        p_array_init(&worker->subs_namespaces, pool, 8);
221
 
        dsync_drop_extra_namespaces(worker);
222
 
        return &worker->worker;
223
 
}
224
 
 
225
 
static void local_worker_deinit(struct dsync_worker *_worker)
226
 
{
227
 
        struct local_dsync_worker *worker =
228
 
                (struct local_dsync_worker *)_worker;
229
 
        struct mail_namespace **nsp;
230
 
 
231
 
        i_assert(worker->save_input == NULL);
232
 
 
233
 
        array_foreach_modifiable(&worker->subs_namespaces, nsp)
234
 
                mail_namespace_unref(nsp);
235
 
 
236
 
        local_worker_msg_box_close(worker);
237
 
        local_worker_mailbox_close(worker);
238
 
        hash_table_destroy(&worker->mailbox_hash);
239
 
        if (worker->mailbox_changes_hash != NULL)
240
 
                hash_table_destroy(&worker->mailbox_changes_hash);
241
 
        if (worker->dir_changes_hash != NULL)
242
 
                hash_table_destroy(&worker->dir_changes_hash);
243
 
        array_free(&worker->msg_get_queue);
244
 
        array_free(&worker->saved_uids);
245
 
        pool_unref(&worker->pool);
246
 
}
247
 
 
248
 
static bool local_worker_is_output_full(struct dsync_worker *worker ATTR_UNUSED)
249
 
{
250
 
        return FALSE;
251
 
}
252
 
 
253
 
static int local_worker_output_flush(struct dsync_worker *worker ATTR_UNUSED)
254
 
{
255
 
        return 1;
256
 
}
257
 
 
258
 
static void
259
 
dsync_worker_save_mailbox_change(struct local_dsync_worker *worker,
260
 
                                 const struct mailbox_log_record *rec)
261
 
{
262
 
        struct local_dsync_mailbox_change *change;
263
 
        time_t stamp;
264
 
 
265
 
        change = hash_table_lookup(worker->mailbox_changes_hash,
266
 
                                   rec->mailbox_guid);
267
 
        if (change == NULL) {
268
 
                change = i_new(struct local_dsync_mailbox_change, 1);
269
 
                memcpy(change->guid.guid, rec->mailbox_guid,
270
 
                       sizeof(change->guid.guid));
271
 
                hash_table_insert(worker->mailbox_changes_hash,
272
 
                                  change->guid.guid, change);
273
 
        }
274
 
 
275
 
        stamp = mailbox_log_record_get_timestamp(rec);
276
 
        switch (rec->type) {
277
 
        case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
278
 
                change->deleted_mailbox = TRUE;
279
 
                if (change->last_delete < stamp)
280
 
                        change->last_delete = stamp;
281
 
                break;
282
 
        case MAILBOX_LOG_RECORD_DELETE_DIR:
283
 
        case MAILBOX_LOG_RECORD_RENAME:
284
 
        case MAILBOX_LOG_RECORD_SUBSCRIBE:
285
 
        case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
286
 
                i_unreached();
287
 
        }
288
 
}
289
 
 
290
 
static void
291
 
dsync_worker_save_dir_change(struct local_dsync_worker *worker,
292
 
                             struct mailbox_list *list,
293
 
                             const struct mailbox_log_record *rec)
294
 
{
295
 
        struct local_dsync_dir_change *change, new_change;
296
 
        time_t stamp;
297
 
 
298
 
        memset(&new_change, 0, sizeof(new_change));
299
 
        new_change.list = list;
300
 
        memcpy(new_change.name_sha1.guid, rec->mailbox_guid,
301
 
               sizeof(new_change.name_sha1.guid));
302
 
 
303
 
        stamp = mailbox_log_record_get_timestamp(rec);
304
 
        change = hash_table_lookup(worker->dir_changes_hash, &new_change);
305
 
        if (change == NULL) {
306
 
                change = i_new(struct local_dsync_dir_change, 1);
307
 
                *change = new_change;
308
 
                hash_table_insert(worker->dir_changes_hash, change, change);
309
 
        }
310
 
 
311
 
        switch (rec->type) {
312
 
        case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
313
 
                i_unreached();
314
 
        case MAILBOX_LOG_RECORD_DELETE_DIR:
315
 
                change->deleted_dir = TRUE;
316
 
                if (change->last_delete < stamp)
317
 
                        change->last_delete = stamp;
318
 
                break;
319
 
        case MAILBOX_LOG_RECORD_RENAME:
320
 
                if (change->last_rename < stamp)
321
 
                        change->last_rename = stamp;
322
 
                break;
323
 
        case MAILBOX_LOG_RECORD_SUBSCRIBE:
324
 
        case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
325
 
                if (change->last_subs_change > stamp) {
326
 
                        /* we've already seen a newer subscriptions state. this
327
 
                           is probably a stale record created by dsync */
328
 
                } else {
329
 
                        change->last_subs_change = stamp;
330
 
                        change->unsubscribed =
331
 
                                rec->type == MAILBOX_LOG_RECORD_UNSUBSCRIBE;
332
 
                }
333
 
                break;
334
 
        }
335
 
}
336
 
 
337
 
static int
338
 
dsync_worker_get_list_mailbox_log(struct local_dsync_worker *worker,
339
 
                                  struct mailbox_list *list)
340
 
{
341
 
        struct mailbox_log *log;
342
 
        struct mailbox_log_iter *iter;
343
 
        const struct mailbox_log_record *rec;
344
 
 
345
 
        log = mailbox_list_get_changelog(list);
346
 
        iter = mailbox_log_iter_init(log);
347
 
        while ((rec = mailbox_log_iter_next(iter)) != NULL) {
348
 
                switch (rec->type) {
349
 
                case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
350
 
                        dsync_worker_save_mailbox_change(worker, rec);
351
 
                        break;
352
 
                case MAILBOX_LOG_RECORD_DELETE_DIR:
353
 
                case MAILBOX_LOG_RECORD_RENAME:
354
 
                case MAILBOX_LOG_RECORD_SUBSCRIBE:
355
 
                case MAILBOX_LOG_RECORD_UNSUBSCRIBE:
356
 
                        dsync_worker_save_dir_change(worker, list, rec);
357
 
                        break;
358
 
                }
359
 
        }
360
 
        return mailbox_log_iter_deinit(&iter);
361
 
}
362
 
 
363
 
static unsigned int mailbox_log_record_hash(const void *p)
364
 
{
365
 
        const uint8_t *guid = p;
366
 
 
367
 
        return ((unsigned int)guid[0] << 24) |
368
 
                ((unsigned int)guid[1] << 16) |
369
 
                ((unsigned int)guid[2] << 8) |
370
 
                (unsigned int)guid[3];
371
 
}
372
 
 
373
 
static int mailbox_log_record_cmp(const void *p1, const void *p2)
374
 
{
375
 
        return memcmp(p1, p2, MAIL_GUID_128_SIZE);
376
 
}
377
 
 
378
 
static unsigned int dir_change_hash(const void *p)
379
 
{
380
 
        const struct local_dsync_dir_change *change = p;
381
 
 
382
 
        return mailbox_log_record_hash(change->name_sha1.guid) ^
383
 
                POINTER_CAST_TO(change->list, unsigned int);
384
 
}
385
 
 
386
 
static int dir_change_cmp(const void *p1, const void *p2)
387
 
{
388
 
        const struct local_dsync_dir_change *c1 = p1, *c2 = p2;
389
 
 
390
 
        if (c1->list != c2->list)
391
 
                return 1;
392
 
 
393
 
        return memcmp(c1->name_sha1.guid, c2->name_sha1.guid,
394
 
                      MAIL_GUID_128_SIZE);
395
 
}
396
 
 
397
 
static int dsync_worker_get_mailbox_log(struct local_dsync_worker *worker)
398
 
{
399
 
        struct mail_namespace *ns;
400
 
        int ret = 0;
401
 
 
402
 
        if (worker->mailbox_changes_hash != NULL)
403
 
                return 0;
404
 
 
405
 
        worker->mailbox_changes_hash =
406
 
                hash_table_create(default_pool, worker->pool, 0,
407
 
                                  mailbox_log_record_hash,
408
 
                                  mailbox_log_record_cmp);
409
 
        worker->dir_changes_hash =
410
 
                hash_table_create(default_pool, worker->pool, 0,
411
 
                                  dir_change_hash, dir_change_cmp);
412
 
        for (ns = worker->user->namespaces; ns != NULL; ns = ns->next) {
413
 
                if (ns->alias_for != NULL)
414
 
                        continue;
415
 
 
416
 
                if (dsync_worker_get_list_mailbox_log(worker, ns->list) < 0)
417
 
                        ret = -1;
418
 
        }
419
 
        return ret;
420
 
}
421
 
 
422
 
static struct dsync_worker_mailbox_iter *
423
 
local_worker_mailbox_iter_init(struct dsync_worker *_worker)
424
 
{
425
 
        struct local_dsync_worker *worker =
426
 
                (struct local_dsync_worker *)_worker;
427
 
        struct local_dsync_worker_mailbox_iter *iter;
428
 
        enum mailbox_list_iter_flags list_flags =
429
 
                MAILBOX_LIST_ITER_SKIP_ALIASES |
430
 
                MAILBOX_LIST_ITER_NO_AUTO_INBOX;
431
 
        static const char *patterns[] = { "*", NULL };
432
 
 
433
 
        iter = i_new(struct local_dsync_worker_mailbox_iter, 1);
434
 
        iter->iter.worker = _worker;
435
 
        iter->ret_pool = pool_alloconly_create("local mailbox iter", 1024);
436
 
        iter->list_iter =
437
 
                mailbox_list_iter_init_namespaces(worker->user->namespaces,
438
 
                                                  patterns, NAMESPACE_PRIVATE,
439
 
                                                  list_flags);
440
 
        (void)dsync_worker_get_mailbox_log(worker);
441
 
        return &iter->iter;
442
 
}
443
 
 
444
 
static void
445
 
local_dsync_worker_add_mailbox(struct local_dsync_worker *worker,
446
 
                               struct mail_namespace *ns,
447
 
                               const char *storage_name,
448
 
                               const mailbox_guid_t *guid)
449
 
{
450
 
        struct local_dsync_mailbox *lbox;
451
 
 
452
 
        lbox = p_new(worker->pool, struct local_dsync_mailbox, 1);
453
 
        lbox->ns = ns;
454
 
        memcpy(lbox->guid.guid, guid->guid, sizeof(lbox->guid.guid));
455
 
        lbox->storage_name = p_strdup(worker->pool, storage_name);
456
 
 
457
 
        hash_table_insert(worker->mailbox_hash, &lbox->guid, lbox);
458
 
}
459
 
 
460
 
static int
461
 
iter_next_deleted(struct local_dsync_worker_mailbox_iter *iter,
462
 
                  struct local_dsync_worker *worker,
463
 
                  struct dsync_mailbox *dsync_box_r)
464
 
{
465
 
        void *key, *value;
466
 
 
467
 
        if (iter->deleted_iter == NULL) {
468
 
                iter->deleted_iter =
469
 
                        hash_table_iterate_init(worker->mailbox_changes_hash);
470
 
        }
471
 
        while (hash_table_iterate(iter->deleted_iter, &key, &value)) {
472
 
                const struct local_dsync_mailbox_change *change = value;
473
 
 
474
 
                if (change->deleted_mailbox) {
475
 
                        /* the name doesn't matter */
476
 
                        dsync_box_r->name = "";
477
 
                        dsync_box_r->mailbox_guid = change->guid;
478
 
                        dsync_box_r->last_change = change->last_delete;
479
 
                        dsync_box_r->flags |=
480
 
                                DSYNC_MAILBOX_FLAG_DELETED_MAILBOX;
481
 
                        return 1;
482
 
                }
483
 
        }
484
 
 
485
 
        if (iter->deleted_dir_iter == NULL) {
486
 
                iter->deleted_dir_iter =
487
 
                        hash_table_iterate_init(worker->dir_changes_hash);
488
 
        }
489
 
        while (hash_table_iterate(iter->deleted_dir_iter, &key, &value)) {
490
 
                const struct local_dsync_dir_change *change = value;
491
 
 
492
 
                if (change->deleted_dir) {
493
 
                        /* the name doesn't matter */
494
 
                        dsync_box_r->name = "";
495
 
                        dsync_box_r->name_sha1 = change->name_sha1;
496
 
                        dsync_box_r->last_change = change->last_delete;
497
 
                        dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_NOSELECT |
498
 
                                DSYNC_MAILBOX_FLAG_DELETED_DIR;
499
 
                        return 1;
500
 
                }
501
 
        }
502
 
        hash_table_iterate_deinit(&iter->deleted_iter);
503
 
        return -1;
504
 
}
505
 
 
506
 
static int
507
 
local_worker_mailbox_iter_next(struct dsync_worker_mailbox_iter *_iter,
508
 
                               struct dsync_mailbox *dsync_box_r)
509
 
{
510
 
        struct local_dsync_worker_mailbox_iter *iter =
511
 
                (struct local_dsync_worker_mailbox_iter *)_iter;
512
 
        struct local_dsync_worker *worker =
513
 
                (struct local_dsync_worker *)_iter->worker;
514
 
        enum mailbox_flags flags =
515
 
                MAILBOX_FLAG_READONLY | MAILBOX_FLAG_KEEP_RECENT;
516
 
        const struct mailbox_info *info;
517
 
        const char *storage_name;
518
 
        struct mailbox *box;
519
 
        struct mailbox_status status;
520
 
        uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
521
 
        struct local_dsync_mailbox_change *change;
522
 
        struct local_dsync_dir_change *dir_change, change_lookup;
523
 
        struct local_dsync_mailbox *old_lbox;
524
 
        enum mail_error error;
525
 
        const char *const *fields;
526
 
        unsigned int i, field_count;
527
 
 
528
 
        memset(dsync_box_r, 0, sizeof(*dsync_box_r));
529
 
 
530
 
        info = mailbox_list_iter_next(iter->list_iter);
531
 
        if (info == NULL)
532
 
                return iter_next_deleted(iter, worker, dsync_box_r);
533
 
 
534
 
        dsync_box_r->name = info->name;
535
 
        dsync_box_r->name_sep = info->ns->sep;
536
 
 
537
 
        storage_name = mail_namespace_get_storage_name(info->ns, info->name);
538
 
        dsync_str_sha_to_guid(storage_name, &dsync_box_r->name_sha1);
539
 
 
540
 
        /* get last change timestamp */
541
 
        change_lookup.list = info->ns->list;
542
 
        change_lookup.name_sha1 = dsync_box_r->name_sha1;
543
 
        dir_change = hash_table_lookup(worker->dir_changes_hash,
544
 
                                       &change_lookup);
545
 
        if (dir_change != NULL) {
546
 
                /* it shouldn't be marked as deleted, but drop it to be sure */
547
 
                dir_change->deleted_dir = FALSE;
548
 
                dsync_box_r->last_change = dir_change->last_rename;
549
 
        }
550
 
 
551
 
        if ((info->flags & MAILBOX_NOSELECT) != 0) {
552
 
                dsync_box_r->flags |= DSYNC_MAILBOX_FLAG_NOSELECT;
553
 
                local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
554
 
                                               &dsync_box_r->name_sha1);
555
 
                return 1;
556
 
        }
557
 
 
558
 
        box = mailbox_alloc(info->ns->list, storage_name, flags);
559
 
        if (mailbox_sync(box, 0) < 0 ||
560
 
            mailbox_get_guid(box, mailbox_guid) < 0) {
561
 
                struct mail_storage *storage = mailbox_get_storage(box);
562
 
 
563
 
                i_error("Failed to sync mailbox %s: %s", info->name,
564
 
                        mail_storage_get_last_error(storage, &error));
565
 
                mailbox_free(&box);
566
 
                if (error == MAIL_ERROR_NOTFOUND ||
567
 
                    error == MAIL_ERROR_NOTPOSSIBLE) {
568
 
                        /* Mailbox isn't selectable, try the next one. We
569
 
                           should have already caught \Noselect mailboxes, but
570
 
                           check them anyway here. The NOTPOSSIBLE check is
571
 
                           mainly for invalid mbox files. */
572
 
                        return local_worker_mailbox_iter_next(_iter,
573
 
                                                              dsync_box_r);
574
 
                }
575
 
                _iter->failed = TRUE;
576
 
                return -1;
577
 
        }
578
 
 
579
 
        mailbox_get_status(box, STATUS_UIDNEXT | STATUS_UIDVALIDITY |
580
 
                           STATUS_HIGHESTMODSEQ | STATUS_CACHE_FIELDS |
581
 
                           STATUS_FIRST_RECENT_UID, &status);
582
 
 
583
 
        change = hash_table_lookup(worker->mailbox_changes_hash, mailbox_guid);
584
 
        if (change != NULL) {
585
 
                /* it shouldn't be marked as deleted, but drop it to be sure */
586
 
                change->deleted_mailbox = FALSE;
587
 
        }
588
 
 
589
 
        memcpy(dsync_box_r->mailbox_guid.guid, mailbox_guid,
590
 
               sizeof(dsync_box_r->mailbox_guid.guid));
591
 
        dsync_box_r->uid_validity = status.uidvalidity;
592
 
        dsync_box_r->uid_next = status.uidnext;
593
 
        dsync_box_r->message_count = status.messages;
594
 
        dsync_box_r->first_recent_uid = status.first_recent_uid;
595
 
        dsync_box_r->highest_modseq = status.highest_modseq;
596
 
 
597
 
        p_clear(iter->ret_pool);
598
 
        fields = array_get(status.cache_fields, &field_count);
599
 
        p_array_init(&dsync_box_r->cache_fields, iter->ret_pool, field_count);
600
 
        for (i = 0; i < field_count; i++) {
601
 
                const char *field_name = p_strdup(iter->ret_pool, fields[i]);
602
 
 
603
 
                array_append(&dsync_box_r->cache_fields, &field_name, 1);
604
 
        }
605
 
 
606
 
        old_lbox = hash_table_lookup(worker->mailbox_hash,
607
 
                                     &dsync_box_r->mailbox_guid);
608
 
        if (old_lbox != NULL) {
609
 
                i_error("Mailboxes don't have unique GUIDs: "
610
 
                        "%s is shared by %s and %s",
611
 
                        dsync_guid_to_str(&dsync_box_r->mailbox_guid),
612
 
                        old_lbox->storage_name, storage_name);
613
 
                mailbox_free(&box);
614
 
                _iter->failed = TRUE;
615
 
                return -1;
616
 
        }
617
 
        local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
618
 
                                       &dsync_box_r->mailbox_guid);
619
 
        mailbox_free(&box);
620
 
        return 1;
621
 
}
622
 
 
623
 
static int
624
 
local_worker_mailbox_iter_deinit(struct dsync_worker_mailbox_iter *_iter)
625
 
{
626
 
        struct local_dsync_worker_mailbox_iter *iter =
627
 
                (struct local_dsync_worker_mailbox_iter *)_iter;
628
 
        int ret = _iter->failed ? -1 : 0;
629
 
 
630
 
        if (mailbox_list_iter_deinit(&iter->list_iter) < 0)
631
 
                ret = -1;
632
 
        pool_unref(&iter->ret_pool);
633
 
        i_free(iter);
634
 
        return ret;
635
 
}
636
 
 
637
 
static struct dsync_worker_subs_iter *
638
 
local_worker_subs_iter_init(struct dsync_worker *_worker)
639
 
{
640
 
        struct local_dsync_worker *worker =
641
 
                (struct local_dsync_worker *)_worker;
642
 
        struct local_dsync_worker_subs_iter *iter;
643
 
        enum mailbox_list_iter_flags list_flags =
644
 
                MAILBOX_LIST_ITER_SKIP_ALIASES |
645
 
                MAILBOX_LIST_ITER_SELECT_SUBSCRIBED;
646
 
        static const char *patterns[] = { "*", NULL };
647
 
 
648
 
        iter = i_new(struct local_dsync_worker_subs_iter, 1);
649
 
        iter->iter.worker = _worker;
650
 
        iter->list_iter =
651
 
                mailbox_list_iter_init_namespaces(worker->user->namespaces,
652
 
                                                  patterns, NAMESPACE_PRIVATE,
653
 
                                                  list_flags);
654
 
        (void)dsync_worker_get_mailbox_log(worker);
655
 
        return &iter->iter;
656
 
}
657
 
 
658
 
static struct mail_namespace *
659
 
find_subscription_ns(struct local_dsync_worker *worker, const char *vname)
660
 
{
661
 
        struct mail_namespace *const *nsp;
662
 
 
663
 
        array_foreach(&worker->subs_namespaces, nsp) {
664
 
                if (strncmp((*nsp)->prefix, vname, (*nsp)->prefix_len) == 0)
665
 
                        return *nsp;
666
 
        }
667
 
        return NULL;
668
 
}
669
 
 
670
 
static int
671
 
local_worker_subs_iter_next(struct dsync_worker_subs_iter *_iter,
672
 
                            struct dsync_worker_subscription *rec_r)
673
 
{
674
 
        struct local_dsync_worker_subs_iter *iter =
675
 
                (struct local_dsync_worker_subs_iter *)_iter;
676
 
        struct local_dsync_worker *worker =
677
 
                (struct local_dsync_worker *)_iter->worker;
678
 
        struct local_dsync_dir_change *change, change_lookup;
679
 
        const struct mailbox_info *info;
680
 
        struct mail_namespace *subs_ns;
681
 
        const char *storage_name;
682
 
 
683
 
        memset(rec_r, 0, sizeof(*rec_r));
684
 
 
685
 
        info = mailbox_list_iter_next(iter->list_iter);
686
 
        if (info == NULL)
687
 
                return -1;
688
 
 
689
 
        subs_ns = find_subscription_ns(worker, info->name);
690
 
        if (subs_ns == NULL)
691
 
                subs_ns = info->ns;
692
 
        storage_name = mail_namespace_get_storage_name(subs_ns, info->name);
693
 
        if (subs_ns != info->ns)
694
 
                storage_name = t_strconcat(subs_ns->prefix, storage_name, NULL);
695
 
 
696
 
        dsync_str_sha_to_guid(storage_name, &change_lookup.name_sha1);
697
 
        change_lookup.list = info->ns->list;
698
 
 
699
 
        change = hash_table_lookup(worker->dir_changes_hash,
700
 
                                   &change_lookup);
701
 
        if (change != NULL) {
702
 
                /* it shouldn't be marked as unsubscribed, but drop it to
703
 
                   be sure */
704
 
                change->unsubscribed = FALSE;
705
 
                rec_r->last_change = change->last_subs_change;
706
 
        }
707
 
        rec_r->ns_prefix = info->ns->prefix;
708
 
        rec_r->vname = info->name;
709
 
        rec_r->storage_name = storage_name;
710
 
        return 1;
711
 
}
712
 
 
713
 
static int
714
 
local_worker_subs_iter_next_un(struct dsync_worker_subs_iter *_iter,
715
 
                               struct dsync_worker_unsubscription *rec_r)
716
 
{
717
 
        struct local_dsync_worker_subs_iter *iter =
718
 
                (struct local_dsync_worker_subs_iter *)_iter;
719
 
        struct local_dsync_worker *worker =
720
 
                (struct local_dsync_worker *)_iter->worker;
721
 
        void *key, *value;
722
 
 
723
 
        if (iter->deleted_iter == NULL) {
724
 
                iter->deleted_iter =
725
 
                        hash_table_iterate_init(worker->dir_changes_hash);
726
 
        }
727
 
        while (hash_table_iterate(iter->deleted_iter, &key, &value)) {
728
 
                const struct local_dsync_dir_change *change = value;
729
 
 
730
 
                if (change->unsubscribed) {
731
 
                        /* the name doesn't matter */
732
 
                        struct mail_namespace *ns =
733
 
                                mailbox_list_get_namespace(change->list);
734
 
                        memset(rec_r, 0, sizeof(*rec_r));
735
 
                        rec_r->name_sha1 = change->name_sha1;
736
 
                        rec_r->ns_prefix = ns->prefix;
737
 
                        rec_r->last_change = change->last_subs_change;
738
 
                        return 1;
739
 
                }
740
 
        }
741
 
        hash_table_iterate_deinit(&iter->deleted_iter);
742
 
        return -1;
743
 
}
744
 
 
745
 
static int
746
 
local_worker_subs_iter_deinit(struct dsync_worker_subs_iter *_iter)
747
 
{
748
 
        struct local_dsync_worker_subs_iter *iter =
749
 
                (struct local_dsync_worker_subs_iter *)_iter;
750
 
        int ret = _iter->failed ? -1 : 0;
751
 
 
752
 
        if (mailbox_list_iter_deinit(&iter->list_iter) < 0)
753
 
                ret = -1;
754
 
        i_free(iter);
755
 
        return ret;
756
 
}
757
 
 
758
 
static void
759
 
local_worker_set_subscribed(struct dsync_worker *_worker,
760
 
                            const char *name, time_t last_change, bool set)
761
 
{
762
 
        struct local_dsync_worker *worker =
763
 
                (struct local_dsync_worker *)_worker;
764
 
        struct mail_namespace *ns, *subs_ns;
765
 
        const char *storage_name;
766
 
 
767
 
        storage_name = name;
768
 
        ns = mail_namespace_find(worker->user->namespaces,
769
 
                                 &storage_name);
770
 
        if (ns == NULL) {
771
 
                i_error("Can't find namespace for mailbox %s", name);
772
 
                return;
773
 
        }
774
 
 
775
 
        subs_ns = find_subscription_ns(worker, name);
776
 
        if (subs_ns != NULL) {
777
 
                /* subscription is being written to a different namespace
778
 
                   than where the mailbox exists. */
779
 
                storage_name = mail_namespace_get_storage_name(subs_ns, name);
780
 
                storage_name = t_strconcat(subs_ns->prefix, storage_name, NULL);
781
 
                /* drop the common prefix */
782
 
                i_assert(strncmp(ns->prefix, storage_name,
783
 
                                 strlen(ns->prefix)) == 0);
784
 
                storage_name += strlen(ns->prefix);
785
 
        }
786
 
 
787
 
        mailbox_list_set_changelog_timestamp(ns->list, last_change);
788
 
        if (mailbox_list_set_subscribed(ns->list, storage_name, set) < 0) {
789
 
                dsync_worker_set_failure(_worker);
790
 
                i_error("Can't update subscription %s: %s", name,
791
 
                        mailbox_list_get_last_error(ns->list, NULL));
792
 
        }
793
 
        mailbox_list_set_changelog_timestamp(ns->list, (time_t)-1);
794
 
}
795
 
 
796
 
static int local_mailbox_open(struct local_dsync_worker *worker,
797
 
                              const mailbox_guid_t *guid,
798
 
                              struct mailbox **box_r)
799
 
{
800
 
        enum mailbox_flags flags = MAILBOX_FLAG_KEEP_RECENT;
801
 
        struct local_dsync_mailbox *lbox;
802
 
        struct mailbox *box;
803
 
        uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
804
 
 
805
 
        lbox = hash_table_lookup(worker->mailbox_hash, guid);
806
 
        if (lbox == NULL) {
807
 
                i_error("Trying to open a non-listed mailbox with guid=%s",
808
 
                        dsync_guid_to_str(guid));
809
 
                return -1;
810
 
        }
811
 
        if (lbox->deleted) {
812
 
                *box_r = NULL;
813
 
                return 0;
814
 
        }
815
 
 
816
 
        box = mailbox_alloc(lbox->ns->list, lbox->storage_name, flags);
817
 
        if (mailbox_sync(box, 0) < 0 ||
818
 
            mailbox_get_guid(box, mailbox_guid) < 0) {
819
 
                struct mail_storage *storage = mailbox_get_storage(box);
820
 
 
821
 
                i_error("Failed to sync mailbox %s: %s", lbox->storage_name,
822
 
                        mail_storage_get_last_error(storage, NULL));
823
 
                mailbox_free(&box);
824
 
                return -1;
825
 
        }
826
 
 
827
 
        if (memcmp(mailbox_guid, guid->guid, sizeof(guid->guid)) != 0) {
828
 
                i_error("Mailbox %s changed its GUID (%s -> %s)",
829
 
                        lbox->storage_name, dsync_guid_to_str(guid),
830
 
                        mail_guid_128_to_string(mailbox_guid));
831
 
                mailbox_free(&box);
832
 
                return -1;
833
 
        }
834
 
        *box_r = box;
835
 
        return 1;
836
 
}
837
 
 
838
 
static int iter_local_mailbox_open(struct local_dsync_worker_msg_iter *iter)
839
 
{
840
 
        struct local_dsync_worker *worker =
841
 
                (struct local_dsync_worker *)iter->iter.worker;
842
 
        mailbox_guid_t *guid;
843
 
        struct mailbox *box;
844
 
        struct mailbox_transaction_context *trans;
845
 
        struct mail_search_args *search_args;
846
 
        int ret;
847
 
 
848
 
        for (;;) {
849
 
                if (iter->mailbox_idx == iter->mailbox_count) {
850
 
                        /* no more mailboxes */
851
 
                        return -1;
852
 
                }
853
 
 
854
 
                guid = &iter->mailboxes[iter->mailbox_idx];
855
 
                ret = local_mailbox_open(worker, guid, &box);
856
 
                if (ret != 0)
857
 
                        break;
858
 
                /* mailbox was deleted. try next one. */
859
 
                iter->mailbox_idx++;
860
 
        }
861
 
        if (ret < 0) {
862
 
                i_error("msg iteration failed: Couldn't open mailbox %s",
863
 
                        dsync_guid_to_str(guid));
864
 
                iter->iter.failed = TRUE;
865
 
                return -1;
866
 
        }
867
 
 
868
 
        search_args = mail_search_build_init();
869
 
        mail_search_build_add_all(search_args);
870
 
 
871
 
        trans = mailbox_transaction_begin(box, 0);
872
 
        iter->search_ctx = mailbox_search_init(trans, search_args, NULL);
873
 
        iter->mail = mail_alloc(trans, MAIL_FETCH_FLAGS | MAIL_FETCH_GUID,
874
 
                                NULL);
875
 
        return 0;
876
 
}
877
 
 
878
 
static void
879
 
iter_local_mailbox_close(struct local_dsync_worker_msg_iter *iter)
880
 
{
881
 
        struct mailbox *box = iter->mail->box;
882
 
        struct mailbox_transaction_context *trans = iter->mail->transaction;
883
 
 
884
 
        iter->expunges_set = FALSE;
885
 
        mail_free(&iter->mail);
886
 
        if (mailbox_search_deinit(&iter->search_ctx) < 0) {
887
 
                struct mail_storage *storage =
888
 
                        mailbox_get_storage(iter->mail->box);
889
 
 
890
 
                i_error("msg search failed: %s",
891
 
                        mail_storage_get_last_error(storage, NULL));
892
 
                iter->iter.failed = TRUE;
893
 
        }
894
 
        (void)mailbox_transaction_commit(&trans);
895
 
        mailbox_free(&box);
896
 
}
897
 
 
898
 
static struct dsync_worker_msg_iter *
899
 
local_worker_msg_iter_init(struct dsync_worker *worker,
900
 
                           const mailbox_guid_t mailboxes[],
901
 
                           unsigned int mailbox_count)
902
 
{
903
 
        struct local_dsync_worker_msg_iter *iter;
904
 
        unsigned int i;
905
 
 
906
 
        iter = i_new(struct local_dsync_worker_msg_iter, 1);
907
 
        iter->iter.worker = worker;
908
 
        iter->mailboxes = mailbox_count == 0 ? NULL :
909
 
                i_new(mailbox_guid_t, mailbox_count);
910
 
        iter->mailbox_count = mailbox_count;
911
 
        for (i = 0; i < mailbox_count; i++) {
912
 
                memcpy(iter->mailboxes[i].guid, &mailboxes[i],
913
 
                       sizeof(iter->mailboxes[i].guid));
914
 
        }
915
 
        i_array_init(&iter->expunges, 32);
916
 
        iter->tmp_guid_str = str_new(default_pool, MAIL_GUID_128_SIZE * 2 + 1);
917
 
        (void)iter_local_mailbox_open(iter);
918
 
        return &iter->iter;
919
 
}
920
 
 
921
 
static int mailbox_expunge_rec_cmp(const struct mailbox_expunge_rec *e1,
922
 
                                   const struct mailbox_expunge_rec *e2)
923
 
{
924
 
        if (e1->uid < e2->uid)
925
 
                return -1;
926
 
        else if (e1->uid > e2->uid)
927
 
                return 1;
928
 
        else
929
 
                return 0;
930
 
}
931
 
 
932
 
static bool
933
 
iter_local_mailbox_next_expunge(struct local_dsync_worker_msg_iter *iter,
934
 
                                uint32_t prev_uid, struct dsync_message *msg_r)
935
 
{
936
 
        struct mailbox *box = iter->mail->box;
937
 
        struct mailbox_status status;
938
 
        const uint8_t *guid_128;
939
 
        const struct mailbox_expunge_rec *expunges;
940
 
        unsigned int count;
941
 
 
942
 
        if (iter->expunges_set) {
943
 
                expunges = array_get(&iter->expunges, &count);
944
 
                if (iter->expunge_idx == count)
945
 
                        return FALSE;
946
 
 
947
 
                memset(msg_r, 0, sizeof(*msg_r));
948
 
                str_truncate(iter->tmp_guid_str, 0);
949
 
                guid_128 = expunges[iter->expunge_idx].guid_128;
950
 
                if (!mail_guid_128_is_empty(guid_128)) {
951
 
                        binary_to_hex_append(iter->tmp_guid_str, guid_128,
952
 
                                             MAIL_GUID_128_SIZE);
953
 
                }
954
 
                msg_r->guid = str_c(iter->tmp_guid_str);
955
 
                msg_r->uid = expunges[iter->expunge_idx].uid;
956
 
                msg_r->flags = DSYNC_MAIL_FLAG_EXPUNGED;
957
 
                iter->expunge_idx++;
958
 
                return TRUE;
959
 
        }
960
 
 
961
 
        /* initialize list of expunged messages at the end of mailbox */
962
 
        iter->expunge_idx = 0;
963
 
        array_clear(&iter->expunges);
964
 
        iter->expunges_set = TRUE;
965
 
 
966
 
        mailbox_get_status(box, STATUS_UIDNEXT, &status);
967
 
        if (prev_uid + 1 >= status.uidnext) {
968
 
                /* no expunged messages at the end of mailbox */
969
 
                return FALSE;
970
 
        }
971
 
 
972
 
        T_BEGIN {
973
 
                ARRAY_TYPE(seq_range) uids_filter;
974
 
 
975
 
                t_array_init(&uids_filter, 1);
976
 
                seq_range_array_add_range(&uids_filter, prev_uid + 1,
977
 
                                          status.uidnext - 1);
978
 
                (void)mailbox_get_expunges(box, 0, &uids_filter,
979
 
                                           &iter->expunges);
980
 
                array_sort(&iter->expunges, mailbox_expunge_rec_cmp);
981
 
        } T_END;
982
 
        return iter_local_mailbox_next_expunge(iter, prev_uid, msg_r);
983
 
}
984
 
 
985
 
static int
986
 
local_worker_msg_iter_next(struct dsync_worker_msg_iter *_iter,
987
 
                           unsigned int *mailbox_idx_r,
988
 
                           struct dsync_message *msg_r)
989
 
{
990
 
        struct local_dsync_worker_msg_iter *iter =
991
 
                (struct local_dsync_worker_msg_iter *)_iter;
992
 
        const char *guid;
993
 
        uint32_t prev_uid;
994
 
 
995
 
        if (_iter->failed || iter->search_ctx == NULL)
996
 
                return -1;
997
 
 
998
 
        prev_uid = iter->mail->uid;
999
 
        if (!mailbox_search_next(iter->search_ctx, iter->mail)) {
1000
 
                if (iter_local_mailbox_next_expunge(iter, prev_uid, msg_r)) {
1001
 
                        *mailbox_idx_r = iter->mailbox_idx;
1002
 
                        return 1;
1003
 
                }
1004
 
                iter_local_mailbox_close(iter);
1005
 
                iter->mailbox_idx++;
1006
 
                if (iter_local_mailbox_open(iter) < 0)
1007
 
                        return -1;
1008
 
                return local_worker_msg_iter_next(_iter, mailbox_idx_r, msg_r);
1009
 
        }
1010
 
        *mailbox_idx_r = iter->mailbox_idx;
1011
 
 
1012
 
        if (mail_get_special(iter->mail, MAIL_FETCH_GUID, &guid) < 0) {
1013
 
                if (!iter->mail->expunged) {
1014
 
                        struct mail_storage *storage =
1015
 
                                mailbox_get_storage(iter->mail->box);
1016
 
 
1017
 
                        i_error("msg guid lookup failed: %s",
1018
 
                                mail_storage_get_last_error(storage, NULL));
1019
 
                        _iter->failed = TRUE;
1020
 
                        return -1;
1021
 
                }
1022
 
                return local_worker_msg_iter_next(_iter, mailbox_idx_r, msg_r);
1023
 
        }
1024
 
 
1025
 
        memset(msg_r, 0, sizeof(*msg_r));
1026
 
        msg_r->guid = guid;
1027
 
        msg_r->uid = iter->mail->uid;
1028
 
        msg_r->flags = mail_get_flags(iter->mail);
1029
 
        msg_r->keywords = mail_get_keywords(iter->mail);
1030
 
        msg_r->modseq = mail_get_modseq(iter->mail);
1031
 
        if (mail_get_save_date(iter->mail, &msg_r->save_date) < 0)
1032
 
                msg_r->save_date = (time_t)-1;
1033
 
        return 1;
1034
 
}
1035
 
 
1036
 
static int
1037
 
local_worker_msg_iter_deinit(struct dsync_worker_msg_iter *_iter)
1038
 
{
1039
 
        struct local_dsync_worker_msg_iter *iter =
1040
 
                (struct local_dsync_worker_msg_iter *)_iter;
1041
 
        int ret = _iter->failed ? -1 : 0;
1042
 
 
1043
 
        if (iter->mail != NULL)
1044
 
                iter_local_mailbox_close(iter);
1045
 
        array_free(&iter->expunges);
1046
 
        str_free(&iter->tmp_guid_str);
1047
 
        i_free(iter->mailboxes);
1048
 
        i_free(iter);
1049
 
        return ret;
1050
 
}
1051
 
 
1052
 
static void
1053
 
local_worker_copy_mailbox_update(const struct dsync_mailbox *dsync_box,
1054
 
                                 struct mailbox_update *update_r)
1055
 
{
1056
 
        memset(update_r, 0, sizeof(*update_r));
1057
 
        memcpy(update_r->mailbox_guid, dsync_box->mailbox_guid.guid,
1058
 
               sizeof(update_r->mailbox_guid));
1059
 
        update_r->uid_validity = dsync_box->uid_validity;
1060
 
        update_r->min_next_uid = dsync_box->uid_next;
1061
 
        update_r->min_first_recent_uid = dsync_box->first_recent_uid;
1062
 
        update_r->min_highest_modseq = dsync_box->highest_modseq;
1063
 
}
1064
 
 
1065
 
static const char *
1066
 
mailbox_name_convert(struct local_dsync_worker *worker,
1067
 
                     const char *name, char src_sep, char dest_sep)
1068
 
{
1069
 
        char *dest_name, *p;
1070
 
 
1071
 
        dest_name = t_strdup_noconst(name);
1072
 
        for (p = dest_name; *p != '\0'; p++) {
1073
 
                if (*p == dest_sep && worker->alt_char != '\0')
1074
 
                        *p = worker->alt_char;
1075
 
                else if (*p == src_sep)
1076
 
                        *p = dest_sep;
1077
 
        }
1078
 
        return dest_name;
1079
 
}
1080
 
 
1081
 
static const char *
1082
 
mailbox_name_cleanup(const char *input, char real_sep, char alt_char)
1083
 
{
1084
 
        char *output, *p;
1085
 
 
1086
 
        output = t_strdup_noconst(input);
1087
 
        for (p = output; *p != '\0'; p++) {
1088
 
                if (*p == real_sep || (uint8_t)*input < 32 ||
1089
 
                    (uint8_t)*input >= 0x80)
1090
 
                        *p = alt_char;
1091
 
        }
1092
 
        return output;
1093
 
}
1094
 
 
1095
 
static const char *mailbox_name_force_cleanup(const char *input, char alt_char)
1096
 
{
1097
 
        char *output, *p;
1098
 
 
1099
 
        output = t_strdup_noconst(input);
1100
 
        for (p = output; *p != '\0'; p++) {
1101
 
                if (!i_isalnum(*p))
1102
 
                        *p = alt_char;
1103
 
        }
1104
 
        return output;
1105
 
}
1106
 
 
1107
 
static const char *
1108
 
local_worker_convert_mailbox_name(struct local_dsync_worker *worker,
1109
 
                                  const char *name, struct mail_namespace *ns,
1110
 
                                  const struct dsync_mailbox *dsync_box,
1111
 
                                  bool creating)
1112
 
{
1113
 
        if (dsync_box->name_sep != ns->sep) {
1114
 
                /* mailbox names use different separators. convert them. */
1115
 
                name = mailbox_name_convert(worker, name,
1116
 
                                            dsync_box->name_sep, ns->sep);
1117
 
        }
1118
 
        if (creating) {
1119
 
                if (!mailbox_list_is_valid_create_name(ns->list, name)) {
1120
 
                        /* change any real separators to alt separators,
1121
 
                           drop any potentially invalid characters */
1122
 
                        name = mailbox_name_cleanup(name, ns->real_sep,
1123
 
                                                    worker->alt_char);
1124
 
                }
1125
 
                if (!mailbox_list_is_valid_create_name(ns->list, name)) {
1126
 
                        /* still not working, apparently it's not valid mUTF-7.
1127
 
                           just drop all non-alphanumeric characters. */
1128
 
                        name = mailbox_name_force_cleanup(name,
1129
 
                                                          worker->alt_char);
1130
 
                }
1131
 
                if (!mailbox_list_is_valid_create_name(ns->list, name)) {
1132
 
                        /* probably some reserved name (e.g. dbox-Mails) */
1133
 
                        name = t_strconcat("_", name, NULL);
1134
 
                }
1135
 
                if (!mailbox_list_is_valid_create_name(ns->list, name)) {
1136
 
                        /* name is too long? just give up and generate a
1137
 
                           unique name */
1138
 
                        uint8_t guid[MAIL_GUID_128_SIZE];
1139
 
 
1140
 
                        mail_generate_guid_128(guid);
1141
 
                        name = mail_guid_128_to_string(guid);
1142
 
                }
1143
 
                i_assert(mailbox_list_is_valid_create_name(ns->list, name));
1144
 
        }
1145
 
        return name;
1146
 
}
1147
 
 
1148
 
static struct mailbox *
1149
 
local_worker_mailbox_alloc(struct local_dsync_worker *worker,
1150
 
                           const struct dsync_mailbox *dsync_box, bool creating)
1151
 
{
1152
 
        struct mail_namespace *ns;
1153
 
        struct local_dsync_mailbox *lbox;
1154
 
        const char *name;
1155
 
 
1156
 
        lbox = dsync_mailbox_is_noselect(dsync_box) ? NULL :
1157
 
                hash_table_lookup(worker->mailbox_hash,
1158
 
                                  &dsync_box->mailbox_guid);
1159
 
        if (lbox != NULL) {
1160
 
                /* use the existing known mailbox name */
1161
 
                return mailbox_alloc(lbox->ns->list, lbox->storage_name, 0);
1162
 
        }
1163
 
 
1164
 
        name = dsync_box->name;
1165
 
        ns = mail_namespace_find(worker->user->namespaces, &name);
1166
 
        if (ns == NULL) {
1167
 
                i_error("Can't find namespace for mailbox %s", dsync_box->name);
1168
 
                return NULL;
1169
 
        }
1170
 
 
1171
 
        name = local_worker_convert_mailbox_name(worker, name, ns,
1172
 
                                                 dsync_box, creating);
1173
 
        if (!dsync_mailbox_is_noselect(dsync_box)) {
1174
 
                local_dsync_worker_add_mailbox(worker, ns, name,
1175
 
                                               &dsync_box->mailbox_guid);
1176
 
        }
1177
 
        return mailbox_alloc(ns->list, name, 0);
1178
 
}
1179
 
 
1180
 
static int local_worker_create_dir(struct mailbox *box,
1181
 
                                   const struct dsync_mailbox *dsync_box)
1182
 
{
1183
 
        struct mailbox_list *list = mailbox_get_namespace(box)->list;
1184
 
        const char *errstr;
1185
 
        enum mail_error error;
1186
 
 
1187
 
        if (mailbox_list_create_dir(list, mailbox_get_name(box)) == 0)
1188
 
                return 0;
1189
 
 
1190
 
        errstr = mailbox_list_get_last_error(list, &error);
1191
 
        switch (error) {
1192
 
        case MAIL_ERROR_EXISTS:
1193
 
                /* directory already exists - that's ok */
1194
 
                return 0;
1195
 
        case MAIL_ERROR_NOTPOSSIBLE:
1196
 
                /* \noselect mailboxes not supported - just ignore them
1197
 
                   (we don't want to create a selectable mailbox if the other
1198
 
                   side of the sync doesn't support dual-use mailboxes,
1199
 
                   e.g. mbox) */
1200
 
                return 0;
1201
 
        default:
1202
 
                i_error("Can't create mailbox %s: %s", dsync_box->name, errstr);
1203
 
                return -1;
1204
 
        }
1205
 
}
1206
 
 
1207
 
static int
1208
 
local_worker_create_allocated_mailbox(struct local_dsync_worker *worker,
1209
 
                                      struct mailbox *box,
1210
 
                                      const struct dsync_mailbox *dsync_box)
1211
 
{
1212
 
        struct mailbox_update update;
1213
 
        const char *errstr;
1214
 
        enum mail_error error;
1215
 
 
1216
 
        local_worker_copy_mailbox_update(dsync_box, &update);
1217
 
 
1218
 
        if (dsync_mailbox_is_noselect(dsync_box)) {
1219
 
                if (local_worker_create_dir(box, dsync_box) < 0) {
1220
 
                        dsync_worker_set_failure(&worker->worker);
1221
 
                        return -1;
1222
 
                }
1223
 
                return 1;
1224
 
        }
1225
 
 
1226
 
        if (mailbox_create(box, &update, FALSE) < 0) {
1227
 
                errstr = mail_storage_get_last_error(mailbox_get_storage(box),
1228
 
                                                     &error);
1229
 
                if (error == MAIL_ERROR_EXISTS) {
1230
 
                        /* mailbox already exists */
1231
 
                        return 0;
1232
 
                }
1233
 
 
1234
 
                dsync_worker_set_failure(&worker->worker);
1235
 
                i_error("Can't create mailbox %s: %s", dsync_box->name, errstr);
1236
 
                return -1;
1237
 
        }
1238
 
 
1239
 
        local_dsync_worker_add_mailbox(worker,
1240
 
                                       mailbox_get_namespace(box),
1241
 
                                       mailbox_get_name(box),
1242
 
                                       &dsync_box->mailbox_guid);
1243
 
        return 1;
1244
 
}
1245
 
 
1246
 
static void
1247
 
local_worker_create_mailbox(struct dsync_worker *_worker,
1248
 
                            const struct dsync_mailbox *dsync_box)
1249
 
{
1250
 
        struct local_dsync_worker *worker =
1251
 
                (struct local_dsync_worker *)_worker;
1252
 
        struct mailbox *box;
1253
 
        struct mail_namespace *ns;
1254
 
        const char *new_name;
1255
 
        int ret;
1256
 
 
1257
 
        box = local_worker_mailbox_alloc(worker, dsync_box, TRUE);
1258
 
        if (box == NULL) {
1259
 
                dsync_worker_set_failure(_worker);
1260
 
                return;
1261
 
        }
1262
 
 
1263
 
        ret = local_worker_create_allocated_mailbox(worker, box, dsync_box);
1264
 
        if (ret != 0) {
1265
 
                mailbox_free(&box);
1266
 
                return;
1267
 
        }
1268
 
 
1269
 
        /* mailbox name already exists. add mailbox guid to the name,
1270
 
           that shouldn't exist. */
1271
 
        new_name = t_strconcat(mailbox_get_name(box), "_",
1272
 
                               dsync_guid_to_str(&dsync_box->mailbox_guid),
1273
 
                               NULL);
1274
 
        ns = mailbox_get_namespace(box);
1275
 
        mailbox_free(&box);
1276
 
 
1277
 
        local_dsync_worker_add_mailbox(worker, ns, new_name,
1278
 
                                       &dsync_box->mailbox_guid);
1279
 
        box = mailbox_alloc(ns->list, new_name, 0);
1280
 
        (void)local_worker_create_allocated_mailbox(worker, box, dsync_box);
1281
 
        mailbox_free(&box);
1282
 
}
1283
 
 
1284
 
static void
1285
 
local_worker_delete_mailbox(struct dsync_worker *_worker,
1286
 
                            const struct dsync_mailbox *dsync_box)
1287
 
{
1288
 
        struct local_dsync_worker *worker =
1289
 
                (struct local_dsync_worker *)_worker;
1290
 
        struct local_dsync_mailbox *lbox;
1291
 
        const mailbox_guid_t *mailbox = &dsync_box->mailbox_guid;
1292
 
        struct mailbox *box;
1293
 
 
1294
 
        lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
1295
 
        if (lbox == NULL) {
1296
 
                i_error("Trying to delete a non-listed mailbox with guid=%s",
1297
 
                        dsync_guid_to_str(mailbox));
1298
 
                dsync_worker_set_failure(_worker);
1299
 
                return;
1300
 
        }
1301
 
 
1302
 
        mailbox_list_set_changelog_timestamp(lbox->ns->list,
1303
 
                                             dsync_box->last_change);
1304
 
        box = mailbox_alloc(lbox->ns->list, lbox->storage_name, 0);
1305
 
        if (mailbox_delete(box) < 0) {
1306
 
                struct mail_storage *storage = mailbox_get_storage(box);
1307
 
 
1308
 
                i_error("Can't delete mailbox %s: %s", lbox->storage_name,
1309
 
                        mail_storage_get_last_error(storage, NULL));
1310
 
                dsync_worker_set_failure(_worker);
1311
 
        } else {
1312
 
                lbox->deleted = TRUE;
1313
 
        }
1314
 
        mailbox_free(&box);
1315
 
        mailbox_list_set_changelog_timestamp(lbox->ns->list, (time_t)-1);
1316
 
}
1317
 
 
1318
 
static void
1319
 
local_worker_delete_dir(struct dsync_worker *_worker,
1320
 
                        const struct dsync_mailbox *dsync_box)
1321
 
{
1322
 
        struct local_dsync_worker *worker =
1323
 
                (struct local_dsync_worker *)_worker;
1324
 
        struct mail_namespace *ns;
1325
 
        const char *storage_name;
1326
 
        enum mail_error error;
1327
 
 
1328
 
        storage_name = dsync_box->name;
1329
 
        ns = mail_namespace_find(worker->user->namespaces, &storage_name);
1330
 
 
1331
 
        mailbox_list_set_changelog_timestamp(ns->list, dsync_box->last_change);
1332
 
        if (mailbox_list_delete_dir(ns->list, storage_name) < 0) {
1333
 
                (void)mailbox_list_get_last_error(ns->list, &error);
1334
 
                if (error == MAIL_ERROR_EXISTS) {
1335
 
                        /* we're probably doing Maildir++ -> FS layout sync,
1336
 
                           where a nonexistent Maildir++ mailbox had to be
1337
 
                           created as \Noselect FS directory.
1338
 
                           just ignore this. */
1339
 
                } else {
1340
 
                        i_error("Can't delete mailbox directory %s: %s",
1341
 
                                dsync_box->name,
1342
 
                                mailbox_list_get_last_error(ns->list, NULL));
1343
 
                }
1344
 
        }
1345
 
        mailbox_list_set_changelog_timestamp(ns->list, (time_t)-1);
1346
 
}
1347
 
 
1348
 
static void
1349
 
local_worker_rename_mailbox(struct dsync_worker *_worker,
1350
 
                            const mailbox_guid_t *mailbox,
1351
 
                            const struct dsync_mailbox *dsync_box)
1352
 
{
1353
 
        struct local_dsync_worker *worker =
1354
 
                (struct local_dsync_worker *)_worker;
1355
 
        struct local_dsync_mailbox *lbox;
1356
 
        struct mailbox_list *list;
1357
 
        struct mailbox *old_box, *new_box;
1358
 
        const char *newname;
1359
 
 
1360
 
        lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
1361
 
        if (lbox == NULL) {
1362
 
                i_error("Trying to rename a non-listed mailbox with guid=%s",
1363
 
                        dsync_guid_to_str(mailbox));
1364
 
                dsync_worker_set_failure(_worker);
1365
 
                return;
1366
 
        }
1367
 
 
1368
 
        list = lbox->ns->list;
1369
 
        newname = local_worker_convert_mailbox_name(worker, dsync_box->name,
1370
 
                                                    lbox->ns, dsync_box, TRUE);
1371
 
        if (strcmp(lbox->storage_name, newname) == 0) {
1372
 
                /* nothing changed after all. probably because some characters
1373
 
                   in mailbox name weren't valid. */
1374
 
                return;
1375
 
        }
1376
 
 
1377
 
        mailbox_list_set_changelog_timestamp(list, dsync_box->last_change);
1378
 
        old_box = mailbox_alloc(list, lbox->storage_name, 0);
1379
 
        new_box = mailbox_alloc(list, newname, 0);
1380
 
        if (mailbox_rename(old_box, new_box, FALSE) < 0) {
1381
 
                struct mail_storage *storage = mailbox_get_storage(old_box);
1382
 
 
1383
 
                i_error("Can't rename mailbox %s to %s: %s", lbox->storage_name,
1384
 
                        newname, mail_storage_get_last_error(storage, NULL));
1385
 
                dsync_worker_set_failure(_worker);
1386
 
        } else {
1387
 
                lbox->storage_name = p_strdup(worker->pool, newname);
1388
 
        }
1389
 
        mailbox_free(&old_box);
1390
 
        mailbox_free(&new_box);
1391
 
        mailbox_list_set_changelog_timestamp(list, (time_t)-1);
1392
 
}
1393
 
 
1394
 
static bool
1395
 
has_expected_save_uids(struct local_dsync_worker *worker,
1396
 
                       const struct mail_transaction_commit_changes *changes)
1397
 
{
1398
 
        struct seq_range_iter iter;
1399
 
        const uint32_t *expected_uids;
1400
 
        uint32_t uid;
1401
 
        unsigned int i, n, expected_count;
1402
 
 
1403
 
        expected_uids = array_get(&worker->saved_uids, &expected_count);
1404
 
        seq_range_array_iter_init(&iter, &changes->saved_uids); i = n = 0;
1405
 
        while (seq_range_array_iter_nth(&iter, n++, &uid)) {
1406
 
                if (i == expected_count || uid != expected_uids[i++])
1407
 
                        return FALSE;
1408
 
        }
1409
 
        return i == expected_count;
1410
 
}
1411
 
 
1412
 
static void local_worker_mailbox_close(struct local_dsync_worker *worker)
1413
 
{
1414
 
        struct mailbox_transaction_context *trans, *ext_trans;
1415
 
        struct mail_transaction_commit_changes changes;
1416
 
 
1417
 
        i_assert(worker->save_input == NULL);
1418
 
 
1419
 
        memset(&worker->selected_box_guid, 0,
1420
 
               sizeof(worker->selected_box_guid));
1421
 
 
1422
 
        if (worker->selected_box == NULL)
1423
 
                return;
1424
 
 
1425
 
        trans = worker->mail->transaction;
1426
 
        ext_trans = worker->ext_mail->transaction;
1427
 
        mail_free(&worker->mail);
1428
 
        mail_free(&worker->ext_mail);
1429
 
 
1430
 
        /* all saves and copies go to ext_trans */
1431
 
        if (mailbox_transaction_commit_get_changes(&ext_trans, &changes) < 0)
1432
 
                dsync_worker_set_failure(&worker->worker);
1433
 
        else {
1434
 
                if (changes.ignored_modseq_changes != 0) {
1435
 
                        if (worker->worker.verbose) {
1436
 
                                i_info("%s: Ignored %u modseq changes",
1437
 
                                       mailbox_get_vname(worker->selected_box),
1438
 
                                       changes.ignored_modseq_changes);
1439
 
                        }
1440
 
                        worker->worker.unexpected_changes = TRUE;
1441
 
                }
1442
 
                if (!has_expected_save_uids(worker, &changes)) {
1443
 
                        if (worker->worker.verbose) {
1444
 
                                i_info("%s: Couldn't keep all uids",
1445
 
                                       mailbox_get_vname(worker->selected_box));
1446
 
                        }
1447
 
                        worker->worker.unexpected_changes = TRUE;
1448
 
                }
1449
 
                pool_unref(&changes.pool);
1450
 
        }
1451
 
        array_clear(&worker->saved_uids);
1452
 
 
1453
 
        if (mailbox_transaction_commit(&trans) < 0 ||
1454
 
            mailbox_sync(worker->selected_box, MAILBOX_SYNC_FLAG_FULL_WRITE) < 0)
1455
 
                dsync_worker_set_failure(&worker->worker);
1456
 
 
1457
 
        mailbox_free(&worker->selected_box);
1458
 
}
1459
 
 
1460
 
static void
1461
 
local_worker_update_mailbox(struct dsync_worker *_worker,
1462
 
                            const struct dsync_mailbox *dsync_box)
1463
 
{
1464
 
        struct local_dsync_worker *worker =
1465
 
                (struct local_dsync_worker *)_worker;
1466
 
        struct mailbox *box;
1467
 
        struct mailbox_update update;
1468
 
        bool selected = FALSE;
1469
 
 
1470
 
        /* if we're updating a selected mailbox, close it first so that all
1471
 
           pending changes get committed. */
1472
 
        selected = worker->selected_box != NULL &&
1473
 
                dsync_guid_equals(&dsync_box->mailbox_guid,
1474
 
                                  &worker->selected_box_guid);
1475
 
        if (selected)
1476
 
                local_worker_mailbox_close(worker);
1477
 
 
1478
 
        box = local_worker_mailbox_alloc(worker, dsync_box, FALSE);
1479
 
        if (box == NULL) {
1480
 
                dsync_worker_set_failure(_worker);
1481
 
                return;
1482
 
        }
1483
 
 
1484
 
        local_worker_copy_mailbox_update(dsync_box, &update);
1485
 
        if (mailbox_update(box, &update) < 0) {
1486
 
                dsync_worker_set_failure(_worker);
1487
 
                i_error("Can't update mailbox %s: %s", dsync_box->name,
1488
 
                        mail_storage_get_last_error(mailbox_get_storage(box),
1489
 
                                                    NULL));
1490
 
        }
1491
 
        mailbox_free(&box);
1492
 
 
1493
 
        if (selected)
1494
 
                dsync_worker_select_mailbox(_worker, dsync_box);
1495
 
}
1496
 
 
1497
 
static void
1498
 
local_worker_set_cache_fields(struct local_dsync_worker *worker,
1499
 
                              const ARRAY_TYPE(const_string) *cache_fields)
1500
 
{
1501
 
        struct mailbox_update update;
1502
 
        const char *const *fields, **new_fields;
1503
 
        unsigned int count;
1504
 
 
1505
 
        fields = array_get(cache_fields, &count);
1506
 
        new_fields = t_new(const char *, count + 1);
1507
 
        memcpy(new_fields, fields, sizeof(const char *) * count);
1508
 
 
1509
 
        memset(&update, 0, sizeof(update));
1510
 
        update.cache_fields = new_fields;
1511
 
        mailbox_update(worker->selected_box, &update);
1512
 
}
1513
 
 
1514
 
static void
1515
 
local_worker_select_mailbox(struct dsync_worker *_worker,
1516
 
                            const mailbox_guid_t *mailbox,
1517
 
                            const ARRAY_TYPE(const_string) *cache_fields)
1518
 
{
1519
 
        struct local_dsync_worker *worker =
1520
 
                (struct local_dsync_worker *)_worker;
1521
 
        struct mailbox_transaction_context *trans, *ext_trans;
1522
 
 
1523
 
        if (dsync_guid_equals(&worker->selected_box_guid, mailbox)) {
1524
 
                /* already selected or previous select failed */
1525
 
                i_assert(worker->selected_box != NULL || _worker->failed);
1526
 
                return;
1527
 
        }
1528
 
        if (worker->selected_box != NULL)
1529
 
                local_worker_mailbox_close(worker);
1530
 
        worker->selected_box_guid = *mailbox;
1531
 
 
1532
 
        if (local_mailbox_open(worker, mailbox, &worker->selected_box) <= 0) {
1533
 
                dsync_worker_set_failure(_worker);
1534
 
                return;
1535
 
        }
1536
 
        if (cache_fields != NULL && array_is_created(cache_fields))
1537
 
                local_worker_set_cache_fields(worker, cache_fields);
1538
 
 
1539
 
        ext_trans = mailbox_transaction_begin(worker->selected_box,
1540
 
                                        MAILBOX_TRANSACTION_FLAG_EXTERNAL |
1541
 
                                        MAILBOX_TRANSACTION_FLAG_ASSIGN_UIDS);
1542
 
        trans = mailbox_transaction_begin(worker->selected_box, 0);
1543
 
        worker->mail = mail_alloc(trans, 0, NULL);
1544
 
        worker->ext_mail = mail_alloc(ext_trans, 0, NULL);
1545
 
}
1546
 
 
1547
 
static void
1548
 
local_worker_msg_update_metadata(struct dsync_worker *_worker,
1549
 
                                 const struct dsync_message *msg)
1550
 
{
1551
 
        struct local_dsync_worker *worker =
1552
 
                (struct local_dsync_worker *)_worker;
1553
 
        struct mail_keywords *keywords;
1554
 
 
1555
 
        if (msg->modseq > 1) {
1556
 
                (void)mailbox_enable(worker->mail->box,
1557
 
                                     MAILBOX_FEATURE_CONDSTORE);
1558
 
        }
1559
 
 
1560
 
        if (!mail_set_uid(worker->mail, msg->uid))
1561
 
                dsync_worker_set_failure(_worker);
1562
 
        else {
1563
 
                mail_update_flags(worker->mail, MODIFY_REPLACE, msg->flags);
1564
 
 
1565
 
                keywords = mailbox_keywords_create_valid(worker->mail->box,
1566
 
                                                         msg->keywords);
1567
 
                mail_update_keywords(worker->mail, MODIFY_REPLACE, keywords);
1568
 
                mailbox_keywords_unref(worker->mail->box, &keywords);
1569
 
                mail_update_modseq(worker->mail, msg->modseq);
1570
 
        }
1571
 
}
1572
 
 
1573
 
static void
1574
 
local_worker_msg_update_uid(struct dsync_worker *_worker,
1575
 
                            uint32_t old_uid, uint32_t new_uid)
1576
 
{
1577
 
        struct local_dsync_worker *worker =
1578
 
                (struct local_dsync_worker *)_worker;
1579
 
        struct mail_save_context *save_ctx;
1580
 
 
1581
 
        if (!mail_set_uid(worker->ext_mail, old_uid)) {
1582
 
                dsync_worker_set_failure(_worker);
1583
 
                return;
1584
 
        }
1585
 
 
1586
 
        save_ctx = mailbox_save_alloc(worker->ext_mail->transaction);
1587
 
        mailbox_save_copy_flags(save_ctx, worker->ext_mail);
1588
 
        mailbox_save_set_uid(save_ctx, new_uid);
1589
 
        if (mailbox_copy(&save_ctx, worker->ext_mail) == 0)
1590
 
                mail_expunge(worker->ext_mail);
1591
 
}
1592
 
 
1593
 
static void local_worker_msg_expunge(struct dsync_worker *_worker, uint32_t uid)
1594
 
{
1595
 
        struct local_dsync_worker *worker =
1596
 
                (struct local_dsync_worker *)_worker;
1597
 
 
1598
 
        if (mail_set_uid(worker->mail, uid))
1599
 
                mail_expunge(worker->mail);
1600
 
}
1601
 
 
1602
 
static void
1603
 
local_worker_msg_save_set_metadata(struct local_dsync_worker *worker,
1604
 
                                   struct mailbox *box,
1605
 
                                   struct mail_save_context *save_ctx,
1606
 
                                   const struct dsync_message *msg)
1607
 
{
1608
 
        struct mail_keywords *keywords;
1609
 
 
1610
 
        i_assert(msg->uid != 0);
1611
 
 
1612
 
        if (msg->modseq > 1)
1613
 
                (void)mailbox_enable(box, MAILBOX_FEATURE_CONDSTORE);
1614
 
 
1615
 
        keywords = str_array_length(msg->keywords) == 0 ? NULL :
1616
 
                mailbox_keywords_create_valid(box, msg->keywords);
1617
 
        mailbox_save_set_flags(save_ctx, msg->flags, keywords);
1618
 
        if (keywords != NULL)
1619
 
                mailbox_keywords_unref(box, &keywords);
1620
 
        mailbox_save_set_uid(save_ctx, msg->uid);
1621
 
        mailbox_save_set_save_date(save_ctx, msg->save_date);
1622
 
        mailbox_save_set_min_modseq(save_ctx, msg->modseq);
1623
 
 
1624
 
        array_append(&worker->saved_uids, &msg->uid, 1);
1625
 
}
1626
 
 
1627
 
static void
1628
 
local_worker_msg_copy(struct dsync_worker *_worker,
1629
 
                      const mailbox_guid_t *src_mailbox, uint32_t src_uid,
1630
 
                      const struct dsync_message *dest_msg,
1631
 
                      dsync_worker_copy_callback_t *callback, void *context)
1632
 
{
1633
 
        struct local_dsync_worker *worker =
1634
 
                (struct local_dsync_worker *)_worker;
1635
 
        struct mailbox *src_box;
1636
 
        struct mailbox_transaction_context *src_trans;
1637
 
        struct mail *src_mail;
1638
 
        struct mail_save_context *save_ctx;
1639
 
        int ret;
1640
 
 
1641
 
        if (local_mailbox_open(worker, src_mailbox, &src_box) <= 0) {
1642
 
                callback(FALSE, context);
1643
 
                return;
1644
 
        }
1645
 
 
1646
 
        src_trans = mailbox_transaction_begin(src_box, 0);
1647
 
        src_mail = mail_alloc(src_trans, 0, NULL);
1648
 
        if (!mail_set_uid(src_mail, src_uid))
1649
 
                ret = -1;
1650
 
        else {
1651
 
                save_ctx = mailbox_save_alloc(worker->ext_mail->transaction);
1652
 
                local_worker_msg_save_set_metadata(worker, worker->mail->box,
1653
 
                                                   save_ctx, dest_msg);
1654
 
                ret = mailbox_copy(&save_ctx, src_mail);
1655
 
        }
1656
 
 
1657
 
        mail_free(&src_mail);
1658
 
        (void)mailbox_transaction_commit(&src_trans);
1659
 
        mailbox_free(&src_box);
1660
 
 
1661
 
        callback(ret == 0, context);
1662
 
}
1663
 
 
1664
 
static void dsync_worker_try_finish(struct local_dsync_worker *worker)
1665
 
{
1666
 
        if (worker->finish_callback == NULL)
1667
 
                return;
1668
 
        if (worker->save_io != NULL || worker->reading_mail)
1669
 
                return;
1670
 
 
1671
 
        i_assert(worker->finishing);
1672
 
        i_assert(!worker->finished);
1673
 
        worker->finishing = FALSE;
1674
 
        worker->finished = TRUE;
1675
 
        worker->finish_callback(!worker->worker.failed, worker->finish_context);
1676
 
}
1677
 
 
1678
 
static void
1679
 
local_worker_save_msg_continue(struct local_dsync_worker *worker)
1680
 
{
1681
 
        struct mailbox *dest_box = worker->ext_mail->box;
1682
 
        dsync_worker_save_callback_t *callback;
1683
 
        ssize_t ret;
1684
 
        bool save_failed = FALSE;
1685
 
 
1686
 
        while ((ret = i_stream_read(worker->save_input)) > 0 || ret == -2) {
1687
 
                if (mailbox_save_continue(worker->save_ctx) < 0) {
1688
 
                        save_failed = TRUE;
1689
 
                        ret = -1;
1690
 
                        break;
1691
 
                }
1692
 
        }
1693
 
        if (ret == 0) {
1694
 
                if (worker->save_io != NULL)
1695
 
                        return;
1696
 
                worker->save_io =
1697
 
                        io_add(i_stream_get_fd(worker->save_input), IO_READ,
1698
 
                               local_worker_save_msg_continue, worker);
1699
 
                return;
1700
 
        }
1701
 
        i_assert(ret == -1);
1702
 
 
1703
 
        /* drop save_io before destroying save_input, so that save_input's
1704
 
           destroy callback can add io back to its fd. */
1705
 
        if (worker->save_io != NULL)
1706
 
                io_remove(&worker->save_io);
1707
 
        if (worker->save_input->stream_errno != 0) {
1708
 
                errno = worker->save_input->stream_errno;
1709
 
                i_error("read(msg input) failed: %m");
1710
 
                mailbox_save_cancel(&worker->save_ctx);
1711
 
                dsync_worker_set_failure(&worker->worker);
1712
 
        } else if (save_failed) {
1713
 
                mailbox_save_cancel(&worker->save_ctx);
1714
 
                dsync_worker_set_failure(&worker->worker);
1715
 
        } else {
1716
 
                i_assert(worker->save_input->eof);
1717
 
                if (mailbox_save_finish(&worker->save_ctx) < 0) {
1718
 
                        struct mail_storage *storage =
1719
 
                                mailbox_get_storage(dest_box);
1720
 
 
1721
 
                        i_error("Can't save message to mailbox %s: %s",
1722
 
                                mailbox_get_vname(dest_box),
1723
 
                                mail_storage_get_last_error(storage, NULL));
1724
 
                        dsync_worker_set_failure(&worker->worker);
1725
 
                }
1726
 
        }
1727
 
        callback = worker->save_callback;
1728
 
        worker->save_callback = NULL;
1729
 
        i_stream_unref(&worker->save_input);
1730
 
 
1731
 
        dsync_worker_try_finish(worker);
1732
 
        /* call the callback last, since it could call worker code again and
1733
 
           cause problems (e.g. if _try_finish() is called again, it could
1734
 
           cause a duplicate finish_callback()) */
1735
 
        callback(worker->save_context);
1736
 
}
1737
 
 
1738
 
static void
1739
 
local_worker_msg_save(struct dsync_worker *_worker,
1740
 
                      const struct dsync_message *msg,
1741
 
                      const struct dsync_msg_static_data *data,
1742
 
                      dsync_worker_save_callback_t *callback,
1743
 
                      void *context)
1744
 
{
1745
 
        struct local_dsync_worker *worker =
1746
 
                (struct local_dsync_worker *)_worker;
1747
 
        struct mailbox *dest_box = worker->ext_mail->box;
1748
 
        struct mail_save_context *save_ctx;
1749
 
 
1750
 
        i_assert(worker->save_input == NULL);
1751
 
 
1752
 
        save_ctx = mailbox_save_alloc(worker->ext_mail->transaction);
1753
 
        mailbox_save_set_guid(save_ctx, msg->guid);
1754
 
        local_worker_msg_save_set_metadata(worker, worker->mail->box,
1755
 
                                           save_ctx, msg);
1756
 
        if (*data->pop3_uidl != '\0')
1757
 
                mailbox_save_set_pop3_uidl(save_ctx, data->pop3_uidl);
1758
 
 
1759
 
        mailbox_save_set_received_date(save_ctx, data->received_date, 0);
1760
 
 
1761
 
        if (mailbox_save_begin(&save_ctx, data->input) < 0) {
1762
 
                struct mail_storage *storage = mailbox_get_storage(dest_box);
1763
 
                i_error("Can't save message to mailbox %s: %s",
1764
 
                        mailbox_get_vname(dest_box),
1765
 
                        mail_storage_get_last_error(storage, NULL));
1766
 
                dsync_worker_set_failure(_worker);
1767
 
                callback(context);
1768
 
                return;
1769
 
        }
1770
 
 
1771
 
        worker->save_callback = callback;
1772
 
        worker->save_context = context;
1773
 
        worker->save_input = data->input;
1774
 
        worker->save_ctx = save_ctx;
1775
 
        i_stream_ref(worker->save_input);
1776
 
        local_worker_save_msg_continue(worker);
1777
 
}
1778
 
 
1779
 
static void local_worker_msg_save_cancel(struct dsync_worker *_worker)
1780
 
{
1781
 
        struct local_dsync_worker *worker =
1782
 
                (struct local_dsync_worker *)_worker;
1783
 
 
1784
 
        if (worker->save_input == NULL)
1785
 
                return;
1786
 
 
1787
 
        if (worker->save_io != NULL)
1788
 
                io_remove(&worker->save_io);
1789
 
        mailbox_save_cancel(&worker->save_ctx);
1790
 
        i_stream_unref(&worker->save_input);
1791
 
}
1792
 
 
1793
 
static void local_worker_msg_get_done(struct local_dsync_worker *worker)
1794
 
{
1795
 
        const struct local_dsync_worker_msg_get *gets;
1796
 
        struct local_dsync_worker_msg_get get;
1797
 
        unsigned int count;
1798
 
 
1799
 
        worker->reading_mail = FALSE;
1800
 
 
1801
 
        gets = array_get(&worker->msg_get_queue, &count);
1802
 
        if (count == 0)
1803
 
                dsync_worker_try_finish(worker);
1804
 
        else {
1805
 
                get = gets[0];
1806
 
                array_delete(&worker->msg_get_queue, 0, 1);
1807
 
                local_worker_msg_get_next(worker, &get);
1808
 
        }
1809
 
}
1810
 
 
1811
 
static void local_worker_msg_box_close(struct local_dsync_worker *worker)
1812
 
{
1813
 
        struct mailbox_transaction_context *trans;
1814
 
        struct mailbox *box;
1815
 
 
1816
 
        if (worker->get_mail == NULL)
1817
 
                return;
1818
 
 
1819
 
        box = worker->get_mail->box;
1820
 
        trans = worker->get_mail->transaction;
1821
 
 
1822
 
        mail_free(&worker->get_mail);
1823
 
        (void)mailbox_transaction_commit(&trans);
1824
 
        mailbox_free(&box);
1825
 
        memset(&worker->get_mailbox, 0, sizeof(worker->get_mailbox));
1826
 
}
1827
 
 
1828
 
static void
1829
 
local_worker_msg_get_next(struct local_dsync_worker *worker,
1830
 
                          const struct local_dsync_worker_msg_get *get)
1831
 
{
1832
 
        struct dsync_msg_static_data data;
1833
 
        struct mailbox_transaction_context *trans;
1834
 
        struct mailbox *box;
1835
 
 
1836
 
        i_assert(!worker->reading_mail);
1837
 
 
1838
 
        if (!dsync_guid_equals(&worker->get_mailbox, &get->mailbox)) {
1839
 
                local_worker_msg_box_close(worker);
1840
 
                if (local_mailbox_open(worker, &get->mailbox, &box) <= 0) {
1841
 
                        get->callback(DSYNC_MSG_GET_RESULT_FAILED,
1842
 
                                      NULL, get->context);
1843
 
                        return;
1844
 
                }
1845
 
                worker->get_mailbox = get->mailbox;
1846
 
 
1847
 
                trans = mailbox_transaction_begin(box, 0);
1848
 
                worker->get_mail = mail_alloc(trans, 0, NULL);
1849
 
        }
1850
 
 
1851
 
        if (!mail_set_uid(worker->get_mail, get->uid)) {
1852
 
                get->callback(DSYNC_MSG_GET_RESULT_EXPUNGED,
1853
 
                              NULL, get->context);
1854
 
                return;
1855
 
        }
1856
 
 
1857
 
        memset(&data, 0, sizeof(data));
1858
 
        if (mail_get_special(worker->get_mail, MAIL_FETCH_UIDL_BACKEND,
1859
 
                             &data.pop3_uidl) < 0 ||
1860
 
            mail_get_received_date(worker->get_mail, &data.received_date) < 0 ||
1861
 
            mail_get_stream(worker->get_mail, NULL, NULL, &data.input) < 0) {
1862
 
                get->callback(worker->get_mail->expunged ?
1863
 
                              DSYNC_MSG_GET_RESULT_EXPUNGED :
1864
 
                              DSYNC_MSG_GET_RESULT_FAILED, NULL, get->context);
1865
 
        } else {
1866
 
                worker->reading_mail = TRUE;
1867
 
                data.pop3_uidl = t_strdup(data.pop3_uidl);
1868
 
                data.input = i_stream_create_limit(data.input, (uoff_t)-1);
1869
 
                i_stream_set_destroy_callback(data.input,
1870
 
                                              local_worker_msg_get_done,
1871
 
                                              worker);
1872
 
                get->callback(DSYNC_MSG_GET_RESULT_SUCCESS,
1873
 
                              &data, get->context);
1874
 
        }
1875
 
}
1876
 
 
1877
 
static void
1878
 
local_worker_msg_get(struct dsync_worker *_worker,
1879
 
                     const mailbox_guid_t *mailbox, uint32_t uid,
1880
 
                     dsync_worker_msg_callback_t *callback, void *context)
1881
 
{
1882
 
        struct local_dsync_worker *worker =
1883
 
                (struct local_dsync_worker *)_worker;
1884
 
        struct local_dsync_worker_msg_get get;
1885
 
 
1886
 
        memset(&get, 0, sizeof(get));
1887
 
        get.mailbox = *mailbox;
1888
 
        get.uid = uid;
1889
 
        get.callback = callback;
1890
 
        get.context = context;
1891
 
 
1892
 
        if (!worker->reading_mail)
1893
 
                local_worker_msg_get_next(worker, &get);
1894
 
        else
1895
 
                array_append(&worker->msg_get_queue, &get, 1);
1896
 
}
1897
 
 
1898
 
static void
1899
 
local_worker_finish(struct dsync_worker *_worker,
1900
 
                    dsync_worker_finish_callback_t *callback, void *context)
1901
 
{
1902
 
        struct local_dsync_worker *worker =
1903
 
                (struct local_dsync_worker *)_worker;
1904
 
 
1905
 
        i_assert(!worker->finishing);
1906
 
 
1907
 
        worker->finishing = TRUE;
1908
 
        worker->finished = FALSE;
1909
 
        worker->finish_callback = callback;
1910
 
        worker->finish_context = context;
1911
 
 
1912
 
        dsync_worker_try_finish(worker);
1913
 
}
1914
 
 
1915
 
struct dsync_worker_vfuncs local_dsync_worker = {
1916
 
        local_worker_deinit,
1917
 
 
1918
 
        local_worker_is_output_full,
1919
 
        local_worker_output_flush,
1920
 
 
1921
 
        local_worker_mailbox_iter_init,
1922
 
        local_worker_mailbox_iter_next,
1923
 
        local_worker_mailbox_iter_deinit,
1924
 
 
1925
 
        local_worker_subs_iter_init,
1926
 
        local_worker_subs_iter_next,
1927
 
        local_worker_subs_iter_next_un,
1928
 
        local_worker_subs_iter_deinit,
1929
 
        local_worker_set_subscribed,
1930
 
 
1931
 
        local_worker_msg_iter_init,
1932
 
        local_worker_msg_iter_next,
1933
 
        local_worker_msg_iter_deinit,
1934
 
 
1935
 
        local_worker_create_mailbox,
1936
 
        local_worker_delete_mailbox,
1937
 
        local_worker_delete_dir,
1938
 
        local_worker_rename_mailbox,
1939
 
        local_worker_update_mailbox,
1940
 
 
1941
 
        local_worker_select_mailbox,
1942
 
        local_worker_msg_update_metadata,
1943
 
        local_worker_msg_update_uid,
1944
 
        local_worker_msg_expunge,
1945
 
        local_worker_msg_copy,
1946
 
        local_worker_msg_save,
1947
 
        local_worker_msg_save_cancel,
1948
 
        local_worker_msg_get,
1949
 
        local_worker_finish
1950
 
};