mail-index-transaction-view.c revision 6ceec000fb554d3b38715288039b16534d7e3d4b
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen/* Copyright (c) 2004-2008 Dovecot authors, see the included COPYING file */
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "lib.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "array.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "buffer.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "seq-range-array.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "mail-index-private.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "mail-index-view-private.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen#include "mail-index-transaction-private.h"
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstruct mail_index_view_transaction {
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_view view;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_view_vfuncs *super;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_transaction *t;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_map *lookup_map;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_header hdr;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen buffer_t *lookup_return_data;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen};
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainenstatic void tview_close(struct mail_index_view *view)
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen{
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_view_transaction *tview =
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen (struct mail_index_view_transaction *)view;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen struct mail_index_transaction *t = tview->t;
6e8f0036cad59d1d6bcd9ef69bfe712d01656ca3Timo Sirainen
if (tview->lookup_map != NULL)
mail_index_unmap(&tview->lookup_map);
if (tview->lookup_return_data != NULL)
buffer_free(&tview->lookup_return_data);
tview->super->close(view);
mail_index_transaction_unref(&t);
}
static uint32_t tview_get_message_count(struct mail_index_view *view)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
return view->map->hdr.messages_count +
(tview->t->last_new_seq == 0 ? 0 :
tview->t->last_new_seq - tview->t->first_new_seq + 1);
}
static const struct mail_index_header *
tview_get_header(struct mail_index_view *view)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const struct mail_index_header *hdr;
uint32_t next_uid;
/* FIXME: header counters may not be correct */
hdr = tview->super->get_header(view);
next_uid = mail_index_transaction_get_next_uid(tview->t);
if (next_uid != hdr->next_uid) {
tview->hdr = *hdr;
tview->hdr.next_uid = next_uid;
hdr = &tview->hdr;
}
return hdr;
}
static const struct mail_index_record *
tview_lookup_full(struct mail_index_view *view, uint32_t seq,
struct mail_index_map **map_r, bool *expunged_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const struct mail_index_record *rec;
if (seq >= tview->t->first_new_seq) {
/* FIXME: is this right to return index map..?
it's not there yet. */
*map_r = view->index->map;
*expunged_r = FALSE;
return mail_index_transaction_lookup(tview->t, seq);
}
rec = tview->super->lookup_full(view, seq, map_r, expunged_r);
/* if we're expunged within this transaction, return 0 */
if (array_is_created(&tview->t->expunges) &&
seq_range_exists(&tview->t->expunges, seq))
*expunged_r = TRUE;
return rec;
}
static void
tview_lookup_uid(struct mail_index_view *view, uint32_t seq, uint32_t *uid_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
if (seq >= tview->t->first_new_seq)
*uid_r = mail_index_transaction_lookup(tview->t, seq)->uid;
else
tview->super->lookup_uid(view, seq, uid_r);
}
static void tview_lookup_seq_range(struct mail_index_view *view,
uint32_t first_uid, uint32_t last_uid,
uint32_t *first_seq_r, uint32_t *last_seq_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const struct mail_index_record *rec;
uint32_t seq;
tview->super->lookup_seq_range(view, first_uid, last_uid,
first_seq_r, last_seq_r);
if (tview->t->last_new_seq == 0) {
/* no new messages, the results are final. */
return;
}
rec = mail_index_transaction_lookup(tview->t, tview->t->first_new_seq);
if (rec->uid == 0) {
/* new messages don't have UIDs */
return;
}
if (last_uid < rec->uid) {
/* all wanted messages were existing */
return;
}
/* at least some of the wanted messages are newly created */
if (*first_seq_r == 0) {
seq = tview->t->first_new_seq;
for (; seq <= tview->t->last_new_seq; seq++) {
rec = mail_index_transaction_lookup(tview->t, seq);
if (first_uid <= rec->uid)
break;
}
if (seq > tview->t->last_new_seq) {
/* no messages in range */
return;
}
*first_seq_r = seq;
}
seq = tview->t->last_new_seq;
for (; seq >= tview->t->first_new_seq; seq--) {
rec = mail_index_transaction_lookup(tview->t, seq);
if (rec->uid <= last_uid) {
*last_seq_r = seq;
break;
}
}
i_assert(seq >= tview->t->first_new_seq);
}
static void tview_lookup_first(struct mail_index_view *view,
enum mail_flags flags, uint8_t flags_mask,
uint32_t *seq_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const struct mail_index_record *rec;
unsigned int append_count;
uint32_t seq, message_count;
tview->super->lookup_first(view, flags, flags_mask, seq_r);
if (*seq_r != 0)
return;
rec = array_get(&tview->t->appends, &append_count);
seq = tview->t->first_new_seq;
message_count = tview->t->last_new_seq;
i_assert(append_count == message_count - seq + 1);
for (; seq <= message_count; seq++, rec++) {
if ((rec->flags & flags_mask) == (uint8_t)flags) {
*seq_r = seq;
break;
}
}
}
static struct mail_index_map *
tview_get_lookup_map(struct mail_index_view_transaction *tview)
{
if (tview->lookup_map == NULL) {
tview->lookup_map =
mail_index_map_clone(tview->view.index->map);
}
return tview->lookup_map;
}
static const void *
tview_return_updated_ext(struct mail_index_view_transaction *tview,
const void *data, uint32_t ext_id)
{
const struct mail_index_ext *ext;
const struct mail_index_registered_ext *rext;
const struct mail_transaction_ext_intro *intro;
unsigned int record_align, record_size;
uint32_t ext_idx;
/* data begins with a 32bit sequence, followed by the actual
extension data */
data = CONST_PTR_OFFSET(data, sizeof(uint32_t));
if (!mail_index_map_get_ext_idx(tview->lookup_map, ext_id, &ext_idx)) {
/* we're adding the extension now. */
rext = array_idx(&tview->view.index->extensions, ext_id);
record_align = rext->record_align;
record_size = rext->record_size;
} else {
ext = array_idx(&tview->lookup_map->extensions, ext_idx);
record_align = ext->record_align;
record_size = ext->record_size;
}
/* see if the extension has been resized within this transaction */
if (array_is_created(&tview->t->ext_resizes) &&
ext_id < array_count(&tview->t->ext_resizes)) {
intro = array_idx(&tview->t->ext_resizes, ext_id);
if (intro[ext_id].name_size != 0) {
record_align = intro->record_align;
record_size = intro->record_size;
}
}
if (record_align <= sizeof(uint32_t)) {
/* data is 32bit aligned already */
return data;
} else {
/* assume we want 64bit alignment - copy the data to
temporary buffer and return it */
if (tview->lookup_return_data == NULL) {
tview->lookup_return_data =
buffer_create_dynamic(default_pool,
record_size + 64);
}
buffer_write(tview->lookup_return_data, 0, data, record_size);
return tview->lookup_return_data->data;
}
}
static void
tview_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, bool *expunged_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const ARRAY_TYPE(seq_array) *ext_buf;
const void *data;
unsigned int idx;
i_assert(ext_id < array_count(&view->index->extensions));
*expunged_r = FALSE;
if (array_is_created(&tview->t->ext_rec_updates) &&
ext_id < array_count(&tview->t->ext_rec_updates)) {
/* there are some ext updates in transaction.
see if there's any for this sequence. */
ext_buf = array_idx(&tview->t->ext_rec_updates, ext_id);
if (array_is_created(ext_buf) &&
mail_index_seq_array_lookup(ext_buf, seq, &idx)) {
data = array_idx(ext_buf, idx);
*map_r = tview_get_lookup_map(tview);
*data_r = tview_return_updated_ext(tview, data,
ext_id);
return;
}
}
/* not updated, return the existing value */
if (seq < tview->t->first_new_seq) {
tview->super->lookup_ext_full(view, seq, ext_id,
map_r, data_r, expunged_r);
} else {
*map_r = view->index->map;
*data_r = NULL;
}
}
static void tview_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)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
/* FIXME: check updates */
tview->super->get_header_ext(view, map, ext_id, data_r, data_size_r);
}
static bool tview_ext_get_reset_id(struct mail_index_view *view,
struct mail_index_map *map,
uint32_t ext_id, uint32_t *reset_id_r)
{
struct mail_index_view_transaction *tview =
(struct mail_index_view_transaction *)view;
const uint32_t *reset_id_p;
if (array_is_created(&tview->t->ext_reset_ids) &&
ext_id < array_count(&tview->t->ext_reset_ids) &&
map == tview->lookup_map) {
reset_id_p = array_idx(&tview->t->ext_reset_ids, ext_id);
*reset_id_r = *reset_id_p;
return TRUE;
}
return tview->super->ext_get_reset_id(view, map, ext_id, reset_id_r);
}
static struct mail_index_view_vfuncs trans_view_vfuncs = {
tview_close,
tview_get_message_count,
tview_get_header,
tview_lookup_full,
tview_lookup_uid,
tview_lookup_seq_range,
tview_lookup_first,
tview_lookup_ext_full,
tview_get_header_ext,
tview_ext_get_reset_id
};
struct mail_index_view *
mail_index_transaction_open_updated_view(struct mail_index_transaction *t)
{
struct mail_index_view_transaction *tview;
if (t->view->syncing) {
/* transaction view is being synced. while it's done, it's not
possible to add new messages, but the view itself might
change. so we can't make a copy of the view. */
mail_index_view_ref(t->view);
return t->view;
}
tview = i_new(struct mail_index_view_transaction, 1);
mail_index_view_clone(&tview->view, t->view);
tview->view.v = trans_view_vfuncs;
tview->super = &t->view->v;
tview->t = t;
mail_index_transaction_ref(t);
return &tview->view;
}