maildir-uidlist.c revision 7fe735511b5c0ab554e6cc986f35b1e24453156e
1516N/A/* Copyright (C) 2003 Timo Sirainen */
50N/A
50N/A#include "lib.h"
50N/A#include "ioloop.h"
50N/A#include "buffer.h"
50N/A#include "hash.h"
50N/A#include "istream.h"
50N/A#include "str.h"
50N/A#include "file-dotlock.h"
50N/A#include "write-full.h"
50N/A#include "maildir-storage.h"
50N/A#include "maildir-uidlist.h"
50N/A
50N/A#include <stdio.h>
50N/A#include <stdlib.h>
50N/A#include <sys/stat.h>
50N/A#include <utime.h>
50N/A
50N/A/* how many seconds to wait before overriding uidlist.lock */
50N/A#define UIDLIST_LOCK_STALE_TIMEOUT (60*5)
50N/A
50N/A#define UIDLIST_IS_LOCKED(uidlist) \
2026N/A ((uidlist)->lock_fd != -1)
3158N/A
2026N/Astruct maildir_uidlist_rec {
50N/A uint32_t uid;
2627N/A uint32_t flags;
3159N/A char *filename;
202N/A};
2690N/A
3159N/Astruct maildir_uidlist {
3159N/A struct index_mailbox *ibox;
1431N/A char *fname;
1755N/A int lock_fd;
1352N/A
1618N/A time_t last_mtime;
2690N/A
72N/A pool_t record_pool;
2690N/A buffer_t *record_buf;
2690N/A struct hash_table *files;
2690N/A
1431N/A unsigned int version;
50N/A unsigned int uid_validity, next_uid, last_read_uid;
2690N/A uint32_t first_recent_uid;
1618N/A
50N/A unsigned int initial_read:1;
50N/A unsigned int initial_sync:1;
50N/A};
66N/A
135N/Astruct maildir_uidlist_sync_ctx {
66N/A struct maildir_uidlist *uidlist;
66N/A
50N/A pool_t record_pool;
2205N/A buffer_t *record_buf;
2627N/A struct hash_table *files;
2690N/A
2690N/A unsigned int first_new_pos;
2627N/A unsigned int new_files_count;
2627N/A
2627N/A unsigned int partial:1;
2627N/A unsigned int finished:1;
2627N/A unsigned int failed:1;
2627N/A};
2627N/A
2627N/Astruct maildir_uidlist_iter_ctx {
2627N/A const struct maildir_uidlist_rec *const *next, *const *end;
2627N/A};
2627N/A
2205N/Astatic int maildir_uidlist_lock_timeout(struct maildir_uidlist *uidlist,
1846N/A unsigned int timeout)
2690N/A{
2690N/A const char *path;
2690N/A mode_t old_mask;
2690N/A int fd;
2690N/A
2690N/A if (UIDLIST_IS_LOCKED(uidlist))
2690N/A return 1;
2690N/A
2690N/A path = t_strconcat(uidlist->ibox->control_dir,
2690N/A "/" MAILDIR_UIDLIST_NAME, NULL);
2690N/A old_mask = umask(0777 & ~uidlist->ibox->mail_create_mode);
2690N/A fd = file_dotlock_open(path, uidlist->ibox->storage->temp_prefix,
2690N/A NULL, timeout, 0, UIDLIST_LOCK_STALE_TIMEOUT,
2690N/A NULL, NULL);
2690N/A umask(old_mask);
2690N/A if (fd == -1) {
2690N/A if (errno == EAGAIN)
2690N/A return 0;
2690N/A mail_storage_set_critical(uidlist->ibox->box.storage,
2690N/A "file_dotlock_open(%s) failed: %m", path);
2690N/A return -1;
2690N/A }
2690N/A uidlist->lock_fd = fd;
2690N/A
2690N/A /* our view of uidlist must be up-to-date if we plan on changing it */
2690N/A if (maildir_uidlist_update(uidlist) < 0)
2690N/A return -1;
2690N/A
2690N/A return 1;
2690N/A}
2690N/A
2690N/Aint maildir_uidlist_lock(struct maildir_uidlist *uidlist)
3105N/A{
2690N/A return maildir_uidlist_lock_timeout(uidlist,
2690N/A UIDLIST_LOCK_STALE_TIMEOUT);
2690N/A}
2690N/A
2690N/Aint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
2690N/A{
2690N/A return maildir_uidlist_lock_timeout(uidlist, 0);
2690N/A}
2690N/A
2690N/Avoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
2690N/A{
2690N/A const char *path;
2690N/A
2627N/A if (!UIDLIST_IS_LOCKED(uidlist))
50N/A return;
2054N/A
50N/A path = t_strconcat(uidlist->ibox->control_dir,
1846N/A "/" MAILDIR_UIDLIST_NAME, NULL);
2054N/A (void)file_dotlock_delete(path, NULL, uidlist->lock_fd);
1846N/A uidlist->lock_fd = -1;
1846N/A}
50N/A
1846N/Astruct maildir_uidlist *maildir_uidlist_init(struct index_mailbox *ibox)
235N/A{
2690N/A struct maildir_uidlist *uidlist;
2690N/A
2453N/A uidlist = i_new(struct maildir_uidlist, 1);
1846N/A uidlist->ibox = ibox;
1846N/A uidlist->fname =
2205N/A i_strconcat(ibox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
2690N/A uidlist->lock_fd = -1;
2690N/A uidlist->record_buf =
2690N/A buffer_create_dynamic(default_pool, 512, (size_t)-1);
2690N/A uidlist->files = hash_create(default_pool, default_pool, 4096,
2690N/A maildir_hash, maildir_cmp);
2690N/A
2690N/A uidlist->next_uid = 1;
2690N/A
2690N/A return uidlist;
2690N/A}
2690N/A
2690N/Avoid maildir_uidlist_deinit(struct maildir_uidlist *uidlist)
2690N/A{
2690N/A i_assert(!UIDLIST_IS_LOCKED(uidlist));
2690N/A
2690N/A hash_destroy(uidlist->files);
2690N/A if (uidlist->record_pool != NULL)
2690N/A pool_unref(uidlist->record_pool);
2690N/A
2690N/A buffer_free(uidlist->record_buf);
2690N/A i_free(uidlist->fname);
2690N/A i_free(uidlist);
2690N/A}
2690N/A
2690N/Astatic void
2690N/Amaildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
2690N/A{
2690N/A if (uidlist->first_recent_uid == 0 ||
2690N/A uid < uidlist->first_recent_uid)
2690N/A uidlist->first_recent_uid = uid;
2690N/A}
2690N/A
2690N/Astatic int maildir_uidlist_next(struct maildir_uidlist *uidlist,
2690N/A const char *line, uint32_t last_uid)
2690N/A{
2690N/A struct maildir_uidlist_rec *rec;
2690N/A uint32_t uid, flags;
2690N/A
2690N/A uid = flags = 0;
2690N/A while (*line >= '0' && *line <= '9') {
2690N/A uid = uid*10 + (*line - '0');
2690N/A line++;
2690N/A }
2690N/A
2690N/A if (uid <= last_uid) {
2690N/A /* we already have this */
2690N/A return 1;
2690N/A }
2690N/A
2690N/A if (uid == 0 || *line != ' ') {
2690N/A /* invalid file */
2690N/A mail_storage_set_critical(uidlist->ibox->box.storage,
2690N/A "Invalid data in file %s", uidlist->fname);
2690N/A return 0;
2690N/A }
2690N/A if (uid <= uidlist->last_read_uid) {
580N/A mail_storage_set_critical(uidlist->ibox->box.storage,
63N/A "UIDs not ordered in file %s (%u > %u)",
3158N/A uidlist->fname, uid, uidlist->last_read_uid);
3158N/A return 0;
202N/A }
3158N/A uidlist->last_read_uid = uid;
72N/A
63N/A if (uid >= uidlist->next_uid) {
1618N/A mail_storage_set_critical(uidlist->ibox->box.storage,
1618N/A "UID larger than next_uid in file %s (%u >= %u)",
1618N/A uidlist->fname, uid, uidlist->next_uid);
1618N/A return 0;
1618N/A }
1618N/A
1618N/A while (*line == ' ') line++;
1618N/A
2690N/A if (uidlist->version == 2) {
1618N/A /* skip flags parameter */
1618N/A while (*line != ' ') line++;
1618N/A while (*line == ' ') line++;
1618N/A }
1618N/A
1618N/A if (hash_lookup_full(uidlist->files, line, NULL, NULL)) {
1505N/A mail_storage_set_critical(uidlist->ibox->box.storage,
1505N/A "Duplicate file in uidlist file %s: %s",
1505N/A uidlist->fname, line);
1505N/A return 0;
1505N/A }
1505N/A
1271N/A rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
3159N/A rec->uid = uid;
3159N/A rec->flags = MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
3159N/A rec->filename = p_strdup(uidlist->record_pool, line);
3159N/A hash_insert(uidlist->files, rec->filename, rec);
3159N/A buffer_append(uidlist->record_buf, &rec, sizeof(rec));
3159N/A return 1;
3159N/A}
3159N/A
3159N/Aint maildir_uidlist_update(struct maildir_uidlist *uidlist)
3159N/A{
3159N/A struct mail_storage *storage = uidlist->ibox->box.storage;
3159N/A const struct maildir_uidlist_rec *const *rec_p;
3159N/A const char *line;
3159N/A unsigned int uid_validity, next_uid;
3159N/A struct istream *input;
3159N/A struct stat st;
3159N/A uint32_t last_uid;
3159N/A size_t size;
3159N/A int fd, ret;
3159N/A
3159N/A if (uidlist->last_mtime != 0) {
3159N/A if (stat(uidlist->fname, &st) < 0) {
3159N/A if (errno != ENOENT) {
3159N/A mail_storage_set_critical(storage,
3159N/A "stat(%s) failed: %m", uidlist->fname);
3159N/A return -1;
3159N/A }
3159N/A return 0;
3159N/A }
3159N/A
3159N/A if (st.st_mtime == uidlist->last_mtime) {
3159N/A /* unchanged */
3159N/A return 1;
3159N/A }
2453N/A }
1505N/A
1505N/A fd = open(uidlist->fname, O_RDONLY);
1505N/A if (fd == -1) {
2453N/A if (errno != ENOENT) {
2453N/A mail_storage_set_critical(storage,
1505N/A "open(%s) failed: %m", uidlist->fname);
583N/A return -1;
2453N/A }
1271N/A uidlist->initial_read = TRUE;
2205N/A return 0;
3159N/A }
3159N/A
3159N/A if (fstat(fd, &st) < 0) {
3159N/A mail_storage_set_critical(storage,
3159N/A "fstat(%s) failed: %m", uidlist->fname);
3159N/A return -1;
3159N/A }
3159N/A
3159N/A if (uidlist->record_pool == NULL) {
3159N/A uidlist->record_pool =
3159N/A pool_alloconly_create("uidlist record_pool",
2205N/A nearest_power(st.st_size -
3159N/A st.st_size/8));
3159N/A }
3159N/A
3159N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
3159N/A last_uid = size == 0 ? 0 : rec_p[(size / sizeof(*rec_p))-1]->uid;
3159N/A
2205N/A uidlist->version = 0;
50N/A
235N/A input = i_stream_create_file(fd, default_pool, 4096, TRUE);
235N/A
235N/A /* get header */
1713N/A line = i_stream_read_next_line(input);
1713N/A if (line == NULL || sscanf(line, "%u %u %u", &uidlist->version,
50N/A &uid_validity, &next_uid) != 3 ||
307N/A uidlist->version < 1 || uidlist->version > 2) {
307N/A /* broken file */
307N/A mail_storage_set_critical(storage,
307N/A "Corrupted header in file %s (version = %u)",
307N/A uidlist->fname, uidlist->version);
307N/A ret = 0;
307N/A } else if (uid_validity == uidlist->uid_validity &&
307N/A next_uid < uidlist->next_uid) {
307N/A mail_storage_set_critical(storage,
1271N/A "%s: next_uid was lowered (%u -> %u)",
2608N/A uidlist->fname, uidlist->next_uid, next_uid);
2608N/A ret = 0;
66N/A } else {
50N/A uidlist->uid_validity = uid_validity;
2026N/A uidlist->next_uid = next_uid;
2026N/A
2026N/A ret = 1;
2026N/A while ((line = i_stream_read_next_line(input)) != NULL) {
2261N/A if (!maildir_uidlist_next(uidlist, line, last_uid)) {
2261N/A ret = 0;
2261N/A break;
2261N/A }
2261N/A }
2261N/A }
2261N/A
2261N/A if (ret != 0)
2261N/A uidlist->last_mtime = st.st_mtime;
2261N/A else {
2261N/A (void)unlink(uidlist->fname);
2797N/A uidlist->last_mtime = 0;
2797N/A }
2797N/A
2797N/A i_stream_unref(input);
2797N/A uidlist->initial_read = TRUE;
2797N/A return ret;
2797N/A}
2797N/A
2797N/Astatic const struct maildir_uidlist_rec *
2797N/Amaildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
2797N/A unsigned int *idx_r)
3041N/A{
2261N/A const struct maildir_uidlist_rec *const *rec_p;
2261N/A unsigned int idx, left_idx, right_idx;
2261N/A size_t size;
2261N/A
2261N/A if (!uidlist->initial_read) {
2261N/A /* first time we need to read uidlist */
2261N/A if (maildir_uidlist_update(uidlist) < 0)
2797N/A return NULL;
2458N/A }
2458N/A
2458N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
3171N/A size /= sizeof(*rec_p);
2261N/A
2610N/A idx = 0;
2610N/A left_idx = 0;
2610N/A right_idx = size;
2610N/A
2261N/A while (left_idx < right_idx) {
2608N/A idx = (left_idx + right_idx) / 2;
2608N/A
2608N/A if (rec_p[idx]->uid < uid)
2608N/A left_idx = idx+1;
2608N/A else if (rec_p[idx]->uid > uid)
2608N/A right_idx = idx;
2608N/A else {
2608N/A *idx_r = idx;
2608N/A return rec_p[idx];
2026N/A }
1271N/A }
1271N/A
50N/A if (idx > 0) idx--;
307N/A *idx_r = idx;
307N/A return NULL;
307N/A}
307N/A
307N/Aconst char *
1045N/Amaildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
1045N/A enum maildir_uidlist_rec_flag *flags_r)
1045N/A{
1045N/A const struct maildir_uidlist_rec *rec;
1045N/A unsigned int idx;
307N/A
2277N/A rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
2277N/A if (rec == NULL)
2277N/A return NULL;
2277N/A
1045N/A *flags_r = rec->flags;
1045N/A return rec->filename;
1045N/A}
1045N/A
1045N/Aint maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
1045N/A{
1713N/A enum maildir_uidlist_rec_flag flags;
2987N/A
2987N/A if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
307N/A return FALSE;
400N/A
1846N/A if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
1174N/A return FALSE;
1174N/A
400N/A i_assert(uidlist->first_recent_uid != uid ||
2453N/A (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
2453N/A return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
2453N/A}
2453N/A
2453N/Auint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist)
2453N/A{
2453N/A const struct maildir_uidlist_rec *const *rec_p;
2690N/A unsigned int idx;
583N/A size_t size;
1618N/A uint32_t count;
1618N/A
1618N/A if (!uidlist->initial_sync) {
1618N/A /* we haven't synced yet, trust index */
2453N/A const struct mail_index_header *hdr;
2453N/A
2117N/A if (mail_index_get_header(uidlist->ibox->view, &hdr) < 0)
2117N/A return 0;
2117N/A return hdr->recent_messages_count;
2117N/A }
2117N/A
2117N/A /* all recent messages were in new/ dir, so even if we did only
2117N/A a partial sync we should know all the recent messages. */
2117N/A
2117N/A if (uidlist->first_recent_uid == 0)
2117N/A return 0;
2117N/A
2117N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
2117N/A size /= sizeof(*rec_p);
1618N/A
1618N/A maildir_uidlist_lookup_rec(uidlist, uidlist->first_recent_uid, &idx);
1618N/A for (count = 0; idx < size; idx++) {
1618N/A if ((rec_p[idx]->flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
1618N/A count++;
1618N/A }
2690N/A return count;
1618N/A}
1618N/A
1618N/Auint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
1618N/A{
1618N/A return uidlist->uid_validity;
1618N/A}
1618N/A
1618N/Avoid maildir_uidlist_set_uid_validity(struct maildir_uidlist *uidlist,
1618N/A uint32_t uid_validity)
1618N/A{
1618N/A uidlist->uid_validity = uid_validity;
1618N/A}
1618N/A
1618N/Auint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist)
1618N/A{
1618N/A return !uidlist->initial_read ? 0 : uidlist->next_uid;
1618N/A}
1618N/A
1618N/Astatic int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
1618N/A const char *temp_path)
2690N/A{
1618N/A struct mail_storage *storage = uidlist->ibox->box.storage;
1618N/A struct maildir_uidlist_iter_ctx *iter;
1618N/A struct utimbuf ut;
1618N/A string_t *str;
1618N/A uint32_t uid;
235N/A enum maildir_uidlist_rec_flag flags;
400N/A const char *filename;
400N/A int ret = 0;
235N/A
400N/A uidlist->version = 1;
400N/A
235N/A if (uidlist->uid_validity == 0)
1767N/A uidlist->uid_validity = ioloop_time;
487N/A
400N/A str = t_str_new(4096);
2286N/A str_printfa(str, "%u %u %u\n", uidlist->version,
2286N/A uidlist->uid_validity, uidlist->next_uid);
2286N/A
2286N/A iter = maildir_uidlist_iter_init(uidlist->ibox->uidlist);
2286N/A while (maildir_uidlist_iter_next(iter, &uid, &flags, &filename)) {
2286N/A /* avoid overflowing str buffer so we don't eat more memory
235N/A than we need. */
400N/A if (str_len(str) + MAX_INT_STRLEN +
235N/A strlen(filename) + 5 + 10 >= 4096) {
2407N/A /* flush buffer */
2407N/A if (write_full(uidlist->lock_fd,
2407N/A str_data(str), str_len(str)) < 0) {
2407N/A mail_storage_set_critical(storage,
2407N/A "write_full(%s) failed: %m", temp_path);
2407N/A ret = -1;
2446N/A break;
2446N/A }
2446N/A str_truncate(str, 0);
2407N/A }
2446N/A
2446N/A str_printfa(str, "%u %s\n", uid, filename);
2446N/A }
2693N/A maildir_uidlist_iter_deinit(iter);
235N/A
2693N/A if (ret < 0)
235N/A return -1;
2693N/A
235N/A if (write_full(uidlist->lock_fd, str_data(str), str_len(str)) < 0) {
289N/A mail_storage_set_critical(storage,
50N/A "write_full(%s) failed: %m", temp_path);
388N/A return -1;
388N/A }
135N/A
66N/A /* uidlist's mtime must grow every time */
66N/A uidlist->last_mtime = ioloop_time <= uidlist->last_mtime ?
66N/A uidlist->last_mtime + 1 : ioloop_time;
66N/A ut.actime = ioloop_time;
111N/A ut.modtime = uidlist->last_mtime;
1618N/A if (utime(temp_path, &ut) < 0) {
1618N/A mail_storage_set_critical(storage,
1618N/A "utime(%s) failed: %m", temp_path);
1618N/A return -1;
1618N/A }
1618N/A
1618N/A if (fsync(uidlist->lock_fd) < 0) {
1618N/A mail_storage_set_critical(storage,
1618N/A "fsync(%s) failed: %m", temp_path);
1618N/A return -1;
1618N/A }
1618N/A
1618N/A return 0;
202N/A}
72N/A
136N/Astatic int maildir_uidlist_rewrite(struct maildir_uidlist *uidlist)
66N/A{
136N/A struct index_mailbox *ibox = uidlist->ibox;
66N/A const char *temp_path, *db_path;
2627N/A int ret;
481N/A
2693N/A i_assert(UIDLIST_IS_LOCKED(uidlist));
1191N/A
2627N/A temp_path = t_strconcat(ibox->control_dir,
1191N/A "/" MAILDIR_UIDLIST_NAME ".lock", NULL);
1448N/A ret = maildir_uidlist_rewrite_fd(uidlist, temp_path);
2693N/A
1191N/A if (ret == 0) {
1191N/A db_path = t_strconcat(ibox->control_dir,
481N/A "/" MAILDIR_UIDLIST_NAME, NULL);
1767N/A
1191N/A if (file_dotlock_replace(db_path, NULL, uidlist->lock_fd,
481N/A FALSE) <= 0) {
1618N/A mail_storage_set_critical(ibox->box.storage,
2693N/A "file_dotlock_replace(%s) failed: %m", db_path);
235N/A (void)unlink(temp_path);
2690N/A ret = -1;
2690N/A }
2690N/A uidlist->lock_fd = -1;
2690N/A } else {
2690N/A maildir_uidlist_unlock(uidlist);
2690N/A }
2690N/A
2690N/A return ret;
2690N/A}
2690N/A
237N/Astatic void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
1713N/A int nonsynced)
237N/A{
289N/A struct maildir_uidlist_rec **rec_p;
237N/A size_t i, size;
1713N/A
237N/A rec_p = buffer_get_modifyable_data(uidlist->record_buf, &size);
289N/A size /= sizeof(*rec_p);
237N/A
1713N/A if (nonsynced) {
237N/A for (i = 0; i < size; i++)
237N/A rec_p[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
237N/A } else {
237N/A for (i = 0; i < size; i++)
2690N/A rec_p[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
237N/A }
237N/A}
2293N/A
1755N/Astruct maildir_uidlist_sync_ctx *
1755N/Amaildir_uidlist_sync_init(struct maildir_uidlist *uidlist, int partial)
1755N/A{
3171N/A struct maildir_uidlist_sync_ctx *ctx;
3158N/A size_t size;
3158N/A
3158N/A ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
3158N/A ctx->uidlist = uidlist;
237N/A ctx->partial = partial;
202N/A
237N/A if (partial) {
1618N/A /* initially mark all nonsynced */
2690N/A maildir_uidlist_mark_all(uidlist, TRUE);
237N/A return ctx;
237N/A }
2293N/A
1755N/A ctx->record_pool =
1755N/A pool_alloconly_create("maildir_uidlist_sync", 16384);
1755N/A ctx->files = hash_create(default_pool, ctx->record_pool, 4096,
3171N/A maildir_hash, maildir_cmp);
3158N/A
3158N/A size = buffer_get_used_size(uidlist->record_buf);
3158N/A ctx->record_buf = buffer_create_dynamic(default_pool, size, (size_t)-1);
3158N/A return ctx;
237N/A}
59N/A
237N/Astatic int
237N/Amaildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx,
2690N/A const char *filename,
237N/A enum maildir_uidlist_rec_flag flags)
237N/A{
2293N/A struct maildir_uidlist *uidlist = ctx->uidlist;
1755N/A struct maildir_uidlist_rec *rec;
1755N/A
1755N/A /* we'll update uidlist directly */
3171N/A rec = hash_lookup(uidlist->files, filename);
3158N/A i_assert(rec != NULL || UIDLIST_IS_LOCKED(uidlist));
3158N/A
3158N/A if (rec == NULL) {
3158N/A if (ctx->new_files_count == 0) {
237N/A ctx->first_new_pos =
289N/A buffer_get_used_size(uidlist->record_buf) /
66N/A sizeof(rec);
400N/A }
135N/A ctx->new_files_count++;
66N/A
66N/A if (uidlist->record_pool == NULL) {
66N/A uidlist->record_pool =
66N/A pool_alloconly_create("uidlist record_pool",
66N/A 1024);
202N/A }
72N/A
136N/A rec = p_new(uidlist->record_pool,
66N/A struct maildir_uidlist_rec, 1);
136N/A rec->uid = (uint32_t)-1;
2293N/A buffer_append(uidlist->record_buf, &rec, sizeof(rec));
2293N/A }
2293N/A
2293N/A if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
2293N/A rec->uid != (uint32_t)-1)
2293N/A maildir_uidlist_mark_recent(uidlist, rec->uid);
2690N/A
2470N/A rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
3121N/A rec->filename = p_strdup(uidlist->record_pool, filename);
3121N/A hash_insert(uidlist->files, rec->filename, rec);
3121N/A return 1;
3121N/A}
3121N/A
2690N/Aint maildir_uidlist_sync_next_pre(struct maildir_uidlist_sync_ctx *ctx,
2470N/A const char *filename)
2470N/A{
2470N/A if (!UIDLIST_IS_LOCKED(ctx->uidlist) &&
2470N/A hash_lookup(ctx->uidlist->files, filename) == NULL &&
2470N/A (ctx->partial || hash_lookup(ctx->files, filename) == NULL)) {
2470N/A if (!ctx->uidlist->initial_read) {
2470N/A /* first time reading the uidlist */
2690N/A if (maildir_uidlist_update(ctx->uidlist) < 0) {
2470N/A ctx->failed = TRUE;
2690N/A return -1;
2470N/A }
2470N/A return maildir_uidlist_sync_next_pre(ctx, filename);
2470N/A }
2470N/A
2470N/A return 0;
2608N/A }
2608N/A
2608N/A return 1;
2608N/A}
2608N/A
2608N/Aint maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
2690N/A const char *filename,
2608N/A enum maildir_uidlist_rec_flag flags)
2608N/A{
2608N/A struct maildir_uidlist *uidlist = ctx->uidlist;
2608N/A struct maildir_uidlist_rec *rec, *old_rec;
2608N/A
2608N/A if (ctx->failed)
2690N/A return -1;
if (ctx->partial)
return maildir_uidlist_sync_next_partial(ctx, filename, flags);
rec = hash_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;
}
rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
MAILDIR_UIDLIST_REC_FLAG_MOVED);
} else {
old_rec = hash_lookup(uidlist->files, filename);
i_assert(old_rec != NULL || UIDLIST_IS_LOCKED(uidlist));
rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
if (old_rec != NULL)
*rec = *old_rec;
else {
rec->uid = (uint32_t)-1;
ctx->new_files_count++;
}
buffer_append(ctx->record_buf, &rec, sizeof(rec));
}
if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
rec->uid != (uint32_t)-1)
maildir_uidlist_mark_recent(uidlist, rec->uid);
rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
rec->filename = p_strdup(ctx->record_pool, filename);
hash_insert(ctx->files, rec->filename, rec);
return 1;
}
static int maildir_time_cmp(const void *p1, const void *p2)
{
const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
const char *s1 = (*rec1)->filename, *s2 = (*rec2)->filename;
time_t t1 = 0, t2 = 0;
/* we have to do numeric comparision, strcmp() will break when
there's different amount of digits (mostly the 999999999 ->
1000000000 change in Sep 9 2001) */
while (*s1 >= '0' && *s1 <= '9') {
t1 = t1*10 + (*s1 - '0');
s1++;
}
while (*s2 >= '0' && *s2 <= '9') {
t2 = t2*10 + (*s2 - '0');
s2++;
}
return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
}
static void maildir_uidlist_assign_uids(struct maildir_uidlist *uidlist,
unsigned int first_new_pos)
{
struct maildir_uidlist_rec **rec_p;
unsigned int dest;
size_t size;
i_assert(UIDLIST_IS_LOCKED(uidlist));
rec_p = buffer_get_modifyable_data(uidlist->record_buf, &size);
size /= sizeof(*rec_p);
/* sort new files and assign UIDs for them */
qsort(rec_p + first_new_pos, size - first_new_pos,
sizeof(*rec_p), maildir_time_cmp);
for (dest = first_new_pos; dest < size; dest++) {
i_assert(rec_p[dest]->uid == (uint32_t)-1);
rec_p[dest]->uid = uidlist->next_uid++;
rec_p[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
if ((rec_p[dest]->flags &
MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
maildir_uidlist_mark_recent(uidlist, rec_p[dest]->uid);
}
}
static int maildir_uid_cmp(const void *p1, const void *p2)
{
const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
return (*rec1)->uid < (*rec2)->uid ? -1 :
(*rec1)->uid > (*rec2)->uid ? 1 : 0;
}
static void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec **rec_p;
size_t size;
/* buffer is unsorted, sort it by UID */
rec_p = buffer_get_modifyable_data(ctx->record_buf, &size);
size /= sizeof(*rec_p);
qsort(rec_p, size, sizeof(*rec_p), maildir_uid_cmp);
buffer_free(uidlist->record_buf);
uidlist->record_buf = ctx->record_buf;
ctx->record_buf = NULL;
hash_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) {
maildir_uidlist_assign_uids(uidlist,
size - ctx->new_files_count);
}
}
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) {
maildir_uidlist_assign_uids(ctx->uidlist,
ctx->first_new_pos);
}
}
ctx->finished = TRUE;
ctx->uidlist->initial_sync = TRUE;
}
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx *ctx)
{
int ret = ctx->failed ? -1 : 0;
if (!ctx->finished)
maildir_uidlist_sync_finish(ctx);
if (ctx->new_files_count != 0 && !ctx->failed) {
t_push();
ret = maildir_uidlist_rewrite(ctx->uidlist);
t_pop();
}
if (ctx->partial)
maildir_uidlist_mark_all(ctx->uidlist, FALSE);
if (UIDLIST_IS_LOCKED(ctx->uidlist))
maildir_uidlist_unlock(ctx->uidlist);
if (ctx->files != NULL)
hash_destroy(ctx->files);
if (ctx->record_pool != NULL)
pool_unref(ctx->record_pool);
if (ctx->record_buf != NULL)
buffer_free(ctx->record_buf);
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_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;
size_t size;
ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
ctx->next = buffer_get_data(uidlist->record_buf, &size);
size /= sizeof(*ctx->next);
ctx->end = ctx->next + size;
return ctx;
}
int 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)
{
if (ctx->next == ctx->end)
return 0;
*uid_r = (*ctx->next)->uid;
*flags_r = (*ctx->next)->flags;
*filename_r = (*ctx->next)->filename;
ctx->next++;
return 1;
}
void maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx *ctx)
{
i_free(ctx);
}