maildir-uidlist.c revision 5b440b4d921cb1a36d74b4082599ccd3bb0f0401
5f5870385cff47efd2f58e7892f251cf13761528Timo Sirainen/* Copyright (c) 2003-2009 Dovecot authors, see the included COPYING file */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen/*
16f816d3f3c32ae3351834253f52ddd0212bcbf3Timo Sirainen Version 1 format has been used for most versions of Dovecot up to v1.0.x.
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen It's also compatible with Courier IMAP's courierimapuiddb file.
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen The format is:
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen header: 1 <uid validity> <next uid>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen entry: <uid> <filename>
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen --
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen Version 2 format was written by a few development Dovecot versions, but
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen v1.0.x still parses the format. The format has <flags> field after <uid>.
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen --
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e8fd7988ec183fb6c104aed19a61f1a096c51d34Timo Sirainen Version 3 format is an extensible format used by Dovecot v1.1 and later.
e8fd7988ec183fb6c104aed19a61f1a096c51d34Timo Sirainen It's also parsed by v1.0.2 (and later). The format is:
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen header: 3 [<key><value> ...]
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen entry: <uid> [<key><value> ...] :<filename>
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen See enum maildir_uidlist_*_ext_key for used keys.
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen*/
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen#include "lib.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "array.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "hash.h"
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen#include "istream.h"
ad850190d946d34966a56838cfdb216e021b5b5fTimo Sirainen#include "ostream.h"
ad850190d946d34966a56838cfdb216e021b5b5fTimo Sirainen#include "str.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "hex-binary.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "file-dotlock.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "close-keep-errno.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "nfs-workarounds.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "eacces-error.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "maildir-storage.h"
d35fee8d1e5e31614dba5e64d45ed23c7d6bfa53Timo Sirainen#include "maildir-sync.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-filename.h"
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#include "maildir-uidlist.h"
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen#include <stdio.h>
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen#include <stdlib.h>
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen#include <sys/stat.h>
eddd9bf1a1369aea4a2715f6be1137da6d17d293Timo Sirainen
19e8adccba16ff419f5675b1575358c2956dce83Timo Sirainen/* NFS: How many times to retry reading dovecot-uidlist file if ESTALE
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen error occurs in the middle of reading it */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_ESTALE_RETRY_COUNT NFS_ESTALE_RETRY_COUNT
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
8aacc9e7c84f8376822823ec98c2f551d4919b2eTimo Sirainen#define UIDLIST_VERSION 3
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_COMPRESS_PERCENTAGE 75
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_IS_LOCKED(uidlist) \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ((uidlist)->lock_count > 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen#define UIDLIST_ALLOW_WRITING(uidlist) \
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen (UIDLIST_IS_LOCKED(uidlist) || (uidlist)->mbox == NULL)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstruct maildir_uidlist_rec {
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uint32_t uid;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t flags;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen char *filename;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned char *extensions; /* <data>\0[<data>\0 ...]\0 */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen};
6e235046e1d8e9d89fc948f5c623676c20421a28Timo SirainenARRAY_DEFINE_TYPE(maildir_uidlist_rec_p, struct maildir_uidlist_rec *);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainenstruct maildir_uidlist {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct maildir_mailbox *mbox;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen struct index_mailbox *ibox;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen char *path;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int fd;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen dev_t fd_dev;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ino_t fd_ino;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen off_t fd_size;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen unsigned int lock_count;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen struct dotlock_settings dotlock_settings;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen struct dotlock *dotlock;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen pool_t record_pool;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen ARRAY_TYPE(maildir_uidlist_rec_p) records;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen struct hash_table *files;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen unsigned int change_counter;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen unsigned int version;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid;
85da8c055280cd45553b6b335e9fb226d6e2801eTimo Sirainen unsigned int hdr_next_uid;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen unsigned int read_records_count, read_line_count;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen uoff_t last_read_offset;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen string_t *hdr_extensions;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen uint8_t mailbox_guid[MAILBOX_GUID_SIZE];
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int recreate:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int initial_read:1;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen unsigned int initial_hdr_read:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int retry_rewind:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int locked_refresh:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int unsorted:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int have_mailbox_guid:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen};
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainenstruct maildir_uidlist_sync_ctx {
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen struct maildir_uidlist *uidlist;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen enum maildir_uidlist_sync_flags sync_flags;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen pool_t record_pool;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen ARRAY_TYPE(maildir_uidlist_rec_p) records;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen struct hash_table *files;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int first_unwritten_pos, first_nouid_pos;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int new_files_count;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen unsigned int finish_change_counter;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen
04b8a90af181cc4c7959266855e8ed50a22ed413Timo Sirainen unsigned int partial:1;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen unsigned int finished:1;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen unsigned int changed:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int failed:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen unsigned int locked:1;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen};
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainenstruct maildir_uidlist_iter_ctx {
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen struct maildir_uidlist *uidlist;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen struct maildir_uidlist_rec *const *next, *const *end;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen unsigned int change_counter;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen uint32_t prev_uid;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen};
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainenstatic bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx,
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen struct maildir_uidlist_rec **rec_r);
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen
85da8c055280cd45553b6b335e9fb226d6e2801eTimo Sirainenstatic int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen bool nonblock, bool refresh,
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen bool refresh_when_locked)
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen{
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen struct mailbox *box = &uidlist->ibox->box;
2131ef7a3390f15ea6a958256ea54908f1096350Timo Sirainen const char *control_dir, *path;
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen mode_t old_mask;
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen const enum dotlock_create_flags dotlock_flags =
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen nonblock ? DOTLOCK_CREATE_FLAG_NONBLOCK : 0;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen int i, ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (uidlist->lock_count > 0) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (!uidlist->locked_refresh && refresh_when_locked) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (maildir_uidlist_refresh(uidlist) < 0)
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return -1;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen }
69e03a846f6980144aa75bff0590c04852bffbbcTimo Sirainen uidlist->lock_count++;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen return 1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen index_storage_lock_notify_reset(&uidlist->mbox->ibox);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen control_dir = mailbox_list_get_path(box->list, box->name,
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL);
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen path = t_strconcat(control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen for (i = 0;; i++) {
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen old_mask = umask(0777 & ~box->file_create_mode);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen ret = file_dotlock_create(&uidlist->dotlock_settings, path,
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen dotlock_flags, &uidlist->dotlock);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen umask(old_mask);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (ret > 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen break;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen /* failure */
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (ret == 0) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_storage_set_error(box->storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen MAIL_ERROR_TEMP, MAIL_ERRSTR_LOCK_TIMEOUT);
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen return 0;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen }
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT ||
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen uidlist->mbox == NULL) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen if (errno == EACCES) {
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen mail_storage_set_critical(box->storage, "%s",
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen eacces_error_get_creating("file_dotlock_create", path));
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen } else {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen mail_storage_set_critical(box->storage,
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen "file_dotlock_create(%s) failed: %m",
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen path);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen return -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen /* the control dir doesn't exist. create it unless the whole
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen mailbox was just deleted. */
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen if (!maildir_set_deleted(&uidlist->mbox->ibox.box))
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen return -1;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen uidlist->lock_count++;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen uidlist->locked_refresh = FALSE;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen if (refresh) {
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen /* make sure we have the latest changes before
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen changing anything */
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen if (maildir_uidlist_refresh(uidlist) < 0) {
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen maildir_uidlist_unlock(uidlist);
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen return -1;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen }
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen }
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen return 1;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen}
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainenint maildir_uidlist_lock(struct maildir_uidlist *uidlist)
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen{
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen return maildir_uidlist_lock_timeout(uidlist, FALSE, TRUE, FALSE);
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen}
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainenint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen{
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen return maildir_uidlist_lock_timeout(uidlist, TRUE, TRUE, FALSE);
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen}
6ef7e31619edfaa17ed044b45861d106a86191efTimo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenint maildir_uidlist_lock_touch(struct maildir_uidlist *uidlist)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(UIDLIST_IS_LOCKED(uidlist));
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return file_dotlock_touch(uidlist->dotlock);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenbool maildir_uidlist_is_locked(struct maildir_uidlist *uidlist)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen return UIDLIST_IS_LOCKED(uidlist);
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen}
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainenvoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen{
e192a3b1ca8ae857e7d87298ea507d32977ba570Timo Sirainen i_assert(uidlist->lock_count > 0);
e192a3b1ca8ae857e7d87298ea507d32977ba570Timo Sirainen
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen if (--uidlist->lock_count > 0)
e192a3b1ca8ae857e7d87298ea507d32977ba570Timo Sirainen return;
811f2e26d9782d9cb99fdf82e18ffa0a77564fe2Timo Sirainen
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen uidlist->locked_refresh = FALSE;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen (void)file_dotlock_delete(&uidlist->dotlock);
c4877db8b6559846f4b58be8e42422dc734c193fTimo Sirainen}
6bc0f424bcdb9119d8159874cf98adfa53eefd9aTimo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainenstatic bool dotlock_callback(unsigned int secs_left, bool stale, void *context)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen struct index_mailbox *ibox = context;
8b9342aa96b2f297e23afb261f9f7dd859800952Timo Sirainen
8b9342aa96b2f297e23afb261f9f7dd859800952Timo Sirainen index_storage_lock_notify(ibox, stale ?
8b9342aa96b2f297e23afb261f9f7dd859800952Timo Sirainen MAILBOX_LOCK_NOTIFY_MAILBOX_OVERRIDE :
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen MAILBOX_LOCK_NOTIFY_MAILBOX_ABORT,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen secs_left);
37e6cf44d61a81c6839e3ab76234b54309d8d292Timo Sirainen return TRUE;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainenstruct maildir_uidlist *
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenmaildir_uidlist_init_readonly(struct index_mailbox *ibox)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mailbox *box = &ibox->box;
a40d26f83af808a0ea1e212c001d682a96d870b0Timo Sirainen struct maildir_uidlist *uidlist;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *control_dir;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen control_dir = mailbox_list_get_path(box->list, box->name,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen MAILBOX_LIST_PATH_TYPE_CONTROL);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist = i_new(struct maildir_uidlist, 1);
56f45b3f3ae20e5c933701f4657dda5ef1916855Timo Sirainen uidlist->fd = -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen uidlist->ibox = ibox;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->path = i_strconcat(control_dir, "/"MAILDIR_UIDLIST_NAME, NULL);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen i_array_init(&uidlist->records, 128);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->files = hash_table_create(default_pool, default_pool, 4096,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_filename_base_hash,
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen maildir_filename_base_cmp);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->next_uid = 1;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->hdr_extensions = str_new(default_pool, 128);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen uidlist->dotlock_settings.use_io_notify = TRUE;
6b85bc4b03e552cfaeeae872d63c2d8ac5fcb7c4Timo Sirainen uidlist->dotlock_settings.use_excl_lock =
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen box->storage->set->dotlock_use_excl;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen uidlist->dotlock_settings.nfs_flush =
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen box->storage->set->mail_nfs_storage;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->dotlock_settings.timeout =
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT + 2;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->dotlock_settings.stale_timeout =
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen MAILDIR_UIDLIST_LOCK_STALE_TIMEOUT;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->dotlock_settings.callback = dotlock_callback;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->dotlock_settings.context = ibox;
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen return uidlist;
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen}
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainenstruct maildir_uidlist *maildir_uidlist_init(struct maildir_mailbox *mbox)
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen{
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen struct maildir_uidlist *uidlist;
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen
1e47cfede3a0b62654105daab00e97b5d660bc6bTimo Sirainen uidlist = maildir_uidlist_init_readonly(&mbox->ibox);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->mbox = mbox;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->dotlock_settings.temp_prefix = mbox->storage->temp_prefix;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen return uidlist;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainenstatic void maildir_uidlist_close(struct maildir_uidlist *uidlist)
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen{
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen struct mail_storage *storage = uidlist->ibox->box.storage;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen if (uidlist->fd != -1) {
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen if (close(uidlist->fd) < 0) {
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen mail_storage_set_critical(storage,
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen "close(%s) failed: %m", uidlist->path);
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen }
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen uidlist->fd = -1;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen uidlist->fd_ino = 0;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen }
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen uidlist->last_read_offset = 0;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen uidlist->read_line_count = 0;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen}
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainenstatic void maildir_uidlist_reset(struct maildir_uidlist *uidlist)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen maildir_uidlist_close(uidlist);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen uidlist->last_seen_uid = 0;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->initial_hdr_read = FALSE;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_table_clear(uidlist->files, FALSE);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_clear(&uidlist->records);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenvoid maildir_uidlist_deinit(struct maildir_uidlist **_uidlist)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen struct maildir_uidlist *uidlist = *_uidlist;
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen i_assert(!UIDLIST_IS_LOCKED(uidlist));
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen *_uidlist = NULL;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen maildir_uidlist_update(uidlist);
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen maildir_uidlist_close(uidlist);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen hash_table_destroy(&uidlist->files);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->record_pool != NULL)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pool_unref(&uidlist->record_pool);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen array_free(&uidlist->records);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen str_free(&uidlist->hdr_extensions);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(uidlist->path);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_free(uidlist);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainenstatic int maildir_uid_cmp(struct maildir_uidlist_rec *const *rec1,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist_rec *const *rec2)
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return (*rec1)->uid < (*rec2)->uid ? -1 :
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen (*rec1)->uid > (*rec2)->uid ? 1 : 0;
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainen}
f519e4c2ad4ef826f1b08f3e0138b9b287a52c80Timo Sirainen
58be9d6bcc3800f5b3d76a064ee767fbe31a5a8aTimo Sirainenstatic void ATTR_FORMAT(2, 3)
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainenmaildir_uidlist_set_corrupted(struct maildir_uidlist *uidlist,
d565eaa943f29a49b97230ced57eec40ee65b4f9Timo Sirainen const char *fmt, ...)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage *storage = uidlist->ibox->box.storage;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen va_list args;
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen
25c22e54d1071d120641e9eecd0023e7373e65ffTimo Sirainen va_start(args, fmt);
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen if (uidlist->retry_rewind) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen mail_storage_set_critical(storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "Broken or unexpectedly changed file %s "
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen "line %u: %s - re-reading from beginning",
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->path, uidlist->read_line_count,
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen t_strdup_vprintf(fmt, args));
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen } else {
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen mail_storage_set_critical(storage, "Broken file %s line %u: %s",
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen uidlist->path, uidlist->read_line_count,
4b231ca0bbe3b536acbd350101e183441ce0247aTimo Sirainen t_strdup_vprintf(fmt, args));
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen }
c53e8ee216904ffe6de4f6518d9f9f5107b7610eTimo Sirainen va_end(args);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainenstatic void maildir_uidlist_update_hdr(struct maildir_uidlist *uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const struct stat *st)
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_index_header *mhdr;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uidlist->mbox == NULL) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen /* dbox is using this */
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return;
abe7afb8f1766fbcef1b9df513109e43d7d16e49Timo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen mhdr = &uidlist->mbox->maildir_hdr;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (mhdr->uidlist_mtime == 0) {
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen if (!uidlist->initial_read)
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen (void)maildir_uidlist_refresh(uidlist);
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen if (uidlist->version != UIDLIST_VERSION) {
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen /* upgrading from older verson. don't update the
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen uidlist times until it uses the new format */
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen uidlist->recreate = TRUE;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen return;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen }
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen }
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen mhdr->uidlist_mtime = st->st_mtime;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen mhdr->uidlist_mtime_nsecs = ST_MTIME_NSEC(*st);
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen mhdr->uidlist_size = st->st_size;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen}
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainenstatic unsigned int
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainenmaildir_uidlist_records_array_delete(struct maildir_uidlist *uidlist,
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen struct maildir_uidlist_rec *rec)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct maildir_uidlist_rec *const *recs, *const *pos;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen unsigned int idx;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen pos = array_bsearch(&uidlist->records, &rec, maildir_uid_cmp);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen i_assert(pos != NULL);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen recs = array_idx(&uidlist->records, 0);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen idx = pos - recs;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen array_delete(&uidlist->records, idx, 1);
a835194f9a9dae88528367a791cbc282589f6c01Timo Sirainen return idx;
0b878c6a17c608fcd8b52a5762ed2c6a5cf4700aTimo Sirainen}
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainenstatic bool
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainenmaildir_uidlist_read_extended(struct maildir_uidlist *uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char **line_p,
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen struct maildir_uidlist_rec *rec)
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen{
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen const char *start, *line = *line_p;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen buffer_t *buf;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(), 128);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen while (*line != '\0' && *line != ':') {
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen /* skip over an extension field */
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen start = line;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen while (*line != ' ' && *line != '\0') line++;
66ecc94150cbce23aad3240135e0782e0a74d479Timo Sirainen buffer_append(buf, start, line - start);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen buffer_append_c(buf, '\0');
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen while (*line == ' ') line++;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen }
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen if (buf->used > 0) {
59151b71059df1190acd75d8717ed04a7920c862Timo Sirainen /* save the extensions */
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen buffer_append_c(buf, '\0');
5626ae5e3316eced244adb6485c0927f1c7fdc41Timo Sirainen rec->extensions = p_malloc(uidlist->record_pool, buf->used);
9315dd69233d554452df0c12bc57002d2042a8f4Timo Sirainen memcpy(rec->extensions, buf->data, buf->used);
9315dd69233d554452df0c12bc57002d2042a8f4Timo Sirainen }
69bd816e46fdee6182d0cb2e4c6be32399a555c8Timo Sirainen
69bd816e46fdee6182d0cb2e4c6be32399a555c8Timo Sirainen if (*line == ':')
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen line++;
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen if (*line == '\0')
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen return FALSE;
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen
f23298fea47eecbeded985ee2537a34c4c4ef56bTimo Sirainen *line_p = line;
da5d50534cfca45d0aaaf0bdac17b287b4588809Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen}
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainenstatic bool maildir_uidlist_next(struct maildir_uidlist *uidlist,
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen const char *line)
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen{
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen struct maildir_uidlist_rec *rec, *old_rec, *const *recs;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen unsigned int count;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen uint32_t uid;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen uid = 0;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen while (*line >= '0' && *line <= '9') {
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen uid = uid*10 + (*line - '0');
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen line++;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen }
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen
5196f9ea42d02000f9c3d22f20aa816140af4422Timo Sirainen if (uid == 0 || *line != ' ') {
5196f9ea42d02000f9c3d22f20aa816140af4422Timo Sirainen /* invalid file */
5196f9ea42d02000f9c3d22f20aa816140af4422Timo Sirainen maildir_uidlist_set_corrupted(uidlist, "Invalid data: %s",
5196f9ea42d02000f9c3d22f20aa816140af4422Timo Sirainen line);
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen return FALSE;
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen }
f239eb76f77afcbc0bfc97c9b52b4407d1bc3fe6Timo Sirainen if (uid <= uidlist->prev_read_uid) {
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen maildir_uidlist_set_corrupted(uidlist,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "UIDs not ordered (%u >= %u)",
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen uid, uidlist->prev_read_uid);
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen return FALSE;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen if (uid >= (uint32_t)-1) {
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen "UID too high (%u)", uid);
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen return FALSE;
d5cebe7f98e63d4e2822863ef2faa4971e8b3a5dTimo Sirainen }
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->prev_read_uid = uid;
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen if (uid <= uidlist->last_seen_uid) {
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen /* we already have this */
519e0a461271843833a2b42626ad93f6e7ddc497Timo Sirainen return TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->last_seen_uid = uid;
3ccab0bac68040f179a7de45c516cec258e28fdbTimo Sirainen
648d24583c1574441c4fa0331a90bd4d6e7996c5Timo Sirainen if (uid >= uidlist->next_uid && uidlist->version == 1) {
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainen maildir_uidlist_set_corrupted(uidlist,
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen "UID larger than next_uid (%u >= %u)",
0cea9b1f4fa0495a48f5f097e40492517d67e1baTimo Sirainen uid, uidlist->next_uid);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return FALSE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen rec->uid = uid;
2af769daebd83719ac696a440e06f6020471cec0Timo Sirainen rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen while (*line == ' ') line++;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen if (uidlist->version == UIDLIST_VERSION) {
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen /* read extended fields */
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen bool ret;
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen T_BEGIN {
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen ret = maildir_uidlist_read_extended(uidlist, &line,
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen rec);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen } T_END;
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen if (!ret) {
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen "Invalid extended fields: %s", line);
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen return FALSE;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen }
036626b19f14bef582f96e556913ae91b1d67881Timo Sirainen }
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen if (strchr(line, '/') != NULL) {
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen maildir_uidlist_set_corrupted(uidlist,
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen "%s: Broken filename at line %u: %s",
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen uidlist->path, uidlist->read_line_count, line);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return FALSE;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen }
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen old_rec = hash_table_lookup(uidlist->files, line);
4aae8acbcfa9cac96b4af39bfabcbe569e804827Timo Sirainen if (old_rec == NULL) {
5bdad39213d28ab35e615a7f4ea1712ab25b6a80Timo Sirainen /* no conflicts */
5bdad39213d28ab35e615a7f4ea1712ab25b6a80Timo Sirainen } else if (old_rec->uid == uid) {
5bdad39213d28ab35e615a7f4ea1712ab25b6a80Timo Sirainen /* most likely this is a record we saved ourself, but couldn't
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen update last_seen_uid because uidlist wasn't refreshed while
763f83d3cc47bce05cbc396419c4db2b71dd8e68Timo Sirainen it was locked.
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen
4aae8acbcfa9cac96b4af39bfabcbe569e804827Timo Sirainen another possibility is a duplicate file record. currently
4aae8acbcfa9cac96b4af39bfabcbe569e804827Timo Sirainen it would be a bug, but not that big of a deal. also perhaps
4aae8acbcfa9cac96b4af39bfabcbe569e804827Timo Sirainen in future such duplicate lines could be used to update
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen extended fields. so just let it through anyway.
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen we'll waste a bit of memory here by allocating the record
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen twice, but that's not really a problem. */
bc564f1d3d953cf724828322b11ae89e0f59ffc9Timo Sirainen rec->filename = old_rec->filename;
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen hash_table_insert(uidlist->files, rec->filename, rec);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen uidlist->unsorted = TRUE;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return TRUE;
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen } else {
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen /* This can happen if expunged file is moved back and the file
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen was appended to uidlist. */
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen i_warning("%s: Duplicate file entry at line %u: "
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen "%s (uid %u -> %u)",
6cb2c6ecddcdbeac9e6c73a292244747e12a793eTimo Sirainen uidlist->path, uidlist->read_line_count, line,
2af769daebd83719ac696a440e06f6020471cec0Timo Sirainen old_rec->uid, uid);
2af769daebd83719ac696a440e06f6020471cec0Timo Sirainen /* Delete the old UID */
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen maildir_uidlist_records_array_delete(uidlist, old_rec);
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen /* Replace the old record with this new one */
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen *old_rec = *rec;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen rec = old_rec;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen uidlist->recreate = TRUE;
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen }
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen recs = array_get(&uidlist->records, &count);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (count > 0 && recs[count-1]->uid > uid) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen /* we most likely have some records in the array that we saved
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen ourself without refreshing uidlist */
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen uidlist->unsorted = TRUE;
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen }
6a8a4c9f530668cd8961b73d702856ed94f05f80Timo Sirainen
0d0451206a91e9f96e522075dce28a89adc2325dTimo Sirainen rec->filename = p_strdup(uidlist->record_pool, line);
1c0590b2729567ad60dafde4d2c5f19635755a3dTimo Sirainen hash_table_insert(uidlist->files, rec->filename, rec);
1c0590b2729567ad60dafde4d2c5f19635755a3dTimo Sirainen array_append(&uidlist->records, &rec, 1);
ba482d3624ca4f1b3d638e6e8470ba5134f21493Timo Sirainen return TRUE;
ba482d3624ca4f1b3d638e6e8470ba5134f21493Timo Sirainen}
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainenstatic int
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainenmaildir_uidlist_read_v3_header(struct maildir_uidlist *uidlist,
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen const char *line,
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen unsigned int *uid_validity_r,
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen unsigned int *next_uid_r)
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen{
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen buffer_t *buf;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen char key;
6a19e109ee8c5a6f688da83a86a7f6abeb71abddTimo Sirainen
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen str_truncate(uidlist->hdr_extensions, 0);
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen while (*line != '\0') {
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen const char *value;
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen key = *line;
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen value = ++line;
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen while (*line != '\0' && *line != ' ') line++;
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen value = t_strdup_until(value, line);
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen switch (key) {
dc0474dc9d5652d76cb41f439844dd80c8b96642Timo Sirainen case MAILDIR_UIDLIST_HDR_EXT_UID_VALIDITY:
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen *uid_validity_r = strtoul(value, NULL, 10);
e6e43b396799aa5704c679a3017d6c7195f9347dTimo Sirainen break;
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen case MAILDIR_UIDLIST_HDR_EXT_NEXT_UID:
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen *next_uid_r = strtoul(value, NULL, 10);
cf3bea6d9b57f8608bec22d98ad547a507b05f66Timo Sirainen break;
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen case MAILDIR_UIDLIST_HDR_EXT_GUID:
84da9c6d6e162b064608cbfa9a47e0d60553c593Timo Sirainen buf = buffer_create_dynamic(pool_datastack_create(),
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen MAILBOX_GUID_SIZE);
84da9c6d6e162b064608cbfa9a47e0d60553c593Timo Sirainen if (hex_to_binary(value, buf) < 0 ||
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen buf->used != MAILBOX_GUID_SIZE) {
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen "Invalid mailbox GUID: %s", value);
df6478c4cf605bd81b3891c148b84c14eb6c4035Timo Sirainen return -1;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen }
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen memcpy(uidlist->mailbox_guid, buf->data,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen sizeof(uidlist->mailbox_guid));
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen uidlist->have_mailbox_guid = TRUE;
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen break;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen default:
fd2f5fbc1f07aa93e2214a28cdf02437fb7d06c8Timo Sirainen if (str_len(uidlist->hdr_extensions) > 0)
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen str_append_c(uidlist->hdr_extensions, ' ');
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen str_printfa(uidlist->hdr_extensions,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen "%c%s", key, value);
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen break;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen }
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen while (*line == ' ') line++;
114a0f74e0f825c6bd8aeadfafb248a030762a1fTimo Sirainen }
7981779f9aebd25728d3c26555d598ff842cf2e2Timo Sirainen return 0;
7981779f9aebd25728d3c26555d598ff842cf2e2Timo Sirainen}
7981779f9aebd25728d3c26555d598ff842cf2e2Timo Sirainen
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainenstatic int maildir_uidlist_read_header(struct maildir_uidlist *uidlist,
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen struct istream *input)
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen{
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen unsigned int uid_validity = 0, next_uid = 0;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen const char *line;
6fdf8b5e4e71a69f5974f59eec2b8c19bc421fe2Timo Sirainen int ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen line = i_stream_read_next_line(input);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (line == NULL) {
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen /* I/O error / empty file */
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen return input->stream_errno == 0 ? 0 : -1;
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen }
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen uidlist->read_line_count = 1;
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen if (*line < '0' || *line > '9' || line[1] != ' ') {
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen maildir_uidlist_set_corrupted(uidlist,
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen "Corrupted header (invalid version number)");
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen return 0;
d0bbbc7057aa33b52ee378196dee7d773437468fTimo Sirainen }
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uidlist->version = *line - '0';
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen line += 2;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen switch (uidlist->version) {
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen case 1:
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (sscanf(line, "%u %u", &uid_validity, &next_uid) != 2) {
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen "Corrupted header (version 1)");
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen return 0;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen }
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen break;
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen case UIDLIST_VERSION:
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen T_BEGIN {
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen ret = maildir_uidlist_read_v3_header(uidlist, line,
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen &uid_validity,
62cfc346eb7b0a4fd9e1ab6edd63b98711161229Timo Sirainen &next_uid);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen } T_END;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen if (ret < 0)
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen return 0;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen break;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen default:
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen maildir_uidlist_set_corrupted(uidlist, "Unsupported version %u",
1098fc409a45e7603701dc94635927a673bee0c1Timo Sirainen uidlist->version);
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen return 0;
72cbf33ae81fde08384d30c779ff540752d9256cTimo Sirainen }
b780aa272b742a43579cdb523cc79cc8d4521306Timo Sirainen
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen if (uid_validity == 0 || next_uid == 0) {
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen "Broken header (uidvalidity = %u, next_uid=%u)",
51b979b6414b940f04677a7e2d064be119345954Timo Sirainen uid_validity, next_uid);
d9fdacd5fb3e07997e5c389739d2054f0c8441d8Timo Sirainen return 0;
a817fdcc43aedf423e2134091d5f83f91d64bcc9Timo Sirainen }
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (uid_validity == uidlist->uid_validity &&
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen next_uid < uidlist->hdr_next_uid) {
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen maildir_uidlist_set_corrupted(uidlist,
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen "next_uid header was lowered (%u -> %u)",
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen uidlist->hdr_next_uid, next_uid);
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen return 0;
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen }
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen uidlist->uid_validity = uid_validity;
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen uidlist->next_uid = next_uid;
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen uidlist->hdr_next_uid = next_uid;
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen return 1;
f501ad38c51cf1d8f4f84313922c785e6ae6e81fTimo Sirainen}
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainenstatic void maildir_uidlist_records_sort_by_uid(struct maildir_uidlist *uidlist)
9566c1b4506d49778659e3dc65997f3c0399cb7eTimo Sirainen{
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen array_sort(&uidlist->records, maildir_uid_cmp);
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen uidlist->unsorted = FALSE;
dbd9604da561399cc6255289d5b6f6f662ab2d00Timo Sirainen}
f3976df875193529127d584cb713983e8160bdcfTimo Sirainen
f3976df875193529127d584cb713983e8160bdcfTimo Sirainenstatic int
f3976df875193529127d584cb713983e8160bdcfTimo Sirainenmaildir_uidlist_update_read(struct maildir_uidlist *uidlist,
f3976df875193529127d584cb713983e8160bdcfTimo Sirainen bool *retry_r, bool try_retry)
f3976df875193529127d584cb713983e8160bdcfTimo Sirainen{
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct mail_storage *storage = uidlist->ibox->box.storage;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen const char *line;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uint32_t orig_next_uid, orig_uid_validity;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct istream *input;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen struct stat st;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen uoff_t last_read_offset;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen int fd, ret;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen *retry_r = FALSE;
e015e2f7e7f48874495f9df8b0dd192b7ffcb5ccTimo Sirainen
4b058f90f9e8a2c6b2eed275de4eb8cc5195a71dTimo Sirainen if (uidlist->fd == -1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen fd = nfs_safe_open(uidlist->path, O_RDWR);
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (fd == -1) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen if (errno != ENOENT) {
128ea07dab8d67124ea74bcc085a478784b6358aTimo Sirainen mail_storage_set_critical(storage,
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen "open(%s) failed: %m", uidlist->path);
128ea07dab8d67124ea74bcc085a478784b6358aTimo Sirainen return -1;
128ea07dab8d67124ea74bcc085a478784b6358aTimo Sirainen }
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen return 0;
6f73af3a3a6ee900c7e736874587968d76a20bc0Timo Sirainen }
128ea07dab8d67124ea74bcc085a478784b6358aTimo Sirainen last_read_offset = 0;
5724e7103eed12fe36b55a7b5a8653284a2184b9Timo Sirainen } else {
5724e7103eed12fe36b55a7b5a8653284a2184b9Timo Sirainen /* the file was updated */
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen fd = uidlist->fd;
4bbee99b3aef449a9a2a11a5b5cf1ca486915c49Timo Sirainen if (lseek(fd, 0, SEEK_SET) < 0) {
0f66f12eb4cdbf47670975044c88d8f388bf92dfTimo Sirainen if (errno == ESTALE && try_retry) {
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen *retry_r = TRUE;
0cb2e8eb55e70f8ebe1e8349bdf49e4cbe5d8834Timo Sirainen return -1;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen }
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen mail_storage_set_critical(storage,
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen "lseek(%s) failed: %m", uidlist->path);
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen return -1;
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen }
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen uidlist->fd = -1;
8fa86f7ef06aa6cf0239c7ca2eb98889691d40d4Timo Sirainen uidlist->fd_ino = 0;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen last_read_offset = uidlist->last_read_offset;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen uidlist->last_read_offset = 0;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen }
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen if (fstat(fd, &st) < 0) {
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen close_keep_errno(fd);
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen if (errno == ESTALE && try_retry) {
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen *retry_r = TRUE;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen return -1;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen }
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen mail_storage_set_critical(storage,
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen "fstat(%s) failed: %m", uidlist->path);
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen return -1;
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen }
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen if (uidlist->record_pool == NULL) {
956f7778e413d3184d69e7b96e4a6b3cd5570bcdTimo Sirainen uidlist->record_pool =
pool_alloconly_create(MEMPOOL_GROWING
"uidlist record_pool",
nearest_power(st.st_size -
st.st_size/8));
}
input = i_stream_create_fd(fd, 4096, FALSE);
i_stream_seek(input, last_read_offset);
orig_uid_validity = uidlist->uid_validity;
orig_next_uid = uidlist->next_uid;
ret = input->v_offset != 0 ? 1 :
maildir_uidlist_read_header(uidlist, input);
if (ret > 0) {
uidlist->prev_read_uid = 0;
uidlist->change_counter++;
uidlist->read_records_count = 0;
uidlist->retry_rewind = last_read_offset != 0 && try_retry;
ret = 1;
while ((line = i_stream_read_next_line(input)) != NULL) {
uidlist->read_records_count++;
uidlist->read_line_count++;
if (!maildir_uidlist_next(uidlist, line)) {
if (!uidlist->retry_rewind)
ret = 0;
else {
ret = -1;
*retry_r = TRUE;
}
break;
}
}
uidlist->retry_rewind = FALSE;
if (input->stream_errno != 0)
ret = -1;
if (uidlist->unsorted) {
uidlist->recreate = TRUE;
maildir_uidlist_records_sort_by_uid(uidlist);
}
if (uidlist->next_uid <= uidlist->prev_read_uid)
uidlist->next_uid = uidlist->prev_read_uid + 1;
if (ret > 0 && uidlist->uid_validity != orig_uid_validity) {
uidlist->recreate = TRUE;
} else if (ret > 0 && uidlist->next_uid < orig_next_uid) {
mail_storage_set_critical(storage,
"%s: next_uid was lowered (%u -> %u, hdr=%u)",
uidlist->path, orig_next_uid,
uidlist->next_uid, uidlist->hdr_next_uid);
uidlist->recreate = TRUE;
uidlist->next_uid = orig_next_uid;
}
}
if (ret == 0) {
/* file is broken */
(void)unlink(uidlist->path);
} else if (ret > 0) {
/* success */
uidlist->fd = fd;
uidlist->fd_dev = st.st_dev;
uidlist->fd_ino = st.st_ino;
uidlist->fd_size = st.st_size;
uidlist->last_read_offset = input->v_offset;
maildir_uidlist_update_hdr(uidlist, &st);
} else if (!*retry_r) {
/* I/O error */
if (input->stream_errno == ESTALE && try_retry)
*retry_r = TRUE;
else {
errno = input->stream_errno;
mail_storage_set_critical(storage,
"read(%s) failed: %m", uidlist->path);
}
uidlist->last_read_offset = 0;
}
i_stream_destroy(&input);
if (ret <= 0) {
if (close(fd) < 0) {
mail_storage_set_critical(storage,
"close(%s) failed: %m", uidlist->path);
}
}
return ret;
}
static int
maildir_uidlist_stat(struct maildir_uidlist *uidlist, struct stat *st_r)
{
struct mail_storage *storage = uidlist->ibox->box.storage;
if (storage->set->mail_nfs_storage) {
nfs_flush_file_handle_cache(uidlist->path);
nfs_flush_attr_cache_unlocked(uidlist->path);
}
if (nfs_safe_stat(uidlist->path, st_r) < 0) {
if (errno != ENOENT) {
mail_storage_set_critical(storage,
"stat(%s) failed: %m", uidlist->path);
return -1;
}
return 0;
}
return 1;
}
static int
maildir_uidlist_has_changed(struct maildir_uidlist *uidlist, bool *recreated_r)
{
struct mail_storage *storage = uidlist->ibox->box.storage;
struct stat st;
int ret;
*recreated_r = FALSE;
if ((ret = maildir_uidlist_stat(uidlist, &st)) <= 0)
return ret;
if (st.st_ino != uidlist->fd_ino ||
!CMP_DEV_T(st.st_dev, uidlist->fd_dev)) {
/* file recreated */
*recreated_r = TRUE;
return 1;
}
if (storage->set->mail_nfs_storage) {
/* NFS: either the file hasn't been changed, or it has already
been deleted and the inodes just happen to be the same.
check if the fd is still valid. */
if (fstat(uidlist->fd, &st) < 0) {
if (errno == ESTALE) {
*recreated_r = TRUE;
return 1;
}
mail_storage_set_critical(storage,
"fstat(%s) failed: %m", uidlist->path);
return -1;
}
}
if (st.st_size != uidlist->fd_size) {
/* file modified but not recreated */
return 1;
} else {
/* unchanged */
return 0;
}
}
int maildir_uidlist_refresh(struct maildir_uidlist *uidlist)
{
unsigned int i;
bool retry, recreated;
int ret;
if (uidlist->fd != -1) {
ret = maildir_uidlist_has_changed(uidlist, &recreated);
if (ret <= 0) {
if (UIDLIST_IS_LOCKED(uidlist))
uidlist->locked_refresh = TRUE;
return ret;
}
if (recreated)
maildir_uidlist_close(uidlist);
}
for (i = 0; ; i++) {
ret = maildir_uidlist_update_read(uidlist, &retry,
i < UIDLIST_ESTALE_RETRY_COUNT);
if (!retry)
break;
/* ESTALE - try reopening and rereading */
maildir_uidlist_close(uidlist);
}
if (ret >= 0) {
uidlist->initial_read = TRUE;
uidlist->initial_hdr_read = TRUE;
if (UIDLIST_IS_LOCKED(uidlist))
uidlist->locked_refresh = TRUE;
if (!uidlist->have_mailbox_guid) {
uidlist->recreate = TRUE;
(void)maildir_uidlist_update(uidlist);
}
}
return ret;
}
int maildir_uidlist_refresh_fast_init(struct maildir_uidlist *uidlist)
{
const struct maildir_index_header *mhdr = &uidlist->mbox->maildir_hdr;
struct mail_index *index = uidlist->mbox->ibox.index;
struct mail_index_view *view;
const struct mail_index_header *hdr;
struct stat st;
int ret;
i_assert(UIDLIST_IS_LOCKED(uidlist));
if (uidlist->fd != -1)
return maildir_uidlist_refresh(uidlist);
if ((ret = maildir_uidlist_stat(uidlist, &st)) < 0)
return ret;
if (st.st_size == mhdr->uidlist_size &&
st.st_mtime == (time_t)mhdr->uidlist_mtime &&
ST_NTIMES_EQUAL(ST_MTIME_NSEC(st), mhdr->uidlist_mtime_nsecs) &&
(!mail_index_is_in_memory(index) || st.st_mtime < ioloop_time-1)) {
/* index is up-to-date. look up the uidvalidity and next-uid
from it. we'll need to create a new view temporarily to
make sure we get the latest values. */
view = mail_index_view_open(index);
hdr = mail_index_get_header(view);
uidlist->uid_validity = hdr->uid_validity;
uidlist->next_uid = hdr->next_uid;
uidlist->initial_hdr_read = TRUE;
mail_index_view_close(&view);
return 1;
} else {
return maildir_uidlist_refresh(uidlist);
}
}
static int
maildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
unsigned int *idx_r,
struct maildir_uidlist_rec **rec_r)
{
struct maildir_uidlist_rec *const *recs;
unsigned int idx, left_idx, right_idx;
if (!uidlist->initial_read) {
/* first time we need to read uidlist */
if (maildir_uidlist_refresh(uidlist) < 0)
return -1;
}
idx = left_idx = 0;
recs = array_get(&uidlist->records, &right_idx);
while (left_idx < right_idx) {
idx = (left_idx + right_idx) / 2;
if (recs[idx]->uid < uid)
left_idx = idx+1;
else if (recs[idx]->uid > uid)
right_idx = idx;
else {
*idx_r = idx;
*rec_r = recs[idx];
return 1;
}
}
if (idx > 0) idx--;
*idx_r = idx;
return 0;
}
int maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
enum maildir_uidlist_rec_flag *flags_r,
const char **fname_r)
{
int ret;
ret = maildir_uidlist_lookup_nosync(uidlist, uid, flags_r, fname_r);
if (ret <= 0) {
if (ret < 0)
return -1;
if (uidlist->fd != -1 || uidlist->mbox == NULL) {
/* refresh uidlist and check again in case it was added
after the last mailbox sync */
if (maildir_uidlist_refresh(uidlist) < 0)
return -1;
} else {
/* the uidlist doesn't exist. */
if (maildir_storage_sync_force(uidlist->mbox, uid) < 0)
return -1;
}
/* try again */
ret = maildir_uidlist_lookup_nosync(uidlist, uid,
flags_r, fname_r);
}
return ret;
}
int maildir_uidlist_lookup_nosync(struct maildir_uidlist *uidlist, uint32_t uid,
enum maildir_uidlist_rec_flag *flags_r,
const char **fname_r)
{
struct maildir_uidlist_rec *rec;
unsigned int idx;
int ret;
if ((ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec)) <= 0)
return ret;
*flags_r = rec->flags;
*fname_r = rec->filename;
return 1;
}
const char *
maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
enum maildir_uidlist_rec_ext_key key)
{
struct maildir_uidlist_rec *rec;
unsigned int idx;
const unsigned char *p;
int ret;
ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec);
if (ret <= 0 || rec->extensions == NULL)
return NULL;
p = rec->extensions;
while (*p != '\0') {
/* <key><value>\0 */
if (*p == (char)key)
return (const char *)p + 1;
p += strlen((const char *)p) + 1;
}
return NULL;
}
uint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
{
return uidlist->uid_validity;
}
uint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist)
{
return !uidlist->initial_hdr_read ? 0 : uidlist->next_uid;
}
int maildir_uidlist_get_mailbox_guid(struct maildir_uidlist *uidlist,
uint8_t mailbox_guid[MAILBOX_GUID_SIZE])
{
if (!uidlist->have_mailbox_guid) {
uidlist->recreate = TRUE;
if (maildir_uidlist_update(uidlist) < 0)
return -1;
}
memcpy(mailbox_guid, uidlist->mailbox_guid, MAILBOX_GUID_SIZE);
return 0;
}
void maildir_uidlist_set_mailbox_guid(struct maildir_uidlist *uidlist,
const uint8_t mailbox_guid[MAILBOX_GUID_SIZE])
{
if (memcmp(uidlist->mailbox_guid, mailbox_guid,
sizeof(uidlist->mailbox_guid)) != 0) {
memcpy(uidlist->mailbox_guid, mailbox_guid,
sizeof(uidlist->mailbox_guid));
uidlist->recreate = TRUE;
}
}
void maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist,
uint32_t uid_validity)
{
i_assert(uid_validity != 0);
if (uid_validity != uidlist->uid_validity) {
uidlist->uid_validity = uid_validity;
uidlist->recreate = TRUE;
}
}
void maildir_uidlist_set_next_uid(struct maildir_uidlist *uidlist,
uint32_t next_uid, bool force)
{
if (uidlist->next_uid < next_uid || force) {
i_assert(next_uid != 0);
uidlist->next_uid = next_uid;
uidlist->recreate = TRUE;
}
}
static void
maildir_uidlist_set_ext_real(struct maildir_uidlist *uidlist, uint32_t uid,
enum maildir_uidlist_rec_ext_key key,
const char *value)
{
struct maildir_uidlist_rec *rec;
unsigned int idx;
const unsigned char *p;
buffer_t *buf;
unsigned int len;
int ret;
ret = maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec);
if (ret <= 0) {
if (ret < 0)
return;
/* maybe it's a new message */
if (maildir_uidlist_refresh(uidlist) < 0)
return;
if (maildir_uidlist_lookup_rec(uidlist, uid, &idx, &rec) <= 0) {
/* message is already expunged, ignore */
return;
}
}
buf = buffer_create_dynamic(pool_datastack_create(), 128);
/* copy existing extensions, except for the one we're updating */
if (rec->extensions != NULL) {
p = rec->extensions;
while (*p != '\0') {
/* <key><value>\0 */
len = strlen((const char *)p) + 1;
if (*p != (char)key)
buffer_append(buf, p, len);
p += len;
}
}
if (value != NULL) {
buffer_append_c(buf, key);
buffer_append(buf, value, strlen(value) + 1);
}
buffer_append_c(buf, '\0');
rec->extensions = p_malloc(uidlist->record_pool, buf->used);
memcpy(rec->extensions, buf->data, buf->used);
if (rec->uid != (uint32_t)-1) {
/* message already exists in uidlist, need to recreate it */
uidlist->recreate = TRUE;
}
}
void maildir_uidlist_set_ext(struct maildir_uidlist *uidlist, uint32_t uid,
enum maildir_uidlist_rec_ext_key key,
const char *value)
{
T_BEGIN {
maildir_uidlist_set_ext_real(uidlist, uid, key, value);
} T_END;
}
static void
maildir_uidlist_generate_uid_validity(struct maildir_uidlist *uidlist)
{
const struct mail_index_header *hdr;
if (uidlist->ibox->box.opened) {
hdr = mail_index_get_header(uidlist->ibox->view);
if (hdr->uid_validity != 0) {
uidlist->uid_validity = hdr->uid_validity;
return;
}
}
uidlist->uid_validity =
maildir_get_uidvalidity_next(uidlist->ibox->box.list);
}
static int maildir_uidlist_write_fd(struct maildir_uidlist *uidlist, int fd,
const char *path, unsigned int first_idx,
uoff_t *file_size_r)
{
struct mail_storage *storage = uidlist->ibox->box.storage;
struct maildir_uidlist_iter_ctx *iter;
struct ostream *output;
struct maildir_uidlist_rec *rec;
string_t *str;
const unsigned char *p;
unsigned int len;
int ret;
i_assert(fd != -1);
output = o_stream_create_fd_file(fd, (uoff_t)-1, FALSE);
o_stream_cork(output);
str = t_str_new(512);
if (output->offset == 0) {
i_assert(first_idx == 0);
uidlist->version = UIDLIST_VERSION;
if (uidlist->uid_validity == 0)
maildir_uidlist_generate_uid_validity(uidlist);
if (!uidlist->have_mailbox_guid)
mail_generate_guid_128(uidlist->mailbox_guid);
i_assert(uidlist->next_uid > 0);
str_printfa(str, "%u V%u N%u G%s", uidlist->version,
uidlist->uid_validity, uidlist->next_uid,
binary_to_hex(uidlist->mailbox_guid,
sizeof(uidlist->mailbox_guid)));
if (str_len(uidlist->hdr_extensions) > 0) {
str_append_c(str, ' ');
str_append_str(str, uidlist->hdr_extensions);
}
str_append_c(str, '\n');
o_stream_send(output, str_data(str), str_len(str));
}
iter = maildir_uidlist_iter_init(uidlist);
i_assert(first_idx <= array_count(&uidlist->records));
iter->next += first_idx;
while (maildir_uidlist_iter_next_rec(iter, &rec)) {
uidlist->read_records_count++;
str_truncate(str, 0);
str_printfa(str, "%u", rec->uid);
if (rec->extensions != NULL) {
for (p = rec->extensions; *p != '\0'; ) {
len = strlen((const char *)p);
str_append_c(str, ' ');
str_append_n(str, p, len);
p += len + 1;
}
}
str_printfa(str, " :%s\n", rec->filename);
o_stream_send(output, str_data(str), str_len(str));
}
maildir_uidlist_iter_deinit(&iter);
o_stream_flush(output);
ret = output->stream_errno == 0 ? 0 : -1;
*file_size_r = output->offset;
o_stream_unref(&output);
if (ret < 0) {
mail_storage_set_critical(storage,
"o_stream_send(%s) failed: %m", path);
return -1;
}
if (!storage->set->fsync_disable) {
if (fdatasync(fd) < 0) {
mail_storage_set_critical(storage,
"fdatasync(%s) failed: %m", path);
return -1;
}
}
return 0;
}
static int maildir_uidlist_recreate(struct maildir_uidlist *uidlist)
{
struct mailbox *box = &uidlist->ibox->box;
const char *control_dir, *temp_path;
struct stat st;
mode_t old_mask;
uoff_t file_size;
int i, fd, ret;
i_assert(uidlist->initial_read);
control_dir = mailbox_list_get_path(box->list, box->name,
MAILBOX_LIST_PATH_TYPE_CONTROL);
temp_path = t_strconcat(control_dir,
"/" MAILDIR_UIDLIST_NAME ".tmp", NULL);
for (i = 0;; i++) {
old_mask = umask(0777 & ~box->file_create_mode);
fd = open(temp_path, O_RDWR | O_CREAT | O_TRUNC, 0777);
umask(old_mask);
if (fd != -1)
break;
if (errno != ENOENT || i == MAILDIR_DELETE_RETRY_COUNT ||
uidlist->mbox == NULL) {
mail_storage_set_critical(box->storage,
"open(%s, O_CREAT) failed: %m", temp_path);
return -1;
}
/* the control dir doesn't exist. create it unless the whole
mailbox was just deleted. */
if (!maildir_set_deleted(&uidlist->mbox->ibox.box))
return -1;
}
if (box->file_create_gid != (gid_t)-1 &&
fchown(fd, (uid_t)-1, box->file_create_gid) < 0) {
if (errno == EPERM) {
mail_storage_set_critical(box->storage, "%s",
eperm_error_get_chgrp("fchown", temp_path,
box->file_create_gid,
box->file_create_gid_origin));
} else {
mail_storage_set_critical(box->storage,
"fchown(%s) failed: %m", temp_path);
}
}
uidlist->read_records_count = 0;
ret = maildir_uidlist_write_fd(uidlist, fd, temp_path, 0, &file_size);
if (ret == 0) {
if (rename(temp_path, uidlist->path) < 0) {
mail_storage_set_critical(box->storage,
"rename(%s, %s) failed: %m",
temp_path, uidlist->path);
ret = -1;
}
}
if (ret < 0) {
if (unlink(temp_path) < 0) {
mail_storage_set_critical(box->storage,
"unlink(%s) failed: %m", temp_path);
}
} else if (fstat(fd, &st) < 0) {
mail_storage_set_critical(box->storage,
"fstat(%s) failed: %m", temp_path);
ret = -1;
} else if (file_size != (uoff_t)st.st_size) {
i_assert(!file_dotlock_is_locked(uidlist->dotlock));
mail_storage_set_critical(box->storage,
"Maildir uidlist dotlock overridden: %s",
uidlist->path);
ret = -1;
} else {
maildir_uidlist_close(uidlist);
uidlist->fd = fd;
uidlist->fd_dev = st.st_dev;
uidlist->fd_ino = st.st_ino;
uidlist->fd_size = st.st_size;
uidlist->last_read_offset = st.st_size;
uidlist->recreate = FALSE;
uidlist->have_mailbox_guid = TRUE;
maildir_uidlist_update_hdr(uidlist, &st);
}
if (ret < 0)
(void)close(fd);
return ret;
}
int maildir_uidlist_update(struct maildir_uidlist *uidlist)
{
int ret;
if (!uidlist->recreate)
return 0;
if (maildir_uidlist_lock(uidlist) <= 0)
return -1;
ret = maildir_uidlist_recreate(uidlist);
maildir_uidlist_unlock(uidlist);
return ret;
}
static bool maildir_uidlist_want_compress(struct maildir_uidlist_sync_ctx *ctx)
{
unsigned int min_rewrite_count;
if (!ctx->uidlist->locked_refresh)
return FALSE;
if (ctx->uidlist->recreate)
return TRUE;
min_rewrite_count =
(ctx->uidlist->read_records_count + ctx->new_files_count) *
UIDLIST_COMPRESS_PERCENTAGE / 100;
return min_rewrite_count >= array_count(&ctx->uidlist->records);
}
static bool maildir_uidlist_want_recreate(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
if (!uidlist->locked_refresh)
return FALSE;
if (ctx->finish_change_counter != uidlist->change_counter)
return TRUE;
if (uidlist->fd == -1 || uidlist->version != UIDLIST_VERSION ||
!uidlist->have_mailbox_guid)
return TRUE;
return maildir_uidlist_want_compress(ctx);
}
static int maildir_uidlist_sync_update(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct mail_storage *storage = uidlist->ibox->box.storage;
struct stat st;
uoff_t file_size;
if (maildir_uidlist_want_recreate(ctx))
return maildir_uidlist_recreate(uidlist);
if (uidlist->fd == -1) {
/* NOREFRESH flag used. we're just appending some messages. */
i_assert(uidlist->initial_hdr_read);
uidlist->fd = nfs_safe_open(uidlist->path, O_RDWR);
if (uidlist->fd == -1) {
mail_storage_set_critical(storage,
"open(%s) failed: %m", uidlist->path);
return -1;
}
}
i_assert(ctx->first_unwritten_pos != (unsigned int)-1);
if (lseek(uidlist->fd, 0, SEEK_END) < 0) {
mail_storage_set_critical(storage,
"lseek(%s) failed: %m", uidlist->path);
return -1;
}
if (maildir_uidlist_write_fd(uidlist, uidlist->fd, uidlist->path,
ctx->first_unwritten_pos, &file_size) < 0)
return -1;
if (fstat(uidlist->fd, &st) < 0) {
mail_storage_set_critical(storage,
"fstat(%s) failed: %m", uidlist->path);
return -1;
}
if ((uoff_t)st.st_size != file_size) {
i_warning("%s: file size changed unexpectedly after write",
uidlist->path);
} else if (uidlist->locked_refresh) {
uidlist->fd_size = st.st_size;
uidlist->last_read_offset = st.st_size;
maildir_uidlist_update_hdr(uidlist, &st);
}
return 0;
}
static void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
bool nonsynced)
{
struct maildir_uidlist_rec **recs;
unsigned int i, count;
recs = array_get_modifiable(&uidlist->records, &count);
if (nonsynced) {
for (i = 0; i < count; i++)
recs[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
} else {
for (i = 0; i < count; i++)
recs[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
}
}
static int maildir_uidlist_sync_lock(struct maildir_uidlist *uidlist,
enum maildir_uidlist_sync_flags sync_flags,
bool *locked_r)
{
bool nonblock, refresh;
int ret;
*locked_r = FALSE;
if ((sync_flags & MAILDIR_UIDLIST_SYNC_NOLOCK) != 0) {
if (maildir_uidlist_refresh(uidlist) < 0)
return -1;
return 1;
}
nonblock = (sync_flags & MAILDIR_UIDLIST_SYNC_TRYLOCK) != 0;
refresh = (sync_flags & MAILDIR_UIDLIST_SYNC_NOREFRESH) == 0;
ret = maildir_uidlist_lock_timeout(uidlist, nonblock, refresh, refresh);
if (ret <= 0) {
if (ret < 0 || !nonblock)
return ret;
/* couldn't lock it */
if ((sync_flags & MAILDIR_UIDLIST_SYNC_FORCE) == 0)
return 0;
/* forcing the sync anyway */
if (maildir_uidlist_refresh(uidlist) < 0)
return -1;
} else {
*locked_r = TRUE;
}
return 1;
}
void maildir_uidlist_set_all_nonsynced(struct maildir_uidlist *uidlist)
{
maildir_uidlist_mark_all(uidlist, TRUE);
}
int maildir_uidlist_sync_init(struct maildir_uidlist *uidlist,
enum maildir_uidlist_sync_flags sync_flags,
struct maildir_uidlist_sync_ctx **sync_ctx_r)
{
struct maildir_uidlist_sync_ctx *ctx;
bool locked;
int ret;
ret = maildir_uidlist_sync_lock(uidlist, sync_flags, &locked);
if (ret <= 0)
return ret;
*sync_ctx_r = ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
ctx->uidlist = uidlist;
ctx->sync_flags = sync_flags;
ctx->partial = (!locked && ctx->uidlist->mbox != NULL) ||
(sync_flags & MAILDIR_UIDLIST_SYNC_PARTIAL) != 0;
ctx->locked = locked;
ctx->first_unwritten_pos = (unsigned int)-1;
ctx->first_nouid_pos = (unsigned int)-1;
if (ctx->partial) {
if ((sync_flags & MAILDIR_UIDLIST_SYNC_KEEP_STATE) == 0) {
/* initially mark all nonsynced */
maildir_uidlist_mark_all(uidlist, TRUE);
}
return 1;
}
i_assert(uidlist->locked_refresh);
ctx->record_pool = pool_alloconly_create(MEMPOOL_GROWING
"maildir_uidlist_sync", 16384);
ctx->files = hash_table_create(default_pool, ctx->record_pool, 4096,
maildir_filename_base_hash,
maildir_filename_base_cmp);
i_array_init(&ctx->records, array_count(&uidlist->records));
return 1;
}
static void
maildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec *rec;
/* we'll update uidlist directly */
rec = hash_table_lookup(uidlist->files, filename);
if (rec == NULL) {
/* doesn't exist in uidlist */
if (!ctx->locked) {
/* we can't add it, so just ignore it */
return;
}
if (ctx->first_nouid_pos == (unsigned int)-1)
ctx->first_nouid_pos = array_count(&uidlist->records);
ctx->new_files_count++;
ctx->changed = TRUE;
if (uidlist->record_pool == NULL) {
uidlist->record_pool =
pool_alloconly_create(MEMPOOL_GROWING
"uidlist record_pool",
1024);
}
rec = p_new(uidlist->record_pool,
struct maildir_uidlist_rec, 1);
rec->uid = (uint32_t)-1;
array_append(&uidlist->records, &rec, 1);
uidlist->change_counter++;
}
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
rec->filename = p_strdup(uidlist->record_pool, filename);
hash_table_insert(uidlist->files, rec->filename, rec);
ctx->finished = FALSE;
}
static unsigned char *ext_dup(pool_t pool, const unsigned char *extensions)
{
unsigned char *ret;
if (extensions == NULL)
return NULL;
T_BEGIN {
unsigned int len;
for (len = 0; extensions[len] != '\0'; len++) {
while (extensions[len] != '\0') len++;
}
ret = p_malloc(pool, len + 1);
memcpy(ret, extensions, len);
} T_END;
return ret;
}
int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec *rec, *old_rec;
const char *p, *dir;
if (ctx->failed)
return -1;
for (p = filename; *p != '\0'; p++) {
if (*p == 13 || *p == 10) {
struct mailbox *box = &uidlist->ibox->box;
dir = mailbox_list_get_path(box->list, box->name,
MAILBOX_LIST_PATH_TYPE_MAILBOX);
i_warning("Maildir %s: Ignoring a file with #0x%x: %s",
dir, *p, filename);
return 1;
}
}
if (ctx->partial) {
maildir_uidlist_sync_next_partial(ctx, filename, flags);
return 1;
}
rec = hash_table_lookup(ctx->files, filename);
if (rec != NULL) {
if ((rec->flags & (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
/* possibly duplicate */
return 0;
}
/* probably was in new/ and now we're seeing it in cur/.
remove new/moved flags so if this happens again we'll know
to check for duplicates. */
rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
MAILDIR_UIDLIST_REC_FLAG_MOVED);
} else {
old_rec = hash_table_lookup(uidlist->files, filename);
i_assert(old_rec != NULL || UIDLIST_ALLOW_WRITING(uidlist));
rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
if (old_rec != NULL) {
*rec = *old_rec;
rec->extensions =
ext_dup(ctx->record_pool, rec->extensions);
} else {
rec->uid = (uint32_t)-1;
ctx->new_files_count++;
ctx->changed = TRUE;
/* didn't exist in uidlist, it's recent */
flags |= MAILDIR_UIDLIST_REC_FLAG_RECENT;
}
array_append(&ctx->records, &rec, 1);
}
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
rec->filename = p_strdup(ctx->record_pool, filename);
hash_table_insert(ctx->files, rec->filename, rec);
return 1;
}
void maildir_uidlist_sync_remove(struct maildir_uidlist_sync_ctx *ctx,
const char *filename)
{
struct maildir_uidlist_rec *rec;
unsigned int idx;
i_assert(ctx->partial);
i_assert(ctx->uidlist->locked_refresh);
rec = hash_table_lookup(ctx->uidlist->files, filename);
i_assert(rec != NULL);
i_assert(rec->uid != (uint32_t)-1);
hash_table_remove(ctx->uidlist->files, filename);
idx = maildir_uidlist_records_array_delete(ctx->uidlist, rec);
if (ctx->first_unwritten_pos != (unsigned int)-1) {
i_assert(ctx->first_unwritten_pos > idx);
ctx->first_unwritten_pos--;
}
if (ctx->first_nouid_pos != (unsigned int)-1) {
i_assert(ctx->first_nouid_pos > idx);
ctx->first_nouid_pos--;
}
ctx->changed = TRUE;
ctx->uidlist->recreate = TRUE;
}
const char *
maildir_uidlist_sync_get_full_filename(struct maildir_uidlist_sync_ctx *ctx,
const char *filename)
{
struct maildir_uidlist_rec *rec;
rec = hash_table_lookup(ctx->files, filename);
return rec == NULL ? NULL : rec->filename;
}
bool maildir_uidlist_get_uid(struct maildir_uidlist *uidlist,
const char *filename, uint32_t *uid_r)
{
struct maildir_uidlist_rec *rec;
rec = hash_table_lookup(uidlist->files, filename);
if (rec == NULL)
return FALSE;
*uid_r = rec->uid;
return TRUE;
}
const char *
maildir_uidlist_get_full_filename(struct maildir_uidlist *uidlist,
const char *filename)
{
struct maildir_uidlist_rec *rec;
rec = hash_table_lookup(uidlist->files, filename);
return rec == NULL ? NULL : rec->filename;
}
static int maildir_time_cmp(const void *p1, const void *p2)
{
const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
return maildir_filename_sort_cmp((*rec1)->filename, (*rec2)->filename);
}
static void maildir_uidlist_assign_uids(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist_rec **recs;
unsigned int dest, count;
i_assert(UIDLIST_ALLOW_WRITING(ctx->uidlist));
i_assert(ctx->first_nouid_pos != (unsigned int)-1);
if (ctx->first_unwritten_pos == (unsigned int)-1)
ctx->first_unwritten_pos = ctx->first_nouid_pos;
/* sort new files and assign UIDs for them */
recs = array_get_modifiable(&ctx->uidlist->records, &count);
qsort(recs + ctx->first_nouid_pos, count - ctx->first_nouid_pos,
sizeof(*recs), maildir_time_cmp);
for (dest = ctx->first_nouid_pos; dest < count; dest++) {
i_assert(recs[dest]->uid == (uint32_t)-1);
i_assert(ctx->uidlist->next_uid < (uint32_t)-1);
recs[dest]->uid = ctx->uidlist->next_uid++;
recs[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
}
if (ctx->uidlist->locked_refresh)
ctx->uidlist->last_seen_uid = ctx->uidlist->next_uid-1;
ctx->new_files_count = 0;
ctx->first_nouid_pos = (unsigned int)-1;
ctx->uidlist->change_counter++;
ctx->finish_change_counter = ctx->uidlist->change_counter;
}
static void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
/* buffer is unsorted, sort it by UID */
array_sort(&ctx->records, maildir_uid_cmp);
array_free(&uidlist->records);
uidlist->records = ctx->records;
ctx->records.arr.buffer = NULL;
hash_table_destroy(&uidlist->files);
uidlist->files = ctx->files;
ctx->files = NULL;
if (uidlist->record_pool != NULL)
pool_unref(&uidlist->record_pool);
uidlist->record_pool = ctx->record_pool;
ctx->record_pool = NULL;
if (ctx->new_files_count != 0) {
ctx->first_nouid_pos = array_count(&uidlist->records) -
ctx->new_files_count;
maildir_uidlist_assign_uids(ctx);
} else {
ctx->uidlist->change_counter++;
}
}
void maildir_uidlist_sync_recreate(struct maildir_uidlist_sync_ctx *ctx)
{
ctx->uidlist->recreate = TRUE;
}
void maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx)
{
if (!ctx->partial) {
if (!ctx->failed)
maildir_uidlist_swap(ctx);
} else {
if (ctx->new_files_count != 0 && !ctx->failed) {
i_assert(ctx->changed);
i_assert(ctx->locked);
maildir_uidlist_assign_uids(ctx);
}
}
ctx->finished = TRUE;
/* mbox=NULL means we're coming from dbox rebuilding code.
the dbox is already locked, so allow uidlist recreation */
i_assert(ctx->locked || !ctx->changed || ctx->uidlist->mbox == NULL);
if ((ctx->changed || maildir_uidlist_want_compress(ctx)) &&
!ctx->failed && (ctx->locked || ctx->uidlist->mbox == NULL)) {
T_BEGIN {
if (maildir_uidlist_sync_update(ctx) < 0) {
/* we couldn't write everything we wanted. make
sure we don't continue using those UIDs */
maildir_uidlist_reset(ctx->uidlist);
ctx->failed = TRUE;
}
} T_END;
}
}
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx **_ctx,
bool success)
{
struct maildir_uidlist_sync_ctx *ctx = *_ctx;
int ret;
*_ctx = NULL;
if (!success)
ctx->failed = TRUE;
ret = ctx->failed ? -1 : 0;
if (!ctx->finished)
maildir_uidlist_sync_finish(ctx);
if (ctx->partial)
maildir_uidlist_mark_all(ctx->uidlist, FALSE);
if (ctx->locked)
maildir_uidlist_unlock(ctx->uidlist);
if (ctx->files != NULL)
hash_table_destroy(&ctx->files);
if (ctx->record_pool != NULL)
pool_unref(&ctx->record_pool);
if (array_is_created(&ctx->records))
array_free(&ctx->records);
i_free(ctx);
return ret;
}
void maildir_uidlist_add_flags(struct maildir_uidlist *uidlist,
const char *filename,
enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist_rec *rec;
rec = hash_table_lookup(uidlist->files, filename);
i_assert(rec != NULL);
rec->flags |= flags;
}
struct maildir_uidlist_iter_ctx *
maildir_uidlist_iter_init(struct maildir_uidlist *uidlist)
{
struct maildir_uidlist_iter_ctx *ctx;
unsigned int count;
ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
ctx->uidlist = uidlist;
ctx->next = array_get(&uidlist->records, &count);
ctx->end = ctx->next + count;
ctx->change_counter = ctx->uidlist->change_counter;
return ctx;
}
static void
maildir_uidlist_iter_update_idx(struct maildir_uidlist_iter_ctx *ctx)
{
unsigned int old_rev_idx, idx, count;
old_rev_idx = ctx->end - ctx->next;
ctx->next = array_get(&ctx->uidlist->records, &count);
ctx->end = ctx->next + count;
idx = old_rev_idx >= count ? 0 :
count - old_rev_idx;
while (idx < count && ctx->next[idx]->uid <= ctx->prev_uid)
idx++;
while (idx > 0 && ctx->next[idx-1]->uid > ctx->prev_uid)
idx--;
ctx->next += idx;
}
static bool maildir_uidlist_iter_next_rec(struct maildir_uidlist_iter_ctx *ctx,
struct maildir_uidlist_rec **rec_r)
{
struct maildir_uidlist_rec *rec;
if (ctx->change_counter != ctx->uidlist->change_counter)
maildir_uidlist_iter_update_idx(ctx);
if (ctx->next == ctx->end)
return FALSE;
rec = *ctx->next;
i_assert(rec->uid != (uint32_t)-1);
ctx->prev_uid = rec->uid;
ctx->next++;
*rec_r = rec;
return TRUE;
}
bool maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
uint32_t *uid_r,
enum maildir_uidlist_rec_flag *flags_r,
const char **filename_r)
{
struct maildir_uidlist_rec *rec;
if (!maildir_uidlist_iter_next_rec(ctx, &rec))
return FALSE;
*uid_r = rec->uid;
*flags_r = rec->flags;
*filename_r = rec->filename;
return TRUE;
}
void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx **_ctx)
{
i_free(*_ctx);
*_ctx = NULL;
}