33
33
#include "file-dotlock.h"
34
34
#include "close-keep-errno.h"
35
35
#include "nfs-workarounds.h"
36
#include "eacces-error.h"
36
37
#include "maildir-storage.h"
37
38
#include "maildir-sync.h"
38
39
#include "maildir-filename.h"
124
127
struct maildir_uidlist_rec **rec_r);
126
129
static int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
127
bool nonblock, bool refresh)
130
bool nonblock, bool refresh,
131
bool refresh_when_locked)
129
133
struct mailbox *box = &uidlist->ibox->box;
130
134
const char *control_dir, *path;
136
140
if (uidlist->lock_count > 0) {
141
if (!uidlist->locked_refresh && refresh_when_locked) {
142
if (maildir_uidlist_refresh(uidlist) < 0)
137
145
uidlist->lock_count++;
149
index_storage_lock_notify_reset(uidlist->ibox);
141
151
control_dir = mailbox_list_get_path(box->storage->list, box->name,
142
152
MAILBOX_LIST_PATH_TYPE_CONTROL);
143
153
path = t_strconcat(control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
159
169
if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT ||
160
170
uidlist->mbox == NULL) {
161
mail_storage_set_critical(box->storage,
162
"file_dotlock_create(%s) failed: %m", path);
171
if (errno == EACCES) {
172
mail_storage_set_critical(box->storage, "%s",
173
eacces_error_get_creating("file_dotlock_create", path));
175
mail_storage_set_critical(box->storage,
176
"file_dotlock_create(%s) failed: %m",
165
181
/* the control dir doesn't exist. create it unless the whole
166
182
mailbox was just deleted. */
167
if (maildir_set_deleted(uidlist->mbox))
183
if (!maildir_set_deleted(uidlist->mbox))
171
187
uidlist->lock_count++;
188
uidlist->locked_refresh = FALSE;
174
191
/* make sure we have the latest changes before
184
201
int maildir_uidlist_lock(struct maildir_uidlist *uidlist)
186
return maildir_uidlist_lock_timeout(uidlist, FALSE, TRUE);
203
return maildir_uidlist_lock_timeout(uidlist, FALSE, TRUE, FALSE);
189
206
int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
191
return maildir_uidlist_lock_timeout(uidlist, TRUE, TRUE);
208
return maildir_uidlist_lock_timeout(uidlist, TRUE, TRUE, FALSE);
194
211
int maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist)
210
227
if (--uidlist->lock_count > 0)
230
uidlist->locked_refresh = FALSE;
213
231
(void)file_dotlock_delete(&uidlist->dotlock);
234
static bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
236
struct index_mailbox *ibox = context;
238
index_storage_lock_notify(ibox, stale ?
239
MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE :
240
MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
216
245
struct maildir_uidlist *
217
246
maildir_uidlist_init_readonly(struct index_mailbox *ibox)
228
257
uidlist->ibox = ibox;
229
258
uidlist->path = i_strconcat(control_dir, "/"MAILDIR_UIDLIST_NAME, NULL);
230
259
i_array_init(&uidlist->records, 128);
231
uidlist->files = hash_create(default_pool, default_pool, 4096,
232
maildir_filename_base_hash,
233
maildir_filename_base_cmp);
260
uidlist->files = hash_table_create(default_pool, default_pool, 4096,
261
maildir_filename_base_hash,
262
maildir_filename_base_cmp);
234
263
uidlist->next_uid = 1;
235
264
uidlist->hdr_extensions = str_new(default_pool, 128);
261
292
static void maildir_uidlist_close(struct maildir_uidlist *uidlist)
294
struct mail_storage *storage = uidlist->ibox->box.storage;
263
296
if (uidlist->fd != -1) {
264
if (close(uidlist->fd) < 0)
265
i_error("close(%s) failed: %m", uidlist->path);
297
if (close(uidlist->fd) < 0) {
298
mail_storage_set_critical(storage,
299
"close(%s) failed: %m", uidlist->path);
266
301
uidlist->fd = -1;
267
302
uidlist->fd_ino = 0;
270
305
uidlist->read_line_count = 0;
308
static void maildir_uidlist_reset(struct maildir_uidlist *uidlist)
310
maildir_uidlist_close(uidlist);
311
uidlist->last_seen_uid = 0;
312
uidlist->initial_hdr_read = FALSE;
314
hash_table_clear(uidlist->files, FALSE);
315
array_clear(&uidlist->records);
273
318
void maildir_uidlist_deinit(struct maildir_uidlist **_uidlist)
275
320
struct maildir_uidlist *uidlist = *_uidlist;
420
466
uid, uidlist->prev_read_uid);
469
if (uid >= (uint32_t)-1) {
470
maildir_uidlist_set_corrupted(uidlist,
471
"UID too high (%u)", uid);
423
474
uidlist->prev_read_uid = uid;
425
476
if (uid <= uidlist->last_seen_uid) {
466
old_rec = hash_lookup(uidlist->files, line);
467
if (old_rec != NULL) {
517
old_rec = hash_table_lookup(uidlist->files, line);
518
if (old_rec == NULL) {
520
} else if (old_rec->uid == uid) {
521
/* most likely this is a record we saved ourself, but couldn't
522
update last_seen_uid because uidlist wasn't refreshed while
525
another possibility is a duplicate file record. currently
526
it would be a bug, but not that big of a deal. also perhaps
527
in future such duplicate lines could be used to update
528
extended fields. so just let it through anyway.
530
we'll waste a bit of memory here by allocating the record
531
twice, but that's not really a problem. */
532
rec->filename = old_rec->filename;
533
hash_table_insert(uidlist->files, rec->filename, rec);
534
uidlist->unsorted = TRUE;
468
537
/* This can happen if expunged file is moved back and the file
469
538
was appended to uidlist. */
470
539
i_warning("%s: Duplicate file entry at line %u: "
479
548
uidlist->recreate = TRUE;
551
recs = array_get(&uidlist->records, &count);
552
if (count > 0 && recs[count-1]->uid > uid) {
553
/* we most likely have some records in the array that we saved
554
ourself without refreshing uidlist */
555
uidlist->unsorted = TRUE;
482
558
rec->filename = p_strdup(uidlist->record_pool, line);
483
hash_insert(uidlist->files, rec->filename, rec);
559
hash_table_insert(uidlist->files, rec->filename, rec);
484
560
array_append(&uidlist->records, &rec, 1);
516
592
"Corrupted header (version 1)");
519
if (uid_validity == uidlist->uid_validity &&
520
next_uid < uidlist->next_uid) {
521
maildir_uidlist_set_corrupted(uidlist,
522
"next_uid was lowered (v1, %u -> %u)",
523
uidlist->next_uid, next_uid);
527
596
case UIDLIST_VERSION:
528
597
ext_hdr = uidlist->hdr_extensions;
637
if (uid_validity == uidlist->uid_validity &&
638
next_uid < uidlist->hdr_next_uid) {
639
maildir_uidlist_set_corrupted(uidlist,
640
"next_uid header was lowered (%u -> %u)",
641
uidlist->hdr_next_uid, next_uid);
568
645
uidlist->uid_validity = uid_validity;
569
646
uidlist->next_uid = next_uid;
647
uidlist->hdr_next_uid = next_uid;
651
static void maildir_uidlist_records_sort_by_uid(struct maildir_uidlist *uidlist)
653
struct maildir_uidlist_rec **recs;
656
recs = array_get_modifiable(&uidlist->records, &count);
657
qsort(recs, count, sizeof(*recs), maildir_uid_cmp);
659
uidlist->unsorted = FALSE;
574
663
maildir_uidlist_update_read(struct maildir_uidlist *uidlist,
575
664
bool *retry_r, bool try_retry)
577
666
struct mail_storage *storage = uidlist->ibox->box.storage;
578
667
const char *line;
579
unsigned int orig_next_uid;
668
uint32_t orig_next_uid, orig_uid_validity;
580
669
struct istream *input;
582
671
uoff_t last_read_offset;
635
724
input = i_stream_create_fd(fd, 4096, FALSE);
636
725
i_stream_seek(input, last_read_offset);
727
orig_uid_validity = uidlist->uid_validity;
638
728
orig_next_uid = uidlist->next_uid;
639
729
ret = input->v_offset != 0 ? 1 :
640
730
maildir_uidlist_read_header(uidlist, input);
662
752
if (input->stream_errno != 0)
755
if (uidlist->unsorted) {
756
uidlist->recreate = TRUE;
757
maildir_uidlist_records_sort_by_uid(uidlist);
665
759
if (uidlist->next_uid <= uidlist->prev_read_uid)
666
760
uidlist->next_uid = uidlist->prev_read_uid + 1;
667
if (uidlist->next_uid < orig_next_uid) {
761
if (ret > 0 && uidlist->uid_validity != orig_uid_validity &&
762
orig_uid_validity != 0) {
763
uidlist->recreate = TRUE;
764
} else if (ret > 0 && uidlist->next_uid < orig_next_uid) {
668
765
mail_storage_set_critical(storage,
669
"%s: next_uid was lowered (%u -> %u)",
766
"%s: next_uid was lowered (%u -> %u, hdr=%u)",
670
767
uidlist->path, orig_next_uid,
768
uidlist->next_uid, uidlist->hdr_next_uid);
672
769
uidlist->recreate = TRUE;
673
770
uidlist->next_uid = orig_next_uid;
694
791
mail_storage_set_critical(storage,
695
792
"read(%s) failed: %m", uidlist->path);
794
uidlist->last_read_offset = 0;
699
797
i_stream_destroy(&input);
702
i_error("close(%s) failed: %m", uidlist->path);
800
mail_storage_set_critical(storage,
801
"close(%s) failed: %m", uidlist->path);
800
905
int maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist)
802
907
const struct maildir_index_header *mhdr = &uidlist->mbox->maildir_hdr;
908
struct mail_index *index = uidlist->mbox->ibox.index;
909
struct mail_index_view *view;
803
910
const struct mail_index_header *hdr;
914
i_assert(UIDLIST_IS_LOCKED(uidlist));
807
916
if (uidlist->fd != -1)
808
917
return maildir_uidlist_refresh(uidlist);
810
919
if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0)
813
if ((uoff_t)st.st_size == mhdr->uidlist_size &&
922
if (ret > 0 && st.st_size == mhdr->uidlist_size &&
814
923
st.st_mtime == (time_t)mhdr->uidlist_mtime &&
815
ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), mhdr->uidlist_mtime_nsecs)) {
816
/* index is up-to-date */
817
hdr = mail_index_get_header(uidlist->mbox->ibox.view);
924
ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), mhdr->uidlist_mtime_nsecs) &&
925
(!mail_index_is_in_memory(index) || st.st_mtime < ioloop_time-1)) {
926
/* index is up-to-date. look up the uidvalidity and next-uid
927
from it. we'll need to create a new view temporarily to
928
make sure we get the latest values. */
929
view = mail_index_view_open(index);
930
hdr = mail_index_get_header(view);
818
931
uidlist->uid_validity = hdr->uid_validity;
819
932
uidlist->next_uid = hdr->next_uid;
820
933
uidlist->initial_hdr_read = TRUE;
934
mail_index_view_close(&view);
823
937
return maildir_uidlist_refresh(uidlist);
827
static struct maildir_uidlist_rec *
828
942
maildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
944
struct maildir_uidlist_rec **rec_r)
831
946
struct maildir_uidlist_rec *const *recs;
832
947
unsigned int idx, left_idx, right_idx;
855
971
if (idx > 0) idx--;
861
maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
862
enum maildir_uidlist_rec_flag *flags_r)
976
int maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
977
enum maildir_uidlist_rec_flag *flags_r,
978
const char **fname_r)
866
fname = maildir_uidlist_lookup_nosync(uidlist, uid, flags_r);
982
ret = maildir_uidlist_lookup_nosync(uidlist, uid, flags_r, fname_r);
868
986
if (uidlist->fd != -1 || uidlist->mbox == NULL) {
869
987
/* refresh uidlist and check again in case it was added
870
988
after the last mailbox sync */
871
989
if (maildir_uidlist_refresh(uidlist) < 0)
874
992
/* the uidlist doesn't exist. */
875
993
if (maildir_storage_sync_force(uidlist->mbox, uid) < 0)
880
fname = maildir_uidlist_lookup_nosync(uidlist, uid, flags_r);
998
ret = maildir_uidlist_lookup_nosync(uidlist, uid,
887
maildir_uidlist_lookup_nosync(struct maildir_uidlist *uidlist, uint32_t uid,
888
enum maildir_uidlist_rec_flag *flags_r)
1005
int maildir_uidlist_lookup_nosync(struct maildir_uidlist *uidlist, uint32_t uid,
1006
enum maildir_uidlist_rec_flag *flags_r,
1007
const char **fname_r)
890
const struct maildir_uidlist_rec *rec;
1009
struct maildir_uidlist_rec *rec;
891
1010
unsigned int idx;
893
rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
1013
if ((ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec)) <= 0)
897
1016
*flags_r = rec->flags;
898
return rec->filename;
1017
*fname_r = rec->filename;
902
1022
maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
903
1023
enum maildir_uidlist_rec_ext_key key)
905
const struct maildir_uidlist_rec *rec;
1025
struct maildir_uidlist_rec *rec;
906
1026
unsigned int idx;
907
1027
const unsigned char *p;
910
rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
911
if (rec == NULL || rec->extensions == NULL)
1030
ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec);
1031
if (ret <= 0 || rec->extensions == NULL)
914
p = rec->extensions; value = NULL;
1034
p = rec->extensions;
915
1035
while (*p != '\0') {
916
1036
/* <key><value>\0 */
917
1037
if (*p == (char)key)
957
1079
const unsigned char *p;
959
1081
unsigned int len;
961
rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
1084
ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec);
963
1089
/* maybe it's a new message */
964
1090
if (maildir_uidlist_refresh(uidlist) < 0)
966
rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
1092
if (maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec) <= 0) {
968
1093
/* message is already expunged, ignore */
992
1117
rec->extensions = p_malloc(uidlist->record_pool, buf->used);
993
1118
memcpy(rec->extensions, buf->data, buf->used);
995
uidlist->recreate = TRUE;
1120
if (rec->uid != (uint32_t)-1) {
1121
/* message already exists in uidlist, need to recreate it */
1122
uidlist->recreate = TRUE;
998
1126
void maildir_uidlist_set_ext(struct maildir_uidlist *uidlist, uint32_t uid,
1114
1242
/* the control dir doesn't exist. create it unless the whole
1115
1243
mailbox was just deleted. */
1116
if (maildir_set_deleted(uidlist->mbox))
1244
if (!maildir_set_deleted(uidlist->mbox))
1120
if (box->file_create_gid != (gid_t)-1) {
1121
if (fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
1248
if (box->file_create_gid != (gid_t)-1 &&
1249
fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
1250
if (errno == EPERM) {
1251
mail_storage_set_critical(box->storage, "%s",
1252
eperm_error_get_chgrp("fchown", temp_path,
1253
box->file_create_gid,
1254
box->file_create_gid_origin));
1122
1256
mail_storage_set_critical(box->storage,
1123
1257
"fchown(%s) failed: %m", temp_path);
1197
1333
struct maildir_uidlist *uidlist = ctx->uidlist;
1199
if (uidlist->recreate ||
1200
ctx->finish_change_counter != uidlist->change_counter)
1335
if (!uidlist->locked_refresh)
1338
if (ctx->finish_change_counter != uidlist->change_counter)
1203
if (!uidlist->initial_read)
1206
1340
if (uidlist->fd == -1 || uidlist->version != UIDLIST_VERSION)
1208
1342
return maildir_uidlist_want_compress(ctx);
1261
1395
if ((uoff_t)st.st_size != file_size) {
1262
1396
i_warning("%s: file size changed unexpectedly after write",
1263
1397
uidlist->path);
1398
} else if (uidlist->locked_refresh) {
1265
1399
uidlist->fd_size = st.st_size;
1266
1400
uidlist->last_read_offset = st.st_size;
1267
1401
maildir_uidlist_update_hdr(uidlist, &st);
1288
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
1289
enum maildir_uidlist_sync_flags sync_flags,
1290
struct maildir_uidlist_sync_ctx **sync_ctx_r)
1422
static int maildir_uidlist_sync_lock(struct maildir_uidlist *uidlist,
1423
enum maildir_uidlist_sync_flags sync_flags,
1292
struct maildir_uidlist_sync_ctx *ctx;
1293
bool nonblock, refresh, locked;
1426
bool nonblock, refresh;
1431
if ((sync_flags & MAILDIR_UIDLIST_SYNC_NOLOCK) != 0) {
1432
if (maildir_uidlist_refresh(uidlist) < 0)
1296
1437
nonblock = (sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0;
1297
1438
refresh = (sync_flags & MAILDIR_UIDLIST_SYNC_NOREFRESH) == 0;
1299
ret = maildir_uidlist_lock_timeout(uidlist, nonblock, refresh);
1440
ret = maildir_uidlist_lock_timeout(uidlist, nonblock, refresh, refresh);
1300
1441
if (ret <= 0) {
1301
1442
if (ret < 0 || !nonblock)
1307
1448
/* forcing the sync anyway */
1308
1449
if (maildir_uidlist_refresh(uidlist) < 0)
1457
void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist)
1459
maildir_uidlist_mark_all(uidlist, TRUE);
1462
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
1463
enum maildir_uidlist_sync_flags sync_flags,
1464
struct maildir_uidlist_sync_ctx **sync_ctx_r)
1466
struct maildir_uidlist_sync_ctx *ctx;
1470
ret = maildir_uidlist_sync_lock(uidlist, sync_flags, &locked);
1315
1474
*sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
1316
1475
ctx->uidlist = uidlist;
1490
i_assert(uidlist->locked_refresh);
1332
1492
ctx->record_pool = pool_alloconly_create(MEMPOOL_GROWING
1333
1493
"maildir_uidlist_sync", 16384);
1334
ctx->files = hash_create(default_pool, ctx->record_pool, 4096,
1335
maildir_filename_base_hash,
1336
maildir_filename_base_cmp);
1494
ctx->files = hash_table_create(default_pool, ctx->record_pool, 4096,
1495
maildir_filename_base_hash,
1496
maildir_filename_base_cmp);
1338
1498
i_array_init(&ctx->records, array_count(&uidlist->records));
1377
1537
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
1378
1538
rec->filename = p_strdup(uidlist->record_pool, filename);
1379
hash_insert(uidlist->files, rec->filename, rec);
1539
hash_table_insert(uidlist->files, rec->filename, rec);
1381
1541
ctx->finished = FALSE;
1438
1598
rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
1439
1599
MAILDIR_UIDLIST_REC_FLAG_MOVED);
1441
old_rec = hash_lookup(uidlist->files, filename);
1601
old_rec = hash_table_lookup(uidlist->files, filename);
1442
1602
i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist));
1444
1604
rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
1461
1621
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
1462
1622
rec->filename = p_strdup(ctx->record_pool, filename);
1463
hash_insert(ctx->files, rec->filename, rec);
1623
hash_table_insert(ctx->files, rec->filename, rec);
1471
1631
unsigned int idx;
1473
1633
i_assert(ctx->partial);
1634
i_assert(ctx->uidlist->locked_refresh);
1475
rec = hash_lookup(ctx->uidlist->files, filename);
1636
rec = hash_table_lookup(ctx->uidlist->files, filename);
1476
1637
i_assert(rec != NULL);
1477
1638
i_assert(rec->uid != (uint32_t)-1);
1479
hash_remove(ctx->uidlist->files, filename);
1640
hash_table_remove(ctx->uidlist->files, filename);
1480
1641
idx = maildir_uidlist_records_array_delete(ctx->uidlist, rec);
1482
1643
if (ctx->first_unwritten_pos != (unsigned int)-1) {
1551
1712
for (dest = ctx->first_nouid_pos; dest < count; dest++) {
1552
1713
i_assert(recs[dest]->uid == (uint32_t)-1);
1714
i_assert(ctx->uidlist->next_uid < (uint32_t)-1);
1553
1715
recs[dest]->uid = ctx->uidlist->next_uid++;
1554
1716
recs[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
1719
if (ctx->uidlist->locked_refresh)
1720
ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1;
1557
1722
ctx->new_files_count = 0;
1558
1723
ctx->first_nouid_pos = (unsigned int)-1;
1560
ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1;
1561
1724
ctx->uidlist->change_counter++;
1562
1725
ctx->finish_change_counter = ctx->uidlist->change_counter;
1599
1762
if (!ctx->failed)
1600
1763
maildir_uidlist_swap(ctx);
1602
if (ctx->new_files_count != 0)
1765
if (ctx->new_files_count != 0 && !ctx->failed) {
1766
i_assert(ctx->changed);
1767
i_assert(ctx->locked);
1603
1768
maildir_uidlist_assign_uids(ctx);
1606
1772
ctx->finished = TRUE;
1607
ctx->uidlist->initial_sync = TRUE;
1609
1774
i_assert(ctx->locked || !ctx->changed);
1610
if ((ctx->changed || ctx->uidlist->recreate ||
1611
maildir_uidlist_want_compress(ctx)) &&
1775
if ((ctx->changed || maildir_uidlist_want_compress(ctx)) &&
1612
1776
!ctx->failed && ctx->locked) T_BEGIN {
1613
if (maildir_uidlist_sync_update(ctx) < 0)
1777
if (maildir_uidlist_sync_update(ctx) < 0) {
1778
/* we couldn't write everything we wanted. make sure
1779
we don't continue using those UIDs */
1780
maildir_uidlist_reset(ctx->uidlist);
1614
1781
ctx->failed = TRUE;
1618
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx)
1786
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx,
1620
1789
struct maildir_uidlist_sync_ctx *ctx = *_ctx;
1621
int ret = ctx->failed ? -1 : 0;
1796
ret = ctx->failed ? -1 : 0;
1625
1798
if (!ctx->finished)
1626
1799
maildir_uidlist_sync_finish(ctx);
1627
1800
if (ctx->partial)