view src/lib-storage/index/dbox-common/dbox-mail.c @ 10660:56b1d4dd9c7d HEAD

lib-storage: *_mailboxes don't descend from index_mailbox anymore, it's now a context.
author Timo Sirainen <tss@iki.fi>
date Sun, 07 Feb 2010 17:30:24 +0200
parents 615eef3139c2
children e202b2b86702
line wrap: on
line source

/* Copyright (c) 2007-2010 Dovecot authors, see the included COPYING file */

#include "lib.h"
#include "istream.h"
#include "str.h"
#include "index-storage.h"
#include "index-mail.h"
#include "dbox-storage.h"
#include "dbox-file.h"
#include "dbox-mail.h"

#include <stdlib.h>

struct mail *
dbox_mail_alloc(struct mailbox_transaction_context *t,
		enum mail_fetch_field wanted_fields,
		struct mailbox_header_lookup_ctx *wanted_headers)
{
	struct dbox_mail *mail;
	pool_t pool;

	pool = pool_alloconly_create("mail", 1024);
	mail = p_new(pool, struct dbox_mail, 1);
	mail->imail.mail.pool = pool;

	index_mail_init(&mail->imail, t, wanted_fields, wanted_headers);
	return &mail->imail.mail.mail;
}

void dbox_mail_close(struct mail *_mail)
{
	struct dbox_mail *mail = (struct dbox_mail *)_mail;

	if (mail->open_file != NULL)
		dbox_file_unref(&mail->open_file);
	index_mail_close(_mail);
}

int dbox_mail_metadata_read(struct dbox_mail *mail, struct dbox_file **file_r)
{
	struct dbox_storage *storage =
		(struct dbox_storage *)mail->imail.mail.mail.box->storage;
	uoff_t offset, size;

	if (storage->v.mail_open(mail, &offset, file_r) < 0)
		return -1;

	if (dbox_file_get_mail_stream(*file_r, offset, &size, NULL) <= 0)
		return -1;
	if (dbox_file_metadata_read(*file_r) <= 0)
		return -1;
	return 0;
}

int dbox_mail_get_physical_size(struct mail *_mail, uoff_t *size_r)
{
	struct index_mail *mail = (struct index_mail *)_mail;
	struct index_mail_data *data = &mail->data;
	struct istream *input;

	if (index_mail_get_physical_size(_mail, size_r) == 0)
		return 0;

	if (mail_get_stream(_mail, NULL, NULL, &input) < 0)
		return -1;

	i_assert(data->physical_size != (uoff_t)-1);
	*size_r = data->physical_size;
	return 0;
}

int dbox_mail_get_virtual_size(struct mail *_mail, uoff_t *size_r)
{
	struct dbox_mail *mail = (struct dbox_mail *)_mail;
	struct index_mail_data *data = &mail->imail.data;
	struct dbox_file *file;
	const char *value;

	if (index_mail_get_cached_virtual_size(&mail->imail, size_r))
		return 0;

	if (dbox_mail_metadata_read(mail, &file) < 0)
		return -1;

	value = dbox_file_metadata_get(file, DBOX_METADATA_VIRTUAL_SIZE);
	if (value == NULL)
		return index_mail_get_virtual_size(_mail, size_r);

	data->virtual_size = strtoul(value, NULL, 16);
	*size_r = data->virtual_size;
	return 0;
}

int dbox_mail_get_received_date(struct mail *_mail, time_t *date_r)
{
	struct dbox_mail *mail = (struct dbox_mail *)_mail;
	struct index_mail_data *data = &mail->imail.data;
	struct dbox_file *file;
	const char *value;

	if (index_mail_get_received_date(_mail, date_r) == 0)
		return 0;

	if (dbox_mail_metadata_read(mail, &file) < 0)
		return -1;

	value = dbox_file_metadata_get(file, DBOX_METADATA_RECEIVED_TIME);
	data->received_date = value == NULL ? 0 : strtoul(value, NULL, 16);
	*date_r = data->received_date;
	return 0;
}

