view src/lib-storage/index/index-status.c @ 14133:ba770cba5598

Updated copyright notices to include year 2012.
author Timo Sirainen <tss@iki.fi>
date Sun, 12 Feb 2012 18:55:28 +0200
parents 3830d5a57fd4
children 1222b7e38bf3
line wrap: on
line source

/* Copyright (c) 2002-2012 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "array.h"
#include "mail-cache.h"
#include "mail-search-build.h"
#include "index-storage.h"
#include "mail-index-modseq.h"

static void
get_last_cached_seq(struct mailbox *box, uint32_t *last_cached_seq_r)
{
	const struct mail_index_header *hdr;
	struct mail_cache_view *cache_view;
	uint32_t seq;

	*last_cached_seq_r = 0;
	if (!mail_cache_exists(box->cache))
		return;

	cache_view = mail_cache_view_open(box->cache, box->view);
	hdr = mail_index_get_header(box->view);
	for (seq = hdr->messages_count; seq > 0; seq--) {
		if (mail_cache_field_exists_any(cache_view, seq)) {
			*last_cached_seq_r = seq;
			break;
		}
	}
	mail_cache_view_close(&cache_view);
}

int index_storage_get_status(struct mailbox *box,
			     enum mailbox_status_items items,
			     struct mailbox_status *status_r)
{
	const struct mail_index_header *hdr;

	memset(status_r, 0, sizeof(struct mailbox_status));

	if (!box->opened) {
		if (mailbox_open(box) < 0)
			return -1;
		if (mailbox_sync(box, 0) < 0)
			return -1;
	}

	/* we can get most of the status items without any trouble */
	hdr = mail_index_get_header(box->view);
	status_r->messages = hdr->messages_count;
	if ((items & STATUS_RECENT) != 0) {
		/* make sure recent count is set, in case syncing hasn't
		   been done yet */
		index_sync_update_recent_count(box);
		status_r->recent = index_mailbox_get_recent_count(box);
		i_assert(status_r->recent <= status_r->messages);
	}
	status_r->unseen = hdr->messages_count - hdr->seen_messages_count;
	status_r->uidvalidity = hdr->uid_validity;
	status_r->uidnext = hdr->next_uid;
	status_r->first_recent_uid = hdr->first_recent_uid;
	status_r->nonpermanent_modseqs = mail_index_is_in_memory(box->index);
	if ((items & STATUS_HIGHESTMODSEQ) != 0) {
		status_r->highest_modseq =
			mail_index_modseq_get_highest(box->view);
		if (status_r->highest_modseq == 0) {
			/* modseqs not enabled yet, but we can't return 0 */
			status_r->highest_modseq = 1;
		}
	}

	if ((items & STATUS_FIRST_UNSEEN_SEQ) != 0) {
		mail_index_lookup_first(box->view, 0, MAIL_SEEN,
					&status_r->first_unseen_seq);
	}
	if ((items & STATUS_LAST_CACHED_SEQ) != 0)
		get_last_cached_seq(box, &status_r->last_cached_seq);

	if ((items & STATUS_KEYWORDS) != 0)
		status_r->keywords = mail_index_get_keywords(box->index);
	if ((items & STATUS_PERMANENT_FLAGS) != 0) {
		if (!mailbox_is_readonly(box)) {
			status_r->permanent_flags = MAIL_FLAGS_NONRECENT;
			status_r->permanent_keywords = TRUE;
			status_r->allow_new_keywords =
				!box->disallow_new_keywords;
		}
	}
	return 0;
}

static void
get_metadata_cache_fields(struct mailbox *box,
			  struct mailbox_metadata *metadata_r)
{
	const struct mail_cache_field *fields;
	enum mail_cache_decision_type dec;
	ARRAY_TYPE(mailbox_cache_field) *cache_fields;
	struct mailbox_cache_field *cf;
	unsigned int i, count;

	fields = mail_cache_register_get_list(box->cache,
					      pool_datastack_create(), &count);

	cache_fields = t_new(ARRAY_TYPE(mailbox_cache_field), 1);
	t_array_init(cache_fields, count);
	for (i = 0; i < count; i++) {
		dec = fields[i].decision & ~MAIL_CACHE_DECISION_FORCED;
		if (dec != MAIL_CACHE_DECISION_NO) {
			cf = array_append_space(cache_fields);
			cf->name = fields[i].name;
			cf->decision = fields[i].decision;
			cf->last_used = fields[i].last_used;
		}
	}
	metadata_r->cache_fields = cache_fields;
}

static void get_metadata_precache_fields(struct mailbox *box,
					 struct mailbox_metadata *metadata_r)
{
	const struct mail_cache_field *fields;
	unsigned int i, count;
	enum mail_fetch_field cache = 0;

