data->flags.flags = data->rec->flags;
/*FIXME:data->flags.custom_flags =
mail_custom_flags_list_get(mail->ibox->index->custom_flags);
- data->flags.custom_flags_count = MAIL_CUSTOM_FLAGS_COUNT;
-
- if (data->rec->uid >= mail->ibox->index->first_recent_uid)
- data->flags.flags |= MAIL_RECENT;*/
+ data->flags.custom_flags_count = MAIL_CUSTOM_FLAGS_COUNT;*/
return &data->flags;
}
/* Returns >0 = matched, 0 = not matched, -1 = unknown */
static int search_arg_match_index(struct index_mailbox *ibox,
- const struct mail_index_record *rec,
+ struct index_mail *imail,
enum mail_search_arg_type type,
const char *value)
{
+ const struct mail_index_record *rec = imail->data.rec;
+ const struct mail_full_flags *full_flags;
+
switch (type) {
case SEARCH_ALL:
return 1;
case SEARCH_SEEN:
return rec->flags & MAIL_SEEN;
case SEARCH_RECENT:
- //FIXME:return rec->uid >= ibox->index->first_recent_uid;
- return FALSE;
+ full_flags = imail->mail.get_flags(&imail->mail);
+ return full_flags->flags & MAIL_RECENT;
case SEARCH_KEYWORD:
return search_keyword(ibox->index, rec, value);
return;
}
- switch (search_arg_match_index(ctx->ibox, ctx->imail.data.rec,
+ switch (search_arg_match_index(ctx->ibox, &ctx->imail,
arg->type, arg->value.str)) {
case -1:
/* unknown */
}
}
- /*FIXME:if (items & STATUS_RECENT)
- status->recent = index_storage_get_recent_count(view);*/
+ if ((items & STATUS_RECENT) != 0)
+ status->recent = ibox->get_recent_count(ibox);
/*FIXME:if (items & STATUS_CUSTOM_FLAGS)
get_custom_flags(ibox, status);*/
struct mail_cache_view *cache_view;
struct mail *mail_interface;
+ uint32_t (*get_recent_count)(struct index_mailbox *ibox);
+
struct timeout *autosync_to;
struct index_autosync_file *autosync_files;
struct index_autosync_io *autosync_ios;
size_t i, expunges_count;
void *sc_context;
enum mail_index_sync_type sync_mask;
- uint32_t seq, new_count;
+ uint32_t seq, new_count, recent_count;
int ret, appends;
sync_mask = MAIL_INDEX_SYNC_MASK_ALL;
if (appends) {
new_count = mail_index_view_get_message_count(ibox->view);
- sc->new_messages(&ibox->box, new_count, 0, sc_context);
+ recent_count = ibox->get_recent_count(ibox);
+ sc->new_messages(&ibox->box, new_count, recent_count,
+ sc_context);
}
mail_index_view_unlock(ibox->view);
}
}
+static const struct mail_full_flags *maildir_mail_get_flags(struct mail *_mail)
+{
+ struct index_mail *mail = (struct index_mail *)_mail;
+ struct index_mail_data *data = &mail->data;
+ const struct mail_full_flags *flags;
+
+ flags = index_mail_get_flags(_mail);
+
+ if (maildir_uidlist_is_recent(mail->ibox->uidlist, _mail->uid))
+ data->flags.flags |= MAIL_RECENT;
+ return &data->flags;
+}
+
static time_t maildir_mail_get_received_date(struct mail *_mail)
{
struct index_mail *mail = (struct index_mail *) _mail;
struct index_mail_data *data = &mail->data;
const char *fname, *p;
uoff_t virtual_size;
- int new_dir;
+ enum maildir_uidlist_rec_flag flags;
if (data->size != (uoff_t)-1)
return data->size;
}
fname = maildir_uidlist_lookup(mail->ibox->uidlist,
- mail->mail.uid, &new_dir);
+ mail->mail.uid, &flags);
if (fname == NULL)
return (uoff_t)-1;
struct mail maildir_mail = {
0, 0, 0, 0, 0, 0,
- index_mail_get_flags,
+ maildir_mail_get_flags,
index_mail_get_parts,
maildir_mail_get_received_date,
index_mail_get_date,
return 0;
}
+static uint32_t maildir_get_recent_count(struct index_mailbox *ibox)
+{
+ return maildir_uidlist_get_recent_count(ibox->uidlist);
+}
+
static struct mailbox *
maildir_open(struct index_storage *storage, const char *name,
enum mailbox_open_flags flags)
ibox->path = i_strdup(path);
ibox->control_dir = i_strdup(control_dir);
+ ibox->get_recent_count = maildir_get_recent_count;
ibox->mail_interface = &maildir_mail;
ibox->uidlist = maildir_uidlist_init(ibox);
DIR *dirp;
string_t *src, *dest;
struct dirent *dp;
- int move_new, this_new, ret = 1;
+ enum maildir_uidlist_rec_flag flags;
+ int move_new, ret = 1;
src = t_str_new(1024);
dest = t_str_new(1024);
return -1;
}
- move_new = new_dir;
+ move_new = new_dir && !mailbox_is_readonly(&ctx->ibox->box);
while ((dp = readdir(dirp)) != NULL) {
if (dp->d_name[0] == '.')
continue;
- this_new = new_dir;
+ flags = 0;
if (move_new) {
str_truncate(src, 0);
str_truncate(dest, 0);
str_printfa(src, "%s/%s", ctx->new_dir, dp->d_name);
str_printfa(dest, "%s/%s", ctx->cur_dir, dp->d_name);
- if (rename(str_c(src), str_c(dest)) == 0 ||
- ENOTFOUND(errno)) {
- /* moved - we'll look at it later in cur/ dir */
- this_new = FALSE;
- continue;
+ if (rename(str_c(src), str_c(dest)) == 0) {
+ /* we moved it - it's \Recent for use */
+ flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
+ MAILDIR_UIDLIST_REC_FLAG_RECENT;
+ } else if (ENOTFOUND(errno)) {
+ /* someone else moved it already */
+ flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED;
} else if (ENOSPACE(errno)) {
/* not enough disk space, leave here */
+ flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
+ MAILDIR_UIDLIST_REC_FLAG_RECENT;
move_new = FALSE;
} else {
+ flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
+ MAILDIR_UIDLIST_REC_FLAG_RECENT;
mail_storage_set_critical(storage,
"rename(%s, %s) failed: %m",
str_c(src), str_c(dest));
}
+ } else if (new_dir) {
+ flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
+ MAILDIR_UIDLIST_REC_FLAG_RECENT;
}
ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
- dp->d_name, this_new);
+ dp->d_name, flags);
if (ret <= 0) {
if (ret < 0)
break;
}
maildir_filename_get_flags(filename, &flags, custom_flags);
- if (rec->flags & MAIL_RECENT)
- flags |= MAIL_RECENT;
if ((uint8_t)flags != (rec->flags & MAIL_FLAGS_MASK) ||
memcmp(custom_flags, rec->custom_flags,
INDEX_CUSTOM_FLAGS_BYTE_COUNT) != 0) {
return ret;
}
-static int maildir_sync_context(struct maildir_sync_context *ctx,
- int *changes_r)
+static int maildir_sync_context(struct maildir_sync_context *ctx)
{
int ret, new_changed, cur_changed;
if (maildir_sync_quick_check(ctx, &new_changed, &cur_changed) < 0)
return -1;
+ if (!new_changed && !cur_changed)
+ return 0;
+
+ // FIXME: don't sync cur/ directory if not needed
ctx->uidlist_sync_ctx = maildir_uidlist_sync_init(ctx->ibox->uidlist);
if (maildir_scan_dir(ctx, TRUE) < 0)
{
struct index_mailbox *ibox = (struct index_mailbox *)box;
struct maildir_sync_context *ctx;
- int changes, ret;
+ int ret;
if ((flags & MAILBOX_SYNC_FLAG_FAST) == 0 ||
ibox->sync_last_check + MAILBOX_FULL_SYNC_INTERVAL <= ioloop_time) {
ibox->sync_last_check = ioloop_time;
ctx = maildir_sync_context_new(ibox);
- ret = maildir_sync_context(ctx, &changes);
+ ret = maildir_sync_context(ctx);
maildir_sync_deinit(ctx);
if (ret < 0)
#define UIDLIST_IS_LOCKED(uidlist) \
((uidlist)->lock_fd != -1)
-#define MAILDIR_UIDLIST_REC_FLAG_NEW_DIR 0x01
-
struct maildir_uidlist_rec {
uint32_t uid;
uint32_t flags;
unsigned int version;
unsigned int uid_validity, next_uid, last_read_uid;
+ uint32_t first_recent_uid;
};
struct maildir_uidlist_sync_ctx {
pool_t filename_pool;
struct hash_table *files;
+ buffer_t *new_record_buf;
- struct maildir_uidlist_rec new_rec, cur_rec;
unsigned int new_files:1;
unsigned int synced:1;
unsigned int failed:1;
i_free(uidlist);
}
+static void
+maildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
+{
+ if (uidlist->first_recent_uid == 0)
+ uidlist->first_recent_uid = uid;
+ i_assert(uid >= uidlist->first_recent_uid);
+}
+
static int maildir_uidlist_next(struct maildir_uidlist *uidlist,
- const char *line)
+ const char *line, uint32_t last_uid)
{
struct maildir_uidlist_rec *rec;
uint32_t uid, flags;
line++;
}
+ if (uid <= last_uid) {
+ /* we already have this */
+ return 1;
+ }
+
if (uid == 0 || *line != ' ') {
/* invalid file */
mail_storage_set_critical(uidlist->ibox->box.storage,
int maildir_uidlist_update(struct maildir_uidlist *uidlist)
{
struct mail_storage *storage = uidlist->ibox->box.storage;
+ const struct maildir_uidlist_rec *rec;
const char *line;
struct istream *input;
struct stat st;
+ uint32_t last_uid;
+ size_t size;
int fd, ret;
if (uidlist->last_mtime != 0) {
return -1;
}
- hash_clear(uidlist->files, FALSE);
- if (uidlist->filename_pool != NULL)
- p_clear(uidlist->filename_pool);
- else {
+ if (uidlist->filename_pool == NULL) {
uidlist->filename_pool =
pool_alloconly_create("uidlist filename_pool",
nearest_power(st.st_size -
st.st_size/8));
}
- buffer_set_used_size(uidlist->record_buf, 0);
+ rec = buffer_get_data(uidlist->record_buf, &size);
+ last_uid = size == 0 ? 0 : rec[(size / sizeof(*rec))-1].uid;
+
uidlist->version = 0;
input = i_stream_create_file(fd, default_pool, 4096, TRUE);
} else {
ret = 1;
while ((line = i_stream_read_next_line(input)) != NULL) {
- if (!maildir_uidlist_next(uidlist, line)) {
+ if (!maildir_uidlist_next(uidlist, line, last_uid)) {
ret = 0;
break;
}
return ret;
}
-const char *maildir_uidlist_lookup(struct maildir_uidlist *uidlist,
- uint32_t uid, int *new_dir_r)
+static const struct maildir_uidlist_rec *
+maildir_uidlist_lookup_rec(struct maildir_uidlist *uidlist, uint32_t uid,
+ unsigned int *idx_r)
{
const struct maildir_uidlist_rec *rec;
unsigned int idx, left_idx, right_idx;
else if (rec[idx].uid > uid)
right_idx = idx;
else {
- *new_dir_r = (rec[idx].flags &
- MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0;
- return rec[idx].filename;
+ *idx_r = idx;
+ return &rec[idx];
}
}
+ if (idx > 0) idx--;
+ *idx_r = idx;
return NULL;
}
+const char *
+maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
+ enum maildir_uidlist_rec_flag *flags_r)
+{
+ const struct maildir_uidlist_rec *rec;
+ unsigned int idx;
+
+ rec = maildir_uidlist_lookup_rec(uidlist, uid, &idx);
+ if (rec == NULL)
+ return NULL;
+
+ *flags_r = rec->flags;
+ return rec->filename;
+}
+
+int maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
+{
+ enum maildir_uidlist_rec_flag flags;
+
+ if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
+ return FALSE;
+
+ if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
+ return FALSE;
+
+ i_assert(uidlist->first_recent_uid != uid ||
+ (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
+ return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
+}
+
+uint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist)
+{
+ const struct maildir_uidlist_rec *rec;
+ unsigned int idx;
+ size_t size;
+ uint32_t count;
+
+ if (uidlist->first_recent_uid == 0)
+ return 0;
+
+ rec = buffer_get_data(uidlist->record_buf, &size);
+ size /= sizeof(*rec);
+
+ maildir_uidlist_lookup(uidlist, uidlist->first_recent_uid, &idx);
+ for (count = 0; idx < size; idx++) {
+ if ((rec[idx].flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
+ count++;
+ }
+ return count;
+}
+
static int maildir_uidlist_rewrite_fd(struct maildir_uidlist *uidlist,
const char *temp_path)
{
ctx->uidlist = uidlist;
ctx->filename_pool =
pool_alloconly_create("maildir_uidlist_sync", 16384);
+ ctx->new_record_buf =
+ buffer_create_dynamic(default_pool, 512, (size_t)-1);
ctx->files = hash_create(default_pool, ctx->filename_pool, 4096,
maildir_hash, maildir_cmp);
}
int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
- const char *filename, int new_dir)
+ const char *filename,
+ enum maildir_uidlist_rec_flag flags)
{
struct maildir_uidlist_rec *rec;
char *fname;
rec = hash_lookup(ctx->files, filename);
if (rec != NULL) {
- if ((rec->flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) == 0) {
+ 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;
+ rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
+ MAILDIR_UIDLIST_REC_FLAG_MOVED);
} else {
rec = hash_lookup(ctx->uidlist->files, filename);
if (rec == NULL && !ctx->synced) {
if (rec == NULL) {
ctx->new_files = TRUE;
- rec = new_dir ? &ctx->new_rec : &ctx->cur_rec;
+ rec = buffer_append_space_unsafe(ctx->new_record_buf,
+ sizeof(*rec));
+ memset(rec, 0, sizeof(*rec));
}
}
+ rec->flags |= flags;
+
fname = p_strdup(ctx->filename_pool, filename);
+ if (rec->filename == NULL)
+ rec->filename = fname;
hash_insert(ctx->files, fname, rec);
return 1;
}
{
struct maildir_uidlist *uidlist = ctx->uidlist;
struct maildir_uidlist_rec *rec;
- struct hash_iterate_context *iter;
void *key, *value;
size_t size;
unsigned int src, dest;
+ /* @UNSAFE */
+
rec = buffer_get_modifyable_data(uidlist->record_buf, &size);
size /= sizeof(*rec);
/* update filename pointers, skip deleted messages */
for (dest = src = 0; src < size; src++) {
- if (hash_lookup_full(ctx->files, rec[src].filename,
- &key, &value)) {
- rec[dest].uid = rec[src].uid;
- rec[dest].flags = rec[src].flags;
- rec[dest].filename = key;
- dest++;
+ if (!hash_lookup_full(ctx->files, rec[src].filename,
+ &key, &value))
+ continue;
+
+ rec[dest].uid = rec[src].uid;
+ rec[dest].flags =
+ rec[src].flags & ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
+ rec[dest].filename = key;
+ if ((rec[dest].flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
+ maildir_uidlist_mark_recent(ctx->uidlist,
+ rec[dest].uid);
}
+ dest++;
}
buffer_set_used_size(uidlist->record_buf, dest * sizeof(*rec));
- /* append new files */
- iter = hash_iterate_init(ctx->files);
- while (hash_iterate(iter, &key, &value)) {
- if (value == &ctx->new_rec ||
- value == &ctx->cur_rec) {
- rec = buffer_append_space_unsafe(uidlist->record_buf,
- sizeof(*rec));
- rec->flags = value == &ctx->cur_rec ?
- 0 : MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
- rec->filename = key;
- hash_update(ctx->files, key, rec);
- }
- }
- hash_iterate_deinit(iter);
+ buffer_append_buf(uidlist->record_buf, ctx->new_record_buf,
+ 0, (size_t)-1);
rec = buffer_get_modifyable_data(uidlist->record_buf, &size);
size /= sizeof(*rec);
/* sort new files and assign UIDs for them */
qsort(rec + dest, size - dest, sizeof(*rec), maildir_time_cmp);
- for (; dest < size; dest++)
+ for (; dest < size; dest++) {
rec[dest].uid = uidlist->next_uid++;
+ rec[dest].flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
+
+ if ((rec[dest].flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
+ maildir_uidlist_mark_recent(ctx->uidlist,
+ rec[dest].uid);
+ }
+ }
hash_destroy(uidlist->files);
uidlist->files = ctx->files;
hash_destroy(ctx->files);
if (ctx->filename_pool != NULL)
pool_unref(ctx->filename_pool);
+ buffer_free(ctx->new_record_buf);
i_free(ctx);
return ret;
}
#define MAILDIR_UIDLIST_NAME "dovecot-uidlist"
+enum maildir_uidlist_rec_flag {
+ MAILDIR_UIDLIST_REC_FLAG_NEW_DIR = 0x01,
+ MAILDIR_UIDLIST_REC_FLAG_MOVED = 0x02,
+ MAILDIR_UIDLIST_REC_FLAG_RECENT = 0x04
+};
+
int maildir_uidlist_try_lock(struct maildir_uidlist *uidlist);
void maildir_uidlist_unlock(struct maildir_uidlist *uidlist);
int maildir_uidlist_update(struct maildir_uidlist *uidlist);
/* Returns uidlist record for given filename, or NULL if not found. */
-const char *maildir_uidlist_lookup(struct maildir_uidlist *uidlist,
- uint32_t uid, int *new_dir_r);
+const char *
+maildir_uidlist_lookup(struct maildir_uidlist *uidlist, uint32_t uid,
+ enum maildir_uidlist_rec_flag *flags_r);
+/* Returns TRUE if mail with given UID is recent. */
+int maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid);
+/* Returns number of recent messages. */
+uint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist);
/* Sync uidlist with what's actually on maildir. */
struct maildir_uidlist_sync_ctx *
maildir_uidlist_sync_init(struct maildir_uidlist *uidlist);
int maildir_uidlist_sync_next(struct maildir_uidlist_sync_ctx *ctx,
- const char *filename, int new_dir);
+ const char *filename,
+ enum maildir_uidlist_rec_flag flags);
int maildir_uidlist_sync_deinit(struct maildir_uidlist_sync_ctx *ctx);
/* List all maildir files. */
maildir_file_do_func *func, void *context)
{
const char *fname, *path;
- int ret, new_dir;
+ enum maildir_uidlist_rec_flag flags;
+ int ret;
- fname = maildir_uidlist_lookup(ibox->uidlist, uid, &new_dir);
+ fname = maildir_uidlist_lookup(ibox->uidlist, uid, &flags);
if (fname == NULL)
return -2; /* expunged */
- if (new_dir) {
+ if ((flags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
/* probably in new/ dir */
path = t_strconcat(ibox->path, "/new/", fname, NULL);
ret = func(ibox, path, context);