mailbox-list-index.c revision 24acd68c82dc137b88bb3ba3258b9d1f7ebcb44d
0N/A/* Copyright (c) 2006-2012 Dovecot authors, see the included COPYING file */
0N/A
0N/A#include "lib.h"
0N/A#include "ioloop.h"
0N/A#include "hash.h"
180N/A#include "str.h"
180N/A#include "mail-index-view-private.h"
51N/A#include "mail-storage-hooks.h"
51N/A#include "mailbox-list-index.h"
51N/A
180N/A#define MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS 1000
180N/A
180N/Astruct mailbox_list_index_module mailbox_list_index_module =
180N/A MODULE_CONTEXT_INIT(&mailbox_list_module_register);
180N/A
0N/Avoid mailbox_list_index_set_index_error(struct mailbox_list *list)
617N/A{
617N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
617N/A
617N/A mailbox_list_set_internal_error(list);
618N/A mail_index_reset_error(ilist->index);
619N/A}
621N/A
617N/Avoid mailbox_list_index_reset(struct mailbox_list_index *ilist)
622N/A{
623N/A i_assert(ilist->iter_refcount == 0);
625N/A
629N/A hash_table_clear(ilist->mailbox_names, FALSE);
630N/A hash_table_clear(ilist->mailbox_hash, FALSE);
631N/A p_clear(ilist->mailbox_pool);
632N/A ilist->mailbox_tree = NULL;
637N/A ilist->highest_name_id = 0;
637N/A ilist->sync_log_file_seq = 0;
637N/A ilist->sync_log_file_offset = 0;
638N/A}
638N/A
639N/Astatic void mailbox_list_index_index_open(struct mailbox_list *list)
640N/A{
641N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
645N/A const struct mail_storage_settings *set = list->mail_set;
647N/A enum mail_index_open_flags index_flags;
648N/A unsigned int lock_timeout;
648N/A
649N/A if (ilist->opened)
617N/A return;
617N/A ilist->opened = TRUE;
565N/A
565N/A index_flags = mail_storage_settings_to_index_flags(set);
565N/A lock_timeout = set->mail_max_lock_timeout == 0 ? -1U :
565N/A set->mail_max_lock_timeout;
565N/A
565N/A mail_index_set_lock_method(ilist->index, set->parsed_lock_method,
579N/A lock_timeout);
568N/A if (mail_index_open_or_create(ilist->index, index_flags) < 0) {
576N/A if (mail_index_move_to_memory(ilist->index) < 0) {
577N/A /* try opening once more. it should be created
580N/A directly into memory now. */
585N/A if (mail_index_open_or_create(ilist->index,
587N/A index_flags) < 0)
593N/A i_panic("in-memory index creation failed");
595N/A }
595N/A }
596N/A}
600N/A
602N/Astruct mailbox_list_index_node *
603N/Amailbox_list_index_node_find_sibling(struct mailbox_list_index_node *node,
604N/A const char *name)
565N/A{
565N/A while (node != NULL) {
525N/A if (strcmp(node->name, name) == 0)
525N/A return node;
525N/A node = node->next;
488N/A }
524N/A return NULL;
524N/A}
524N/A
524N/Astatic struct mailbox_list_index_node *
524N/Amailbox_list_index_lookup_real(struct mailbox_list *list, const char *name)
524N/A{
524N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
524N/A struct mailbox_list_index_node *node = ilist->mailbox_tree;
524N/A const char *const *path;
524N/A unsigned int i;
524N/A char sep[2];
524N/A
524N/A if (*name == '\0')
524N/A return mailbox_list_index_node_find_sibling(node, "");
524N/A
524N/A sep[0] = mailbox_list_get_hierarchy_sep(list); sep[1] = '\0';
524N/A path = t_strsplit(name, sep);
540N/A for (i = 0;; i++) {
544N/A node = mailbox_list_index_node_find_sibling(node, path[i]);
488N/A if (node == NULL || path[i+1] == NULL)
488N/A break;
534N/A node = node->children;
534N/A }
534N/A return node;
534N/A}
534N/A
534N/Astruct mailbox_list_index_node *
534N/Amailbox_list_index_lookup(struct mailbox_list *list, const char *name)
534N/A{
440N/A struct mailbox_list_index_node *node;
440N/A
440N/A T_BEGIN {
440N/A node = mailbox_list_index_lookup_real(list, name);
440N/A } T_END;
442N/A return node;
443N/A}
445N/A
482N/Astruct mailbox_list_index_node *
482N/Amailbox_list_index_lookup_uid(struct mailbox_list_index *ilist, uint32_t uid)
444N/A{
460N/A return hash_table_lookup(ilist->mailbox_hash, POINTER_CAST(uid));
468N/A}
470N/A
472N/Avoid mailbox_list_index_node_get_path(const struct mailbox_list_index_node *node,
440N/A char sep, string_t *str)
440N/A{
336N/A if (node->parent != NULL) {
336N/A mailbox_list_index_node_get_path(node->parent, sep, str);
336N/A str_append_c(str, sep);
336N/A }
365N/A str_append(str, node->name);
408N/A}
336N/A
361N/Astatic int mailbox_list_index_parse_header(struct mailbox_list_index *ilist,
389N/A struct mail_index_view *view)
390N/A{
413N/A const void *data, *p;
430N/A size_t i, len, size;
370N/A uint32_t id, prev_id = 0;
378N/A char *name;
380N/A
392N/A mail_index_map_get_header_ext(view, view->map, ilist->ext_id, &data, &size);
393N/A if (size == 0)
412N/A return 0;
421N/A
423N/A for (i = sizeof(struct mailbox_list_index_header); i < size; ) {
336N/A /* get id */
336N/A if (i + sizeof(id) > size)
204N/A return -1;
204N/A memcpy(&id, CONST_PTR_OFFSET(data, i), sizeof(id));
204N/A i += sizeof(id);
204N/A
254N/A if (id <= prev_id) {
254N/A /* allow extra space in the end as long as last id=0 */
204N/A return id == 0 ? 0 : -1;
217N/A }
265N/A
316N/A /* get name */
206N/A p = memchr(CONST_PTR_OFFSET(data, i), '\0', size-i);
307N/A if (p == NULL)
316N/A return -1;
322N/A len = (const char *)p -
208N/A (const char *)(CONST_PTR_OFFSET(data, i));
209N/A
210N/A name = p_strndup(ilist->mailbox_pool,
216N/A CONST_PTR_OFFSET(data, i), len);
224N/A i += len + 1;
225N/A
240N/A /* add id => name to hash table */
241N/A hash_table_insert(ilist->mailbox_names, POINTER_CAST(id), name);
247N/A ilist->highest_name_id = id;
249N/A }
252N/A i_assert(i == size);
253N/A return 0;
262N/A}
264N/A
267N/Astatic int mailbox_list_index_parse_records(struct mailbox_list_index *ilist,
267N/A struct mail_index_view *view)
270N/A{
270N/A struct mailbox_list_index_node *node;
272N/A const struct mail_index_record *rec;
275N/A const struct mailbox_list_index_record *irec;
282N/A const void *data;
283N/A bool expunged;
328N/A uint32_t seq, count;
204N/A
204N/A count = mail_index_view_get_messages_count(view);
135N/A for (seq = 1; seq <= count; seq++) {
135N/A node = p_new(ilist->mailbox_pool,
135N/A struct mailbox_list_index_node, 1);
135N/A rec = mail_index_lookup(view, seq);
179N/A node->uid = rec->uid;
143N/A node->flags = rec->flags;
158N/A
152N/A mail_index_lookup_ext(view, seq, ilist->ext_id,
159N/A &data, &expunged);
179N/A if (data == NULL)
180N/A return -1;
181N/A irec = data;
180N/A
135N/A node->name_id = irec->name_id;
137N/A node->name = hash_table_lookup(ilist->mailbox_names,
139N/A POINTER_CAST(irec->name_id));
144N/A if (node->name == NULL)
153N/A return -1;
155N/A
169N/A if (irec->parent_uid != 0) {
174N/A node->parent = mailbox_list_index_lookup_uid(ilist,
188N/A irec->parent_uid);
189N/A if (node->parent == NULL)
135N/A return -1;
135N/A node->next = node->parent->children;
3N/A node->parent->children = node;
3N/A } else {
3N/A node->next = ilist->mailbox_tree;
3N/A ilist->mailbox_tree = node;
22N/A }
22N/A hash_table_insert(ilist->mailbox_hash,
3N/A POINTER_CAST(node->uid), node);
38N/A }
35N/A return 0;
35N/A}
36N/A
36N/Aint mailbox_list_index_parse(struct mailbox_list_index *ilist,
90N/A struct mail_index_view *view, bool force)
40N/A{
66N/A const struct mail_index_header *hdr;
66N/A int ret;
79N/A
75N/A i_assert(ilist->iter_refcount == 0);
76N/A
101N/A hdr = mail_index_get_header(view);
104N/A if (!force &&
180N/A hdr->log_file_seq == ilist->sync_log_file_seq &&
180N/A hdr->log_file_head_offset == ilist->sync_log_file_offset) {
180N/A /* nothing changed */
180N/A return 0;
22N/A }
22N/A
33N/A mailbox_list_index_reset(ilist);
33N/A ilist->sync_log_file_seq = hdr->log_file_seq;
45N/A ilist->sync_log_file_offset = hdr->log_file_head_offset;
45N/A
55N/A ret = mailbox_list_index_parse_header(ilist, view);
57N/A if (ret == 0)
58N/A ret = mailbox_list_index_parse_records(ilist, view);
60N/A if (ret < 0) {
62N/A i_error("Corrupted mailbox list index %s", ilist->path);
73N/A mail_index_mark_corrupted(ilist->index);
73N/A return -1;
93N/A }
93N/A return 0;
81N/A}
85N/A
86N/Abool mailbox_list_index_need_refresh(struct mailbox_list_index *ilist,
88N/A struct mail_index_view *view)
91N/A{
96N/A const struct mailbox_list_index_header *hdr;
3N/A const void *data;
3N/A size_t size;
0N/A
0N/A mail_index_get_header_ext(view, ilist->ext_id, &data, &size);
0N/A hdr = data;
0N/A return hdr != NULL && hdr->refresh_flag != 0;
0N/A}
0N/A
0N/Aint mailbox_list_index_refresh(struct mailbox_list *list)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A struct mail_index_view *view;
0N/A int ret;
0N/A
0N/A if (ilist->iter_refcount > 0) {
0N/A /* someone's already iterating. don't break them. */
0N/A return 0;
0N/A }
0N/A
0N/A mailbox_list_index_index_open(list);
0N/A if (mail_index_refresh(ilist->index) < 0) {
0N/A mailbox_list_index_set_index_error(list);
0N/A return -1;
0N/A }
0N/A
0N/A view = mail_index_view_open(ilist->index);
0N/A if (ilist->mailbox_tree == NULL ||
0N/A mailbox_list_index_need_refresh(ilist, view)) {
0N/A /* refresh list of mailboxes */
0N/A ret = mailbox_list_index_sync(list);
0N/A } else {
0N/A ret = mailbox_list_index_parse(ilist, view, FALSE);
0N/A }
0N/A mail_index_view_close(&view);
0N/A return ret;
0N/A}
0N/A
0N/Astatic void mailbox_list_index_refresh_timeout(struct mailbox_list *list)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A
0N/A timeout_remove(&ilist->to_refresh);
0N/A (void)mailbox_list_index_refresh(list);
0N/A}
0N/A
0N/Avoid mailbox_list_index_refresh_later(struct mailbox_list *list)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A struct mailbox_list_index_header new_hdr;
0N/A struct mail_index_view *view;
0N/A struct mail_index_transaction *trans;
0N/A
0N/A mailbox_list_index_index_open(list);
0N/A
0N/A view = mail_index_view_open(ilist->index);
0N/A if (!mailbox_list_index_need_refresh(ilist, view)) {
0N/A new_hdr.refresh_flag = 1;
0N/A
0N/A trans = mail_index_transaction_begin(view,
0N/A MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
0N/A mail_index_update_header_ext(trans, ilist->ext_id,
0N/A offsetof(struct mailbox_list_index_header, refresh_flag),
0N/A &new_hdr.refresh_flag, sizeof(new_hdr.refresh_flag));
0N/A if (mail_index_transaction_commit(&trans) < 0)
0N/A mail_index_mark_corrupted(ilist->index);
0N/A
0N/A }
0N/A mail_index_view_close(&view);
0N/A
0N/A if (ilist->to_refresh == NULL) {
0N/A ilist->to_refresh =
0N/A timeout_add(MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS,
0N/A mailbox_list_index_refresh_timeout, list);
0N/A }
0N/A}
0N/A
0N/Astatic void mailbox_list_index_deinit(struct mailbox_list *list)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A
0N/A if (ilist->to_refresh != NULL)
0N/A timeout_remove(&ilist->to_refresh);
0N/A hash_table_destroy(&ilist->mailbox_hash);
0N/A hash_table_destroy(&ilist->mailbox_names);
0N/A pool_unref(&ilist->mailbox_pool);
0N/A if (ilist->opened)
0N/A mail_index_close(ilist->index);
0N/A mail_index_free(&ilist->index);
0N/A ilist->module_ctx.super.deinit(list);
0N/A}
0N/A
0N/Astatic int
0N/Amailbox_list_index_create_mailbox_dir(struct mailbox_list *list,
0N/A const char *name,
0N/A enum mailbox_dir_create_type type)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A
0N/A mailbox_list_index_refresh_later(list);
0N/A return ilist->module_ctx.super.create_mailbox_dir(list, name, type);
0N/A}
0N/A
0N/Astatic int
0N/Amailbox_list_index_delete_mailbox(struct mailbox_list *list, const char *name)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A
0N/A mailbox_list_index_refresh_later(list);
0N/A return ilist->module_ctx.super.delete_mailbox(list, name);
0N/A}
0N/A
0N/Astatic int
0N/Amailbox_list_index_delete_dir(struct mailbox_list *list, const char *name)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list);
0N/A
0N/A mailbox_list_index_refresh_later(list);
0N/A return ilist->module_ctx.super.delete_dir(list, name);
0N/A}
0N/A
0N/Astatic int
0N/Amailbox_list_index_rename_mailbox(struct mailbox_list *oldlist,
0N/A const char *oldname,
0N/A struct mailbox_list *newlist,
0N/A const char *newname,
0N/A bool rename_children)
0N/A{
0N/A struct mailbox_list_index *oldilist = INDEX_LIST_CONTEXT(oldlist);
0N/A
0N/A mailbox_list_index_refresh_later(oldlist);
0N/A if (oldlist != newlist)
0N/A mailbox_list_index_refresh_later(newlist);
0N/A return oldilist->module_ctx.super.
0N/A rename_mailbox(oldlist, oldname,
0N/A newlist, newname, rename_children);
0N/A}
0N/A
0N/Astatic int
0N/Amailbox_list_index_set_subscribed(struct mailbox_list *_list,
0N/A const char *name, bool set)
0N/A{
0N/A struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(_list);
0N/A struct mail_index_view *view;
0N/A struct mail_index_transaction *trans;
0N/A const void *data;
0N/A size_t size;
0N/A uint32_t counter;
0N/A
0N/A if (ilist->module_ctx.super.set_subscribed(_list, name, set) < 0)
0N/A return -1;
0N/A
0N/A /* update the "subscriptions changed" counter/timestamp. its purpose
0N/A is to trigger NOTIFY watcher to handle SubscriptionChange events */
0N/A mailbox_list_index_index_open(_list);
0N/A view = mail_index_view_open(ilist->index);
0N/A mail_index_get_header_ext(view, ilist->subs_hdr_ext_id, &data, &size);
0N/A if (size != sizeof(counter))
0N/A counter = ioloop_time;
0N/A else {
0N/A memcpy(&counter, data, size);
0N/A if (++counter < ioloop_time)
0N/A counter = ioloop_time;
0N/A }
0N/A
0N/A trans = mail_index_transaction_begin(view,
0N/A MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL);
0N/A mail_index_update_header_ext(trans, ilist->subs_hdr_ext_id,
0N/A 0, &counter, sizeof(counter));
0N/A (void)mail_index_transaction_commit(&trans);
0N/A mail_index_view_close(&view);
0N/A return 0;
0N/A}
0N/A
0N/Astatic void mailbox_list_index_created(struct mailbox_list *list)
0N/A{
0N/A struct mailbox_list_index *ilist;
0N/A const char *dir;
0N/A
0N/A dir = mailbox_list_get_root_path(list, MAILBOX_LIST_PATH_TYPE_INDEX);
0N/A if (!list->mail_set->mailbox_list_index) {
0N/A /* reserve the module context anyway, so syncing code knows
0N/A that the index is disabled */
0N/A ilist = NULL;
0N/A MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist);
0N/A return;
0N/A }
0N/A if (*dir == '\0') {
0N/A /* in-memory indexes */
0N/A dir = NULL;
0N/A } else if (list->ns->type != MAIL_NAMESPACE_TYPE_PRIVATE) {
0N/A /* don't create index files for shared/public mailboxes.
0N/A their indexes may be shared between multiple users,
0N/A each of which may have different ACLs */
0N/A dir = NULL;
0N/A }
0N/A
0N/A ilist = p_new(list->pool, struct mailbox_list_index, 1);
0N/A ilist->module_ctx.super = list->v;
0N/A
0N/A list->v.deinit = mailbox_list_index_deinit;
0N/A list->v.iter_init = mailbox_list_index_iter_init;
0N/A list->v.iter_deinit = mailbox_list_index_iter_deinit;
0N/A list->v.iter_next = mailbox_list_index_iter_next;
0N/A
0N/A list->v.create_mailbox_dir = mailbox_list_index_create_mailbox_dir;
0N/A list->v.delete_mailbox = mailbox_list_index_delete_mailbox;
0N/A list->v.delete_dir = mailbox_list_index_delete_dir;
0N/A list->v.rename_mailbox = mailbox_list_index_rename_mailbox;
0N/A list->v.set_subscribed = mailbox_list_index_set_subscribed;
0N/A
0N/A list->v.notify_init = mailbox_list_index_notify_init;
0N/A list->v.notify_next = mailbox_list_index_notify_next;
0N/A list->v.notify_deinit = mailbox_list_index_notify_deinit;
0N/A list->v.notify_wait = mailbox_list_index_notify_wait;
0N/A
0N/A MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist);
0N/A
0N/A ilist->path = dir == NULL ? "(in-memory mailbox list index)" :
0N/A p_strdup_printf(list->pool, "%s/"MAILBOX_LIST_INDEX_PREFIX, dir);
0N/A ilist->index = mail_index_alloc(dir, MAILBOX_LIST_INDEX_PREFIX);
0N/A
0N/A ilist->ext_id = mail_index_ext_register(ilist->index, "list",
0N/A sizeof(struct mailbox_list_index_header),
0N/A sizeof(struct mailbox_list_index_record),
0N/A sizeof(uint32_t));
0N/A ilist->subs_hdr_ext_id = mail_index_ext_register(ilist->index, "subs",
0N/A sizeof(uint32_t), 0,
0N/A sizeof(uint32_t));
0N/A
0N/A ilist->mailbox_pool = pool_alloconly_create("mailbox list index", 4096);
0N/A hash_table_create_direct(&ilist->mailbox_names, ilist->mailbox_pool, 0);
0N/A hash_table_create_direct(&ilist->mailbox_hash, ilist->mailbox_pool, 0);
0N/A
0N/A mailbox_list_index_status_init_list(list);
0N/A}
0N/A
0N/Astatic struct mail_storage_hooks mailbox_list_index_hooks = {
0N/A .mailbox_list_created = mailbox_list_index_created
0N/A};
0N/A
0N/Avoid mailbox_list_index_init(void); /* called in mailbox-list-register.c */
0N/A
0N/Avoid mailbox_list_index_init(void)
0N/A{
0N/A mail_storage_hooks_add_internal(&mailbox_list_index_hooks);
0N/A mailbox_list_index_status_init();
0N/A}
0N/A