]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Header caching redesigned. New design allows caching decisions per field, so
authorTimo Sirainen <tss@iki.fi>
Sun, 18 Jul 2004 02:25:06 +0000 (05:25 +0300)
committerTimo Sirainen <tss@iki.fi>
Sun, 18 Jul 2004 02:25:06 +0000 (05:25 +0300)
they can be divided to temporary/permanent. Cached headers are now always
returned in original order, old code didn't guarantee it. Some other caching
changes. (still missing code to store changes in caching decisions)

--HG--
branch : HEAD

31 files changed:
src/imap/imap-fetch-body-section.c
src/imap/imap-fetch.c
src/imap/imap-fetch.h
src/imap/imap-sort.c
src/imap/imap-thread.c
src/lib-imap/imap-bodystructure.c
src/lib-index/Makefile.am
src/lib-index/mail-cache-compress.c
src/lib-index/mail-cache-decisions.c
src/lib-index/mail-cache-fields.c [new file with mode: 0644]
src/lib-index/mail-cache-lookup.c
src/lib-index/mail-cache-private.h
src/lib-index/mail-cache-transaction.c
src/lib-index/mail-cache.c
src/lib-index/mail-cache.h
src/lib-mail/istream-header-filter.c
src/lib-mail/istream-header-filter.h
src/lib-storage/index/index-mail-headers.c
src/lib-storage/index/index-mail.c
src/lib-storage/index/index-mail.h
src/lib-storage/index/index-search.c
src/lib-storage/index/index-storage.c
src/lib-storage/index/index-storage.h
src/lib-storage/index/maildir/maildir-storage.c
src/lib-storage/index/mbox/mbox-mail.c
src/lib-storage/index/mbox/mbox-storage.c
src/lib-storage/mail-storage-private.h
src/lib-storage/mail-storage.c
src/lib-storage/mail-storage.h
src/lib-storage/proxy-mail.c
src/lib-storage/proxy-mailbox.c