int dbox_mail_get_save_date(struct mail *_mail, time_t *date_r)
{
	struct dbox_mail *mail = (struct dbox_mail *)_mail;
	struct index_mail_data *data = &mail->imail.data;
	struct dbox_file *file;
	struct stat st;
	const char *value;

 	if (index_mail_get_save_date(_mail, date_r) == 0)
		return 0;

	if (dbox_mail_metadata_read(mail, &file) < 0)
		return -1;

	value = dbox_file_metadata_get(file, DBOX_METADATA_SAVE_TIME);
	data->save_date = value == NULL ? 0 : strtoul(value, NULL, 16);

	if (data->save_date == 0) {
		/* missing / corrupted save time - use the file's ctime */
		i_assert(dbox_file_is_open(file));
		mail->imail.mail.stats_fstat_lookup_count++;
		if (fstat(file->fd, &st) < 0) {
			mail_storage_set_critical(_mail->box->storage,
				"fstat(%s) failed: %m", file->cur_path);
			return -1;
		}
		data->save_date = st.st_ctime;
	}
	*date_r = data->save_date;
	return 0;
}

static int
dbox_get_cached_metadata(struct dbox_mail *mail, enum dbox_metadata_key key,
			 enum index_cache_field cache_field,
			 const char **value_r)
{
	struct index_mail *imail = &mail->imail;
	struct index_mailbox_context *ibox =
		INDEX_STORAGE_CONTEXT(imail->mail.mail.box);
	struct dbox_file *file;
	const char *value;
	string_t *str;

	str = str_new(imail->data_pool, 64);
	if (mail_cache_lookup_field(imail->trans->cache_view, str,
				    imail->mail.mail.seq,
				    ibox->cache_fields[cache_field].idx) > 0) {
		*value_r = str_c(str);
		return 0;
	}

	if (dbox_mail_metadata_read(mail, &file) < 0)
		return -1;

	value = dbox_file_metadata_get(file, key);
	if (value == NULL)
		value = "";
	index_mail_cache_add_idx(imail, ibox->cache_fields[cache_field].idx,
				 value, strlen(value)+1);
	*value_r = value;
	return 0;
}

int dbox_mail_get_special(struct mail *_mail, enum mail_fetch_field field,
			  const char **value_r)
{
	struct dbox_mail *mail = (struct dbox_mail *)_mail;

	/* keep the UIDL in cache file, otherwise POP3 would open all
	   mail files and read the metadata. same for GUIDs if they're
	   used. */
	switch (field) {
	case MAIL_FETCH_UIDL_BACKEND:
		return dbox_get_cached_metadata(mail, DBOX_METADATA_POP3_UIDL,
						MAIL_CACHE_POP3_UIDL, value_r);
	case MAIL_FETCH_GUID:
		return dbox_get_cached_metadata(mail, DBOX_METADATA_GUID,
						MAIL_CACHE_GUID, value_r);
	default:
		break;
	}

	return index_mail_get_special(_mail, field, value_r);
}
							
int dbox_mail_get_stream(struct mail *_mail, struct message_size *hdr_size,
			 struct message_size *body_size,
			 struct istream **stream_r)
{
	struct dbox_storage *storage =
		(struct dbox_storage *)_mail->box->storage;
	struct dbox_mail *mail = (struct dbox_mail *)_mail;
	struct index_mail_data *data = &mail->imail.data;
	struct istream *input;
	uoff_t offset, size;
	int ret;

	if (data->stream == NULL) {
		if (storage->v.mail_open(mail, &offset, &mail->open_file) < 0)
			return -1;

		ret = dbox_file_get_mail_stream(mail->open_file, offset,
						&size, &input);
		if (ret <= 0) {
			if (ret < 0)
				return -1;
			dbox_file_set_corrupted(mail->open_file,
				"uid=%u points to broken data at offset="
				"%"PRIuUOFF_T, _mail->uid, offset);
			return -1;
		}
		data->physical_size = size;
		data->stream = input;
	}

	return index_mail_init_stream(&mail->imail, hdr_size, body_size,
				      stream_r);
}