1
/* Copyright (c) 2009-2011 Dovecot authors, see the included COPYING file */
7
#include "hex-binary.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"
20
struct local_dsync_worker_mailbox_iter {
21
struct dsync_worker_mailbox_iter iter;
23
struct mailbox_list_iterate_context *list_iter;
24
struct hash_iterate_context *deleted_iter;
25
struct hash_iterate_context *deleted_dir_iter;
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;
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;
39
struct mail_search_context *search_ctx;
42
string_t *tmp_guid_str;
43
ARRAY_TYPE(mailbox_expunge_rec) expunges;
44
unsigned int expunge_idx;
45
unsigned int expunges_set:1;
48
struct local_dsync_mailbox {
49
struct mail_namespace *ns;
51
const char *storage_name;
55
struct local_dsync_mailbox_change {
59
unsigned int deleted_mailbox:1;
62
struct local_dsync_dir_change {
63
mailbox_guid_t name_sha1;
64
struct mailbox_list *list;
68
time_t last_subs_change;
70
unsigned int unsubscribed:1;
71
unsigned int deleted_dir:1;
74
struct local_dsync_worker_msg_get {
75
mailbox_guid_t mailbox;
77
dsync_worker_msg_callback_t *callback;
81
struct local_dsync_worker {
82
struct dsync_worker worker;
83
struct mail_user *user;
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;
94
ARRAY_DEFINE(subs_namespaces, struct mail_namespace *);
96
mailbox_guid_t selected_box_guid;
97
struct mailbox *selected_box;
98
struct mail *mail, *ext_mail;
100
ARRAY_TYPE(uint32_t) saved_uids;
102
mailbox_guid_t get_mailbox;
103
struct mail *get_mail;
104
ARRAY_DEFINE(msg_get_queue, struct local_dsync_worker_msg_get);
107
struct mail_save_context *save_ctx;
108
struct istream *save_input;
109
dsync_worker_save_callback_t *save_callback;
112
dsync_worker_finish_callback_t *finish_callback;
113
void *finish_context;
115
unsigned int reading_mail:1;
116
unsigned int finishing:1;
117
unsigned int finished:1;
120
extern struct dsync_worker_vfuncs local_dsync_worker;
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);
125
local_worker_msg_get_next(struct local_dsync_worker *worker,
126
const struct local_dsync_worker_msg_get *get);
128
static int mailbox_guid_cmp(const void *p1, const void *p2)
130
const mailbox_guid_t *g1 = p1, *g2 = p2;
132
return memcmp(g1->guid, g2->guid, sizeof(g1->guid));
135
static unsigned int mailbox_guid_hash(const void *p)
137
const mailbox_guid_t *guid = p;
138
const uint8_t *s = guid->guid;
139
unsigned int i, g, h = 0;
141
for (i = 0; i < sizeof(guid->guid); i++) {
143
if ((g = h & 0xf0000000UL)) {
151
static struct mail_namespace *
152
namespace_find_set(struct mail_user *user,
153
const struct mail_namespace_settings *set)
155
struct mail_namespace *ns;
157
for (ns = user->namespaces; ns != NULL; ns = ns->next) {
158
/* compare settings pointers so that it'll work
159
for shared namespaces */
166
static void dsync_drop_extra_namespaces(struct local_dsync_worker *worker)
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;
173
if (!array_is_created(&user->unexpanded_set->namespaces))
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)
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
191
mail_namespace_ref(ns);
192
array_append(&worker->subs_namespaces, &ns, 1);
194
mail_namespace_destroy(ns);
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.");
203
struct dsync_worker *
204
dsync_worker_init_local(struct mail_user *user, char alt_char)
206
struct local_dsync_worker *worker;
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;
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;
225
static void local_worker_deinit(struct dsync_worker *_worker)
227
struct local_dsync_worker *worker =
228
(struct local_dsync_worker *)_worker;
229
struct mail_namespace **nsp;
231
i_assert(worker->save_input == NULL);
233
array_foreach_modifiable(&worker->subs_namespaces, nsp)
234
mail_namespace_unref(nsp);
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);
248
static bool local_worker_is_output_full(struct dsync_worker *worker ATTR_UNUSED)
253
static int local_worker_output_flush(struct dsync_worker *worker ATTR_UNUSED)
259
dsync_worker_save_mailbox_change(struct local_dsync_worker *worker,
260
const struct mailbox_log_record *rec)
262
struct local_dsync_mailbox_change *change;
265
change = hash_table_lookup(worker->mailbox_changes_hash,
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);
275
stamp = mailbox_log_record_get_timestamp(rec);
277
case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
278
change->deleted_mailbox = TRUE;
279
if (change->last_delete < stamp)
280
change->last_delete = stamp;
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:
291
dsync_worker_save_dir_change(struct local_dsync_worker *worker,
292
struct mailbox_list *list,
293
const struct mailbox_log_record *rec)
295
struct local_dsync_dir_change *change, new_change;
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));
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);
312
case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
314
case MAILBOX_LOG_RECORD_DELETE_DIR:
315
change->deleted_dir = TRUE;
316
if (change->last_delete < stamp)
317
change->last_delete = stamp;
319
case MAILBOX_LOG_RECORD_RENAME:
320
if (change->last_rename < stamp)
321
change->last_rename = stamp;
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 */
329
change->last_subs_change = stamp;
330
change->unsubscribed =
331
rec->type == MAILBOX_LOG_RECORD_UNSUBSCRIBE;
338
dsync_worker_get_list_mailbox_log(struct local_dsync_worker *worker,
339
struct mailbox_list *list)
341
struct mailbox_log *log;
342
struct mailbox_log_iter *iter;
343
const struct mailbox_log_record *rec;
345
log = mailbox_list_get_changelog(list);
346
iter = mailbox_log_iter_init(log);
347
while ((rec = mailbox_log_iter_next(iter)) != NULL) {
349
case MAILBOX_LOG_RECORD_DELETE_MAILBOX:
350
dsync_worker_save_mailbox_change(worker, rec);
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);
360
return mailbox_log_iter_deinit(&iter);
363
static unsigned int mailbox_log_record_hash(const void *p)
365
const uint8_t *guid = p;
367
return ((unsigned int)guid[0] << 24) |
368
((unsigned int)guid[1] << 16) |
369
((unsigned int)guid[2] << 8) |
370
(unsigned int)guid[3];
373
static int mailbox_log_record_cmp(const void *p1, const void *p2)
375
return memcmp(p1, p2, MAIL_GUID_128_SIZE);
378
static unsigned int dir_change_hash(const void *p)
380
const struct local_dsync_dir_change *change = p;
382
return mailbox_log_record_hash(change->name_sha1.guid) ^
383
POINTER_CAST_TO(change->list, unsigned int);
386
static int dir_change_cmp(const void *p1, const void *p2)
388
const struct local_dsync_dir_change *c1 = p1, *c2 = p2;
390
if (c1->list != c2->list)
393
return memcmp(c1->name_sha1.guid, c2->name_sha1.guid,
397
static int dsync_worker_get_mailbox_log(struct local_dsync_worker *worker)
399
struct mail_namespace *ns;
402
if (worker->mailbox_changes_hash != NULL)
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)
416
if (dsync_worker_get_list_mailbox_log(worker, ns->list) < 0)
422
static struct dsync_worker_mailbox_iter *
423
local_worker_mailbox_iter_init(struct dsync_worker *_worker)
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 };
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);
437
mailbox_list_iter_init_namespaces(worker->user->namespaces,
438
patterns, NAMESPACE_PRIVATE,
440
(void)dsync_worker_get_mailbox_log(worker);
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)
450
struct local_dsync_mailbox *lbox;
452
lbox = p_new(worker->pool, struct local_dsync_mailbox, 1);
454
memcpy(lbox->guid.guid, guid->guid, sizeof(lbox->guid.guid));
455
lbox->storage_name = p_strdup(worker->pool, storage_name);
457
hash_table_insert(worker->mailbox_hash, &lbox->guid, lbox);
461
iter_next_deleted(struct local_dsync_worker_mailbox_iter *iter,
462
struct local_dsync_worker *worker,
463
struct dsync_mailbox *dsync_box_r)
467
if (iter->deleted_iter == NULL) {
469
hash_table_iterate_init(worker->mailbox_changes_hash);
471
while (hash_table_iterate(iter->deleted_iter, &key, &value)) {
472
const struct local_dsync_mailbox_change *change = value;
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;
485
if (iter->deleted_dir_iter == NULL) {
486
iter->deleted_dir_iter =
487
hash_table_iterate_init(worker->dir_changes_hash);
489
while (hash_table_iterate(iter->deleted_dir_iter, &key, &value)) {
490
const struct local_dsync_dir_change *change = value;
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;
502
hash_table_iterate_deinit(&iter->deleted_iter);
507
local_worker_mailbox_iter_next(struct dsync_worker_mailbox_iter *_iter,
508
struct dsync_mailbox *dsync_box_r)
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;
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;
528
memset(dsync_box_r, 0, sizeof(*dsync_box_r));
530
info = mailbox_list_iter_next(iter->list_iter);
532
return iter_next_deleted(iter, worker, dsync_box_r);
534
dsync_box_r->name = info->name;
535
dsync_box_r->name_sep = info->ns->sep;
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);
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,
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;
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);
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);
563
i_error("Failed to sync mailbox %s: %s", info->name,
564
mail_storage_get_last_error(storage, &error));
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,
575
_iter->failed = TRUE;
579
mailbox_get_status(box, STATUS_UIDNEXT | STATUS_UIDVALIDITY |
580
STATUS_HIGHESTMODSEQ | STATUS_CACHE_FIELDS |
581
STATUS_FIRST_RECENT_UID, &status);
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;
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;
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]);
603
array_append(&dsync_box_r->cache_fields, &field_name, 1);
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);
614
_iter->failed = TRUE;
617
local_dsync_worker_add_mailbox(worker, info->ns, storage_name,
618
&dsync_box_r->mailbox_guid);
624
local_worker_mailbox_iter_deinit(struct dsync_worker_mailbox_iter *_iter)
626
struct local_dsync_worker_mailbox_iter *iter =
627
(struct local_dsync_worker_mailbox_iter *)_iter;
628
int ret = _iter->failed ? -1 : 0;
630
if (mailbox_list_iter_deinit(&iter->list_iter) < 0)
632
pool_unref(&iter->ret_pool);
637
static struct dsync_worker_subs_iter *
638
local_worker_subs_iter_init(struct dsync_worker *_worker)
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 };
648
iter = i_new(struct local_dsync_worker_subs_iter, 1);
649
iter->iter.worker = _worker;
651
mailbox_list_iter_init_namespaces(worker->user->namespaces,
652
patterns, NAMESPACE_PRIVATE,
654
(void)dsync_worker_get_mailbox_log(worker);
658
static struct mail_namespace *
659
find_subscription_ns(struct local_dsync_worker *worker, const char *vname)
661
struct mail_namespace *const *nsp;
663
array_foreach(&worker->subs_namespaces, nsp) {
664
if (strncmp((*nsp)->prefix, vname, (*nsp)->prefix_len) == 0)
671
local_worker_subs_iter_next(struct dsync_worker_subs_iter *_iter,
672
struct dsync_worker_subscription *rec_r)
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;
683
memset(rec_r, 0, sizeof(*rec_r));
685
info = mailbox_list_iter_next(iter->list_iter);
689
subs_ns = find_subscription_ns(worker, info->name);
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);
696
dsync_str_sha_to_guid(storage_name, &change_lookup.name_sha1);
697
change_lookup.list = info->ns->list;
699
change = hash_table_lookup(worker->dir_changes_hash,
701
if (change != NULL) {
702
/* it shouldn't be marked as unsubscribed, but drop it to
704
change->unsubscribed = FALSE;
705
rec_r->last_change = change->last_subs_change;
707
rec_r->ns_prefix = info->ns->prefix;
708
rec_r->vname = info->name;
709
rec_r->storage_name = storage_name;
714
local_worker_subs_iter_next_un(struct dsync_worker_subs_iter *_iter,
715
struct dsync_worker_unsubscription *rec_r)
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;
723
if (iter->deleted_iter == NULL) {
725
hash_table_iterate_init(worker->dir_changes_hash);
727
while (hash_table_iterate(iter->deleted_iter, &key, &value)) {
728
const struct local_dsync_dir_change *change = value;
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;
741
hash_table_iterate_deinit(&iter->deleted_iter);
746
local_worker_subs_iter_deinit(struct dsync_worker_subs_iter *_iter)
748
struct local_dsync_worker_subs_iter *iter =
749
(struct local_dsync_worker_subs_iter *)_iter;
750
int ret = _iter->failed ? -1 : 0;
752
if (mailbox_list_iter_deinit(&iter->list_iter) < 0)
759
local_worker_set_subscribed(struct dsync_worker *_worker,
760
const char *name, time_t last_change, bool set)
762
struct local_dsync_worker *worker =
763
(struct local_dsync_worker *)_worker;
764
struct mail_namespace *ns, *subs_ns;
765
const char *storage_name;
768
ns = mail_namespace_find(worker->user->namespaces,
771
i_error("Can't find namespace for mailbox %s", name);
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);
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));
793
mailbox_list_set_changelog_timestamp(ns->list, (time_t)-1);
796
static int local_mailbox_open(struct local_dsync_worker *worker,
797
const mailbox_guid_t *guid,
798
struct mailbox **box_r)
800
enum mailbox_flags flags = MAILBOX_FLAG_KEEP_RECENT;
801
struct local_dsync_mailbox *lbox;
803
uint8_t mailbox_guid[MAIL_GUID_128_SIZE];
805
lbox = hash_table_lookup(worker->mailbox_hash, guid);
807
i_error("Trying to open a non-listed mailbox with guid=%s",
808
dsync_guid_to_str(guid));
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);
821
i_error("Failed to sync mailbox %s: %s", lbox->storage_name,
822
mail_storage_get_last_error(storage, NULL));
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));
838
static int iter_local_mailbox_open(struct local_dsync_worker_msg_iter *iter)
840
struct local_dsync_worker *worker =
841
(struct local_dsync_worker *)iter->iter.worker;
842
mailbox_guid_t *guid;
844
struct mailbox_transaction_context *trans;
845
struct mail_search_args *search_args;
849
if (iter->mailbox_idx == iter->mailbox_count) {
850
/* no more mailboxes */
854
guid = &iter->mailboxes[iter->mailbox_idx];
855
ret = local_mailbox_open(worker, guid, &box);
858
/* mailbox was deleted. try next one. */
862
i_error("msg iteration failed: Couldn't open mailbox %s",
863
dsync_guid_to_str(guid));
864
iter->iter.failed = TRUE;
868
search_args = mail_search_build_init();
869
mail_search_build_add_all(search_args);
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,
879
iter_local_mailbox_close(struct local_dsync_worker_msg_iter *iter)
881
struct mailbox *box = iter->mail->box;
882
struct mailbox_transaction_context *trans = iter->mail->transaction;
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);
890
i_error("msg search failed: %s",
891
mail_storage_get_last_error(storage, NULL));
892
iter->iter.failed = TRUE;
894
(void)mailbox_transaction_commit(&trans);
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)
903
struct local_dsync_worker_msg_iter *iter;
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));
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);
921
static int mailbox_expunge_rec_cmp(const struct mailbox_expunge_rec *e1,
922
const struct mailbox_expunge_rec *e2)
924
if (e1->uid < e2->uid)
926
else if (e1->uid > e2->uid)
933
iter_local_mailbox_next_expunge(struct local_dsync_worker_msg_iter *iter,
934
uint32_t prev_uid, struct dsync_message *msg_r)
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;
942
if (iter->expunges_set) {
943
expunges = array_get(&iter->expunges, &count);
944
if (iter->expunge_idx == count)
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,
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;
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;
966
mailbox_get_status(box, STATUS_UIDNEXT, &status);
967
if (prev_uid + 1 >= status.uidnext) {
968
/* no expunged messages at the end of mailbox */
973
ARRAY_TYPE(seq_range) uids_filter;
975
t_array_init(&uids_filter, 1);
976
seq_range_array_add_range(&uids_filter, prev_uid + 1,
978
(void)mailbox_get_expunges(box, 0, &uids_filter,
980
array_sort(&iter->expunges, mailbox_expunge_rec_cmp);
982
return iter_local_mailbox_next_expunge(iter, prev_uid, msg_r);
986
local_worker_msg_iter_next(struct dsync_worker_msg_iter *_iter,
987
unsigned int *mailbox_idx_r,
988
struct dsync_message *msg_r)
990
struct local_dsync_worker_msg_iter *iter =
991
(struct local_dsync_worker_msg_iter *)_iter;
995
if (_iter->failed || iter->search_ctx == NULL)
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;
1004
iter_local_mailbox_close(iter);
1005
iter->mailbox_idx++;
1006
if (iter_local_mailbox_open(iter) < 0)
1008
return local_worker_msg_iter_next(_iter, mailbox_idx_r, msg_r);
1010
*mailbox_idx_r = iter->mailbox_idx;
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);
1017
i_error("msg guid lookup failed: %s",
1018
mail_storage_get_last_error(storage, NULL));
1019
_iter->failed = TRUE;
1022
return local_worker_msg_iter_next(_iter, mailbox_idx_r, msg_r);
1025
memset(msg_r, 0, sizeof(*msg_r));
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;
1037
local_worker_msg_iter_deinit(struct dsync_worker_msg_iter *_iter)
1039
struct local_dsync_worker_msg_iter *iter =
1040
(struct local_dsync_worker_msg_iter *)_iter;
1041
int ret = _iter->failed ? -1 : 0;
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);
1053
local_worker_copy_mailbox_update(const struct dsync_mailbox *dsync_box,
1054
struct mailbox_update *update_r)
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;
1066
mailbox_name_convert(struct local_dsync_worker *worker,
1067
const char *name, char src_sep, char dest_sep)
1069
char *dest_name, *p;
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)
1082
mailbox_name_cleanup(const char *input, char real_sep, char alt_char)
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)
1095
static const char *mailbox_name_force_cleanup(const char *input, char alt_char)
1099
output = t_strdup_noconst(input);
1100
for (p = output; *p != '\0'; p++) {
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,
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);
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,
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,
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);
1135
if (!mailbox_list_is_valid_create_name(ns->list, name)) {
1136
/* name is too long? just give up and generate a
1138
uint8_t guid[MAIL_GUID_128_SIZE];
1140
mail_generate_guid_128(guid);
1141
name = mail_guid_128_to_string(guid);
1143
i_assert(mailbox_list_is_valid_create_name(ns->list, name));
1148
static struct mailbox *
1149
local_worker_mailbox_alloc(struct local_dsync_worker *worker,
1150
const struct dsync_mailbox *dsync_box, bool creating)
1152
struct mail_namespace *ns;
1153
struct local_dsync_mailbox *lbox;
1156
lbox = dsync_mailbox_is_noselect(dsync_box) ? NULL :
1157
hash_table_lookup(worker->mailbox_hash,
1158
&dsync_box->mailbox_guid);
1160
/* use the existing known mailbox name */
1161
return mailbox_alloc(lbox->ns->list, lbox->storage_name, 0);
1164
name = dsync_box->name;
1165
ns = mail_namespace_find(worker->user->namespaces, &name);
1167
i_error("Can't find namespace for mailbox %s", dsync_box->name);
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);
1177
return mailbox_alloc(ns->list, name, 0);
1180
static int local_worker_create_dir(struct mailbox *box,
1181
const struct dsync_mailbox *dsync_box)
1183
struct mailbox_list *list = mailbox_get_namespace(box)->list;
1185
enum mail_error error;
1187
if (mailbox_list_create_dir(list, mailbox_get_name(box)) == 0)
1190
errstr = mailbox_list_get_last_error(list, &error);
1192
case MAIL_ERROR_EXISTS:
1193
/* directory already exists - that's ok */
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,
1202
i_error("Can't create mailbox %s: %s", dsync_box->name, errstr);
1208
local_worker_create_allocated_mailbox(struct local_dsync_worker *worker,
1209
struct mailbox *box,
1210
const struct dsync_mailbox *dsync_box)
1212
struct mailbox_update update;
1214
enum mail_error error;
1216
local_worker_copy_mailbox_update(dsync_box, &update);
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);
1226
if (mailbox_create(box, &update, FALSE) < 0) {
1227
errstr = mail_storage_get_last_error(mailbox_get_storage(box),
1229
if (error == MAIL_ERROR_EXISTS) {
1230
/* mailbox already exists */
1234
dsync_worker_set_failure(&worker->worker);
1235
i_error("Can't create mailbox %s: %s", dsync_box->name, errstr);
1239
local_dsync_worker_add_mailbox(worker,
1240
mailbox_get_namespace(box),
1241
mailbox_get_name(box),
1242
&dsync_box->mailbox_guid);
1247
local_worker_create_mailbox(struct dsync_worker *_worker,
1248
const struct dsync_mailbox *dsync_box)
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;
1257
box = local_worker_mailbox_alloc(worker, dsync_box, TRUE);
1259
dsync_worker_set_failure(_worker);
1263
ret = local_worker_create_allocated_mailbox(worker, box, dsync_box);
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),
1274
ns = mailbox_get_namespace(box);
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);
1285
local_worker_delete_mailbox(struct dsync_worker *_worker,
1286
const struct dsync_mailbox *dsync_box)
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;
1294
lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
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);
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);
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);
1312
lbox->deleted = TRUE;
1315
mailbox_list_set_changelog_timestamp(lbox->ns->list, (time_t)-1);
1319
local_worker_delete_dir(struct dsync_worker *_worker,
1320
const struct dsync_mailbox *dsync_box)
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;
1328
storage_name = dsync_box->name;
1329
ns = mail_namespace_find(worker->user->namespaces, &storage_name);
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. */
1340
i_error("Can't delete mailbox directory %s: %s",
1342
mailbox_list_get_last_error(ns->list, NULL));
1345
mailbox_list_set_changelog_timestamp(ns->list, (time_t)-1);
1349
local_worker_rename_mailbox(struct dsync_worker *_worker,
1350
const mailbox_guid_t *mailbox,
1351
const struct dsync_mailbox *dsync_box)
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;
1360
lbox = hash_table_lookup(worker->mailbox_hash, mailbox);
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);
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. */
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);
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);
1387
lbox->storage_name = p_strdup(worker->pool, newname);
1389
mailbox_free(&old_box);
1390
mailbox_free(&new_box);
1391
mailbox_list_set_changelog_timestamp(list, (time_t)-1);
1395
has_expected_save_uids(struct local_dsync_worker *worker,
1396
const struct mail_transaction_commit_changes *changes)
1398
struct seq_range_iter iter;
1399
const uint32_t *expected_uids;
1401
unsigned int i, n, expected_count;
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++])
1409
return i == expected_count;
1412
static void local_worker_mailbox_close(struct local_dsync_worker *worker)
1414
struct mailbox_transaction_context *trans, *ext_trans;
1415
struct mail_transaction_commit_changes changes;
1417
i_assert(worker->save_input == NULL);
1419
memset(&worker->selected_box_guid, 0,
1420
sizeof(worker->selected_box_guid));
1422
if (worker->selected_box == NULL)
1425
trans = worker->mail->transaction;
1426
ext_trans = worker->ext_mail->transaction;
1427
mail_free(&worker->mail);
1428
mail_free(&worker->ext_mail);
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);
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);
1440
worker->worker.unexpected_changes = TRUE;
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));
1447
worker->worker.unexpected_changes = TRUE;
1449
pool_unref(&changes.pool);
1451
array_clear(&worker->saved_uids);
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);
1457
mailbox_free(&worker->selected_box);
1461
local_worker_update_mailbox(struct dsync_worker *_worker,
1462
const struct dsync_mailbox *dsync_box)
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;
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);
1476
local_worker_mailbox_close(worker);
1478
box = local_worker_mailbox_alloc(worker, dsync_box, FALSE);
1480
dsync_worker_set_failure(_worker);
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),
1494
dsync_worker_select_mailbox(_worker, dsync_box);
1498
local_worker_set_cache_fields(struct local_dsync_worker *worker,
1499
const ARRAY_TYPE(const_string) *cache_fields)
1501
struct mailbox_update update;
1502
const char *const *fields, **new_fields;
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);
1509
memset(&update, 0, sizeof(update));
1510
update.cache_fields = new_fields;
1511
mailbox_update(worker->selected_box, &update);
1515
local_worker_select_mailbox(struct dsync_worker *_worker,
1516
const mailbox_guid_t *mailbox,
1517
const ARRAY_TYPE(const_string) *cache_fields)
1519
struct local_dsync_worker *worker =
1520
(struct local_dsync_worker *)_worker;
1521
struct mailbox_transaction_context *trans, *ext_trans;
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);
1528
if (worker->selected_box != NULL)
1529
local_worker_mailbox_close(worker);
1530
worker->selected_box_guid = *mailbox;
1532
if (local_mailbox_open(worker, mailbox, &worker->selected_box) <= 0) {
1533
dsync_worker_set_failure(_worker);
1536
if (cache_fields != NULL && array_is_created(cache_fields))
1537
local_worker_set_cache_fields(worker, cache_fields);
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);
1548
local_worker_msg_update_metadata(struct dsync_worker *_worker,
1549
const struct dsync_message *msg)
1551
struct local_dsync_worker *worker =
1552
(struct local_dsync_worker *)_worker;
1553
struct mail_keywords *keywords;
1555
if (msg->modseq > 1) {
1556
(void)mailbox_enable(worker->mail->box,
1557
MAILBOX_FEATURE_CONDSTORE);
1560
if (!mail_set_uid(worker->mail, msg->uid))
1561
dsync_worker_set_failure(_worker);
1563
mail_update_flags(worker->mail, MODIFY_REPLACE, msg->flags);
1565
keywords = mailbox_keywords_create_valid(worker->mail->box,
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);
1574
local_worker_msg_update_uid(struct dsync_worker *_worker,
1575
uint32_t old_uid, uint32_t new_uid)
1577
struct local_dsync_worker *worker =
1578
(struct local_dsync_worker *)_worker;
1579
struct mail_save_context *save_ctx;
1581
if (!mail_set_uid(worker->ext_mail, old_uid)) {
1582
dsync_worker_set_failure(_worker);
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);
1593
static void local_worker_msg_expunge(struct dsync_worker *_worker, uint32_t uid)
1595
struct local_dsync_worker *worker =
1596
(struct local_dsync_worker *)_worker;
1598
if (mail_set_uid(worker->mail, uid))
1599
mail_expunge(worker->mail);
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)
1608
struct mail_keywords *keywords;
1610
i_assert(msg->uid != 0);
1612
if (msg->modseq > 1)
1613
(void)mailbox_enable(box, MAILBOX_FEATURE_CONDSTORE);
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);
1624
array_append(&worker->saved_uids, &msg->uid, 1);
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)
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;
1641
if (local_mailbox_open(worker, src_mailbox, &src_box) <= 0) {
1642
callback(FALSE, context);
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))
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);
1657
mail_free(&src_mail);
1658
(void)mailbox_transaction_commit(&src_trans);
1659
mailbox_free(&src_box);
1661
callback(ret == 0, context);
1664
static void dsync_worker_try_finish(struct local_dsync_worker *worker)
1666
if (worker->finish_callback == NULL)
1668
if (worker->save_io != NULL || worker->reading_mail)
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);
1679
local_worker_save_msg_continue(struct local_dsync_worker *worker)
1681
struct mailbox *dest_box = worker->ext_mail->box;
1682
dsync_worker_save_callback_t *callback;
1684
bool save_failed = FALSE;
1686
while ((ret = i_stream_read(worker->save_input)) > 0 || ret == -2) {
1687
if (mailbox_save_continue(worker->save_ctx) < 0) {
1694
if (worker->save_io != NULL)
1697
io_add(i_stream_get_fd(worker->save_input), IO_READ,
1698
local_worker_save_msg_continue, worker);
1701
i_assert(ret == -1);
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);
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);
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);
1727
callback = worker->save_callback;
1728
worker->save_callback = NULL;
1729
i_stream_unref(&worker->save_input);
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);
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,
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;
1750
i_assert(worker->save_input == NULL);
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,
1756
if (*data->pop3_uidl != '\0')
1757
mailbox_save_set_pop3_uidl(save_ctx, data->pop3_uidl);
1759
mailbox_save_set_received_date(save_ctx, data->received_date, 0);
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);
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);
1779
static void local_worker_msg_save_cancel(struct dsync_worker *_worker)
1781
struct local_dsync_worker *worker =
1782
(struct local_dsync_worker *)_worker;
1784
if (worker->save_input == NULL)
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);
1793
static void local_worker_msg_get_done(struct local_dsync_worker *worker)
1795
const struct local_dsync_worker_msg_get *gets;
1796
struct local_dsync_worker_msg_get get;
1799
worker->reading_mail = FALSE;
1801
gets = array_get(&worker->msg_get_queue, &count);
1803
dsync_worker_try_finish(worker);
1806
array_delete(&worker->msg_get_queue, 0, 1);
1807
local_worker_msg_get_next(worker, &get);
1811
static void local_worker_msg_box_close(struct local_dsync_worker *worker)
1813
struct mailbox_transaction_context *trans;
1814
struct mailbox *box;
1816
if (worker->get_mail == NULL)
1819
box = worker->get_mail->box;
1820
trans = worker->get_mail->transaction;
1822
mail_free(&worker->get_mail);
1823
(void)mailbox_transaction_commit(&trans);
1825
memset(&worker->get_mailbox, 0, sizeof(worker->get_mailbox));
1829
local_worker_msg_get_next(struct local_dsync_worker *worker,
1830
const struct local_dsync_worker_msg_get *get)
1832
struct dsync_msg_static_data data;
1833
struct mailbox_transaction_context *trans;
1834
struct mailbox *box;
1836
i_assert(!worker->reading_mail);
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);
1845
worker->get_mailbox = get->mailbox;
1847
trans = mailbox_transaction_begin(box, 0);
1848
worker->get_mail = mail_alloc(trans, 0, NULL);
1851
if (!mail_set_uid(worker->get_mail, get->uid)) {
1852
get->callback(DSYNC_MSG_GET_RESULT_EXPUNGED,
1853
NULL, get->context);
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);
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,
1872
get->callback(DSYNC_MSG_GET_RESULT_SUCCESS,
1873
&data, get->context);
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)
1882
struct local_dsync_worker *worker =
1883
(struct local_dsync_worker *)_worker;
1884
struct local_dsync_worker_msg_get get;
1886
memset(&get, 0, sizeof(get));
1887
get.mailbox = *mailbox;
1889
get.callback = callback;
1890
get.context = context;
1892
if (!worker->reading_mail)
1893
local_worker_msg_get_next(worker, &get);
1895
array_append(&worker->msg_get_queue, &get, 1);
1899
local_worker_finish(struct dsync_worker *_worker,
1900
dsync_worker_finish_callback_t *callback, void *context)
1902
struct local_dsync_worker *worker =
1903
(struct local_dsync_worker *)_worker;
1905
i_assert(!worker->finishing);
1907
worker->finishing = TRUE;
1908
worker->finished = FALSE;
1909
worker->finish_callback = callback;
1910
worker->finish_context = context;
1912
dsync_worker_try_finish(worker);
1915
struct dsync_worker_vfuncs local_dsync_worker = {
1916
local_worker_deinit,
1918
local_worker_is_output_full,
1919
local_worker_output_flush,
1921
local_worker_mailbox_iter_init,
1922
local_worker_mailbox_iter_next,
1923
local_worker_mailbox_iter_deinit,
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,
1931
local_worker_msg_iter_init,
1932
local_worker_msg_iter_next,
1933
local_worker_msg_iter_deinit,
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,
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,