maildir-uidlist.c revision 054fdbfc20f57cb4a386e088ec773d9980ac8b2c
1516N/A/* Copyright (C) 2003 Timo Sirainen */
39N/A
39N/A#include "lib.h"
39N/A#include "ioloop.h"
39N/A#include "buffer.h"
39N/A#include "hash.h"
39N/A#include "istream.h"
39N/A#include "str.h"
39N/A#include "file-dotlock.h"
39N/A#include "write-full.h"
39N/A#include "maildir-storage.h"
39N/A#include "maildir-uidlist.h"
39N/A
39N/A#include <stdio.h>
39N/A#include <stdlib.h>
39N/A#include <sys/stat.h>
39N/A#include <utime.h>
39N/A
39N/A/* how many seconds to wait before overriding uidlist.lock */
39N/A#define UIDLIST_LOCK_STALE_TIMEOUT (60*5)
39N/A
926N/A#define UIDLIST_IS_LOCKED(uidlist) \
926N/A ((uidlist)->lock_fd != -1)
2238N/A
926N/Astruct maildir_uidlist_rec {
39N/A uint32_t uid;
2453N/A uint32_t flags;
342N/A char *filename;
1516N/A};
1636N/A
1386N/Astruct maildir_uidlist {
838N/A struct index_mailbox *ibox;
39N/A char *fname;
51N/A int lock_fd;
2073N/A
2144N/A time_t last_mtime;
1066N/A
1231N/A pool_t record_pool;
2453N/A buffer_t *record_buf;
1352N/A struct hash_table *files;
1890N/A
296N/A unsigned int version;
39N/A unsigned int uid_validity, next_uid, last_read_uid;
1713N/A uint32_t first_recent_uid;
1713N/A
39N/A unsigned int initial_read:1;
39N/A};
39N/A
39N/Astruct maildir_uidlist_sync_ctx {
39N/A struct maildir_uidlist *uidlist;
205N/A
39N/A pool_t record_pool;
205N/A buffer_t *record_buf;
39N/A struct hash_table *files;
39N/A
39N/A unsigned int first_new_pos;
39N/A unsigned int new_files_count;
39N/A
39N/A unsigned int partial:1;
39N/A unsigned int synced:1;
39N/A unsigned int locked:1;
39N/A unsigned int finished:1;
39N/A unsigned int failed:1;
39N/A};
39N/A
39N/Astruct maildir_uidlist_iter_ctx {
39N/A const struct maildir_uidlist_rec *const *next, *const *end;
39N/A};
39N/A
39N/Aint maildir_uidlist_try_lock(struct maildir_uidlist *uidlist)
39N/A{
39N/A const char *path;
39N/A mode_t old_mask;
39N/A int fd;
39N/A
48N/A if (UIDLIST_IS_LOCKED(uidlist))
48N/A return 1;
48N/A
48N/A path = t_strconcat(uidlist->ibox->control_dir,
48N/A "/" MAILDIR_UIDLIST_NAME, NULL);
59N/A old_mask = umask(0777 & ~uidlist->ibox->mail_create_mode);
48N/A fd = file_dotlock_open(path, uidlist->ibox->storage->temp_prefix,
2073N/A NULL, 0, 0, UIDLIST_LOCK_STALE_TIMEOUT,
2073N/A NULL, NULL);
39N/A umask(old_mask);
39N/A if (fd == -1) {
567N/A if (errno == EAGAIN)
838N/A return 0;
838N/A mail_storage_set_critical(uidlist->ibox->box.storage,
838N/A "file_dotlock_open(%s) failed: %m", path);
1890N/A return -1;
2453N/A }
39N/A
39N/A uidlist->lock_fd = fd;
39N/A return 1;
1431N/A}
1431N/A
39N/Avoid maildir_uidlist_unlock(struct maildir_uidlist *uidlist)
227N/A{
315N/A const char *path;
315N/A
39N/A if (!UIDLIST_IS_LOCKED(uidlist))
1352N/A return;
1352N/A
1352N/A path = t_strconcat(uidlist->ibox->control_dir,
1352N/A "/" MAILDIR_UIDLIST_NAME, NULL);
1431N/A (void)file_dotlock_delete(path, NULL, uidlist->lock_fd);
1431N/A uidlist->lock_fd = -1;
429N/A}
315N/A
1352N/Astruct maildir_uidlist *maildir_uidlist_init(struct index_mailbox *ibox)
429N/A{
1352N/A struct maildir_uidlist *uidlist;
1352N/A
39N/A uidlist = i_new(struct maildir_uidlist, 1);
926N/A uidlist->ibox = ibox;
926N/A uidlist->fname =
203N/A i_strconcat(ibox->control_dir, "/" MAILDIR_UIDLIST_NAME, NULL);
203N/A uidlist->lock_fd = -1;
203N/A uidlist->record_buf =
203N/A buffer_create_dynamic(default_pool, 512, (size_t)-1);
1045N/A uidlist->files = hash_create(default_pool, default_pool, 4096,
1045N/A maildir_hash, maildir_cmp);
72N/A
72N/A uidlist->uid_validity = ioloop_time;
59N/A uidlist->next_uid = 1;
2054N/A
1045N/A return uidlist;
1045N/A}
1045N/A
1045N/Avoid maildir_uidlist_deinit(struct maildir_uidlist *uidlist)
1713N/A{
1045N/A i_assert(!UIDLIST_IS_LOCKED(uidlist));
1045N/A
1045N/A hash_destroy(uidlist->files);
2084N/A if (uidlist->record_pool != NULL)
2084N/A pool_unref(uidlist->record_pool);
2084N/A
2084N/A buffer_free(uidlist->record_buf);
2084N/A i_free(uidlist->fname);
2084N/A i_free(uidlist);
2084N/A}
72N/A
2084N/Astatic void
838N/Amaildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
72N/A{
72N/A if (uidlist->first_recent_uid == 0)
2084N/A uidlist->first_recent_uid = uid;
838N/A i_assert(uid >= uidlist->first_recent_uid);
72N/A}
59N/A
72N/Astatic int maildir_uidlist_next(struct maildir_uidlist *uidlist,
72N/A const char *line, uint32_t last_uid)
59N/A{
72N/A struct maildir_uidlist_rec *rec;
72N/A uint32_t uid, flags;
72N/A
72N/A uid = flags = 0;
72N/A while (*line >= '0' && *line <= '9') {
2144N/A uid = uid*10 + (*line - '0');
72N/A line++;
59N/A }
72N/A
72N/A if (uid <= last_uid) {
72N/A /* we already have this */
72N/A return 1;
59N/A }
72N/A
72N/A if (uid == 0 || *line != ' ') {
72N/A /* invalid file */
202N/A mail_storage_set_critical(uidlist->ibox->box.storage,
72N/A "Invalid data in file %s", uidlist->fname);
72N/A return 0;
59N/A }
1713N/A if (uid <= uidlist->last_read_uid) {
59N/A mail_storage_set_critical(uidlist->ibox->box.storage,
838N/A "UIDs not ordered in file %s (%u > %u)",
2240N/A uidlist->fname, uid, uidlist->last_read_uid);
838N/A return 0;
838N/A }
838N/A uidlist->last_read_uid = uid;
926N/A
838N/A if (uid >= uidlist->next_uid) {
838N/A mail_storage_set_critical(uidlist->ibox->box.storage,
2240N/A "UID larger than next_uid in file %s (%u >= %u)",
2240N/A uidlist->fname, uid, uidlist->next_uid);
2284N/A return 0;
2240N/A }
838N/A
2240N/A while (*line == ' ') line++;
2240N/A
2240N/A flags = 0;
2240N/A if (uidlist->version > 1) {
2240N/A while (*line != ' ') {
2240N/A switch (*line) {
2240N/A case 'N':
2240N/A flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
2240N/A break;
2317N/A }
2240N/A line++;
2240N/A }
2240N/A while (*line == ' ') line++;
2240N/A } else {
2240N/A /* old version, have to assume it's in new dir since we
2240N/A don't know */
2240N/A flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
2240N/A }
2240N/A
2240N/A if (hash_lookup_full(uidlist->files, line, NULL, NULL)) {
2240N/A mail_storage_set_critical(uidlist->ibox->box.storage,
2284N/A "Duplicate file in uidlist file %s: %s",
2284N/A uidlist->fname, line);
2284N/A return 0;
2284N/A }
2240N/A
2240N/A rec = p_new(uidlist->record_pool, struct maildir_uidlist_rec, 1);
2284N/A rec->uid = uid;
2284N/A rec->flags = flags | MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
2284N/A rec->filename = p_strdup(uidlist->record_pool, line);
838N/A hash_insert(uidlist->files, rec->filename, rec);
838N/A buffer_append(uidlist->record_buf, &rec, sizeof(rec));
838N/A return 1;
838N/A}
838N/A
838N/Aint maildir_uidlist_update(struct maildir_uidlist *uidlist)
838N/A{
926N/A struct mail_storage *storage = uidlist->ibox->box.storage;
838N/A const struct maildir_uidlist_rec *const *rec_p;
838N/A const char *line;
838N/A struct istream *input;
838N/A struct stat st;
838N/A uint32_t last_uid;
838N/A size_t size;
838N/A int fd, ret;
845N/A
845N/A if (uidlist->last_mtime != 0) {
926N/A if (stat(uidlist->fname, &st) < 0) {
838N/A if (errno != ENOENT) {
845N/A mail_storage_set_critical(storage,
845N/A "stat(%s) failed: %m", uidlist->fname);
845N/A return -1;
838N/A }
845N/A return 0;
845N/A }
838N/A
926N/A if (st.st_mtime == uidlist->last_mtime) {
203N/A /* unchanged */
315N/A return 1;
838N/A }
203N/A }
838N/A
48N/A fd = open(uidlist->fname, O_RDONLY);
48N/A if (fd == -1) {
48N/A if (errno != ENOENT) {
48N/A mail_storage_set_critical(storage,
48N/A "open(%s) failed: %m", uidlist->fname);
838N/A return -1;
203N/A }
48N/A uidlist->initial_read = TRUE;
203N/A return 0;
72N/A }
181N/A
72N/A if (fstat(fd, &st) < 0) {
181N/A mail_storage_set_critical(storage,
46N/A "fstat(%s) failed: %m", uidlist->fname);
181N/A return -1;
237N/A }
46N/A
2453N/A if (uidlist->record_pool == NULL) {
2453N/A uidlist->record_pool =
2453N/A pool_alloconly_create("uidlist record_pool",
2453N/A nearest_power(st.st_size -
2339N/A st.st_size/8));
2453N/A }
2453N/A
2453N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
2453N/A last_uid = size == 0 ? 0 : rec_p[(size / sizeof(*rec_p))-1]->uid;
2453N/A
2339N/A uidlist->version = 0;
2339N/A
2339N/A input = i_stream_create_file(fd, default_pool, 4096, TRUE);
2339N/A
2339N/A /* get header */
2339N/A line = i_stream_read_next_line(input);
2453N/A if (line == NULL || sscanf(line, "%u %u %u", &uidlist->version,
2453N/A &uidlist->uid_validity,
2453N/A &uidlist->next_uid) != 3 ||
2453N/A uidlist->version < 1 || uidlist->version > 2) {
2453N/A /* broken file */
2453N/A mail_storage_set_critical(storage,
2453N/A "Corrupted header in file %s (version = %u)",
2453N/A uidlist->fname, uidlist->version);
2453N/A ret = 0;
2453N/A } else {
2453N/A ret = 1;
2453N/A while ((line = i_stream_read_next_line(input)) != NULL) {
2339N/A if (!maildir_uidlist_next(uidlist, line, last_uid)) {
2453N/A ret = 0;
2453N/A break;
2453N/A }
2453N/A }
2453N/A }
2453N/A
2453N/A if (ret != 0)
2453N/A uidlist->last_mtime = st.st_mtime;
2453N/A else {
2453N/A (void)unlink(uidlist->fname);
2453N/A uidlist->last_mtime = 0;
2453N/A }
2453N/A
2453N/A i_stream_unref(input);
2453N/A uidlist->initial_read = TRUE;
2453N/A return ret;
2453N/A}
2453N/A
2453N/Astatic const struct maildir_uidlist_rec *
2453N/Amaildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
2453N/A unsigned int *idx_r)
2453N/A{
2453N/A const struct maildir_uidlist_rec *const *rec_p;
2453N/A unsigned int idx, left_idx, right_idx;
2453N/A size_t size;
2453N/A
2453N/A if (!uidlist->initial_read) {
2453N/A /* first time we need to read uidlist */
2339N/A if (maildir_uidlist_update(uidlist) < 0)
2339N/A return NULL;
2339N/A }
2339N/A
2339N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
2453N/A size /= sizeof(*rec_p);
2453N/A
2453N/A idx = 0;
2453N/A left_idx = 0;
2453N/A right_idx = size;
2339N/A
2453N/A while (left_idx < right_idx) {
2453N/A idx = (left_idx + right_idx) / 2;
2453N/A
2453N/A if (rec_p[idx]->uid < uid)
2339N/A left_idx = idx+1;
2453N/A else if (rec_p[idx]->uid > uid)
2453N/A right_idx = idx;
2453N/A else {
2453N/A *idx_r = idx;
2453N/A return rec_p[idx];
2339N/A }
2339N/A }
2339N/A
2339N/A if (idx > 0) idx--;
2339N/A *idx_r = idx;
2339N/A return NULL;
2453N/A}
2453N/A
2339N/Aconst char *
2453N/Amaildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
2339N/A enum maildir_uidlist_rec_flag *flags_r)
2540N/A{
2339N/A const struct maildir_uidlist_rec *rec;
2339N/A unsigned int idx;
2339N/A
2339N/A rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
2339N/A if (rec == NULL)
2339N/A return NULL;
2339N/A
2339N/A *flags_r = rec->flags;
2339N/A return rec->filename;
2339N/A}
2453N/A
2453N/Aint maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
2453N/A{
2453N/A enum maildir_uidlist_rec_flag flags;
2453N/A
2453N/A if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
2453N/A return FALSE;
2453N/A
2453N/A if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
2453N/A return FALSE;
2453N/A
2453N/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;
2540N/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;
2453N/A unsigned int idx;
2453N/A size_t size;
2453N/A uint32_t count;
2453N/A
2453N/A if (uidlist->first_recent_uid == 0)
2453N/A return 0;
2453N/A
2453N/A rec_p = buffer_get_data(uidlist->record_buf, &size);
2453N/A size /= sizeof(*rec_p);
2453N/A
2453N/A maildir_uidlist_lookup_rec(uidlist, uidlist->first_recent_uid, &idx);
2453N/A for (count = 0; idx < size; idx++) {
2453N/A if ((rec_p[idx]->flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
2453N/A count++;
2453N/A }
2453N/A return count;
2453N/A}
2453N/A
2453N/Astatic int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
2453N/A const char *temp_path)
2453N/A{
2453N/A struct mail_storage *storage = uidlist->ibox->box.storage;
2453N/A struct maildir_uidlist_iter_ctx *iter;
838N/A struct utimbuf ut;
838N/A string_t *str;
838N/A uint32_t uid;
838N/A enum maildir_uidlist_rec_flag flags;
838N/A const char *filename, *flags_str;
838N/A int ret = 0;
838N/A
838N/A uidlist->version = 2;
342N/A
926N/A str = t_str_new(4096);
838N/A str_printfa(str, "%u %u %u\n", uidlist->version,
838N/A uidlist->uid_validity, uidlist->next_uid);
926N/A
838N/A iter = maildir_uidlist_iter_init(uidlist->ibox->uidlist);
838N/A while (maildir_uidlist_iter_next(iter, &uid, &flags, &filename)) {
838N/A if (str_len(str) + MAX_INT_STRLEN +
838N/A strlen(filename) + 2 >= 4096) {
838N/A /* flush buffer */
926N/A if (write_full(uidlist->lock_fd,
2453N/A str_data(str), str_len(str)) < 0) {
2453N/A mail_storage_set_critical(storage,
2453N/A "write_full(%s) failed: %m", temp_path);
2453N/A ret = -1;
2453N/A break;
2453N/A }
2453N/A str_truncate(str, 0);
2453N/A }
926N/A
615N/A flags_str = (flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 ?
615N/A "N" : "-";
615N/A str_printfa(str, "%u %s %s\n", uid, flags_str, filename);
615N/A }
615N/A maildir_uidlist_iter_deinit(iter);
926N/A
615N/A if (ret < 0)
615N/A return -1;
838N/A
111N/A if (write_full(uidlist->lock_fd, str_data(str), str_len(str)) < 0) {
111N/A mail_storage_set_critical(storage,
111N/A "write_full(%s) failed: %m", temp_path);
111N/A return -1;
111N/A }
111N/A
111N/A /* uidlist's mtime must grow every time */
111N/A uidlist->last_mtime = ioloop_time <= uidlist->last_mtime ?
113N/A uidlist->last_mtime + 1 : ioloop_time;
926N/A ut.actime = ioloop_time;
838N/A ut.modtime = uidlist->last_mtime;
926N/A if (utime(temp_path, &ut) < 0) {
113N/A mail_storage_set_critical(storage,
113N/A "utime(%s) failed: %m", temp_path);
113N/A return -1;
113N/A }
113N/A
113N/A if (fsync(uidlist->lock_fd) < 0) {
113N/A mail_storage_set_critical(storage,
113N/A "fsync(%s) failed: %m", temp_path);
113N/A return -1;
111N/A }
1500N/A
1432N/A return 0;
1542N/A}
1970N/A
2073N/Astatic int maildir_uidlist_rewrite(struct maildir_uidlist *uidlist)
2073N/A{
2073N/A struct index_mailbox *ibox = uidlist->ibox;
2073N/A const char *temp_path, *db_path;
2073N/A int ret;
2073N/A
1542N/A i_assert(UIDLIST_IS_LOCKED(uidlist));
146N/A
1432N/A temp_path = t_strconcat(ibox->control_dir,
1432N/A "/" MAILDIR_UIDLIST_NAME ".lock", NULL);
50N/A ret = maildir_uidlist_rewrite_fd(uidlist, temp_path);
1432N/A
1432N/A if (ret == 0) {
1432N/A db_path = t_strconcat(ibox->control_dir,
51N/A "/" MAILDIR_UIDLIST_NAME, NULL);
1636N/A
1432N/A if (file_dotlock_replace(db_path, NULL, uidlist->lock_fd,
1507N/A FALSE) <= 0) {
51N/A mail_storage_set_critical(ibox->box.storage,
1500N/A "file_dotlock_replace(%s) failed: %m", db_path);
591N/A ret = -1;
1970N/A }
1970N/A } else {
1970N/A (void)close(uidlist->lock_fd);
591N/A }
1542N/A uidlist->lock_fd = -1;
1970N/A
1970N/A if (ret < 0)
1970N/A (void)unlink(temp_path);
2073N/A return ret;
1500N/A}
2073N/A
2073N/Astatic void maildir_uidlist_mark_all(struct maildir_uidlist *uidlist,
1713N/A int nonsynced)
1713N/A{
2073N/A struct maildir_uidlist_rec **rec_p;
2073N/A size_t i, size;
2073N/A
1713N/A rec_p = buffer_get_modifyable_data(uidlist->record_buf, &size);
2073N/A size /= sizeof(*rec_p);
1713N/A
2284N/A if (nonsynced) {
2073N/A for (i = 0; i < size; i++)
2073N/A rec_p[i]->flags |= MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
1713N/A } else {
2073N/A for (i = 0; i < size; i++)
2073N/A rec_p[i]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
2073N/A }
1713N/A}
1713N/A
2073N/Astruct maildir_uidlist_sync_ctx *
2073N/Amaildir_uidlist_sync_init(struct maildir_uidlist *uidlist, int partial)
2073N/A{
1500N/A struct maildir_uidlist_sync_ctx *ctx;
1500N/A size_t size;
1500N/A
1500N/A ctx = i_new(struct maildir_uidlist_sync_ctx, 1);
1500N/A ctx->uidlist = uidlist;
51N/A ctx->partial = partial;
1500N/A
1500N/A if (partial) {
1500N/A /* initially mark all nonsynced */
1500N/A maildir_uidlist_mark_all(uidlist, TRUE);
1500N/A return ctx;
1500N/A }
1500N/A
2073N/A ctx->record_pool =
2073N/A pool_alloconly_create("maildir_uidlist_sync", 16384);
2073N/A ctx->files = hash_create(default_pool, ctx->record_pool, 4096,
2073N/A maildir_hash, maildir_cmp);
2073N/A
2073N/A size = buffer_get_used_size(uidlist->record_buf);
1890N/A ctx->record_buf = buffer_create_dynamic(default_pool, size, (size_t)-1);
1500N/A return ctx;
2073N/A}
2073N/A
2073N/Astatic int maildir_uidlist_sync_uidlist(struct maildir_uidlist_sync_ctx *ctx)
1500N/A{
1500N/A int ret;
1500N/A
1500N/A if (!ctx->uidlist->initial_read) {
926N/A /* first time reading the uidlist,
1500N/A no locking yet */
2026N/A if (maildir_uidlist_update(ctx->uidlist) < 0) {
1500N/A ctx->failed = TRUE;
2026N/A return -1;
2026N/A }
2026N/A return 0;
2026N/A }
2073N/A
2026N/A /* lock and update uidlist to see if it's just been added */
2026N/A ret = maildir_uidlist_try_lock(ctx->uidlist);
1500N/A if (ret <= 0) {
1500N/A if (ret == 0) {
1500N/A ctx->locked = TRUE;
1500N/A return -1;
1500N/A }
123N/A ctx->failed = TRUE;
1500N/A return -1;
1500N/A }
877N/A if (maildir_uidlist_update(ctx->uidlist) < 0) {
1500N/A ctx->failed = TRUE;
1500N/A return -1;
1500N/A }
1500N/A
1500N/A ctx->synced = TRUE;
1500N/A return 1;
1500N/A}
1500N/A
1500N/Astatic int
1500N/Amaildir_uidlist_sync_next_partial(struct maildir_uidlist_sync_ctx *ctx,
1500N/A const char *filename,
1500N/A enum maildir_uidlist_rec_flag flags)
1500N/A{
1500N/A struct maildir_uidlist *uidlist = ctx->uidlist;
39N/A struct maildir_uidlist_rec *rec;
1500N/A
1500N/A /* we'll update uidlist directly */
1500N/A rec = hash_lookup(uidlist->files, filename);
1500N/A i_assert(rec != NULL || ctx->synced);
1500N/A
838N/A if (rec == NULL) {
1500N/A if (ctx->new_files_count == 0) {
1500N/A ctx->first_new_pos =
1500N/A buffer_get_used_size(uidlist->record_buf) /
1500N/A sizeof(rec);
1500N/A }
1500N/A ctx->new_files_count++;
2238N/A
2238N/A rec = p_new(uidlist->record_pool,
2238N/A struct maildir_uidlist_rec, 1);
2238N/A rec->uid = (uint32_t)-1;
1500N/A buffer_append(uidlist->record_buf, &rec, sizeof(rec));
1500N/A }
2238N/A
2238N/A rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
2238N/A rec->filename = p_strdup(uidlist->record_pool, filename);
2238N/A hash_insert(uidlist->files, rec->filename, rec);
2238N/A return 1;
2238N/A}
39N/A
956N/Aint maildir_uidlist_sync_next_pre(struct maildir_uidlist_sync_ctx *ctx,
956N/A const char *filename)
956N/A{
956N/A int ret;
1431N/A
1431N/A if (!ctx->synced &&
956N/A hash_lookup(ctx->uidlist->files, filename) == NULL &&
956N/A (ctx->partial || hash_lookup(ctx->files, filename) == NULL)) {
956N/A if (ctx->locked)
956N/A return 0;
956N/A
956N/A ret = maildir_uidlist_sync_uidlist(ctx);
941N/A if (ret < 0)
1007N/A return ctx->locked ? 0 : -1;
1007N/A if (ret == 0)
1007N/A return maildir_uidlist_sync_next_pre(ctx, filename);
1007N/A }
1007N/A
1007N/A return 1;
1007N/A}
1007N/A
1007N/Aint maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
1007N/A const char *filename,
1007N/A enum maildir_uidlist_rec_flag flags)
1007N/A{
1007N/A struct maildir_uidlist *uidlist = ctx->uidlist;
1007N/A struct maildir_uidlist_rec *rec, *old_rec;
1100N/A
1407N/A i_assert(!ctx->locked);
941N/A
941N/A if (ctx->failed)
144N/A return -1;
941N/A
1100N/A if (ctx->partial)
1100N/A return maildir_uidlist_sync_next_partial(ctx, filename, flags);
1100N/A
1100N/A rec = hash_lookup(ctx->files, filename);
1100N/A if (rec != NULL) {
1100N/A if ((rec->flags & (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
1100N/A MAILDIR_UIDLIST_REC_FLAG_MOVED)) == 0) {
1100N/A /* possibly duplicate */
1100N/A return 0;
1100N/A }
941N/A
941N/A rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
941N/A MAILDIR_UIDLIST_REC_FLAG_MOVED);
941N/A } else {
941N/A old_rec = hash_lookup(uidlist->files, filename);
941N/A i_assert(old_rec != NULL || ctx->synced);
941N/A
941N/A rec = p_new(ctx->record_pool, struct maildir_uidlist_rec, 1);
941N/A
941N/A if (old_rec != NULL)
941N/A *rec = *old_rec;
941N/A else {
941N/A rec->uid = (uint32_t)-1;
941N/A ctx->new_files_count++;
941N/A }
429N/A
941N/A buffer_append(ctx->record_buf, &rec, sizeof(rec));
941N/A }
941N/A
941N/A if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
941N/A rec->uid != (uint32_t)-1)
429N/A maildir_uidlist_mark_recent(uidlist, rec->uid);
941N/A
941N/A rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
941N/A rec->filename = p_strdup(ctx->record_pool, filename);
941N/A hash_insert(ctx->files, rec->filename, rec);
941N/A return 1;
1007N/A}
1007N/A
1234N/Astatic int maildir_time_cmp(const void *p1, const void *p2)
1007N/A{
1007N/A const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
1007N/A const char *s1 = (*rec1)->filename, *s2 = (*rec2)->filename;
1007N/A time_t t1 = 0, t2 = 0;
1007N/A
1007N/A /* we have to do numeric comparision, strcmp() will break when
1007N/A there's different amount of digits (mostly the 999999999 ->
1007N/A 1000000000 change in Sep 9 2001) */
1007N/A while (*s1 >= '0' && *s1 <= '9') {
1007N/A t1 = t1*10 + (*s1 - '0');
1007N/A s1++;
1007N/A }
1007N/A while (*s2 >= '0' && *s2 <= '9') {
1007N/A t2 = t2*10 + (*s2 - '0');
1007N/A s2++;
1007N/A }
1007N/A
1007N/A return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
1007N/A}
1007N/A
1007N/Astatic void maildir_uidlist_assign_uids(struct maildir_uidlist *uidlist,
1007N/A unsigned int first_new_pos)
1007N/A{
1007N/A struct maildir_uidlist_rec **rec_p;
1007N/A unsigned int dest;
1007N/A size_t size;
1007N/A
1007N/A rec_p = buffer_get_modifyable_data(uidlist->record_buf, &size);
941N/A size /= sizeof(*rec_p);
941N/A
941N/A /* sort new files and assign UIDs for them */
144N/A qsort(rec_p + first_new_pos, size - first_new_pos,
144N/A sizeof(*rec_p), maildir_time_cmp);
1472N/A for (dest = first_new_pos; dest < size; dest++) {
1472N/A i_assert(rec_p[dest]->uid == (uint32_t)-1);
1472N/A rec_p[dest]->uid = uidlist->next_uid++;
1472N/A rec_p[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
1352N/A
1516N/A if ((rec_p[dest]->flags &
1890N/A MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
1890N/A maildir_uidlist_mark_recent(uidlist, rec_p[dest]->uid);
1890N/A }
1890N/A}
1890N/A
1352N/Astatic int maildir_uid_cmp(const void *p1, const void *p2)
1472N/A{
1352N/A const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
1352N/A
1352N/A return (*rec1)->uid < (*rec2)->uid ? -1 :
1352N/A (*rec1)->uid > (*rec2)->uid ? 1 : 0;
1352N/A}
1352N/A
1352N/Astatic void maildir_uidlist_swap(struct maildir_uidlist_sync_ctx *ctx)
2073N/A{
1352N/A struct maildir_uidlist *uidlist = ctx->uidlist;
429N/A struct maildir_uidlist_rec **rec_p;
429N/A size_t size;
144N/A
1386N/A /* buffer is unsorted, sort it by UID up to beginning of new messages */
1386N/A rec_p = buffer_get_modifyable_data(ctx->record_buf, &size);
1386N/A size /= sizeof(*rec_p);
1636N/A qsort(rec_p, size, sizeof(*rec_p), maildir_uid_cmp);
1636N/A
1636N/A buffer_free(uidlist->record_buf);
1636N/A uidlist->record_buf = ctx->record_buf;
2073N/A ctx->record_buf = NULL;
1636N/A
2073N/A hash_destroy(uidlist->files);
1636N/A uidlist->files = ctx->files;
1636N/A ctx->files = NULL;
1636N/A
1386N/A if (uidlist->record_pool != NULL)
1636N/A pool_unref(uidlist->record_pool);
1636N/A uidlist->record_pool = ctx->record_pool;
1636N/A ctx->record_pool = NULL;
1636N/A
2073N/A if (ctx->new_files_count != 0) {
1636N/A maildir_uidlist_assign_uids(uidlist,
2284N/A size - ctx->new_files_count);
1636N/A }
1636N/A}
1636N/A
1386N/Aint maildir_uidlist_sync_finish(struct maildir_uidlist_sync_ctx *ctx)
315N/A{
315N/A if (!ctx->partial) {
315N/A if (!ctx->failed && !ctx->locked)
315N/A maildir_uidlist_swap(ctx);
315N/A } else {
315N/A if (ctx->new_files_count != 0) {
315N/A maildir_uidlist_assign_uids(ctx->uidlist,
315N/A ctx->first_new_pos);
1636N/A }
1636N/A }
1636N/A ctx->finished = TRUE;
1636N/A return !ctx->locked;
1636N/A}
1636N/A
2073N/Aint maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx *ctx)
1636N/A{
2073N/A int ret = ctx->failed ? -1 : 0;
1636N/A
1636N/A if (!ctx->finished)
144N/A maildir_uidlist_sync_finish(ctx);
838N/A
838N/A if (ctx->new_files_count != 0 && !ctx->failed && !ctx->locked)
838N/A ret = maildir_uidlist_rewrite(ctx->uidlist);
926N/A
926N/A if (ctx->partial)
926N/A maildir_uidlist_mark_all(ctx->uidlist, FALSE);
926N/A
838N/A if (UIDLIST_IS_LOCKED(ctx->uidlist))
1231N/A maildir_uidlist_unlock(ctx->uidlist);
1231N/A
2091N/A if (ctx->files != NULL)
1231N/A hash_destroy(ctx->files);
1231N/A if (ctx->record_pool != NULL)
1231N/A pool_unref(ctx->record_pool);
1231N/A if (ctx->record_buf != NULL)
1231N/A buffer_free(ctx->record_buf);
205N/A i_free(ctx);
257N/A return ret;
257N/A}
257N/A
205N/Astruct maildir_uidlist_iter_ctx *
205N/Amaildir_uidlist_iter_init(struct maildir_uidlist *uidlist)
1461N/A{
1461N/A struct maildir_uidlist_iter_ctx *ctx;
1461N/A size_t size;
1461N/A
1461N/A ctx = i_new(struct maildir_uidlist_iter_ctx, 1);
1461N/A ctx->next = buffer_get_data(uidlist->record_buf, &size);
1461N/A size /= sizeof(*ctx->next);
1461N/A ctx->end = ctx->next + size;
1461N/A return ctx;
1461N/A}
1461N/A
1461N/Aint maildir_uidlist_iter_next(struct maildir_uidlist_iter_ctx *ctx,
1044N/A uint32_t *uid_r,
1044N/A enum maildir_uidlist_rec_flag *flags_r,
1044N/A const char **filename_r)
1044N/A{
1044N/A if (ctx->next == ctx->end)
1044N/A return 0;
1044N/A
1044N/A *uid_r = (*ctx->next)->uid;
1044N/A *flags_r = (*ctx->next)->flags;
1044N/A *filename_r = (*ctx->next)->filename;
2286N/A ctx->next++;
1044N/A return 1;
1044N/A}
205N/A
838N/Avoid maildir_uidlist_iter_deinit(struct maildir_uidlist_iter_ctx *ctx)
838N/A{
205N/A i_free(ctx);
296N/A}
296N/A