From: Timo Sirainen Date: Thu, 10 Mar 2011 16:32:08 +0000 (+0200) Subject: imapc: Initial support for local index files. X-Git-Tag: 2.1.alpha1~302 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d83e46e7cd1ffd76210823dadcac549124c96d4e;p=thirdparty%2Fdovecot%2Fcore.git imapc: Initial support for local index files. --- diff --git a/src/lib-storage/index/imapc/imapc-list.c b/src/lib-storage/index/imapc/imapc-list.c index 72ba525b4f..7217ed525d 100644 --- a/src/lib-storage/index/imapc/imapc-list.c +++ b/src/lib-storage/index/imapc/imapc-list.c @@ -182,22 +182,84 @@ static char imapc_list_get_hierarchy_sep(struct mailbox_list *_list) return list->sep; } +static struct mailbox_list *imapc_list_get_fs(struct imapc_mailbox_list *list) +{ + struct mailbox_list_settings list_set; + const char *error, *dir; + + dir = list->list.set.index_dir; + if (dir == NULL) + dir = list->list.set.root_dir; + + if (dir == NULL) { + /* indexes disabled */ + } else if (list->index_list == NULL && !list->index_list_failed) { + memset(&list_set, 0, sizeof(list_set)); + list_set.layout = MAILBOX_LIST_NAME_MAILDIRPLUSPLUS; + /* the root dir shouldn't actually ever be used. we just need + it to be different from index_dir so the index directories + get autocreated */ + list_set.root_dir = t_strconcat(dir, "/", NULL); + list_set.index_dir = dir; + list_set.escape_char = '%'; + + if (mailbox_list_create(list_set.layout, list->list.ns, + &list_set, MAILBOX_LIST_FLAG_SECONDARY, + &list->index_list, &error) < 0) { + i_error("imapc: Couldn't create %s mailbox list: %s", + list_set.layout, error); + list->index_list_failed = TRUE; + } + } + return list->index_list; +} + static const char * -imapc_list_get_path(struct mailbox_list *list ATTR_UNUSED, - const char *name ATTR_UNUSED, +imapc_list_get_fs_name(struct imapc_mailbox_list *list, const char *name) +{ + struct mailbox_list *fs_list = imapc_list_get_fs(list); + const char *vname; + + if (name == NULL) + return name; + + vname = mailbox_list_get_vname(&list->list, name); + return mailbox_list_get_storage_name(fs_list, vname); +} + +static const char * +imapc_list_get_path(struct mailbox_list *_list, const char *name, enum mailbox_list_path_type type) { - if (type == MAILBOX_LIST_PATH_TYPE_INDEX) - return ""; - return NULL; + struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; + struct mailbox_list *fs_list = imapc_list_get_fs(list); + const char *fs_name; + + if (fs_list != NULL) { + fs_name = imapc_list_get_fs_name(list, name); + return mailbox_list_get_path(fs_list, fs_name, type); + } else { + if (type == MAILBOX_LIST_PATH_TYPE_INDEX) + return ""; + return NULL; + } } static const char * -imapc_list_get_temp_prefix(struct mailbox_list *list, bool global ATTR_UNUSED) +imapc_list_get_temp_prefix(struct mailbox_list *_list, bool global) { - i_panic("imapc: Can't return a temp prefix for '%s'", - list->ns->prefix); - return NULL; + struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; + struct mailbox_list *fs_list = imapc_list_get_fs(list); + + if (fs_list != NULL) { + return global ? + mailbox_list_get_global_temp_prefix(fs_list) : + mailbox_list_get_temp_prefix(fs_list); + } else { + i_panic("imapc: Can't return a temp prefix for '%s'", + _list->ns->prefix); + return NULL; + } } static const char * @@ -422,9 +484,15 @@ imapc_list_delete_mailbox(struct mailbox_list *_list, const char *name) } static int -imapc_list_delete_dir(struct mailbox_list *list ATTR_UNUSED, - const char *name ATTR_UNUSED) +imapc_list_delete_dir(struct mailbox_list *_list, const char *name) { + struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)_list; + struct mailbox_list *fs_list = imapc_list_get_fs(list); + + if (fs_list != NULL) { + name = imapc_list_get_fs_name(list, name); + (void)mailbox_list_delete_dir(fs_list, name); + } return 0; } @@ -434,6 +502,7 @@ imapc_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, bool rename_children) { struct imapc_mailbox_list *list = (struct imapc_mailbox_list *)oldlist; + struct mailbox_list *fs_list = imapc_list_get_fs(list); struct imapc_simple_context ctx; if (!rename_children) { @@ -453,6 +522,13 @@ imapc_list_rename_mailbox(struct mailbox_list *oldlist, const char *oldname, imapc_list_simple_callback, &ctx, "RENAME %s %s", oldname, newname); imapc_simple_run(&ctx); + if (ctx.ret == 0 && fs_list != NULL && oldlist == newlist) { + oldname = imapc_list_get_fs_name(list, oldname); + newname = imapc_list_get_fs_name(list, newname); + (void)fs_list->v.rename_mailbox(fs_list, oldname, + fs_list, newname, + rename_children); + } return ctx.ret; } diff --git a/src/lib-storage/index/imapc/imapc-list.h b/src/lib-storage/index/imapc/imapc-list.h index 73c48c4913..90cc35beeb 100644 --- a/src/lib-storage/index/imapc/imapc-list.h +++ b/src/lib-storage/index/imapc/imapc-list.h @@ -10,6 +10,7 @@ struct imap_arg; struct imapc_mailbox_list { struct mailbox_list list; struct imapc_storage *storage; + struct mailbox_list *index_list; struct mailbox_tree_context *mailboxes, *tmp_subscriptions; char sep; @@ -21,6 +22,7 @@ struct imapc_mailbox_list { unsigned int broken:1; unsigned int refreshed_subscriptions:1; unsigned int refreshed_mailboxes:1; + unsigned int index_list_failed:1; }; void imapc_list_register_callbacks(struct imapc_mailbox_list *list); diff --git a/src/lib-storage/index/imapc/imapc-mailbox.c b/src/lib-storage/index/imapc/imapc-mailbox.c index 53d23f4ff0..6677535c46 100644 --- a/src/lib-storage/index/imapc/imapc-mailbox.c +++ b/src/lib-storage/index/imapc/imapc-mailbox.c @@ -6,6 +6,7 @@ #include "imap-util.h" #include "imapc-client.h" #include "imapc-seqmap.h" +#include "imapc-sync.h" #include "imapc-storage.h" #define NOTIFY_DELAY_MSECS 500 @@ -37,6 +38,27 @@ static void imapc_mailbox_init_delayed_trans(struct imapc_mailbox *mbox) mail_index_transaction_open_updated_view(mbox->delayed_sync_trans); } +int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox, + bool *changes_r) +{ + int ret = 0; + + *changes_r = FALSE; + + if (mbox->delayed_sync_view != NULL) + mail_index_view_close(&mbox->delayed_sync_view); + if (mbox->delayed_sync_trans != NULL) { + if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) { + mail_storage_set_index_error(&mbox->box); + ret = -1; + } + *changes_r = TRUE; + } + if (mbox->sync_view != NULL) + mail_index_view_close(&mbox->sync_view); + return ret; +} + static void imapc_newmsgs_callback(const struct imapc_command_reply *reply, void *context) @@ -63,7 +85,7 @@ static void imapc_untagged_exists(const struct imapc_untagged_reply *reply, uint32_t rcount = reply->num; const struct mail_index_header *hdr; struct imapc_seqmap *seqmap; - uint32_t next_lseq, next_rseq; + uint32_t first_uid, next_lseq, next_rseq; if (mbox == NULL) return; @@ -74,14 +96,19 @@ static void imapc_untagged_exists(const struct imapc_untagged_reply *reply, seqmap = imapc_client_mailbox_get_seqmap(mbox->client_box); next_lseq = mail_index_view_get_messages_count(view) + 1; next_rseq = imapc_seqmap_lseq_to_rseq(seqmap, next_lseq); - if (next_rseq > rcount) - return; - - hdr = mail_index_get_header(view); + if (next_rseq > rcount) { + if (rcount == 0 || !mbox->opening) + return; + /* initial SELECT. we don't know what the flags are. */ + first_uid = 1; + } else { + hdr = mail_index_get_header(view); + first_uid = hdr->next_uid; + } mbox->new_msgs = TRUE; imapc_client_mailbox_cmdf(mbox->client_box, imapc_newmsgs_callback, - mbox, "UID FETCH %u:* FLAGS", hdr->next_uid); + mbox, "UID FETCH %u:* FLAGS", first_uid); } static void imapc_mailbox_idle_timeout(struct imapc_mailbox *mbox) @@ -101,6 +128,28 @@ static void imapc_mailbox_idle_notify(struct imapc_mailbox *mbox) } } +static bool keywords_are_equal(struct mail_keywords *kw, + const ARRAY_TYPE(keyword_indexes) *kw_arr) +{ + const unsigned int *kw_idx; + unsigned int i, j, count; + + kw_idx = array_get(kw_arr, &count); + if (count != kw->count) + return FALSE; + + /* there are normally only a few keywords, so O(n^2) is fine */ + for (i = 0; i < count; i++) { + for (j = 0; j < count; j++) { + if (kw->idx[i] == kw_idx[j]) + break; + } + if (j == count) + return FALSE; + } + return TRUE; +} + static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { @@ -158,32 +207,49 @@ static void imapc_untagged_fetch(const struct imapc_untagged_reply *reply, imapc_mailbox_init_delayed_trans(mbox); old_count = mail_index_view_get_messages_count(mbox->delayed_sync_view); + while (lseq <= old_count) { + rec = mail_index_lookup(mbox->delayed_sync_view, lseq); + if (rec->uid == uid || uid == 0) + break; + + if (!mbox->opening || uid < rec->uid) { + imapc_mailbox_set_corrupted(mbox, + "Message UID changed %u -> %u", rec->uid, uid); + return; + } + /* we're opening the mailbox. this message was expunged + externally, so expunge it ourself too. */ + imapc_seqmap_expunge(seqmap, rseq); + mail_index_expunge(mbox->delayed_sync_trans, lseq); + lseq++; + } if (lseq > old_count) { if (uid == 0 || lseq != old_count + 1) return; i_assert(lseq == old_count + 1); mail_index_append(mbox->delayed_sync_trans, uid, &lseq); } - rec = mail_index_lookup(mbox->delayed_sync_view, lseq); - if (rec->uid != uid && uid != 0) { - imapc_mailbox_set_corrupted(mbox, "Message UID changed %u -> %u", - rec->uid, uid); - return; - } if (seen_flags && rec->flags != flags) { mail_index_update_flags(mbox->delayed_sync_trans, lseq, MODIFY_REPLACE, flags); } - if (seen_flags) { + if (seen_flags) T_BEGIN { + ARRAY_TYPE(keyword_indexes) old_kws; struct mail_keywords *kw; + t_array_init(&old_kws, 8); + mail_index_lookup_keywords(mbox->delayed_sync_view, lseq, + &old_kws); + (void)array_append_space(&keywords); kw = mail_index_keywords_create(mbox->box.index, array_idx(&keywords, 0)); - mail_index_update_keywords(mbox->delayed_sync_trans, lseq, - MODIFY_REPLACE, kw); + if (!keywords_are_equal(kw, &old_kws)) { + mail_index_update_keywords(mbox->delayed_sync_trans, + lseq, MODIFY_REPLACE, kw); + } mail_index_keywords_unref(&kw); - } + } T_END; imapc_mailbox_idle_notify(mbox); } @@ -222,32 +288,47 @@ static void imapc_resp_text_uidvalidity(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { + const struct mail_index_header *hdr; uint32_t uid_validity; + bool changes; if (mbox == NULL || reply->resp_text_value == NULL || str_to_uint32(reply->resp_text_value, &uid_validity) < 0) return; - imapc_mailbox_init_delayed_trans(mbox); - mail_index_update_header(mbox->delayed_sync_trans, - offsetof(struct mail_index_header, uid_validity), - &uid_validity, sizeof(uid_validity), TRUE); + hdr = mail_index_get_header(mbox->box.view); + if (hdr->uid_validity != uid_validity) { + imapc_mailbox_init_delayed_trans(mbox); + if (hdr->uid_validity != 0) { + /* uidvalidity changed, reset the entire mailbox */ + mail_index_reset(mbox->delayed_sync_trans); + (void)imapc_mailbox_commit_delayed_trans(mbox, &changes); + imapc_mailbox_init_delayed_trans(mbox); + } + mail_index_update_header(mbox->delayed_sync_trans, + offsetof(struct mail_index_header, uid_validity), + &uid_validity, sizeof(uid_validity), TRUE); + } } static void imapc_resp_text_uidnext(const struct imapc_untagged_reply *reply, struct imapc_mailbox *mbox) { + const struct mail_index_header *hdr; uint32_t uid_next; if (mbox == NULL || reply->resp_text_value == NULL || str_to_uint32(reply->resp_text_value, &uid_next) < 0) return; - imapc_mailbox_init_delayed_trans(mbox); - mail_index_update_header(mbox->delayed_sync_trans, - offsetof(struct mail_index_header, next_uid), - &uid_next, sizeof(uid_next), FALSE); + hdr = mail_index_get_header(mbox->box.view); + if (hdr->next_uid != uid_next) { + imapc_mailbox_init_delayed_trans(mbox); + mail_index_update_header(mbox->delayed_sync_trans, + offsetof(struct mail_index_header, next_uid), + &uid_next, sizeof(uid_next), FALSE); + } } void imapc_mailbox_register_untagged(struct imapc_mailbox *mbox, diff --git a/src/lib-storage/index/imapc/imapc-storage.c b/src/lib-storage/index/imapc/imapc-storage.c index d9edb39278..2369739303 100644 --- a/src/lib-storage/index/imapc/imapc-storage.c +++ b/src/lib-storage/index/imapc/imapc-storage.c @@ -267,8 +267,6 @@ imapc_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, struct index_mailbox_context *ibox; pool_t pool; - flags |= MAILBOX_FLAG_NO_INDEX_FILES; - pool = pool_alloconly_create("imapc mailbox", 1024*3); mbox = p_new(pool, struct imapc_mailbox, 1); mbox->box = imapc_mailbox; @@ -277,7 +275,8 @@ imapc_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, mbox->box.list = list; mbox->box.mail_vfuncs = &imapc_mail_vfuncs; - index_storage_mailbox_alloc(&mbox->box, vname, flags, NULL); + index_storage_mailbox_alloc(&mbox->box, vname, flags, + IMAPC_INDEX_PREFIX); ibox = INDEX_STORAGE_CONTEXT(&mbox->box); ibox->save_commit_pre = imapc_transaction_save_commit_pre; diff --git a/src/lib-storage/index/imapc/imapc-storage.h b/src/lib-storage/index/imapc/imapc-storage.h index f1b8f0ba70..27fedaa268 100644 --- a/src/lib-storage/index/imapc/imapc-storage.h +++ b/src/lib-storage/index/imapc/imapc-storage.h @@ -4,6 +4,7 @@ #include "index-storage.h" #define IMAPC_STORAGE_NAME "imapc" +#define IMAPC_INDEX_PREFIX "dovecot.index" struct imap_arg; struct imapc_untagged_reply; @@ -102,6 +103,8 @@ void imapc_async_stop_callback(const struct imapc_command_reply *reply, void *context); int imapc_mailbox_get_client_box(struct imapc_mailbox *mbox, struct imapc_client_mailbox **client_box_r); +int imapc_mailbox_commit_delayed_trans(struct imapc_mailbox *mbox, + bool *changes_r); void imapc_storage_register_untagged(struct imapc_storage *storage, const char *name, diff --git a/src/lib-storage/index/imapc/imapc-sync.c b/src/lib-storage/index/imapc/imapc-sync.c index 45e312657f..0fa1cdd277 100644 --- a/src/lib-storage/index/imapc/imapc-sync.c +++ b/src/lib-storage/index/imapc/imapc-sync.c @@ -309,7 +309,7 @@ imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) { struct imapc_mailbox *mbox = (struct imapc_mailbox *)box; enum imapc_capability capabilities; - bool changes = FALSE; + bool changes; int ret = 0; if (!box->opened) { @@ -327,18 +327,8 @@ imapc_mailbox_sync_init(struct mailbox *box, enum mailbox_sync_flags flags) imapc_client_run(mbox->storage->client); } - if (mbox->delayed_sync_view != NULL) - mail_index_view_close(&mbox->delayed_sync_view); - if (mbox->delayed_sync_trans != NULL) { - if (mail_index_transaction_commit(&mbox->delayed_sync_trans) < 0) { - mail_storage_set_index_error(&mbox->box); - ret = -1; - } - changes = TRUE; - } - if (mbox->sync_view != NULL) - mail_index_view_close(&mbox->sync_view); - + if (imapc_mailbox_commit_delayed_trans(mbox, &changes) < 0) + ret = -1; if ((changes || index_mailbox_want_full_sync(&mbox->box, flags)) && ret == 0) ret = imapc_sync(mbox); diff --git a/src/lib-storage/index/imapc/imapc-sync.h b/src/lib-storage/index/imapc/imapc-sync.h index d83b240a05..de0da627a6 100644 --- a/src/lib-storage/index/imapc/imapc-sync.h +++ b/src/lib-storage/index/imapc/imapc-sync.h @@ -1,7 +1,9 @@ #ifndef CYDIR_SYNC_H #define CYDIR_SYNC_H +enum mailbox_sync_flags; struct mailbox; +struct mailbox_sync_status; struct imapc_sync_context { struct imapc_mailbox *mbox;