index e96afa059adb3af30a42b96b5aef592589009f6a..b49e661855f10a3beb9b3533a77b126bf7b39ab4 100644 (file)
@@ -254,7 +254,7 @@ static int fetch_header_append(struct fetch_header_field_context *ctx,
 
        if (ctx->dest != NULL)
                buffer_append(ctx->dest, str, size);
-       if (ctx->output != NULL) {
+       else {
                if (o_stream_send(ctx->output, str, size) < 0)
                        return FALSE;
        }
@@ -273,7 +273,9 @@ static int fetch_header_fields(struct istream *input, const char *section,
                ctx->match_func = header_match;
 
                if (ctx->fetch_ctx->body_fetch_from_cache) {
-                       input = ctx->mail->get_headers(ctx->mail, ctx->fields);
+                       input = ctx->mail->
+                               get_headers(ctx->mail,
+                                           ctx->fetch_ctx->headers_ctx);
                        if (input == NULL)
                                return FALSE;
                }
index dc5fbc9e239bbbe96d03fc0625ca9be2b670995c..eb42786031593502ae2ca7ae79bca2982b4e0e2d 100644 (file)
@@ -347,7 +347,7 @@ int imap_fetch(struct client *client,
        struct mail *mail;
        struct imap_fetch_body_data *body;
        const char *null = NULL;
-       const char *const *wanted_headers, *const *arr;
+       const char *const *arr;
        buffer_t *buffer;
 
        memset(&ctx, 0, sizeof(ctx));
@@ -375,27 +375,33 @@ int imap_fetch(struct client *client,
        /* If we have only BODY[HEADER.FIELDS (...)] fetches, get them
           separately rather than parsing the full header so mail storage
           can try to cache them. */
-       ctx.body_fetch_from_cache = TRUE;
-       buffer = buffer_create_dynamic(pool_datastack_create(), 64, (size_t)-1);
-       for (body = bodies; body != NULL; body = body->next) {
-               if (strncmp(body->section, "HEADER.FIELDS ", 14) != 0) {
-                        ctx.body_fetch_from_cache = FALSE;
-                       break;
-               }
+       ctx.body_fetch_from_cache = (imap_data & (IMAP_FETCH_RFC822 |
+                                                 IMAP_FETCH_RFC822_HEADER |
+                                                 IMAP_FETCH_RFC822_TEXT)) == 0;
+       if (ctx.body_fetch_from_cache) {
+               buffer = buffer_create_dynamic(pool_datastack_create(),
+                                              64, (size_t)-1);
+               for (body = bodies; body != NULL; body = body->next) {
+                       if (strncmp(body->section, "HEADER.FIELDS ", 14) != 0) {
+                               ctx.body_fetch_from_cache = FALSE;
+                               break;
+                       }
 
-               arr = imap_fetch_get_body_fields(body->section + 14);
-               while (*arr != NULL) {
-                       buffer_append(buffer, arr, sizeof(*arr));
-                       arr++;
+                       arr = imap_fetch_get_body_fields(body->section + 14);
+                       while (*arr != NULL) {
+                               buffer_append(buffer, arr, sizeof(*arr));
+                               arr++;
+                       }
                }
+               buffer_append(buffer, &null, sizeof(null));
+               ctx.headers_ctx = !ctx.body_fetch_from_cache ? NULL :
+                       mailbox_header_lookup_init(box, buffer_get_data(buffer,
+                                                                       NULL));
        }
-       buffer_append(buffer, &null, sizeof(null));
-       wanted_headers = !ctx.body_fetch_from_cache ? NULL :
-               buffer_get_data(buffer, NULL);
 
        t = mailbox_transaction_begin(box, TRUE);
        ctx.search_ctx = mailbox_search_init(t, NULL, search_args, NULL,
-                                            fetch_data, wanted_headers);
+                                            fetch_data, ctx.headers_ctx);
        if (ctx.search_ctx == NULL)
                ctx.failed = TRUE;
        else {
@@ -411,6 +417,8 @@ int imap_fetch(struct client *client,
                if (mailbox_search_deinit(ctx.search_ctx) < 0)
                        ctx.failed = TRUE;
        }
+       if (ctx.headers_ctx != NULL)
+               mailbox_header_lookup_deinit(ctx.headers_ctx);
 
        if (ctx.failed)
                mailbox_transaction_rollback(t);
index 8232cebb8f91350afe7f0e489e6fa891c413003f..f85f48327b34413b4da53dae242607ada8794882 100644 (file)
@@ -24,6 +24,7 @@ struct imap_fetch_context {
        enum mail_fetch_field fetch_data;
        enum imap_fetch_field imap_data;
        struct imap_fetch_body_data *bodies;
+       struct mailbox_header_lookup_ctx *headers_ctx;
 
        string_t *str;
        struct ostream *output;
index f2be5570ecfbc724f861c45c18361271bbbd17ce..f96f4ef6fb4dd893e3ad76d9cd301846231289c2 100644 (file)
@@ -194,6 +194,7 @@ int imap_sort(struct client *client, const char *charset,
        enum mail_sort_type norm_prog[MAX_SORT_PROGRAM_SIZE];
         enum mail_fetch_field wanted_fields;
        const char *wanted_headers[MAX_WANTED_HEADERS];
+       struct mailbox_header_lookup_ctx *headers_ctx;
        struct sort_context *ctx;
        struct mail *mail;
        buffer_t *buf;
@@ -222,14 +223,17 @@ int imap_sort(struct client *client, const char *charset,
 
        memset(wanted_headers, 0, sizeof(wanted_headers));
        wanted_fields = init_sort_elements(ctx, wanted_headers);
+       headers_ctx = mailbox_header_lookup_init(client->mailbox,
+                                                wanted_headers);
 
        /* initialize searching */
        ctx->t = mailbox_transaction_begin(client->mailbox, FALSE);
        ctx->search_ctx =
                mailbox_search_init(ctx->t, charset, args, norm_prog,
-                                   wanted_fields, wanted_headers);
+                                   wanted_fields, headers_ctx);
        if (ctx->search_ctx == NULL) {
                mailbox_transaction_rollback(ctx->t);
+               mailbox_header_lookup_deinit(headers_ctx);
                return -1;
        }
 
@@ -259,6 +263,7 @@ int imap_sort(struct client *client, const char *charset,
                              str_len(ctx->str));
        }
 
+       mailbox_header_lookup_deinit(headers_ctx);
         mail_sort_deinit(ctx);
        return ret;
 }
index 1ab9d9fa4db221fb021fae72d16b9732a5af7639..508d34fb43ec4f4c130c609b8cb5ce2a018d5552 100644 (file)
@@ -107,6 +107,7 @@ int imap_thread(struct client *client, const char *charset,
                "message-id", "in-reply-to", "references", "subject",
                NULL
        };
+       struct mailbox_header_lookup_ctx *headers_ctx;
        struct thread_context *ctx;
        struct mail *mail;
        int ret;
@@ -115,14 +116,17 @@ int imap_thread(struct client *client, const char *charset,
                i_fatal("Only REFERENCES threading supported");
 
        ctx = t_new(struct thread_context, 1);
+       headers_ctx = mailbox_header_lookup_init(client->mailbox,
+                                                wanted_headers);
 
        /* initialize searching */
        ctx->t = mailbox_transaction_begin(client->mailbox, FALSE);
        ctx->search_ctx =
                mailbox_search_init(ctx->t, charset, args, NULL,
-                                   MAIL_FETCH_DATE, wanted_headers);
+                                   MAIL_FETCH_DATE, headers_ctx);
        if (ctx->search_ctx == NULL) {
                mailbox_transaction_rollback(ctx->t);
+               mailbox_header_lookup_deinit(headers_ctx);
                return -1;
        }
 
@@ -149,6 +153,8 @@ int imap_thread(struct client *client, const char *charset,
        ret = mailbox_search_deinit(ctx->search_ctx);
        if (mailbox_transaction_commit(ctx->t) < 0)
                ret = -1;
+
+       mailbox_header_lookup_deinit(headers_ctx);
         mail_thread_deinit(ctx);
        return ret;
 }
index 502df5eb40493c02deb21adc8156489a5d02ecbb..dd95fae43118c919e512299f1f0836fe941902a7 100644 (file)
@@ -350,24 +350,27 @@ static void part_write_body(struct message_part *part,
                            string_t *str, int extended)
 {
        struct message_part_body_data *data = part->context;
+       int text;
 
        if (data == NULL) {
                /* there was no content headers, use an empty structure */
                data = t_new(struct message_part_body_data, 1);
        }
 
-       if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822)
+       if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) {
                str_append(str, "\"message\" \"rfc822\"");
-       else {
+               text = FALSE;
+       } else {
                /* "content type" "subtype" */
+               text = data->content_type == NULL ||
+                       strcasecmp(data->content_type, "\"text\"") == 0;
                str_append(str, NVL(data->content_type, "\"text\""));
                str_append_c(str, ' ');
 
                if (data->content_subtype != NULL)
                        str_append(str, data->content_subtype);
                else {
-                       if (data->content_type == NULL ||
-                           strcasecmp(data->content_type, "\"text\"") == 0)
+                       if (text)
                                str_append(str, "\"plain\"");
                        else
                                str_append(str, "\"unknown\"");
@@ -378,8 +381,7 @@ static void part_write_body(struct message_part *part,
        /* ("content type param key" "value" ...) */
        str_append_c(str, ' ');
        if (data->content_type_params == NULL) {
-               if (data->content_type != NULL &&
-                   strncasecmp(data->content_type, "\"text\"", 6) != 0)
+               if (!text)
                        str_append(str, "NIL");
                else
                        str_append(str, "("DEFAULT_CHARSET")");
@@ -395,7 +397,7 @@ static void part_write_body(struct message_part *part,
                    NVL(data->content_transfer_encoding, "\"7bit\""),
                    part->body_size.virtual_size);
 
-       if (part->flags & MESSAGE_PART_FLAG_TEXT) {
+       if (text) {
                /* text/.. contains line count */
                str_printfa(str, " %u", part->body_size.lines);
        } else if (part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) {
index 99ece0fad1baa52fad028dd441607860bf2b0453..04399ee00b93658c3b0c98818edb2c33e592929e 100644 (file)
@@ -8,6 +8,7 @@ libindex_a_SOURCES = \
        mail-cache.c \
        mail-cache-compress.c \
        mail-cache-decisions.c \
+       mail-cache-fields.c \
        mail-cache-lookup.c \
        mail-cache-transaction.c \
         mail-index.c \
index 30e419b559f8bec21856070fb03eeb0fb27f979f..071c72e9c8757bb56ac38f96d9423965054d0457 100644 (file)
@@ -10,49 +10,40 @@ static unsigned char null4[4] = { 0, 0, 0, 0 };
 
 struct mail_cache_copy_context {
        int new_msg;
-       char field_seen[32], keep_fields[32], temp_fields[32];
-       buffer_t *buffer, *header;
+       buffer_t *buffer, *field_seen;
+       uint8_t field_seen_value;
 };
 
 static int
-mail_cache_compress_callback(struct mail_cache_view *view __attr_unused__,
-                            enum mail_cache_field field,
+mail_cache_compress_callback(struct mail_cache_view *view, uint32_t file_field,
                             const void *data, size_t data_size, void *context)
 {
-        struct mail_cache_copy_context *ctx = context;
+       struct mail_cache_copy_context *ctx = context;
+       enum mail_cache_decision_type dec;
+       unsigned int field;
+       uint8_t *field_seen;
        uint32_t size32;
-       int i;
 
+       field_seen = buffer_get_space_unsafe(ctx->field_seen, file_field, 1);
+       if (*field_seen == ctx->field_seen_value) {
+               /* duplicate */
+               return 1;
+       }
+       *field_seen = ctx->field_seen_value;
+
+       field = view->cache->file_field_map[file_field];
+       dec = view->cache->fields[field].decision & ~MAIL_CACHE_DECISION_FORCED;
        if (ctx->new_msg) {
-               if (!ctx->temp_fields[field])
+               if (dec == MAIL_CACHE_DECISION_NO)
                        return 1;
        } else {
-               if (!ctx->keep_fields[field])
-                       return 1;
-       }
-
-       if (ctx->field_seen[field]) {
-               /* drop duplicates */
-               return 1;
-       }
-       ctx->field_seen[field] = TRUE;
-
-       for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
-               if (mail_cache_header_fields[i] == field) {
-                       /* it's header - save it into header field */
-                       size32 = buffer_get_used_size(ctx->header);
-                       if (size32 > 0) {
-                               /* remove old terminating \0 */
-                               buffer_set_used_size(ctx->header, size32-1);
-                       }
-                       buffer_append(ctx->header, data, data_size);
+               if (dec != MAIL_CACHE_DECISION_YES)
                        return 1;
-               }
        }
 
-       buffer_append(ctx->buffer, &field, sizeof(field));
+       buffer_append(ctx->buffer, &file_field, sizeof(file_field));
 
-       if (mail_cache_field_sizes[field] == (unsigned int)-1) {
+       if (view->cache->fields[field].field_size == (unsigned int)-1) {
                size32 = (uint32_t)data_size;
                buffer_append(ctx->buffer, &size32, sizeof(size32));
        }
@@ -72,12 +63,11 @@ mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
        const struct mail_index_header *idx_hdr;
        struct mail_cache_header hdr;
        struct mail_cache_record cache_rec;
-        enum mail_cache_field field;
        struct ostream *output;
-       const char *str;
-       uint32_t size32, message_count, seq, first_new_seq, old_offset;
+       buffer_t *buffer;
+       size_t size;
+       uint32_t message_count, seq, first_new_seq, old_offset;
        uoff_t offset;
-       int i, ret, header_idx;
 
        /* get sequence of first message which doesn't need it's temp fields
           removed. */
@@ -104,85 +94,44 @@ mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
        hdr.indexid = idx_hdr->indexid;
        hdr.file_seq = idx_hdr->cache_file_seq + 1;
 
-       if (cache->hdr != NULL) {
-               memcpy(hdr.field_usage_decision_type,
-                      cache->hdr->field_usage_decision_type,
-                      sizeof(hdr.field_usage_decision_type));
-               memcpy(hdr.field_usage_last_used,
-                      cache->hdr->field_usage_last_used,
-                      sizeof(hdr.field_usage_last_used));
-       } else {
-               memcpy(hdr.field_usage_decision_type,
-                      cache->default_field_usage_decision_type,
-                      sizeof(hdr.field_usage_decision_type));
-       }
-
-       memset(&ctx, 0, sizeof(ctx));
-       ctx.buffer = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
-       ctx.header = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
-
-       for (i = 0; i < 32; i++) {
-               if (hdr.field_usage_decision_type[i] & MAIL_CACHE_DECISION_YES)
-                       ctx.keep_fields[i] = TRUE;
-               else if (hdr.field_usage_decision_type[i] &
-                        MAIL_CACHE_DECISION_TEMP) {
-                       ctx.temp_fields[i] = TRUE;
-                       ctx.keep_fields[i] = TRUE;
-               }
+       if (cache->fields_count != 0) {
+               hdr.field_header_offset =
+                       mail_cache_uint32_to_offset(sizeof(hdr));
        }
-
        o_stream_send(output, &hdr, sizeof(hdr));
 
-       /* merge all the header pieces into one. if some message doesn't have
-          all the required pieces, we'll just have to drop them all. */
-       for (i = MAIL_CACHE_HEADERS_COUNT-1; i >= 0; i--) {
-               str = mail_cache_get_header_fields_str(cache, i);
-               if (str != NULL)
-                       break;
+       if (cache->fields_count != 0) {
+               t_push();
+               buffer = buffer_create_dynamic(pool_datastack_create(),
+                                              256, (size_t)-1);
+               mail_cache_header_fields_get(cache, buffer);
+               o_stream_send(output, buffer_get_data(buffer, NULL),
+                             buffer_get_used_size(buffer));
+               t_pop();
        }
 
-       if (str == NULL)
-               header_idx = -1;
-       else {
-               hdr.header_offsets[0] =
-                       mail_cache_uint32_to_offset(output->offset);
-               header_idx = i;
-
-               size32 = strlen(str) + 1;
-               o_stream_send(output, &size32, sizeof(size32));
-               o_stream_send(output, str, size32);
-               if ((size32 & 3) != 0)
-                       o_stream_send(output, null4, 4 - (size32 & 3));
-       }
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.buffer = buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+       ctx.field_seen = buffer_create_dynamic(default_pool, 64, (size_t)-1);
+       ctx.field_seen_value = 0;
 
        mail_index_reset_cache(t, hdr.file_seq);
 
-       ret = 0;
        for (seq = 1; seq <= message_count; seq++) {
                ctx.new_msg = seq >= first_new_seq;
                buffer_set_used_size(ctx.buffer, 0);
-               buffer_set_used_size(ctx.header, 0);
-               memset(ctx.field_seen, 0, sizeof(ctx.field_seen));
+
+               if (++ctx.field_seen_value == 0) {
+                       memset(buffer_get_modifyable_data(ctx.field_seen, NULL),
+                              0, buffer_get_size(ctx.field_seen));
+                       ctx.field_seen_value++;
+               }
 
                memset(&cache_rec, 0, sizeof(cache_rec));
                buffer_append(ctx.buffer, &cache_rec, sizeof(cache_rec));
 
-               mail_cache_foreach(cache_view, seq,
-                                  mail_cache_compress_callback, &ctx);
-
-               size32 = buffer_get_used_size(ctx.header);
-               if (size32 > 0 && ctx.field_seen[header_idx]) {
-                       field = MAIL_CACHE_HEADERS1;
-                       buffer_append(ctx.buffer, &field, sizeof(field));
-                       buffer_append(ctx.buffer, &size32, sizeof(size32));
-                       buffer_append(ctx.buffer,
-                                     buffer_get_data(ctx.header, NULL),
-                                     size32);
-                       if ((size32 & 3) != 0) {
-                               buffer_append(ctx.buffer, null4,
-                                             4 - (size32 & 3));
-                       }
-               }
+               (void)mail_cache_foreach(cache_view, seq,
+                                        mail_cache_compress_callback, &ctx);
 
                cache_rec.size = buffer_get_used_size(ctx.buffer);
                if (cache_rec.size == sizeof(cache_rec))
@@ -197,7 +146,6 @@ mail_cache_copy(struct mail_cache *cache, struct mail_index_view *view, int fd)
        }
        hdr.used_file_size = output->offset;
        buffer_free(ctx.buffer);
-       buffer_free(ctx.header);
 
        o_stream_seek(output, 0);
        o_stream_send(output, &hdr, sizeof(hdr));
@@ -236,6 +184,12 @@ int mail_cache_compress(struct mail_cache *cache, struct mail_index_view *view)
                return -1;
        locked = ret > 0;
 
+       /* get the latest info on fields */
+       if (mail_cache_header_fields_read(cache) < 0) {
+               if (locked) mail_cache_unlock(cache);
+               return -1;
+       }
+
 #ifdef DEBUG
        i_warning("Compressing cache file %s", cache->filepath);
 #endif
@@ -246,6 +200,7 @@ int mail_cache_compress(struct mail_cache *cache, struct mail_index_view *view)
                               MAIL_CACHE_LOCK_IMMEDIATE_TIMEOUT, NULL, NULL);
        if (fd == -1) {
                mail_cache_set_syscall_error(cache, "file_dotlock_open()");
+               if (locked) mail_cache_unlock(cache);
                return -1;
        }
 
@@ -268,13 +223,11 @@ int mail_cache_compress(struct mail_cache *cache, struct mail_index_view *view)
 
                        if (mail_cache_map(cache, 0, 0) < 0)
                                ret = -1;
+                       else if (mail_cache_header_fields_read(cache) < 0)
+                               ret = -1;
                }
        }
 
-       /* headers could have changed, reread them */
-       memset(cache->split_offsets, 0, sizeof(cache->split_offsets));
-       memset(cache->split_headers, 0, sizeof(cache->split_headers));
-
        if (locked)
                mail_cache_unlock(cache);
 
index 73ccd506134e81ef844f9127fed67be5db2b3ee7..7e1e4921f5d1be7b1084cce145e54f97e8913c50 100644 (file)
 */
 
 #include "lib.h"
-#include "write-full.h"
+#include "ioloop.h"
 #include "mail-cache-private.h"
 
-#include <stddef.h>
-
-static void
-mail_cache_set_decision_type(struct mail_cache *cache,
-                            enum mail_cache_field field,
-                            enum mail_cache_decision_type type)
-{
-       uint8_t value = type;
-
-       /* update the header without locking, we'll just write one byte and
-          it's very unlikely someone else tries to write different value for
-          it at the same time. even then it's just a wrong decision which
-          will be corrected sometimes later, not too bad.. */
-       if (pwrite_full(cache->fd, &value, 1,
-                       offsetof(struct mail_cache_header,
-                                field_usage_decision_type) + field) < 0) {
-               mail_cache_set_syscall_error(cache, "pwrite_full()");
-       }
-}
-
 void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
-                               enum mail_cache_field field)
+                               unsigned int field)
 {
+       struct mail_cache *cache = view->cache;
        const struct mail_index_header *hdr;
        uint32_t uid;
 
-       if (view->cache->hdr->field_usage_decision_type[field] !=
-           MAIL_CACHE_DECISION_TEMP) {
+       i_assert(field < cache->fields_count);
+
+       if (cache->fields[field].decision != MAIL_CACHE_DECISION_TEMP) {
                /* a) forced decision
-                  b) not cached, mail_cache_mark_missing() will handle this
+                  b) not cached, mail_cache_decision_add() will handle this
                   c) permanently cached already, okay. */
                return;
        }
@@ -109,7 +91,13 @@ void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
            mail_index_get_header(view->view, &hdr) < 0)
                return;
 
-       if (uid < view->cache->field_usage_uid_highwater[field] ||
+       if (ioloop_time - cache->fields[field].last_used > 3600*24) {
+               /* update last_used about once a day */
+               cache->fields[field].last_used = ioloop_time;
+               cache->field_header_write_pending = TRUE;
+       }
+
+       if (uid < cache->fields[field].uid_highwater ||
            uid < hdr->day_first_uid[7]) {
                /* a) nonordered access within this session. if client doesn't
                      request messages in growing order, we assume it doesn't
@@ -118,32 +106,34 @@ void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
                      client with no local cache. if it was just a new client
                      generating the local cache for the first time, we'll
                      drop back to TEMP within few months. */
-               mail_cache_set_decision_type(view->cache, field,
-                                            MAIL_CACHE_DECISION_YES);
+               cache->fields[field].decision = MAIL_CACHE_DECISION_YES;
+               cache->field_header_write_pending = TRUE;
        } else {
-               view->cache->field_usage_uid_highwater[field] = uid;
+               cache->fields[field].uid_highwater = uid;
        }
 }
 
 void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
-                            enum mail_cache_field field)
+                            unsigned int field)
 {
+       struct mail_cache *cache = view->cache;
        uint32_t uid;
 
-       if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+       i_assert(field < cache->fields_count);
+
+       if (MAIL_CACHE_IS_UNUSABLE(cache))
                return;
 
-       if (view->cache->hdr->field_usage_decision_type[field] !=
-           MAIL_CACHE_DECISION_NO) {
+       if (cache->fields[field].decision != MAIL_CACHE_DECISION_NO) {
                /* a) forced decision
                   b) we're already caching it, so it just wasn't in cache */
                return;
        }
 
        /* field used the first time */
-       mail_cache_set_decision_type(view->cache, field,
-                                    MAIL_CACHE_DECISION_TEMP);
+       cache->fields[field].decision = MAIL_CACHE_DECISION_TEMP;
+       cache->field_header_write_pending = TRUE;
 
        if (mail_index_lookup_uid(view->view, seq, &uid) == 0)
-               view->cache->field_usage_uid_highwater[field] = uid;
+               cache->fields[field].uid_highwater = uid;
 }
diff --git a/src/lib-index/mail-cache-fields.c b/src/lib-index/mail-cache-fields.c
new file mode 100644 (file)
index 0000000..fb2960e
--- /dev/null
@@ -0,0 +1,276 @@
+/* Copyright (C) 2004 Timo Sirainen */
+
+#include "lib.h"
+#include "buffer.h"
+#include "hash.h"
+#include "mail-cache-private.h"
+
+#include <stddef.h>
+
+#define CACHE_HDR_PREFETCH 1024
+
+static const unsigned char *null4[] = { 0, 0, 0, 0 };
+
+void mail_cache_register_fields(struct mail_cache *cache,
+                               struct mail_cache_field *fields,
+                               size_t fields_count)
+{
+       void *orig_key, *orig_value;
+       unsigned int new_idx;
+       size_t i;
+
+       new_idx = cache->fields_count;
+       for (i = 0; i < fields_count; i++) {
+               if (hash_lookup_full(cache->field_name_hash, fields[i].name,
+                                    &orig_key, &orig_value)) {
+                       fields[i].idx =
+                               POINTER_CAST_TO(orig_value, unsigned int);
+                       continue;
+               }
+
+               fields[i].idx = new_idx++;
+       }
+
+       if (new_idx == cache->fields_count)
+               return;
+
+       /* @UNSAFE */
+       cache->fields = p_realloc(cache->field_pool, cache->fields,
+                                 cache->fields_count * sizeof(*cache->fields),
+                                 new_idx * sizeof(*cache->fields));
+       cache->field_file_map =
+               p_realloc(cache->field_pool, cache->field_file_map,
+                         cache->fields_count * sizeof(*cache->field_file_map),
+                         new_idx * sizeof(*cache->field_file_map));
+
+       for (i = 0; i < fields_count; i++) {
+               unsigned int idx = fields[i].idx;
+
+               if (idx < cache->fields_count)
+                       continue;
+
+               /* new index - save it */
+               cache->fields[idx] = fields[i];
+               cache->fields[idx].name =
+                       p_strdup(cache->field_pool, fields[i].name);
+               cache->field_file_map[idx] = (uint32_t)-1;
+
+               switch (cache->fields[idx].type) {
+               case MAIL_CACHE_FIELD_FIXED_SIZE:
+               case MAIL_CACHE_FIELD_BITMASK:
+                       break;
+               case MAIL_CACHE_FIELD_VARIABLE_SIZE:
+               case MAIL_CACHE_FIELD_STRING:
+               case MAIL_CACHE_FIELD_HEADER:
+                       cache->fields[idx].field_size = (unsigned int)-1;
+                       break;
+               }
+
+               hash_insert(cache->field_name_hash,
+                           (char *)cache->fields[idx].name,
+                           POINTER_CAST(idx));
+       }
+       cache->fields_count = new_idx;
+}
+
+unsigned int
+mail_cache_register_lookup(struct mail_cache *cache, const char *name)
+{
+       void *orig_key, *orig_value;
+
+       if (hash_lookup_full(cache->field_name_hash, name,
+                            &orig_key, &orig_value))
+               return POINTER_CAST_TO(orig_value, unsigned int);
+       else
+               return (unsigned int)-1;
+}
+
+static int mail_cache_header_fields_get_offset(struct mail_cache *cache,
+                                              uint32_t *offset_r)
+{
+       const struct mail_cache_header_fields *field_hdr;
+       uint32_t offset, next_offset;
+
+       if (MAIL_CACHE_IS_UNUSABLE(cache)) {
+               *offset_r = 0;
+               return 0;
+       }
+
+       /* find the latest header */
+       offset = 0;
+       next_offset =
+               mail_cache_offset_to_uint32(cache->hdr->field_header_offset);
+       while (next_offset != 0) {
+               offset = next_offset;
+
+               if (mail_cache_map(cache, offset,
+                                  sizeof(*field_hdr) + CACHE_HDR_PREFETCH) < 0)
+                       return -1;
+
+               field_hdr = CONST_PTR_OFFSET(cache->mmap_base, offset);
+               next_offset =
+                       mail_cache_offset_to_uint32(field_hdr->next_offset);
+       }
+
+       *offset_r = offset;
+       return 0;
+}
+
+int mail_cache_header_fields_read(struct mail_cache *cache)
+{
+       const struct mail_cache_header_fields *field_hdr = NULL;
+       struct mail_cache_field field;
+       const uint32_t *last_used, *sizes;
+       const uint8_t *types, *decisions;
+       const char *p, *names, *end;
+       uint32_t offset, i;
+
+       if (mail_cache_header_fields_get_offset(cache, &offset) < 0)
+               return -1;
+
+       if (offset == 0) {
+               /* no fields - the file is empty */
+               return 0;
+       }
+
+       field_hdr = CONST_PTR_OFFSET(cache->mmap_base, offset);
+       if (offset + field_hdr->size > cache->mmap_length) {
+               mail_cache_set_corrupted(cache,
+                                        "field header points outside file");
+               return -1;
+       }
+
+       /* check the fixed size of the header. name[] has to be checked
+          separately */
+       if (field_hdr->size < sizeof(*field_hdr) +
+           field_hdr->fields_count * (sizeof(uint32_t)*2 + 1 + 2)) {
+               mail_cache_set_corrupted(cache, "invalid field header size");
+               return -1;
+       }
+
+       if (field_hdr->size > sizeof(*field_hdr) + CACHE_HDR_PREFETCH) {
+               if (mail_cache_map(cache, offset, field_hdr->size) < 0)
+                       return -1;
+       }
+       field_hdr = CONST_PTR_OFFSET(cache->mmap_base, offset);
+
+       cache->file_field_map =
+               i_realloc(cache->file_field_map,
+                         cache->file_fields_count * sizeof(unsigned int),
+                         field_hdr->fields_count * sizeof(unsigned int));
+       cache->file_fields_count = field_hdr->fields_count;
+
+        last_used = MAIL_CACHE_FIELD_LAST_USED(field_hdr);
+       sizes = MAIL_CACHE_FIELD_SIZE(field_hdr);
+       types = MAIL_CACHE_FIELD_TYPE(field_hdr);
+       decisions = MAIL_CACHE_FIELD_DECISION(field_hdr);
+       names = MAIL_CACHE_FIELD_NAMES(field_hdr);
+       end = CONST_PTR_OFFSET(field_hdr, field_hdr->size);
+
+       /* clear the old mapping */
+       for (i = 0; i < cache->fields_count; i++)
+               cache->field_file_map[i] = (uint32_t)-1;
+
+       memset(&field, 0, sizeof(field));
+       for (i = 0; i < field_hdr->fields_count; i++) {
+               for (p = names; p != end && *p != '\0'; p++) ;
+               if (p == end) {
+                       mail_cache_set_corrupted(cache,
+                               "field header names corrupted");
+                       return -1;
+               }
+
+               field.name = names;
+               field.type = types[i];
+               field.field_size = sizes[i];
+               field.decision = decisions[i];
+               field.last_used = (time_t)last_used[i];
+               mail_cache_register_fields(cache, &field, 1);
+               cache->field_file_map[field.idx] = i;
+               cache->file_field_map[i] = field.idx;
+
+               names = p + 1;
+       }
+       return 0;
+}
+
+int mail_cache_header_fields_update(struct mail_cache *cache)
+{
+       int locked = cache->locked;
+
+       if (!locked) {
+               if (mail_cache_lock(cache) <= 0)
+                       return -1;
+       }
+
+       // FIXME
+
+       if (!locked)
+               mail_cache_unlock(cache);
+}
+
+#define UGLY_COPY_MACRO(field_name, type) \
+       for (i = 0; i < cache->file_fields_count; i++) {                \
+               field = cache->file_field_map[i];                       \
+               field_name = (type)cache->fields[field].field_name;     \
+               buffer_append(dest, &field_name, sizeof(field_name));   \
+       }                                                               \
+       for (i = 0; i < cache->fields_count; i++) {                     \
+               if (cache->field_file_map[i] != (uint32_t)-1)           \
+                       continue;                                       \
+               field_name = (type)cache->fields[i].field_name;         \
+               buffer_append(dest, &field_name, sizeof(field_name));   \
+       }
+
+void mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest)
+{
+       struct mail_cache_header_fields hdr;
+       unsigned int field;
+       const char *name;
+       uint32_t i, last_used, field_size;
+       uint8_t type, decision;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.fields_count = cache->fields_count;
+       buffer_append(dest, &hdr, sizeof(hdr));
+
+       /* we have to keep the field order for the existing fields. */
+        UGLY_COPY_MACRO(last_used, uint32_t);
+        UGLY_COPY_MACRO(field_size, uint32_t);
+        UGLY_COPY_MACRO(type, uint8_t);
+        UGLY_COPY_MACRO(decision, uint8_t);
+
+       for (i = 0; i < cache->file_fields_count; i++) {
+               field = cache->file_field_map[i];
+               name = cache->fields[field].name;
+               buffer_append(dest, name, strlen(name)+1);
+       }
+       for (i = 0; i < cache->fields_count; i++) {
+               if (cache->field_file_map[i] != (uint32_t)-1)
+                       continue;
+               name = cache->fields[i].name;
+               buffer_append(dest, name, strlen(name)+1);
+       }
+
+       hdr.size = buffer_get_used_size(dest);
+       buffer_write(dest, 0, &hdr, sizeof(hdr));
+
+       if ((hdr.size & 3) != 0)
+               buffer_append(dest, null4, 4 - (hdr.size & 3));
+}
+
+int mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
+                                            uint32_t *offset_r)
+{
+       if (mail_cache_header_fields_get_offset(cache, offset_r) < 0)
+               return -1;
+
+       if (*offset_r == 0) {
+               *offset_r = offsetof(struct mail_cache_header,
+                                    field_header_offset);
+       } else {
+               *offset_r += offsetof(struct mail_cache_header_fields,
+                                     next_offset);
+       }
+       return 0;
+}
index df7dae76de78340190a46f8175c01750cd4ceb04..a88408d5ef6b0e88a2ce8a50691410e5e356078b 100644 (file)
@@ -5,114 +5,9 @@
 #include "str.h"
 #include "mail-cache-private.h"
 
-#define CACHE_PREFETCH 1024
-
-const char *
-mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx)
-{
-       uint32_t offset, data_size;
-       const unsigned char *buf;
-
-       if (MAIL_CACHE_IS_UNUSABLE(cache))
-               return NULL;
-
-       offset = mail_cache_offset_to_uint32(cache->hdr->header_offsets[idx]);
-
-       if (offset == 0)
-               return NULL;
-
-       if (mail_cache_map(cache, offset, CACHE_PREFETCH) < 0)
-               return NULL;
-
-       if (offset + sizeof(data_size) > cache->mmap_length) {
-               mail_cache_set_corrupted(cache, "Header %u points outside file",
-                                        idx);
-               return NULL;
-       }
-
-       buf = cache->mmap_base;
-       memcpy(&data_size, buf + offset, sizeof(data_size));
-       offset += sizeof(data_size);
-
-       if (data_size == 0) {
-               mail_cache_set_corrupted(cache,
-                       "Header %u points to empty string", idx);
-               return NULL;
-       }
-
-       if (data_size + sizeof(data_size) > CACHE_PREFETCH) {
-               if (mail_cache_map(cache, offset, data_size) < 0)
-                       return NULL;
-       }
-
-       if (offset + data_size > cache->mmap_length) {
-               mail_cache_set_corrupted(cache, "Header %u points outside file",
-                                        idx);
-               return NULL;
-       }
-
-       buf = cache->mmap_base;
-       if (buf[offset + data_size - 1] != '\0') {
-               mail_cache_set_corrupted(cache,
-                       "Header %u points to invalid string", idx);
-               return NULL;
-       }
-
-       return buf + offset;
-}
-
-const char *const *
-mail_cache_split_header(struct mail_cache *cache, const char *header)
-{
-       const char *const *arr, *const *tmp;
-       const char *null = NULL;
-       char *str;
-       buffer_t *buf;
+#include <stdlib.h>
 
-       if (header == NULL)
-               return NULL;
-
-       arr = t_strsplit(header, "\n");
-       buf = buffer_create_dynamic(cache->split_header_pool, 32, (size_t)-1);
-       for (tmp = arr; *tmp != NULL; tmp++) {
-               str = p_strdup(cache->split_header_pool, *tmp);
-               buffer_append(buf, &str, sizeof(str));
-       }
-       buffer_append(buf, &null, sizeof(null));
-
-       return buffer_get_data(buf, NULL);
-}
-
-const char *const *mail_cache_get_header_fields(struct mail_cache_view *view,
-                                               unsigned int idx)
-{
-       struct mail_cache *cache = view->cache;
-       const char *str;
-       int i;
-
-       i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
-
-       if (MAIL_CACHE_IS_UNUSABLE(view->cache))
-               return NULL;
-
-       /* t_strsplit() is a bit slow, so we cache it */
-       if (cache->hdr->header_offsets[idx] != cache->split_offsets[idx]) {
-               p_clear(cache->split_header_pool);
-
-               t_push();
-               for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
-                       cache->split_offsets[i] =
-                               cache->hdr->header_offsets[i];
-
-                       str = mail_cache_get_header_fields_str(cache, i);
-                       cache->split_headers[i] =
-                               mail_cache_split_header(cache, str);
-               }
-               t_pop();
-       }
-
-       return cache->split_headers[idx];
-}
+#define CACHE_PREFETCH 1024
 
 struct mail_cache_record *
 mail_cache_get_record(struct mail_cache *cache, uint32_t offset)
@@ -177,23 +72,38 @@ mail_cache_foreach_rec(struct mail_cache_view *view,
                       const struct mail_cache_record *cache_rec,
                       mail_cache_foreach_callback_t *callback, void *context)
 {
+       struct mail_cache *cache = view->cache;
        size_t pos, next_pos, max_size, data_size;
-       uint32_t field;
+       uint32_t file_field;
+       unsigned int field;
        int ret;
 
        max_size = cache_rec->size;
        if (max_size < sizeof(*cache_rec) + sizeof(uint32_t)*2) {
-               mail_cache_set_corrupted(view->cache,
-                                        "record has invalid size");
+               mail_cache_set_corrupted(cache, "record has invalid size");
                return -1;
        }
        max_size -= sizeof(uint32_t);
 
        for (pos = sizeof(*cache_rec); pos < max_size; ) {
-               field = *((const uint32_t *)CONST_PTR_OFFSET(cache_rec, pos));
+               file_field =
+                       *((const uint32_t *)CONST_PTR_OFFSET(cache_rec, pos));
                pos += sizeof(uint32_t);
 
-               data_size = mail_cache_field_sizes[field];
+               if (file_field >= cache->file_fields_count) {
+                       /* new field, have to re-read fields header to figure
+                          out it's size */
+                       if (mail_cache_header_fields_read(cache) < 0)
+                               return -1;
+                       if (file_field >= cache->file_fields_count) {
+                               mail_cache_set_corrupted(cache,
+                                       "field index too large");
+                               return -1;
+                       }
+               }
+
+               field = cache->file_field_map[file_field];
+               data_size = cache->fields[field].field_size;
                if (data_size == (unsigned int)-1) {
                        data_size = *((const uint32_t *)
                                      CONST_PTR_OFFSET(cache_rec, pos));
@@ -202,14 +112,15 @@ mail_cache_foreach_rec(struct mail_cache_view *view,
 
                next_pos = pos + ((data_size + 3) & ~3);
                if (next_pos > cache_rec->size) {
-                       mail_cache_set_corrupted(view->cache,
+                       mail_cache_set_corrupted(cache,
                                "Record continues outside it's allocated size");
                        return -1;
                }
 
-               ret = callback(view, field, CONST_PTR_OFFSET(cache_rec, pos),
+               ret = callback(view, file_field,
+                              CONST_PTR_OFFSET(cache_rec, pos),
                               data_size, context);
-               if (ret <= 0)
+               if (ret != 1)
                        return ret;
 
                pos = next_pos;
@@ -227,8 +138,15 @@ int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
         if (MAIL_CACHE_IS_UNUSABLE(view->cache))
                return 0;
 
-       if ((ret = mail_cache_lookup_offset(view, seq, &offset)) <= 0)
-               return ret;
+       if (view->cached_offset_seq == seq)
+               offset = view->cached_offset;
+       else {
+               if ((ret = mail_cache_lookup_offset(view, seq, &offset)) <= 0)
+                       return ret;
+
+               view->cached_offset_seq = seq;
+               view->cached_offset = offset;
+       }
 
        cache_rec = mail_cache_get_record(view->cache, offset);
        while (cache_rec != NULL) {
@@ -251,13 +169,14 @@ int mail_cache_foreach(struct mail_cache_view *view, uint32_t seq,
        return 1;
 }
 
-static int mail_cache_seq_callback(struct mail_cache_view *view,
-                                  enum mail_cache_field field,
-                                  const void *data __attr_unused__,
-                                  size_t data_size __attr_unused__,
-                                  void *context __attr_unused__)
+static int
+mail_cache_seq_callback(struct mail_cache_view *view, uint32_t file_field,
+                       const void *data __attr_unused__,
+                       size_t data_size __attr_unused__,
+                       void *context __attr_unused__)
 {
-       view->cached_exists[field] = TRUE;
+       buffer_write(view->cached_exists_buf, file_field,
+                    &view->cached_exists_value, 1);
        return 1;
 }
 
@@ -265,53 +184,63 @@ static int mail_cache_seq(struct mail_cache_view *view, uint32_t seq)
 {
        int ret;
 
-       view->cached_exists_seq = seq;
-       memset(view->cached_exists, 0, sizeof(view->cached_exists));
+       if (++view->cached_exists_value == 0) {
+               /* wrapped, we'll have to clear the buffer */
+               memset(buffer_get_modifyable_data(view->cached_exists_buf,
+                                                 NULL), 0,
+                      buffer_get_size(view->cached_exists_buf));
+               view->cached_exists_value++;
+       }
 
+       view->cached_exists_seq = seq;
        ret = mail_cache_foreach(view, seq, mail_cache_seq_callback, NULL);
        return ret < 0 ? -1 : 0;
 }
 
 int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
-                           enum mail_cache_field field)
+                           unsigned int field)
 {
-       i_assert(field < MAIL_CACHE_FIELD_COUNT);
+       const uint8_t *data;
+       uint32_t file_field;
+       size_t size;
 
-        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
+       i_assert(field < view->cache->fields_count);
+
+       file_field = view->cache->field_file_map[field];
+       if (file_field == (uint32_t)-1)
                return 0;
 
        if (view->cached_exists_seq != seq) {
                if (mail_cache_seq(view, seq) < 0)
                        return -1;
        }
-       return view->cached_exists[field];
+
+       data = buffer_get_data(view->cached_exists_buf, &size);
+       return size <= file_field ? FALSE :
+               data[file_field] == view->cached_exists_value;
 }
 
 enum mail_cache_decision_type
-mail_cache_field_get_decision(struct mail_cache *cache,
-                             enum mail_cache_field field)
+mail_cache_field_get_decision(struct mail_cache *cache, unsigned int field)
 {
-       i_assert(field < MAIL_CACHE_FIELD_COUNT);
-
-        if (MAIL_CACHE_IS_UNUSABLE(cache))
-               return cache->default_field_usage_decision_type[field];
+       i_assert(field < cache->fields_count);
 
-       return cache->hdr->field_usage_decision_type[field];
+       return cache->fields[field].decision;
 }
 
 struct mail_cache_lookup_context {
        buffer_t *dest_buf;
-       enum mail_cache_field field;
+       uint32_t file_field;
 };
 
 static int
 mail_cache_lookup_callback(struct mail_cache_view *view __attr_unused__,
-                          enum mail_cache_field field,
-                          const void *data, size_t data_size, void *context)
+                          uint32_t file_field, const void *data,
+                          size_t data_size, void *context)
 {
         struct mail_cache_lookup_context *ctx = context;
 
-       if (ctx->field != field)
+       if (ctx->file_field != file_field)
                return 1;
 
        buffer_append(ctx->dest_buf, data, data_size);
@@ -319,60 +248,174 @@ mail_cache_lookup_callback(struct mail_cache_view *view __attr_unused__,
 }
 
 int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf,
-                           uint32_t seq, enum mail_cache_field field)
+                           uint32_t seq, unsigned int field)
 {
-        struct mail_cache_lookup_context ctx;
-
-       i_assert(field < MAIL_CACHE_FIELD_COUNT);
+       struct mail_cache_lookup_context ctx;
+       int ret;
 
-        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
-               return 0;
+       if ((ret = mail_cache_field_exists(view, seq, field)) <= 0)
+               return ret;
 
        mail_cache_decision_lookup(view, seq, field);
 
-       if (view->cached_exists_seq != seq) {
-               if (mail_cache_seq(view, seq) < 0)
-                       return -1;
-       }
-
-       if (!view->cached_exists[field])
-               return 0;
-
        /* should exist. find it. */
-       ctx.field = field;
+       ctx.file_field = view->cache->field_file_map[field];
        ctx.dest_buf = dest_buf;
        return mail_cache_foreach(view, seq, mail_cache_lookup_callback,
                                  &ctx) == 0;
 }
 
-int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
-                                  uint32_t seq, enum mail_cache_field field)
-{
-       size_t old_size, new_size;
+struct header_lookup_data_rec {
+       uint32_t offset;
+       uint32_t data_size;
+};
 
-       i_assert(field < MAIL_CACHE_FIELD_COUNT);
+struct header_lookup_data {
+       uint32_t line_num;
+        struct header_lookup_data_rec *data;
+};
 
-        if (MAIL_CACHE_IS_UNUSABLE(view->cache))
-               return 0;
+struct header_lookup_context {
+       unsigned int *fields;
+       size_t fields_count;
+       buffer_t *data;
 
-       old_size = str_len(dest);
-       if (!mail_cache_lookup_field(view, dest, seq, field))
-               return 0;
+       unsigned int max_field;
+       uint8_t *fields_found;
+};
 
-       new_size = str_len(dest);
-       if (old_size == new_size ||
-           str_data(dest)[new_size-1] != '\0') {
-               mail_cache_set_corrupted(view->cache,
-                       "String field %x doesn't end with NUL", field);
-               return -1;
+static int
+headers_find_callback(struct mail_cache_view *view, uint32_t file_field,
+                     const void *data, size_t data_size, void *context)
+{
+       struct header_lookup_context *ctx = context;
+       const uint32_t *lines = data;
+       struct header_lookup_data hdr_data;
+        struct header_lookup_data_rec *hdr_data_rec;
+       unsigned int i, lines_count;
+
+       if (file_field > ctx->max_field || ctx->fields_found[file_field] != 1) {
+               /* a) don't want it, b) duplicate */
+               return 1;
+       }
+       ctx->fields_found[file_field]++;
+
+       /* data = { line_nums[], 0, "headers" } */
+       for (i = 0; data_size >= sizeof(uint32_t); i++) {
+               data_size -= sizeof(uint32_t);
+               if (lines[i] == 0)
+                       break;
+       }
+       lines_count = i;
+
+       /* FIXME: this relies on mmap() too heavily */
+       hdr_data_rec = t_new(struct header_lookup_data_rec, 1);
+       hdr_data_rec->offset = (const char *)&lines[lines_count+1] -
+               (const char *)view->cache->mmap_base;
+       hdr_data_rec->data_size = (uint32_t)data_size;
+
+       for (i = 0; i < lines_count; i++) {
+               hdr_data.line_num = lines[i];
+               hdr_data.data = hdr_data_rec;
+               buffer_append(ctx->data, &hdr_data, sizeof(hdr_data));
        }
-       str_truncate(dest, new_size-1);
        return 1;
 }
 
-enum mail_cache_record_flag
-mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq)
+static int header_lookup_data_cmp(const void *p1, const void *p2)
 {
-       // FIXME:
-       return 0;
+       const struct header_lookup_data *d1 = p1, *d2 = p2;
+
+       return (int)d1->line_num - (int)d2->line_num;
+}
+
+int mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest,
+                             uint32_t seq, unsigned int fields[],
+                             size_t fields_count)
+{
+       struct mail_cache *cache = view->cache;
+       struct header_lookup_context ctx;
+       struct header_lookup_data *data;
+       const unsigned char *p, *start, *end;
+       size_t i, size, hdr_size;
+       unsigned int field_idx;
+       int ret;
+
+       if (fields_count == 0)
+               return 1;
+
+       t_push();
+
+       /* @UNSAFE */
+       memset(&ctx, 0, sizeof(ctx));
+       ctx.fields = t_new(unsigned int, fields_count);
+       ctx.fields_count = fields_count;
+
+       ctx.max_field = 1;
+       ctx.fields_found = t_buffer_get(ctx.max_field);
+       for (i = 0; i < fields_count; i++) {
+               i_assert(fields[i] < cache->fields_count);
+               field_idx = cache->field_file_map[fields[i]];
+               if (field_idx == (unsigned int)-1) {
+                       /* not cached at all */
+                       t_pop();
+                       return 0;
+               }
+
+               if (field_idx > ctx.max_field) {
+                       ctx.fields_found = t_buffer_reget(ctx.fields_found,
+                                                         field_idx + 1);
+                       memset(ctx.fields_found + ctx.max_field + 1, 0,
+                              field_idx - ctx.max_field - 1);
+                       ctx.max_field = field_idx;
+               }
+               ctx.fields_found[field_idx] = 1;
+                ctx.fields[i] = field_idx;
+       }
+        t_buffer_alloc(ctx.max_field + 1);
+
+       ctx.data = buffer_create_dynamic(pool_datastack_create(),
+                                        256, (size_t)-1);
+
+       /* we need to return them in sorted order. create array:
+          { line number -> cache file offset } */
+       ret = mail_cache_foreach(view, seq, headers_find_callback, &ctx);
+       if (ret <= 0) {
+               t_pop();
+               return ret;
+       }
+
+       /* check that all fields were found */
+       for (i = 0; i < ctx.max_field; i++) {
+               if (ctx.fields_found[i] == 1) {
+                       t_pop();
+                       return 0;
+               }
+       }
+
+       data = buffer_get_modifyable_data(ctx.data, &size);
+       size /= sizeof(*data);
+       qsort(data, size, sizeof(*data), header_lookup_data_cmp);
+
+       /* then start filling dest buffer from the headers */
+       for (i = 0; i < size; i++) {
+               start = CONST_PTR_OFFSET(cache->mmap_base,
+                                        data[i].data->offset);
+               end = start + data[i].data->data_size;
+
+               for (p = start; p != end; p++) {
+                       if (*p == '\n' &&
+                           (p+1 == end || (p[1] != ' ' && p[1] != '\t'))) {
+                               p++;
+                               break;
+                       }
+               }
+               hdr_size = (size_t)(p - start);
+               data[i].data->offset += hdr_size;
+               data[i].data->data_size += hdr_size;
+               buffer_append(dest, start, hdr_size);
+       }
+
+       t_pop();
+       return 1;
 }
index b89e0414af157a33dd2840230e7d5dc402067150..f3e408eced0ecadfc0ca9678f3be4eb15e43aa16 100644 (file)
@@ -50,12 +50,44 @@ struct mail_cache_header {
        uint32_t used_file_size;
        uint32_t deleted_space;
 
-       uint32_t field_usage_last_used[32]; /* time_t */
-       uint8_t field_usage_decision_type[32];
+       uint32_t field_header_offset;
+};
 
-       uint32_t header_offsets[MAIL_CACHE_HEADERS_COUNT];
+struct mail_cache_header_fields {
+       uint32_t next_offset;
+       uint32_t size;
+       uint32_t fields_count;
+
+#if 0
+       /* last time the field was accessed. not updated more often than
+          once a day. */
+       uint32_t last_used[fields_count];
+       /* (uint32_t)-1 for variable sized fields */
+       uint32_t size[fields_count];
+       /* enum mail_cache_field_type */
+       uint8_t type[fields_count];
+       /* enum mail_cache_decision_type */
+       uint8_t decision[fields_count];
+       /* NUL-separated list of field names */
+       char name[fields_count][];
+#endif
 };
 
+#define MAIL_CACHE_FIELD_LAST_USED(field_hdr) \
+       CONST_PTR_OFFSET(field_hdr, sizeof(uint32_t) * 3)
+#define MAIL_CACHE_FIELD_SIZE(field_hdr) \
+       CONST_PTR_OFFSET(MAIL_CACHE_FIELD_LAST_USED(field_hdr), \
+                        sizeof(uint32_t) * (field_hdr)->fields_count)
+#define MAIL_CACHE_FIELD_TYPE(field_hdr) \
+       CONST_PTR_OFFSET(MAIL_CACHE_FIELD_SIZE(field_hdr), \
+                        sizeof(uint32_t) * (field_hdr)->fields_count)
+#define MAIL_CACHE_FIELD_DECISION(field_hdr) \
+       CONST_PTR_OFFSET(MAIL_CACHE_FIELD_TYPE(field_hdr), \
+                        sizeof(uint8_t) * (field_hdr)->fields_count)
+#define MAIL_CACHE_FIELD_NAMES(field_hdr) \
+       CONST_PTR_OFFSET(MAIL_CACHE_FIELD_DECISION(field_hdr), \
+                        sizeof(uint8_t) * (field_hdr)->fields_count)
+
 struct mail_cache_record {
        uint32_t prev_offset;
        uint32_t size; /* full record size, including this header */
@@ -85,16 +117,19 @@ struct mail_cache {
        const struct mail_cache_header *hdr;
        struct mail_cache_header hdr_copy;
 
-       pool_t split_header_pool;
-       uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
-       const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
+       pool_t field_pool;
+       struct mail_cache_field *fields;
+       uint32_t *field_file_map;
+       unsigned int fields_count;
+       struct hash_table *field_name_hash; /* name -> idx */
 
-       uint8_t default_field_usage_decision_type[32];
-       uint32_t field_usage_uid_highwater[32];
+       unsigned int *file_field_map;
+       unsigned int file_fields_count;
 
        unsigned int locked:1;
        unsigned int need_compress:1;
        unsigned int hdr_modified:1;
+       unsigned int field_header_write_pending:1;
 };
 
 struct mail_cache_view {
@@ -104,18 +139,20 @@ struct mail_cache_view {
        struct mail_cache_transaction_ctx *transaction;
        uint32_t trans_seq1, trans_seq2;
 
-       char cached_exists[32];
+       /* if cached_exists_buf[field] == cached_exists_value, it's cached.
+          this allows us to avoid constantly clearing the whole buffer.
+          it needs to be cleared only when cached_exists_value is wrapped. */
+       buffer_t *cached_exists_buf;
+       uint8_t cached_exists_value;
        uint32_t cached_exists_seq;
+       uint32_t cached_offset, cached_offset_seq;
 };
 
 typedef int mail_cache_foreach_callback_t(struct mail_cache_view *view,
-                                         enum mail_cache_field field,
+                                         uint32_t file_field,
                                          const void *data, size_t data_size,
                                          void *context);
 
-extern unsigned int mail_cache_field_sizes[32];
-extern enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT];
-
 uint32_t mail_cache_uint32_to_offset(uint32_t offset);
 uint32_t mail_cache_offset_to_uint32(uint32_t offset);
 
@@ -124,10 +161,11 @@ uint32_t mail_cache_offset_to_uint32(uint32_t offset);
 int mail_cache_lock(struct mail_cache *cache);
 void mail_cache_unlock(struct mail_cache *cache);
 
-const char *
-mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
-const char *const *
-mail_cache_split_header(struct mail_cache *cache, const char *header);
+int mail_cache_header_fields_read(struct mail_cache *cache);
+int mail_cache_header_fields_update(struct mail_cache *cache);
+void mail_cache_header_fields_get(struct mail_cache *cache, buffer_t *dest);
+int mail_cache_header_fields_get_next_offset(struct mail_cache *cache,
+                                            uint32_t *offset_r);
 
 struct mail_cache_record *
 mail_cache_get_record(struct mail_cache *cache, uint32_t offset);
@@ -152,9 +190,9 @@ int mail_cache_link(struct mail_cache *cache, uint32_t old_offset,
 int mail_cache_delete(struct mail_cache *cache, uint32_t offset);
 
 void mail_cache_decision_lookup(struct mail_cache_view *view, uint32_t seq,
-                               enum mail_cache_field field);
+                               uint32_t field);
 void mail_cache_decision_add(struct mail_cache_view *view, uint32_t seq,
-                            enum mail_cache_field field);
+                            uint32_t field);
 
 void mail_cache_set_syscall_error(struct mail_cache *cache,
                                  const char *function);
index e9cf3a218a10bc737ffb6650c97426c4076b8abd..12a410afa9b3387c69e256185fa8900effb9ea5a 100644 (file)
@@ -16,9 +16,6 @@ struct mail_cache_transaction_ctx {
        struct mail_cache_view *view;
        struct mail_index_transaction *trans;
 
-       uint32_t update_header_offsets[MAIL_CACHE_HEADERS_COUNT];
-       unsigned int next_unused_header_lowwater;
-
        buffer_t *cache_data, *cache_data_seq;
        uint32_t prev_seq;
        size_t prev_pos;
@@ -279,6 +276,9 @@ mail_cache_transaction_get_space(struct mail_cache_transaction_ctx *ctx,
        size_t size;
        int ret;
 
+       i_assert((min_size & 3) == 0);
+       i_assert((max_size & 3) == 0);
+
        if (min_size > ctx->reserved_space) {
                if (!locked) {
                        if (mail_cache_lock(ctx->cache) <= 0)
@@ -302,6 +302,7 @@ mail_cache_transaction_get_space(struct mail_cache_transaction_ctx *ctx,
        ctx->reserved_space -= size;
        if (available_space_r != NULL)
                *available_space_r = size;
+       i_assert((size & 3) == 0);
 
        if (size == max_size && commit) {
                /* final commit - see if we can free the rest of the
@@ -436,8 +437,7 @@ mail_cache_transaction_switch_seq(struct mail_cache_transaction_ctx *ctx)
 int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
 {
        struct mail_cache *cache = ctx->cache;
-       uint32_t offset;
-       int i, ret = 0;
+       int ret = 0;
 
        if (!ctx->changes) {
                mail_cache_transaction_free(ctx);
@@ -461,24 +461,7 @@ int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
                ret = -1;
        }
 
-       if (ret == 0) {
-               for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
-                       offset = ctx->update_header_offsets[i];
-                       if (offset != 0) {
-                               cache->hdr_copy.header_offsets[i] =
-                                       mail_cache_uint32_to_offset(offset);
-                               cache->hdr_modified = TRUE;
-                       }
-               }
-       }
-
        mail_cache_unlock(cache);
-
-       if (ctx->next_unused_header_lowwater == MAIL_CACHE_HEADERS_COUNT) {
-               /* they're all used - compress the cache to get more */
-               cache->need_compress = TRUE;
-       }
-
        mail_cache_transaction_free(ctx);
        return ret;
 }
@@ -488,7 +471,6 @@ void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx)
        struct mail_cache *cache = ctx->cache;
        const uint32_t *buf;
        size_t size;
-       unsigned int i;
 
        if (mail_cache_lock(cache) > 0) {
                mail_cache_transaction_free_space(ctx);
@@ -510,112 +492,86 @@ void mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx)
                mail_cache_unlock(cache);
        }
 
-       /* make sure we don't cache the headers */
-       for (i = 0; i < ctx->next_unused_header_lowwater; i++) {
-               uint32_t offset = cache->hdr->header_offsets[i];
-               if (mail_cache_offset_to_uint32(offset) == 0)
-                       cache->split_offsets[i] = 1;
-       }
-
        mail_cache_transaction_free(ctx);
 }
 
-static const char *write_header_string(const char *const headers[],
-                                      uint32_t *size_r)
+static int
+mail_cache_header_write_fields(struct mail_cache_transaction_ctx *ctx)
 {
+       struct mail_cache *cache = ctx->cache;
        buffer_t *buffer;
+       const void *data;
        size_t size;
+       uint32_t offset, hdr_offset;
+       int ret = 0;
 
-       buffer = buffer_create_dynamic(pool_datastack_create(),
-                                      512, (size_t)-1);
-
-       while (*headers != NULL) {
-               if (buffer_get_used_size(buffer) != 0)
-                       buffer_append(buffer, "\n", 1);
-               buffer_append(buffer, *headers, strlen(*headers));
-               headers++;
-       }
-       buffer_append(buffer, null4, 1);
-
-       size = buffer_get_used_size(buffer);
-       if ((size & 3) != 0) {
-               buffer_append(buffer, null4, 4 - (size & 3));
-               size += 4 - (size & 3);
-       }
-       *size_r = size;
-       return buffer_get_data(buffer, NULL);
-}
-
-int mail_cache_set_header_fields(struct mail_cache_transaction_ctx *ctx,
-                                unsigned int idx, const char *const headers[])
-{
-       struct mail_cache *cache = ctx->cache;
-       uint32_t offset, size, total_size;
-       const char *header_str, *prev_str;
-
-       i_assert(*headers != NULL);
-       i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
-       i_assert(idx >= ctx->next_unused_header_lowwater);
-       i_assert(mail_cache_offset_to_uint32(cache->hdr->
-                                            header_offsets[idx]) == 0);
+       if (mail_cache_lock(cache) <= 0)
+               return -1;
 
        t_push();
+       buffer = buffer_create_dynamic(pool_datastack_create(),
+                                      256, (size_t)-1);
+       mail_cache_header_fields_get(cache, buffer);
+       data = buffer_get_data(buffer, &size);
 
-       header_str = write_header_string(headers, &size);
-       if (idx != 0) {
-               prev_str = mail_cache_get_header_fields_str(cache, idx-1);
-               if (prev_str == NULL) {
-                       t_pop();
-                       return FALSE;
-               }
-
-               i_assert(strcmp(header_str, prev_str) != 0);
-       }
-
-       total_size = size + sizeof(uint32_t);
-       offset = mail_cache_transaction_get_space(ctx, total_size, total_size,
-                                                 NULL, FALSE);
-       if (offset != 0) {
-               if (pwrite_full(cache->fd, &size, sizeof(size), offset) < 0 ||
-                   pwrite_full(cache->fd, header_str, size,
-                               offset + sizeof(uint32_t)) < 0) {
+       offset = mail_cache_transaction_get_space(ctx, size, size, &size, TRUE);
+       if (offset == 0)
+               ret = -1;
+       else if (pwrite_full(cache->fd, data, size, offset) < 0) {
+               mail_cache_set_syscall_error(cache, "pwrite_full()");
+               ret = -1;
+       } else if (fdatasync(cache->fd) < 0) {
+               mail_cache_set_syscall_error(cache, "fdatasync()");
+               ret = -1;
+       } else if (mail_cache_header_fields_get_next_offset(cache,
+                                                           &hdr_offset) < 0)
+               ret = -1;
+       else {
+               /* after it's guaranteed to be in disk, update header offset */
+               offset = mail_cache_uint32_to_offset(offset);
+               if (pwrite_full(cache->fd, &offset, sizeof(offset),
+                               hdr_offset) < 0) {
                        mail_cache_set_syscall_error(cache, "pwrite_full()");
-                       offset = 0;
+                       ret = -1;
+               } else {
+                       /* we'll need to fix mappings. */
+                       if (mail_cache_header_fields_read(cache) < 0)
+                               ret = -1;
                }
        }
-
-       if (offset != 0) {
-               ctx->update_header_offsets[idx] = offset;
-               ctx->changes = TRUE;
-
-               /* update cached headers */
-               cache->split_offsets[idx] = cache->hdr->header_offsets[idx];
-               cache->split_headers[idx] =
-                       mail_cache_split_header(cache, header_str);
-
-               /* make sure get_header_fields() still works for this header
-                  while the transaction isn't yet committed. */
-               ctx->next_unused_header_lowwater = idx + 1;
-       }
-
        t_pop();
-       return offset > 0;
+
+       mail_cache_unlock(cache);
+       return ret;
 }
 
 void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
-                   enum mail_cache_field field,
-                   const void *data, size_t data_size)
+                   unsigned int field, const void *data, size_t data_size)
 {
-       uint32_t fixed_size, data_size32;
+       uint32_t file_field, data_size32;
+       unsigned int fixed_size;
        size_t full_size;
 
-       i_assert(field < MAIL_CACHE_FIELD_COUNT);
-       i_assert(data_size > 0);
+       i_assert(field < ctx->cache->fields_count);
        i_assert(data_size < (uint32_t)-1);
 
+       if (ctx->cache->fields[field].decision ==
+           (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED))
+               return;
+
+       file_field = ctx->cache->field_file_map[field];
+       if (file_field == (uint32_t)-1) {
+               /* we'll have to add this field to headers */
+               if (mail_cache_header_write_fields(ctx) < 0)
+                       return;
+
+               file_field = ctx->cache->field_file_map[field];
+               i_assert(file_field != (uint32_t)-1);
+       }
+
        mail_cache_decision_add(ctx->view, seq, field);
 
-       fixed_size = mail_cache_field_sizes[field];
+       fixed_size = ctx->cache->fields[field].field_size;
        i_assert(fixed_size == (unsigned int)-1 || fixed_size == data_size);
 
        data_size32 = (uint32_t)data_size;
@@ -643,7 +599,7 @@ void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
                        return;
        }
 
-       buffer_append(ctx->cache_data, &field, sizeof(field));
+       buffer_append(ctx->cache_data, &file_field, sizeof(file_field));
        if (fixed_size == (unsigned int)-1) {
                buffer_append(ctx->cache_data, &data_size32,
                              sizeof(data_size32));
@@ -654,12 +610,6 @@ void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
                 buffer_append(ctx->cache_data, null4, 4 - (data_size & 3));
 }
 
-int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
-                                  enum mail_cache_record_flag flags)
-{
-       return -1;
-}
-
 int mail_cache_transaction_lookup(struct mail_cache_transaction_ctx *ctx,
                                  uint32_t seq, uint32_t *offset_r)
 {
index be9d60d405b4f6922886546158a66b7aa532b8eb..820218aa00850dbe076d266522391810b51890ad 100644 (file)
@@ -1,37 +1,15 @@
 /* Copyright (C) 2003-2004 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
+#include "hash.h"
 #include "file-lock.h"
-#include "file-set-size.h"
 #include "mmap-util.h"
 #include "write-full.h"
 #include "mail-cache-private.h"
 
 #include <unistd.h>
 
-unsigned int mail_cache_field_sizes[32] = {
-       sizeof(enum mail_cache_record_flag),
-       sizeof(struct mail_sent_date),
-       sizeof(time_t),
-       sizeof(uoff_t),
-
-       /* variable sized */
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1,
-       (unsigned int)-1, (unsigned int)-1, (unsigned int)-1, (unsigned int)-1
-};
-
-enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT] = {
-       MAIL_CACHE_HEADERS1,
-       MAIL_CACHE_HEADERS2,
-       MAIL_CACHE_HEADERS3,
-       MAIL_CACHE_HEADERS4
-};
-
 uint32_t mail_cache_uint32_to_offset(uint32_t offset)
 {
        unsigned char buf[4];
@@ -129,6 +107,9 @@ int mail_cache_reopen(struct mail_cache *cache)
        if (mail_cache_map(cache, 0, 0) < 0)
                return -1;
 
+       if (mail_cache_header_fields_read(cache) < 0)
+               return -1;
+
        if (cache->hdr->file_seq != cache->index->hdr->cache_file_seq) {
                /* still different - maybe a race condition or maybe the
                   file_seq really is corrupted. either way, this shouldn't
@@ -240,7 +221,10 @@ static int mail_cache_open_and_verify(struct mail_cache *cache)
                return -1;
        }
 
-       return mail_cache_map(cache, 0, sizeof(struct mail_cache_header));
+       if (mail_cache_map(cache, 0, sizeof(struct mail_cache_header)) < 0)
+               return -1;
+
+       return mail_cache_header_fields_read(cache);
 }
 
 struct mail_cache *mail_cache_open_or_create(struct mail_index *index)
@@ -250,7 +234,10 @@ struct mail_cache *mail_cache_open_or_create(struct mail_index *index)
        cache = i_new(struct mail_cache, 1);
        cache->index = index;
        cache->fd = -1;
-        cache->split_header_pool = pool_alloconly_create("Headers", 512);
+        cache->field_pool = pool_alloconly_create("Cache fields", 512);
+       cache->field_name_hash =
+               hash_create(default_pool, cache->field_pool, 0,
+                           strcase_hash, (hash_cmp_callback_t *)strcasecmp);
 
        if (!index->mmap_disable && !index->mmap_no_write) {
                if (mail_cache_open_and_verify(cache) < 0) {
@@ -267,18 +254,13 @@ void mail_cache_free(struct mail_cache *cache)
 {
        mail_cache_file_close(cache);
 
-       pool_unref(cache->split_header_pool);
+       hash_destroy(cache->field_name_hash);
+       pool_unref(cache->field_pool);
+       i_free(cache->file_field_map);
        i_free(cache->filepath);
        i_free(cache);
 }
 
-void mail_cache_set_defaults(struct mail_cache *cache,
-                            const enum mail_cache_decision_type dec[32])
-{
-       memcpy(cache->default_field_usage_decision_type, dec,
-              sizeof(cache->default_field_usage_decision_type));
-}
-
 int mail_cache_lock(struct mail_cache *cache)
 {
        int i, ret;
@@ -367,10 +349,15 @@ mail_cache_view_open(struct mail_cache *cache, struct mail_index_view *iview)
        view = i_new(struct mail_cache_view, 1);
        view->cache = cache;
        view->view = iview;
+       view->cached_exists_buf =
+               buffer_create_dynamic(default_pool,
+                                     cache->file_fields_count + 10,
+                                     (size_t)-1);
        return view;
 }
 
 void mail_cache_view_close(struct mail_cache_view *view)
 {
+       buffer_free(view->cached_exists_buf);
        i_free(view);
 }
index 44477d39968958c94300729fe92903757fbe5006..28b3e31dd3ca72db50dfdcdb17c953aff41e5ea4 100644 (file)
@@ -5,8 +5,6 @@
 
 #define MAIL_CACHE_FILE_PREFIX ".cache"
 
-#define MAIL_CACHE_HEADERS_COUNT 4
-
 struct mail_cache;
 struct mail_cache_view;
 struct mail_cache_transaction_ctx;
@@ -23,54 +21,37 @@ enum mail_cache_decision_type {
        MAIL_CACHE_DECISION_FORCED      = 0x80
 };
 
-enum mail_cache_record_flag {
-       /* If binary flags are set, it's not checked whether mail is
-          missing CRs. So this flag may be set as an optimization for
-          regular non-binary mails as well if it's known that it contains
-          valid CR+LF line breaks. */
-       MAIL_INDEX_FLAG_BINARY_HEADER           = 0x0001,
-       MAIL_INDEX_FLAG_BINARY_BODY             = 0x0002,
-
-       /* Mail header or body is known to contain NUL characters. */
-       MAIL_INDEX_FLAG_HAS_NULS                = 0x0004,
-       /* Mail header or body is known to not contain NUL characters. */
-       MAIL_INDEX_FLAG_HAS_NO_NULS             = 0x0008
+enum mail_cache_field_type {
+       MAIL_CACHE_FIELD_FIXED_SIZE,
+       MAIL_CACHE_FIELD_VARIABLE_SIZE,
+       MAIL_CACHE_FIELD_STRING,
+       MAIL_CACHE_FIELD_BITMASK,
+       MAIL_CACHE_FIELD_HEADER
 };
 
-/* when modifying, remember to update mail_cache_field_sizes[] too */
-enum mail_cache_field {
-       /* fixed size fields */
-       MAIL_CACHE_INDEX_FLAGS = 0,
-       MAIL_CACHE_SENT_DATE,
-       MAIL_CACHE_RECEIVED_DATE,
-       MAIL_CACHE_VIRTUAL_FULL_SIZE,
-
-       /* variable sized field */
-       MAIL_CACHE_HEADERS1,
-       MAIL_CACHE_HEADERS2,
-       MAIL_CACHE_HEADERS3,
-       MAIL_CACHE_HEADERS4,
-       MAIL_CACHE_BODY,
-       MAIL_CACHE_BODYSTRUCTURE,
-       MAIL_CACHE_ENVELOPE,
-       MAIL_CACHE_MESSAGEPART,
-       MAIL_CACHE_UID_STRING,
-
-       MAIL_CACHE_FIELD_COUNT
-};
+struct mail_cache_field {
+       const char *name;
+       unsigned int idx;
 
-struct mail_sent_date {
-       time_t time;
-       int32_t timezone;
-};
+       enum mail_cache_field_type type;
+       unsigned int field_size;
+       enum mail_cache_decision_type decision;
 
-extern enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT];
+       /* internal: */
+       uint32_t uid_highwater;
+       time_t last_used;
+};
 
 struct mail_cache *mail_cache_open_or_create(struct mail_index *index);
 void mail_cache_free(struct mail_cache *cache);
 
-void mail_cache_set_defaults(struct mail_cache *cache,
-                            const enum mail_cache_decision_type dec[32]);
+/* Register fields. fields[].idx is updated to contain field index. */
+void mail_cache_register_fields(struct mail_cache *cache,
+                               struct mail_cache_field *fields,
+                               size_t fields_count);
+/* Returns registered field index, or (unsigned int)-1 if not found. */
+unsigned int
+mail_cache_register_lookup(struct mail_cache *cache, const char *name);
 
 /* Returns TRUE if cache should be compressed. */
 int mail_cache_need_compress(struct mail_cache *cache);
@@ -86,46 +67,28 @@ struct mail_cache_transaction_ctx *
 mail_cache_get_transaction(struct mail_cache_view *view,
                           struct mail_index_transaction *t);
 
-/* Return NULL-terminated list of headers for given index, or NULL if
-   header index isn't used. */
-const char *const *mail_cache_get_header_fields(struct mail_cache_view *view,
-                                               unsigned int idx);
-/* Set list of headers for given index. */
-int mail_cache_set_header_fields(struct mail_cache_transaction_ctx *ctx,
-                                unsigned int idx, const char *const headers[]);
-
 /* Add new field to given record. Updates are not allowed. Fixed size fields
    must be exactly the expected size. */
 void mail_cache_add(struct mail_cache_transaction_ctx *ctx, uint32_t seq,
-                   enum mail_cache_field field,
-                   const void *data, size_t data_size);
+                   unsigned int field, const void *data, size_t data_size);
 
-/* Retursn TRUE if field exists. */
+/* Retursn 1 if field exists, 0 if not, -1 if error. */
 int mail_cache_field_exists(struct mail_cache_view *view, uint32_t seq,
-                           enum mail_cache_field field);
+                           unsigned int field);
 /* Returns current caching decision for given field. */
 enum mail_cache_decision_type
-mail_cache_field_get_decision(struct mail_cache *cache,
-                             enum mail_cache_field field);
+mail_cache_field_get_decision(struct mail_cache *cache, unsigned int field);
 
 /* Set data_r and size_r to point to wanted field in cache file.
-   Returns TRUE if field was found. If field contains multiple fields,
-   first one found is returned. This is mostly useful for finding headers. */
+   Returns 1 if field was found, 0 if not, -1 if error. */
 int mail_cache_lookup_field(struct mail_cache_view *view, buffer_t *dest_buf,
-                           uint32_t seq, enum mail_cache_field field);
-
-/* Return string field. */
-int mail_cache_lookup_string_field(struct mail_cache_view *view, string_t *dest,
-                                  uint32_t seq, enum mail_cache_field field);
-
-/* Return record flags. */
-enum mail_cache_record_flag
-mail_cache_get_record_flags(struct mail_cache_view *view, uint32_t seq);
+                           uint32_t seq, unsigned int field);
 
-/* Update record flags. The cache file must be locked and the flags must be
-   already inserted to the record. */
-int mail_cache_update_record_flags(struct mail_cache_view *view, uint32_t seq,
-                                  enum mail_cache_record_flag flags);
+/* Return specified cached headers. Returns 1 if all fields were found,
+   0 if not, -1 if error. dest is updated only if all fields were found. */
+int mail_cache_lookup_headers(struct mail_cache_view *view, string_t *dest,
+                             uint32_t seq, unsigned int fields[],
+                             size_t fields_count);
 
 /* "Error in index cache file %s: ...". */
 void mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
index 41e23ae939c121aa2d4565dc4f6d9d310a42e4e0..b866349c35210be482e19484a758133c152ec588 100644 (file)
@@ -112,14 +112,16 @@ static void _seek(struct _istream *stream, uoff_t v_offset)
        }
 }
 
-static void read_and_hide_headers(struct istream *input,
-                                 const char *const *headers,
-                                 size_t headers_count, buffer_t *dest,
-                                 struct message_size *hdr_size)
+static void
+read_and_hide_headers(struct istream *input, int filter,
+                     const char *const *headers, size_t headers_count,
+                     buffer_t *dest, struct message_size *hdr_size,
+                     header_filter_callback *callback, void *context)
 {
        struct message_header_parser_ctx *hdr_ctx;
        struct message_header_line *hdr;
        uoff_t virtual_size = 0;
+       int matched;
 
        hdr_ctx = message_parse_header_init(input, hdr_size, FALSE);
        while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
@@ -131,8 +133,12 @@ static void read_and_hide_headers(struct istream *input,
                        break;
                }
 
-               if (bsearch(hdr->name, headers, headers_count,
-                           sizeof(*headers), bsearch_strcasecmp) != NULL) {
+               matched = bsearch(hdr->name, headers, headers_count,
+                                 sizeof(*headers), bsearch_strcasecmp) != NULL;
+               if (callback != NULL)
+                       callback(hdr, matched, context);
+
+               if (matched == filter) {
                        /* ignore */
                } else if (dest != NULL) {
                        if (!hdr->continued) {
@@ -157,8 +163,9 @@ static void read_and_hide_headers(struct istream *input,
 }
 
 struct istream *
-i_stream_create_header_filter(pool_t pool, struct istream *input,
-                             const char *const *headers, size_t headers_count)
+i_stream_create_header_filter(pool_t pool, struct istream *input, int filter,
+                             const char *const *headers, size_t headers_count,
+                             header_filter_callback *callback, void *context)
 {
        struct header_filter_istream *mstream;
 
@@ -168,8 +175,11 @@ i_stream_create_header_filter(pool_t pool, struct istream *input,
 
        mstream->headers = buffer_create_dynamic(default_pool,
                                                 8192, (size_t)-1);
-       read_and_hide_headers(input, headers, headers_count, mstream->headers,
-                             &mstream->header_size);
+       read_and_hide_headers(input, filter, headers, headers_count,
+                             mstream->headers, &mstream->header_size,
+                             callback, context);
+       if (callback != NULL)
+               callback(NULL, FALSE, context);
 
        mstream->istream.buffer = buffer_get_data(mstream->headers, NULL);
        mstream->istream.pos = mstream->header_size.virtual_size;
index a36b27da8a50e82a6c3c7e7a0ac7dc112aea1b34..8e10db716400f18b8ae389abd82d59e76dd62e69 100644 (file)
@@ -1,9 +1,14 @@
 #ifndef __ISTREAM_HEADER_FILTER_H
 #define __ISTREAM_HEADER_FILTER_H
 
-/* NOTE: NULL-terminated headers list must be sorted. */
+typedef void header_filter_callback(struct message_header_line *hdr,
+                                   int matched, void *context);
+
+/* NOTE: headers list must be sorted. If filter is TRUE, given headers are
+   removed from output, otherwise only given headers are included in output. */
 struct istream *
-i_stream_create_header_filter(pool_t pool, struct istream *input,
-                             const char *const *headers, size_t headers_count);
+i_stream_create_header_filter(pool_t pool, struct istream *input, int filter,
+                             const char *const *headers, size_t headers_count,
+                             header_filter_callback *callback, void *context);
 
 #endif
index 273e2f6a930f9a555530cbc26efb6786f02a4419..093011ed23eaa1525df74a7204c2b052c9fa8624 100644 (file)
@@ -1,45 +1,12 @@
 /* Copyright (C) 2003 Timo Sirainen */
 
-/*
-   Headers are stored in 1-4 pieces. There's a list of header names that each
-   piece contains, so if piece doesn't actually contain some listed header,
-   it's known not to exist in the mail at all.
-
-   Header name lists are stored in sorted order, so we can use binary
-   searching.
-
-   We have to be able to do 3 things:
-    - Get value for one header
-    - Get a list of headers, possibly containing more than requested
-    - Save some of the uncached headers into cache
-
-   First is easy. Second means that we have to store the wanted headers in
-   a single string which we can directly return.
-
-   Third is a bit tricky if we want to avoid parsing and copying the data
-   uselessly. It's possible if we want to cache all requested uncached
-   headers. That should be the common case, so I'll optimize for that.
-   Another even more common case is that everything is already cached. So:
-
-   - If we request only cached headers, parse them and copy only wanted
-     headers to header_data.
-   - If we request a non-cached header, trash the header_data and all
-     pointers to it. Copy all cached headers to beginning if it and save
-     a marker where it ends.
-   - If we again request single cached header, we'll have to parse the
-     header_data up to the marker again.
-   - When saving the uncached headers, we know that they all come after the
-     marker. If we want to save them all, it's directly there in a string.
-     Otherwise we have to parse them and copy the wanted headers, but it's
-     still less work.
-*/
-
 #include "lib.h"
 #include "istream.h"
 #include "buffer.h"
 #include "str.h"
 #include "message-date.h"
 #include "message-parser.h"
+#include "istream-header-filter.h"
 #include "imap-envelope.h"
 #include "imap-bodystructure.h"
 #include "index-storage.h"
 
 #include <stdlib.h>
 
-struct cached_header {
-       const char *name;
-       size_t value_idx; /* in header_data */
+struct index_header_lookup_ctx {
+       struct mailbox_header_lookup_ctx ctx;
+       pool_t pool;
 
-       unsigned int parsing:1;
-       unsigned int fully_saved:1;
+       size_t count;
+       unsigned int *idx;
+       const char **name;
 };
 
-static struct cached_header *
-cached_header_find(struct index_mail *mail, const char *name,
-                  unsigned int *idx_r)
+static int header_line_cmp(const void *p1, const void *p2)
 {
-       struct cached_header **data;
-       size_t size;
-       unsigned int idx, left_idx, right_idx;
-       int ret;
-
-       data = buffer_get_modifyable_data(mail->data.headers, &size);
-
-       idx = left_idx = 0;
-       right_idx = size / sizeof(struct cached_header *);
-
-       while (left_idx < right_idx) {
-               idx = (left_idx + right_idx) / 2;
-
-               ret = strcasecmp(data[idx]->name, name);
-               if (ret < 0)
-                       left_idx = ++idx;
-               else if (ret > 0)
-                       right_idx = idx;
-               else {
-                       if (idx_r != NULL)
-                               *idx_r = idx;
-                       return data[idx];
-               }
-       }
+       const struct index_mail_line *l1 = p1, *l2 = p2;
+       int diff;
 
-       if (idx_r != NULL)
-               *idx_r = idx;
-       return NULL;
+       diff = (int)l1->field_idx - (int)l2->field_idx;
+       return diff != 0 ? diff :
+               (int)l1->line_num - (int)l2->line_num;
 }
 
-static struct cached_header *
-cached_header_add(struct index_mail *mail, const char *name)
+static void index_mail_parse_header_finish(struct index_mail *mail)
 {
-       struct cached_header *hdr;
-       unsigned int idx;
-
-       i_assert(*name != '\0');
+       static uint32_t null = 0;
+       struct index_mail_line *lines;
+       const unsigned char *header, *data;
+       const uint8_t *match;
+       buffer_t *buf;
+       size_t i, j, size, data_size, match_idx, match_size;
+       int noncontiguous;
 
-       hdr = cached_header_find(mail, name, &idx);
-       if (hdr != NULL)
-               return hdr;
-
-       hdr = p_new(mail->pool, struct cached_header, 1);
-       hdr->name = p_strdup(mail->pool, name);
-
-       buffer_insert(mail->data.headers, idx * sizeof(hdr), &hdr, sizeof(hdr));
-       return hdr;
-}
-
-static int strcasecmp_p(const void *p1, const void *p2)
-{
-       char *const *s1 = p1, *const *s2 = p2;
-
-       return strcasecmp(*s1, *s2);
-}
+       t_push();
 
-static const char *const *sort_array(const char *const *arr)
-{
-       static const char *null = NULL;
-       buffer_t *buffer;
-       const char **data;
-       int i, already_sorted;
-
-       /* copy the wanted_headers array */
-       buffer = buffer_create_dynamic(pool_datastack_create(),
-                                      256, (size_t)-1);
-       already_sorted = TRUE;
-       for (i = 0; arr[i] != NULL; i++) {
-               if (i > 0 && already_sorted &&
-                   strcasecmp(arr[i], arr[i-1]) <= 0)
-                       already_sorted = FALSE;
-               buffer_append(buffer, &arr[i], sizeof(const char *));
-       }
-       buffer_append(buffer, &null, sizeof(const char *));
+       lines = buffer_get_modifyable_data(mail->header_lines, &size);
+       size /= sizeof(*lines);
+
+       /* sort it first so fields are grouped together and ordered by
+          line number */
+       qsort(lines, size, sizeof(*lines), header_line_cmp);
+
+       match = buffer_get_data(mail->header_match, &match_size);
+       header = buffer_get_data(mail->header_data, NULL);
+       buf = buffer_create_dynamic(pool_datastack_create(), 256, (size_t)-1);
+
+       for (i = match_idx = 0; i < size; i = j) {
+               while (match_idx < lines[i].field_idx &&
+                      match_idx < match_size) {
+                       if (match[match_idx] == mail->header_match_value) {
+                               /* this header doesn't exist. remember that. */
+                               mail_cache_add(mail->trans->cache_trans,
+                                              mail->data.seq, match_idx,
+                                              NULL, 0);
+                       }
+                       match_idx++;
+               }
+               match_idx++;
 
-       /* and sort it */
-       data = buffer_get_modifyable_data(buffer, NULL);
-       if (!already_sorted)
-               qsort(data, i, sizeof(const char *), strcasecmp_p);
-       return data;
-}
+               buffer_set_used_size(buf, 0);
+               buffer_append(buf, &lines[i].line_num,
+                             sizeof(lines[i].line_num));
 
-static int find_wanted_headers(struct mail_cache_view *cache_view,
-                              const char *const wanted_headers[])
-{
-       const char *const *headers, *const *tmp;
-       int i, ret, cmp;
+               noncontiguous = FALSE;
+               for (j = i+1; j < size; j++) {
+                       if (lines[j].field_idx != lines[i].field_idx)
+                               break;
 
-       if (wanted_headers == NULL || *wanted_headers == NULL)
-               return -1;
+                       if (lines[j].start_pos != lines[j-1].end_pos)
+                               noncontiguous = TRUE;
+                       buffer_append(buf, &lines[j].line_num,
+                                     sizeof(lines[j].line_num));
+               }
+               buffer_append(buf, &null, sizeof(uint32_t));
 
-       t_push();
-       wanted_headers = sort_array(wanted_headers);
-
-       ret = -1;
-       for (i = MAIL_CACHE_HEADERS_COUNT-1; i >= 0; i--) {
-               headers = mail_cache_get_header_fields(cache_view, i);
-               if (headers == NULL)
-                       continue;
-
-               for (tmp = wanted_headers; *headers != NULL; headers++) {
-                       cmp = strcasecmp(*tmp, *headers);
-                       if (cmp == 0) {
-                               if (*++tmp == NULL)
-                                       break;
-                       } else {
-                               if (cmp < 0)
-                                       break;
+               if (noncontiguous) {
+                       for (; i < j; i++) {
+                               buffer_append(buf, header + lines[i].start_pos,
+                                             lines[i].end_pos -
+                                             lines[i].start_pos);
                        }
+               } else {
+                       buffer_append(buf, header + lines[i].start_pos,
+                                     lines[j-1].end_pos - lines[i].start_pos);
                }
 
-               if (*tmp != NULL)
-                       break;
-
-               /* find the minimum matching header number */
-               ret = i;
+               data = buffer_get_data(buf, &data_size);
+               mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+                              lines[i].field_idx, data, data_size);
        }
-       t_pop();
 
-       return ret;
+       t_pop();
 }
 
-static int mail_find_wanted_headers(struct index_mail *mail,
-                                   const char *const wanted_headers[])
+void index_mail_parse_header_init(struct index_mail *mail,
+                                 struct mailbox_header_lookup_ctx *_headers)
 {
-       int idx;
-
-       idx = find_wanted_headers(mail->trans->cache_view, wanted_headers);
-       if (idx < 0)
-               return -1;
-
-       for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
-               if (mail_cache_field_exists(mail->trans->cache_view,
-                                           mail->data.seq,
-                                           mail_cache_header_fields[idx]) > 0)
-                       return idx;
+       struct index_header_lookup_ctx *headers =
+               (struct index_header_lookup_ctx *)_headers;
+       size_t i;
+
+       if (mail->header_data == NULL) {
+               mail->header_data =
+                       buffer_create_dynamic(default_pool, 4096, (size_t)-1);
+               mail->header_lines =
+                       buffer_create_dynamic(default_pool, 256, (size_t)-1);
+               mail->header_match =
+                       buffer_create_dynamic(default_pool, 64, (size_t)-1);
+       } else {
+               buffer_set_used_size(mail->header_data, 0);
+               buffer_set_used_size(mail->header_lines, 0);
        }
 
-       return -1;
-}
-
-static const char *const *cached_header_get_names(struct index_mail *mail)
-{
-       const struct cached_header **data;
-       const char *null = NULL;
-       buffer_t *buffer;
-       size_t i, size;
-
-       data = buffer_get_modifyable_data(mail->data.headers, &size);
-       size /= sizeof(struct cached_header *);
-
-       buffer = buffer_create_dynamic(pool_datastack_create(),
-                                      128, (size_t)-1);
-       for (i = 0; i < size; i++)
-               buffer_append(buffer, &data[i]->name, sizeof(const char *));
-       buffer_append(buffer, &null, sizeof(const char *));
-
-       return buffer_get_data(buffer, NULL);
-}
-
-static void cached_headers_mark_fully_saved(struct index_mail *mail)
-{
-       struct cached_header **data;
-       size_t i, size;
+       if (++mail->header_match_value == 0) {
+               /* wrapped, we'll have to clear the buffer */
+               memset(buffer_get_modifyable_data(mail->header_match, NULL), 0,
+                      buffer_get_size(mail->header_match));
+               mail->header_match_value++;
+       }
 
-       data = buffer_get_modifyable_data(mail->data.headers, &size);
-       size /= sizeof(struct cached_header *);
+       if (headers != NULL) {
+               for (i = 0; i < headers->count; i++) {
+                       buffer_write(mail->header_match, headers->idx[i],
+                                    &mail->header_match_value, 1);
+               }
+       }
 
-       for (i = 0; i < size; i++) {
-               if (data[i]->parsing) {
-                       data[i]->parsing = FALSE;
-                       data[i]->fully_saved = TRUE;
+       if (mail->wanted_headers != NULL && mail->wanted_headers != headers) {
+               headers = mail->wanted_headers;
+               for (i = 0; i < headers->count; i++) {
+                       buffer_write(mail->header_match, headers->idx[i],
+                                    &mail->header_match_value, 1);
                }
        }
 }
 
-void index_mail_parse_header_init(struct index_mail *mail,
-                                 const char *const headers[])
+static void index_mail_parse_finish_imap_envelope(struct index_mail *mail)
 {
-       struct cached_header **data;
-       size_t i, size;
-       int cmp;
-
-       if (mail->data.header_data == NULL)
-               mail->data.header_data = str_new(mail->pool, 4096);
-
-       data = buffer_get_modifyable_data(mail->data.headers, &size);
-       size /= sizeof(struct cached_header *);
+       string_t *str;
 
-       mail->data.parsing_count = 0;
-       if (headers == NULL) {
-               /* parsing all headers */
-               for (i = 0; i < size; i++) {
-                       if (!data[i]->fully_saved) {
-                               data[i]->parsing = TRUE;
-                               mail->data.parsing_count++;
-                       }
-               }
-       } else {
-               t_push();
-               headers = sort_array(headers);
-               for (i = 0; i < size && *headers != NULL;) {
-                       cmp = strcasecmp(*headers, data[i]->name);
-                       if (cmp <= 0) {
-                               if (cmp == 0) {
-                                       if (!data[i]->fully_saved) {
-                                               data[i]->parsing = TRUE;
-                                               mail->data.parsing_count++;
-                                       }
-                                       i++;
-                               }
-                               headers++;
-                       } else {
-                               i++;
-                       }
-               }
-               t_pop();
-       }
+       str = str_new(mail->pool, 256);
+       imap_envelope_write_part_data(mail->data.envelope_data, str);
+       mail->data.envelope = str_c(str);
 
-       if (mail->data.save_sent_date || mail->data.save_envelope) {
-               /* parse the whole header */
-               mail->data.parsing_count = -1;
-       }
+       mail_cache_add(mail->trans->cache_trans, mail->data.seq,
+                      MAIL_CACHE_ENVELOPE, str_data(str), str_len(str));
 }
 
 int index_mail_parse_header(struct message_part *part,
@@ -289,27 +163,24 @@ int index_mail_parse_header(struct message_part *part,
                            struct index_mail *mail)
 {
        struct index_mail_data *data = &mail->data;
-       struct cached_header *cached_hdr;
+       enum mail_cache_decision_type decision;
+       const char *cache_field_name;
+       unsigned int field_idx;
        int timezone;
 
-       if (data->bodystructure_header_parse)
-               imap_bodystructure_parse_header(mail->pool, part, hdr);
+        data->parse_line_num++;
 
-       if (part != NULL && part->parent != NULL)
-               return FALSE;
+       if (data->save_bodystructure_header) {
+               i_assert(part != NULL);
+               imap_bodystructure_parse_header(mail->pool, part, hdr);
+       }
 
        if (data->save_envelope) {
                imap_envelope_parse_header(mail->pool,
                                           &data->envelope_data, hdr);
 
-               if (hdr == NULL) {
-                       /* finalize the envelope */
-                       string_t *str;
-
-                       str = str_new(mail->pool, 256);
-                       imap_envelope_write_part_data(data->envelope_data, str);
-                       data->envelope = str_c(str);
-               }
+               if (hdr == NULL)
+                        index_mail_parse_finish_imap_envelope(mail);
        }
 
        if (hdr == NULL) {
@@ -325,8 +196,8 @@ int index_mail_parse_header(struct message_part *part,
                                       MAIL_CACHE_SENT_DATE, &data->sent_date,
                                       sizeof(data->sent_date));
                }
-
-               cached_headers_mark_fully_saved(mail);
+               index_mail_parse_header_finish(mail);
+                data->save_bodystructure_header = FALSE;
                return TRUE;
        }
 
@@ -347,444 +218,319 @@ int index_mail_parse_header(struct message_part *part,
                }
        }
 
-       cached_hdr = cached_header_find(mail, hdr->name, NULL);
-       if (cached_hdr != NULL && !cached_hdr->fully_saved) {
-               if (data->header_stream == NULL) {
-                       if (!hdr->continued) {
-                               str_append(data->header_data, hdr->name);
-                               str_append(data->header_data, ": ");
-                       }
-                       if (cached_hdr->value_idx == 0) {
-                               cached_hdr->value_idx =
-                                       str_len(data->header_data);
-                       }
-                       str_append_n(data->header_data,
-                                    hdr->value, hdr->value_len);
-                       if (!hdr->no_newline)
-                               str_append(data->header_data, "\n");
-               } else {
-                       /* it's already in header_data. it means it's fully
-                          cached and we don't have to worry about other than
-                          the first header line. */
-                       i_assert(cached_hdr->value_idx == 0);
-                       cached_hdr->value_idx = data->header_stream->v_offset;
-
-                       cached_hdr->fully_saved = TRUE;
-                       if (--data->parsing_count == 0)
-                               return FALSE;
-               }
+       if (!hdr->continued) {
+               t_push();
+               cache_field_name = t_strconcat("hdr.", hdr->name, NULL);
+               data->parse_line.field_idx =
+                       mail_cache_register_lookup(mail->ibox->cache,
+                                                  cache_field_name);
+               t_pop();
        }
-       return TRUE;
-}
-
-static void index_mail_parse_header_cb(struct message_part *part,
-                                      struct message_header_line *hdr,
-                                      void *context)
-{
-       struct index_mail *mail = context;
-
-       (void)index_mail_parse_header(part, hdr, mail);
-}
-
-static int index_mail_can_cache_headers(struct index_mail *mail)
-{
-       enum mail_cache_field field;
-
-       field = mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT-1];
-       if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
-                                   field) != 0)
-               return FALSE; /* all headers used */
+       field_idx = data->parse_line.field_idx;
 
-       /* FIXME: add some smart checks here. we don't necessarily want to
-          cache everything.. */
-       return TRUE;
-}
-
-static void cached_headers_clear_values(struct index_mail *mail)
-{
-       struct cached_header **data;
-       size_t i, size, clear_offset;
-
-       clear_offset = str_len(mail->data.header_data);
-       data = buffer_get_modifyable_data(mail->data.headers, &size);
-       size /= sizeof(struct cached_header *);
+       if (field_idx == (unsigned int)-1) {
+               /* we don't want this field */
+               return TRUE;
+       }
 
-       for (i = 0; i < size; i++) {
-               if (data[i]->value_idx >= clear_offset)
-                       data[i]->value_idx = 0;
+       if (!hdr->continued) {
+               decision = mail_cache_field_get_decision(mail->ibox->cache,
+                                                        field_idx);
+               data->parse_line.cache =
+                       (decision & ~MAIL_CACHE_DECISION_FORCED) !=
+                       MAIL_CACHE_DECISION_NO;
+               if (data->parse_line.cache &&
+                   mail_cache_field_exists(mail->trans->cache_view,
+                                           data->seq, field_idx) > 0) {
+                       /* already cached */
+                       data->parse_line.cache = FALSE;
+               }
        }
-}
 
-static int parse_cached_headers(struct index_mail *mail, int idx)
-{
-       struct index_mail_data *data = &mail->data;
-       struct message_header_parser_ctx *hdr_ctx;
-       struct message_header_line *hdr;
-       struct istream *istream;
-       const char *const *idx_headers;
-       string_t *str;
+       if (!data->parse_line.cache) {
+               const uint8_t *match;
+               size_t size;
 
-       if (idx < data->header_data_cached) {
-               /* it's already in header_data. */
-               istream = i_stream_create_from_data(mail->pool,
-                                                   str_data(data->header_data),
-                                                   str_len(data->header_data));
-               /* we might be parsing a bit more.. */
-               idx = data->header_data_cached-1;
-               data->header_stream = istream;
-       } else {
-               str = str_new(mail->pool, 32);
-               if (!mail_cache_lookup_string_field(
-                               mail->trans->cache_view, str, data->seq,
-                               mail_cache_header_fields[idx])) {
-                       /* broken - we expected the header to exist */
-                       return FALSE;
+               match = buffer_get_data(mail->header_match, &size);
+               if (field_idx >= size ||
+                   match[field_idx] != mail->header_match_value) {
+                       /* we don't need to do anything with this header */
+                       return TRUE;
                }
-
-               data->header_data_cached_partial = TRUE;
-               istream = i_stream_create_from_data(mail->pool, str_data(str),
-                                                   str_len(str));
        }
 
-       idx_headers = mail_cache_get_header_fields(mail->trans->cache_view,
-                                                  idx);
-       if (idx_headers == NULL) {
-               mail_cache_set_corrupted(mail->ibox->cache,
-                       "Headers %d names not found", idx);
-               return FALSE;
+       if (!hdr->continued) {
+               data->parse_line.start_pos = str_len(mail->header_data);
+               data->parse_line.line_num = data->parse_line_num;
+               str_append(mail->header_data, hdr->name);
+               str_append(mail->header_data, ": ");
        }
-
-       index_mail_parse_header_init(mail, idx_headers);
-
-       hdr_ctx = message_parse_header_init(istream, NULL, TRUE);
-       while ((hdr = message_parse_header_next(hdr_ctx)) != NULL) {
-               if (!index_mail_parse_header(NULL, hdr, mail))
-                       break;
+       str_append_n(mail->header_data, hdr->value, hdr->value_len);
+       if (!hdr->no_newline)
+               str_append(mail->header_data, "\n");
+       if (!hdr->continues) {
+               data->parse_line.end_pos = str_len(mail->header_data);
+               buffer_append(mail->header_lines, &data->parse_line,
+                             sizeof(data->parse_line));
        }
-       message_parse_header_deinit(hdr_ctx);
-       index_mail_parse_header(NULL, NULL, mail);
-
-       data->header_stream = NULL;
-       i_stream_unref(istream);
-
        return TRUE;
 }
 
-static void trash_partial_headers(struct index_mail *mail)
+static void
+index_mail_parse_header_cb(struct message_part *part,
+                          struct message_header_line *hdr, void *context)
 {
-       struct index_mail_data *data = &mail->data;
-
-       data->header_data_cached_partial = FALSE;
-       data->header_data_cached = data->header_data_cached_contiguous;
+       struct index_mail *mail = context;
 
-       str_truncate(data->header_data, data->header_data_uncached_offset);
-       cached_headers_clear_values(mail);
+       (void)index_mail_parse_header(part, hdr, mail);
 }
 
 int index_mail_parse_headers(struct index_mail *mail)
 {
        struct index_mail_data *data = &mail->data;
-       const char *const *headers;
-       int idx, max;
 
        if (data->stream == NULL) {
                if (mail->mail.get_stream(&mail->mail, NULL, NULL) == NULL)
                        return FALSE;
        }
 
-       if (mail->data.header_data == NULL)
-               mail->data.header_data = str_new(mail->pool, 4096);
-
-       if (!data->header_fully_parsed && index_mail_can_cache_headers(mail)) {
-               if (data->header_data_cached_partial) {
-                       /* too difficult to handle efficiently, trash it */
-                        trash_partial_headers(mail);
-               }
-
-               /* add all cached headers to beginning of header_data */
-                idx = data->header_data_cached; max = idx-1;
-               for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
-                       if (!mail_cache_lookup_string_field(
-                               mail->trans->cache_view, mail->data.header_data,
-                               mail->data.seq, mail_cache_header_fields[idx]))
-                               continue;
-
-                       max = idx;
-               }
-               data->header_data_cached = max+1;
-               data->header_data_uncached_offset =
-                       str_len(mail->data.header_data);
-
-               /* make sure we cache everything */
-               for (idx = MAIL_CACHE_HEADERS_COUNT-1; idx >= 0; idx--) {
-                       headers = mail_cache_get_header_fields(
-                                       mail->trans->cache_view, idx);
-                       if (headers != NULL)
-                               break;
-               }
-
-               if (headers != NULL) {
-                       while (*headers != NULL) {
-                               cached_header_add(mail, *headers);
-                               headers++;
-                       }
-               }
-
-               if (max >= 0) {
-                       /* now we'll have to set value_idx for all headers that
-                          are already cached */
-                       if (!parse_cached_headers(mail, max)) {
-                               /* FIXME: handle better */
-                               return FALSE;
-                       }
-               }
-
-               /* it's possible that we're parsing headers without wanting
-                  to save any of them */
-               if (buffer_get_used_size(data->headers) != 0) {
-                       data->header_save = TRUE;
-                       data->header_save_idx = idx;
-               }
-       }
-
-       data->bodystructure_header_parse = data->bodystructure_header_want;
        index_mail_parse_header_init(mail, NULL);
 
-       if (data->parts != NULL || data->parser_ctx != NULL) {
-               message_parse_header(data->parts, data->stream, &data->hdr_size,
-                                    index_mail_parse_header_cb, mail);
-       } else {
+       if (data->parts == NULL && data->parser_ctx == NULL) {
+               /* initialize bodystructure parsing in case we read the whole
+                  message. */
                data->parser_ctx =
                        message_parser_init(mail->pool, data->stream);
                message_parser_parse_header(data->parser_ctx, &data->hdr_size,
                                            index_mail_parse_header_cb, mail);
+       } else {
+               /* just read the header */
+               message_parse_header(data->parts, data->stream, &data->hdr_size,
+                                    index_mail_parse_header_cb, mail);
        }
        data->hdr_size_set = TRUE;
+       data->parse_header = FALSE;
+
+       return TRUE;
+}
 
-       if (data->bodystructure_header_want) {
-               data->bodystructure_header_want = FALSE;
-               data->bodystructure_header_parse = FALSE;
-               data->bodystructure_header_parsed = TRUE;
+static void
+imap_envelope_parse_callback(struct message_part *part __attr_unused__,
+                            struct message_header_line *hdr, void *context)
+{
+       struct index_mail *mail = context;
+
+       imap_envelope_parse_header(mail->pool, &mail->data.envelope_data, hdr);
+
+       if (hdr == NULL)
+               index_mail_parse_finish_imap_envelope(mail);
+}
+
+void index_mail_headers_get_envelope(struct index_mail *mail)
+{
+       struct mailbox_header_lookup_ctx *header_ctx;
+       struct istream *stream;
+
+       header_ctx = mailbox_header_lookup_init(&mail->ibox->box,
+                                               imap_envelope_headers);
+       stream = mail->mail.get_headers(&mail->mail, header_ctx);
+       if (mail->data.envelope == NULL) {
+               /* we got the headers from cache - parse them to get the
+                  envelope */
+               message_parse_header(NULL, stream, NULL,
+                                    imap_envelope_parse_callback, mail);
+               mail->data.save_envelope = FALSE;
        }
+       mailbox_header_lookup_deinit(header_ctx);
+}
 
-       data->parse_header = FALSE;
-       data->header_fully_parsed = TRUE;
+static unsigned int
+get_header_field_idx(struct index_mailbox *ibox, const char *field)
+{
+       struct mail_cache_field header_field = {
+               NULL, 0, MAIL_CACHE_FIELD_HEADER, 0,
+               MAIL_CACHE_DECISION_TEMP, 0, 0
+       };
+       const char *cache_field_name;
+       unsigned int field_idx;
 
-       return TRUE;
+       t_push();
+       cache_field_name = t_strconcat("hdr.", field, NULL);
+       field_idx = mail_cache_register_lookup(ibox->cache, cache_field_name);
+       if (field_idx == (unsigned int)-1) {
+               header_field.name = cache_field_name;
+               mail_cache_register_fields(ibox->cache, &header_field, 1);
+               field_idx = header_field.idx;
+       }
+       t_pop();
+       return field_idx;
 }
 
 const char *index_mail_get_header(struct mail *_mail, const char *field)
 {
-       struct index_mail *mail = (struct index_mail *) _mail;
-       struct cached_header *hdr;
-       const unsigned char *start, *end, *p;
-       const char *arr[2];
-       int idx;
-
-       hdr = cached_header_add(mail, field);
-       if (!hdr->fully_saved) {
-               if (mail->data.parse_header) {
-                       /* we need to parse header anyway */
-                       idx = -1;
-               } else {
-                       arr[0] = field; arr[1] = NULL;
-                       idx = mail_find_wanted_headers(mail, arr);
-
-                       if (idx >= 0) {
-                               if (!parse_cached_headers(mail, idx)) {
-                                       /* broken cache, parse again */
-                                       idx = -1;
-                               }
-                       }
-               }
+       struct index_mail *mail = (struct index_mail *)_mail;
+       const unsigned char *data;
+       unsigned int field_idx;
+       string_t *dest;
+       size_t i, len, value_pos;
+       int ret;
 
-               if (idx < 0) {
-                       if (!index_mail_parse_headers(mail))
-                               return NULL;
+       field_idx = get_header_field_idx(mail->ibox, field);
 
-                       /* might have been moved in memory, get it again */
-                       hdr = cached_header_find(mail, field, NULL);
-               }
-       }
+       dest = str_new(mail->pool, 128);
+       if (mail_cache_lookup_headers(mail->trans->cache_view, dest,
+                                     mail->data.seq, &field_idx, 1) <= 0) {
+               /* not in cache / error */
+               if (index_mail_parse_headers(mail) < 0)
+                       return NULL;
 
-       if (hdr->value_idx == 0)
-               return NULL;
+               ret = mail_cache_lookup_headers(mail->trans->cache_view, dest,
+                                               mail->data.seq, &field_idx, 1);
+               i_assert(ret != 0);
+               if (ret < 0)
+                       return NULL;
+       }
 
-       start = str_data(mail->data.header_data);
-       end = start + str_len(mail->data.header_data);
-       start += hdr->value_idx;
-       for (p = start; p != end; p++) {
-               if (*p == '\n') {
-                       if (p+1 == end || (p[1] != ' ' && p[1] != '\t'))
+       /* cached. skip "header name: " in dest. */
+       data = str_data(dest);
+       len = str_len(dest);
+       for (i = 0; i < len; i++) {
+               if (data[i] == ':') {
+                       if (i+1 != len && data[++i] == ' ') i++;
+                       break;
+               }
+       }
+       value_pos = i;
+
+       /* return only the first field in case there's multiple. */
+       for (; i < len; i++) {
+               if (data[i] == '\n') {
+                       if (i+1 == len ||
+                           (data[i+1] != ' ' && data[i+1] != '\t')) {
+                               buffer_set_used_size(dest, i);
                                break;
+                       }
                }
        }
 
-       return t_strdup_until(start, p);
+       return str_c(dest) + value_pos;
 }
 
-struct istream *index_mail_get_headers(struct mail *_mail,
-                                      const char *const minimum_fields[])
+static void header_cache_callback(struct message_header_line *hdr,
+                                 int matched __attr_unused__, void *context)
 {
-       struct index_mail *mail = (struct index_mail *) _mail;
-       struct index_mail_data *data = &mail->data;
-       struct cached_header *hdr;
-       const char *const *tmp;
-       int i, idx, all_saved;
-
-       i_assert(*minimum_fields != NULL);
-
-       if (data->header_data == NULL)
-               data->header_data = str_new(mail->pool, 4096);
-
-       idx = mail_find_wanted_headers(mail, minimum_fields);
-       if (idx >= 0) {
-               /* copy from cache to header_data */
-               if (data->header_data_cached_partial) {
-                       /* Some headers may already partially be in
-                          header_data, we don't want them twice */
-                       trash_partial_headers(mail);
-               }
-               for (i = data->header_data_cached; i <= idx; i++) {
-                       (void)mail_cache_lookup_string_field(
-                               mail->trans->cache_view, data->header_data,
-                               data->seq, mail_cache_header_fields[i]);
-               }
-               data->header_data_cached = idx+1;
-               data->header_data_uncached_offset = str_len(data->header_data);
-               data->header_data_cached_contiguous = idx+1;
-       } else {
-               /* it's not cached yet - see if we have them parsed */
-               all_saved = TRUE;
-               for (tmp = minimum_fields; *tmp != NULL; tmp++) {
-                       hdr = cached_header_add(mail, *tmp);
-                       if (!hdr->fully_saved)
-                               all_saved = FALSE;
-               }
-
-               if (!all_saved) {
-                       if (!index_mail_parse_headers(mail))
-                               return NULL;
-               }
-       }
+       struct index_mail *mail = context;
 
-       return i_stream_create_from_data(mail->pool,
-                                        str_data(data->header_data),
-                                        str_len(data->header_data));
+       (void)index_mail_parse_header(NULL, hdr, mail);
 }
 
-void index_mail_headers_init(struct index_mail *mail)
+struct istream *
+index_mail_get_headers(struct mail *_mail,
+                      struct mailbox_header_lookup_ctx *_headers)
 {
-       struct mail_cache_view *cache_view = mail->trans->cache_view;
-       int idx = -2, idx2 = -2;
+       struct index_mail *mail = (struct index_mail *)_mail;
+       struct index_header_lookup_ctx *headers =
+               (struct index_header_lookup_ctx *)_headers;
+       string_t *dest;
+
+       dest = str_new(mail->pool, 256);
+       if (mail_cache_lookup_headers(mail->trans->cache_view, dest,
+                                     mail->data.seq, headers->idx,
+                                     headers->count) > 0) {
+               return i_stream_create_from_data(mail->pool,
+                                                str_data(dest), str_len(dest));
+       }
+       /* not in cache / error */
+       p_free(mail->pool, dest);
+
+       if (mail->data.stream == NULL) {
+               if (mail->mail.get_stream(&mail->mail, NULL, NULL) == NULL)
+                       return FALSE;
+       }
 
-       if (mail->wanted_headers != NULL && *mail->wanted_headers != NULL)
-               idx = find_wanted_headers(cache_view, mail->wanted_headers);
+       if (mail->data.filter_stream != NULL)
+               i_stream_unref(mail->data.filter_stream);
 
-       if (idx != -1 && (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE))
-               idx2 = find_wanted_headers(cache_view, imap_envelope_headers);
+       index_mail_parse_header_init(mail, _headers);
+       mail->data.filter_stream =
+               i_stream_create_header_filter(mail->pool, mail->data.stream,
+                                             FALSE,
+                                             headers->name, headers->count,
+                                             header_cache_callback, mail);
+       return mail->data.filter_stream;
+}
 
-       mail->wanted_headers_idx = idx == -1 || idx2 == -1 ? -1 :
-               idx > idx2 ? idx : idx2;
+static int strcasecmp_p(const void *p1, const void *p2)
+{
+       const char *const *s1 = p1, *const *s2 = p2;
+
+       return strcasecmp(*s1, *s2);
 }
 
-void index_mail_headers_init_next(struct index_mail *mail)
+struct mailbox_header_lookup_ctx *
+index_header_lookup_init(struct mailbox *box, const char *const headers[])
 {
-       struct index_mail_data *data = &mail->data;
-       const char *const *tmp;
-       int idx;
+       struct index_mailbox *ibox = (struct index_mailbox *)box;
+       struct mail_cache_field *fields, header_field = {
+               NULL, 0, MAIL_CACHE_FIELD_HEADER, 0,
+               MAIL_CACHE_DECISION_TEMP, 0, 0
+       };
+       struct index_header_lookup_ctx *ctx;
+       const char *const *name;
+       const char **sorted_headers;
+       buffer_t *buf;
+       pool_t pool;
+       size_t i, size;
 
-       mail->data.headers = buffer_create_dynamic(mail->pool, 64, (size_t)-1);
+       for (size = 0, name = headers; *name != NULL; name++)
+               size++;
 
-       idx = mail->wanted_headers_idx;
-       if (mail->wanted_headers != NULL) {
-               const char *const *tmp;
+       t_push();
 
-               for (tmp = mail->wanted_headers; *tmp != NULL; tmp++)
-                       cached_header_add(mail, *tmp);
+       if (size > 0) {
+               /* headers need to be sorted for filter stream. */
+               sorted_headers = t_new(const char *, size);
+               memcpy(sorted_headers, headers, size * sizeof(*sorted_headers));
+               qsort(sorted_headers, size, sizeof(*sorted_headers),
+                     strcasecmp_p);
+               headers = sorted_headers;
        }
 
-       if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) {
-               for (tmp = imap_envelope_headers; *tmp != NULL; tmp++)
-                       cached_header_add(mail, *tmp);
-       } else if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
-                  data->sent_date.time == (time_t)-1) {
-               cached_header_add(mail, "Date");
-               if (idx != -1) {
-                       /* see if it's cached */
-                       const char *headers[] = { "Date", NULL };
-                       idx = mail_find_wanted_headers(mail, headers);
-               }
+       buf = buffer_create_dynamic(pool_datastack_create(), 128, (size_t)-1);
+       for (i = 0; i < size; i++) {
+               header_field.name = t_strconcat("hdr.", headers[i], NULL);
+               buffer_append(buf, &header_field, sizeof(header_field));
        }
 
-       /* See if we're going to have to parse the header */
-       if (idx != -2) {
-               if (idx >= 0) {
-                       for (; idx < MAIL_CACHE_HEADERS_COUNT; idx++) {
-                               if (mail_cache_field_exists(
-                                       mail->trans->cache_view,
-                                       data->seq,
-                                       mail_cache_header_fields[idx]) > 0)
-                                       break;
-                       }
-               }
-               if (idx < 0 || idx >= MAIL_CACHE_HEADERS_COUNT)
-                       data->parse_header = TRUE;
-       }
-}
+       fields = buffer_get_modifyable_data(buf, &size);
+       size /= sizeof(*fields);
+       mail_cache_register_fields(ibox->cache, fields, size);
 
-static int find_unused_header_idx(struct mail_cache_view *cache_view)
-{
-       int i;
+       pool = pool_alloconly_create("index_header_lookup_ctx", 256);
+       ctx = p_new(pool, struct index_header_lookup_ctx, 1);
+       ctx->ctx.box = box;
+       ctx->pool = pool;
+       ctx->count = size;
+
+       if (size > 0) {
+               ctx->idx = p_new(pool, unsigned int, size);
+               ctx->name = p_new(pool, const char *, size);
 
-       for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
-               if (mail_cache_get_header_fields(cache_view, i) == NULL)
-                       return i;
+               /* @UNSAFE */
+               for (i = 0; i < size; i++) {
+                       ctx->idx[i] = fields[i].idx;
+                       ctx->name[i] = p_strdup(pool, sorted_headers[i]);
+               }
        }
 
-       return -1;
+       t_pop();
+       return &ctx->ctx;
 }
 
-void index_mail_headers_close(struct index_mail *mail)
+void index_header_lookup_deinit(struct mailbox_header_lookup_ctx *_ctx)
 {
-       struct index_mail_data *data = &mail->data;
-       const char *str, *const *headers;
-       size_t len;
-       int idx;
-
-       if (!data->header_save)
-               return;
-
-       /* FIXME: this breaks if fetch_uid() and fetch/search are both
-          accessing headers from same message. index_mails should probably be
-          shared.. */
-       headers = cached_header_get_names(mail);
-       idx = find_wanted_headers(mail->trans->cache_view, headers);
-       if (idx >= 0) {
-               /* all headers found */
-               if (idx != mail->data.header_save_idx) {
-                       mail_cache_set_corrupted(mail->ibox->cache,
-                               "Duplicated header names list (%d and %d)",
-                               idx, mail->data.header_save_idx);
-               }
-       } else {
-               /* there's some new headers */
-               idx = find_unused_header_idx(mail->trans->cache_view);
-               if (idx < 0)
-                       return;
-
-               if (!mail_cache_set_header_fields(mail->trans->cache_trans,
-                                                 idx, headers))
-                       return;
-       }
-
-       str = str_c(mail->data.header_data) + data->header_data_uncached_offset;
-       len = str_len(mail->data.header_data) -
-               data->header_data_uncached_offset;
+       struct index_header_lookup_ctx *ctx =
+               (struct index_header_lookup_ctx *)_ctx;
 
-       if (idx >= data->header_data_cached) {
-               mail_cache_add(mail->trans->cache_trans, data->seq,
-                              mail_cache_header_fields[idx], str, len+1);
-       }
-       data->header_save = FALSE;
+       pool_unref(ctx->pool);
 }
index c5ef65f8672b7a0b889292c9b784a9a643ed5f92..a1ef8d5403f33543ee89ace255931ec277dafe0a 100644 (file)
 #include "index-storage.h"
 #include "index-mail.h"
 
+struct mail_cache_field cache_fields[MAIL_CACHE_FIELD_COUNT] = {
+       { "index.flags", 0, MAIL_CACHE_FIELD_BITMASK,
+         sizeof(uint32_t), 0, 0, 0 },
+       { "date.sent", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
+         sizeof(struct mail_sent_date), 0, 0, 0 },
+       { "date.received", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
+         sizeof(time_t), 0, 0, 0 },
+       { "size.virtual", 0, MAIL_CACHE_FIELD_FIXED_SIZE,
+         sizeof(uoff_t), 0, 0, 0 },
+       { "imap.body", 0, MAIL_CACHE_FIELD_STRING, 0, 0, 0, 0 },
+       { "imap.bodystructure", 0, MAIL_CACHE_FIELD_STRING, 0, 0, 0, 0 },
+       { "imap.envelope", 0, MAIL_CACHE_FIELD_STRING, 0, 0, 0, 0 },
+       { "mime.parts", 0, MAIL_CACHE_FIELD_VARIABLE_SIZE, 0, 0, 0, 0 },
+       { "mail.uid", 0, MAIL_CACHE_FIELD_STRING, 0, 0, 0, 0 }
+};
+
 static void index_mail_parse_body(struct index_mail *mail, int need_parts);
 
 static struct message_part *get_cached_parts(struct index_mail *mail)
@@ -25,8 +41,8 @@ static struct message_part *get_cached_parts(struct index_mail *mail)
        part_buf = buffer_create_dynamic(pool_datastack_create(),
                                         128, (size_t)-1);
        if (mail_cache_lookup_field(mail->trans->cache_view, part_buf,
-                                   mail->data.seq,
-                                   MAIL_CACHE_MESSAGEPART) <= 0) {
+                       mail->data.seq,
+                       cache_fields[MAIL_CACHE_MESSAGEPART].idx) <= 0) {
                t_pop();
                return NULL;
        }
@@ -56,13 +72,14 @@ static struct message_part *get_cached_parts(struct index_mail *mail)
 }
 
 const char *index_mail_get_cached_string(struct index_mail *mail,
-                                        enum mail_cache_field field)
+                                        enum index_cache_field field)
 {
        string_t *str;
 
        str = str_new(mail->pool, 32);
-       if (mail_cache_lookup_string_field(mail->trans->cache_view, str,
-                                          mail->data.seq, field) <= 0) {
+       if (mail_cache_lookup_field(mail->trans->cache_view, str,
+                                   mail->data.seq,
+                                   cache_fields[field].idx) <= 0) {
                p_free(mail->pool, str);
                return NULL;
        }
@@ -71,7 +88,7 @@ const char *index_mail_get_cached_string(struct index_mail *mail,
 }
 
 static int index_mail_get_fixed_field(struct index_mail *mail,
-                                     enum mail_cache_field field,
+                                     enum index_cache_field field,
                                      void *data, size_t data_size)
 {
        buffer_t *buf;
@@ -80,7 +97,8 @@ static int index_mail_get_fixed_field(struct index_mail *mail,
        t_push();
        buf = buffer_create_data(pool_datastack_create(), data, data_size);
        if (mail_cache_lookup_field(mail->trans->cache_view, buf,
-                                   mail->data.seq, field) <= 0) {
+                                   mail->data.seq,
+                                   cache_fields[field].idx) <= 0) {
                ret = FALSE;
        } else {
                i_assert(buffer_get_used_size(buf) == data_size);
@@ -92,11 +110,12 @@ static int index_mail_get_fixed_field(struct index_mail *mail,
 }
 
 uoff_t index_mail_get_cached_uoff_t(struct index_mail *mail,
-                                   enum mail_cache_field field)
+                                   enum index_cache_field field)
 {
        uoff_t uoff;
 
-       if (!index_mail_get_fixed_field(mail, field, &uoff, sizeof(uoff)))
+       if (!index_mail_get_fixed_field(mail, cache_fields[field].idx,
+                                       &uoff, sizeof(uoff)))
                uoff = (uoff_t)-1;
 
        return uoff;
@@ -207,7 +226,8 @@ time_t index_mail_get_date(struct mail *_mail, int *timezone)
                        }
                         data->sent_date.timezone = tz;
                        mail_cache_add(mail->trans->cache_trans, mail->data.seq,
-                                      MAIL_CACHE_SENT_DATE, &data->sent_date,
+                                      cache_fields[MAIL_CACHE_SENT_DATE].idx,
+                                      &data->sent_date,
                                       sizeof(data->sent_date));
                }
        }
@@ -258,9 +278,9 @@ uoff_t index_mail_get_size(struct mail *_mail)
        return data->size;
 }
 
-static void parse_bodystructure_header(struct message_part *part,
-                                      struct message_header_line *hdr,
-                                      void *context)
+static void parse_bodystructure_part_header(struct message_part *part,
+                                           struct message_header_line *hdr,
+                                           void *context)
 {
        pool_t pool = context;
 
@@ -281,11 +301,12 @@ static void index_mail_parse_body(struct index_mail *mail, int need_parts)
 
        i_stream_seek(data->stream, data->hdr_size.physical_size);
 
-       if (data->bodystructure_header_parsed) {
+       if (data->save_bodystructure_body) {
                /* bodystructure header is parsed, we want the body's mime
                   headers too */
+               i_assert(!data->save_bodystructure_header);
                message_parser_parse_body(data->parser_ctx,
-                                         parse_bodystructure_header,
+                                         parse_bodystructure_part_header,
                                          NULL, mail->pool);
        } else {
                message_parser_parse_body(data->parser_ctx, NULL, NULL, NULL);
@@ -307,7 +328,7 @@ static void index_mail_parse_body(struct index_mail *mail, int need_parts)
                }
 
                /* update cache_flags */
-               cache_flags =
+               /*FIXME:cache_flags =
                        mail_cache_get_record_flags(mail->trans->cache_view,
                                                    mail->data.seq);
                if (mail->mail.has_nuls)
@@ -317,16 +338,16 @@ static void index_mail_parse_body(struct index_mail *mail, int need_parts)
 
                (void)mail_cache_update_record_flags(mail->trans->cache_view,
                                                     mail->data.seq,
-                                                    cache_flags);
+                                                    cache_flags);*/
        }
 
        /* see if we want to cache the message part */
        if (mail_cache_field_exists(mail->trans->cache_view, mail->data.seq,
-                                   MAIL_CACHE_MESSAGEPART) != 0)
+                       cache_fields[MAIL_CACHE_MESSAGEPART].idx) != 0)
                return;
 
        decision = mail_cache_field_get_decision(mail->ibox->cache,
-                                                MAIL_CACHE_MESSAGEPART);
+                               cache_fields[MAIL_CACHE_MESSAGEPART].idx);
        if (decision != (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED) &&
            (decision != MAIL_CACHE_DECISION_NO || need_parts ||
             (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) != 0)) {
@@ -337,7 +358,8 @@ static void index_mail_parse_body(struct index_mail *mail, int need_parts)
 
                buf_data = buffer_get_data(buffer, &buf_size);
                mail_cache_add(mail->trans->cache_trans, mail->data.seq,
-                              MAIL_CACHE_MESSAGEPART, buf_data, buf_size);
+                              cache_fields[MAIL_CACHE_MESSAGEPART].idx,
+                              buf_data, buf_size);
                t_pop();
        }
 }
@@ -378,15 +400,17 @@ struct istream *index_mail_init_stream(struct index_mail *_mail,
 }
 
 static void index_mail_parse_bodystructure(struct index_mail *mail,
-                                          enum mail_cache_field field)
+                                          enum index_cache_field field)
 {
        struct index_mail_data *data = &mail->data;
        enum mail_cache_decision_type dec;
        string_t *str;
        int bodystructure_cached = FALSE;
 
-       if (!data->bodystructure_header_parsed) {
-               data->bodystructure_header_want = TRUE;
+       if (data->save_bodystructure_header || !data->save_bodystructure_body) {
+               /* we haven't parsed the header yet */
+               data->save_bodystructure_header = TRUE;
+               data->save_bodystructure_body = TRUE;
                if (!index_mail_parse_headers(mail))
                        return;
        }
@@ -394,18 +418,18 @@ static void index_mail_parse_bodystructure(struct index_mail *mail,
        if (data->parts != NULL) {
                i_assert(data->parts->next == NULL);
                message_parse_from_parts(data->parts->children, data->stream,
-                                        parse_bodystructure_header,
+                                        parse_bodystructure_part_header,
                                         mail->pool);
        } else {
                index_mail_parse_body(mail, FALSE);
        }
 
        dec = mail_cache_field_get_decision(mail->ibox->cache,
-                                           MAIL_CACHE_BODYSTRUCTURE);
+                               cache_fields[MAIL_CACHE_BODYSTRUCTURE].idx);
        if (field == MAIL_CACHE_BODYSTRUCTURE ||
            ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
             mail_cache_field_exists(mail->trans->cache_view, data->seq,
-                                     MAIL_CACHE_BODYSTRUCTURE)) == 0) {
+                       cache_fields[MAIL_CACHE_BODYSTRUCTURE].idx) == 0)) {
                str = str_new(mail->pool, 128);
                imap_bodystructure_write(data->parts, str, TRUE);
                data->bodystructure = str_c(str);
@@ -413,17 +437,18 @@ static void index_mail_parse_bodystructure(struct index_mail *mail,
                if (dec !=
                    (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
                        mail_cache_add(mail->trans->cache_trans, data->seq,
-                                      MAIL_CACHE_BODYSTRUCTURE,
-                                      str_c(str), str_len(str)+1);
+                               cache_fields[MAIL_CACHE_BODYSTRUCTURE].idx,
+                               str_c(str), str_len(str)+1);
                        bodystructure_cached = TRUE;
                }
        }
 
-       dec = mail_cache_field_get_decision(mail->ibox->cache, MAIL_CACHE_BODY);
+       dec = mail_cache_field_get_decision(mail->ibox->cache,
+                                           cache_fields[MAIL_CACHE_BODY].idx);
        if (field == MAIL_CACHE_BODY ||
            ((dec & ~MAIL_CACHE_DECISION_FORCED) != MAIL_CACHE_DECISION_NO &&
             mail_cache_field_exists(mail->trans->cache_view, data->seq,
-                                    MAIL_CACHE_BODY)) == 0) {
+                                    cache_fields[MAIL_CACHE_BODY].idx) == 0)) {
                str = str_new(mail->pool, 128);
                imap_bodystructure_write(data->parts, str, FALSE);
                data->body = str_c(str);
@@ -431,7 +456,7 @@ static void index_mail_parse_bodystructure(struct index_mail *mail,
                if (!bodystructure_cached && dec !=
                    (MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED)) {
                        mail_cache_add(mail->trans->cache_trans, data->seq,
-                                      MAIL_CACHE_BODY,
+                                      cache_fields[MAIL_CACHE_BODY].idx,
                                       str_c(str), str_len(str)+1);
                }
        }
@@ -455,16 +480,16 @@ const char *index_mail_get_special(struct mail *_mail,
                      depending on what we want cached */
 
                str = str_new(mail->pool, 128);
-               if (mail_cache_lookup_string_field(mail->trans->cache_view,
-                                                  str, mail->data.seq,
-                                                  MAIL_CACHE_BODY) > 0) {
+               if (mail_cache_lookup_field(mail->trans->cache_view, str,
+                               mail->data.seq,
+                               cache_fields[MAIL_CACHE_BODY].idx) > 0) {
                        data->body = str_c(str);
                        return data->body;
                }
-               if (mail_cache_lookup_string_field(mail->trans->cache_view,
-                                       str, mail->data.seq,
-                                       MAIL_CACHE_BODYSTRUCTURE) > 0) {
-                       data->bodystructure = str_c(str);
+               if (mail_cache_lookup_field(mail->trans->cache_view, str,
+                             mail->data.seq,
+                             cache_fields[MAIL_CACHE_BODYSTRUCTURE].idx) > 0) {
+                       data->bodystructure = p_strdup(mail->pool, str_c(str));
                        str_truncate(str, 0);
 
                        if (imap_body_parse_from_bodystructure(
@@ -474,10 +499,10 @@ const char *index_mail_get_special(struct mail *_mail,
                        }
 
                        /* broken, continue.. */
-                       data->bodystructure = NULL;
                        mail_cache_set_corrupted(mail->ibox->cache,
                                "Corrupted BODYSTRUCTURE for mail %u",
                                mail->mail.uid);
+                       data->bodystructure = NULL;
                }
                p_free(mail->pool, str);
 
@@ -488,9 +513,9 @@ const char *index_mail_get_special(struct mail *_mail,
                        return data->bodystructure;
 
                str = str_new(mail->pool, 128);
-               if (mail_cache_lookup_string_field(mail->trans->cache_view,
-                                       str, mail->data.seq,
-                                       MAIL_CACHE_BODYSTRUCTURE) > 0) {
+               if (mail_cache_lookup_field(mail->trans->cache_view, str,
+                             mail->data.seq,
+                             cache_fields[MAIL_CACHE_BODYSTRUCTURE].idx) > 0) {
                        data->bodystructure = str_c(str);
                        return data->bodystructure;
                }
@@ -499,11 +524,8 @@ const char *index_mail_get_special(struct mail *_mail,
                index_mail_parse_bodystructure(mail, MAIL_CACHE_BODYSTRUCTURE);
                return data->bodystructure;
        case MAIL_FETCH_IMAP_ENVELOPE:
-               if (data->envelope != NULL)
-                       return data->envelope;
-
-               data->save_envelope = TRUE;
-               (void)_mail->get_header(_mail, "Date");
+               if (data->envelope == NULL)
+                       index_mail_headers_get_envelope(mail);
                return data->envelope;
        case MAIL_FETCH_FROM_ENVELOPE:
                return NULL;
@@ -523,8 +545,10 @@ const char *index_mail_get_special(struct mail *_mail,
 void index_mail_init(struct index_transaction_context *t,
                     struct index_mail *mail,
                     enum mail_fetch_field wanted_fields,
-                    const char *const wanted_headers[])
+                    struct mailbox_header_lookup_ctx *_wanted_headers)
 {
+       struct index_header_lookup_ctx *wanted_headers =
+               (struct index_header_lookup_ctx *)_wanted_headers;
        const struct mail_index_header *hdr;
        int ret;
 
@@ -541,16 +565,14 @@ void index_mail_init(struct index_transaction_context *t,
        mail->trans = t;
        mail->wanted_fields = wanted_fields;
        mail->wanted_headers = wanted_headers;
-
-       index_mail_headers_init(mail);
 }
 
 static void index_mail_close(struct index_mail *mail)
 {
        if (mail->data.stream != NULL)
                i_stream_unref(mail->data.stream);
-
-       index_mail_headers_close(mail);
+       if (mail->data.filter_stream != NULL)
+               i_stream_unref(mail->data.filter_stream);
 }
 
 int index_mail_next(struct index_mail *mail, uint32_t seq)
@@ -564,13 +586,12 @@ int index_mail_next(struct index_mail *mail, uint32_t seq)
                return -1;
        }
 
-       t_push();
-
        index_mail_close(mail);
+
        memset(data, 0, sizeof(*data));
        p_clear(mail->pool);
 
-       cache_flags = mail_cache_get_record_flags(mail->trans->cache_view, seq);
+       cache_flags = 0;//FIXME:mail_cache_get_record_flags(mail->trans->cache_view, seq);
 
        mail->mail.seq = seq;
        mail->mail.uid = rec->uid;
@@ -583,6 +604,8 @@ int index_mail_next(struct index_mail *mail, uint32_t seq)
        data->size = (uoff_t)-1;
        data->received_date = data->sent_date.time = (time_t)-1;
 
+       t_push();
+
        /* if some wanted fields are cached, get them */
        if (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS)
                data->parts = get_cached_parts(mail);
@@ -611,20 +634,20 @@ int index_mail_next(struct index_mail *mail, uint32_t seq)
                        data->parts = get_cached_parts(mail);
                data->open_mail = TRUE;
                data->parse_header = data->parts == NULL;
-               data->bodystructure_header_want = TRUE;
+               data->save_bodystructure_header = TRUE;
+               data->save_bodystructure_body = TRUE;
        } else if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) &&
                   data->body == NULL && data->bodystructure == NULL) {
                if (data->parts == NULL)
                        data->parts = get_cached_parts(mail);
                data->open_mail = TRUE;
                data->parse_header = data->parts == NULL;
-                data->bodystructure_header_want = TRUE;
+               data->save_bodystructure_header = TRUE;
+               data->save_bodystructure_body = TRUE;
        } else if (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
                                          MAIL_FETCH_STREAM_BODY))
                data->open_mail = TRUE;
 
-        index_mail_headers_init_next(mail);
-
        if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
            data->sent_date.time == (time_t)-1)
                data->save_sent_date = TRUE;
@@ -641,9 +664,14 @@ void index_mail_deinit(struct index_mail *mail)
        if (mail->ibox->mail_deinit != NULL)
                 mail->ibox->mail_deinit(mail);
 
-       t_push();
        index_mail_close(mail);
-       t_pop();
+
+       if (mail->header_data != NULL)
+               buffer_free(mail->header_data);
+       if (mail->header_lines != NULL)
+               buffer_free(mail->header_lines);
+       if (mail->header_match != NULL)
+               buffer_free(mail->header_match);
 
        pool_unref(mail->pool);
        memset(mail, 0, sizeof(*mail));
index 391a16e4b6289596246a3f007947341c8adfd5de..9bb0544385da157042535369b6ec8b6116c69777 100644 (file)
@@ -5,6 +5,50 @@
 #include "mail-cache.h"
 #include "mail-storage-private.h"
 
+enum index_cache_field {
+       /* fixed size fields */
+       MAIL_CACHE_INDEX_FLAGS = 0,
+       MAIL_CACHE_SENT_DATE,
+       MAIL_CACHE_RECEIVED_DATE,
+       MAIL_CACHE_VIRTUAL_FULL_SIZE,
+
+       /* variable sized field */
+       MAIL_CACHE_BODY,
+       MAIL_CACHE_BODYSTRUCTURE,
+       MAIL_CACHE_ENVELOPE,
+       MAIL_CACHE_MESSAGEPART,
+       MAIL_CACHE_UID_STRING,
+
+       MAIL_CACHE_FIELD_COUNT
+};
+extern struct mail_cache_field cache_fields[MAIL_CACHE_FIELD_COUNT];
+
+enum mail_cache_record_flag {
+       /* If binary flags are set, it's not checked whether mail is
+          missing CRs. So this flag may be set as an optimization for
+          regular non-binary mails as well if it's known that it contains
+          valid CR+LF line breaks. */
+       MAIL_INDEX_FLAG_BINARY_HEADER           = 0x0001,
+       MAIL_INDEX_FLAG_BINARY_BODY             = 0x0002,
+
+       /* Mail header or body is known to contain NUL characters. */
+       MAIL_INDEX_FLAG_HAS_NULS                = 0x0004,
+       /* Mail header or body is known to not contain NUL characters. */
+       MAIL_INDEX_FLAG_HAS_NO_NULS             = 0x0008
+};
+
+struct mail_sent_date {
+       time_t time;
+       int32_t timezone;
+};
+
+struct index_mail_line {
+       unsigned int field_idx;
+       uint32_t start_pos, end_pos;
+       uint32_t line_num;
+       unsigned int cache:1;
+};
+
 struct message_header_line;
 
 struct index_mail_data {
@@ -13,13 +57,8 @@ struct index_mail_data {
        uoff_t size;
 
        struct mail_sent_date sent_date;
-
-       buffer_t *headers;
-       string_t *header_data;
-       int header_data_cached, header_data_cached_contiguous;
-       size_t header_data_uncached_offset;
-       struct istream *header_stream;
-       int header_save_idx;
+       struct index_mail_line parse_line;
+       uint32_t parse_line_num;
 
        struct message_part *parts;
        const char *envelope, *body, *bodystructure, *uid_string;
@@ -28,24 +67,20 @@ struct index_mail_data {
        uint32_t seq;
        const struct mail_index_record *rec;
 
-       struct istream *stream;
+       struct istream *stream, *filter_stream;
        struct message_size hdr_size, body_size;
        struct message_parser_ctx *parser_ctx;
        int parsing_count;
 
        unsigned int parse_header:1;
-       unsigned int bodystructure_header_want:1;
-       unsigned int bodystructure_header_parse:1;
-       unsigned int bodystructure_header_parsed:1;
        unsigned int save_envelope:1;
        unsigned int save_sent_date:1;
+       unsigned int save_bodystructure_header:1;
+       unsigned int save_bodystructure_body:1;
        unsigned int hdr_size_set:1;
        unsigned int body_size_set:1;
-       unsigned int deleted:1;
-       unsigned int header_data_cached_partial:1;
-       unsigned int header_fully_parsed:1;
-       unsigned int header_save:1;
        unsigned int open_mail:1;
+       unsigned int deleted:1;
 };
 
 struct index_mail {
@@ -55,37 +90,37 @@ struct index_mail {
        pool_t pool;
        struct index_mailbox *ibox;
        struct index_transaction_context *trans;
-       unsigned int expunge_counter;
-       buffer_t *header_buf;
        uint32_t uid_validity;
 
        enum mail_fetch_field wanted_fields;
-       const char *const *wanted_headers;
-       int wanted_headers_idx;
+       struct index_header_lookup_ctx *wanted_headers;
+
+       /* per-mail variables, here for performance reasons: */
+       string_t *header_data;
+       buffer_t *header_lines;
+       buffer_t *header_match;
+       uint8_t header_match_value;
 };
 
 void index_mail_init(struct index_transaction_context *t,
                     struct index_mail *mail,
                     enum mail_fetch_field wanted_fields,
-                    const char *const wanted_headers[]);
+                    struct mailbox_header_lookup_ctx *wanted_headers);
 int index_mail_next(struct index_mail *mail, uint32_t seq);
 void index_mail_deinit(struct index_mail *mail);
 
 void index_mail_parse_header_init(struct index_mail *mail,
-                                 const char *const headers[]);
+                                 struct mailbox_header_lookup_ctx *headers);
 int index_mail_parse_header(struct message_part *part,
                            struct message_header_line *hdr,
                            struct index_mail *mail);
-
 int index_mail_parse_headers(struct index_mail *mail);
-
-void index_mail_headers_init(struct index_mail *mail);
-void index_mail_headers_init_next(struct index_mail *mail);
-void index_mail_headers_close(struct index_mail *mail);
+void index_mail_headers_get_envelope(struct index_mail *mail);
 
 const char *index_mail_get_header(struct mail *_mail, const char *field);
-struct istream *index_mail_get_headers(struct mail *_mail,
-                                      const char *const minimum_fields[]);
+struct istream *
+index_mail_get_headers(struct mail *_mail,
+                      struct mailbox_header_lookup_ctx *headers);
 
 const struct mail_full_flags *index_mail_get_flags(struct mail *_mail);
 const struct message_part *index_mail_get_parts(struct mail *_mail);
@@ -104,9 +139,9 @@ int index_mail_update_flags(struct mail *mail,
 int index_mail_expunge(struct mail *mail);
 
 const char *index_mail_get_cached_string(struct index_mail *mail,
-                                        enum mail_cache_field field);
+                                        enum index_cache_field field);
 uoff_t index_mail_get_cached_uoff_t(struct index_mail *mail,
-                                   enum mail_cache_field field);
+                                   enum index_cache_field field);
 uoff_t index_mail_get_cached_virtual_size(struct index_mail *mail);
 time_t index_mail_get_cached_received_date(struct index_mail *mail);
 
index d7b7b433c616190fe1945606cff92ffd8844a212..888f8a7fd94aaeb8633d3ee32eca92b5a229f03e 100644 (file)
@@ -444,7 +444,7 @@ static void search_header(struct message_part *part,
        if (hdr->eoh)
                return;
 
-       index_mail_parse_header(part, hdr, &ctx->index_context->imail);
+       index_mail_parse_header(NULL, hdr, &ctx->index_context->imail);
 
        if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
                ctx->hdr = hdr;
@@ -482,6 +482,7 @@ static int search_arg_match_text(struct mail_search_arg *args,
                                 struct index_search_context *ctx)
 {
        struct istream *input;
+       struct mailbox_header_lookup_ctx *headers_ctx;
        const char *const *headers;
        int have_headers, have_body;
 
@@ -496,20 +497,33 @@ static int search_arg_match_text(struct mail_search_arg *args,
                if (have_body)
                        headers = NULL;
 
-               input = headers == NULL ?
-                       ctx->mail->get_stream(ctx->mail, NULL, NULL) :
-                       ctx->mail->get_headers(ctx->mail, headers);
-               if (input == NULL)
-                       return FALSE;
+               if (headers == NULL) {
+                       headers_ctx = NULL;
+                       input = ctx->mail->get_stream(ctx->mail, NULL, NULL);
+                       if (input == NULL)
+                               return FALSE;
+               } else {
+                       /* FIXME: do this once in init */
+                       headers_ctx =
+                               mailbox_header_lookup_init(&ctx->ibox->box,
+                                                          headers);
+                       input = ctx->mail->get_headers(ctx->mail, headers_ctx);
+                       if (input == NULL) {
+                               mailbox_header_lookup_deinit(headers_ctx);
+                               return FALSE;
+                       }
+               }
 
                memset(&hdr_ctx, 0, sizeof(hdr_ctx));
                hdr_ctx.index_context = ctx;
                hdr_ctx.custom_header = TRUE;
                hdr_ctx.args = args;
 
-               index_mail_parse_header_init(&ctx->imail, headers);
+               index_mail_parse_header_init(&ctx->imail, headers_ctx);
                message_parse_header(NULL, input, NULL,
                                     search_header, &hdr_ctx);
+               if (headers_ctx != NULL)
+                       mailbox_header_lookup_deinit(headers_ctx);
        } else {
                struct message_size hdr_size;
 
@@ -720,7 +734,7 @@ index_storage_search_init(struct mailbox_transaction_context *_t,
                          const char *charset, struct mail_search_arg *args,
                          const enum mail_sort_type *sort_program,
                          enum mail_fetch_field wanted_fields,
-                         const char *const wanted_headers[])
+                         struct mailbox_header_lookup_ctx *wanted_headers)
 {
        struct index_transaction_context *t =
                (struct index_transaction_context *)_t;
index 97f976032c52c51ae9d1e7e27357ce0d659c9161..c709b34c15f6238e411108e3c3c056f5b0c63551 100644 (file)
@@ -11,6 +11,8 @@
 #include <unistd.h>
 #include <sys/stat.h>
 
+#define DEFAULT_NEVER_CACHE_FIELDS "imap.envelope"
+
 /* How many seconds to keep index opened for reuse after it's been closed */
 #define INDEX_CACHE_TIMEOUT 10
 /* How many closed indexes to keep */
@@ -173,28 +175,9 @@ void index_storage_destroy_unrefed(void)
        destroy_unrefed(TRUE);
 }
 
-static void set_cache_fields(const char *fields,
-                            enum mail_cache_decision_type dest[32],
-                            enum mail_cache_decision_type dec)
+static void set_cache_decisions(const char *fields,
+                               enum mail_cache_decision_type dec)
 {
-       static enum mail_cache_field field_enums[] = {
-               MAIL_CACHE_SENT_DATE,
-               MAIL_CACHE_RECEIVED_DATE,
-               MAIL_CACHE_VIRTUAL_FULL_SIZE,
-               MAIL_CACHE_BODY,
-               MAIL_CACHE_BODYSTRUCTURE,
-               MAIL_CACHE_MESSAGEPART
-       };
-       static const char *field_names[] = {
-               "sent_date",
-               "received_date",
-               "virtual_size",
-               "body",
-               "bodystructure",
-               "messagepart",
-               NULL
-       };
-
        const char *const *arr;
        int i;
 
@@ -202,33 +185,34 @@ static void set_cache_fields(const char *fields,
                return;
 
        for (arr = t_strsplit_spaces(fields, " ,"); *arr != NULL; arr++) {
-               for (i = 0; field_names[i] != NULL; i++) {
-                       if (strcasecmp(field_names[i], *arr) == 0) {
-                               dest[field_enums[i]] = dec;
+               for (i = 0; i < MAIL_CACHE_FIELD_COUNT; i++) {
+                       if (strcasecmp(cache_fields[i].name, *arr) == 0) {
+                               cache_fields[i].decision = dec;
                                break;
                        }
                }
-               if (field_names[i] == NULL) {
+               if (i == MAIL_CACHE_FIELD_COUNT) {
                        i_error("Invalid cache field name '%s', ignoring ",
                                *arr);
                }
        }
 }
 
-static const enum mail_cache_decision_type *get_default_cache_decisions(void)
+static void index_cache_register_defaults(struct mail_cache *cache)
 {
-       static enum mail_cache_decision_type dec[32];
-       static int dec_set = FALSE;
-
-       if (dec_set)
-               return dec;
-
-       memset(dec, 0, sizeof(dec));
-       set_cache_fields(getenv("MAIL_CACHE_FIELDS"), dec,
-                        MAIL_CACHE_DECISION_TEMP);
-       set_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"), dec,
-                        MAIL_CACHE_DECISION_NO | MAIL_CACHE_DECISION_FORCED);
-       return dec;
+       const char *never_env;
+
+       never_env = getenv("MAIL_NEVER_CACHE_FIELDS");
+       if (never_env == NULL)
+               never_env = DEFAULT_NEVER_CACHE_FIELDS;
+
+       set_cache_decisions(getenv("MAIL_CACHE_FIELDS"),
+                           MAIL_CACHE_DECISION_TEMP);
+       set_cache_decisions(never_env, MAIL_CACHE_DECISION_NO |
+                           MAIL_CACHE_DECISION_FORCED);
+
+       mail_cache_register_fields(cache, cache_fields,
+                                  MAIL_CACHE_FIELD_COUNT);
 }
 
 void index_storage_lock_notify(struct index_mailbox *ibox,
@@ -327,8 +311,7 @@ index_storage_mailbox_init(struct index_storage *storage, struct mailbox *box,
                        break;
 
                ibox->cache = mail_index_get_cache(index);
-               mail_cache_set_defaults(ibox->cache,
-                                       get_default_cache_decisions());
+               index_cache_register_defaults(ibox->cache);
                ibox->view = mail_index_view_open(index);
                return ibox;
        } while (0);
index caa16952b245cfd14d266d0ccf29288f8d45db3a..6e4fe66a93dcd42e3f85137b2bff2c4f626de4b9 100644 (file)
@@ -181,6 +181,10 @@ index_storage_fetch(struct mailbox_transaction_context *t, uint32_t seq,
 int index_storage_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2,
                           uint32_t *seq1_r, uint32_t *seq2_r);
 
+struct mailbox_header_lookup_ctx *
+index_header_lookup_init(struct mailbox *box, const char *const headers[]);
+void index_header_lookup_deinit(struct mailbox_header_lookup_ctx *ctx);
+
 int index_storage_search_get_sorting(struct mailbox *box,
                                     enum mail_sort_type *sort_program);
 struct mail_search_context *
@@ -188,7 +192,7 @@ index_storage_search_init(struct mailbox_transaction_context *t,
                          const char *charset, struct mail_search_arg *args,
                          const enum mail_sort_type *sort_program,
                          enum mail_fetch_field wanted_fields,
-                         const char *const wanted_headers[]);
+                         struct mailbox_header_lookup_ctx *wanted_headers);
 int index_storage_search_deinit(struct mail_search_context *ctx);
 struct mail *index_storage_search_next(struct mail_search_context *ctx);
 
index 667158d2f583a6b6bf6e63585d1896521d609dc3..b78e29f21775bc0c52084b1a58ea6c3c123c29bd 100644 (file)
@@ -861,7 +861,9 @@ struct mailbox maildir_mailbox = {
        maildir_transaction_rollback,
        index_storage_fetch,
        index_storage_get_uids,
-        index_storage_search_get_sorting,
+       index_header_lookup_init,
+        index_header_lookup_deinit,
+       index_storage_search_get_sorting,
        index_storage_search_init,
        index_storage_search_deinit,
        index_storage_search_next,
index 243eea3b8799bc03b13807adc028caffa0a1761e..f5f23299e3e07c50c68d044618ed604d7136ed63 100644 (file)
@@ -130,9 +130,10 @@ static struct istream *mbox_mail_get_stream(struct mail *_mail,
                                                   offset, (uoff_t)-1);
                data->stream =
                        i_stream_create_header_filter(default_pool,
-                                                     raw_stream,
+                                                     raw_stream, TRUE,
                                                      mbox_hide_headers,
-                                                     mbox_hide_headers_count);
+                                                     mbox_hide_headers_count,
+                                                     NULL, NULL);
                i_stream_unref(raw_stream);
        }
 
index 0cc1cb31a912af79f350cf108fea1c3afa3143cf..5ef3614a98eb5e1f45406a4ecf7f7494c46e5b1a 100644 (file)
@@ -889,6 +889,8 @@ struct mailbox mbox_mailbox = {
        mbox_transaction_rollback,
        index_storage_fetch,
        index_storage_get_uids,
+       index_header_lookup_init,
+        index_header_lookup_deinit,
         index_storage_search_get_sorting,
        index_storage_search_init,
        index_storage_search_deinit,
index d34be8ff47197e5354e0c6f7ad9c2a18fc594ab4..e237089588fee3344bf56d8ac7da7970b5bf0bf4 100644 (file)
@@ -89,15 +89,19 @@ struct mailbox {
        int (*get_uids)(struct mailbox *box, uint32_t uid1, uint32_t uid2,
                        uint32_t *seq1_r, uint32_t *seq2_r);
 
+       struct mailbox_header_lookup_ctx *
+               (*header_lookup_init)(struct mailbox *box,
+                                     const char *const headers[]);
+       void (*header_lookup_deinit)(struct mailbox_header_lookup_ctx *ctx);
+
        int (*search_get_sorting)(struct mailbox *box,
                                  enum mail_sort_type *sort_program);
        struct mail_search_context *
-               (*search_init)(struct mailbox_transaction_context *t,
-                              const char *charset,
-                              struct mail_search_arg *args,
-                              const enum mail_sort_type *sort_program,
-                              enum mail_fetch_field wanted_fields,
-                              const char *const wanted_headers[]);
+       (*search_init)(struct mailbox_transaction_context *t,
+                      const char *charset, struct mail_search_arg *args,
+                      const enum mail_sort_type *sort_program,
+                      enum mail_fetch_field wanted_fields,
+                      struct mailbox_header_lookup_ctx *wanted_headers);
        int (*search_deinit)(struct mail_search_context *ctx);
        struct mail *(*search_next)(struct mail_search_context *ctx);
 
@@ -128,6 +132,10 @@ struct mailbox_sync_context {
        struct mailbox *box;
 };
 
+struct mailbox_header_lookup_ctx {
+       struct mailbox *box;
+};
+
 /* Set error message in storage. Critical errors are logged with i_error(),
    but user sees only "internal error" message. */
 void mail_storage_clear_error(struct mail_storage *storage);
index 45fbbf899a2019db07e60b2cb69aaabcf06f94d0..4990b1246945b43bafae43ca48affb4f3b6552da 100644 (file)
@@ -370,6 +370,17 @@ int mailbox_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2,
        return box->get_uids(box, uid1, uid2, seq1_r, seq2_r);
 }
 
+struct mailbox_header_lookup_ctx *
+mailbox_header_lookup_init(struct mailbox *box, const char *const headers[])
+{
+       return box->header_lookup_init(box, headers);
+}
+
+void mailbox_header_lookup_deinit(struct mailbox_header_lookup_ctx *ctx)
+{
+       ctx->box->header_lookup_deinit(ctx);
+}
+
 int mailbox_search_get_sorting(struct mailbox *box,
                               enum mail_sort_type *sort_program)
 {
@@ -381,7 +392,7 @@ mailbox_search_init(struct mailbox_transaction_context *t,
                    const char *charset, struct mail_search_arg *args,
                    const enum mail_sort_type *sort_program,
                    enum mail_fetch_field wanted_fields,
-                   const char *const wanted_headers[])
+                   struct mailbox_header_lookup_ctx *wanted_headers)
 {
        return t->box->search_init(t, charset, args, sort_program,
                                   wanted_fields, wanted_headers);
index 84ea4d3b1e978950f0eed268f7eb06fee4a0fe5c..d4ae1b90e18cbc63db4eac9dfc0a66eebe6f298a 100644 (file)
@@ -289,6 +289,11 @@ struct mail *mailbox_fetch(struct mailbox_transaction_context *t, uint32_t seq,
 int mailbox_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2,
                     uint32_t *seq1_r, uint32_t *seq2_r);
 
+/* Initialize header lookup for given headers. */
+struct mailbox_header_lookup_ctx *
+mailbox_header_lookup_init(struct mailbox *box, const char *const headers[]);
+void mailbox_header_lookup_deinit(struct mailbox_header_lookup_ctx *ctx);
+
 /* Modify sort_program to specify a sort program acceptable for
    search_init(). If mailbox supports no sorting, it's simply set to
    {MAIL_SORT_END}. */
@@ -308,7 +313,7 @@ mailbox_search_init(struct mailbox_transaction_context *t,
                    const char *charset, struct mail_search_arg *args,
                    const enum mail_sort_type *sort_program,
                    enum mail_fetch_field wanted_fields,
-                   const char *const wanted_headers[]);
+                    struct mailbox_header_lookup_ctx *wanted_headers);
 /* Deinitialize search request. */
 int mailbox_search_deinit(struct mail_search_context *ctx);
 /* Search the next message. Returned mail object can be used until
@@ -365,10 +370,10 @@ struct mail {
 
        /* Get value for single header field */
        const char *(*get_header)(struct mail *mail, const char *field);
-       /* Returns partial headers which contain _at least_ the given fields,
-          but it may contain others as well. */
-       struct istream *(*get_headers)(struct mail *mail,
-                                      const char *const minimum_fields[]);
+       /* Returns stream containing specified headers. */
+       struct istream *
+               (*get_headers)(struct mail *mail,
+                              struct mailbox_header_lookup_ctx *headers);
 
        /* Returns input stream pointing to beginning of message header.
           hdr_size and body_size are updated unless they're NULL. */
@@ -376,7 +381,7 @@ struct mail {
                                      struct message_size *hdr_size,
                                      struct message_size *body_size);
 
-       /* Get the any of the "special" fields. */
+       /* Get any of the "special" fields. */
        const char *(*get_special)(struct mail *mail,
                                   enum mail_fetch_field field);
 
index a52a577825459984587b21a05bd7e87849ada22b..198faee63a612862224ba076a918ac3306ebf46e 100644 (file)
@@ -45,6 +45,14 @@ static const char *_get_header(struct mail *mail, const char *field)
        return p->mail->get_header(p->mail, field);
 }
 
+static struct istream *
+_get_headers(struct mail *mail, struct mailbox_header_lookup_ctx *headers)
+{
+       struct proxy_mail *p = (struct proxy_mail *) mail;
+
+       return p->mail->get_headers(p->mail, headers);
+}
+
 static struct istream *_get_stream(struct mail *mail,
                                   struct message_size *hdr_size,
                                   struct message_size *body_size)
@@ -91,6 +99,7 @@ void proxy_mail_init(struct proxy_mail *proxy, struct mail *mail)
        pm->get_date = _get_date;
        pm->get_size = _get_size;
        pm->get_header = _get_header;
+       pm->get_headers = _get_headers;
        pm->get_stream = _get_stream;
        pm->get_special = _get_special;
        pm->update_flags = _update_flags;
index 962fb1f954f6fd8e9d75186dfdf3200d89a25e6c..84d31b47abff0faeb7acff75086a6585007e1177 100644 (file)
@@ -66,6 +66,14 @@ static int _get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2,
        return p->box->get_uids(p->box, uid1, uid2, seq1_r, seq2_r);
 }
 
+static struct mailbox_header_lookup_ctx *
+_header_lookup_init(struct mailbox *box, const char *const headers[])
+{
+       struct proxy_mailbox *p = (struct proxy_mailbox *) box;
+
+       return p->box->header_lookup_init(p->box, headers);
+}
+
 static int _search_get_sorting(struct mailbox *box,
                               enum mail_sort_type *sort_program)
 {
@@ -79,7 +87,7 @@ _search_init(struct mailbox_transaction_context *t,
             const char *charset, struct mail_search_arg *args,
             const enum mail_sort_type *sort_program,
             enum mail_fetch_field wanted_fields,
-            const char *const wanted_headers[])
+            struct mailbox_header_lookup_ctx *wanted_headers)
 {
        struct proxy_mailbox_transaction_context *pt =
                (struct proxy_mailbox_transaction_context *)t;
@@ -157,6 +165,7 @@ void proxy_mailbox_init(struct proxy_mailbox *proxy, struct mailbox *box)
        pb->notify_changes = _notify_changes;
        pb->fetch = _fetch;
        pb->get_uids = _get_uids;
+       pb->header_lookup_init = _header_lookup_init;
 
        pb->search_get_sorting = _search_get_sorting;
        pb->search_init = _search_init;