view src/lib-storage/index/index-fetch.c @ 160:ff05b320482c HEAD

Bigger changes.. full_virtual_size was removed from index record and MessagePart caching is now forced. Also added per-message flags, including binary flags which can be used to check if CRs need to be inserted into message data. Added mbox-rewrite support which can be used to write out mbox file with updated flags. This still has the problem of being able to read changed custom flags, that'll require another bigger change. There's also several other mostly mbox related fixes.
author Timo Sirainen <tss@iki.fi>
date Fri, 06 Sep 2002 16:43:58 +0300
parents 2f67de235489
children 4716cf66c2cc
line wrap: on
line source

/* Copyright (C) 2002 Timo Sirainen */

#include "lib.h"
#include "iobuffer.h"
#include "temp-string.h"
#include "index-storage.h"
#include "index-fetch.h"
#include "mail-messageset.h"
#include "message-send.h"
#include "imap-date.h"
#include "imap-util.h"
#include "imap-message-cache.h"

#include <unistd.h>

static void index_fetch_body(MailIndexRecord *rec, FetchContext *ctx)
{
	const char *body;

	body = imap_msgcache_get(ctx->cache, IMAP_CACHE_BODY);
	if (body != NULL)
		t_string_printfa(ctx->str, " BODY %s", body);
	else {
		i_error("Couldn't generate BODY for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
	}
}

static void index_fetch_bodystructure(MailIndexRecord *rec, FetchContext *ctx)
{
	const char *bodystructure;

	bodystructure = imap_msgcache_get(ctx->cache, IMAP_CACHE_BODYSTRUCTURE);
	if (bodystructure != NULL) {
		t_string_printfa(ctx->str, " BODYSTRUCTURE %s",
				 bodystructure);
	} else {
		i_error("Couldn't generate BODYSTRUCTURE for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
	}
}

static void index_fetch_envelope(MailIndexRecord *rec, FetchContext *ctx)
{
	const char *envelope;

	envelope = imap_msgcache_get(ctx->cache, IMAP_CACHE_ENVELOPE);
	if (envelope != NULL)
		t_string_printfa(ctx->str, " ENVELOPE (%s)", envelope);
	else {
		i_error("Couldn't generate ENVELOPE for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
	}
}

static void index_fetch_rfc822_size(MailIndexRecord *rec, FetchContext *ctx)
{
	MessageSize hdr_size, body_size;

	if (!imap_msgcache_get_rfc822(ctx->cache, NULL,
				      &hdr_size, &body_size)) {
		i_error("Couldn't get RFC822.SIZE for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
		return;
	}

	t_string_printfa(ctx->str, " RFC822.SIZE %"UOFF_T_FORMAT,
			 hdr_size.virtual_size + body_size.virtual_size);
}

static void index_fetch_flags(MailIndexRecord *rec, FetchContext *ctx)
{
	MailFlags flags;

	flags = rec->msg_flags;
	if (rec->uid >= ctx->index->first_recent_uid)
		flags |= MAIL_RECENT;
	if (ctx->update_seen)
		flags |= MAIL_SEEN;

	t_string_printfa(ctx->str, " FLAGS (%s)",
			 imap_write_flags(flags, ctx->custom_flags));
}

static void index_fetch_internaldate(MailIndexRecord *rec, FetchContext *ctx)
{
	t_string_printfa(ctx->str, " INTERNALDATE \"%s\"",
                         imap_to_datetime(rec->internal_date));
}

static void index_fetch_uid(MailIndexRecord *rec, FetchContext *ctx)
{
	t_string_printfa(ctx->str, " UID %u", rec->uid);
}

static void index_fetch_rfc822(MailIndexRecord *rec, FetchContext *ctx)
{
	MessageSize hdr_size, body_size;
	IOBuffer *inbuf;
	const char *str;

	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf,
				      &hdr_size, &body_size)) {
		i_error("Couldn't get RFC822 for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
		return;
	}

	str = t_strdup_printf(" RFC822 {%"UOFF_T_FORMAT"}\r\n",
			      hdr_size.virtual_size + body_size.virtual_size);
	if (ctx->first) str++; else ctx->first = FALSE;
	(void)io_buffer_send(ctx->outbuf, str, strlen(str));

	body_size.physical_size += hdr_size.physical_size;
	body_size.virtual_size += hdr_size.virtual_size;
	(void)message_send(ctx->outbuf, inbuf, &body_size, 0, (uoff_t)-1);
}

static void index_fetch_rfc822_header(MailIndexRecord *rec, FetchContext *ctx)
{
	MessageSize hdr_size;
	IOBuffer *inbuf;
	const char *str;

	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, &hdr_size, NULL)) {
		i_error("Couldn't get RFC822.HEADER for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
		return;
	}

	str = t_strdup_printf(" RFC822.HEADER {%"UOFF_T_FORMAT"}\r\n",
			      hdr_size.virtual_size);
	if (ctx->first) str++; else ctx->first = FALSE;
	(void)io_buffer_send(ctx->outbuf, str, strlen(str));
	(void)message_send(ctx->outbuf, inbuf, &hdr_size, 0, (uoff_t)-1);
}

static void index_fetch_rfc822_text(MailIndexRecord *rec, FetchContext *ctx)
{
	MessageSize body_size;
	IOBuffer *inbuf;
	const char *str;

	if (!imap_msgcache_get_rfc822(ctx->cache, &inbuf, NULL, &body_size)) {
		i_error("Couldn't get RFC822.TEXT for UID %u (index %s)",
			rec->uid, ctx->index->filepath);
		return;
	}

	str = t_strdup_printf(" RFC822.TEXT {%"UOFF_T_FORMAT"}\r\n",
			      body_size.virtual_size);
	if (ctx->first) str++; else ctx->first = FALSE;
	(void)io_buffer_send(ctx->outbuf, str, strlen(str));
	(void)message_send(ctx->outbuf, inbuf, &body_size, 0, (uoff_t)-1);
}

static ImapCacheField index_get_cache(MailFetchData *fetch_data)
{
	MailFetchBodyData *sect;
	ImapCacheField field;

	field = 0;
	if (fetch_data->body)
		field |= IMAP_CACHE_BODY;
	if (fetch_data->bodystructure)
		field |= IMAP_CACHE_BODYSTRUCTURE;
	if (fetch_data->envelope)
		field |= IMAP_CACHE_ENVELOPE;

	if (fetch_data->rfc822_size) {
		field |= IMAP_CACHE_MESSAGE_HDR_SIZE |
			IMAP_CACHE_MESSAGE_BODY_SIZE;
	}
	if (fetch_data->rfc822) {
		field |= IMAP_CACHE_MESSAGE_OPEN | IMAP_CACHE_MESSAGE_HDR_SIZE |
			IMAP_CACHE_MESSAGE_BODY_SIZE;
	}
	if (fetch_data->rfc822_header)
		field |= IMAP_CACHE_MESSAGE_OPEN | IMAP_CACHE_MESSAGE_HDR_SIZE;
	if (fetch_data->rfc822_text)
		field |= IMAP_CACHE_MESSAGE_OPEN | IMAP_CACHE_MESSAGE_BODY_SIZE;

	/* check what body[] sections want */
	sect = fetch_data->body_sections;
	for (; sect != NULL; sect = sect->next)
		field |= index_fetch_body_get_cache(sect->section);
	return field;
}

static void index_msgcache_open(FetchContext *ctx, MailIndexRecord *rec)
{
	ImapCacheField fields;
	uoff_t virtual_header_size, virtual_body_size;
	void *mail_cache_context;

	fields = index_get_cache(ctx->fetch_data);
	if (fields == 0)
		return;

        mail_cache_context = index_msgcache_get_context(ctx->index, rec);

	virtual_header_size =
		(rec->index_flags & INDEX_MAIL_FLAG_BINARY_HEADER) ?
		rec->header_size : 0;
	virtual_body_size =
		(rec->index_flags & INDEX_MAIL_FLAG_BINARY_BODY) ?
		rec->body_size : 0;

	imap_msgcache_open(ctx->cache, rec->uid, fields,
			   virtual_header_size, virtual_body_size,
			   mail_cache_context);
}

static int index_fetch_mail(MailIndex *index __attr_unused__,
			    MailIndexRecord *rec, unsigned int seq,
			    void *context)
{
	FetchContext *ctx = context;
	MailFetchBodyData *sect;

	ctx->str = t_string_new(2048);

	t_string_printfa(ctx->str, "* %u FETCH (", seq);
	(void)io_buffer_send(ctx->outbuf, ctx->str->str, ctx->str->len);
	t_string_truncate(ctx->str, 0);

	/* first see what we need to do. this way we don't first do some
	   light parsing and later notice that we need to do heavier parsing
	   anyway */
	index_msgcache_open(ctx, rec);

	if (ctx->fetch_data->uid)
		index_fetch_uid(rec, ctx);
	if (ctx->fetch_data->flags)
		index_fetch_flags(rec, ctx);
	if (ctx->fetch_data->internaldate)
		index_fetch_internaldate(rec, ctx);

	if (ctx->fetch_data->body)
		index_fetch_body(rec, ctx);
	if (ctx->fetch_data->bodystructure)
		index_fetch_bodystructure(rec, ctx);
	if (ctx->fetch_data->envelope)
		index_fetch_envelope(rec, ctx);
	if (ctx->fetch_data->rfc822_size)
		index_fetch_rfc822_size(rec, ctx);

	/* send the data written into temp string, skipping the initial space */
	if (ctx->str->len > 0) {
		(void)io_buffer_send(ctx->outbuf, ctx->str->str+1,
				     ctx->str->len-1);
	}
	ctx->first = ctx->str->len == 0;

	/* large data */
	if (ctx->fetch_data->rfc822)
		index_fetch_rfc822(rec, ctx);
	if (ctx->fetch_data->rfc822_text)
		index_fetch_rfc822_text(rec, ctx);
	if (ctx->fetch_data->rfc822_header)
		index_fetch_rfc822_header(rec, ctx);

	sect = ctx->fetch_data->body_sections;
	for (; sect != NULL; sect = sect->next)
		index_fetch_body_section(rec, seq, sect, ctx);

	(void)io_buffer_send(ctx->outbuf, ")\r\n", 3);

	imap_msgcache_close(ctx->cache);
	return TRUE;
}

int index_storage_fetch(Mailbox *box, MailFetchData *fetch_data,
			IOBuffer *outbuf, int *all_found)
{
	IndexMailbox *ibox = (IndexMailbox *) box;
	FetchContext ctx;
	MailFetchBodyData *sect;
	int ret;

	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED))
		return mail_storage_set_index_error(ibox);

	memset(&ctx, 0, sizeof(ctx));
	ctx.box = box;
	ctx.cache = ibox->cache;
	ctx.index = ibox->index;
	ctx.custom_flags = flags_file_list_get(ibox->flagsfile);

	ctx.fetch_data = fetch_data;
	ctx.outbuf = outbuf;

	/* If we have any BODY[..] sections, \Seen flag is added for
	   all messages */
	sect = ctx.fetch_data->body_sections;
	for (; sect != NULL; sect = sect->next) {
		if (!sect->peek) {
			ctx.update_seen = TRUE;
			break;
		}
	}

	ret = index_messageset_foreach(ibox, fetch_data->messageset,
				       fetch_data->uidset,
				       index_fetch_mail, &ctx);

        flags_file_list_unref(ibox->flagsfile);

	if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_UNLOCK))
		return mail_storage_set_index_error(ibox);

	if (all_found != NULL)
		*all_found = ret == 1;

	if (ret >= 1 && ctx.update_seen && !box->readonly) {
		/* BODY[..] was fetched, set \Seen flag for all messages.
		   This needs to be done separately because we need exclusive
		   lock for it */
		if (!index_storage_update_flags(box, fetch_data->messageset,
						fetch_data->uidset,
						MAIL_SEEN, NULL, MODIFY_ADD,
						NULL, NULL, NULL))
			return FALSE;
	}

	return ret >= 0;
}