Mercurial > dovecot > original-hg > dovecot-1.2
view src/lib-imap/imap-message-cache.c @ 363:567e932cdc66 HEAD
Added autoclose_fd-flag for io_buffer_create_file() and
io_buffer_create_mmap().
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Sun, 06 Oct 2002 06:09:36 +0300 |
parents | cdd72de7214b |
children | ea958a5b9de1 |
line wrap: on
line source
/* Copyright (C) 2002 Timo Sirainen */ #include "lib.h" #include "iobuffer.h" #include "temp-string.h" #include "mmap-util.h" #include "message-parser.h" #include "message-part-serialize.h" #include "message-size.h" #include "imap-bodystructure.h" #include "imap-envelope.h" #include "imap-message-cache.h" #include <unistd.h> /* It's not very useful to cache lots of messages, as they're mostly wanted just once. The biggest reason for this cache to exist is to get just the latest message. */ #define MAX_CACHED_MESSAGES 16 #define DEFAULT_MESSAGE_POOL_SIZE 4096 typedef struct _CachedMessage CachedMessage; struct _CachedMessage { CachedMessage *next; Pool pool; unsigned int uid; MessagePart *part; MessageSize *hdr_size; MessageSize *body_size; MessageSize *partial_size; char *cached_body; char *cached_bodystructure; char *cached_envelope; MessagePartEnvelopeData *envelope; }; struct _ImapMessageCache { ImapMessageCacheIface *iface; CachedMessage *messages; int messages_count; CachedMessage *open_msg; IOBuffer *open_inbuf; void *context; }; ImapMessageCache *imap_msgcache_alloc(ImapMessageCacheIface *iface) { ImapMessageCache *cache; cache = i_new(ImapMessageCache, 1); cache->iface = iface; return cache; } static void cached_message_free(CachedMessage *msg) { pool_unref(msg->pool); } void imap_msgcache_clear(ImapMessageCache *cache) { CachedMessage *next; imap_msgcache_close(cache); while (cache->messages != NULL) { next = cache->messages->next; cached_message_free(cache->messages); cache->messages = next; } } void imap_msgcache_free(ImapMessageCache *cache) { imap_msgcache_clear(cache); i_free(cache); } static CachedMessage *cache_new(ImapMessageCache *cache, unsigned int uid) { CachedMessage *msg, **msgp; Pool pool; if (cache->messages_count < MAX_CACHED_MESSAGES) cache->messages_count++; else { /* remove the last message from cache */ msgp = &cache->messages; while ((*msgp)->next != NULL) msgp = &(*msgp)->next; cached_message_free(*msgp); *msgp = NULL; } pool = pool_create("CachedMessage", DEFAULT_MESSAGE_POOL_SIZE, FALSE); msg = p_new(pool, CachedMessage, 1); msg->pool = pool; msg->uid = uid; msg->next = cache->messages; cache->messages = msg; return msg; } static CachedMessage *cache_open_or_create(ImapMessageCache *cache, unsigned int uid) { CachedMessage **pos, *msg; pos = &cache->messages; for (; *pos != NULL; pos = &(*pos)->next) { if ((*pos)->uid == uid) break; } if (*pos == NULL) { /* not found, add it */ msg = cache_new(cache, uid); } else if (*pos != cache->messages) { /* move it to first in list */ msg = *pos; *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->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->offset); io_buffer_skip(cache->open_inbuf, offset - cache->open_inbuf->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 void cache_fields(ImapMessageCache *cache, ImapCacheField fields) { CachedMessage *msg; const char *value; msg = cache->open_msg; t_push(); if ((fields & IMAP_CACHE_BODY) && msg->cached_body == NULL) { value = cache->iface->get_cached_field(IMAP_CACHE_BODY, 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, FALSE); } msg->cached_body = p_strdup(msg->pool, value); } 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); } 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); } if (msg->envelope == NULL) { imap_envelope_parse_header(msg->pool, &msg->envelope, "", "", 0); } value = imap_envelope_get_part_data(msg->envelope); } if (value != NULL) msg->cached_envelope = p_strdup(msg->pool, value); } 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. */ 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); } } 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; } else { /* need to do some light parsing */ if (imap_msgcache_get_inbuf(cache, 0)) { message_get_header_size(cache->open_inbuf, msg->hdr_size); } } } t_pop(); } void imap_msgcache_open(ImapMessageCache *cache, unsigned int uid, ImapCacheField fields, uoff_t virtual_header_size, uoff_t virtual_body_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 (virtual_header_size != 0 && 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 = virtual_header_size; } if (virtual_body_size != 0 && 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 = virtual_body_size; } cache_fields(cache, fields); } void imap_msgcache_close(ImapMessageCache *cache) { if (cache->open_inbuf != NULL) { io_buffer_destroy(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_assert(0); } return NULL; } MessagePart *imap_msgcache_get_parts(ImapMessageCache *cache) { i_assert(cache->open_msg != NULL); if (cache->open_msg->part == NULL) cache_fields(cache, IMAP_CACHE_MESSAGE_PART); return cache->open_msg->part; } int imap_msgcache_get_rfc822(ImapMessageCache *cache, IOBuffer **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) { 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(IOBuffer *inbuf, uoff_t virtual_skip, uoff_t max_virtual_size, MessageSize *partial, MessageSize *dest) { unsigned char *msg; size_t size; int cr_skipped; /* see if we can use the existing partial */ if (partial->virtual_size > virtual_skip) memset(partial, 0, sizeof(MessageSize)); else { io_buffer_skip(inbuf, partial->physical_size); virtual_skip -= partial->virtual_size; } message_skip_virtual(inbuf, virtual_skip, partial, &cr_skipped); if (!cr_skipped) { /* see if we need to add virtual CR */ if (io_buffer_read_data_blocking(inbuf, &msg, &size, 0) > 0) { if (msg[0] == '\n') dest->virtual_size++; } } message_get_body_size(inbuf, dest, max_virtual_size); } int imap_msgcache_get_rfc822_partial(ImapMessageCache *cache, uoff_t virtual_skip, uoff_t max_virtual_size, int get_header, MessageSize *size, IOBuffer **inbuf) { CachedMessage *msg; uoff_t physical_skip; int size_got; i_assert(cache->open_msg != NULL); *inbuf = NULL; msg = cache->open_msg; if (msg->hdr_size == NULL) { if (!imap_msgcache_get_inbuf(cache, 0)) return FALSE; msg->hdr_size = p_new(msg->pool, MessageSize, 1); message_get_header_size(cache->open_inbuf, msg->hdr_size); } physical_skip = get_header ? 0 : msg->hdr_size->physical_size; /* 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; } if (max_virtual_size >= msg->body_size->virtual_size) { *size = *msg->body_size; size_got = TRUE; } } if (!size_got) { if (!imap_msgcache_get_inbuf(cache, msg->hdr_size->physical_size)) return FALSE; if (msg->partial_size == NULL) msg->partial_size = p_new(msg->pool, MessageSize, 1); get_partial_size(cache->open_inbuf, virtual_skip, max_virtual_size, msg->partial_size, size); physical_skip += msg->partial_size->physical_size; } if (get_header) message_size_add(size, msg->hdr_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, IOBuffer **inbuf) { i_assert(cache->open_msg != NULL); if (!imap_msgcache_get_inbuf(cache, 0)) return FALSE; *inbuf = cache->open_inbuf; return TRUE; }