mail-index-view.c revision bf91bed88d4e294b4577ba2a3b14d87cf35ae135
a82212bd36e1074408974b466798b9966bbaf49bvboxsync/* Copyright (C) 2003-2004 Timo Sirainen */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#include "lib.h"
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#include "array.h"
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#include "buffer.h"
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#include "mail-index-view-private.h"
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#include "mail-transaction-log.h"
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_clone(struct mail_index_view *dest,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync const struct mail_index_view *src)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync memset(dest, 0, sizeof(dest));
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->refcount = 1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->v = src->v;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->index = src->index;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (src->log_view != NULL) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->log_view =
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_transaction_log_view_open(src->index->log);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->indexid = src->indexid;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->map = src->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (dest->map != NULL)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->map->refcount++;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->log_file_expunge_seq = src->log_file_expunge_seq;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->log_file_expunge_offset = src->log_file_expunge_offset;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->log_file_head_seq = src->log_file_head_seq;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync dest->log_file_head_offset = src->log_file_head_offset;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_array_init(&dest->module_contexts,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync I_MIN(5, mail_index_module_register.id));
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_ref(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync view->refcount++;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic void _view_close(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_assert(view->refcount == 0);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* we're not unlocking the view, because views could be temporarily
a82212bd36e1074408974b466798b9966bbaf49bvboxsync created and closed and the current map should stay locked
a82212bd36e1074408974b466798b9966bbaf49bvboxsync (especially syncing) */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_transaction_log_view_close(&view->log_view);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (array_is_created(&view->syncs_hidden))
a82212bd36e1074408974b466798b9966bbaf49bvboxsync array_free(&view->syncs_hidden);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_unmap(&view->map);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (array_is_created(&view->map_refs)) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_view_unref_maps(view);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync array_free(&view->map_refs);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync array_free(&view->module_contexts);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_free(view);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#ifdef DEBUG
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic void mail_index_view_check_nextuid(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_record *rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (view->map->hdr.messages_count == 0)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync rec = MAIL_INDEX_MAP_IDX(view->map, view->map->hdr.messages_count-1);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_assert(rec->uid < view->map->hdr.next_uid);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#endif
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_unlock(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#ifdef DEBUG
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_view_check_nextuid(view);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync#endif
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* currently this is a no-op. if we unlock any maps, they might get
a82212bd36e1074408974b466798b9966bbaf49bvboxsync changed and then it's unspecified what parts of the memory mapping
a82212bd36e1074408974b466798b9966bbaf49bvboxsync are up-to-date. we could also copy the map to memory always, but
a82212bd36e1074408974b466798b9966bbaf49bvboxsync that kinds of defeats the purpose of mmaps. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncbool mail_index_view_is_inconsistent(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (view->index->indexid != view->indexid)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync view->inconsistent = TRUE;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return view->inconsistent;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstruct mail_index *mail_index_view_get_index(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return view->index;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_transaction_ref(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync view->transactions++;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_transaction_unref(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_assert(view->transactions > 0);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync view->transactions--;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic void mail_index_view_ref_map(struct mail_index_view *view,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_map *map)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_map *const *maps;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync unsigned int i, count;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (array_is_created(&view->map_refs)) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync maps = array_get(&view->map_refs, &count);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* if map is already referenced, do nothing */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync for (i = 0; i < count; i++) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (maps[i] == map)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync } else {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_array_init(&view->map_refs, 4);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* reference the given mapping. the reference is dropped when the view
a82212bd36e1074408974b466798b9966bbaf49bvboxsync is synchronized or closed. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync map->refcount++;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync array_append(&view->map_refs, &map, 1);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncvoid mail_index_view_unref_maps(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_map **maps;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync unsigned int i, count;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (!array_is_created(&view->map_refs))
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync maps = array_get_modifiable(&view->map_refs, &count);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync for (i = 0; i < count; i++)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_unmap(&maps[i]);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync array_clear(&view->map_refs);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic uint32_t _view_get_messages_count(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return view->map->hdr.messages_count;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic const struct mail_index_header *
a82212bd36e1074408974b466798b9966bbaf49bvboxsync_view_get_header(struct mail_index_view *view)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return &view->map->hdr;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic int _view_lookup_full(struct mail_index_view *view, uint32_t seq,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_map **map_r,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync const struct mail_index_record **rec_r)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync struct mail_index_map *map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync const struct mail_index_record *rec, *head_rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (mail_index_map_lock(view->map) < 0)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return -1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* look up the record */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync rec = MAIL_INDEX_MAP_IDX(view->map, seq-1);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (rec->uid == 0) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_set_error(view->index, "Corrupted Index file %s: "
a82212bd36e1074408974b466798b9966bbaf49bvboxsync "Record [%u].uid=0", view->index->filepath, seq);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_mark_corrupted(view->index);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return -1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (view->map == view->index->map) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* view's mapping is latest. we can use it directly. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *map_r = view->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *rec_r = rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return 1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* look up the record from head mapping. it may contain some changes. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (mail_index_map_lock(view->index->map) < 0)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return -1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* start looking up from the same sequence as in the old view.
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if there are no expunges, it's there. otherwise it's somewhere
a82212bd36e1074408974b466798b9966bbaf49bvboxsync before (since records can't be inserted).
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync usually there are only a few expunges, so just going downwards from
a82212bd36e1074408974b466798b9966bbaf49bvboxsync our initial sequence position is probably faster than binary
a82212bd36e1074408974b466798b9966bbaf49bvboxsync search. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (seq > view->index->map->hdr.messages_count)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync seq = view->index->map->hdr.messages_count;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (seq == 0) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* everything is expunged from head. use the old record. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *map_r = view->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *rec_r = rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return 0;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync map = view->index->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync do {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync seq--;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync head_rec = MAIL_INDEX_MAP_IDX(map, seq);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (head_rec->uid <= rec->uid)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync break;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync } while (seq > 0);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync if (head_rec->uid == rec->uid) {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* found it. use it. reference the index mapping so that the
a82212bd36e1074408974b466798b9966bbaf49bvboxsync returned record doesn't get invalidated after next sync. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync mail_index_view_ref_map(view, view->index->map);
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *map_r = view->index->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *rec_r = head_rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return 1;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync } else {
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* expuned from head. use the old record. */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *map_r = view->map;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *rec_r = rec;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return 0;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync }
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsyncstatic int _view_lookup_uid(struct mail_index_view *view, uint32_t seq,
a82212bd36e1074408974b466798b9966bbaf49bvboxsync uint32_t *uid_r)
a82212bd36e1074408974b466798b9966bbaf49bvboxsync{
a82212bd36e1074408974b466798b9966bbaf49bvboxsync i_assert(seq > 0 && seq <= mail_index_view_get_messages_count(view));
a82212bd36e1074408974b466798b9966bbaf49bvboxsync
a82212bd36e1074408974b466798b9966bbaf49bvboxsync /* UID lookups don't require the view to be locked. only expunges
a82212bd36e1074408974b466798b9966bbaf49bvboxsync change them, and expunges will recreate the file */
a82212bd36e1074408974b466798b9966bbaf49bvboxsync *uid_r = MAIL_INDEX_MAP_IDX(view->map, seq-1)->uid;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync return 0;
a82212bd36e1074408974b466798b9966bbaf49bvboxsync}
static uint32_t mail_index_bsearch_uid(struct mail_index_view *view,
uint32_t uid, uint32_t left_idx,
int nearest_side)
{
const struct mail_index_record *rec_base, *rec;
uint32_t idx, right_idx, record_size;
i_assert(view->map->hdr.messages_count <= view->map->records_count);
if (uid == 1) {
/* optimization: the message can be only the first one */
return 1;
}
rec_base = view->map->records;
record_size = view->map->hdr.record_size;
idx = left_idx;
right_idx = view->map->hdr.messages_count;
while (left_idx < right_idx) {
idx = (left_idx + right_idx) / 2;
rec = CONST_PTR_OFFSET(rec_base, idx * record_size);
if (rec->uid < uid)
left_idx = idx+1;
else if (rec->uid > uid)
right_idx = idx;
else
break;
}
i_assert(idx < view->map->hdr.messages_count);
rec = CONST_PTR_OFFSET(rec_base, idx * record_size);
if (rec->uid != uid) {
if (nearest_side > 0) {
/* we want uid or larger */
return rec->uid > uid ? idx+1 :
(idx == view->map->hdr.messages_count-1 ?
0 : idx+2);
} else {
/* we want uid or smaller */
return rec->uid < uid ? idx + 1 : idx;
}
}
return idx+1;
}
static int _view_lookup_uid_range(struct mail_index_view *view,
uint32_t first_uid, uint32_t last_uid,
uint32_t *first_seq_r, uint32_t *last_seq_r)
{
i_assert(first_uid > 0);
i_assert(first_uid <= last_uid);
/* no locking needed for UIDs, see _view_lookup_uid() */
if (view->map->hdr.messages_count == 0) {
*first_seq_r = *last_seq_r = 0;
return 0;
}
*first_seq_r = mail_index_bsearch_uid(view, first_uid, 0, 1);
if (*first_seq_r == 0 ||
MAIL_INDEX_MAP_IDX(view->map, *first_seq_r-1)->uid > last_uid) {
*first_seq_r = *last_seq_r = 0;
return 0;
}
if (last_uid >= view->map->hdr.next_uid-1) {
/* we want the last message */
last_uid = view->map->hdr.next_uid-1;
if (first_uid > last_uid) {
*first_seq_r = *last_seq_r = 0;
return 0;
}
*last_seq_r = view->map->hdr.messages_count;
return 0;
}
if (first_uid == last_uid)
*last_seq_r = *first_seq_r;
else {
/* optimization - binary lookup only from right side: */
*last_seq_r = mail_index_bsearch_uid(view, last_uid,
*first_seq_r - 1, -1);
}
i_assert(*last_seq_r >= *first_seq_r);
return 0;
}
static int _view_lookup_first(struct mail_index_view *view,
enum mail_flags flags, uint8_t flags_mask,
uint32_t *seq_r)
{
#define LOW_UPDATE(x) \
STMT_START { if ((x) > low_uid) low_uid = x; } STMT_END
const struct mail_index_record *rec;
uint32_t seq, low_uid = 1;
*seq_r = 0;
if ((flags_mask & MAIL_RECENT) != 0 && (flags & MAIL_RECENT) != 0)
LOW_UPDATE(view->map->hdr.first_recent_uid_lowwater);
if ((flags_mask & MAIL_SEEN) != 0 && (flags & MAIL_SEEN) == 0)
LOW_UPDATE(view->map->hdr.first_unseen_uid_lowwater);
if ((flags_mask & MAIL_DELETED) != 0 && (flags & MAIL_DELETED) != 0)
LOW_UPDATE(view->map->hdr.first_deleted_uid_lowwater);
if (low_uid == 1)
seq = 1;
else {
if (mail_index_lookup_uid_range(view, low_uid, low_uid,
&seq, &seq) < 0)
return -1;
if (seq == 0)
return 0;
}
i_assert(view->map->hdr.messages_count <= view->map->records_count);
/* we can delay locking until we're looking at the flags */
if (mail_index_map_lock(view->map) < 0)
return -1;
for (; seq <= view->map->hdr.messages_count; seq++) {
rec = MAIL_INDEX_MAP_IDX(view->map, seq-1);
if ((rec->flags & flags_mask) == (uint8_t)flags) {
*seq_r = seq;
break;
}
}
return 0;
}
static int _view_lookup_ext_full(struct mail_index_view *view, uint32_t seq,
uint32_t ext_id, struct mail_index_map **map_r,
const void **data_r)
{
const struct mail_index_ext *ext;
const struct mail_index_record *rec;
uint32_t idx, offset;
int ret;
if ((ret = mail_index_lookup_full(view, seq, map_r, &rec)) < 0)
return -1;
if (rec == NULL || !mail_index_map_get_ext_idx(*map_r, ext_id, &idx)) {
*data_r = NULL;
return ret;
}
ext = array_idx(&(*map_r)->extensions, idx);
offset = ext->record_offset;
*data_r = offset == 0 ? NULL : CONST_PTR_OFFSET(rec, offset);
return ret;
}
static int _view_get_header_ext(struct mail_index_view *view,
struct mail_index_map *map, uint32_t ext_id,
const void **data_r, size_t *data_size_r)
{
const struct mail_index_ext *ext;
uint32_t idx;
if (map == NULL) {
/* no mapping given, use head mapping */
map = view->index->map;
}
if (mail_index_map_lock(map) < 0)
return -1;
if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) {
/* extension doesn't exist in this index file */
*data_r = NULL;
*data_size_r = 0;
return 0;
}
ext = array_idx(&map->extensions, idx);
*data_r = CONST_PTR_OFFSET(map->hdr_base, ext->hdr_offset);
*data_size_r = ext->hdr_size;
return 0;
}
void mail_index_view_close(struct mail_index_view **_view)
{
struct mail_index_view *view = *_view;
*_view = NULL;
if (--view->refcount > 0)
return;
i_assert(view->transactions == 0);
view->v.close(view);
}
uint32_t mail_index_view_get_messages_count(struct mail_index_view *view)
{
return view->v.get_messages_count(view);
}
const struct mail_index_header *
mail_index_get_header(struct mail_index_view *view)
{
return view->v.get_header(view);
}
int mail_index_lookup(struct mail_index_view *view, uint32_t seq,
const struct mail_index_record **rec_r)
{
struct mail_index_map *map;
return mail_index_lookup_full(view, seq, &map, rec_r);
}
int mail_index_lookup_full(struct mail_index_view *view, uint32_t seq,
struct mail_index_map **map_r,
const struct mail_index_record **rec_r)
{
return view->v.lookup_full(view, seq, map_r, rec_r);
}
int mail_index_lookup_keywords(struct mail_index_view *view, uint32_t seq,
ARRAY_TYPE(keyword_indexes) *keyword_idx)
{
struct mail_index_map *map;
const void *data;
const unsigned char *keyword_data;
const unsigned int *keyword_idx_map;
unsigned int i, j, keyword_count, index_idx;
uint32_t ext_id, idx;
uint16_t record_size;
int ret;
array_clear(keyword_idx);
/* get the keywords data. */
ext_id = view->index->keywords_ext_id;
ret = mail_index_lookup_ext_full(view, seq, ext_id, &map, &data);
if (ret < 0)
return -1;
if (data == NULL) {
/* no keywords at all in index */
return ret;
}
(void)mail_index_ext_get_size(view, ext_id, map, NULL,
&record_size, NULL);
/* keyword_idx_map[] contains file => index keyword mapping */
if (!array_is_created(&map->keyword_idx_map)) {
keyword_idx_map = NULL;
keyword_count = 0;
} else {
keyword_idx_map = array_get(&map->keyword_idx_map,
&keyword_count);
}
keyword_data = data;
for (i = 0, idx = 0; i < record_size; i++) {
/* first do the quick check to see if there's keywords at all */
if (keyword_data[i] == 0)
continue;
idx = i * CHAR_BIT;
for (j = 0; j < CHAR_BIT; j++, idx++) {
if ((keyword_data[i] & (1 << j)) == 0)
continue;
if (idx >= keyword_count) {
/* keyword header was updated, parse it again
it so we know what this keyword is called */
if (mail_index_map_parse_keywords(map) < 0)
return -1;
if (!array_is_created(&map->keyword_idx_map))
return ret;
/* pointer may have changed. update it. */
keyword_idx_map =
array_get(&map->keyword_idx_map,
&keyword_count);
if (idx >= keyword_count) {
/* extra bits set in keyword bytes.
shouldn't happen, but just ignore. */
break;
}
}
index_idx = keyword_idx_map[idx];
array_append(keyword_idx, &index_idx, 1);
}
}
return ret;
}
int mail_index_lookup_uid(struct mail_index_view *view, uint32_t seq,
uint32_t *uid_r)
{
return view->v.lookup_uid(view, seq, uid_r);
}
int mail_index_lookup_uid_range(struct mail_index_view *view,
uint32_t first_uid, uint32_t last_uid,
uint32_t *first_seq_r, uint32_t *last_seq_r)
{
return view->v.lookup_uid_range(view, first_uid, last_uid,
first_seq_r, last_seq_r);
}
int mail_index_lookup_first(struct mail_index_view *view, enum mail_flags flags,
uint8_t flags_mask, uint32_t *seq_r)
{
return view->v.lookup_first(view, flags, flags_mask, seq_r);
}
int mail_index_lookup_ext(struct mail_index_view *view, uint32_t seq,
uint32_t ext_id, const void **data_r)
{
struct mail_index_map *map;
return view->v.lookup_ext_full(view, seq, ext_id, &map, data_r);
}
int mail_index_lookup_ext_full(struct mail_index_view *view, uint32_t seq,
uint32_t ext_id, struct mail_index_map **map_r,
const void **data_r)
{
return view->v.lookup_ext_full(view, seq, ext_id, map_r, data_r);
}
int mail_index_get_header_ext(struct mail_index_view *view, uint32_t ext_id,
const void **data_r, size_t *data_size_r)
{
return view->v.get_header_ext(view, NULL, ext_id, data_r, data_size_r);
}
int mail_index_map_get_header_ext(struct mail_index_view *view,
struct mail_index_map *map, uint32_t ext_id,
const void **data_r, size_t *data_size_r)
{
return view->v.get_header_ext(view, map, ext_id, data_r, data_size_r);
}
int mail_index_ext_get_size(struct mail_index_view *view __attr_unused__,
uint32_t ext_id, struct mail_index_map *map,
uint32_t *hdr_size_r, uint16_t *record_size_r,
uint16_t *record_align_r)
{
const struct mail_index_ext *ext;
uint32_t idx;
i_assert(map != NULL);
if (!mail_index_map_get_ext_idx(map, ext_id, &idx)) {
/* extension doesn't exist in this index file */
if (hdr_size_r != NULL)
*hdr_size_r = 0;
if (record_size_r != NULL)
*record_size_r = 0;
if (record_align_r != NULL)
*record_align_r = 0;
return 0;
}
ext = array_idx(&map->extensions, idx);
if (hdr_size_r != NULL)
*hdr_size_r = ext->hdr_size;
if (record_size_r != NULL)
*record_size_r = ext->record_size;
if (record_align_r != NULL)
*record_align_r = ext->record_align;
return 0;
}
static struct mail_index_view_vfuncs view_vfuncs = {
_view_close,
_view_get_messages_count,
_view_get_header,
_view_lookup_full,
_view_lookup_uid,
_view_lookup_uid_range,
_view_lookup_first,
_view_lookup_ext_full,
_view_get_header_ext
};
struct mail_index_view *
mail_index_view_open_with_map(struct mail_index *index,
struct mail_index_map *map)
{
struct mail_index_view *view;
view = i_new(struct mail_index_view, 1);
view->refcount = 1;
view->v = view_vfuncs;
view->index = index;
view->log_view = mail_transaction_log_view_open(index->log);
view->indexid = index->indexid;
view->map = map;
view->map->refcount++;
view->log_file_expunge_seq = view->log_file_head_seq =
view->map->hdr.log_file_seq;
view->log_file_expunge_offset = view->log_file_head_offset =
view->map->hdr.log_file_head_offset;
i_array_init(&view->module_contexts,
I_MIN(5, mail_index_module_register.id));
return view;
}
struct mail_index_view *mail_index_view_open(struct mail_index *index)
{
return mail_index_view_open_with_map(index, index->map);
}
const struct mail_index_ext *
mail_index_view_get_ext(struct mail_index_view *view, uint32_t ext_id)
{
uint32_t idx;
if (!mail_index_map_get_ext_idx(view->map, ext_id, &idx))
return 0;
return array_idx(&view->map->extensions, idx);
}