# HG changeset patch # User Timo Sirainen # Date 1344860433 -10800 # Node ID a16d77a075bb03aa0c946b637419ba434e9ca544 # Parent 026b688b379fd35a16cd68f52ad6309fcf41b25e lib-storage: Added mailbox-list-notify API for tracking changes in all mailboxes. Requires mailbox_list_index=yes to work. diff -r 026b688b379f -r a16d77a075bb src/lib-storage/Makefile.am --- a/src/lib-storage/Makefile.am Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/Makefile.am Mon Aug 13 15:20:33 2012 +0300 @@ -43,6 +43,7 @@ mailbox-keywords.c \ mailbox-list.c \ mailbox-list-iter.c \ + mailbox-list-notify.c \ mailbox-search-result.c \ mailbox-tree.c \ mailbox-uidvalidity.c @@ -70,6 +71,7 @@ mailbox-guid-cache.h \ mailbox-list.h \ mailbox-list-private.h \ + mailbox-list-notify.h \ mailbox-search-result-private.h \ mailbox-tree.h \ mailbox-uidvalidity.h diff -r 026b688b379f -r a16d77a075bb src/lib-storage/index/imapc/imapc-list.c --- a/src/lib-storage/index/imapc/imapc-list.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/index/imapc/imapc-list.c Mon Aug 13 15:20:33 2012 +0300 @@ -720,6 +720,7 @@ imapc_list_delete_mailbox, imapc_list_delete_dir, imapc_list_delete_symlink, - imapc_list_rename_mailbox + imapc_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; diff -r 026b688b379f -r a16d77a075bb src/lib-storage/index/shared/shared-list.c --- a/src/lib-storage/index/shared/shared-list.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/index/shared/shared-list.c Mon Aug 13 15:20:33 2012 +0300 @@ -359,6 +359,7 @@ shared_list_delete_mailbox, shared_list_delete_dir, shared_list_delete_symlink, - shared_list_rename_mailbox + shared_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/Makefile.am --- a/src/lib-storage/list/Makefile.am Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/Makefile.am Mon Aug 13 15:20:33 2012 +0300 @@ -15,11 +15,13 @@ mailbox-list-fs-iter.c \ mailbox-list-index.c \ mailbox-list-index-iter.c \ + mailbox-list-index-notify.c \ mailbox-list-index-status.c \ mailbox-list-index-sync.c \ mailbox-list-maildir.c \ mailbox-list-maildir-iter.c \ mailbox-list-none.c \ + mailbox-list-notify-tree.c \ mailbox-list-subscriptions.c \ subscription-file.c @@ -28,6 +30,7 @@ mailbox-list-fs.h \ mailbox-list-index.h \ mailbox-list-maildir.h \ + mailbox-list-notify-tree.h \ mailbox-list-subscriptions.h \ subscription-file.h diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-fs.c --- a/src/lib-storage/list/mailbox-list-fs.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-fs.c Mon Aug 13 15:20:33 2012 +0300 @@ -651,6 +651,7 @@ fs_list_delete_mailbox, fs_list_delete_dir, mailbox_list_delete_symlink_default, - fs_list_rename_mailbox + fs_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-index-notify.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-index-notify.c Mon Aug 13 15:20:33 2012 +0300 @@ -0,0 +1,699 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "ioloop.h" +#include "str.h" +#include "mail-index-private.h" +#include "mail-transaction-log-private.h" +#include "mail-storage.h" +#include "mailbox-list-notify.h" +#include "mailbox-list-notify-tree.h" +#include "mailbox-list-index.h" + +#include + +#define NOTIFY_DELAY_MSECS 500 + +enum ilist_ext_type { + ILIST_EXT_NONE, + ILIST_EXT_BASE, + ILIST_EXT_MSGS, + ILIST_EXT_HIGHESTMODSEQ, + ILIST_EXT_UNKNOWN +}; + +struct mailbox_list_notify_rename { + uint32_t old_uid, new_uid; +}; + +struct mailbox_list_inotify_entry { + uint32_t uid; + guid_128_t guid; + bool expunge; +}; + +struct mailbox_list_notify_index { + struct mailbox_list_notify notify; + + struct mailbox_list_notify_tree *tree; + struct mail_index_view *view, *old_view; + struct mail_index_view_sync_ctx *sync_ctx; + enum ilist_ext_type cur_ext; + uint32_t cur_ext_id; + + void (*wait_callback)(void *context); + void *wait_context; + struct io *io_wait; + struct timeout *to_wait, *to_notify; + + ARRAY_TYPE(seq_range) new_uids, expunged_uids, changed_uids; + ARRAY_DEFINE(renames, struct mailbox_list_notify_rename); + struct seq_range_iter new_uids_iter, expunged_uids_iter; + struct seq_range_iter changed_uids_iter; + unsigned int new_uids_n, expunged_uids_n, changed_uids_n; + unsigned int rename_idx; + + struct mailbox_list_notify_rec notify_rec; + string_t *rec_name; + + struct stat last_st; + + unsigned int initialized:1; + unsigned int read_failed:1; +}; + +int mailbox_list_index_notify_init(struct mailbox_list *list, + enum mailbox_list_notify_event mask, + struct mailbox_list_notify **notify_r) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); + struct mailbox_list_notify_index *inotify; + + if (ilist == NULL) { + /* can't do this without mailbox list indexes */ + return -1; + } + + (void)mailbox_list_index_refresh(list); + + inotify = i_new(struct mailbox_list_notify_index, 1); + inotify->notify.list = list; + inotify->notify.mask = mask; + inotify->view = mail_index_view_open(ilist->index); + inotify->old_view = mail_index_view_dup_private(inotify->view); + inotify->tree = mailbox_list_notify_tree_init(list); + i_array_init(&inotify->new_uids, 8); + i_array_init(&inotify->expunged_uids, 8); + i_array_init(&inotify->changed_uids, 16); + i_array_init(&inotify->renames, 16); + inotify->rec_name = str_new(default_pool, 64); + + *notify_r = &inotify->notify; + return 1; +} + +void mailbox_list_index_notify_deinit(struct mailbox_list_notify *notify) +{ + struct mailbox_list_notify_index *inotify = + (struct mailbox_list_notify_index *)notify; + bool b; + + if (inotify->io_wait != NULL) + io_remove(&inotify->io_wait); + if (inotify->to_wait != NULL) + timeout_remove(&inotify->to_wait); + if (inotify->to_notify != NULL) + timeout_remove(&inotify->to_notify); + if (inotify->sync_ctx != NULL) + (void)mail_index_view_sync_commit(&inotify->sync_ctx, &b); + mail_index_view_close(&inotify->view); + mail_index_view_close(&inotify->old_view); + mailbox_list_notify_tree_deinit(&inotify->tree); + array_free(&inotify->new_uids); + array_free(&inotify->expunged_uids); + array_free(&inotify->changed_uids); + array_free(&inotify->renames); + str_free(&inotify->rec_name); + i_free(inotify); +} + +static struct mailbox_list_index_node * +notify_lookup_guid(struct mailbox_list_notify_index *inotify, + struct mail_index_view *view, + uint32_t uid, enum mailbox_status_items items, + struct mailbox_status *status_r, guid_128_t guid_r) +{ + struct mailbox_list_index *ilist = + INDEX_LIST_CONTEXT(inotify->notify.list); + struct mailbox_list_index_node *index_node; + uint32_t seq; + + if (!mail_index_lookup_seq(view, uid, &seq)) + return NULL; + + index_node = mailbox_list_index_lookup_uid(ilist, uid); + if (index_node == NULL) { + /* re-parse the index list using the given view. we could be + jumping here between old and new view. */ + (void)mailbox_list_index_parse(ilist, view, FALSE); + index_node = mailbox_list_index_lookup_uid(ilist, uid); + if (index_node == NULL) + return NULL; + } + + /* get GUID */ + memset(status_r, 0, sizeof(*status_r)); + memset(guid_r, 0, GUID_128_SIZE); + (void)mailbox_list_index_status(inotify->notify.list, view, seq, + items, status_r, guid_r); + return index_node; +} + +static void notify_update_stat(struct mailbox_list_notify_index *inotify) +{ + struct mailbox_list_index *ilist = + INDEX_LIST_CONTEXT(inotify->notify.list); + const char *path = ilist->index->log->filepath; + + if (stat(path, &inotify->last_st) < 0 && errno != ENOENT) { + i_error("stat(%s) failed: %m", path); + mailbox_list_index_notify_wait(&inotify->notify, NULL, NULL); + } +} + +static void +mailbox_list_index_notify_sync_init(struct mailbox_list_notify_index *inotify) +{ + struct mail_index_view_sync_rec sync_rec; + + notify_update_stat(inotify); + (void)mail_index_refresh(inotify->view->index); + + /* sync the view so that map extensions gets updated */ + inotify->sync_ctx = mail_index_view_sync_begin(inotify->view, 0); + mail_transaction_log_view_mark(inotify->view->log_view); + while (mail_index_view_sync_next(inotify->sync_ctx, &sync_rec)) ; + mail_transaction_log_view_rewind(inotify->view->log_view); + + inotify->cur_ext = ILIST_EXT_NONE; + inotify->cur_ext_id = (uint32_t)-1; +} + +static bool notify_ext_rec(struct mailbox_list_notify_index *inotify, + uint32_t uid) +{ + struct mailbox_list_notify *notify = &inotify->notify; + + switch (inotify->cur_ext) { + case ILIST_EXT_NONE: + i_unreached(); + case ILIST_EXT_BASE: + /* UIDVALIDITY changed */ + if ((notify->mask & MAILBOX_LIST_NOTIFY_UIDVALIDITY) == 0) + return FALSE; + break; + case ILIST_EXT_MSGS: + /* APPEND, EXPUNGE, \Seen or \Recent flag change */ + if ((notify->mask & MAILBOX_LIST_NOTIFY_STATUS) == 0) + return FALSE; + break; + case ILIST_EXT_HIGHESTMODSEQ: + /* when this doesn't come with EXT_MSGS update, + it can only be a flag change or an explicit + modseq change */ + if ((notify->mask & MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) == 0) + return FALSE; + break; + case ILIST_EXT_UNKNOWN: + return FALSE; + } + seq_range_array_add(&inotify->changed_uids, uid); + return TRUE; +} + +static int +mailbox_list_index_notify_read_next(struct mailbox_list_notify_index *inotify) +{ + struct mailbox_list_notify *notify = &inotify->notify; + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(notify->list); + const struct mail_transaction_header *hdr; + const void *data; + int ret; + + ret = mail_transaction_log_view_next(inotify->view->log_view, + &hdr, &data); + if (ret <= 0) + return ret; + + if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { + /* all mailbox index updates are external */ + return 1; + } + switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { + case MAIL_TRANSACTION_APPEND: { + /* mailbox added or renamed */ + const struct mail_index_record *rec, *end; + + if ((notify->mask & (MAILBOX_LIST_NOTIFY_CREATE | + MAILBOX_LIST_NOTIFY_RENAME)) == 0) + break; + + end = CONST_PTR_OFFSET(data, hdr->size); + for (rec = data; rec != end; rec++) + seq_range_array_add(&inotify->new_uids, rec->uid); + break; + } + case MAIL_TRANSACTION_EXPUNGE_GUID: { + /* mailbox deleted or renamed */ + const struct mail_transaction_expunge_guid *rec, *end; + + if ((notify->mask & (MAILBOX_LIST_NOTIFY_DELETE | + MAILBOX_LIST_NOTIFY_RENAME)) == 0) + break; + + end = CONST_PTR_OFFSET(data, hdr->size); + for (rec = data; rec != end; rec++) + seq_range_array_add(&inotify->expunged_uids, rec->uid); + break; + } + case MAIL_TRANSACTION_EXT_INTRO: { + struct mail_index_map *map = inotify->view->map; + const struct mail_transaction_ext_intro *rec = data; + const struct mail_index_ext *ext = NULL; + const char *name; + uint32_t ext_map_idx; + + if (!array_is_created(&map->extensions)) + break; + /* we want to know what extension the future + ext-rec-updates are changing. we're assuming here that + there is only one ext-intro record before those, + which is true at least for now. */ + if (rec->ext_id != (uint32_t)-1 && + rec->ext_id < array_count(&map->extensions)) { + /* get extension by id */ + ext = array_idx(&map->extensions, rec->ext_id); + } else if (rec->name_size > 0) { + /* by name */ + name = t_strndup(rec+1, rec->name_size); + if (mail_index_map_lookup_ext(map, name, &ext_map_idx)) + ext = array_idx(&map->extensions, ext_map_idx); + } + if (ext != NULL) { + if (ext->index_idx == ilist->ext_id) + inotify->cur_ext = ILIST_EXT_BASE; + else if (ext->index_idx == ilist->msgs_ext_id) + inotify->cur_ext = ILIST_EXT_MSGS; + else if (ext->index_idx == ilist->hmodseq_ext_id) + inotify->cur_ext = ILIST_EXT_HIGHESTMODSEQ; + else + inotify->cur_ext = ILIST_EXT_UNKNOWN; + inotify->cur_ext_id = ext->index_idx; + } + break; + } + case MAIL_TRANSACTION_EXT_REC_UPDATE: { + const struct mail_index_registered_ext *ext; + const struct mail_transaction_ext_rec_update *rec; + unsigned int i, record_size; + + if (inotify->cur_ext == ILIST_EXT_NONE) { + i_error("%s: Missing ext-intro for ext-rec-update", + ilist->index->filepath); + break; + } + + /* the record is padded to 32bits in the transaction log */ + ext = array_idx(&inotify->view->index->extensions, + inotify->cur_ext_id); + record_size = (sizeof(*rec) + ext->record_size + 3) & ~3; + for (i = 0; i < hdr->size; i += record_size) { + rec = CONST_PTR_OFFSET(data, i); + + if (i + record_size > hdr->size) + break; + if (!notify_ext_rec(inotify, rec->uid)) + break; + } + break; + } + } + return 1; +} + +static int +mailbox_list_inotify_entry_guid_cmp(const struct mailbox_list_inotify_entry *r1, + const struct mailbox_list_inotify_entry *r2) +{ + int ret; + + ret = memcmp(r1->guid, r2->guid, sizeof(r1->guid)); + if (ret != 0) + return ret; + + if (r1->expunge == r2->expunge) { + /* this really shouldn't happen */ + return 0; + } + return r1->expunge ? -1 : 1; +} + +static void +mailbox_list_index_notify_find_renames(struct mailbox_list_notify_index *inotify) +{ + struct mailbox_list_index *ilist = + INDEX_LIST_CONTEXT(inotify->notify.list); + ARRAY_DEFINE(entries, struct mailbox_list_inotify_entry); + struct mailbox_status status; + struct mailbox_list_notify_rename *rename; + struct mailbox_list_inotify_entry *entry; + const struct mailbox_list_inotify_entry *e; + unsigned int i, count; + guid_128_t guid; + uint32_t uid; + + /* first get all of the added and expunged GUIDs */ + t_array_init(&entries, array_count(&inotify->new_uids) + + array_count(&inotify->expunged_uids)); + while (seq_range_array_iter_nth(&inotify->expunged_uids_iter, + inotify->expunged_uids_n++, &uid)) { + if (notify_lookup_guid(inotify, inotify->old_view, uid, + 0, &status, guid) != NULL && + !guid_128_is_empty(guid)) { + entry = array_append_space(&entries); + entry->uid = uid; + entry->expunge = TRUE; + memcpy(entry->guid, guid, sizeof(entry->guid)); + } + } + + (void)mailbox_list_index_parse(ilist, inotify->view, TRUE); + while (seq_range_array_iter_nth(&inotify->new_uids_iter, + inotify->new_uids_n++, &uid)) { + if (notify_lookup_guid(inotify, inotify->view, uid, + 0, &status, guid) != NULL && + !guid_128_is_empty(guid)) { + entry = array_append_space(&entries); + entry->uid = uid; + memcpy(entry->guid, guid, sizeof(entry->guid)); + } + } + + /* now sort the entries by GUID and find those that have been both + added and expunged */ + array_sort(&entries, mailbox_list_inotify_entry_guid_cmp); + + e = array_get(&entries, &count); + for (i = 1; i < count; i++) { + if (e[i-1].expunge && !e[i].expunge && + memcmp(e[i-1].guid, e[i].guid, sizeof(e[i].guid)) == 0) { + rename = array_append_space(&inotify->renames); + rename->old_uid = e[i-1].uid; + rename->new_uid = e[i].uid; + + seq_range_array_remove(&inotify->expunged_uids, + rename->old_uid); + seq_range_array_remove(&inotify->new_uids, + rename->new_uid); + } + } +} + +static void +mailbox_list_index_notify_reset_iters(struct mailbox_list_notify_index *inotify) +{ + seq_range_array_iter_init(&inotify->new_uids_iter, + &inotify->new_uids); + seq_range_array_iter_init(&inotify->expunged_uids_iter, + &inotify->expunged_uids); + seq_range_array_iter_init(&inotify->changed_uids_iter, + &inotify->changed_uids); + inotify->changed_uids_n = 0; + inotify->new_uids_n = 0; + inotify->expunged_uids_n = 0; + inotify->rename_idx = 0; +} + +static void +mailbox_list_index_notify_read_init(struct mailbox_list_notify_index *inotify) +{ + bool b; + int ret; + + mailbox_list_index_notify_sync_init(inotify); + + /* read all changes from .log file */ + while ((ret = mailbox_list_index_notify_read_next(inotify)) > 0) ; + inotify->read_failed = ret < 0; + + (void)mail_index_view_sync_commit(&inotify->sync_ctx, &b); + + /* remove changes for already deleted mailboxes */ + seq_range_array_remove_seq_range(&inotify->new_uids, + &inotify->expunged_uids); + seq_range_array_remove_seq_range(&inotify->changed_uids, + &inotify->expunged_uids); + mailbox_list_index_notify_reset_iters(inotify); + if (array_count(&inotify->new_uids) > 0 && + array_count(&inotify->expunged_uids) > 0) { + mailbox_list_index_notify_find_renames(inotify); + mailbox_list_index_notify_reset_iters(inotify); + } + + inotify->initialized = TRUE; +} + +static void +mailbox_list_index_notify_read_deinit(struct mailbox_list_notify_index *inotify) +{ + /* save the old view so we can look up expunged records */ + mail_index_view_close(&inotify->old_view); + inotify->old_view = mail_index_view_dup_private(inotify->view); + + array_clear(&inotify->new_uids); + array_clear(&inotify->expunged_uids); + array_clear(&inotify->changed_uids); + array_clear(&inotify->renames); + + inotify->initialized = FALSE; +} + +static bool +mailbox_list_index_notify_lookup(struct mailbox_list_notify_index *inotify, + struct mail_index_view *view, + uint32_t uid, enum mailbox_status_items items, + struct mailbox_status *status_r, + struct mailbox_list_notify_rec **rec_r) +{ + struct mailbox_list_notify_rec *rec = &inotify->notify_rec; + struct mailbox_list_index_node *index_node; + const char *storage_name; + char ns_sep = mailbox_list_get_hierarchy_sep(inotify->notify.list); + + memset(rec, 0, sizeof(*rec)); + index_node = notify_lookup_guid(inotify, view, uid, + items, status_r, rec->guid); + if (index_node == NULL) + return FALSE; + + /* get storage_name */ + str_truncate(inotify->rec_name, 0); + mailbox_list_index_node_get_path(index_node, ns_sep, inotify->rec_name); + storage_name = str_c(inotify->rec_name); + + rec->storage_name = storage_name; + rec->vname = mailbox_list_get_vname(inotify->notify.list, + rec->storage_name); + *rec_r = rec; + return TRUE; +} + +static bool +mailbox_list_index_notify_rename(struct mailbox_list_notify_index *inotify, + unsigned int idx) +{ + const struct mailbox_list_notify_rename *rename; + struct mailbox_list_notify_rec *rec; + struct mailbox_status status; + const char *old_vname; + + rename = array_idx(&inotify->renames, idx); + + /* lookup the old name */ + if (!mailbox_list_index_notify_lookup(inotify, inotify->old_view, + rename->old_uid, 0, &status, &rec)) + return FALSE; + old_vname = t_strdup(rec->vname); + + /* return using the new name */ + if (!mailbox_list_index_notify_lookup(inotify, inotify->view, + rename->new_uid, 0, &status, &rec)) + return FALSE; + + rec->old_vname = old_vname; + rec->event = MAILBOX_LIST_NOTIFY_RENAME; + return TRUE; +} + +static bool +mailbox_list_index_notify_expunge(struct mailbox_list_notify_index *inotify, + uint32_t uid) +{ + struct mailbox_list_notify_rec *rec; + struct mailbox_status status; + + if (!mailbox_list_index_notify_lookup(inotify, inotify->old_view, + uid, 0, &status, &rec)) + return FALSE; + rec->event = MAILBOX_LIST_NOTIFY_DELETE; + return TRUE; +} + +static bool +mailbox_list_index_notify_new(struct mailbox_list_notify_index *inotify, + uint32_t uid) +{ + struct mailbox_list_notify_rec *rec; + struct mailbox_status status; + + if (!mailbox_list_index_notify_lookup(inotify, inotify->view, + uid, 0, &status, &rec)) + i_unreached(); + rec->event = MAILBOX_LIST_NOTIFY_CREATE; + return TRUE; +} + +static bool +mailbox_list_index_notify_change(struct mailbox_list_notify_index *inotify, + uint32_t uid) +{ + const enum mailbox_status_items status_items = + STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | + STATUS_UNSEEN | STATUS_HIGHESTMODSEQ; + struct mailbox_list_notify_rec *rec; + struct mailbox_notify_node *nnode; + struct mailbox_status status; + + if (!mailbox_list_index_notify_lookup(inotify, inotify->view, + uid, status_items, &status, &rec)) + i_unreached(); + + /* get the old status */ + nnode = mailbox_list_notify_tree_lookup(inotify->tree, + rec->storage_name); + if (nnode == NULL || nnode->uidvalidity != status.uidvalidity) + rec->event = MAILBOX_LIST_NOTIFY_UIDVALIDITY; + else if (nnode->uidnext != status.uidnext) + rec->event = MAILBOX_LIST_NOTIFY_APPENDS; + else if (nnode->messages > status.messages) + rec->event = MAILBOX_LIST_NOTIFY_EXPUNGES; + else if (nnode->unseen != status.unseen) + rec->event = MAILBOX_LIST_NOTIFY_SEEN_CHANGES; + else if (nnode->highest_modseq < status.highest_modseq) + rec->event = MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES; + else { + /* nothing changed */ + return FALSE; + } + + /* update internal state */ + if (nnode != NULL) { + nnode->uidvalidity = status.uidvalidity; + nnode->uidnext = status.uidnext; + nnode->messages = status.messages; + nnode->unseen = status.unseen; + nnode->highest_modseq = status.highest_modseq; + } + return TRUE; +} + +static bool +mailbox_list_index_notify_try_next(struct mailbox_list_notify_index *inotify) +{ + uint32_t uid; + + /* first show mailbox deletes */ + if (seq_range_array_iter_nth(&inotify->expunged_uids_iter, + inotify->expunged_uids_n++, &uid)) + return mailbox_list_index_notify_expunge(inotify, uid); + + /* mailbox renames */ + if (inotify->rename_idx < array_count(&inotify->renames)) { + return mailbox_list_index_notify_rename(inotify, + inotify->rename_idx++); + } + + /* next mailbox creates */ + if (seq_range_array_iter_nth(&inotify->new_uids_iter, + inotify->new_uids_n++, &uid)) + return mailbox_list_index_notify_new(inotify, uid); + + /* STATUS updates */ + while (seq_range_array_iter_nth(&inotify->changed_uids_iter, + inotify->changed_uids_n++, &uid)) { + if (mailbox_list_index_notify_change(inotify, uid)) + return TRUE; + } + return FALSE; +} + +int mailbox_list_index_notify_next(struct mailbox_list_notify *notify, + const struct mailbox_list_notify_rec **rec_r) +{ + struct mailbox_list_notify_index *inotify = + (struct mailbox_list_notify_index *)notify; + + if (!inotify->initialized) + mailbox_list_index_notify_read_init(inotify); + while (mailbox_list_index_notify_try_next(inotify)) { + if ((inotify->notify_rec.event & inotify->notify.mask) != 0) { + *rec_r = &inotify->notify_rec; + return 1; + } else { + /* caller doesn't care about this change */ + } + } + + mailbox_list_index_notify_read_deinit(inotify); + return inotify->read_failed ? -1 : 0; +} + +static void notify_now_callback(struct mailbox_list_notify_index *inotify) +{ + timeout_remove(&inotify->to_notify); + inotify->wait_callback(inotify->wait_context); +} + +static void notify_callback(struct mailbox_list_notify_index *inotify) +{ + struct stat prev_st = inotify->last_st; + + notify_update_stat(inotify); + if (inotify->last_st.st_mtime != prev_st.st_mtime || + ST_MTIME_NSEC(inotify->last_st) != ST_MTIME_NSEC(prev_st) || + inotify->last_st.st_size != prev_st.st_size || + inotify->last_st.st_ino != prev_st.st_ino) { + /* log has changed. call the callback with a small delay + to allow bundling multiple changes together */ + if (inotify->to_notify != NULL) { + /* already doing this */ + return; + } + inotify->to_notify = + timeout_add_short(NOTIFY_DELAY_MSECS, + notify_now_callback, inotify); + } +} + +void mailbox_list_index_notify_wait(struct mailbox_list_notify *notify, + void (*callback)(void *context), + void *context) +{ + struct mailbox_list_notify_index *inotify = + (struct mailbox_list_notify_index *)notify; + const char *path; + unsigned int check_interval; + + inotify->wait_callback = callback; + inotify->wait_context = context; + + if (callback == NULL) { + if (inotify->io_wait != NULL) + io_remove(&inotify->io_wait); + if (inotify->to_wait != NULL) + timeout_remove(&inotify->to_wait); + if (inotify->to_notify != NULL) + timeout_remove(&inotify->to_notify); + } else if (inotify->to_wait == NULL) { + path = inotify->view->index->log->filepath; + (void)io_add_notify(path, notify_callback, inotify, + &inotify->io_wait); + /* check with timeout as well, in case io_add_notify() + doesn't work (e.g. NFS) */ + check_interval = notify->list->mail_set->mailbox_idle_check_interval; + i_assert(check_interval > 0); + inotify->to_wait = timeout_add(check_interval * 1000, + notify_callback, inotify); + notify_update_stat(inotify); + } +} diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-index-status.c --- a/src/lib-storage/list/mailbox-list-index-status.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-index-status.c Mon Aug 13 15:20:33 2012 +0300 @@ -60,13 +60,13 @@ return 1; } -static bool ATTR_NULL(6) -index_list_get_view_status(struct mailbox *box, struct mail_index_view *view, - uint32_t seq, enum mailbox_status_items items, - struct mailbox_status *status_r, - uint8_t *mailbox_guid) +bool mailbox_list_index_status(struct mailbox_list *list, + struct mail_index_view *view, + uint32_t seq, enum mailbox_status_items items, + struct mailbox_status *status_r, + uint8_t *mailbox_guid) { - struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); const void *data; bool expunged; bool ret = TRUE; @@ -77,10 +77,14 @@ mail_index_lookup_ext(view, seq, ilist->ext_id, &data, &expunged); rec = data; - if (rec == NULL || rec->uid_validity == 0) + if (rec == NULL) ret = FALSE; else { - status_r->uidvalidity = rec->uid_validity; + if ((items & STATUS_UIDVALIDITY) != 0 && + rec->uid_validity == 0) + ret = FALSE; + else + status_r->uidvalidity = rec->uid_validity; if (mailbox_guid != NULL) memcpy(mailbox_guid, rec->guid, GUID_128_SIZE); } @@ -131,8 +135,8 @@ if (ret <= 0) return ret; - ret = index_list_get_view_status(box, view, seq, items, - status_r, NULL) ? 1 : 0; + ret = mailbox_list_index_status(box->list, view, seq, items, + status_r, NULL) ? 1 : 0; mail_index_view_close(&view); return ret; } @@ -154,17 +158,23 @@ static int index_list_get_cached_guid(struct mailbox *box, guid_128_t guid_r) { + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(box->list); struct mailbox_status status; struct mail_index_view *view; uint32_t seq; int ret; + if (ilist->syncing) { + /* syncing wants to know the GUID for a new mailbox. */ + return 0; + } + ret = index_list_open_view(box, &view, &seq); if (ret <= 0) return ret; - ret = index_list_get_view_status(box, view, seq, 0, - &status, guid_r) ? 1 : 0; + ret = mailbox_list_index_status(box->list, view, seq, 0, + &status, guid_r) ? 1 : 0; if (ret > 0 && guid_128_is_empty(guid_r)) ret = 0; mail_index_view_close(&view); @@ -204,11 +214,14 @@ memset(&metadata, 0, sizeof(metadata)); memset(&old_status, 0, sizeof(old_status)); - (void)index_list_get_view_status(box, view, seq, CACHED_STATUS_ITEMS, - &old_status, mailbox_guid); + memset(mailbox_guid, 0, sizeof(mailbox_guid)); + (void)mailbox_list_index_status(box->list, view, seq, CACHED_STATUS_ITEMS, + &old_status, mailbox_guid); - rec_changed = old_status.uidvalidity != status->uidvalidity || - memcmp(metadata.guid, mailbox_guid, sizeof(metadata.guid)) == 0; + rec_changed = old_status.uidvalidity != status->uidvalidity; + if (memcmp(metadata.guid, mailbox_guid, sizeof(metadata.guid)) != 0 && + guid_128_is_empty(metadata.guid)) + rec_changed = TRUE; msgs_changed = old_status.messages != status->messages || old_status.unseen != status->unseen || old_status.recent != status->recent || @@ -249,7 +262,8 @@ memcpy(&rec, old_data, sizeof(rec)); rec.uid_validity = status->uidvalidity; - memcpy(rec.guid, mailbox_guid, sizeof(rec.guid)); + if (!guid_128_is_empty(metadata.guid)) + memcpy(rec.guid, metadata.guid, sizeof(rec.guid)); mail_index_update_ext(trans, seq, ilist->ext_id, &rec, NULL); } @@ -386,8 +400,8 @@ } status.recent = 0; - (void)index_list_get_view_status(box, view, seq, STATUS_RECENT, - &status, NULL); + (void)mailbox_list_index_status(box->list, view, seq, STATUS_RECENT, + &status, NULL); mail_index_view_close(&view); if (status.recent != 0) diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-index-sync.c --- a/src/lib-storage/list/mailbox-list-index-sync.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-index-sync.c Mon Aug 13 15:20:33 2012 +0300 @@ -3,10 +3,13 @@ #include "lib.h" #include "ioloop.h" #include "hash.h" +#include "str.h" #include "mail-index.h" +#include "mail-storage.h" #include "mailbox-list-index.h" struct mailbox_list_index_sync_context { + struct mailbox_list *list; struct mailbox_list_index *ilist; char sep[2]; uint32_t next_uid; @@ -17,6 +20,25 @@ }; static void +node_lookup_guid(struct mailbox_list_index_sync_context *ctx, + const struct mailbox_list_index_node *node, guid_128_t guid_r) +{ + struct mailbox *box; + struct mailbox_metadata metadata; + const char *vname; + string_t *str = t_str_new(128); + char ns_sep = mailbox_list_get_hierarchy_sep(ctx->list); + + mailbox_list_index_node_get_path(node, ns_sep, str); + + vname = mailbox_list_get_vname(ctx->list, str_c(str)); + box = mailbox_alloc(ctx->list, vname, 0); + if (mailbox_get_metadata(box, MAILBOX_METADATA_GUID, &metadata) == 0) + memcpy(guid_r, metadata.guid, GUID_128_SIZE); + mailbox_free(&box); +} + +static void node_add_to_index(struct mailbox_list_index_sync_context *ctx, const struct mailbox_list_index_node *node, uint32_t *seq_r) { @@ -28,6 +50,12 @@ if (node->parent != NULL) irec.parent_uid = node->parent->uid; + /* get mailbox GUID if possible. we need to do this early in here to + make mailbox rename detection work in NOTIFY */ + T_BEGIN { + node_lookup_guid(ctx, node, irec.guid); + } T_END; + mail_index_append(ctx->trans, node->uid, &seq); mail_index_update_flags(ctx->trans, seq, MODIFY_REPLACE, (enum mail_flags)MAILBOX_LIST_INDEX_FLAG_NONEXISTENT); @@ -237,6 +265,7 @@ mailbox_list_index_reset(ilist); memset(&sync_ctx, 0, sizeof(sync_ctx)); + sync_ctx.list = list; sync_ctx.ilist = ilist; sync_ctx.sep[0] = mailbox_list_get_hierarchy_sep(list); if (mail_index_sync_begin(ilist->index, &sync_ctx.sync_ctx, @@ -269,6 +298,7 @@ /* don't include autocreated mailboxes in index until they're actually created. */ + ilist->syncing = TRUE; patterns[0] = "*"; patterns[1] = NULL; iter = ilist->module_ctx.super. iter_init(list, patterns, MAILBOX_LIST_ITER_NO_AUTO_BOXES); @@ -294,6 +324,7 @@ } if (ilist->module_ctx.super.iter_deinit(iter) < 0) { mail_index_sync_rollback(&sync_ctx.sync_ctx); + ilist->syncing = FALSE; return -1; } @@ -313,6 +344,7 @@ offsetof(struct mailbox_list_index_header, refresh_flag), &new_hdr.refresh_flag, sizeof(new_hdr.refresh_flag)); } + ilist->syncing = FALSE; if (mail_index_sync_commit(&sync_ctx.sync_ctx) < 0) { mailbox_list_index_set_index_error(list); diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-index.c --- a/src/lib-storage/list/mailbox-list-index.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-index.c Mon Aug 13 15:20:33 2012 +0300 @@ -1,11 +1,15 @@ /* Copyright (c) 2006-2012 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "ioloop.h" #include "hash.h" -#include "mail-index.h" +#include "str.h" +#include "mail-index-view-private.h" #include "mail-storage-hooks.h" #include "mailbox-list-index.h" +#define MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS 1000 + struct mailbox_list_index_module mailbox_list_index_module = MODULE_CONTEXT_INIT(&mailbox_list_module_register); @@ -104,6 +108,22 @@ return node; } +struct mailbox_list_index_node * +mailbox_list_index_lookup_uid(struct mailbox_list_index *ilist, uint32_t uid) +{ + return hash_table_lookup(ilist->mailbox_hash, POINTER_CAST(uid)); +} + +void mailbox_list_index_node_get_path(const struct mailbox_list_index_node *node, + char sep, string_t *str) +{ + if (node->parent != NULL) { + mailbox_list_index_node_get_path(node->parent, sep, str); + str_append_c(str, sep); + } + str_append(str, node->name); +} + static int mailbox_list_index_parse_header(struct mailbox_list_index *ilist, struct mail_index_view *view) { @@ -112,7 +132,7 @@ uint32_t id, prev_id = 0; char *name; - mail_index_get_header_ext(view, ilist->ext_id, &data, &size); + mail_index_map_get_header_ext(view, view->map, ilist->ext_id, &data, &size); if (size == 0) return 0; @@ -178,8 +198,8 @@ return -1; if (irec->parent_uid != 0) { - node->parent = hash_table_lookup(ilist->mailbox_hash, - POINTER_CAST(irec->parent_uid)); + node->parent = mailbox_list_index_lookup_uid(ilist, + irec->parent_uid); if (node->parent == NULL) return -1; node->next = node->parent->children; @@ -266,6 +286,14 @@ return ret; } +static void mailbox_list_index_refresh_timeout(struct mailbox_list *list) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); + + timeout_remove(&ilist->to_refresh); + (void)mailbox_list_index_refresh(list); +} + void mailbox_list_index_refresh_later(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); @@ -289,12 +317,20 @@ } mail_index_view_close(&view); + + if (ilist->to_refresh == NULL) { + ilist->to_refresh = + timeout_add(MAILBOX_LIST_INDEX_REFRESH_DELAY_MSECS, + mailbox_list_index_refresh_timeout, list); + } } static void mailbox_list_index_deinit(struct mailbox_list *list) { struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(list); + if (ilist->to_refresh != NULL) + timeout_remove(&ilist->to_refresh); hash_table_destroy(&ilist->mailbox_hash); hash_table_destroy(&ilist->mailbox_names); pool_unref(&ilist->mailbox_pool); @@ -386,6 +422,11 @@ list->v.delete_dir = mailbox_list_index_delete_dir; list->v.rename_mailbox = mailbox_list_index_rename_mailbox; + list->v.notify_init = mailbox_list_index_notify_init; + list->v.notify_next = mailbox_list_index_notify_next; + list->v.notify_deinit = mailbox_list_index_notify_deinit; + list->v.notify_wait = mailbox_list_index_notify_wait; + MODULE_CONTEXT_SET(list, mailbox_list_index_module, ilist); ilist->path = dir == NULL ? "(in-memory mailbox list index)" : diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-index.h --- a/src/lib-storage/list/mailbox-list-index.h Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-index.h Mon Aug 13 15:20:33 2012 +0300 @@ -23,6 +23,7 @@ */ #include "module-context.h" +#include "mail-types.h" #include "mailbox-list-private.h" #define MAILBOX_LIST_INDEX_PREFIX "dovecot.list.index" @@ -30,8 +31,10 @@ #define INDEX_LIST_CONTEXT(obj) \ MODULE_CONTEXT(obj, mailbox_list_index_module) +enum mailbox_status_items; struct mail_index_view; struct mailbox; +struct mailbox_status; /* stored in mail_index_record.flags: */ enum mailbox_list_index_flags { @@ -96,14 +99,15 @@ uint32_t sync_log_file_seq; uoff_t sync_log_file_offset; - uint32_t sync_stamp; + struct timeout *to_refresh; /* uint32_t uid => struct mailbox_list_index_node* */ struct hash_table *mailbox_hash; struct mailbox_list_index_node *mailbox_tree; unsigned int opened:1; + unsigned int syncing:1; }; struct mailbox_list_index_iterate_context { @@ -127,6 +131,10 @@ void mailbox_list_index_set_index_error(struct mailbox_list *list); struct mailbox_list_index_node * mailbox_list_index_lookup(struct mailbox_list *list, const char *name); +struct mailbox_list_index_node * +mailbox_list_index_lookup_uid(struct mailbox_list_index *ilist, uint32_t uid); +void mailbox_list_index_node_get_path(const struct mailbox_list_index_node *node, + char sep, string_t *str); bool mailbox_list_index_need_refresh(struct mailbox_list_index *ilist, struct mail_index_view *view); @@ -149,9 +157,24 @@ mailbox_list_index_iter_next(struct mailbox_list_iterate_context *ctx); int mailbox_list_index_iter_deinit(struct mailbox_list_iterate_context *ctx); +bool mailbox_list_index_status(struct mailbox_list *list, + struct mail_index_view *view, + uint32_t seq, enum mailbox_status_items items, + struct mailbox_status *status_r, + uint8_t *mailbox_guid); void mailbox_list_index_status_set_info_flags(struct mailbox *box, uint32_t uid, enum mailbox_info_flags *flags); +int mailbox_list_index_notify_init(struct mailbox_list *list, + enum mailbox_list_notify_event mask, + struct mailbox_list_notify **notify_r); +void mailbox_list_index_notify_deinit(struct mailbox_list_notify *notify); +int mailbox_list_index_notify_next(struct mailbox_list_notify *notify, + const struct mailbox_list_notify_rec **rec_r); +void mailbox_list_index_notify_wait(struct mailbox_list_notify *notify, + void (*callback)(void *context), + void *context); + void mailbox_list_index_status_init(void); void mailbox_list_index_status_init_list(struct mailbox_list *list); diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-maildir.c --- a/src/lib-storage/list/mailbox-list-maildir.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-maildir.c Mon Aug 13 15:20:33 2012 +0300 @@ -664,7 +664,8 @@ maildir_list_delete_mailbox, maildir_list_delete_dir, mailbox_list_delete_symlink_default, - maildir_list_rename_mailbox + maildir_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; @@ -699,6 +700,7 @@ maildir_list_delete_mailbox, maildir_list_delete_dir, mailbox_list_delete_symlink_default, - maildir_list_rename_mailbox + maildir_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-none.c --- a/src/lib-storage/list/mailbox-list-none.c Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/list/mailbox-list-none.c Mon Aug 13 15:20:33 2012 +0300 @@ -212,6 +212,7 @@ none_list_delete_mailbox, none_list_delete_dir, none_list_delete_dir, - none_list_rename_mailbox + none_list_rename_mailbox, + NULL, NULL, NULL, NULL } }; diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-notify-tree.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-notify-tree.c Mon Aug 13 15:20:33 2012 +0300 @@ -0,0 +1,129 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "str.h" +#include "mail-index.h" +#include "mail-storage.h" +#include "mailbox-list-private.h" +#include "mailbox-list-index.h" +#include "mailbox-list-notify-tree.h" + +struct mailbox_list_notify_tree { + struct mailbox_list *list; + struct mailbox_tree_context *mailboxes; + + struct mail_index_view *view; + bool failed; +}; + +static void +mailbox_list_notify_node_get_status(struct mailbox_list_notify_tree *tree, + struct mailbox_notify_node *nnode) +{ + struct mailbox_status status; + uint32_t seq; + + if (!mail_index_lookup_seq(tree->view, nnode->index_uid, &seq)) + return; + + memset(&status, 0, sizeof(status)); + (void)mailbox_list_index_status(tree->list, tree->view, seq, + STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES | + STATUS_UNSEEN | STATUS_HIGHESTMODSEQ, &status, nnode->guid); + nnode->uidvalidity = status.uidvalidity; + nnode->uidnext = status.uidnext; + nnode->messages = status.messages; + nnode->unseen = status.unseen; + nnode->highest_modseq = status.highest_modseq; +} + +static void +mailbox_list_notify_node_build(struct mailbox_list_notify_tree *tree, + struct mailbox_list_index_node *index_node, + string_t *path) +{ + struct mailbox_node *node; + struct mailbox_notify_node *nnode; + unsigned int prefix_len; + bool created; + + str_append(path, index_node->name); + + node = mailbox_tree_get(tree->mailboxes, str_c(path), &created); + nnode = (struct mailbox_notify_node *)node; + nnode->index_uid = index_node->uid; + + if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NONEXISTENT) != 0) + node->flags = MAILBOX_NONEXISTENT; + else if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NOSELECT) != 0) + node->flags = MAILBOX_NOSELECT; + else { + node->flags = 0; + mailbox_list_notify_node_get_status(tree, nnode); + } + if ((index_node->flags & MAILBOX_LIST_INDEX_FLAG_NOINFERIORS) != 0) + node->flags |= MAILBOX_NOINFERIORS; + + if (index_node->children != NULL) { + str_append_c(path, mailbox_list_get_hierarchy_sep(tree->list)); + prefix_len = str_len(path); + index_node = index_node->children; + for (; index_node != NULL; index_node = index_node->next) { + str_truncate(path, prefix_len); + mailbox_list_notify_node_build(tree, index_node, path); + } + } +} + +static void +mailbox_list_notify_tree_build(struct mailbox_list_notify_tree *tree) +{ + struct mailbox_list_index *ilist = INDEX_LIST_CONTEXT(tree->list); + struct mailbox_list_index_node *index_node; + string_t *path = t_str_new(128); + + if (mailbox_list_index_refresh(tree->list) < 0) + tree->failed = TRUE; + + tree->view = mail_index_view_open(ilist->index); + index_node = ilist->mailbox_tree; + for (; index_node != NULL; index_node = index_node->next) { + str_truncate(path, 0); + mailbox_list_notify_node_build(tree, index_node, path); + } + mail_index_view_close(&tree->view); +} + +struct mailbox_list_notify_tree * +mailbox_list_notify_tree_init(struct mailbox_list *list) +{ + struct mailbox_list_notify_tree *tree; + + tree = i_new(struct mailbox_list_notify_tree, 1); + tree->list = list; + tree->mailboxes = + mailbox_tree_init_size(mailbox_list_get_hierarchy_sep(list), + sizeof(struct mailbox_notify_node)); + mailbox_list_notify_tree_build(tree); + return tree; +} + +void mailbox_list_notify_tree_deinit(struct mailbox_list_notify_tree **_tree) +{ + struct mailbox_list_notify_tree *tree = *_tree; + + *_tree = NULL; + + mailbox_tree_deinit(&tree->mailboxes); + i_free(tree); +} + +struct mailbox_notify_node * +mailbox_list_notify_tree_lookup(struct mailbox_list_notify_tree *tree, + const char *storage_name) +{ + struct mailbox_node *node; + + node = mailbox_tree_lookup(tree->mailboxes, storage_name); + return (struct mailbox_notify_node *)node; +} diff -r 026b688b379f -r a16d77a075bb src/lib-storage/list/mailbox-list-notify-tree.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/list/mailbox-list-notify-tree.h Mon Aug 13 15:20:33 2012 +0300 @@ -0,0 +1,27 @@ +#ifndef MAILBOX_LIST_NOTIFY_TREE_H +#define MAILBOX_LIST_NOTIFY_TREE_H + +#include "mailbox-tree.h" + +struct mailbox_notify_node { + struct mailbox_node node; + + guid_128_t guid; + uint32_t index_uid; + + uint32_t uidvalidity; + uint32_t uidnext; + uint32_t messages; + uint32_t unseen; + uint64_t highest_modseq; +}; + +struct mailbox_list_notify_tree * +mailbox_list_notify_tree_init(struct mailbox_list *list); +void mailbox_list_notify_tree_deinit(struct mailbox_list_notify_tree **tree); + +struct mailbox_notify_node * +mailbox_list_notify_tree_lookup(struct mailbox_list_notify_tree *tree, + const char *storage_name); + +#endif diff -r 026b688b379f -r a16d77a075bb src/lib-storage/mailbox-list-notify.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mailbox-list-notify.c Mon Aug 13 15:20:33 2012 +0300 @@ -0,0 +1,35 @@ +/* Copyright (c) 2012 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "mailbox-list-private.h" +#include "mailbox-list-notify.h" + +int mailbox_list_notify_init(struct mailbox_list *list, + enum mailbox_list_notify_event mask, + struct mailbox_list_notify **notify_r) +{ + if (list->v.notify_init == NULL) + return -1; + return list->v.notify_init(list, mask, notify_r); +} + +void mailbox_list_notify_deinit(struct mailbox_list_notify **_notify) +{ + struct mailbox_list_notify *notify = *_notify; + + *_notify = NULL; + + notify->list->v.notify_deinit(notify); +} + +int mailbox_list_notify_next(struct mailbox_list_notify *notify, + const struct mailbox_list_notify_rec **rec_r) +{ + return notify->list->v.notify_next(notify, rec_r); +} + +void mailbox_list_notify_wait(struct mailbox_list_notify *notify, + void (*callback)(void *context), void *context) +{ + notify->list->v.notify_wait(notify, callback, context); +} diff -r 026b688b379f -r a16d77a075bb src/lib-storage/mailbox-list-notify.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib-storage/mailbox-list-notify.h Mon Aug 13 15:20:33 2012 +0300 @@ -0,0 +1,58 @@ +#ifndef MAILBOX_LIST_NOTIFY_H +#define MAILBOX_LIST_NOTIFY_H + +#include "guid.h" + +struct mailbox_list_notify; + +enum mailbox_list_notify_event { + MAILBOX_LIST_NOTIFY_CREATE = 0x01, + MAILBOX_LIST_NOTIFY_DELETE = 0x02, + MAILBOX_LIST_NOTIFY_RENAME = 0x04, + MAILBOX_LIST_NOTIFY_SUBSCRIPTION_CHANGE = 0x08, + + MAILBOX_LIST_NOTIFY_UIDVALIDITY = 0x10, + MAILBOX_LIST_NOTIFY_APPENDS = 0x20, + MAILBOX_LIST_NOTIFY_EXPUNGES = 0x40, + MAILBOX_LIST_NOTIFY_SEEN_CHANGES = 0x80, + MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES = 0x100 +#define MAILBOX_LIST_NOTIFY_STATUS \ + (MAILBOX_LIST_NOTIFY_APPENDS | \ + MAILBOX_LIST_NOTIFY_EXPUNGES | \ + MAILBOX_LIST_NOTIFY_SEEN_CHANGES | \ + MAILBOX_LIST_NOTIFY_MODSEQ_CHANGES) +}; + +struct mailbox_list_notify { + struct mailbox_list *list; + enum mailbox_list_notify_event mask; +}; + +struct mailbox_list_notify_rec { + enum mailbox_list_notify_event event; + + /* For all events: */ + const char *storage_name, *vname; + /* For selectable mailboxes: */ + guid_128_t guid; + + /* For rename: */ + const char *old_vname; +}; + +/* Monitor for specified changes in the mailbox list. + Returns 0 if ok, -1 if notifications aren't supported. */ +int mailbox_list_notify_init(struct mailbox_list *list, + enum mailbox_list_notify_event mask, + struct mailbox_list_notify **notify_r); +void mailbox_list_notify_deinit(struct mailbox_list_notify **notify); + +/* Get the next change. Returns 1 if record was returned, 0 if there are no + more changes currently or -1 if some error occurred */ +int mailbox_list_notify_next(struct mailbox_list_notify *notify, + const struct mailbox_list_notify_rec **rec_r); +/* Call the specified callback when something changes. */ +void mailbox_list_notify_wait(struct mailbox_list_notify *notify, + void (*callback)(void *context), void *context); + +#endif diff -r 026b688b379f -r a16d77a075bb src/lib-storage/mailbox-list-private.h --- a/src/lib-storage/mailbox-list-private.h Mon Aug 13 15:15:07 2012 +0300 +++ b/src/lib-storage/mailbox-list-private.h Mon Aug 13 15:20:33 2012 +0300 @@ -12,10 +12,13 @@ #define MAILBOX_LOG_FILE_NAME "dovecot.mailbox.log" enum mailbox_log_record_type; +enum mailbox_list_notify_event; struct stat; struct dirent; struct imap_match_glob; struct mailbox_tree_context; +struct mailbox_list_notify; +struct mailbox_list_notify_rec; #define MAILBOX_INFO_FLAGS_FINISHED(flags) \ (((flags) & (MAILBOX_SELECT | MAILBOX_NOSELECT | \ @@ -85,6 +88,15 @@ int (*rename_mailbox)(struct mailbox_list *oldlist, const char *oldname, struct mailbox_list *newlist, const char *newname, bool rename_children); + + int (*notify_init)(struct mailbox_list *list, + enum mailbox_list_notify_event mask, + struct mailbox_list_notify **notify_r); + int (*notify_next)(struct mailbox_list_notify *notify, + const struct mailbox_list_notify_rec **rec_r); + void (*notify_deinit)(struct mailbox_list_notify *notify); + void (*notify_wait)(struct mailbox_list_notify *notify, + void (*callback)(void *context), void *context); }; struct mailbox_list_module_register {