imap-message-cache.c revision d8e43743157d05e416eb5d3b45cddb3b3122dc07
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov/* Copyright (C) 2002 Timo Sirainen */
d82741b1a8ada493ca74efa5d5c8b731412d035cLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "lib.h"
4e17c050dac8f2c6e2d278c4c4a27001c8d7d164Jakub Hrozek#include "ibuffer.h"
1921d739ff7b028baa591272cc8969e330c8f872Jakub Hrozek#include "temp-string.h"
38b07019861240cf5107f5d51fc0027519e21619Lukas Slebodnik#include "mmap-util.h"
b8946a5dbde01a87465de707092716349a35248bJakub Hrozek#include "message-parser.h"
b4633e73067d7bf3b0dbaf212569c123de88f306Lukas Slebodnik#include "message-part-serialize.h"
8578fba1500d43ad9632784462c255bf8bb360feJakub Hrozek#include "message-size.h"
3728db53ac32da51fcaae96b132e8e56ebbaebfaJakub Hrozek#include "imap-bodystructure.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "imap-envelope.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include "imap-message-cache.h"
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov#include <unistd.h>
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
8d1dcb6af723f2968410c4b088d06d63d02b4feaPavel Reichl/* It's not very useful to cache lots of messages, as they're mostly wanted
586f512ab8b6e5a03349598846141f43c1d505b8Michal Židek just once. The biggest reason for this cache to exist is to get just the
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov latest message. */
1f4dc2971bac4ceb0803b18f86a746656a0f1990Lukas Slebodnik#define MAX_CACHED_MESSAGES 16
49a5412cbc98e630de17359c29cb8d6ce0e16168Lukas Slebodnik
a2c10cf31d14bac598f5cd008973375c3f9575a6Lukas Slebodnik#define DEFAULT_MESSAGE_POOL_SIZE 4096
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashov
b8946a5dbde01a87465de707092716349a35248bJakub Hrozektypedef struct _CachedMessage CachedMessage;
05457ed0e399aaacc919b7aacee5d8210e1c1072Petr Cech
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozekstruct _CachedMessage {
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek CachedMessage *next;
35ecfab87a24031e55798b22975e02832ee0f2adMichal Židek
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek Pool pool;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek unsigned int uid;
8bdb8c0970dc9acb5b0a54dab0bae306ca964944Jakub Hrozek
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek MessagePart *part;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek MessageSize *hdr_size;
0700118d8388c38b8cb28279510b206b76a3a411Jakub Hrozek MessageSize *body_size;
1f331476e7d33bb03cc35a2a9064ee1cc5bed6cfSumit Bose MessageSize *partial_size;
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik
36df33cd44774a5b5eab52ab222bcd3240b3ca5aLukas Slebodnik time_t internal_date;
da7a3c347dd630085839afa7ec245ee9d36f6ce2Lukas Slebodnik uoff_t full_virtual_size;
0df79781d78973e5462dbef1e89d8fd6001da05cLukas Slebodnik
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov char *cached_body;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov char *cached_bodystructure;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik char *cached_envelope;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik MessagePartEnvelopeData *envelope;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik};
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstruct _ImapMessageCache {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik ImapMessageCacheIface *iface;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik CachedMessage *messages;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik int messages_count;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik CachedMessage *open_msg;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik IBuffer *open_inbuf;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik void *context;
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik};
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas SlebodnikImapMessageCache *imap_msgcache_alloc(ImapMessageCacheIface *iface)
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik{
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik ImapMessageCache *cache;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik cache = i_new(ImapMessageCache, 1);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik cache->iface = iface;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik return cache;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik}
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikstatic void cached_message_free(CachedMessage *msg)
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik{
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik pool_unref(msg->pool);
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik}
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnikvoid imap_msgcache_clear(ImapMessageCache *cache)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov CachedMessage *next;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov imap_msgcache_close(cache);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov while (cache->messages != NULL) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov next = cache->messages->next;
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek cached_message_free(cache->messages);
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek cache->messages = next;
db0982c52294ee5ea08ed242d27660783fde29cdJakub Hrozek }
91b0592cdab22915dff27ceae6d8e49c608aea4aJakub Hrozek}
53a4219e2f51cd0443931aa931505bf0b4bf5a45Nikolai Kondrashov
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnikvoid imap_msgcache_free(ImapMessageCache *cache)
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik{
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik imap_msgcache_clear(cache);
a3bed9df5a47bfc84b82341f0f7e693e2b14a67aLukas Slebodnik i_free(cache);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic CachedMessage *cache_new(ImapMessageCache *cache, unsigned int uid)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov CachedMessage *msg, **msgp;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov Pool pool;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (cache->messages_count < MAX_CACHED_MESSAGES)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov cache->messages_count++;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov else {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov /* remove the last message from cache */
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msgp = &cache->messages;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov while ((*msgp)->next != NULL)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msgp = &(*msgp)->next;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov cached_message_free(*msgp);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov *msgp = NULL;
52ae4eeba9c97c0254a2025ec3b5ffe90588b775Lukas Slebodnik }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov pool = pool_create("CachedMessage", DEFAULT_MESSAGE_POOL_SIZE, FALSE);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg = p_new(pool, CachedMessage, 1);
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg->pool = pool;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg->uid = uid;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg->internal_date = (time_t)-1;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg->full_virtual_size = (uoff_t)-1;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg->next = cache->messages;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov cache->messages = msg;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov return msg;
82c36227e36de155b13e6eb7cfa3e80a25774157Lukas Slebodnik}
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashovstatic CachedMessage *cache_open_or_create(ImapMessageCache *cache,
841bcb5e1f66bb9c41e1884a2ab1dae654def13eLukas Slebodnik unsigned int uid)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov{
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov CachedMessage **pos, *msg;
f75ba99fc8dd64e45af2f642d9fb7660860fd28fLukas Slebodnik
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik pos = &cache->messages;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov for (; *pos != NULL; pos = &(*pos)->next) {
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if ((*pos)->uid == uid)
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov break;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov }
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov if (*pos == NULL) {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik /* not found, add it */
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik msg = cache_new(cache, uid);
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik } else if (*pos != cache->messages) {
e64696e1fab85c42aaeda65ddf49ee1b7e3f07e1Lukas Slebodnik /* move it to first in list */
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov msg = *pos;
9d453f1e8b28983b363b44c49b7cd701a994fd97Nikolai Kondrashov *pos = msg->next;
msg->next = cache->messages;
cache->messages = msg;
} else {
msg = *pos;
}
return msg;
}
static void parse_envelope_header(MessagePart *part,
const char *name, size_t name_len,
const char *value, size_t value_len,
void *context)
{
CachedMessage *msg = context;
if (part == NULL || part->parent == NULL) {
/* parse envelope headers if we're at the root message part */
imap_envelope_parse_header(msg->pool, &msg->envelope,
t_strndup(name, name_len),
value, value_len);
}
}
static int imap_msgcache_get_inbuf(ImapMessageCache *cache, uoff_t offset)
{
if (cache->open_inbuf == NULL)
cache->open_inbuf = cache->iface->open_mail(cache->context);
else if (offset < cache->open_inbuf->v_offset) {
/* need to rewind */
cache->open_inbuf =
cache->iface->inbuf_rewind(cache->open_inbuf,
cache->context);
}
if (cache->open_inbuf == NULL)
return FALSE;
i_assert(offset >= cache->open_inbuf->v_offset);
i_buffer_skip(cache->open_inbuf, offset - cache->open_inbuf->v_offset);
return TRUE;
}
static void msg_get_part(ImapMessageCache *cache)
{
if (cache->open_msg->part == NULL) {
cache->open_msg->part =
cache->iface->get_cached_parts(cache->open_msg->pool,
cache->context);
}
}
/* Caches the fields for given message if possible */
static int cache_fields(ImapMessageCache *cache, ImapCacheField fields)
{
CachedMessage *msg;
const char *value;
int failed;
msg = cache->open_msg;
failed = FALSE;
t_push();
if ((fields & IMAP_CACHE_BODYSTRUCTURE) &&
msg->cached_bodystructure == NULL) {
value = cache->iface->get_cached_field(IMAP_CACHE_BODYSTRUCTURE,
cache->context);
if (value == NULL && imap_msgcache_get_inbuf(cache, 0)) {
msg_get_part(cache);
value = imap_part_get_bodystructure(msg->pool,
&msg->part,
cache->open_inbuf,
TRUE);
}
msg->cached_bodystructure = p_strdup(msg->pool, value);
failed = value == NULL;
}
if ((fields & IMAP_CACHE_BODY) && msg->cached_body == NULL) {
value = cache->iface->get_cached_field(IMAP_CACHE_BODY,
cache->context);
if (value == NULL && cache->open_inbuf != NULL) {
/* we can generate it from cached BODYSTRUCTURE.
do it only if the file isn't open already, since
this takes more CPU than parsing message headers. */
value = cache->iface->get_cached_field(
IMAP_CACHE_BODYSTRUCTURE, cache->context);
if (value != NULL) {
value = imap_body_parse_from_bodystructure(
value);
}
}
if (value == NULL && imap_msgcache_get_inbuf(cache, 0)) {
msg_get_part(cache);
value = imap_part_get_bodystructure(msg->pool,
&msg->part,
cache->open_inbuf,
FALSE);
}
msg->cached_body = p_strdup(msg->pool, value);
failed = value == NULL;
}
if ((fields & IMAP_CACHE_ENVELOPE) && msg->cached_envelope == NULL) {
value = cache->iface->get_cached_field(IMAP_CACHE_ENVELOPE,
cache->context);
if (value == NULL) {
if (msg->envelope == NULL &&
imap_msgcache_get_inbuf(cache, 0)) {
/* envelope isn't parsed yet, do it. header
size is calculated anyway so save it */
if (msg->hdr_size == NULL) {
msg->hdr_size = p_new(msg->pool,
MessageSize, 1);
}
message_parse_header(NULL, cache->open_inbuf,
msg->hdr_size,
parse_envelope_header,
msg);
}
value = imap_envelope_get_part_data(msg->envelope);
}
msg->cached_envelope = p_strdup(msg->pool, value);
failed = value == NULL;
}
if ((fields & IMAP_CACHE_VIRTUAL_SIZE) &&
msg->full_virtual_size == (uoff_t)-1) {
fields |= IMAP_CACHE_MESSAGE_HDR_SIZE |
IMAP_CACHE_MESSAGE_BODY_SIZE;
}
if ((fields & IMAP_CACHE_MESSAGE_BODY_SIZE) && msg->body_size == NULL) {
/* we don't have body size. and since we're already going
to scan the whole message body, we might as well build
the MessagePart. FIXME: this slows down things when it's
not needed, do we really want to? */
fields |= IMAP_CACHE_MESSAGE_PART;
}
if (fields & IMAP_CACHE_MESSAGE_PART) {
msg_get_part(cache);
if (msg->part == NULL && imap_msgcache_get_inbuf(cache, 0)) {
/* we need to parse the message */
MessageHeaderFunc func;
if ((fields & IMAP_CACHE_ENVELOPE) &&
msg->cached_envelope == NULL) {
/* we need envelope too, fill the info
while parsing headers */
func = parse_envelope_header;
} else {
func = NULL;
}
msg->part = message_parse(msg->pool, cache->open_inbuf,
func, msg);
} else {
failed = TRUE;
}
}
if ((fields & IMAP_CACHE_MESSAGE_BODY_SIZE) && msg->body_size == NULL) {
i_assert(msg->part != NULL);
msg->body_size = p_new(msg->pool, MessageSize, 1);
if (msg->hdr_size == NULL)
msg->hdr_size = p_new(msg->pool, MessageSize, 1);
*msg->hdr_size = msg->part->header_size;
*msg->body_size = msg->part->body_size;
}
if ((fields & IMAP_CACHE_MESSAGE_HDR_SIZE) && msg->hdr_size == NULL) {
msg_get_part(cache);
msg->hdr_size = p_new(msg->pool, MessageSize, 1);
if (msg->part != NULL) {
/* easy, get it from root part */
*msg->hdr_size = msg->part->header_size;
if (msg->body_size == NULL) {
msg->body_size = p_new(msg->pool,
MessageSize, 1);
*msg->body_size = msg->part->body_size;
}
} else {
/* need to do some light parsing */
if (imap_msgcache_get_inbuf(cache, 0)) {
message_get_header_size(cache->open_inbuf,
msg->hdr_size);
} else {
failed = TRUE;
}
}
}
if ((fields & IMAP_CACHE_VIRTUAL_SIZE) &&
msg->full_virtual_size == (uoff_t)-1) {
if (msg->hdr_size == NULL || msg->body_size == NULL)
failed = TRUE;
else {
msg->full_virtual_size = msg->hdr_size->virtual_size +
msg->body_size->virtual_size;
}
}
if (fields & IMAP_CACHE_MESSAGE_OPEN) {
/* this isn't needed for anything else than pre-opening the
mail and seeing if it fails. */
failed = !imap_msgcache_get_inbuf(cache, 0);
}
if ((fields & IMAP_CACHE_INTERNALDATE) &&
msg->internal_date == (time_t)-1) {
/* keep this last, since we may get it when mail file is
opened. */
msg->internal_date =
cache->iface->get_internal_date(cache->context);
failed = msg->internal_date == (time_t)-1;
}
t_pop();
return !failed;
}
int imap_msgcache_open(ImapMessageCache *cache, unsigned int uid,
ImapCacheField fields,
uoff_t vp_header_size, uoff_t vp_body_size,
uoff_t full_virtual_size, void *context)
{
CachedMessage *msg;
msg = cache_open_or_create(cache, uid);
if (cache->open_msg != msg) {
imap_msgcache_close(cache);
cache->open_msg = msg;
cache->context = context;
}
if (vp_header_size != (uoff_t)-1 && msg->hdr_size == NULL) {
/* physical size == virtual size */
msg->hdr_size = p_new(msg->pool, MessageSize, 1);
msg->hdr_size->physical_size = msg->hdr_size->virtual_size =
vp_header_size;
}
if (vp_body_size != (uoff_t)-1 && msg->body_size == NULL) {
/* physical size == virtual size */
msg->body_size = p_new(msg->pool, MessageSize, 1);
msg->body_size->physical_size = msg->body_size->virtual_size =
vp_body_size;
}
msg->full_virtual_size = full_virtual_size;
return cache_fields(cache, fields);
}
void imap_msgcache_close(ImapMessageCache *cache)
{
if (cache->open_inbuf != NULL) {
i_buffer_unref(cache->open_inbuf);
cache->open_inbuf = NULL;
}
cache->open_msg = NULL;
cache->context = NULL;
}
const char *imap_msgcache_get(ImapMessageCache *cache, ImapCacheField field)
{
CachedMessage *msg;
i_assert(cache->open_msg != NULL);
msg = cache->open_msg;
switch (field) {
case IMAP_CACHE_BODY:
if (msg->cached_body == NULL)
cache_fields(cache, field);
return msg->cached_body;
case IMAP_CACHE_BODYSTRUCTURE:
if (msg->cached_bodystructure == NULL)
cache_fields(cache, field);
return msg->cached_bodystructure;
case IMAP_CACHE_ENVELOPE:
if (msg->cached_envelope == NULL)
cache_fields(cache, field);
return msg->cached_envelope;
default:
i_unreached();
}
return NULL;
}
MessagePart *imap_msgcache_get_parts(ImapMessageCache *cache)
{
if (cache->open_msg->part == NULL)
cache_fields(cache, IMAP_CACHE_MESSAGE_PART);
return cache->open_msg->part;
}
uoff_t imap_msgcache_get_virtual_size(ImapMessageCache *cache)
{
if (cache->open_msg->full_virtual_size == (uoff_t)-1)
cache_fields(cache, IMAP_CACHE_VIRTUAL_SIZE);
return cache->open_msg->full_virtual_size;
}
time_t imap_msgcache_get_internal_date(ImapMessageCache *cache)
{
if (cache->open_msg->internal_date == (time_t)-1)
cache_fields(cache, IMAP_CACHE_INTERNALDATE);
return cache->open_msg->internal_date;
}
int imap_msgcache_get_rfc822(ImapMessageCache *cache, IBuffer **inbuf,
MessageSize *hdr_size, MessageSize *body_size)
{
CachedMessage *msg;
uoff_t offset;
i_assert(cache->open_msg != NULL);
msg = cache->open_msg;
if (inbuf != NULL) {
if (msg->hdr_size == NULL)
cache_fields(cache, IMAP_CACHE_MESSAGE_HDR_SIZE);
offset = hdr_size != NULL ? 0 :
msg->hdr_size->physical_size;
if (!imap_msgcache_get_inbuf(cache, offset))
return FALSE;
*inbuf = cache->open_inbuf;
}
if (body_size != NULL) {
if (msg->body_size == NULL)
cache_fields(cache, IMAP_CACHE_MESSAGE_BODY_SIZE);
if (msg->body_size == NULL)
return FALSE;
*body_size = *msg->body_size;
}
if (hdr_size != NULL) {
if (msg->hdr_size == NULL)
cache_fields(cache, IMAP_CACHE_MESSAGE_HDR_SIZE);
if (msg->hdr_size == NULL)
return FALSE;
*hdr_size = *msg->hdr_size;
}
return TRUE;
}
static void get_partial_size(IBuffer *inbuf,
uoff_t virtual_skip, uoff_t max_virtual_size,
MessageSize *partial, MessageSize *dest,
int *cr_skipped)
{
/* see if we can use the existing partial */
if (partial->virtual_size > virtual_skip)
memset(partial, 0, sizeof(MessageSize));
else {
i_buffer_skip(inbuf, partial->physical_size);
virtual_skip -= partial->virtual_size;
}
message_skip_virtual(inbuf, virtual_skip, partial, cr_skipped);
message_get_body_size(inbuf, dest, max_virtual_size);
if (*cr_skipped) {
dest->virtual_size--;
partial->virtual_size--;
}
}
int imap_msgcache_get_rfc822_partial(ImapMessageCache *cache,
uoff_t virtual_skip,
uoff_t max_virtual_size,
int get_header, MessageSize *size,
IBuffer **inbuf, int *cr_skipped)
{
CachedMessage *msg;
uoff_t physical_skip, full_size;
int size_got;
i_assert(cache->open_msg != NULL);
memset(size, 0, sizeof(MessageSize));
*inbuf = NULL;
*cr_skipped = FALSE;
msg = cache->open_msg;
if (msg->hdr_size == NULL) {
cache_fields(cache, IMAP_CACHE_MESSAGE_HDR_SIZE);
if (msg->hdr_size == NULL)
return FALSE;
}
/* see if we can do this easily */
size_got = FALSE;
if (virtual_skip == 0) {
if (msg->body_size == NULL) {
cache_fields(cache, IMAP_CACHE_MESSAGE_BODY_SIZE);
if (msg->body_size == NULL)
return FALSE;
}
full_size = msg->body_size->virtual_size;
if (get_header)
full_size += msg->hdr_size->virtual_size;
if (max_virtual_size >= full_size) {
memcpy(size, msg->body_size, sizeof(MessageSize));
if (get_header)
message_size_add(size, msg->hdr_size);
size_got = TRUE;
}
}
if (size_got) {
physical_skip = get_header ? 0 : msg->hdr_size->physical_size;
} else {
if (!imap_msgcache_get_inbuf(cache, 0))
return FALSE;
if (msg->partial_size == NULL)
msg->partial_size = p_new(msg->pool, MessageSize, 1);
if (!get_header)
virtual_skip += msg->hdr_size->virtual_size;
get_partial_size(cache->open_inbuf, virtual_skip,
max_virtual_size, msg->partial_size, size,
cr_skipped);
physical_skip = msg->partial_size->physical_size;
}
/* seek to wanted position */
if (!imap_msgcache_get_inbuf(cache, physical_skip))
return FALSE;
*inbuf = cache->open_inbuf;
return TRUE;
}
int imap_msgcache_get_data(ImapMessageCache *cache, IBuffer **inbuf)
{
i_assert(cache->open_msg != NULL);
if (!imap_msgcache_get_inbuf(cache, 0))
return FALSE;
*inbuf = cache->open_inbuf;
return TRUE;
}