bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2003-2018 Dovecot authors, see the included COPYING file */
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen Version 1 format has been used for most versions of Dovecot up to v1.0.x.
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen It's also compatible with Courier IMAP's courierimapuiddb file.
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen The format is:
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen header: 1 <uid validity> <next uid>
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen entry: <uid> <filename>
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen Version 2 format was written by a few development Dovecot versions, but
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen v1.0.x still parses the format. The format has <flags> field after <uid>.
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen Version 3 format is an extensible format used by Dovecot v1.1 and later.
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen It's also parsed by v1.0.2 (and later). The format is:
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen header: 3 [<key><value> ...]
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen entry: <uid> [<key><value> ...] :<filename>
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen See enum maildir_uidlist_*_ext_key for used keys.
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen/* NFS: How many times to retry reading dovecot-uidlist file if ESTALE
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen error occurs in the middle of reading it */
89b548af722113acb5d63dfffb44423cb60f91e4Timo Sirainen#define UIDLIST_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen unsigned char *extensions; /* <data>\0[<data>\0 ...]\0 */
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo SirainenARRAY_DEFINE_TYPE(maildir_uidlist_rec_p, struct maildir_uidlist_rec *);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo SirainenHASH_TABLE_DEFINE_TYPE(path_to_maildir_uidlist_rec,
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen char *, struct maildir_uidlist_rec *);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE_TYPE(path_to_maildir_uidlist_rec) files;
0360636eced27e96661f210828d1203871e6c47cTimo Sirainen unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid;
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen unsigned int read_records_count, read_line_count;
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen HASH_TABLE_TYPE(path_to_maildir_uidlist_rec) files;
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen unsigned int first_unwritten_pos, first_new_pos;
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen struct maildir_uidlist_rec *const *next, *const *end;
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainenstatic int maildir_uidlist_open_latest(struct maildir_uidlist *uidlist);
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainenstatic bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx,
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenstatic int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen const struct mailbox_permissions *perm = mailbox_get_permissions(box);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen const enum dotlock_create_flags dotlock_flags =
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen if (!uidlist->locked_refresh && refresh_when_locked) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for (i = 0;; i++) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen old_mask = umask(0777 & ~perm->file_create_mode);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen ret = file_dotlock_create(&uidlist->dotlock_settings, path,
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* failure */
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
1dd875d96ab5640f78250079961c10e99ed4aa79Timo Sirainen eacces_error_get_creating("file_dotlock_create", path));
1dd875d96ab5640f78250079961c10e99ed4aa79Timo Sirainen "file_dotlock_create(%s) failed: %m",
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* the control dir doesn't exist. create it unless the whole
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen mailbox was just deleted. */
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen /* make sure we have the latest changes before
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen changing anything */
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenint maildir_uidlist_lock(struct maildir_uidlist *uidlist)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen return maildir_uidlist_lock_timeout(uidlist, FALSE, TRUE, FALSE);
09c3a491f4f6ccebe290c7709bdc0d79a187610bTimo Sirainenint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen return maildir_uidlist_lock_timeout(uidlist, TRUE, TRUE, FALSE);
4673afe816ffbca769585e4518e9b3c3d72e95ddTimo Sirainenint maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist)
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainenbool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist)
a0aedab7cd06125e4d73638b1bd0c01c7caa2626Timo Sirainenbool maildir_uidlist_is_read(struct maildir_uidlist *uidlist)
6013fbad6638795a00e6c2a2dd2cdbee19612494Timo Sirainenbool maildir_uidlist_is_open(struct maildir_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
075086f56fdcc480b0e87aabde63128e30b49002Timo Sirainenstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
d22301419109ed4a38351715e6760011421dadecTimo Sirainenstruct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox)
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL,
228edfb38d7f1a0bfe36f74e7c521006ea48fbdfTimo Sirainen uidlist->path = i_strconcat(control_dir, "/"MAILDIR_UIDLIST_NAME, NULL);
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&uidlist->files, default_pool, 4096,
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen uidlist->hdr_extensions = str_new(default_pool, 128);
fdcb22a688c4676face8db865736b217d9c07d19Timo Sirainen uidlist->dotlock_settings.use_io_notify = TRUE;
075086f56fdcc480b0e87aabde63128e30b49002Timo Sirainen uidlist->dotlock_settings.callback = dotlock_callback;
228edfb38d7f1a0bfe36f74e7c521006ea48fbdfTimo Sirainen uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix;
85452efe6f9edd85e2ce45bfc4f198e9cb9b82bdTimo Sirainenstatic void maildir_uidlist_close(struct maildir_uidlist *uidlist)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainenstatic void maildir_uidlist_reset(struct maildir_uidlist *uidlist)
9740d55b228a670047c854484d5cc979a056a9afTimo Sirainenvoid maildir_uidlist_deinit(struct maildir_uidlist **_uidlist)
fde0b1793a2842da00eaa105d5e13fec465f0443Timo Sirainenstatic int maildir_uid_cmp(struct maildir_uidlist_rec *const *rec1,
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenmaildir_uidlist_set_corrupted(struct maildir_uidlist *uidlist,
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen const char *fmt, ...)
a6249a80d437771867cf654ae9d11f8418c9d07bTimo Sirainen "Broken or unexpectedly changed file %s "
a6249a80d437771867cf654ae9d11f8418c9d07bTimo Sirainen "line %u: %s - re-reading from beginning",
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(uidlist->box, "Broken file %s line %u: %s",
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenstatic void maildir_uidlist_update_hdr(struct maildir_uidlist *uidlist,
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen struct maildir_index_header *mhdr = uidlist->mhdr;
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen if (mhdr->uidlist_mtime == 0 && uidlist->version != UIDLIST_VERSION) {
211c638d81d382517d196ad47565e0d85012c927klemens /* upgrading from older version. don't update the
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen uidlist times until it uses the new format */
14c3da8abe2e666ecdb2900c8000478ef303161bTimo Sirainen mhdr->uidlist_mtime_nsecs = ST_MTIME_NSEC(*st);
dc40ce1dda503f114e9505c2da9371fd3cb34096Timo Sirainenstatic unsigned int
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainenmaildir_uidlist_records_array_delete(struct maildir_uidlist *uidlist,
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen struct maildir_uidlist_rec *const *recs, *const *pos;
0dab9ad19ae7b1e3afe74d3ace2882fabb264a41Timo Sirainen pos = array_bsearch(&uidlist->records, &rec, maildir_uid_cmp);
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainenmaildir_uidlist_read_extended(struct maildir_uidlist *uidlist,
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen const char **line_p,
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen /* skip over an extension field */
df9169221169d9cd8d8f49fc51ad40bed0fb6f64Timo Sirainen if (MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*start)) {
df9169221169d9cd8d8f49fc51ad40bed0fb6f64Timo Sirainen "Invalid extension record, removing: %s",
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen /* save the extensions */
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen rec->extensions = p_malloc(uidlist->record_pool, buf->used);
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen memcpy(rec->extensions, buf->data, buf->used);
97c3cae5873e56c15357686eeeb3144896445e50Timo Sirainenstatic bool maildir_uidlist_next(struct maildir_uidlist *uidlist,
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen struct maildir_uidlist_rec *rec, *old_rec, *const *recs;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* invalid file */
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen maildir_uidlist_set_corrupted(uidlist, "Invalid data: %s",
a6249a80d437771867cf654ae9d11f8418c9d07bTimo Sirainen "UIDs not ordered (%u >= %u)",
0360636eced27e96661f210828d1203871e6c47cTimo Sirainen /* we already have this */
ce84075e7ad4aa67fd45d12e75feceaffc64e523Timo Sirainen if (uid >= uidlist->next_uid && uidlist->version == 1) {
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen "UID larger than next_uid (%u >= %u)",
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen /* read extended fields */
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen ret = maildir_uidlist_read_extended(uidlist, &line,
658d3e1c4b58281c35241123973f5447f812764fTimo Sirainen "%s: Broken filename at line %u: %s",
658d3e1c4b58281c35241123973f5447f812764fTimo Sirainen uidlist->path, uidlist->read_line_count, line);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen old_rec = hash_table_lookup(uidlist->files, line);
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen /* no conflicts */
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen /* most likely this is a record we saved ourself, but couldn't
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen update last_seen_uid because uidlist wasn't refreshed while
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen it was locked.
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen another possibility is a duplicate file record. currently
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen it would be a bug, but not that big of a deal. also perhaps
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen in future such duplicate lines could be used to update
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen extended fields. so just let it through anyway.
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen we'll waste a bit of memory here by allocating the record
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen twice, but that's not really a problem. */
c224fff79d18480a65e9b4504b891b8ea176f5b1Timo Sirainen hash_table_update(uidlist->files, rec->filename, rec);
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen /* This can happen if expunged file is moved back and the file
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen was appended to uidlist. */
3dbc59409d19dbcd3f47c041fdd2fe342647e331Timo Sirainen i_warning("%s: Duplicate file entry at line %u: "
f84e7cabe355c6416898b7ae82b7d59acc3c7fbdTimo Sirainen "%s (uid %u -> %u)%s",
3dbc59409d19dbcd3f47c041fdd2fe342647e331Timo Sirainen uidlist->path, uidlist->read_line_count, line,
f84e7cabe355c6416898b7ae82b7d59acc3c7fbdTimo Sirainen " - retrying by re-reading from beginning" : "");
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen /* Delete the old UID */
e34d170f8f0e084bd94bfbc1a7085ece67e508dfTimo Sirainen (void)maildir_uidlist_records_array_delete(uidlist, old_rec);
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen /* Replace the old record with this new one */
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen /* we most likely have some records in the array that we saved
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen ourself without refreshing uidlist */
033557e1c8ebec5ae31f2f24fab90226a1945168Timo Sirainen rec->filename = p_strdup(uidlist->record_pool, line);
c224fff79d18480a65e9b4504b891b8ea176f5b1Timo Sirainen hash_table_update(uidlist->files, rec->filename, rec);
c0bfb67ba32064347bac3241f1aac9b8a809e2f1Timo Sirainenmaildir_uidlist_read_v3_header(struct maildir_uidlist *uidlist,
c0bfb67ba32064347bac3241f1aac9b8a809e2f1Timo Sirainen unsigned int *next_uid_r)
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainenstatic int maildir_uidlist_read_header(struct maildir_uidlist *uidlist,
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen /* I/O error / empty file */
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen if (*line < '0' || *line > '9' || line[1] != ' ') {
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen "Corrupted header (invalid version number)");
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen if (sscanf(line, "%u %u", &uid_validity, &next_uid) != 2) {
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen "Corrupted header (version 1)");
c0bfb67ba32064347bac3241f1aac9b8a809e2f1Timo Sirainen ret = maildir_uidlist_read_v3_header(uidlist, line,
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen maildir_uidlist_set_corrupted(uidlist, "Unsupported version %u",
819e20e6f108db20751e19784ee5a091b3f50046Timo Sirainen "Broken header (uidvalidity = %u, next_uid=%u)",
5b440b4d921cb1a36d74b4082599ccd3bb0f0401Timo Sirainen "next_uid header was lowered (%u -> %u)",
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainenstatic void maildir_uidlist_records_sort_by_uid(struct maildir_uidlist *uidlist)
c9dea5c23355dea35c6fa423de69f6507852efe4Timo Sirainen array_sort(&uidlist->records, maildir_uid_cmp);
c485524d09c650ff6e6c552129d4257ac6145a8bTimo Sirainenmaildir_uidlist_update_read(struct maildir_uidlist *uidlist,
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen /* the file was updated */
588f592a98fca95a10aaccdc76a589558e9ba125Timo Sirainen "uidlist record_pool",
e93184a9055c2530366dfe617e07199603c399ddMartti Rannanjärvi input = i_stream_create_fd(fd, (size_t)-1);
a6249a80d437771867cf654ae9d11f8418c9d07bTimo Sirainen uidlist->retry_rewind = last_read_offset != 0 && try_retry;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen while ((line = i_stream_read_next_line(input)) != NULL) {
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen if (uidlist->next_uid <= uidlist->prev_read_uid)
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen uidlist->next_uid = uidlist->prev_read_uid + 1;
430acc18d409c92da20be46ef35dc6f1d2a855d2Timo Sirainen if (ret > 0 && uidlist->uid_validity != orig_uid_validity &&
5b440b4d921cb1a36d74b4082599ccd3bb0f0401Timo Sirainen } else if (ret > 0 && uidlist->next_uid < orig_next_uid) {
5b440b4d921cb1a36d74b4082599ccd3bb0f0401Timo Sirainen "%s: next_uid was lowered (%u -> %u, hdr=%u)",
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen /* file is broken */
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen } else if (ret > 0) {
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen /* success */
a6249a80d437771867cf654ae9d11f8418c9d07bTimo Sirainen } else if (!*retry_r) {
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen /* I/O error */
c485524d09c650ff6e6c552129d4257ac6145a8bTimo Sirainen if (input->stream_errno == ESTALE && try_retry)
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenmaildir_uidlist_stat(struct maildir_uidlist *uidlist, struct stat *st_r)
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen struct mail_storage *storage = uidlist->box->storage;
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenmaildir_uidlist_has_changed(struct maildir_uidlist *uidlist, bool *recreated_r)
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen struct mail_storage *storage = uidlist->box->storage;
6f1936d4f5424f2ce766b62ef41f4173ecf5e33bTimo Sirainen if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0)
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen /* file recreated */
54cb36f32da0f2b1225a62e9e5717d521e21aa99Timo Sirainen /* NFS: either the file hasn't been changed, or it has already
54cb36f32da0f2b1225a62e9e5717d521e21aa99Timo Sirainen been deleted and the inodes just happen to be the same.
54cb36f32da0f2b1225a62e9e5717d521e21aa99Timo Sirainen check if the fd is still valid. */
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen /* file modified but not recreated */
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen /* unchanged */
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainenstatic int maildir_uidlist_open_latest(struct maildir_uidlist *uidlist)
4f44284b96302c0a18203e2ac1243aeb193c6840Timo Sirainen ret = maildir_uidlist_has_changed(uidlist, &recreated);
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen uidlist->fd = nfs_safe_open(uidlist->path, O_RDWR);
a0b92d47480ac4135b5dec900c4358defbb3e0e6Timo Sirainen uidlist->fd = nfs_safe_open(uidlist->path, O_RDONLY);
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainenint maildir_uidlist_refresh(struct maildir_uidlist *uidlist)
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen unsigned int i;
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen for (i = 0; ; i++) {
c485524d09c650ff6e6c552129d4257ac6145a8bTimo Sirainen ret = maildir_uidlist_update_read(uidlist, &retry,
dec85d9856c33f427a06dda01e0e50de0bc8fa7dTimo Sirainen /* ESTALE - try reopening and rereading */
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenint maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist)
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen const struct maildir_index_header *mhdr = uidlist->mhdr;
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen struct mail_index *index = uidlist->box->index;
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0)
8c94614f795530b2cbe70e5e828da121f2ddbee2Timo Sirainen if (ret > 0 && st.st_size == mhdr->uidlist_size &&
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), mhdr->uidlist_mtime_nsecs) &&
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen (!mail_index_is_in_memory(index) || st.st_mtime < ioloop_time-1)) {
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen /* index is up-to-date. look up the uidvalidity and next-uid
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen from it. we'll need to create a new view temporarily to
15cc66ca72982a43e3bfa58f307adc57e9caa52dTimo Sirainen make sure we get the latest values. */
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainenmaildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen /* first time we need to read uidlist */
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainenint maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
7ede6554e451ec039a67beec7d6ee4aff61d386eTimo Sirainen const char **fname_r)
e7912167935f67b3dc68c80bf80d719bb1cdc533Timo Sirainen if ((ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec)) <= 0)
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainenmaildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen const unsigned char *p;
e7912167935f67b3dc68c80bf80d719bb1cdc533Timo Sirainen ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec);
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen while (*p != '\0') {
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen /* <key><value>\0 */
4efba37e4f27b93832f6147c3a353d6d22c855c7Timo Sirainen if (*p == (unsigned char)key)
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen return (const char *)p + 1;
e06c0b65c16ccce69bbee009ead14d7d3d17a256Timo Sirainenuint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
6b77095184aeb8a9976a74fa9ba1a06740f40d0eTimo Sirainenuint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist)
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen return !uidlist->initial_hdr_read ? 0 : uidlist->next_uid;
c0bfb67ba32064347bac3241f1aac9b8a809e2f1Timo Sirainenint maildir_uidlist_get_mailbox_guid(struct maildir_uidlist *uidlist,
de62ce819d59a529530da4b57be1b8d6dad13d6bTimo Sirainen memcpy(mailbox_guid, uidlist->mailbox_guid, GUID_128_SIZE);
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainenvoid maildir_uidlist_set_mailbox_guid(struct maildir_uidlist *uidlist,
17ad2164c747cedbf81dae1893063e71a3df0356Timo Sirainen if (memcmp(uidlist->mailbox_guid, mailbox_guid,
92888ef30960c30ccc9e030fe7eab5d4d04a7d1cTimo Sirainenvoid maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist,
6b77095184aeb8a9976a74fa9ba1a06740f40d0eTimo Sirainenvoid maildir_uidlist_set_next_uid(struct maildir_uidlist *uidlist,
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainenmaildir_uidlist_rec_set_ext(struct maildir_uidlist_rec *rec, pool_t pool,
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen const unsigned char *p;
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainen /* copy existing extensions, except for the one we're updating */
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainen while (*p != '\0') {
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainen /* <key><value>\0 */
ac1c79d03888e634d26914780f7a7bc9cf3bd4b6Timo Sirainen i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*p));
4efba37e4f27b93832f6147c3a353d6d22c855c7Timo Sirainen if (*p != (unsigned char)key)
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainen memcpy(rec->extensions, buf->data, buf->used);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenmaildir_uidlist_set_ext_internal(struct maildir_uidlist *uidlist, uint32_t uid,
ac1c79d03888e634d26914780f7a7bc9cf3bd4b6Timo Sirainen i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(key));
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen ret = maildir_uidlist_lookup_rec(uidlist, uid, &rec);
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen /* maybe it's a new message */
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen if (maildir_uidlist_lookup_rec(uidlist, uid, &rec) <= 0) {
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen /* message is already expunged, ignore */
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen maildir_uidlist_rec_set_ext(rec, uidlist->record_pool,
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen /* message already exists in uidlist, need to recreate it */
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid maildir_uidlist_set_ext(struct maildir_uidlist *uidlist, uint32_t uid,
90814c0276d9f7bd6650e430b8cb64a8918ad4b3Timo Sirainen maildir_uidlist_set_ext_internal(uidlist, uid, key, value);
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainenvoid maildir_uidlist_unset_ext(struct maildir_uidlist *uidlist, uint32_t uid,
90814c0276d9f7bd6650e430b8cb64a8918ad4b3Timo Sirainen maildir_uidlist_set_ext_internal(uidlist, uid, key, NULL);
ec9161e5061a34f0262ccbbf7760ee933d409167Timo Sirainenmaildir_uidlist_generate_uid_validity(struct maildir_uidlist *uidlist)
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen hdr = mail_index_get_header(uidlist->box->view);
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen maildir_get_uidvalidity_next(uidlist->box->list);
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainenstatic int maildir_uidlist_write_fd(struct maildir_uidlist *uidlist, int fd,
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen struct mail_storage *storage = uidlist->box->storage;
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen const unsigned char *p;
93fa87cf1a96c4f279ec4f5c311820313ba12c34Timo Sirainen output = o_stream_create_fd_file(fd, (uoff_t)-1, FALSE);
ec9161e5061a34f0262ccbbf7760ee933d409167Timo Sirainen maildir_uidlist_generate_uid_validity(uidlist);
4cc8a792108e1c115c1c6c9eb61746b31f0205dbTimo Sirainen str_printfa(str, "%u %c%u %c%u %c%s", uidlist->version,
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
bb592fc58fe5c3e81ad941637fbb30d1d1cd8694Timo Sirainen i_assert(first_idx <= array_count(&uidlist->records));
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen while (maildir_uidlist_iter_next_rec(iter, &rec)) {
ac1c79d03888e634d26914780f7a7bc9cf3bd4b6Timo Sirainen i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(*p));
db5fdf605555d43ca7abeaeb9cba0dcb178f2688Aki Tuomi strp = strchr(rec->filename, *MAILDIR_INFO_SEP_S);
6af9d209ee997d624aecbaf4a0bcd0ca7d60c31aTimo Sirainen str_append_n(str, rec->filename, strp - rec->filename);
e2a88d59c0d47d63ce1ad5b1fd95e487124a3fd4Timo Sirainen o_stream_nsend(output, str_data(str), str_len(str));
d4002fe1f64d25a792f76fb102ef7dc519cd4e24Martti Rannanjärvi mailbox_set_critical(uidlist->box, "write(%s) failed: %s",
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen if (storage->set->parsed_fsync_mode != FSYNC_MODE_NEVER) {
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainenmaildir_uidlist_records_drop_expunges(struct maildir_uidlist *uidlist)
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen ARRAY_TYPE(maildir_uidlist_rec_p) new_records;
67b328e6950f41c8d680af4775639c2c689f43cdTimo Sirainen /* we could get here when opening and locking mailbox,
67b328e6950f41c8d680af4775639c2c689f43cdTimo Sirainen before index files have been opened. */
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen view = mail_index_view_open(uidlist->box->index);
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen if (count * UIDLIST_COMPRESS_PERCENTAGE / 100 <= hdr->messages_count) {
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen /* too much trouble to be worth it */
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen i_array_init(&new_records, hdr->messages_count + 64);
f63b54b0c5fa2717443fd3a96f37119fdceb39e9Timo Sirainen for (i = 0, seq = 1; i < count && seq <= hdr->messages_count; ) {
f63b54b0c5fa2717443fd3a96f37119fdceb39e9Timo Sirainen /* expunged entry */
d55fb5fbf2dc0601fc34dbd26221369b0cedb5daTimo Sirainen hash_table_remove(uidlist->files, recs[i]->filename);
95c8129d63a2e243402d67f9c93e532beebee394Timo Sirainen /* index isn't up to date. we're probably just
95c8129d63a2e243402d67f9c93e532beebee394Timo Sirainen syncing it here. ignore this entry. */
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen /* drop messages expunged at the end of index */
d55fb5fbf2dc0601fc34dbd26221369b0cedb5daTimo Sirainen while (i < count && recs[i]->uid < hdr->next_uid) {
d55fb5fbf2dc0601fc34dbd26221369b0cedb5daTimo Sirainen hash_table_remove(uidlist->files, recs[i]->filename);
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen /* view might not be completely up-to-date, so preserve any
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen messages left */
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen for (; i < count; i++)
0441036bcde07d0ee015f06967a6579e3e1d5b9bTimo Sirainenstatic int maildir_uidlist_recreate(struct maildir_uidlist *uidlist)
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen const struct mailbox_permissions *perm = mailbox_get_permissions(box);
9405e52abbd62fa7f57fbd86943743e1959fe7acTimo Sirainen maildir_uidlist_records_drop_expunges(uidlist);
9fc97c8aa8190df87624d214bcc5d0b5362bec93Timo Sirainen if (mailbox_get_path_to(box, MAILBOX_LIST_PATH_TYPE_CONTROL,
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen for (i = 0;; i++) {
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen old_mask = umask(0777 & ~perm->file_create_mode);
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT) {
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen /* the control dir doesn't exist. create it unless the whole
32ee977e189266744ef69ac4e832fd3111d6f949Timo Sirainen mailbox was just deleted. */
ca98892a6b8a30ffc1fe26fcf02c7d59e3204e7eTimo Sirainen fchown(fd, (uid_t)-1, perm->file_create_gid) < 0) {
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen ret = maildir_uidlist_write_fd(uidlist, fd, temp_path, 0, &file_size);
de7611687432b9560bdd31a4969609e7b68a5b83Timo Sirainen "rename(%s, %s) failed: %m",
ac432b21f61d8c97f3459134b180ed13b90704e5Timo Sirainen i_assert(!file_dotlock_is_locked(uidlist->dotlock));
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen "Maildir uidlist dotlock overridden: %s",
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainenint maildir_uidlist_update(struct maildir_uidlist *uidlist)
fe761f466e2c0c49445115aa123c77097c0eaf5cTimo Sirainenstatic bool maildir_uidlist_want_compress(struct maildir_uidlist_sync_ctx *ctx)
fe761f466e2c0c49445115aa123c77097c0eaf5cTimo Sirainen (ctx->uidlist->read_records_count + ctx->new_files_count) *
7133574ad9c46d79a6b741adfc5b0ecc04cd9298Timo Sirainen return min_rewrite_count >= array_count(&ctx->uidlist->records);
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainenstatic bool maildir_uidlist_want_recreate(struct maildir_uidlist_sync_ctx *ctx)
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen struct maildir_uidlist *uidlist = ctx->uidlist;
fcadc92fa095335d1119b161584e7fa8568f9267Timo Sirainen if (!uidlist->locked_refresh || !uidlist->initial_read)
da8115ebacf055f87ec71ae1155a421452f2e0d5Timo Sirainen if (ctx->finish_change_counter != uidlist->change_counter)
c0bfb67ba32064347bac3241f1aac9b8a809e2f1Timo Sirainen if (uidlist->fd == -1 || uidlist->version != UIDLIST_VERSION ||
81c7a6e414a6d1c31f65cc977feda823b586d263Timo Sirainenstatic int maildir_uidlist_sync_update(struct maildir_uidlist_sync_ctx *ctx)
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen struct maildir_uidlist *uidlist = ctx->uidlist;
b6ccb2a38287c42aa53f66a927a6ada9d324b0beTimo Sirainen if (maildir_uidlist_want_recreate(ctx) || uidlist->recreate_on_change)
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen if (!uidlist->locked_refresh || uidlist->fd == -1) {
aba2f05e938a7f635b3f114d56c7c3413ee858feTimo Sirainen /* make sure we have the latest file (e.g. NOREFRESH used) */
8ae72ad7d0c69e972cfa65d1e2ce4e3e9a8b765cTimo Sirainen i_assert(ctx->first_unwritten_pos != UINT_MAX);
d5bae4060a1ba3fe663991169de7102f81039304Timo Sirainen if (maildir_uidlist_write_fd(uidlist, uidlist->fd, uidlist->path,
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen i_warning("%s: file size changed unexpectedly after write",
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainenstatic void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen recs = array_get_modifiable(&uidlist->records, &count);
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen for (i = 0; i < count; i++)
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen recs[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen for (i = 0; i < count; i++)
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen recs[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainenstatic int maildir_uidlist_sync_lock(struct maildir_uidlist *uidlist,
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen if ((sync_flags & MAILDIR_UIDLIST_SYNC_NOLOCK) != 0) {
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen nonblock = (sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0;
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen refresh = (sync_flags & MAILDIR_UIDLIST_SYNC_NOREFRESH) == 0;
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen ret = maildir_uidlist_lock_timeout(uidlist, nonblock, refresh, refresh);
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen /* couldn't lock it */
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen if ((sync_flags & MAILDIR_UIDLIST_SYNC_FORCE) == 0)
7c65a07e04f28f2a881d83989f85f9ad0e87a7b4Timo Sirainen /* forcing the sync anyway */
785d9cca224d33ca3938e9166784f6483e8a27d7Timo Sirainenvoid maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist)
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainenint maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
ba3d9eeb0bec6ed8465d68fa2480ad085559b580Timo Sirainen ret = maildir_uidlist_sync_lock(uidlist, sync_flags, &locked);
57a91f930a12d2cd1220da4f3f7cb2c47557cd37Timo Sirainen *sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
f7ad1162969feff6b08f0e640a928db1783daae9Timo Sirainen (sync_flags & MAILDIR_UIDLIST_SYNC_PARTIAL) != 0;
8925c5450daddcdd1834de2750b47fb6d192c6f6Timo Sirainen if ((sync_flags & MAILDIR_UIDLIST_SYNC_KEEP_STATE) == 0) {
8925c5450daddcdd1834de2750b47fb6d192c6f6Timo Sirainen /* initially mark all nonsynced */
588f592a98fca95a10aaccdc76a589558e9ba125Timo Sirainen ctx->record_pool = pool_alloconly_create(MEMPOOL_GROWING
678d0463849ba777106eb7875f27db07a5d8e3dfTimo Sirainen hash_table_create(&ctx->files, ctx->record_pool, 4096,
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen i_array_init(&ctx->records, array_count(&uidlist->records));
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainenmaildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx,
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen struct maildir_uidlist *uidlist = ctx->uidlist;
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen struct maildir_uidlist_rec *rec, *const *recs;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen /* we'll update uidlist directly */
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(uidlist->files, filename);
746a7ec64a09649ed3c96c88b97cdc370a7bbe2fTimo Sirainen /* doesn't exist in uidlist */
1582b4d531679849bba299c17b6ec9402b7df67dTimo Sirainen /* we can't add it, so just ignore it */
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen ctx->first_new_pos = array_count(&uidlist->records);
588f592a98fca95a10aaccdc76a589558e9ba125Timo Sirainen "uidlist record_pool",
042668c0cd5a7d35ce6373ae493695e8f12d3157Timo Sirainen rec->filename = p_strdup(uidlist->record_pool, filename);
042668c0cd5a7d35ce6373ae493695e8f12d3157Timo Sirainen hash_table_insert(uidlist->files, rec->filename, rec);
38920bff33eaa2acef5c200df5ce7088fd61e673Timo Sirainen } else if (strcmp(rec->filename, filename) != 0) {
38920bff33eaa2acef5c200df5ce7088fd61e673Timo Sirainen rec->filename = p_strdup(uidlist->record_pool, filename);
056bbae2011f2d93570e59c0618702d835d7e244Timo Sirainen if (rec->uid != uid && rec->uid != (uint32_t)-1) {
056bbae2011f2d93570e59c0618702d835d7e244Timo Sirainen "Maildir: %s changed UID %u -> %u",
51335acfa1580c6f6b1aa1bdb915d2cb5e0e67a4Timo Sirainen rec->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainenstatic unsigned char *ext_dup(pool_t pool, const unsigned char *extensions)
1930b9fae508a90de5f08fcd74602cbe3a5a5964Timo Sirainen for (len = 0; extensions[len] != '\0'; len++) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen return maildir_uidlist_sync_next_uid(ctx, filename, 0, flags, &rec);
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainenint maildir_uidlist_sync_next_uid(struct maildir_uidlist_sync_ctx *ctx,
edf8c636e80ad4cfe5dd29525c718898685fc2c3Timo Sirainen struct maildir_uidlist *uidlist = ctx->uidlist;
feaa6a3d82ea61496ced1f83a726ff33047c7da2Timo Sirainen const char *p;
32147ee6bdea566e64139b77ad0ea89960200df9Timo Sirainen i_warning("Maildir %s: Ignoring a file with #0x%x: %s",
feaa6a3d82ea61496ced1f83a726ff33047c7da2Timo Sirainen mailbox_get_path(uidlist->box), *p, filename);
056bbae2011f2d93570e59c0618702d835d7e244Timo Sirainen return maildir_uidlist_sync_next_partial(ctx, filename,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(ctx->files, filename);
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen if ((rec->flags & (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* possibly duplicate */
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* probably was in new/ and now we're seeing it in cur/.
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen remove new/moved flags so if this happens again we'll know
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen to check for duplicates. */
641f0c0900ee6e7cf9667f4b40ed95cec7d0cdcaTimo Sirainen rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
38920bff33eaa2acef5c200df5ce7088fd61e673Timo Sirainen rec->filename = p_strdup(ctx->record_pool, filename);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen old_rec = hash_table_lookup(uidlist->files, filename);
d656ea23231ca1232b2dae327cea83bb44c6f6ffTimo Sirainen i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist));
033557e1c8ebec5ae31f2f24fab90226a1945168Timo Sirainen rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
1d3f7c1278168d5b1cbfa9a2cc9929a0909056b4Timo Sirainen /* didn't exist in uidlist, it's recent */
042668c0cd5a7d35ce6373ae493695e8f12d3157Timo Sirainen rec->filename = p_strdup(ctx->record_pool, filename);
042668c0cd5a7d35ce6373ae493695e8f12d3157Timo Sirainen hash_table_insert(ctx->files, rec->filename, rec);
054fdbfc20f57cb4a386e088ec773d9980ac8b2cTimo Sirainen rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
2087637d10ad94ea029fe055f8ee55cd594955a8Timo Sirainenvoid maildir_uidlist_sync_remove(struct maildir_uidlist_sync_ctx *ctx,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(ctx->uidlist->files, filename);
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen hash_table_remove(ctx->uidlist->files, filename);
dc40ce1dda503f114e9505c2da9371fd3cb34096Timo Sirainen idx = maildir_uidlist_records_array_delete(ctx->uidlist, rec);
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainenvoid maildir_uidlist_sync_set_ext(struct maildir_uidlist_sync_ctx *ctx,
ac1c79d03888e634d26914780f7a7bc9cf3bd4b6Timo Sirainen i_assert(MAILDIR_UIDLIST_REC_EXT_KEY_IS_VALID(key));
e4e7475f646d66a257d682738fbff1f206ce4924Timo Sirainen maildir_uidlist_rec_set_ext(rec, pool, key, value);
4b43f50117630aa12b3cfd0cbd05ae22ba27fec1Timo Sirainenmaildir_uidlist_sync_get_full_filename(struct maildir_uidlist_sync_ctx *ctx,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(ctx->files, filename);
228edfb38d7f1a0bfe36f74e7c521006ea48fbdfTimo Sirainenbool maildir_uidlist_get_uid(struct maildir_uidlist *uidlist,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(uidlist->files, filename);
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainenvoid maildir_uidlist_update_fname(struct maildir_uidlist *uidlist,
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen rec = hash_table_lookup(uidlist->files, filename);
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen rec->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
2bde8972f2dcec46d96543407cc5b56954054359Timo Sirainen rec->filename = p_strdup(uidlist->record_pool, filename);
0f506139b4c95589a09a81a5d51636aee994cd1eTimo Sirainenmaildir_uidlist_get_full_filename(struct maildir_uidlist *uidlist,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(uidlist->files, filename);
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainenstatic int maildir_assign_uid_cmp(const void *p1, const void *p2)
033557e1c8ebec5ae31f2f24fab90226a1945168Timo Sirainen const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
65d89650662f7f1681a3431c585bfc9721a85149Timo Sirainen return maildir_filename_sort_cmp((*rec1)->filename, (*rec2)->filename);
bef19e231b696a2018fcb93d546a41fcf732c243Timo Sirainenstatic void maildir_uidlist_assign_uids(struct maildir_uidlist_sync_ctx *ctx)
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen ctx->first_unwritten_pos = ctx->first_new_pos;
b7b81543899e306c71e6152516d8698416162bcbTimo Sirainen /* sort new files and assign UIDs for them */
12e145323c63ecf3d3522bf1381012a985caa844Timo Sirainen recs = array_get_modifiable(&ctx->uidlist->records, &count);
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen qsort(recs + ctx->first_new_pos, count - ctx->first_new_pos,
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen for (dest = ctx->first_new_pos; dest < count; dest++) {
ebc973d8a29738c9b91e5c92a124375661df799bTimo Sirainen i_assert(ctx->uidlist->next_uid < (uint32_t)-1);
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen recs[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
f53a6e86c03f51ca7fb23a03751dfc88aa2d32f0Timo Sirainen if (ctx->uidlist->locked_refresh && ctx->uidlist->initial_read)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1;
bb592fc58fe5c3e81ad941637fbb30d1d1cd8694Timo Sirainen ctx->finish_change_counter = ctx->uidlist->change_counter;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist *uidlist = ctx->uidlist;
b06bfeaa9bb367b72edd51672c33e24d33d8ff1bTimo Sirainen /* buffer is unsorted, sort it by UID */
8409959d66804dc963bc6fcdcc9a01da0d56a978Timo Sirainen i_assert(array_is_created(&uidlist->records));
3054cbf69a94ca08123ca3a8d6a2c19a1784e11dTimo Sirainen ctx->first_new_pos = array_count(&uidlist->records) -
777ff25e82e0305e2696bcbe3c6e0274e3e8ce10Timo Sirainenvoid maildir_uidlist_sync_recreate(struct maildir_uidlist_sync_ctx *ctx)
73c76fa7340a107229c530196d026aadeae979c7Timo Sirainenvoid maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx)
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen if (ctx->new_files_count != 0 && !ctx->failed) {
777ff25e82e0305e2696bcbe3c6e0274e3e8ce10Timo Sirainen /* mbox=NULL means we're coming from dbox rebuilding code.
777ff25e82e0305e2696bcbe3c6e0274e3e8ce10Timo Sirainen the dbox is already locked, so allow uidlist recreation */
da8115ebacf055f87ec71ae1155a421452f2e0d5Timo Sirainen if ((ctx->changed || maildir_uidlist_want_compress(ctx)) &&
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen /* we couldn't write everything we wanted. make
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainen sure we don't continue using those UIDs */
aa62d8779ce53900c2f09bf2ff6fa790bc9f6a89Timo Sirainenint maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx,
5375aa138868dc2c45eb1a4ff37a0b577c2814f8Timo Sirainen maildir_uidlist_mark_all(ctx->uidlist, FALSE);
316689d0ef55e2fa4e2fb4ac5b1ea35ce65688d3Timo Sirainenvoid maildir_uidlist_add_flags(struct maildir_uidlist *uidlist,
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen rec = hash_table_lookup(uidlist->files, filename);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_uidlist_iter_init(struct maildir_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
43f6755bda8c834bf7a7120e6b485c35e27ee8bdTimo Sirainen ctx->next = array_get(&uidlist->records, &count);
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainen ctx->change_counter = ctx->uidlist->change_counter;
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainenmaildir_uidlist_iter_update_idx(struct maildir_uidlist_iter_ctx *ctx)
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainen ctx->next = array_get(&ctx->uidlist->records, &count);
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainen while (idx < count && ctx->next[idx]->uid <= ctx->prev_uid)
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainen while (idx > 0 && ctx->next[idx-1]->uid > ctx->prev_uid)
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainenstatic bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx,
dc4e0821e709d93c8504e50b4ba0aaeb5ce9e032Timo Sirainen if (ctx->change_counter != ctx->uidlist->change_counter)
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainenbool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
621e8c0767de486db8d4ebb317d441b3f3a0434fTimo Sirainen if (!maildir_uidlist_iter_next_rec(ctx, &rec))