	fields = mail_cache_register_get_list(box->cache,
					      pool_datastack_create(), &count);
	for (i = 0; i < count; i++) {
		const char *name = fields[i].name;

		if (strncmp(name, "hdr.", 4) == 0 ||
		    strcmp(name, "date.sent") == 0 ||
		    strcmp(name, "imap.envelope") == 0)
			cache |= MAIL_FETCH_STREAM_HEADER;
		else if (strcmp(name, "mime.parts") == 0 ||
			 strcmp(name, "imap.body") == 0 ||
			 strcmp(name, "imap.bodystructure") == 0)
			cache |= MAIL_FETCH_STREAM_BODY;
		else if (strcmp(name, "date.received") == 0)
			cache |= MAIL_FETCH_RECEIVED_DATE;
		else if (strcmp(name, "date.save") == 0)
			cache |= MAIL_FETCH_SAVE_DATE;
		else if (strcmp(name, "size.virtual") == 0)
			cache |= MAIL_FETCH_VIRTUAL_SIZE;
		else if (strcmp(name, "size.physical") == 0)
			cache |= MAIL_FETCH_PHYSICAL_SIZE;
		else if (strcmp(name, "pop3.uidl") == 0)
			cache |= MAIL_FETCH_UIDL_BACKEND;
		else if (strcmp(name, "guid") == 0)
			cache |= MAIL_FETCH_GUID;
		else if (strcmp(name, "flags") == 0) {
			/* just ignore for now at least.. */
		} else if (box->storage->set->mail_debug)
			i_debug("Ignoring unknown cache field: %s", name);
	}
	metadata_r->precache_fields = cache;
}

static int
virtual_size_add_new(struct mailbox *box,
		     struct index_vsize_header *vsize_hdr)
{
	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
	const struct mail_index_header *hdr;
	struct mailbox_transaction_context *trans;
	struct mail_search_context *search_ctx;
	struct mail_search_args *search_args;
	struct mail *mail;
	uint32_t seq1, seq2;
	uoff_t vsize;
	int ret = 0;

	hdr = mail_index_get_header(box->view);
	if (vsize_hdr->highest_uid == 0)
		seq2 = 0;
	else if (!mail_index_lookup_seq_range(box->view, 1,
					      vsize_hdr->highest_uid,
					      &seq1, &seq2))
		seq2 = 0;

	if (vsize_hdr->message_count != seq2) {
		if (vsize_hdr->message_count < seq2) {
			mail_storage_set_critical(box->storage,
				"vsize-hdr has invalid message-count (%u < %u)",
				vsize_hdr->message_count, seq2);
		} else {
			/* some messages have been expunged, rescan */
		}
		memset(vsize_hdr, 0, sizeof(*vsize_hdr));
		seq2 = 0;
	}

	search_args = mail_search_build_init();
	mail_search_build_add_seqset(search_args, seq2 + 1,
				     hdr->messages_count);

	trans = mailbox_transaction_begin(box, 0);
	search_ctx = mailbox_search_init(trans, search_args, NULL,
					 MAIL_FETCH_VIRTUAL_SIZE, NULL);
	while (mailbox_search_next(search_ctx, &mail)) {
		if (mail_get_virtual_size(mail, &vsize) < 0) {
			if (mail->expunged)
				continue;
			ret = -1;
			break;
		}
		vsize_hdr->vsize += vsize;
		vsize_hdr->highest_uid = mail->uid;
		vsize_hdr->message_count++;
	}
	if (mailbox_search_deinit(&search_ctx) < 0)
		ret = -1;

	if (ret == 0) {
		/* success, cache all */
		vsize_hdr->highest_uid = hdr->next_uid - 1;
	} else {
		/* search failed, cache only up to highest seen uid */
	}
	mail_index_update_header_ext(trans->itrans, ibox->vsize_hdr_ext_id,
				     0, vsize_hdr, sizeof(*vsize_hdr));
	(void)mailbox_transaction_commit(&trans);
	return ret;
}

static int
get_metadata_virtual_size(struct mailbox *box,
			  struct mailbox_metadata *metadata_r)
{
	struct index_mailbox_context *ibox = INDEX_STORAGE_CONTEXT(box);
	struct index_vsize_header vsize_hdr;
	struct mailbox_status status;
	const void *data;
	size_t size;
	int ret;

	mailbox_get_open_status(box, STATUS_MESSAGES | STATUS_UIDNEXT, &status);
	mail_index_get_header_ext(box->view, ibox->vsize_hdr_ext_id,
				  &data, &size);
	if (size == sizeof(vsize_hdr))
		memcpy(&vsize_hdr, data, sizeof(vsize_hdr));
	else {
		if (size != 0) {
			mail_storage_set_critical(box->storage,
				"vsize-hdr has invalid size: %"PRIuSIZE_T,
				size);
		}
		memset(&vsize_hdr, 0, sizeof(vsize_hdr));
	}

	if (vsize_hdr.highest_uid + 1 == status.uidnext &&
	    vsize_hdr.message_count == status.messages) {
		/* up to date */
		metadata_r->virtual_size = vsize_hdr.vsize;
		return 0;
	}
	if (vsize_hdr.highest_uid >= status.uidnext) {
		mail_storage_set_critical(box->storage,
			"vsize-hdr has invalid highest-uid (%u >= %u)",
			vsize_hdr.highest_uid, status.uidnext);
		memset(&vsize_hdr, 0, sizeof(vsize_hdr));
	}
	ret = virtual_size_add_new(box, &vsize_hdr);
	metadata_r->virtual_size = vsize_hdr.vsize;
	return ret;
}

int index_mailbox_get_metadata(struct mailbox *box,
			       enum mailbox_metadata_items items,
			       struct mailbox_metadata *metadata_r)
{
	if (!box->opened) {
		if (mailbox_open(box) < 0)
			return -1;
	}

	if ((items & MAILBOX_METADATA_VIRTUAL_SIZE) != 0) {
		if (get_metadata_virtual_size(box, metadata_r) < 0)
			return -1;
	}
	if ((items & MAILBOX_METADATA_CACHE_FIELDS) != 0)
		get_metadata_cache_fields(box, metadata_r);
	if ((items & MAILBOX_METADATA_PRECACHE_FIELDS) != 0)
		get_metadata_precache_fields(box, metadata_r);
	return 0;
}