]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
Index cache file rewrite. It's not finished yet and mbox support is
authorTimo Sirainen <tss@iki.fi>
Wed, 6 Aug 2003 20:15:30 +0000 (23:15 +0300)
committerTimo Sirainen <tss@iki.fi>
Wed, 6 Aug 2003 20:15:30 +0000 (23:15 +0300)
completely broken. But it's getting difficult to maintain outside cvs :)

--HG--
branch : HEAD

56 files changed:
src/imap/cmd-copy.c
src/imap/cmd-fetch.c
src/imap/cmd-store.c
src/imap/imap-fetch-body-section.c
src/imap/imap-fetch.c
src/imap/imap-fetch.h
src/imap/imap-search.c
src/imap/imap-thread.c
src/lib-imap/imap-envelope.c
src/lib-imap/imap-envelope.h
src/lib-index/Makefile.am
src/lib-index/mail-cache.c [new file with mode: 0644]
src/lib-index/mail-cache.h [new file with mode: 0644]
src/lib-index/mail-custom-flags.c
src/lib-index/mail-index-compress.c
src/lib-index/mail-index-data.c [deleted file]
src/lib-index/mail-index-data.h [deleted file]
src/lib-index/mail-index-fsck.c
src/lib-index/mail-index-open.c
src/lib-index/mail-index-rebuild.c [moved from src/lib-index/maildir/maildir-rebuild.c with 57% similarity]
src/lib-index/mail-index-update-cache.c [deleted file]
src/lib-index/mail-index-update.c [deleted file]
src/lib-index/mail-index.c
src/lib-index/mail-index.h
src/lib-index/maildir/Makefile.am
src/lib-index/maildir/maildir-build.c
src/lib-index/maildir/maildir-expunge.c
src/lib-index/maildir/maildir-index.c
src/lib-index/maildir/maildir-index.h
src/lib-index/maildir/maildir-open.c
src/lib-index/maildir/maildir-sync.c
src/lib-index/maildir/maildir-uidlist.c
src/lib-index/maildir/maildir-update-flags.c
src/lib-index/mbox/Makefile.am
src/lib-index/mbox/mbox-append.c
src/lib-index/mbox/mbox-index.c
src/lib-index/mbox/mbox-index.h
src/lib-index/mbox/mbox-open.c
src/lib-index/mbox/mbox-rebuild.c [deleted file]
src/lib-index/mbox/mbox-rewrite.c
src/lib-index/mbox/mbox-sync-full.c
src/lib-index/mbox/mbox-sync.c
src/lib-storage/index/index-expunge.c
src/lib-storage/index/index-fetch.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-copy.c
src/lib-storage/mail-search.c
src/lib-storage/mail-search.h
src/lib-storage/mail-storage.h
src/lib-storage/proxy-mailbox.c
src/pop3/client.c
src/pop3/commands.c

index ce4854a54aa0226040ef21316a52fa3fe5650776..2130345489f1dbeb81330d15a0d79f93068bf578 100644 (file)
@@ -12,7 +12,8 @@ static int fetch_and_copy(struct mail_copy_context *copy_ctx,
        int failed = FALSE;
 
        fetch_ctx = box->fetch_init(box, MAIL_FETCH_STREAM_HEADER |
-                                   MAIL_FETCH_STREAM_BODY, messageset, uidset);
+                                   MAIL_FETCH_STREAM_BODY, NULL,
+                                   messageset, uidset);
        if (fetch_ctx == NULL)
                return FALSE;
 
index fbbb78ed6684eeab9140f1bcc6b841ba03a2f958..a8f685470264d4089db05d396decc7137f72e33f 100644 (file)
@@ -70,7 +70,10 @@ static int check_section(struct client *client, const char *section,
        }
 
        if (strncmp(section, "HEADER", 6) == 0) {
-               *fetch_data |= MAIL_FETCH_STREAM_HEADER;
+               /* exact header matches could be cached */
+               if (strncmp(section, "HEADER.FIELDS ", 14) != 0)
+                       *fetch_data |= MAIL_FETCH_STREAM_HEADER;
+
                if (check_header_section(section+6))
                        return TRUE;
        } else if (*section >= '0' && *section <= '9') {
index e0fa6918c16572ff0916b4e970ecc42a780a2160..00d4b7f27e0438d9d3b153beb165a2ed72a31d9e 100644 (file)
@@ -96,9 +96,16 @@ int cmd_store(struct client *client)
        /* and update the flags */
        box = client->mailbox;
 
-       failed = !box->lock(box, MAILBOX_LOCK_FLAGS | MAILBOX_LOCK_READ);
+       if (!box->is_readonly(box)) {
+               /* read-only, don't every try to get write locking */
+               failed = FALSE;
+       } else {
+               failed = !box->lock(box, MAILBOX_LOCK_FLAGS |
+                                   MAILBOX_LOCK_READ);
+       }
+
        fetch_ctx = failed ? NULL :
-               box->fetch_init(box, MAIL_FETCH_FLAGS,
+               box->fetch_init(box, MAIL_FETCH_FLAGS, NULL,
                                messageset, client->cmd_uid);
        if (fetch_ctx == NULL)
                failed = TRUE;
index b4be84e9281b48558eedfb25494219ffa442cee9..2a90224ce1f1f53e81943ad748d9a616401a569b 100644 (file)
@@ -20,6 +20,9 @@
 #define MAX_HEADER_BUFFER_SIZE (32*1024)
 
 struct fetch_header_field_context {
+        struct imap_fetch_context *fetch_ctx;
+       struct mail *mail;
+
        buffer_t *dest;
        struct ostream *output;
        uoff_t dest_size;
@@ -116,26 +119,6 @@ static int fetch_body(struct imap_fetch_context *ctx,
        return ret >= 0;
 }
 
-static const char **get_fields_array(const char *fields)
-{
-       const char **field_list, **field;
-
-       while (*fields == ' ')
-               fields++;
-       if (*fields == '(')
-               fields++;
-
-       field_list = t_strsplit(fields, " )");
-
-       /* array ends at ")" element */
-       for (field = field_list; *field != NULL; field++) {
-               if (strcmp(*field, ")") == 0)
-                       *field = NULL;
-       }
-
-       return field_list;
-}
-
 static int header_match(const char *const *fields,
                        const char *name, size_t size)
 {
@@ -252,10 +235,16 @@ static int fetch_header_fields(struct istream *input, const char *section,
        struct message_header_line *hdr;
 
        if (strncmp(section, "HEADER.FIELDS ", 14) == 0) {
-               ctx->fields = get_fields_array(section + 14);
+               ctx->fields = imap_fetch_get_body_fields(section + 14);
                ctx->match_func = header_match;
+
+               if (ctx->fetch_ctx->body_fetch_from_cache) {
+                       input = ctx->mail->get_headers(ctx->mail, ctx->fields);
+                       if (input == NULL)
+                               return FALSE;
+               }
        } else if (strncmp(section, "HEADER.FIELDS.NOT ", 18) == 0) {
-               ctx->fields = get_fields_array(section + 18);
+               ctx->fields = imap_fetch_get_body_fields(section + 18);
                ctx->match_func = header_match_not;
        } else if (strcmp(section, "MIME") == 0) {
                /* Mime-Version + Content-* fields */
@@ -329,18 +318,21 @@ static int fetch_header_from(struct imap_fetch_context *ctx,
           the size first and then send the data directly to output stream. */
 
        memset(&hdr_ctx, 0, sizeof(hdr_ctx));
+       hdr_ctx.mail = mail;
+       hdr_ctx.fetch_ctx = ctx;
        hdr_ctx.skip = body->skip;
        hdr_ctx.max_size = body->max_size;
        hdr_ctx.fix_nuls = !mail->has_no_nuls;
 
        failed = FALSE;
-       start_offset = input->v_offset;
+       start_offset = input == NULL ? 0 : input->v_offset;
 
        t_push();
 
        /* first pass, we need at least the size */
        if (size->virtual_size > MAX_HEADER_BUFFER_SIZE &&
-           body->max_size > MAX_HEADER_BUFFER_SIZE) {
+           body->max_size > MAX_HEADER_BUFFER_SIZE &&
+           !ctx->body_fetch_from_cache) {
                if (!fetch_header_fields(input, header_section, &hdr_ctx))
                        failed = TRUE;
 
@@ -392,9 +384,13 @@ static int fetch_header(struct imap_fetch_context *ctx, struct mail *mail,
        struct istream *stream;
        struct message_size hdr_size;
 
-       stream = mail->get_stream(mail, &hdr_size, NULL);
-       if (stream == NULL)
-               return FALSE;
+       if (ctx->body_fetch_from_cache)
+               stream = NULL;
+       else {
+               stream = mail->get_stream(mail, &hdr_size, NULL);
+               if (stream == NULL)
+                       return FALSE;
+       }
 
        return fetch_header_from(ctx, stream, &hdr_size,
                                 mail, body, body->section);
index 483207627077c895b5d3d1a55c3e07c9a88554d6..6a76a034897a3e8a3e21d622495c793c88d9d066 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "common.h"
+#include "buffer.h"
 #include "istream.h"
 #include "ostream.h"
 #include "str.h"
 
 #include <unistd.h>
 
+const char *const *imap_fetch_get_body_fields(const char *fields)
+{
+       const char **field_list, **field, **dest;
+
+       while (*fields == ' ')
+               fields++;
+       if (*fields == '(')
+               fields++;
+
+       field_list = t_strsplit(fields, " )");
+
+       /* array ends at ")" element */
+       for (field = dest = field_list; *field != NULL; field++) {
+               if (strcmp(*field, ")") == 0)
+                       break;
+               if (**field != '\0') {
+                       *dest = *field;
+                       dest++;
+               }
+       }
+       *dest = NULL;
+
+       return field_list;
+}
+
 static void fetch_uid(struct imap_fetch_context *ctx, struct mail *mail)
 {
        str_printfa(ctx->str, "UID %u ", mail->uid);
@@ -265,6 +291,10 @@ int imap_fetch(struct client *client,
        struct mailbox *box = client->mailbox;
        struct imap_fetch_context ctx;
        struct mail *mail;
+       struct imap_fetch_body_data *body;
+       const char *null = NULL;
+       const char *const *wanted_headers, *const *arr;
+       buffer_t *buffer;
        int all_found;
 
        memset(&ctx, 0, sizeof(ctx));
@@ -277,9 +307,7 @@ int imap_fetch(struct client *client,
 
        if (!box->is_readonly(box)) {
                /* If we have any BODY[..] sections, \Seen flag is added for
-                  all messages */
-               struct imap_fetch_body_data *body;
-
+                  all messages. */
                for (body = bodies; body != NULL; body = body->next) {
                        if (!body->peek) {
                                ctx.update_seen = TRUE;
@@ -291,12 +319,34 @@ int imap_fetch(struct client *client,
                        ctx.update_seen = TRUE;
        }
 
+       /* 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(data_stack_pool, 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++;
+               }
+       }
+       buffer_append(buffer, &null, sizeof(null));
+       wanted_headers = !ctx.body_fetch_from_cache ? NULL :
+               buffer_get_data(buffer, NULL);
+
        if (ctx.update_seen) {
                if (!box->lock(box, MAILBOX_LOCK_FLAGS | MAILBOX_LOCK_READ))
                        return -1;
        }
 
-       ctx.fetch_ctx = box->fetch_init(box, fetch_data, messageset, uidset);
+       ctx.fetch_ctx = box->fetch_init(box, fetch_data, wanted_headers,
+                                       messageset, uidset);
        if (ctx.fetch_ctx == NULL)
                ctx.failed = TRUE;
        else {
index e04ce38dc212a98f626531b343347c0b2888a66a..bd55060ff8807f2a0b7a229b6784c844575df5d5 100644 (file)
@@ -33,7 +33,7 @@ struct imap_fetch_context {
        int update_seen;
        struct mail_full_flags seen_flag;
 
-       int first, failed;
+       int first, failed, body_fetch_from_cache;
 };
 
 int imap_fetch(struct client *client,
@@ -46,4 +46,6 @@ int imap_fetch_body_section(struct imap_fetch_context *ctx,
                            const struct imap_fetch_body_data *body,
                            struct mail *mail);
 
+const char *const *imap_fetch_get_body_fields(const char *fields);
+
 #endif
index bec6b16b3aebd870ee12e08fd56a94c648ce911c..9b2c6168d6ba5a3b94f43fcbba4464a3ef0a713e 100644 (file)
@@ -21,20 +21,26 @@ search_arg_new(pool_t pool, enum mail_search_arg_type type)
        return arg;
 }
 
-#define ARG_NEW(type, value) \
-       arg_new(data, args, next_sarg, type, value)
+#define ARG_NEW(type) \
+       arg_new(data, args, next_sarg, type, TRUE, NULL)
+
+#define ARG_NEW_FLAG(type) \
+       arg_new(data, args, next_sarg, type, FALSE, NULL)
+
+#define ARG_NEW_HEADER(type, hdr_name) \
+       arg_new(data, args, next_sarg, type, TRUE, hdr_name)
 
 static int arg_new(struct search_build_data *data, struct imap_arg **args,
                   struct mail_search_arg **next_sarg,
-                  enum mail_search_arg_type type, int value)
+                  enum mail_search_arg_type type, int have_value,
+                  const char *hdr_name)
 {
        struct mail_search_arg *sarg;
 
        *next_sarg = sarg = search_arg_new(data->pool, type);
-       if (value == 0)
+       if (!have_value)
                return TRUE;
 
-       /* first arg */
        if ((*args)->type == IMAP_ARG_EOL) {
                data->error = "Missing parameter for argument";
                return FALSE;
@@ -49,23 +55,8 @@ static int arg_new(struct search_build_data *data, struct imap_arg **args,
        sarg->value.str = str_ucase(IMAP_ARG_STR(*args));
        *args += 1;
 
-       /* second arg */
-       if (value == 2) {
-               if ((*args)->type == IMAP_ARG_EOL) {
-                       data->error = "Missing parameter for argument";
-                       return FALSE;
-               }
-
-               if ((*args)->type != IMAP_ARG_ATOM &&
-                   (*args)->type != IMAP_ARG_STRING) {
-                       data->error = "Invalid parameter for argument";
-                       return FALSE;
-               }
-
-                sarg->hdr_field_name = sarg->value.str;
-               sarg->value.str = str_ucase(IMAP_ARG_STR(*args));
-               *args += 1;
-       }
+       if (hdr_name != NULL)
+                sarg->hdr_field_name = hdr_name;
 
        return TRUE;
 }
@@ -122,40 +113,40 @@ static int search_arg_build(struct search_build_data *data,
        switch (*str) {
        case 'A':
                if (strcmp(str, "ANSWERED") == 0)
-                       return ARG_NEW(SEARCH_ANSWERED, 0);
+                       return ARG_NEW_FLAG(SEARCH_ANSWERED);
                else if (strcmp(str, "ALL") == 0)
-                       return ARG_NEW(SEARCH_ALL, 0);
+                       return ARG_NEW_FLAG(SEARCH_ALL);
                break;
        case 'B':
                if (strcmp(str, "BODY") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_BODY, 1);
+                       return ARG_NEW(SEARCH_BODY);
                } else if (strcmp(str, "BEFORE") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_BEFORE, 1);
+                       return ARG_NEW(SEARCH_BEFORE);
                } else if (strcmp(str, "BCC") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_BCC, 1);
+                       return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
                }
                break;
        case 'C':
                if (strcmp(str, "CC") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_CC, 1);
+                       return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
                }
                break;
        case 'D':
                if (strcmp(str, "DELETED") == 0)
-                       return ARG_NEW(SEARCH_DELETED, 0);
+                       return ARG_NEW_FLAG(SEARCH_DELETED);
                else if (strcmp(str, "DRAFT") == 0)
-                       return ARG_NEW(SEARCH_DRAFT, 0);
+                       return ARG_NEW_FLAG(SEARCH_DRAFT);
                break;
        case 'F':
                if (strcmp(str, "FLAGGED") == 0)
-                       return ARG_NEW(SEARCH_FLAGGED, 0);
+                       return ARG_NEW_FLAG(SEARCH_FLAGGED);
                else if (strcmp(str, "FROM") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_FROM, 1);
+                       return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
                }
                break;
        case 'H':
@@ -174,43 +165,20 @@ static int search_arg_build(struct search_build_data *data,
                        }
 
                        key = str_ucase(IMAP_ARG_STR(*args));
-
-                       if (strcmp(key, "FROM") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_FROM, 1);
-                       } else if (strcmp(key, "TO") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_TO, 1);
-                       } else if (strcmp(key, "CC") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_CC, 1);
-                       } else if (strcmp(key, "BCC") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_BCC, 1);
-                       } else if (strcmp(key, "SUBJECT") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_SUBJECT, 1);
-                       } else if (strcmp(key, "IN-REPLY-TO") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_IN_REPLY_TO, 1);
-                       } else if (strcmp(key, "MESSAGE-ID") == 0) {
-                               *args += 1;
-                               return ARG_NEW(SEARCH_MESSAGE_ID, 1);
-                       } else {
-                               return ARG_NEW(SEARCH_HEADER, 2);
-                       }
+                       *args += 1;
+                       return ARG_NEW_HEADER(SEARCH_HEADER, key);
                }
                break;
        case 'K':
                if (strcmp(str, "KEYWORD") == 0) {
                        /* <flag> */
-                       return ARG_NEW(SEARCH_KEYWORD, 1);
+                       return ARG_NEW(SEARCH_KEYWORD);
                }
                break;
        case 'L':
                if (strcmp(str, "LARGER") == 0) {
                        /* <n> */
-                       return ARG_NEW(SEARCH_LARGER, 1);
+                       return ARG_NEW(SEARCH_LARGER);
                }
                break;
        case 'N':
@@ -260,10 +228,10 @@ static int search_arg_build(struct search_build_data *data,
                        return TRUE;
                } if (strcmp(str, "ON") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_ON, 1);
+                       return ARG_NEW(SEARCH_ON);
                } if (strcmp(str, "OLD") == 0) {
                        /* OLD == NOT RECENT */
-                       if (!ARG_NEW(SEARCH_RECENT, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_RECENT))
                                return FALSE;
 
                        (*next_sarg)->not = TRUE;
@@ -272,71 +240,71 @@ static int search_arg_build(struct search_build_data *data,
                break;
        case 'R':
                if (strcmp(str, "RECENT") == 0)
-                       return ARG_NEW(SEARCH_RECENT, 0);
+                       return ARG_NEW_FLAG(SEARCH_RECENT);
                break;
        case 'S':
                if (strcmp(str, "SEEN") == 0)
-                       return ARG_NEW(SEARCH_SEEN, 0);
+                       return ARG_NEW_FLAG(SEARCH_SEEN);
                else if (strcmp(str, "SUBJECT") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_SUBJECT, 1);
+                       return ARG_NEW_HEADER(SEARCH_HEADER, str);
                } else if (strcmp(str, "SENTBEFORE") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_SENTBEFORE, 1);
+                       return ARG_NEW(SEARCH_SENTBEFORE);
                } else if (strcmp(str, "SENTON") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_SENTON, 1);
+                       return ARG_NEW(SEARCH_SENTON);
                } else if (strcmp(str, "SENTSINCE") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_SENTSINCE, 1);
+                       return ARG_NEW(SEARCH_SENTSINCE);
                } else if (strcmp(str, "SINCE") == 0) {
                        /* <date> */
-                       return ARG_NEW(SEARCH_SINCE, 1);
+                       return ARG_NEW(SEARCH_SINCE);
                } else if (strcmp(str, "SMALLER") == 0) {
                        /* <n> */
-                       return ARG_NEW(SEARCH_SMALLER, 1);
+                       return ARG_NEW(SEARCH_SMALLER);
                }
                break;
        case 'T':
                if (strcmp(str, "TEXT") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_TEXT, 1);
+                       return ARG_NEW(SEARCH_TEXT);
                } else if (strcmp(str, "TO") == 0) {
                        /* <string> */
-                       return ARG_NEW(SEARCH_TO, 1);
+                       return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
                }
                break;
        case 'U':
                if (strcmp(str, "UID") == 0) {
                        /* <message set> */
-                       return ARG_NEW(SEARCH_UID, 1);
+                       return ARG_NEW(SEARCH_UID);
                } else if (strcmp(str, "UNANSWERED") == 0) {
-                       if (!ARG_NEW(SEARCH_ANSWERED, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_ANSWERED))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
                } else if (strcmp(str, "UNDELETED") == 0) {
-                       if (!ARG_NEW(SEARCH_DELETED, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_DELETED))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
                } else if (strcmp(str, "UNDRAFT") == 0) {
-                       if (!ARG_NEW(SEARCH_DRAFT, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_DRAFT))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
                } else if (strcmp(str, "UNFLAGGED") == 0) {
-                       if (!ARG_NEW(SEARCH_FLAGGED, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_FLAGGED))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
                } else if (strcmp(str, "UNKEYWORD") == 0) {
-                       if (!ARG_NEW(SEARCH_KEYWORD, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_KEYWORD))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
                } else if (strcmp(str, "UNSEEN") == 0) {
-                       if (!ARG_NEW(SEARCH_SEEN, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_SEEN))
                                return FALSE;
                        (*next_sarg)->not = TRUE;
                        return TRUE;
@@ -345,7 +313,7 @@ static int search_arg_build(struct search_build_data *data,
        default:
                if (*str == '*' || (*str >= '0' && *str <= '9')) {
                        /* <message-set> */
-                       if (!ARG_NEW(SEARCH_SET, 0))
+                       if (!ARG_NEW_FLAG(SEARCH_SET))
                                return FALSE;
 
                        (*next_sarg)->value.str = str;
index 56b9e469177afd787221cd63678117e924742fe8..8bda11602456e5a5ac93664948f0634b12a7d845 100644 (file)
@@ -103,7 +103,7 @@ int imap_thread(struct client *client, const char *charset,
                struct mail_search_arg *args, enum mail_thread_type type)
 {
        static const char *wanted_headers[] = {
-               "message-id", "in-reply-to", "references",
+               "message-id", "in-reply-to", "references", "subject",
                NULL
        };
        struct thread_context *ctx;
index 610792557f73afd3b7f6cd55d4bf3d539d8e3d02..fa7c2e9cf33ab64ec8f6bd9912c9f32cd023ff0b 100644 (file)
@@ -19,6 +19,12 @@ struct message_part_envelope_data {
        char *in_reply_to, *message_id;
 };
 
+const char *imap_envelope_headers[] = {
+       "Date", "Subject", "From", "Sender", "Reply-To",
+       "To", "Cc", "Bcc", "In-Reply-To", "Message-ID",
+       NULL
+};
+
 int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret)
 {
        *ret = (enum imap_envelope_field)-1;
index 9952cd71adbb3081489772d19e7b619c9ec72c15..d44d4fda20cc1e97264774777d749ba4821fc745 100644 (file)
@@ -26,6 +26,8 @@ enum imap_envelope_result_type {
 
 struct message_part_envelope_data;
 
+extern const char *imap_envelope_headers[];
+
 int imap_envelope_get_field(const char *name, enum imap_envelope_field *ret);
 
 /* Update envelope data based from given header field */
index d1b8336d4948df34669cb92affaf31b14ade6055..712dc3494b5ad996a9605532573844e6e1ab52a4 100644 (file)
@@ -8,21 +8,20 @@ INCLUDES = \
        -I$(top_srcdir)/src/lib-imap
 
 libindex_a_SOURCES = \
+        mail-cache.c \
        mail-custom-flags.c \
         mail-index.c \
         mail-index-compress.c \
         mail-index-file.c \
         mail-index-fsck.c \
-        mail-index-data.c \
         mail-index-open.c \
-        mail-index-update.c \
-        mail-index-update-cache.c \
+        mail-index-rebuild.c \
         mail-index-util.c \
        mail-modifylog.c
 
 noinst_HEADERS = \
+        mail-cache.h \
        mail-custom-flags.h \
        mail-index.h \
-        mail-index-data.h \
         mail-index-util.h \
        mail-modifylog.h
diff --git a/src/lib-index/mail-cache.c b/src/lib-index/mail-cache.c
new file mode 100644 (file)
index 0000000..8f16c8a
--- /dev/null
@@ -0,0 +1,1705 @@
+/* Copyright (C) 2003 Timo Sirainen */
+
+#define _XOPEN_SOURCE 500 /* for pwrite() / Linux */
+
+#include "lib.h"
+#include "buffer.h"
+#include "byteorder.h"
+#include "file-lock.h"
+#include "file-set-size.h"
+#include "ioloop.h"
+#include "mmap-util.h"
+#include "write-full.h"
+#include "mail-index.h"
+#include "mail-index-util.h"
+#include "mail-cache.h"
+
+#include <stddef.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+/* Never compress the file if it's smaller than this */
+#define COMPRESS_MIN_SIZE (1024*50)
+
+/* Compress the file when deleted space reaches n% of total size */
+#define COMPRESS_PERCENTAGE 20
+
+/* Compress the file when n% of rows contain continued rows.
+   200% means that there's 2 continued rows per record. */
+#define COMPRESS_CONTINUED_PERCENTAGE 200
+
+/* Initial size for the file */
+#define MAIL_CACHE_INITIAL_SIZE (sizeof(struct mail_cache_header) + 10240)
+
+/* When more space is needed, grow the file n% larger than the previous size */
+#define MAIL_CACHE_GROW_PERCENTAGE 10
+
+#define MAIL_CACHE_LOCK_TIMEOUT 120
+#define MAIL_CACHE_LOCK_STALE_TIMEOUT 60
+
+#define CACHE_RECORD(cache, data_pos) \
+       ((struct mail_cache_record *) \
+        ((char *) (cache)->mmap_base + ((data_pos) & ~3)))
+
+struct mail_cache_header {
+       uint32_t indexid;
+       uint32_t continued_record_count;
+
+       uint32_t used_file_size;
+       uint32_t deleted_space;
+
+       uint32_t used_fields; /* enum mail_cache_field */
+
+       uint32_t field_usage_start; /* time_t */
+       uint32_t field_usage_counts[32];
+
+       uint32_t header_offsets[MAIL_CACHE_HEADERS_COUNT];
+};
+
+struct mail_cache_record {
+       uint32_t fields; /* enum mail_cache_field */
+       uint32_t next_offset;
+       uint32_t size; /* full record size, including this header */
+};
+
+struct mail_cache {
+       struct mail_index *index;
+
+       char *filepath;
+       int fd;
+
+       void *mmap_base;
+       size_t mmap_length;
+       uint32_t used_file_size;
+
+       struct mail_cache_header *header;
+
+       pool_t split_header_pool;
+       uint32_t split_offsets[MAIL_CACHE_HEADERS_COUNT];
+       const char *const *split_headers[MAIL_CACHE_HEADERS_COUNT];
+
+       enum mail_cache_field default_cache_fields;
+       enum mail_cache_field never_cache_fields;
+
+        struct mail_cache_transaction_ctx *trans_ctx;
+       unsigned int locks;
+
+       unsigned int anon_mmap:1;
+       unsigned int mmap_refresh:1;
+};
+
+struct mail_cache_transaction_ctx {
+       struct mail_cache *cache;
+
+       unsigned int next_unused_header_lowwater;
+
+       unsigned int last_idx;
+       struct mail_cache_record cache_rec;
+       buffer_t *cache_data;
+
+       unsigned int first_uid, last_uid;
+       buffer_t *index_marks, *cache_marks;
+};
+
+unsigned int mail_cache_field_sizes[32] = {
+       sizeof(enum mail_index_record_flag),
+       sizeof(uoff_t),
+       16,
+       sizeof(struct mail_sent_date),
+       sizeof(time_t),
+       sizeof(uoff_t),
+       sizeof(uoff_t),
+       sizeof(uoff_t),
+       sizeof(uoff_t),
+
+       0, 0, 0,
+
+       /* 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
+};
+
+enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT] = {
+       MAIL_CACHE_HEADERS1,
+       MAIL_CACHE_HEADERS2,
+       MAIL_CACHE_HEADERS3,
+       MAIL_CACHE_HEADERS4
+};
+
+static const unsigned char *null4[] = { 0, 0, 0, 0 };
+
+static const char *
+mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx);
+static int mail_cache_write(struct mail_cache_transaction_ctx *ctx);
+static struct mail_cache_record *
+mail_cache_lookup(struct mail_cache *cache,
+                 const struct mail_index_record *rec);
+
+static int mail_cache_set_syscall_error(struct mail_cache *cache,
+                                       const char *function)
+{
+       i_assert(function != NULL);
+
+       if (ENOSPACE(errno)) {
+               cache->index->nodiskspace = TRUE;
+               return FALSE;
+       }
+
+       index_set_error(cache->index, "%s failed with index cache file %s: %m",
+                       function, cache->filepath);
+       return FALSE;
+}
+
+static int mail_cache_create_memory(struct mail_cache *cache,
+                                   struct mail_cache_header *hdr)
+{
+       cache->mmap_length = MAIL_CACHE_INITIAL_SIZE;
+       cache->mmap_base = mmap_anon(cache->mmap_length);
+       if (cache->mmap_base == MAP_FAILED) {
+               index_set_error(cache->index, "mmap_anon(%"PRIuSIZE_T")",
+                               cache->mmap_length);
+               return FALSE;
+       }
+
+       cache->header = cache->mmap_base;
+       *cache->header = *hdr;
+
+       cache->anon_mmap = TRUE;
+       cache->filepath = i_strdup_printf("(in-memory index cache for %s)",
+                                         cache->index->mailbox_path);
+       return TRUE;
+}
+
+static void mail_cache_file_close(struct mail_cache *cache)
+{
+       if (cache->anon_mmap) {
+               if (munmap_anon(cache->mmap_base, cache->mmap_length) < 0)
+                       mail_cache_set_syscall_error(cache, "munmap_anon()");
+       } else if (cache->mmap_base != NULL) {
+               if (munmap(cache->mmap_base, cache->mmap_length) < 0)
+                       mail_cache_set_syscall_error(cache, "munmap()");
+       }
+
+       cache->mmap_base = NULL;
+       cache->header = NULL;
+       cache->mmap_length = 0;
+
+       if (cache->fd != -1) {
+               if (close(cache->fd) < 0)
+                       mail_cache_set_syscall_error(cache, "close()");
+               cache->fd = -1;
+       }
+}
+
+static int mail_cache_file_reopen(struct mail_cache *cache)
+{
+       int fd;
+
+       i_assert(!cache->anon_mmap);
+
+       fd = open(cache->filepath, O_RDWR);
+       if (fd == -1)
+               return mail_cache_set_syscall_error(cache, "open()");
+
+       mail_cache_file_close(cache);
+
+       cache->fd = fd;
+       return TRUE;
+}
+
+static int mmap_verify_header(struct mail_cache *cache)
+{
+       struct mail_cache_header *hdr = cache->header;
+
+       if (cache->trans_ctx != NULL) {
+               /* we've updated used_file_size, do nothing */
+               return TRUE;
+       }
+
+       cache->used_file_size = nbo_to_uint32(hdr->used_file_size);
+
+       /* only check the header if we're locked */
+       if (cache->locks == 0)
+               return TRUE;
+
+       if (cache->used_file_size < sizeof(struct mail_cache_header)) {
+               mail_cache_set_corrupted(cache, "used_file_size too small");
+               return FALSE;
+       }
+       if ((cache->used_file_size % sizeof(uint32_t)) != 0) {
+               mail_cache_set_corrupted(cache, "used_file_size not aligned");
+               return FALSE;
+       }
+
+       if (cache->used_file_size > cache->mmap_length) {
+               /* maybe a crash truncated the file - just fix it */
+               hdr->used_file_size = uint32_to_nbo(cache->mmap_length & ~3);
+               if (msync(cache->mmap_base, sizeof(*hdr), MS_SYNC) < 0) 
+                       return mail_cache_set_syscall_error(cache, "msync()");
+       }
+       return TRUE;
+}
+
+static int mmap_update(struct mail_cache *cache, size_t offset, size_t size)
+{
+       if (cache->header != NULL &&
+           cache->header->indexid != cache->index->indexid) {
+               /* indexid changed, most likely it was rebuilt.
+                  try reopening. */
+               if (!mail_cache_file_reopen(cache))
+                       return FALSE;
+
+               /* force mmap refresh */
+               size = 0;
+       }
+
+       if (size != 0 && offset < cache->mmap_length &&
+           size <= cache->mmap_length - offset) {
+               /* already mapped */
+               if (!cache->mmap_refresh)
+                       return TRUE;
+
+               cache->mmap_refresh = FALSE;
+       }
+
+       if (cache->anon_mmap)
+               return TRUE;
+
+       if (cache->mmap_base != NULL) {
+               if (cache->locks != 0) {
+                       /* in the middle of transaction - write the changes */
+                       if (msync(cache->mmap_base, cache->mmap_length,
+                                 MS_SYNC) < 0) {
+                               mail_cache_set_syscall_error(cache, "msync()");
+                               return FALSE;
+                       }
+               }
+
+               if (munmap(cache->mmap_base, cache->mmap_length) < 0)
+                       mail_cache_set_syscall_error(cache, "munmap()");
+       }
+
+       i_assert(cache->fd != -1);
+
+       /* map the whole file */
+       cache->header = NULL;
+       cache->mmap_length = 0;
+
+       cache->mmap_base = mmap_rw_file(cache->fd, &cache->mmap_length);
+       if (cache->mmap_base == MAP_FAILED) {
+               cache->mmap_base = NULL;
+               return mail_cache_set_syscall_error(cache, "mmap()");
+       }
+
+       /* check that the header is still ok */
+       if (cache->mmap_length < sizeof(struct mail_cache_header))
+               return mail_cache_set_corrupted(cache, "File too small");
+
+       cache->header = cache->mmap_base;
+       return mmap_verify_header(cache);
+}
+
+static int mail_cache_open_and_verify(struct mail_cache *cache, int silent)
+{
+       struct stat st;
+
+       mail_cache_file_close(cache);
+
+       cache->fd = open(cache->filepath, O_RDWR);
+       if (cache->fd == -1) {
+               if (errno == ENOENT)
+                       return 0;
+
+               mail_cache_set_syscall_error(cache, "open()");
+               return -1;
+       }
+
+       if (fstat(cache->fd, &st) < 0) {
+               mail_cache_set_syscall_error(cache, "fstat()");
+               return -1;
+       }
+
+       if (st.st_size < sizeof(struct mail_cache_header))
+               return 0;
+
+       cache->mmap_refresh = TRUE;
+       if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
+               return -1;
+
+       /* verify that this really is the cache for wanted index */
+       if (cache->header->indexid != cache->index->indexid) {
+               if (!silent)
+                       mail_cache_set_corrupted(cache, "IndexID mismatch");
+               return 0;
+       }
+
+       return 1;
+}
+
+static int mail_cache_open_or_create_file(struct mail_cache *cache,
+                                         struct mail_cache_header *hdr)
+{
+       int ret, fd;
+
+       cache->filepath = i_strconcat(cache->index->filepath,
+                                     MAIL_CACHE_FILE_PREFIX, NULL);
+
+       ret = mail_cache_open_and_verify(cache, FALSE);
+       if (ret != 0)
+               return ret > 0;
+
+       /* maybe a rebuild.. */
+       fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+                              MAIL_CACHE_LOCK_STALE_TIMEOUT, NULL, NULL);
+       if (fd == -1) {
+               mail_cache_set_syscall_error(cache, "file_dotlock_open()");
+               return FALSE;
+       }
+
+       /* see if someone else just created the cache file */
+       ret = mail_cache_open_and_verify(cache, TRUE);
+       if (ret != 0) {
+               (void)file_dotlock_delete(cache->filepath, fd);
+               return ret > 0;
+       }
+
+       /* rebuild then */
+       if (write_full(fd, hdr, sizeof(*hdr)) < 0) {
+               mail_cache_set_syscall_error(cache, "write_full()");
+               (void)file_dotlock_delete(cache->filepath, fd);
+               return FALSE;
+       }
+       if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
+               mail_cache_set_syscall_error(cache, "file_set_size()");
+               (void)file_dotlock_delete(cache->filepath, fd);
+               return FALSE;
+       }
+
+       mail_cache_file_close(cache);
+       cache->fd = dup(fd);
+
+       if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+               mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
+               return FALSE;
+       }
+
+       cache->mmap_refresh = TRUE;
+       if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
+               return FALSE;
+
+       return TRUE;
+}
+
+int mail_cache_open_or_create(struct mail_index *index)
+{
+        struct mail_cache_header hdr;
+       struct mail_cache *cache;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.indexid = index->indexid;
+       hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
+
+       cache = i_new(struct mail_cache, 1);
+       cache->index = index;
+       cache->fd = -1;
+        cache->split_header_pool = pool_alloconly_create("Headers", 512);
+
+       index->cache = cache;
+
+       /* we'll do anon-mmaping only if initially requested. if we fail
+          because of out of disk space, we'll just let the main index code
+          know it and fail. */
+       if (INDEX_IS_IN_MEMORY(index)) {
+               if (!mail_cache_create_memory(cache, &hdr)) {
+                       mail_cache_free(cache);
+                       return FALSE;
+               }
+       } else {
+               if (!mail_cache_open_or_create_file(cache, &hdr)) {
+                       mail_cache_free(cache);
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+void mail_cache_free(struct mail_cache *cache)
+{
+       i_assert(cache->trans_ctx == NULL);
+
+       cache->index->cache = NULL;
+
+       mail_cache_file_close(cache);
+
+       pool_unref(cache->split_header_pool);
+       i_free(cache->filepath);
+       i_free(cache);
+}
+
+void mail_cache_set_defaults(struct mail_cache *cache,
+                            enum mail_cache_field default_cache_fields,
+                            enum mail_cache_field never_cache_fields)
+{
+       cache->default_cache_fields = default_cache_fields;
+       cache->never_cache_fields = never_cache_fields;
+}
+
+static const struct mail_cache_record *
+mail_cache_compress_record(struct mail_cache *cache,
+                          struct mail_index_record *rec,
+                          enum mail_cache_field remove_fields,
+                          uint32_t *size_r)
+{
+       enum mail_cache_field cached_fields, field;
+       struct mail_cache_record cache_rec;
+       buffer_t *buffer;
+       const void *data;
+       size_t size;
+       uint32_t nb_size;
+       int i;
+
+       memset(&cache_rec, 0, sizeof(cache_rec));
+       buffer = buffer_create_dynamic(data_stack_pool, 4096, (size_t)-1);
+
+       cached_fields = mail_cache_get_fields(cache, rec) & ~remove_fields;
+       buffer_append(buffer, &cache_rec, sizeof(cache_rec));
+       for (i = 0; i < 32; i++) {
+               field = nbo32_bitmasks[i];
+               if ((cached_fields & field) == 0)
+                       continue;
+
+               if (!mail_cache_lookup_field(cache, rec, field, &data, &size)) {
+                       cached_fields &= ~field;
+                       continue;
+               }
+
+               nb_size = uint32_to_nbo((uint32_t)size);
+
+               if ((field & MAIL_CACHE_FIXED_MASK) == 0)
+                       buffer_append(buffer, &nb_size, sizeof(nb_size));
+               buffer_append(buffer, data, size);
+               if ((size & 3) != 0)
+                       buffer_append(buffer, null4, 4 - (size & 3));
+       }
+
+       cache_rec.fields = cached_fields;
+       cache_rec.size = uint32_to_nbo(buffer_get_used_size(buffer));
+       buffer_write(buffer, 0, &cache_rec, sizeof(cache_rec));
+
+       data = buffer_get_data(buffer, &size);
+       *size_r = size;
+       return data;
+}
+
+static int mail_cache_copy(struct mail_cache *cache, int fd)
+{
+       struct mail_cache_header *hdr;
+       const struct mail_cache_record *cache_rec;
+       struct mail_index_record *rec;
+        enum mail_cache_field used_fields, remove_fields;
+       unsigned char *mmap_base;
+       const char *str;
+       uint32_t new_file_size, offset, size, nb_size;
+       int i;
+
+       /* pick some reasonably good file size */
+       new_file_size = cache->used_file_size -
+               nbo_to_uint32(cache->header->deleted_space);
+       new_file_size = (new_file_size + 1023) & ~1023;
+       if (new_file_size < MAIL_CACHE_INITIAL_SIZE)
+               new_file_size = MAIL_CACHE_INITIAL_SIZE;
+
+       if (file_set_size(fd, new_file_size) < 0)
+               return mail_cache_set_syscall_error(cache, "file_set_size()");
+
+       mmap_base = mmap(NULL, new_file_size, PROT_READ | PROT_WRITE,
+                        MAP_SHARED, fd, 0);
+       if (mmap_base == MAP_FAILED)
+               return mail_cache_set_syscall_error(cache, "mmap()");
+
+       /* skip file's header */
+       hdr = (struct mail_cache_header *) mmap_base;
+       offset = sizeof(*hdr);
+
+       /* get the newest message header list */
+       remove_fields = 0;
+       for (i = MAIL_CACHE_HEADERS_COUNT-1; i >= 0; i--) {
+               str = mail_cache_get_header_fields_str(cache, i);
+               if (str != NULL) {
+                       hdr->header_offsets[0] = uint32_to_nbo(offset | 2);
+
+                       size = strlen(str) + 1;
+                       nb_size = uint32_to_nbo(size);
+
+                       memcpy(mmap_base + offset, &nb_size, sizeof(nb_size));
+                       offset += sizeof(nb_size);
+                       memcpy(mmap_base + offset, str, size);
+                       offset += (size + 3) & ~3;
+                       break;
+               }
+       }
+
+       /* remove other headers */
+       for (i--; i >= 0; i--)
+               remove_fields |= mail_cache_header_fields[i];
+
+       used_fields = 0;
+       rec = cache->index->lookup(cache->index, 1);
+       while (rec != NULL) {
+               cache_rec = mail_cache_lookup(cache, rec);
+               if (cache_rec == NULL)
+                       rec->data_offset = 0;
+               else if ((nbo_to_uint32(cache_rec->next_offset) & 2) == 0 &&
+                        remove_fields == 0) {
+                       /* just one unmodified block, copy it */
+                       size = nbo_to_uint32(cache_rec->size);
+                       i_assert(offset + size <= new_file_size);
+
+                       memcpy(mmap_base + offset, cache_rec, size);
+                       rec->data_offset = uint32_to_nbo(offset | 2);
+
+                       size = (size + 3) & ~3;
+                       offset += size;
+               } else {
+                       /* multiple blocks, sort them into buffer */
+                       t_push();
+                       cache_rec = mail_cache_compress_record(cache, rec,
+                                                              remove_fields,
+                                                              &size);
+                       i_assert(offset + size <= new_file_size);
+                       memcpy(mmap_base + offset, cache_rec, size);
+                       used_fields |= cache_rec->fields;
+                       t_pop();
+
+                       rec->data_offset = uint32_to_nbo(offset | 2);
+                       offset += size;
+               }
+
+               rec = cache->index->next(cache->index, rec);
+       }
+
+       /* update header */
+       hdr->indexid = cache->index->indexid;
+       hdr->used_file_size = uint32_to_nbo(offset);
+       hdr->used_fields = used_fields;
+       hdr->field_usage_start = uint32_to_nbo(ioloop_time);
+
+       /* write everything to disk */
+       if (msync(mmap_base, offset, MS_SYNC) < 0)
+               return mail_cache_set_syscall_error(cache, "msync()");
+
+       if (fdatasync(fd) < 0)
+               return mail_cache_set_syscall_error(cache, "fdatasync()");
+       return TRUE;
+}
+
+int mail_cache_compress(struct mail_cache *cache)
+{
+       int fd, ret = TRUE;
+
+       i_assert(cache->trans_ctx == NULL);
+
+       if (!cache->index->set_lock(cache->index, MAIL_LOCK_EXCLUSIVE))
+               return FALSE;
+
+       if (mail_cache_lock(cache, TRUE) <= 0)
+               return FALSE;
+
+       fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+                              MAIL_CACHE_LOCK_STALE_TIMEOUT, NULL, NULL);
+       if (fd == -1) {
+               mail_cache_set_syscall_error(cache, "file_dotlock_open()");
+               return FALSE;
+       }
+
+       /* now we'll begin the actual moving. keep rebuild-flag on
+          while doing it. */
+       cache->index->header->flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
+       if (!mail_index_fmdatasync(cache->index, cache->index->header_size))
+               return FALSE;
+
+       if (!mail_cache_copy(cache, fd)) {
+               (void)file_dotlock_delete(cache->filepath, fd);
+               ret = FALSE;
+       } else {
+               mail_cache_file_close(cache);
+               cache->fd = dup(fd);
+
+               if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+                       mail_cache_set_syscall_error(cache,
+                                                    "file_dotlock_replace()");
+                       ret = FALSE;
+               }
+
+               if (!mmap_update(cache, 0, 0))
+                       ret = FALSE;
+       }
+       cache->index->header->flags &= ~(MAIL_INDEX_HDR_FLAG_REBUILD |
+                                        MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE);
+
+       if (!mail_cache_unlock(cache))
+               ret = FALSE;
+
+       return ret;
+}
+
+int mail_cache_truncate(struct mail_cache *cache)
+{
+       struct mail_cache_header hdr;
+       int ret, fd;
+
+       memset(&hdr, 0, sizeof(hdr));
+       hdr.indexid = cache->index->indexid;
+       hdr.used_file_size = uint32_to_nbo(sizeof(hdr));
+       cache->used_file_size = sizeof(hdr);
+
+       if (cache->anon_mmap) {
+               *cache->header = hdr;
+               return TRUE;
+       }
+
+       ret = mail_cache_open_and_verify(cache, FALSE);
+       if (ret != 0)
+               return ret > 0;
+
+       fd = file_dotlock_open(cache->filepath, NULL, MAIL_CACHE_LOCK_TIMEOUT,
+                              MAIL_CACHE_LOCK_STALE_TIMEOUT, NULL, NULL);
+       if (fd == -1) {
+               mail_cache_set_syscall_error(cache, "file_dotlock_open()");
+               return FALSE;
+       }
+
+       if (write_full(fd, &hdr, sizeof(hdr)) < 0) {
+               mail_cache_set_syscall_error(cache, "write_full()");
+               (void)file_dotlock_delete(cache->filepath, fd);
+               return FALSE;
+       }
+       if (file_set_size(fd, MAIL_CACHE_INITIAL_SIZE) < 0) {
+               mail_cache_set_syscall_error(cache, "file_set_size()");
+               (void)file_dotlock_delete(cache->filepath, fd);
+               return FALSE;
+       }
+
+       mail_cache_file_close(cache);
+       cache->fd = dup(fd);
+
+       if (file_dotlock_replace(cache->filepath, fd, FALSE) < 0) {
+               mail_cache_set_syscall_error(cache, "file_dotlock_replace()");
+               return FALSE;
+       }
+
+       cache->mmap_refresh = TRUE;
+       if (!mmap_update(cache, 0, sizeof(struct mail_cache_header)))
+               return FALSE;
+
+       return TRUE;
+}
+
+int mail_cache_mark_file_deleted(struct mail_cache *cache)
+{
+       uint32_t indexid = 0;
+
+       if (cache->anon_mmap)
+               cache->header->indexid = 0;
+       else {
+               if (pwrite(cache->fd, &indexid, sizeof(indexid), 0) < 0)
+                       return mail_cache_set_syscall_error(cache, "pwrite()");
+       }
+       return TRUE;
+}
+
+int mail_cache_lock(struct mail_cache *cache, int nonblock)
+{
+       int ret;
+
+       if (cache->locks++ != 0)
+               return TRUE;
+
+       if (cache->anon_mmap)
+               return TRUE;
+
+       if (nonblock) {
+               ret = file_try_lock(cache->fd, F_WRLCK);
+               if (ret < 0)
+                       mail_cache_set_syscall_error(cache, "file_try_lock()");
+       } else {
+               ret = file_wait_lock(cache->fd, F_WRLCK);
+               if (ret <= 0)
+                       mail_cache_set_syscall_error(cache, "file_wait_lock()");
+       }
+
+       if (ret > 0) {
+               if (!mmap_verify_header(cache)) {
+                       (void)mail_cache_unlock(cache);
+                       ret = -1;
+               }
+       }
+       return ret;
+}
+
+int mail_cache_unlock(struct mail_cache *cache)
+{
+       if (--cache->locks > 0)
+               return TRUE;
+
+       if (cache->anon_mmap)
+               return TRUE;
+
+       if (file_wait_lock(cache->fd, F_UNLCK) <= 0) {
+               mail_cache_set_syscall_error(cache, "file_wait_lock(F_UNLCK)");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+void mail_cache_unlock_later(struct mail_cache *cache)
+{
+       cache->index->cache_later_locks++;
+}
+
+int mail_cache_is_locked(struct mail_cache *cache)
+{
+       return cache->locks > 0;
+}
+
+int mail_cache_transaction_begin(struct mail_cache *cache, int nonblock,
+                                struct mail_cache_transaction_ctx **ctx_r)
+{
+       int ret;
+
+       i_assert(cache->trans_ctx == NULL);
+
+       ret = mail_cache_lock(cache, nonblock);
+       if (ret <= 0)
+               return ret;
+
+       *ctx_r = i_new(struct mail_cache_transaction_ctx, 1);
+       (*ctx_r)->cache = cache;
+       (*ctx_r)->cache_data =
+               buffer_create_dynamic(system_pool, 8192, (size_t)-1);
+       (*ctx_r)->last_idx = (unsigned int)-1;
+
+       cache->trans_ctx = *ctx_r;
+       return 1;
+}
+
+int mail_cache_transaction_end(struct mail_cache_transaction_ctx *ctx)
+{
+       int ret = TRUE;
+
+       i_assert(ctx->cache->trans_ctx != NULL);
+
+       (void)mail_cache_transaction_rollback(ctx);
+
+       if (!mail_cache_unlock(ctx->cache))
+               ret = FALSE;
+
+       ctx->cache->trans_ctx = NULL;
+
+       if (ctx->cache_marks != NULL)
+               buffer_free(ctx->cache_marks);
+       if (ctx->index_marks != NULL)
+               buffer_free(ctx->index_marks);
+       buffer_free(ctx->cache_data);
+       i_free(ctx);
+       return ret;
+}
+
+static void mail_cache_transaction_flush(struct mail_cache_transaction_ctx *ctx)
+{
+       memset(&ctx->cache_rec, 0, sizeof(ctx->cache_rec));
+       ctx->last_idx = (unsigned int)-1;
+
+       ctx->next_unused_header_lowwater = 0;
+       ctx->first_uid = ctx->last_uid = 0;
+
+       if (ctx->cache_marks != NULL)
+               buffer_set_used_size(ctx->cache_marks, 0);
+       if (ctx->index_marks != NULL)
+               buffer_set_used_size(ctx->index_marks, 0);
+       buffer_set_used_size(ctx->cache_data, 0);
+}
+
+static void mark_update(buffer_t **buf, uint32_t offset, uint32_t data)
+{
+       unsigned char lower_data;
+
+       if (*buf == NULL)
+               *buf = buffer_create_dynamic(system_pool, 1024, (size_t)-1);
+
+       /* data is in big endian, we want to update only the lowest byte */
+       offset += sizeof(uint32_t) - 1;
+       buffer_append(*buf, &offset, sizeof(offset));
+
+       lower_data = data & 0xff;
+       buffer_append(*buf, &lower_data, 1);
+}
+
+static int write_mark_updates(struct mail_index *index, buffer_t *marks,
+                             const char *path, int fd)
+{
+       const unsigned char *data, *end;
+       uint32_t offset;
+       size_t size;
+
+       data = buffer_get_data(marks, &size);
+       end = data + size;
+
+       while (data < end) {
+               memcpy(&offset, data, sizeof(offset));
+               data += sizeof(offset);
+
+               if (pwrite(fd, data, sizeof(*data), offset) < 0) {
+                       index_file_set_syscall_error(index, path, "pwrite()");
+                       return FALSE;
+               }
+               data++;
+       }
+       return TRUE;
+}
+
+static void write_mark_updates_in_memory(buffer_t *marks, void *mmap_base,
+                                        size_t mmap_length)
+{
+       const unsigned char *data, *end;
+       uint32_t offset;
+       size_t size;
+
+       data = buffer_get_data(marks, &size);
+       end = data + size;
+
+       while (data < end) {
+               memcpy(&offset, data, sizeof(offset));
+               data += sizeof(offset);
+
+               i_assert(offset < mmap_length);
+               ((char *) mmap_base)[offset] = *data;
+               data++;
+       }
+}
+
+static void commit_all_changes_in_memory(struct mail_cache_transaction_ctx *ctx)
+{
+       struct mail_cache *cache = ctx->cache;
+
+       if (ctx->cache_marks != NULL) {
+               write_mark_updates_in_memory(ctx->cache_marks,
+                                            cache->mmap_base,
+                                            cache->mmap_length);
+       }
+       if (ctx->index_marks != NULL) {
+               write_mark_updates_in_memory(ctx->index_marks,
+                                            cache->index->mmap_base,
+                                            cache->index->mmap_used_length);
+       }
+}
+
+static int commit_all_changes(struct mail_cache_transaction_ctx *ctx)
+{
+       struct mail_cache *cache = ctx->cache;
+       uint32_t cont;
+
+       if (ctx->cache->anon_mmap) {
+               commit_all_changes_in_memory(ctx);
+               return TRUE;
+       }
+
+       /* write everything to disk */
+       if (msync(cache->mmap_base, cache->mmap_length, MS_SYNC) < 0)
+               return mail_cache_set_syscall_error(cache, "msync()");
+
+       if (fdatasync(cache->fd) < 0)
+               return mail_cache_set_syscall_error(cache, "fdatasync()");
+
+       if (ctx->cache_marks != NULL &&
+           buffer_get_used_size(ctx->cache_marks) != 0) {
+               /* now that we're sure it's there, set on all the used-bits */
+               if (!write_mark_updates(cache->index, ctx->cache_marks,
+                                       cache->filepath, cache->fd))
+                       return FALSE;
+
+               /* update continued records count */
+               cont = nbo_to_uint32(cache->header->continued_record_count);
+
+               cont += buffer_get_used_size(ctx->cache_marks) /
+                       (sizeof(uint32_t)+1);
+
+               if (cont * 100 / cache->index->header->messages_count >=
+                   COMPRESS_CONTINUED_PERCENTAGE &&
+                   cache->used_file_size >= COMPRESS_MIN_SIZE) {
+                       /* too many continued rows, compress */
+                       cache->index->set_flags |=
+                               MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;
+               }
+
+               cache->header->continued_record_count = uint32_to_nbo(cont);
+       }
+
+       /* write index last */
+       if (ctx->index_marks != NULL &&
+           buffer_get_used_size(ctx->index_marks) != 0) {
+               if (!mail_index_fmdatasync(cache->index,
+                                          cache->index->mmap_used_length))
+                       return FALSE;
+
+               if (!write_mark_updates(cache->index, ctx->index_marks,
+                                       cache->index->filepath,
+                                       cache->index->fd))
+                       return FALSE;
+       }
+       return TRUE;
+}
+
+int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx)
+{
+       int ret = TRUE;
+
+       if (ctx->last_idx != (unsigned int)-1) {
+               if (!mail_cache_write(ctx))
+                       return FALSE;
+       }
+
+       ctx->cache->header->used_file_size =
+               uint32_to_nbo(ctx->cache->used_file_size);
+
+       if (!commit_all_changes(ctx))
+               ret = FALSE;
+
+       mail_cache_transaction_flush(ctx);
+       return ret;
+}
+
+int mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx)
+{
+       /* no need to actually modify the file - we just didn't update
+          used_file_size */
+       ctx->cache->used_file_size =
+               nbo_to_uint32(ctx->cache->header->used_file_size);
+
+       mail_cache_transaction_flush(ctx);
+       return TRUE;
+}
+
+static int mail_cache_grow(struct mail_cache *cache, uint32_t size)
+{
+       struct stat st;
+       void *base;
+       uoff_t grow_size, new_fsize;
+
+       new_fsize = cache->used_file_size + size;
+       grow_size = new_fsize / 100 * MAIL_CACHE_GROW_PERCENTAGE;
+       if (grow_size < 16384)
+               grow_size = 16384;
+
+       new_fsize += grow_size;
+       new_fsize &= ~1023;
+
+       if (cache->anon_mmap) {
+               i_assert(new_fsize < SSIZE_T_MAX);
+
+               base = mremap_anon(cache->mmap_base, cache->mmap_length,
+                                  (size_t)new_fsize, MREMAP_MAYMOVE);
+               if (base == MAP_FAILED) {
+                       mail_cache_set_syscall_error(cache, "mremap_anon()");
+                       return FALSE;
+               }
+
+               cache->mmap_base = base;
+               cache->mmap_length = (size_t)new_fsize;
+               cache->header = cache->mmap_base;
+               return TRUE;
+       }
+
+       if (fstat(cache->fd, &st) < 0)
+               return mail_cache_set_syscall_error(cache, "fstat()");
+
+       if (cache->used_file_size + size <= (uoff_t)st.st_size) {
+               /* no need to grow, just update mmap */
+               if (!mmap_update(cache, 0, 0))
+                       return FALSE;
+
+               i_assert(cache->mmap_length >= (uoff_t)st.st_size);
+               return TRUE;
+       }
+
+       if (st.st_size < (off_t)sizeof(struct mail_cache_header))
+               return mail_cache_set_corrupted(cache, "Header is missing");
+
+       if (file_set_size(cache->fd, (off_t)new_fsize) < 0)
+               return mail_cache_set_syscall_error(cache, "file_set_size()");
+
+       return mmap_update(cache, 0, 0);
+}
+
+static uint32_t mail_cache_append_space(struct mail_cache_transaction_ctx *ctx,
+                                       uint32_t size)
+{
+       /* NOTE: must be done within transaction or rollback would break it */
+       uint32_t offset;
+
+       i_assert((size % sizeof(uint32_t)) == 0);
+
+       offset = ctx->cache->used_file_size;
+       if (offset + size > ctx->cache->mmap_length) {
+               if (!mail_cache_grow(ctx->cache, size))
+                       return 0;
+       }
+
+       ctx->cache->used_file_size += size;
+       return offset;
+}
+
+static const char *
+mail_cache_get_header_fields_str(struct mail_cache *cache, unsigned int idx)
+{
+       uint32_t offset, data_size;
+       unsigned char *buf;
+
+       offset = nbo_to_uint32(cache->header->header_offsets[idx]);
+
+       if ((offset & 2) == 0 &&
+           (cache->trans_ctx == NULL ||
+            cache->trans_ctx->next_unused_header_lowwater <= idx))
+               return NULL;
+       offset &= ~2;
+
+       if (!mmap_update(cache, offset, 1024))
+               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));
+       data_size = nbo_to_uint32(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 (!mmap_update(cache, offset, data_size))
+               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;
+}
+
+static const char *const *
+split_header(struct mail_cache *cache, const char *header)
+{
+       const char *const *arr, *const *tmp;
+       const char *null = NULL;
+       char *str;
+       buffer_t *buf;
+
+       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 *cache,
+                                               unsigned int idx)
+{
+       const char *str;
+       int i;
+
+       i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
+
+       /* t_strsplit() is a bit slow, so we cache it */
+       if (cache->header->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->header->header_offsets[i];
+
+                       str = mail_cache_get_header_fields_str(cache, idx);
+                       cache->split_headers[i] = split_header(cache, str);
+               }
+               t_pop();
+       }
+
+       return cache->split_headers[idx];
+}
+
+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, update_offset, size;
+       buffer_t *buffer;
+
+       i_assert(idx < MAIL_CACHE_HEADERS_COUNT);
+       i_assert(idx >= ctx->next_unused_header_lowwater);
+       i_assert((nbo_to_uint32(cache->header->header_offsets[idx]) & 2) == 0);
+
+       t_push();
+
+       buffer = buffer_create_dynamic(data_stack_pool, 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);
+       }
+
+       offset = mail_cache_append_space(ctx, size + sizeof(uint32_t));
+       if (offset != 0) {
+               memcpy((char *) cache->mmap_base + offset + sizeof(uint32_t),
+                      buffer_get_data(buffer, NULL), size);
+
+               size = uint32_to_nbo(size);
+               memcpy((char *) cache->mmap_base + offset,
+                      &size, sizeof(uint32_t));
+
+               cache->header->header_offsets[idx] = uint32_to_nbo(offset);
+
+               /* mark used-bit to be updated later. not really needed for
+                  read-safety, but if transaction get rolled back we can't let
+                  this point to invalid location. */
+               update_offset = (char *) &cache->header->header_offsets[idx] -
+                       (char *) cache->mmap_base;
+               mark_update(&ctx->cache_marks, update_offset, offset | 2);
+
+               /* 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;
+}
+
+static struct mail_cache_record *
+cache_get_record(struct mail_cache *cache, uint32_t offset)
+{
+       struct mail_cache_record *cache_rec;
+       size_t size;
+
+       offset = nbo_to_uint32(offset);
+
+       if ((offset & 1) != 0) {
+               mail_cache_set_corrupted(cache, "bit 0 set in data offset");
+               return NULL;
+       }
+       if ((offset & 2) == 0)
+               return NULL;
+       offset &= ~2;
+
+       if (!mmap_update(cache, offset, sizeof(*cache_rec) + 1024))
+               return NULL;
+
+       if (offset + sizeof(*cache_rec) > cache->mmap_length) {
+               mail_cache_set_corrupted(cache, "record points outside file");
+               return NULL;
+       }
+       cache_rec = CACHE_RECORD(cache, offset);
+
+       size = nbo_to_uint32(cache_rec->size);
+       if (!mmap_update(cache, offset, sizeof(*cache_rec) + size))
+               return NULL;
+
+       if (offset + sizeof(*cache_rec) + size > cache->mmap_length) {
+               mail_cache_set_corrupted(cache, "record points outside file");
+               return NULL;
+       }
+       return cache_rec;
+}
+
+static struct mail_cache_record *
+cache_get_next_record(struct mail_cache *cache, struct mail_cache_record *rec)
+{
+       struct mail_cache_record *next;
+
+       next = cache_get_record(cache, rec->next_offset);
+       if (next != NULL && next <= rec) {
+               mail_cache_set_corrupted(cache, "next_offset points backwards");
+               return NULL;
+       }
+       return next;
+}
+
+static int mail_cache_write(struct mail_cache_transaction_ctx *ctx)
+{
+       struct mail_cache *cache = ctx->cache;
+       struct mail_cache_record *cache_rec, *next;
+       struct mail_index_record *rec;
+       uint32_t write_offset, update_offset;
+       const void *buf;
+       size_t size, buf_size;
+
+       buf = buffer_get_data(ctx->cache_data, &buf_size);
+
+       size = sizeof(*cache_rec) + buf_size;
+       ctx->cache_rec.size = uint32_to_nbo(size);
+
+       write_offset = mail_cache_append_space(ctx, size);
+       if (write_offset == 0)
+               return FALSE;
+
+       rec = INDEX_RECORD_AT(ctx->cache->index, ctx->last_idx);
+       ctx->last_idx = (unsigned int)-1;
+
+       cache_rec = cache_get_record(cache, rec->data_offset);
+       if (cache_rec == NULL) {
+               /* first cache record - update offset in index file */
+               i_assert(cache->index->lock_type == MAIL_LOCK_EXCLUSIVE);
+
+               rec->data_offset = uint32_to_nbo(write_offset);
+
+               /* mark used-bit to be updated later */
+               update_offset = (char *) &rec->data_offset -
+                       (char *) cache->index->mmap_base;
+               mark_update(&ctx->index_marks, update_offset, write_offset | 2);
+       } else {
+               /* find the last cache record */
+               while ((next = cache_get_next_record(cache, cache_rec)) != NULL)
+                       cache_rec = next;
+
+               /* set our offset, keep the used-bit still unset */
+               cache_rec->next_offset = uint32_to_nbo(write_offset);
+
+               /* mark used-bit to be updated later */
+               update_offset = (char *) &cache_rec->next_offset -
+                       (char *) cache->mmap_base;
+               mark_update(&ctx->cache_marks, update_offset, write_offset | 2);
+       }
+
+       memcpy((char *) cache->mmap_base + write_offset,
+              &ctx->cache_rec, sizeof(ctx->cache_rec));
+       memcpy((char *) cache->mmap_base + write_offset +
+              sizeof(ctx->cache_rec), buf, buf_size);
+
+       /* reset the write context */
+       memset(&ctx->cache_rec, 0, sizeof(ctx->cache_rec));
+       buffer_set_used_size(ctx->cache_data, 0);
+       return TRUE;
+}
+
+static struct mail_cache_record *
+mail_cache_lookup(struct mail_cache *cache, const struct mail_index_record *rec)
+{
+       struct mail_cache_record *cache_rec;
+       unsigned int idx;
+
+       if (cache->trans_ctx != NULL &&
+           cache->trans_ctx->first_uid <= rec->uid &&
+           cache->trans_ctx->last_uid >= rec->uid) {
+               /* we have to auto-commit since we're not capable of looking
+                  into uncommitted records. it would be possible by checking
+                  index_marks and cache_marks, but it's just more trouble
+                  than worth. */
+               idx = INDEX_RECORD_INDEX(cache->index, rec);
+               if (cache->trans_ctx->last_idx == idx) {
+                       if (!mail_cache_write(cache->trans_ctx))
+                               return NULL;
+               }
+
+               if (!mail_cache_transaction_commit(cache->trans_ctx))
+                       return NULL;
+       }
+
+       cache_rec = cache_get_record(cache, rec->data_offset);
+       if (cache_rec == NULL)
+               return NULL;
+
+       if (cache_rec->fields == 0) {
+               mail_cache_set_corrupted(cache, "record has no fields");
+               return NULL;
+       }
+       return cache_rec;
+}
+
+static int get_field_num(enum mail_cache_field field)
+{
+       int i;
+
+       for (i = 0; i < 32; i++) {
+               if ((field & nbo32_bitmasks[i]) != 0)
+                       return i;
+       }
+
+       return -1;
+}
+
+static size_t get_insert_offset(struct mail_cache_transaction_ctx *ctx,
+                               enum mail_cache_field field)
+{
+       const unsigned char *buf;
+       unsigned int mask;
+       uint32_t data_size;
+       size_t offset = 0;
+       int i;
+
+       buf = buffer_get_data(ctx->cache_data, NULL);
+
+       for (i = 0; i < 32; i++) {
+               mask = nbo32_bitmasks[i];
+               if ((field & mask) != 0)
+                       return offset;
+
+               if ((ctx->cache_rec.fields & mask) != 0) {
+                       if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
+                               data_size = mail_cache_field_sizes[i];
+                       else {
+                               memcpy(&data_size, buf + offset,
+                                      sizeof(data_size));
+                               data_size = nbo_to_uint32(data_size);
+                               offset += sizeof(data_size);
+                       }
+                       offset += (data_size + 3) & ~3;
+               }
+       }
+
+       i_unreached();
+       return offset;
+}
+
+int mail_cache_add(struct mail_cache_transaction_ctx *ctx,
+                  struct mail_index_record *rec, enum mail_cache_field field,
+                  const void *data, size_t data_size)
+{
+       uint32_t nb_data_size;
+       size_t full_size, offset;
+       unsigned char *buf;
+       unsigned int idx;
+       int field_num;
+
+       i_assert(data_size > 0);
+       i_assert(data_size < (uint32_t)-1);
+
+       nb_data_size = uint32_to_nbo((uint32_t)data_size);
+
+       if ((field & MAIL_CACHE_FIXED_MASK) != 0) {
+               field_num = get_field_num(field);
+               i_assert(field_num != -1);
+               i_assert(mail_cache_field_sizes[field_num] == data_size);
+       } else if ((field & MAIL_CACHE_STRING_MASK) != 0) {
+               i_assert(((char *) data)[data_size-1] == '\0');
+       }
+
+       /* NOTE: we use index because the record pointer might not last. */
+        idx = INDEX_RECORD_INDEX(ctx->cache->index, rec);
+       if (ctx->last_idx != idx && ctx->last_idx != (unsigned int)-1) {
+               if (!mail_cache_write(ctx))
+                       return FALSE;
+       }
+       ctx->last_idx = idx;
+
+       i_assert((ctx->cache_rec.fields & field) == 0);
+
+       full_size = (data_size + 3) & ~3;
+       if ((field & MAIL_CACHE_FIXED_MASK) == 0)
+               full_size += sizeof(nb_data_size);
+
+       /* fields must be ordered. find where to insert it. */
+       if (field > ctx->cache_rec.fields)
+                buf = buffer_append_space_unsafe(ctx->cache_data, full_size);
+       else {
+               offset = get_insert_offset(ctx, field);
+               buffer_copy(ctx->cache_data, offset + full_size,
+                           ctx->cache_data, offset, (size_t)-1);
+               buf = buffer_get_space_unsafe(ctx->cache_data,
+                                             offset, full_size);
+       }
+       ctx->cache_rec.fields |= field;
+
+       /* @UNSAFE */
+       if ((field & MAIL_CACHE_FIXED_MASK) == 0) {
+               memcpy(buf, &nb_data_size, sizeof(nb_data_size));
+               buf += sizeof(nb_data_size);
+       }
+       memcpy(buf, data, data_size); buf += data_size;
+       if ((data_size & 3) != 0)
+               memset(buf, 0, 4 - (data_size & 3));
+
+       /* remember the transaction uid range */
+       if (rec->uid < ctx->first_uid || ctx->first_uid == 0)
+               ctx->first_uid = rec->uid;
+       if (rec->uid > ctx->last_uid)
+               ctx->last_uid = rec->uid;
+
+       return TRUE;
+}
+
+int mail_cache_delete(struct mail_cache_transaction_ctx *ctx,
+                     struct mail_index_record *rec)
+{
+       struct mail_cache *cache = ctx->cache;
+       struct mail_cache_record *cache_rec;
+       uint32_t deleted_space;
+       uoff_t max_del_space;
+
+       cache_rec = mail_cache_lookup(cache, rec);
+       if (cache_rec == NULL)
+               return TRUE;
+
+       /* NOTE: it would be nice to erase the cached data for the record,
+          but some other processes might still be using them. So, we just
+          update the deleted_space in header */
+       deleted_space = nbo_to_uint32(cache->header->deleted_space);
+
+       do {
+               deleted_space -= nbo_to_uint32(cache_rec->size);
+               cache_rec = cache_get_next_record(cache, cache_rec);
+       } while (cache_rec != NULL);
+
+       /* see if we've reached the max. deleted space in file */
+       max_del_space = cache->used_file_size / 100 * COMPRESS_PERCENTAGE;
+       if (deleted_space >= max_del_space &&
+           cache->used_file_size >= COMPRESS_MIN_SIZE)
+               cache->index->set_flags |= MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE;
+
+       cache->header->deleted_space = uint32_to_nbo(deleted_space);
+
+       return TRUE;
+}
+
+enum mail_cache_field
+mail_cache_get_fields(struct mail_cache *cache,
+                     const struct mail_index_record *rec)
+{
+       struct mail_cache_record *cache_rec;
+        enum mail_cache_field fields = 0;
+
+       cache_rec = mail_cache_lookup(cache, rec);
+       while (cache_rec != NULL) {
+               fields |= cache_rec->fields;
+               cache_rec = cache_get_next_record(cache, cache_rec);
+       }
+
+       return fields;
+}
+
+static int cache_get_field(struct mail_cache *cache,
+                          struct mail_cache_record *cache_rec,
+                          enum mail_cache_field field,
+                          void **data_r, size_t *size_r)
+{
+       unsigned char *buf;
+       unsigned int mask;
+       uint32_t rec_size, data_size;
+       size_t offset = 0;
+       int i;
+
+       rec_size = nbo_to_uint32(cache_rec->size);
+       buf = (unsigned char *) cache_rec + sizeof(*cache_rec);
+
+       for (i = 0; i < 32; i++) {
+               mask = nbo32_bitmasks[i];
+               if ((cache_rec->fields & mask) == 0)
+                       continue;
+
+               if ((mask & MAIL_CACHE_FIXED_MASK) != 0)
+                       data_size = mail_cache_field_sizes[i];
+               else {
+                       memcpy(&data_size, buf + offset, sizeof(data_size));
+                       data_size = nbo_to_uint32(data_size);
+                       offset += sizeof(data_size);
+               }
+
+               if (field == mask) {
+                       if (data_size == 0) {
+                               mail_cache_set_corrupted(cache,
+                                                        "Field size is 0");
+                               return FALSE;
+                       }
+                       *data_r = buf + offset;
+                       *size_r = data_size;
+                       return TRUE;
+               }
+
+               offset += (data_size + 3) & ~3;
+
+               if (offset >= rec_size) {
+                       mail_cache_set_corrupted(cache,
+                               "Record continues outside it's allocated size");
+                       return FALSE;
+               }
+       }
+
+       i_unreached();
+       return FALSE;
+}
+
+static int cache_lookup_field(struct mail_cache *cache,
+                             const struct mail_index_record *rec,
+                             enum mail_cache_field field,
+                             void **data_r, size_t *size_r)
+{
+       struct mail_cache_record *cache_rec;
+
+       cache_rec = mail_cache_lookup(cache, rec);
+       while (cache_rec != NULL) {
+               if ((cache_rec->fields & field) != 0) {
+                       return cache_get_field(cache, cache_rec, field,
+                                              data_r, size_r);
+               }
+               cache_rec = cache_get_next_record(cache, cache_rec);
+       }
+
+       return FALSE;
+}
+
+int mail_cache_lookup_field(struct mail_cache *cache,
+                           const struct mail_index_record *rec,
+                           enum mail_cache_field field,
+                           const void **data_r, size_t *size_r)
+{
+       void *data;
+
+       if (!cache_lookup_field(cache, rec, field, &data, size_r))
+               return FALSE;
+
+       *data_r = data;
+       return TRUE;
+}
+
+const char *mail_cache_lookup_string_field(struct mail_cache *cache,
+                                          const struct mail_index_record *rec,
+                                          enum mail_cache_field field)
+{
+       const void *data;
+       size_t size;
+
+       i_assert((field & MAIL_CACHE_STRING_MASK) != 0);
+
+       if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
+               return NULL;
+
+       if (((const char *) data)[size-1] != '\0') {
+               mail_cache_set_corrupted(cache,
+                       "String field %x doesn't end with NUL", field);
+               return NULL;
+       }
+       return data;
+}
+
+int mail_cache_copy_fixed_field(struct mail_cache *cache,
+                               const struct mail_index_record *rec,
+                               enum mail_cache_field field,
+                               void *buffer, size_t buffer_size)
+{
+       const void *data;
+       size_t size;
+
+       i_assert((field & MAIL_CACHE_FIXED_MASK) != 0);
+
+       if (!mail_cache_lookup_field(cache, rec, field, &data, &size))
+               return FALSE;
+
+       if (buffer_size != size) {
+               i_panic("cache: fixed field %x wrong size "
+                       "(%"PRIuSIZE_T" vs %"PRIuSIZE_T")",
+                       field, size, buffer_size);
+       }
+
+       memcpy(buffer, data, buffer_size);
+       return TRUE;
+}
+
+void mail_cache_mark_missing(struct mail_cache *cache,
+                            enum mail_cache_field fields)
+{
+       // FIXME: count these
+}
+
+enum mail_index_record_flag
+mail_cache_get_index_flags(struct mail_cache *cache,
+                          const struct mail_index_record *rec)
+{
+       enum mail_index_record_flag flags;
+
+       if (!mail_cache_copy_fixed_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
+                                        &flags, sizeof(flags)))
+               return 0;
+
+       return flags;
+}
+
+int mail_cache_update_index_flags(struct mail_cache *cache,
+                                 struct mail_index_record *rec,
+                                 enum mail_index_record_flag flags)
+{
+       void *data;
+       size_t size;
+
+       i_assert(cache->locks > 0);
+
+       if (!cache_lookup_field(cache, rec, MAIL_CACHE_INDEX_FLAGS,
+                               &data, &size)) {
+               mail_cache_set_corrupted(cache,
+                       "Missing index flags for record %u", rec->uid);
+               return FALSE;
+       }
+
+       memcpy(data, &flags, sizeof(flags));
+       return TRUE;
+}
+
+void *mail_cache_get_mmaped(struct mail_cache *cache, size_t *size)
+{
+       if (!mmap_update(cache, 0, 0))
+               return NULL;
+
+       *size = cache->mmap_length;
+       return cache->mmap_base;
+}
+
+int mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
+{
+       va_list va;
+
+       /* rebuild */
+        INDEX_MARK_CORRUPTED(cache->index);
+        mail_cache_mark_file_deleted(cache);
+
+       va_start(va, fmt);
+       t_push();
+       index_set_error(cache->index, "Corrupted index cache file %s: %s",
+                       cache->filepath, t_strdup_vprintf(fmt, va));
+       t_pop();
+       va_end(va);
+
+       return FALSE;
+}
diff --git a/src/lib-index/mail-cache.h b/src/lib-index/mail-cache.h
new file mode 100644 (file)
index 0000000..3526b64
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef __MAIL_CACHE_H
+#define __MAIL_CACHE_H
+
+#include "byteorder.h"
+#include "mail-index.h"
+
+#define MAIL_CACHE_FILE_PREFIX ".cache"
+
+#define MAIL_CACHE_HEADERS_COUNT 4
+
+struct mail_cache_transaction_ctx;
+
+enum mail_cache_field {
+       /* fixed size fields */
+       MAIL_CACHE_INDEX_FLAGS          = NBO32_BIT0,
+       MAIL_CACHE_LOCATION_OFFSET      = NBO32_BIT1,
+       MAIL_CACHE_MD5                  = NBO32_BIT2,
+       MAIL_CACHE_SENT_DATE            = NBO32_BIT3,
+       MAIL_CACHE_RECEIVED_DATE        = NBO32_BIT4,
+       MAIL_CACHE_HEADER_SIZE          = NBO32_BIT5,
+       MAIL_CACHE_BODY_SIZE            = NBO32_BIT6,
+       MAIL_CACHE_VIRTUAL_HEADER_SIZE  = NBO32_BIT7,
+       MAIL_CACHE_VIRTUAL_FULL_SIZE    = NBO32_BIT8,
+
+       /* variable sized field */
+       MAIL_CACHE_HEADERS1             = NBO32_BIT31,
+       MAIL_CACHE_HEADERS2             = NBO32_BIT30,
+       MAIL_CACHE_HEADERS3             = NBO32_BIT29,
+       MAIL_CACHE_HEADERS4             = NBO32_BIT28,
+       MAIL_CACHE_LOCATION             = NBO32_BIT27,
+       MAIL_CACHE_BODY                 = NBO32_BIT26,
+       MAIL_CACHE_BODYSTRUCTURE        = NBO32_BIT25,
+       MAIL_CACHE_MESSAGEPART          = NBO32_BIT24,
+
+       MAIL_CACHE_FIXED_MASK           = MAIL_CACHE_INDEX_FLAGS |
+                                         MAIL_CACHE_LOCATION_OFFSET |
+                                         MAIL_CACHE_MD5 |
+                                         MAIL_CACHE_SENT_DATE |
+                                         MAIL_CACHE_RECEIVED_DATE |
+                                         MAIL_CACHE_HEADER_SIZE |
+                                         MAIL_CACHE_BODY_SIZE |
+                                         MAIL_CACHE_VIRTUAL_HEADER_SIZE |
+                                         MAIL_CACHE_VIRTUAL_FULL_SIZE,
+       MAIL_CACHE_HEADERS_MASK         = MAIL_CACHE_HEADERS1 |
+                                         MAIL_CACHE_HEADERS2 |
+                                         MAIL_CACHE_HEADERS4 |
+                                         MAIL_CACHE_HEADERS4,
+       MAIL_CACHE_STRING_MASK          = MAIL_CACHE_HEADERS_MASK |
+                                         MAIL_CACHE_LOCATION |
+                                         MAIL_CACHE_BODY |
+                                         MAIL_CACHE_BODYSTRUCTURE,
+       MAIL_CACHE_BODYSTRUCTURE_MASK   = MAIL_CACHE_BODY |
+                                         MAIL_CACHE_BODYSTRUCTURE |
+                                          MAIL_CACHE_MESSAGEPART
+};
+
+struct mail_sent_date {
+       time_t time;
+       int timezone;
+};
+
+extern enum mail_cache_field mail_cache_header_fields[MAIL_CACHE_HEADERS_COUNT];
+
+int 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,
+                            enum mail_cache_field default_cache_fields,
+                            enum mail_cache_field never_cache_fields);
+
+/* Compress cache file. */
+int mail_cache_compress(struct mail_cache *cache);
+
+/* Truncate the cache file and update it's indexid */
+int mail_cache_truncate(struct mail_cache *cache);
+
+/* Set indexid to 0 to notify other processes using this file that they should
+   re-open it. */
+int mail_cache_mark_file_deleted(struct mail_cache *cache);
+
+/* Explicitly lock the cache file. Returns 1 if ok, 0 if nonblock is TRUE and
+   we couldn't immediately get a lock, or -1 if error. */
+int mail_cache_lock(struct mail_cache *cache, int nonblock);
+int mail_cache_unlock(struct mail_cache *cache);
+
+/* Mark the lock to be removed when unlocking index file. */
+void mail_cache_unlock_later(struct mail_cache *cache);
+
+/* Returns TRUE if cache file is locked. */
+int mail_cache_is_locked(struct mail_cache *cache);
+
+/* Begin transaction. Returns same as mail_cache_lock(). Note that if you
+   call lookup functions for messages within first and last message in
+   transaction, the transaction will be automatically committed. */
+int mail_cache_transaction_begin(struct mail_cache *cache, int nonblock,
+                                struct mail_cache_transaction_ctx **ctx_r);
+/* End transaction. Single transaction can have multiple commits/rollbacks.
+   If there's any pending changes, they will be rolled back. */
+int mail_cache_transaction_end(struct mail_cache_transaction_ctx *ctx);
+
+int mail_cache_transaction_commit(struct mail_cache_transaction_ctx *ctx);
+int mail_cache_transaction_rollback(struct mail_cache_transaction_ctx *ctx);
+
+/* 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 *cache,
+                                               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 and they're converted to network byte
+   order in disk. */
+int mail_cache_add(struct mail_cache_transaction_ctx *ctx,
+                  struct mail_index_record *rec, enum mail_cache_field field,
+                  const void *data, size_t data_size);
+
+/* Mark the given record deleted. */
+int mail_cache_delete(struct mail_cache_transaction_ctx *ctx,
+                     struct mail_index_record *rec);
+
+/* Return all fields that are currently cached for record. */
+enum mail_cache_field
+mail_cache_get_fields(struct mail_cache *cache,
+                     const struct mail_index_record *rec);
+
+/* 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. */
+int mail_cache_lookup_field(struct mail_cache *cache,
+                           const struct mail_index_record *rec,
+                           enum mail_cache_field field,
+                           const void **data_r, size_t *size_r);
+
+/* Return string field. */
+const char *mail_cache_lookup_string_field(struct mail_cache *cache,
+                                          const struct mail_index_record *rec,
+                                          enum mail_cache_field field);
+
+
+/* Copy fixed size field to given buffer. buffer_size must be exactly the
+   expected size. The result will be converted to host byte order.
+   Returns TRUE if field was found. */
+int mail_cache_copy_fixed_field(struct mail_cache *cache,
+                               const struct mail_index_record *rec,
+                               enum mail_cache_field field,
+                               void *buffer, size_t buffer_size);
+
+/* Mark given fields as missing, ie. they should be cached when possible. */
+void mail_cache_mark_missing(struct mail_cache *cache,
+                            enum mail_cache_field fields);
+
+/* Return index flags. */
+enum mail_index_record_flag
+mail_cache_get_index_flags(struct mail_cache *cache,
+                          const struct mail_index_record *rec);
+
+/* Update index flags. The cache file must be locked and the flags must be
+   already inserted to the record. */
+int mail_cache_update_index_flags(struct mail_cache *cache,
+                                 struct mail_index_record *rec,
+                                 enum mail_index_record_flag flags);
+
+/* Return the whole file mmaped. */
+void *mail_cache_get_mmaped(struct mail_cache *cache, size_t *size);
+
+/* "Error in index cache file %s: ...". */
+int mail_cache_set_corrupted(struct mail_cache *cache, const char *fmt, ...)
+       __attr_format__(2, 3);
+
+#endif
index a66bf886d1d0407c63655c9bc26575e3bb5d1bd8..ae1753d6ccc37e91272e822fe1b282b8289b8343 100644 (file)
@@ -534,7 +534,7 @@ static int get_flag_index(struct mail_custom_flags *mcf, const char *flag,
        if (!custom_flags_add(mcf, first_empty, flag))
                return -1;
 
-       mcf->index->set_flags |= MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS;
+       mcf->index->set_flags |= MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS;
 
        mcf->custom_flags[first_empty] = i_strdup(flag);
        return first_empty;
index 0f78e0e01a88c3f990473a4a688ccff7fa42398a..52093337ce8db0e8f38584e5b83705b8688f8861 100644 (file)
@@ -3,8 +3,8 @@
 #include "lib.h"
 #include "write-full.h"
 #include "mail-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 #include <unistd.h>
@@ -15,7 +15,8 @@ int mail_index_truncate(struct mail_index *index)
 
        i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
 
-       if (index->mmap_full_length <= INDEX_FILE_MIN_SIZE || index->anon_mmap)
+       if (index->mmap_full_length <= INDEX_FILE_MIN_SIZE(index) ||
+           index->anon_mmap)
                return TRUE;
 
        /* really truncate the file only when it's almost empty */
@@ -29,11 +30,11 @@ int mail_index_truncate(struct mail_index *index)
 
                /* keep the size record-aligned */
                index->mmap_full_length -= (index->mmap_full_length -
-                                           sizeof(struct mail_index_header)) %
+                                           index->header_size) %
                        sizeof(struct mail_index_record);
 
-               if (index->mmap_full_length < INDEX_FILE_MIN_SIZE)
-                        index->mmap_full_length = INDEX_FILE_MIN_SIZE;
+               if (index->mmap_full_length < INDEX_FILE_MIN_SIZE(index))
+                        index->mmap_full_length = INDEX_FILE_MIN_SIZE(index);
 
                if (ftruncate(index->fd, (off_t)index->mmap_full_length) < 0)
                        return index_set_syscall_error(index, "ftruncate()");
@@ -44,6 +45,7 @@ int mail_index_truncate(struct mail_index *index)
        return TRUE;
 }
 
+#if 0
 static int mail_index_copy_data(struct mail_index *index,
                                int fd, const char *path)
 {
@@ -69,7 +71,7 @@ static int mail_index_copy_data(struct mail_index *index,
        /* now we'll begin the actual moving. keep rebuild-flag on
           while doing it. */
        index->header->flags |= MAIL_INDEX_FLAG_REBUILD;
-       if (!mail_index_fmdatasync(index, sizeof(struct mail_index_header)))
+       if (!mail_index_fmdatasync(index, index->header_size))
                return FALSE;
 
        offset = sizeof(data_hdr);
@@ -182,3 +184,4 @@ int mail_index_compress_data(struct mail_index *index)
 
        return mail_index_data_open(index);
 }
+#endif
diff --git a/src/lib-index/mail-index-data.c b/src/lib-index/mail-index-data.c
deleted file mode 100644 (file)
index 6403073..0000000
+++ /dev/null
@@ -1,721 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "file-set-size.h"
-#include "mmap-util.h"
-#include "write-full.h"
-#include "mail-index.h"
-#include "mail-index-data.h"
-#include "mail-index-util.h"
-
-#include <stdio.h>
-#include <fcntl.h>
-
-#define DATA_FILE_POSITION(data, rec) \
-       ((uoff_t) ((char *) (rec) - (char *) ((data)->mmap_base)))
-
-/* Never compress the file if it's smaller than this */
-#define COMPRESS_MIN_SIZE (1024*50)
-
-/* Compress the file when deleted space reaches n% of total size */
-#define COMPRESS_PERCENTAGE 20
-
-/* Initial size for the file */
-#define INDEX_DATA_INITIAL_SIZE (sizeof(struct mail_index_data_header) + 10240)
-
-/* When more space is needed, grow the file n% larger than the previous size */
-#define INDEX_DATA_GROW_PERCENTAGE 10
-
-struct mail_index_data {
-       struct mail_index *index;
-
-       int fd;
-       char *filepath;
-
-       void *mmap_base;
-       size_t mmap_full_length;
-       size_t mmap_used_length;
-
-       struct mail_index_data_header *header;
-
-       unsigned int anon_mmap:1;
-       unsigned int dirty_mmap:1;
-       unsigned int modified:1;
-       unsigned int fsynced:1;
-};
-
-int index_data_set_corrupted(struct mail_index_data *data, const char *fmt, ...)
-{
-       va_list va;
-
-       INDEX_MARK_CORRUPTED(data->index);
-       data->index->inconsistent = TRUE;
-
-       va_start(va, fmt);
-       t_push();
-       index_set_error(data->index, "Corrupted index data file %s: %s",
-                       data->filepath, t_strdup_vprintf(fmt, va));
-       t_pop();
-       va_end(va);
-
-       return FALSE;
-}
-
-static int index_data_set_syscall_error(struct mail_index_data *data,
-                                       const char *function)
-{
-       i_assert(function != NULL);
-
-       if (ENOSPACE(errno)) {
-               data->index->nodiskspace = TRUE;
-               return FALSE;
-       }
-
-       index_set_error(data->index, "%s failed with index data file %s: %m",
-                       function, data->filepath);
-       return FALSE;
-}
-
-static int mail_index_data_msync(struct mail_index_data *data)
-{
-       if (!data->modified)
-               return TRUE;
-
-       if (!data->anon_mmap) {
-               if (msync(data->mmap_base, data->mmap_used_length, MS_SYNC) < 0)
-                       return index_data_set_syscall_error(data, "msync()");
-       }
-
-       data->modified = FALSE;
-       data->fsynced = FALSE;
-       return TRUE;
-}
-
-static void mail_index_data_file_close(struct mail_index_data *data)
-{
-       (void)mail_index_data_msync(data);
-
-       if (data->anon_mmap) {
-               if (munmap_anon(data->mmap_base, data->mmap_full_length) < 0)
-                       index_data_set_syscall_error(data, "munmap_anon()");
-       } else if (data->mmap_base != NULL) {
-               if (munmap(data->mmap_base, data->mmap_full_length) < 0)
-                       index_data_set_syscall_error(data, "munmap()");
-       }
-       data->mmap_base = NULL;
-       data->mmap_used_length = 0;
-       data->mmap_full_length = 0;
-
-       if (data->fd != -1) {
-               if (close(data->fd) < 0)
-                       index_data_set_syscall_error(data, "close()");
-               data->fd = -1;
-       }
-}
-
-static int data_file_reopen(struct mail_index_data *data)
-{
-       int fd;
-
-       i_assert(!data->anon_mmap);
-
-       fd = open(data->filepath, O_RDWR);
-       if (fd == -1)
-               return index_data_set_syscall_error(data, "open()");
-
-       mail_index_data_file_close(data);
-
-       data->fd = fd;
-       return TRUE;
-}
-
-static int mmap_update(struct mail_index_data *data, uoff_t pos, size_t size)
-{
-       struct mail_index_data_header *hdr;
-
-       if (data->index->mmap_invalidate && data->mmap_base != NULL) {
-               if (msync(data->mmap_base, data->mmap_used_length,
-                         MS_SYNC | MS_INVALIDATE) < 0)
-                       return index_data_set_syscall_error(data, "msync()");
-       }
-
-       if (data->header != NULL &&
-           data->header->indexid != data->index->indexid) {
-               if (data->header->indexid != 0) {
-                       /* index was just rebuilt. we should have noticed
-                          this before at index->set_lock() though. */
-                       index_set_error(data->index,
-                                       "Warning: Inconsistency - Index "
-                                       "%s was rebuilt while we had it open",
-                                       data->filepath);
-                       data->index->inconsistent = TRUE;
-                       return FALSE;
-               }
-
-               /* data file was deleted, reopen it */
-               if (!data_file_reopen(data))
-                       return FALSE;
-
-               /* force mmap refresh */
-               size = 0;
-       }
-
-       if (size != 0) {
-               debug_mprotect(data->mmap_base, data->mmap_full_length,
-                              data->index);
-
-               if (pos + size <= data->mmap_used_length)
-                       return TRUE;
-
-               if (pos + size <= data->mmap_full_length) {
-                       data->mmap_used_length = data->header->used_file_size;
-                       if (data->mmap_used_length >=
-                           sizeof(struct mail_index_data_header) &&
-                           data->mmap_used_length <= data->mmap_full_length)
-                               return TRUE;
-
-                       /* file size changed, re-mmap() */
-               }
-       }
-
-       i_assert(!data->anon_mmap);
-
-       if (data->mmap_base != NULL) {
-               if (!mail_index_data_msync(data))
-                       return FALSE;
-
-               if (munmap(data->mmap_base, data->mmap_full_length) < 0)
-                       index_data_set_syscall_error(data, "munmap()");
-       }
-
-       data->header = NULL;
-       data->mmap_used_length = 0;
-
-       data->mmap_base = mmap_rw_file(data->fd, &data->mmap_full_length);
-       if (data->mmap_base == MAP_FAILED) {
-               data->mmap_base = NULL;
-               return index_data_set_syscall_error(data, "mmap()");
-       }
-
-       if (data->mmap_full_length < sizeof(struct mail_index_data_header))
-               return index_data_set_corrupted(data, "File too small");
-
-       hdr = data->mmap_base;
-
-       if (hdr->used_file_size < sizeof(struct mail_index_data_header)) {
-               index_data_set_corrupted(data, "used_file_size too small ("
-                                        "%"PRIuUOFF_T")", hdr->used_file_size);
-               return FALSE;
-       }
-
-       if (hdr->used_file_size > data->mmap_full_length) {
-               index_data_set_corrupted(data,
-                       "used_file_size larger than real file size "
-                       "(%"PRIuUOFF_T" vs %"PRIuSIZE_T")",
-                       hdr->used_file_size, data->mmap_full_length);
-               return FALSE;
-       }
-
-       if ((hdr->used_file_size & (INDEX_ALIGN_SIZE-1)) != 0) {
-               index_data_set_corrupted(data,
-                       "used_file_size not aligned (%"PRIuUOFF_T")",
-                       hdr->used_file_size);
-               return FALSE;
-       }
-
-       data->mmap_used_length = hdr->used_file_size;
-       data->header = hdr;
-       debug_mprotect(data->mmap_base, data->mmap_full_length, data->index);
-       return TRUE;
-}
-
-int mail_index_data_open(struct mail_index *index)
-{
-       struct mail_index_data *data;
-       const char *path;
-       int fd;
-
-       path = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
-       fd = open(path, O_RDWR);
-       if (fd == -1) {
-               if (errno == ENOENT) {
-                       /* doesn't exist, rebuild the index */
-                       INDEX_MARK_CORRUPTED(index);
-               }
-               return index_file_set_syscall_error(index, path, "open()");
-       }
-
-       data = i_new(struct mail_index_data, 1);
-       data->index = index;
-       data->fd = fd;
-       data->filepath = i_strdup(path);
-
-       index->data = data;
-
-       if (!mmap_update(data, 0, sizeof(struct mail_index_data_header))) {
-               mail_index_data_free(data);
-               return FALSE;
-       }
-
-       /* verify that this really is the data file for wanted index */
-       if (data->header->indexid != index->indexid) {
-               INDEX_MARK_CORRUPTED(index);
-               index_set_error(index, "IndexID mismatch for data file %s",
-                               path);
-               mail_index_data_free(data);
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-static int mail_index_data_init(struct mail_index *index,
-                               struct mail_index_data_header *hdr,
-                               const char *path, int fd)
-{
-       if (write_full(fd, hdr, sizeof(*hdr)) < 0) {
-               index_file_set_syscall_error(index, path, "write_full()");
-               return FALSE;
-       }
-
-       if (file_set_size(fd, INDEX_DATA_INITIAL_SIZE) < 0) {
-               index_file_set_syscall_error(index, path, "file_set_size()");
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-int mail_index_data_create(struct mail_index *index)
-{
-        struct mail_index_data_header hdr;
-       struct mail_index_data *data;
-       const char *path;
-       int fd;
-
-       i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
-       memset(&hdr, 0, sizeof(struct mail_index_data_header));
-       hdr.indexid = index->indexid;
-       hdr.used_file_size = sizeof(struct mail_index_data_header);
-
-       /* we'll do anon-mmaping only if initially requested. if we fail
-          because of out of disk space, we'll just let the main index code
-          know it and fail. */
-       if (INDEX_IS_IN_MEMORY(index)) {
-               fd = -1;
-               path = NULL;
-       } else {
-               path = t_strconcat(index->filepath, DATA_FILE_PREFIX, NULL);
-               fd = open(path, O_RDWR | O_CREAT, 0660);
-               if (fd == -1) {
-                       index_file_set_syscall_error(index, path, "open()");
-                       return FALSE;
-               }
-
-               if (!mail_index_data_init(index, &hdr, path, fd)) {
-                       (void)close(fd);
-                       return FALSE;
-               }
-       }
-
-       data = i_new(struct mail_index_data, 1);
-
-       if (fd == -1) {
-               data->mmap_full_length = INDEX_DATA_INITIAL_SIZE;
-               data->mmap_base = mmap_anon(data->mmap_full_length);
-               if (data->mmap_base == MAP_FAILED) {
-                       i_free(data);
-                       return index_file_set_syscall_error(index, path,
-                                                           "mmap_anon()");
-               }
-
-               memcpy(data->mmap_base, &hdr, sizeof(hdr));
-               data->header = data->mmap_base;
-               data->mmap_used_length = data->header->used_file_size;
-
-               data->anon_mmap = TRUE;
-               data->filepath =
-                       i_strdup_printf("(in-memory index data index for %s)",
-                                       index->mailbox_path);
-       } else {
-               data->filepath = i_strdup(path);
-       }
-
-       data->index = index;
-       data->fd = fd;
-
-       if (!mmap_update(data, 0, sizeof(struct mail_index_data_header))) {
-               mail_index_data_free(data);
-               return FALSE;
-       }
-
-       index->data = data;
-       return TRUE;
-}
-
-void mail_index_data_free(struct mail_index_data *data)
-{
-       data->index->data = NULL;
-
-       mail_index_data_file_close(data);
-
-       i_free(data->filepath);
-       i_free(data);
-}
-
-int mail_index_data_reset(struct mail_index_data *data)
-{
-       struct mail_index_data_header hdr;
-
-       memset(&hdr, 0, sizeof(struct mail_index_data_header));
-       hdr.indexid = data->index->indexid;
-       hdr.used_file_size = sizeof(struct mail_index_data_header);
-       memcpy(data->mmap_base, &hdr, sizeof(hdr));
-
-       if (data->anon_mmap)
-               return TRUE;
-
-       if (msync(data->mmap_base, sizeof(hdr), MS_SYNC) < 0)
-               return index_data_set_syscall_error(data, "msync()");
-
-       if (file_set_size(data->fd, INDEX_DATA_INITIAL_SIZE) < 0)
-               return index_data_set_syscall_error(data, "file_set_size()");
-
-       data->modified = FALSE;
-       data->fsynced = FALSE;
-       return mmap_update(data, 0, 0);
-}
-
-int mail_index_data_mark_file_deleted(struct mail_index_data *data)
-{
-       if (data->anon_mmap)
-               return TRUE;
-
-       data->header->indexid = 0;
-       if (msync(data->mmap_base,
-                 sizeof(struct mail_index_data_header), MS_SYNC) < 0)
-               return index_data_set_syscall_error(data, "msync()");
-
-       data->fsynced = FALSE;
-       return TRUE;
-}
-
-void mail_index_data_mark_modified(struct mail_index_data *data)
-{
-       data->modified = TRUE;
-}
-
-static int mail_index_data_grow(struct mail_index_data *data, size_t size)
-{
-       void *base;
-       uoff_t new_fsize;
-       off_t pos;
-
-       new_fsize = data->header->used_file_size + size;
-       new_fsize += new_fsize / 100 * INDEX_DATA_GROW_PERCENTAGE;
-
-       if (data->anon_mmap) {
-               i_assert(new_fsize < SSIZE_T_MAX);
-
-               base = mremap_anon(data->mmap_base, data->mmap_full_length,
-                                  (size_t)new_fsize, MREMAP_MAYMOVE);
-               if (base == MAP_FAILED) {
-                       index_data_set_syscall_error(data, "mremap_anon()");
-                       return FALSE;
-               }
-
-               data->mmap_base = base;
-               data->mmap_full_length = (size_t)new_fsize;
-               data->header = data->mmap_base;
-               return TRUE;
-       }
-
-       pos = lseek(data->fd, 0, SEEK_END);
-       if (pos < 0)
-               return index_data_set_syscall_error(data, "lseek()");
-
-       if (data->header->used_file_size + size <= (uoff_t)pos) {
-               /* no need to grow, just update mmap */
-               if (!mmap_update(data, 0, 0))
-                       return FALSE;
-
-               i_assert(data->mmap_full_length >= (uoff_t)pos);
-               return TRUE;
-       }
-
-       if (pos < (off_t)sizeof(struct mail_index_data_header))
-               return index_data_set_corrupted(data, "Header is missing");
-
-       if (file_set_size(data->fd, (off_t)new_fsize) < 0)
-               return index_data_set_syscall_error(data, "file_set_size()");
-
-       return mmap_update(data, 0, 0);
-}
-
-uoff_t mail_index_data_append(struct mail_index_data *data, const void *buffer,
-                             size_t size)
-{
-       uoff_t offset;
-
-       i_assert((size & (INDEX_ALIGN_SIZE-1)) == 0);
-       i_assert(data->index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
-       if (!mmap_update(data, 0, sizeof(struct mail_index_data_header)))
-               return 0;
-
-       if (size > data->mmap_full_length ||
-           data->mmap_full_length - size < data->header->used_file_size) {
-               if (!mail_index_data_grow(data, size))
-                       return 0;
-       }
-
-       offset = data->header->used_file_size;
-       i_assert(offset + size <= data->mmap_full_length);
-
-       memcpy((char *) data->mmap_base + offset, buffer, size);
-       data->header->used_file_size += size;
-       data->mmap_used_length += size;
-
-        data->modified = TRUE;
-       return offset;
-}
-
-int mail_index_data_delete(struct mail_index_data *data,
-                          struct mail_index_record *index_rec)
-{
-        struct mail_index_data_record_header *rec_hdr;
-       uoff_t max_del_space;
-
-       i_assert(data->index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
-       rec_hdr = mail_index_data_lookup_header(data, index_rec);
-       if (rec_hdr == NULL)
-               return FALSE;
-
-       /* just mark it deleted. */
-       data->header->deleted_space += rec_hdr->data_size;
-
-       /* clear the record data. not really needed, but better not to keep
-          deleted information lying around.. */
-       memset((char *) rec_hdr + sizeof(*rec_hdr), 0,
-              rec_hdr->data_size - sizeof(*rec_hdr));
-
-       /* see if we've reached the max. deleted space in file */
-       if (data->header->used_file_size >= COMPRESS_MIN_SIZE &&
-           (data->index->header->flags & MAIL_INDEX_FLAG_COMPRESS_DATA) == 0) {
-               max_del_space = data->header->used_file_size /
-                       100 * COMPRESS_PERCENTAGE;
-               if (data->header->deleted_space >= max_del_space)
-                       data->index->set_flags |= MAIL_INDEX_FLAG_COMPRESS_DATA;
-       }
-
-        data->modified = TRUE;
-       return TRUE;
-}
-
-int mail_index_data_sync_file(struct mail_index_data *data, int *fsync_fd)
-{
-       *fsync_fd = -1;
-
-       if (data->anon_mmap)
-               return TRUE;
-
-       if (!mail_index_data_msync(data))
-               return FALSE;
-
-       if (!data->fsynced) {
-               data->fsynced = TRUE;
-               *fsync_fd = data->fd;
-       }
-
-       return TRUE;
-}
-
-struct mail_index_data_record_header *
-mail_index_data_lookup_header(struct mail_index_data *data,
-                             struct mail_index_record *index_rec)
-{
-       uoff_t pos;
-
-       pos = index_rec->data_position;
-       if (pos == 0) {
-               /* data not yet written to record */
-               return NULL;
-       }
-
-       if (!mmap_update(data, pos,
-                        sizeof(struct mail_index_data_record_header)))
-               return NULL;
-
-       if (pos + sizeof(struct mail_index_data_record_header) >
-           data->mmap_used_length) {
-               index_data_set_corrupted(data,
-                       "Data position of record %u points outside file "
-                       "(%"PRIuUOFF_T" + %"PRIuSIZE_T" > %"PRIuSIZE_T")",
-                       index_rec->uid, pos,
-                       sizeof(struct mail_index_data_record_header),
-                       data->mmap_used_length);
-               return NULL;
-       }
-
-       if ((pos % INDEX_ALIGN_SIZE) != 0) {
-               index_data_set_corrupted(data, "Data position (%"PRIuUOFF_T") "
-                                        "is not memory aligned for record %u",
-                                        pos, index_rec->uid);
-               return NULL;
-       }
-
-       return (struct mail_index_data_record_header *)
-               ((char *) data->mmap_base + pos);
-}
-
-struct mail_index_data_record *
-mail_index_data_lookup(struct mail_index_data *data,
-                      struct mail_index_record *index_rec,
-                      enum mail_data_field field)
-{
-        struct mail_index_data_record_header *rec_hdr;
-       struct mail_index_data_record *rec;
-       uoff_t pos, max_pos;
-
-       index_reset_error(data->index);
-
-       if (index_rec->data_position == 0) {
-               /* data not yet written to record */
-               return NULL;
-       }
-
-       rec_hdr = mail_index_data_lookup_header(data, index_rec);
-       if (rec_hdr == NULL)
-               return NULL;
-
-       if (!mmap_update(data, index_rec->data_position, rec_hdr->data_size))
-               return NULL;
-
-       pos = index_rec->data_position;
-       max_pos = index_rec->data_position + rec_hdr->data_size;
-
-       if (pos > data->mmap_used_length ||
-           (data->mmap_used_length - pos < rec_hdr->data_size)) {
-               index_data_set_corrupted(data,
-                       "Given data size larger than file size "
-                       "(%u + %u > %"PRIuSIZE_T") for record %u",
-                       index_rec->data_position, rec_hdr->data_size,
-                       data->mmap_used_length, index_rec->uid);
-               return NULL;
-       }
-
-       pos += sizeof(struct mail_index_data_record_header);
-       do {
-               rec = (struct mail_index_data_record *)
-                       ((char *) data->mmap_base + pos);
-
-               if (rec->full_field_size > max_pos ||
-                   pos + sizeof(struct mail_index_data_record) > max_pos ||
-                   pos + DATA_RECORD_SIZE(rec) > max_pos) {
-                       index_data_set_corrupted(data,
-                               "Field %d size points outside file "
-                               "(%"PRIuUOFF_T" / %"PRIuUOFF_T") for record %u",
-                               (int)field, pos, max_pos, index_rec->uid);
-                       break;
-               }
-
-               if ((rec->full_field_size % INDEX_ALIGN_SIZE) != 0) {
-                       index_data_set_corrupted(data, "Field %d size %u "
-                               "is not memory aligned for record %u",
-                               (int)field, rec->full_field_size,
-                               index_rec->uid);
-                       break;
-               }
-
-               if (rec->field == field || field == 0) {
-                       /* match */
-                       return rec;
-               } else if (rec->field < field) {
-                       /* jump to next record */
-                       pos += DATA_RECORD_SIZE(rec);
-               } else {
-                       /* the fields are sorted by field type, so it's not
-                          possible the wanted field could come after this. */
-                       break;
-               }
-       } while (pos < max_pos);
-
-       return NULL;
-}
-
-struct mail_index_data_record *
-mail_index_data_next(struct mail_index_data *data,
-                    struct mail_index_record *index_rec,
-                    struct mail_index_data_record *rec)
-{
-        struct mail_index_data_record_header *rec_hdr;
-       uoff_t pos, end_pos, max_pos;
-
-       index_reset_error(data->index);
-
-       if (rec == NULL)
-               return NULL;
-
-       rec_hdr = (struct mail_index_data_record_header *)
-               ((char *) data->mmap_base + index_rec->data_position);
-
-       /* get position to next record */
-       pos = DATA_FILE_POSITION(data, rec) + DATA_RECORD_SIZE(rec);
-       max_pos = index_rec->data_position + rec_hdr->data_size;
-
-       /* make sure it's within range */
-       if (pos >= max_pos)
-               return NULL;
-
-       rec = (struct mail_index_data_record *)
-               ((char *) data->mmap_base + pos);
-       end_pos = pos + DATA_RECORD_SIZE(rec);
-       if (end_pos < pos || end_pos > max_pos) {
-               index_data_set_corrupted(data, "Field size points outside file "
-                                        "(%"PRIuUOFF_T" + %u > %"PRIuUOFF_T")",
-                                        pos, rec->full_field_size, max_pos);
-               return NULL;
-       }
-
-       return rec;
-}
-
-int mail_index_data_record_verify(struct mail_index_data *data,
-                                 struct mail_index_data_record *rec)
-{
-       int i;
-
-       if (rec->full_field_size > INT_MAX) {
-               /* we already checked that the full_field_size is within file,
-                  so this can happen only if the file really is huge.. */
-               index_data_set_corrupted(data, "full_field_size (%u) > INT_MAX",
-                                        rec->full_field_size);
-               return FALSE;
-       }
-
-       /* make sure the data actually contains \0 */
-       for (i = (int)rec->full_field_size-1; i >= 0; i--) {
-               if (rec->data[i] == '\0') {
-                       /* yes, everything ok */
-                       return TRUE;
-               }
-       }
-
-       index_data_set_corrupted(data, "Missing \\0 with field %u "
-                                "(%"PRIuUOFF_T")", rec->field,
-                                DATA_FILE_POSITION(data, rec));
-       return FALSE;
-}
-
-void *mail_index_data_get_mmaped(struct mail_index_data *data, size_t *size)
-{
-       if (!data->anon_mmap) {
-               if (!mmap_update(data, 0, 0))
-                       return NULL;
-       }
-
-       *size = data->mmap_used_length;
-       return data->mmap_base;
-}
diff --git a/src/lib-index/mail-index-data.h b/src/lib-index/mail-index-data.h
deleted file mode 100644 (file)
index 13bf21c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-#ifndef __MAIL_INDEX_DATA_H
-#define __MAIL_INDEX_DATA_H
-
-#define DATA_FILE_PREFIX ".data"
-
-int mail_index_data_open(struct mail_index *index);
-int mail_index_data_create(struct mail_index *index);
-void mail_index_data_free(struct mail_index_data *data);
-
-/* Truncate the data file and update it's indexid */
-int mail_index_data_reset(struct mail_index_data *data);
-
-/* Set indexid to 0 to notify other processes using this file that they should
-   re-open it. */
-int mail_index_data_mark_file_deleted(struct mail_index_data *data);
-
-/* Mark the file as being modified */
-void mail_index_data_mark_modified(struct mail_index_data *data);
-
-/* Append new data at the end of the file. Returns the position in file
-   where the data begins, or 0 if error occured. */
-uoff_t mail_index_data_append(struct mail_index_data *data, const void *buffer,
-                             size_t size);
-
-/* Mark the given record deleted. */
-int mail_index_data_delete(struct mail_index_data *data,
-                          struct mail_index_record *index_rec);
-
-/* Synchronize the data into disk */
-int mail_index_data_sync_file(struct mail_index_data *data, int *fsync_fd);
-
-/* Looks up a record header from data file. Returns NULL if not found or
-   if error occured. */
-struct mail_index_data_record_header *
-mail_index_data_lookup_header(struct mail_index_data *data,
-                             struct mail_index_record *index_rec);
-
-/* Looks up a field from data file. If field is 0, returns the first field
-   found. Returns NULL if not found or if error occured. */
-struct mail_index_data_record *
-mail_index_data_lookup(struct mail_index_data *data,
-                      struct mail_index_record *index_rec,
-                      enum mail_data_field field);
-
-/* Returns the next record in data file, or NULL if there's no more. */
-struct mail_index_data_record *
-mail_index_data_next(struct mail_index_data *data,
-                    struct mail_index_record *index_rec,
-                    struct mail_index_data_record *rec);
-
-/* Returns TRUE if rec->data is a valid \0-terminated string */
-int mail_index_data_record_verify(struct mail_index_data *data,
-                                 struct mail_index_data_record *rec);
-
-/* Return the whole data file mmap()ed. */
-void *mail_index_data_get_mmaped(struct mail_index_data *data, size_t *size);
-
-/* "Error in index data file %s: ...". Also marks the index file as
-   corrupted. */
-int index_data_set_corrupted(struct mail_index_data *data, const char *fmt, ...)
-       __attr_format__(2, 3);
-
-#endif
index f9b108de6418d12c68075f6bab47b8e74f3a841b..13676719c84406ba302922ab644823d6dd101d07 100644 (file)
@@ -46,7 +46,6 @@ int mail_index_fsck(struct mail_index *index)
 
        i_assert(index->lock_type != MAIL_LOCK_SHARED);
 
-       /* Expunge tree can get easily corrupted, compress it away first. */
        if (!mail_index_compress(index))
                return FALSE;
 
index a5c27e2a0063259f86fa746dc6613e8b46e636d9..ca63b00d1512461c45f23c663105b811d2a7cf18 100644 (file)
@@ -9,8 +9,8 @@
 #include "unlink-lockfiles.h"
 #include "write-full.h"
 #include "mail-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 #include "mail-modifylog.h"
 #include "mail-custom-flags.h"
 
@@ -29,7 +29,7 @@ static int mail_index_open_init(struct mail_index *index,
        index->maildir_have_new =
                (hdr->flags & MAIL_INDEX_FLAG_MAILDIR_NEW) != 0;
 
-       if ((hdr->flags & MAIL_INDEX_FLAG_DIRTY_MESSAGES) != 0)
+       if ((hdr->flags & MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES) != 0)
                index->next_dirty_flush = ioloop_time;
 
        /* update \Recent message counters */
@@ -52,7 +52,7 @@ static int mail_index_open_init(struct mail_index *index,
 
        if (hdr->next_uid >= MAX_ALLOWED_UID - 1000) {
                /* UID values are getting too high, rebuild index */
-               index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
+               index->set_flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
        }
 
        if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
@@ -71,34 +71,16 @@ static int index_open_and_fix(struct mail_index *index,
 {
        int rebuilt;
 
-       /* open/create the index files */
-       if ((flags & _MAIL_INDEX_OPEN_FLAG_CREATING) == 0) {
-               if (!mail_index_data_open(index)) {
-                       if ((index->set_flags & MAIL_INDEX_FLAG_REBUILD) == 0)
-                               return FALSE;
+       if (!mail_cache_open_or_create(index))
+               return FALSE;
 
-                       /* data file is corrupted, need to rebuild index */
-                       flags |= _MAIL_INDEX_OPEN_FLAG_CREATING;
-                       index->set_flags = 0;
-                       index->inconsistent = FALSE;
-               }
-       }
+       if ((index->header->flags & MAIL_INDEX_HDR_FLAG_REBUILD) != 0 ||
+           (index->set_flags & MAIL_INDEX_HDR_FLAG_REBUILD) != 0) {
 
-       if ((flags & _MAIL_INDEX_OPEN_FLAG_CREATING) != 0) {
-               if (!mail_index_set_lock(index, MAIL_LOCK_EXCLUSIVE))
-                       return FALSE;
-               if (!mail_index_data_create(index))
+               if (!index->rebuild(index))
                        return FALSE;
-       }
-
-       /* custom flags file needs to be open before
-          rebuilding index */
-       if (!mail_custom_flags_open_or_create(index))
-               return FALSE;
 
-       if ((flags & _MAIL_INDEX_OPEN_FLAG_CREATING) != 0 ||
-           (index->header->flags & MAIL_INDEX_FLAG_REBUILD) != 0) {
-               if (!index->rebuild(index))
+               if ((index->header->flags & MAIL_INDEX_HDR_FLAG_REBUILD) != 0)
                        return FALSE;
 
                /* no inconsistency problems since we're still opening
@@ -109,6 +91,10 @@ static int index_open_and_fix(struct mail_index *index,
                rebuilt = FALSE;
        }
 
+       /* custom flags file needs to be open before rebuilding index */
+       if (!mail_custom_flags_open_or_create(index))
+               return FALSE;
+
        if ((flags & _MAIL_INDEX_OPEN_FLAG_CREATING) == 0) {
                if (!mail_modifylog_open_or_create(index))
                        return FALSE;
@@ -117,7 +103,7 @@ static int index_open_and_fix(struct mail_index *index,
                        return FALSE;
        }
 
-       if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
+       if (index->header->flags & MAIL_INDEX_HDR_FLAG_FSCK) {
                /* index needs fscking */
                if (!index->fsck(index))
                        return FALSE;
@@ -130,8 +116,6 @@ static int index_open_and_fix(struct mail_index *index,
                                          MAIL_LOCK_SHARED, NULL) &&
                    !index->nodiskspace)
                        return FALSE;
-
-               index->inconsistent = FALSE;
        }
 
        /* we never want to keep shared lock if syncing happens to set it.
@@ -143,23 +127,15 @@ static int index_open_and_fix(struct mail_index *index,
        }
 
        if ((flags & MAIL_INDEX_OPEN_FLAG_FAST) == 0) {
-               if (index->header->flags & MAIL_INDEX_FLAG_COMPRESS) {
+               if (index->header->flags & MAIL_INDEX_HDR_FLAG_COMPRESS) {
                        /* remove deleted blocks from index file */
                        if (!mail_index_compress(index))
                                return FALSE;
                }
 
-               if (index->header->flags & MAIL_INDEX_FLAG_CACHE_FIELDS) {
-                       /* need to update cached fields */
-                       if (!mail_index_update_cache(index))
-                               return FALSE;
-               }
-
-               if (index->header->flags & MAIL_INDEX_FLAG_COMPRESS_DATA) {
-                       /* remove unused space from index data file.
-                          keep after cache updates which may move data
-                          and create unused space */
-                       if (!mail_index_compress_data(index))
+               if (index->header->flags & MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE) {
+                       /* remove unused space from index data file. */
+                       if (!mail_cache_compress(index->cache))
                                return FALSE;
                }
        }
@@ -189,16 +165,6 @@ static int mail_index_read_header(struct mail_index *index,
        return 1;
 }
 
-static int mail_index_is_compatible(const struct mail_index_header *hdr)
-{
-       return hdr->compat_data[0] == MAIL_INDEX_VERSION &&
-               hdr->compat_data[1] == MAIL_INDEX_COMPAT_FLAGS &&
-               hdr->compat_data[2] == sizeof(unsigned int) &&
-               hdr->compat_data[3] == sizeof(time_t) &&
-               hdr->compat_data[4] == sizeof(uoff_t) &&
-               hdr->compat_data[5] == INDEX_ALIGN_SIZE;
-}
-
 static int mail_index_init_file(struct mail_index *index,
                                const struct mail_index_header *hdr)
 {
@@ -224,27 +190,20 @@ static int mail_index_init_file(struct mail_index *index,
        return TRUE;
 }
 
-void mail_index_init_header(struct mail_index *index,
-                           struct mail_index_header *hdr)
+void mail_index_init_header(struct mail_index_header *hdr)
 {
+       i_assert(sizeof(struct mail_index_header) < 256);
+
        memset(hdr, 0, sizeof(*hdr));
-       hdr->compat_data[0] = MAIL_INDEX_VERSION;
-       hdr->compat_data[1] = MAIL_INDEX_COMPAT_FLAGS;
-       hdr->compat_data[2] = sizeof(unsigned int);
-       hdr->compat_data[3] = sizeof(time_t);
-       hdr->compat_data[4] = sizeof(uoff_t);
-       hdr->compat_data[5] = INDEX_ALIGN_SIZE;
+       hdr->major_version = MAIL_INDEX_MAJOR_VERSION;
+       hdr->minor_version = MAIL_INDEX_MINOR_VERSION;
+       hdr->header_size = (uint8_t)sizeof(struct mail_index_header);
+
        hdr->indexid = ioloop_time;
 
        /* mark the index requiring rebuild - rebuild() removes this flag
           when it succeeds */
-       hdr->flags = MAIL_INDEX_FLAG_REBUILD;
-
-       if (!index->anon_mmap) {
-               /* set the fields we always want to cache,
-                  but not if we're building into memory */
-               hdr->cache_fields |= index->default_cache_fields;
-       }
+       hdr->flags = MAIL_INDEX_HDR_FLAG_REBUILD;
 
        hdr->used_file_size = sizeof(struct mail_index_header);
        hdr->uid_validity = ioloop_time;
@@ -282,14 +241,15 @@ static int mail_index_create_memory(struct mail_index *index,
 
        flags |= _MAIL_INDEX_OPEN_FLAG_CREATING;
 
-       index->mmap_full_length = INDEX_FILE_MIN_SIZE;
+       index->header_size = sizeof(struct mail_index_header);
+       index->mmap_full_length = INDEX_FILE_MIN_SIZE(index);
        index->mmap_base = mmap_anon(index->mmap_full_length);
        if (index->mmap_base == MAP_FAILED) {
                index->mmap_base = NULL;
                return index_set_error(index, "mmap_anon() failed: %m");
        }
 
-       mail_index_init_header(index, index->mmap_base);
+       mail_index_init_header(index->mmap_base);
        index->header = index->mmap_base;
        index->mmap_used_length = index->header->used_file_size;
 
@@ -332,8 +292,8 @@ static int mail_index_open_index(struct mail_index *index,
        if ((ret = mail_index_read_header(index, &hdr)) < 0)
                return FALSE;
 
-       if (ret == 0 || !mail_index_is_compatible(&hdr) ||
-           (hdr.flags & MAIL_INDEX_FLAG_REBUILD) != 0) {
+       if (ret == 0 || hdr.major_version != MAIL_INDEX_MAJOR_VERSION ||
+           (hdr.flags & MAIL_INDEX_HDR_FLAG_REBUILD) != 0) {
                if ((flags & MAIL_INDEX_OPEN_FLAG_CREATE) == 0)
                        return FALSE;
 
@@ -347,7 +307,7 @@ static int mail_index_open_index(struct mail_index *index,
                        return mail_index_open_index(index, flags);
                }
 
-               mail_index_init_header(index, &hdr);
+               mail_index_init_header(&hdr);
                if (!mail_index_init_file(index, &hdr))
                        return FALSE;
        }
@@ -367,7 +327,7 @@ static int mail_index_open_index(struct mail_index *index,
        }
 
        if (!index_open_and_fix(index, flags)) {
-               if ((index->set_flags & MAIL_INDEX_FLAG_REBUILD) == 0 ||
+               if ((index->set_flags & MAIL_INDEX_HDR_FLAG_REBUILD) == 0 ||
                    (flags & _MAIL_INDEX_OPEN_FLAG_CREATING) != 0)
                        return FALSE;
 
similarity index 57%
rename from src/lib-index/maildir/maildir-rebuild.c
rename to src/lib-index/mail-index-rebuild.c
index 61bb60c4a559b1d58fac4ea1102dc4c391673b51..25669bc21ee0e58a7efed6b9d26f9ef3643f8b58 100644 (file)
@@ -1,26 +1,23 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2003 Timo Sirainen */
 
 #include "lib.h"
-#include "maildir-index.h"
-#include "mail-index-data.h"
+#include "mail-index.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
-#include <unistd.h>
-#include <sys/stat.h>
 #include <sys/mman.h>
 
-int maildir_index_rebuild(struct mail_index *index)
+int mail_index_rebuild(struct mail_index *index)
 {
        if (!mail_index_set_lock(index, MAIL_LOCK_EXCLUSIVE))
                return FALSE;
 
+       index->set_flags &= ~MAIL_INDEX_HDR_FLAG_REBUILD;
+
        /* reset the header */
-       mail_index_init_header(index, index->header);
+       mail_index_init_header(index->header);
        index->mmap_used_length = index->header->used_file_size;
 
-       /* require these fields */
-       index->header->cache_fields |= DATA_FIELD_LOCATION;
-
        /* update indexid, which also means that our state has completely
           changed */
        index->indexid = index->header->indexid;
@@ -28,13 +25,11 @@ int maildir_index_rebuild(struct mail_index *index)
        index->rebuilding = TRUE;
 
        if (!index->anon_mmap) {
-               if (msync(index->mmap_base,
-                         sizeof(struct mail_index_header), MS_SYNC) < 0)
+               if (msync(index->mmap_base, index->header_size, MS_SYNC) < 0)
                        return index_set_syscall_error(index, "msync()");
        }
 
-       /* reset data file */
-       if (!mail_index_data_reset(index->data))
+       if (!mail_cache_truncate(index->cache))
                return FALSE;
 
        /* read the mails by syncing */
@@ -42,7 +37,11 @@ int maildir_index_rebuild(struct mail_index *index)
                return FALSE;
 
        /* rebuild is complete - remove the flag */
-       index->header->flags &= ~(MAIL_INDEX_FLAG_REBUILD|MAIL_INDEX_FLAG_FSCK);
+       index->header->flags &= ~(MAIL_INDEX_HDR_FLAG_REBUILD |
+                                 MAIL_INDEX_HDR_FLAG_FSCK);
+       index->header->flags |= index->set_flags;
+       index->set_flags = 0;
+
        index->rebuilding = FALSE;
        return TRUE;
 }
diff --git a/src/lib-index/mail-index-update-cache.c b/src/lib-index/mail-index-update-cache.c
deleted file mode 100644 (file)
index 87ca28f..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "istream.h"
-#include "mail-index.h"
-
-#include <unistd.h>
-
-static int cache_record(struct mail_index *index, struct mail_index_record *rec,
-                       enum mail_data_field cache_fields)
-{
-       struct mail_index_update *update;
-       struct istream *input;
-       time_t internal_date;
-       int failed, deleted;
-
-       input = index->open_mail(index, rec, &internal_date, &deleted);
-       if (input == NULL)
-               return deleted;
-
-       cache_fields &= ~rec->data_fields;
-
-       update = index->update_begin(index, rec);
-       index->update_field_raw(update, DATA_HDR_INTERNAL_DATE,
-                               &internal_date, sizeof(internal_date));
-       mail_index_update_headers(update, input, cache_fields, NULL, NULL);
-       failed = !index->update_end(update);
-
-       i_stream_unref(input);
-       return !failed;
-}
-
-int mail_index_update_cache(struct mail_index *index)
-{
-       struct mail_index_record *rec;
-       enum mail_data_field cache_fields;
-
-       if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
-               return FALSE;
-
-       cache_fields = index->header->cache_fields;
-
-       rec = index->lookup(index, 1);
-       while (rec != NULL) {
-               if (((enum mail_data_field)rec->data_fields & cache_fields) !=
-                   cache_fields) {
-                       t_push();
-                       if (!cache_record(index, rec, cache_fields)) {
-                               t_pop();
-                               return FALSE;
-                       }
-                       t_pop();
-               }
-
-               rec = index->next(index, rec);
-       }
-
-       index->header->flags &= ~MAIL_INDEX_FLAG_CACHE_FIELDS;
-       return TRUE;
-}
diff --git a/src/lib-index/mail-index-update.c b/src/lib-index/mail-index-update.c
deleted file mode 100644 (file)
index db680a5..0000000
+++ /dev/null
@@ -1,574 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "buffer.h"
-#include "str.h"
-#include "istream.h"
-#include "ioloop.h"
-#include "str.h"
-#include "message-date.h"
-#include "message-parser.h"
-#include "message-part-serialize.h"
-#include "message-size.h"
-#include "imap-envelope.h"
-#include "imap-bodystructure.h"
-#include "mail-index.h"
-#include "mail-index-data.h"
-#include "mail-index-util.h"
-
-struct mail_index_update {
-       pool_t pool;
-
-       struct mail_index *index;
-       struct mail_index_record *rec;
-       struct mail_index_data_record_header data_hdr;
-
-       unsigned int updated_fields;
-       void *fields[DATA_FIELD_MAX_BITS];
-       size_t field_sizes[DATA_FIELD_MAX_BITS];
-       size_t field_extra_sizes[DATA_FIELD_MAX_BITS];
-};
-
-struct mail_index_update *
-mail_index_update_begin(struct mail_index *index, struct mail_index_record *rec)
-{
-       pool_t pool;
-       struct mail_index_update *update;
-       struct mail_index_data_record_header *data_hdr;
-
-       i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
-       pool = pool_alloconly_create("mail_index_update", 4096);
-
-       update = p_new(pool, struct mail_index_update, 1);
-       update->pool = pool;
-       update->index = index;
-       update->rec = rec;
-
-       data_hdr = mail_index_data_lookup_header(index->data, rec);
-       if (data_hdr != NULL)
-               memcpy(&update->data_hdr, data_hdr, sizeof(*data_hdr));
-       return update;
-}
-
-static int mail_field_get_index(enum mail_data_field field)
-{
-       unsigned int i, mask;
-
-       for (i = 0, mask = 1; i < DATA_FIELD_MAX_BITS; i++, mask <<= 1) {
-               if (field == mask)
-                       return i;
-       }
-
-       return -1;
-}
-
-static void get_data_block_sizes(struct mail_index_update *update,
-                                size_t *min_size, size_t *max_size,
-                                int *no_grown_fields)
-{
-       struct mail_index_data_record *rec;
-       enum mail_data_field field;
-       unsigned int field_min_size;
-       int i, field_exists;
-
-       *min_size = *max_size = sizeof(struct mail_index_data_record_header);
-       *no_grown_fields = TRUE;
-
-       rec = mail_index_data_lookup(update->index->data, update->rec, 0);
-       for (i = 0, field = 1; field != DATA_FIELD_LAST; i++, field <<= 1) {
-               field_exists = rec != NULL && rec->field == field;
-
-               if (update->fields[i] != NULL) {
-                       /* value was modified - use it */
-                       field_min_size = INDEX_ALIGN(update->field_sizes[i]);
-                       *min_size += SIZEOF_MAIL_INDEX_DATA + field_min_size;
-                       *max_size += SIZEOF_MAIL_INDEX_DATA +
-                               INDEX_ALIGN(update->field_sizes[i] +
-                                           update->field_extra_sizes[i]);
-
-                       if (!field_exists ||
-                           rec->full_field_size < field_min_size)
-                               *no_grown_fields = FALSE;
-               } else if (field_exists) {
-                       /* use the old value */
-                       *min_size += SIZEOF_MAIL_INDEX_DATA +
-                               rec->full_field_size;
-                       *max_size += SIZEOF_MAIL_INDEX_DATA +
-                               rec->full_field_size;
-               }
-
-               if (field_exists) {
-                       rec = mail_index_data_next(update->index->data,
-                                                  update->rec, rec);
-               }
-       }
-}
-
-static size_t get_max_align_size(size_t base, size_t extra, size_t *max_extra)
-{
-       size_t size;
-
-       size = INDEX_ALIGN(base);
-       extra -= size - base;
-       if (*max_extra < INDEX_ALIGN_SIZE || extra == 0)
-               return size; /* no extra / extra went into alignment */
-
-       extra = INDEX_ALIGN(extra);
-       if (extra > *max_extra) {
-               /* partial */
-               extra = *max_extra & ~(size_t)(INDEX_ALIGN_SIZE-1);
-               i_assert(extra <= *max_extra);
-       }
-
-       *max_extra -= extra;
-       return size + extra;
-}
-
-/* extra_size is the amount of data in data_size which can be used for
-   field_extra_sizes */
-static void *create_data_block(struct mail_index_update *update,
-                              size_t data_size, size_t extra_size)
-{
-        struct mail_index_data_record *rec, destrec;
-       enum mail_data_field field;
-       buffer_t *buf;
-       const void *src;
-       size_t src_size, filler_size;
-       int i;
-
-       i_assert(data_size <= UINT_MAX);
-
-       buf = buffer_create_static_hard(update->pool, data_size);
-
-       /* set header */
-       update->data_hdr.data_size = data_size;
-       buffer_append(buf, &update->data_hdr, sizeof(update->data_hdr));
-
-       /* set fields */
-       rec = mail_index_data_lookup(update->index->data, update->rec, 0);
-       for (i = 0, field = 1; field != DATA_FIELD_LAST; i++, field <<= 1) {
-               if (update->fields[i] != NULL) {
-                       /* value was modified - use it */
-                       destrec.full_field_size =
-                               get_max_align_size(update->field_sizes[i],
-                                                  update->field_extra_sizes[i],
-                                                  &extra_size);
-                       src = update->fields[i];
-                       src_size = update->field_sizes[i];
-               } else if (rec != NULL && rec->field == field) {
-                       /* use the old value */
-                       destrec.full_field_size = rec->full_field_size;
-                       src = rec->data;
-                       src_size = rec->full_field_size;
-               } else {
-                       /* the field doesn't exist, jump to next */
-                       continue;
-               }
-               i_assert((destrec.full_field_size % INDEX_ALIGN_SIZE) == 0);
-
-               destrec.field = field;
-               buffer_append(buf, &destrec, SIZEOF_MAIL_INDEX_DATA);
-               buffer_append(buf, src, src_size);
-
-               filler_size = destrec.full_field_size - src_size;
-               if (filler_size != 0) {
-                       buffer_set_used_size(buf, buffer_get_used_size(buf) +
-                                            filler_size);
-               }
-
-               if (rec != NULL && rec->field == field) {
-                       rec = mail_index_data_next(update->index->data,
-                                                  update->rec, rec);
-               }
-       }
-
-       return buffer_free_without_data(buf);
-}
-
-/* Append all the data at the end of the data file and update 
-   the index's data position */
-static int update_by_append(struct mail_index_update *update,
-                           size_t data_size, size_t extra_size)
-{
-       void *mem;
-       uoff_t fpos;
-
-       mem = create_data_block(update, data_size, extra_size);
-
-       /* append the data at the end of the data file */
-       fpos = mail_index_data_append(update->index->data, mem, data_size);
-       if (fpos == 0)
-               return FALSE;
-
-       /* the old data is discarded */
-       (void)mail_index_data_delete(update->index->data, update->rec);
-
-       /* update index file position - it's mmap()ed so it'll be written
-          into disk when index is unlocked. */
-       update->rec->data_position = fpos;
-       return TRUE;
-}
-
-/* Replace the whole block - assumes there's enough space to do it */
-static void update_by_replace_block(struct mail_index_update *update,
-                                   size_t extra_size)
-{
-       struct mail_index_data_record_header *data_hdr;
-       size_t data_size;
-       void *mem;
-
-       data_hdr = mail_index_data_lookup_header(update->index->data,
-                                                update->rec);
-
-       data_size = update->data_hdr.data_size;
-       i_assert(data_size == data_hdr->data_size);
-
-       mem = create_data_block(update, data_size, extra_size);
-       memcpy(data_hdr, mem, data_size);
-
-       /* clear the extra space. not really needed. */
-       memset((char *) data_hdr + data_size, 0,
-              data_hdr->data_size - data_size);
-
-        mail_index_data_mark_modified(update->index->data);
-}
-
-/* Replace the modified fields in the file - assumes there's enough
-   space to do it */
-static void update_by_replace_fields(struct mail_index_update *update)
-{
-       struct mail_index_data_record_header *data_hdr;
-       struct mail_index_data_record *rec;
-       size_t field_size;
-       int index;
-
-       /* update header */
-       data_hdr = mail_index_data_lookup_header(update->index->data,
-                                                update->rec);
-       memcpy(data_hdr, &update->data_hdr, sizeof(*data_hdr));
-
-       rec = mail_index_data_lookup(update->index->data, update->rec, 0);
-       while (rec != NULL) {
-               if (rec->field & update->updated_fields) {
-                       /* field was changed */
-                       index = mail_field_get_index(rec->field);
-                       i_assert(index >= 0);
-
-                       field_size = update->field_sizes[index];
-                       i_assert(field_size <= rec->full_field_size);
-
-                       memcpy(rec->data, update->fields[index], field_size);
-
-                       /* clear the extra space. not really needed. */
-                       memset(rec->data + field_size, 0,
-                              rec->full_field_size - field_size);
-               }
-
-               rec = mail_index_data_next(update->index->data,
-                                          update->rec, rec);
-       }
-
-        mail_index_data_mark_modified(update->index->data);
-}
-
-int mail_index_update_end(struct mail_index_update *update)
-{
-       struct mail_index_data_record_header *data_hdr;
-       size_t min_size, max_size, extra_size;
-       int no_grown_fields, failed = FALSE;
-
-       i_assert(update->index->lock_type == MAIL_LOCK_EXCLUSIVE);
-
-       if (update->updated_fields != 0) {
-               /* if fields don't fit to allocated data block, we have
-                  to move it to end of file */
-               get_data_block_sizes(update, &min_size, &max_size,
-                                    &no_grown_fields);
-               extra_size = max_size - min_size;
-
-               data_hdr = mail_index_data_lookup_header(update->index->data,
-                                                        update->rec);
-
-               if (no_grown_fields)
-                       update_by_replace_fields(update);
-               else if (data_hdr != NULL && min_size <= data_hdr->data_size)
-                       update_by_replace_block(update, extra_size);
-               else {
-                       if (!update_by_append(update, max_size, extra_size))
-                               failed = TRUE;
-               }
-
-               if (!failed) {
-                       /* update cached fields mask */
-                       update->rec->data_fields |= update->updated_fields;
-               }
-       }
-
-       pool_unref(update->pool);
-       return !failed;
-}
-
-void mail_index_update_abort(struct mail_index_update *update)
-{
-       pool_unref(update->pool);
-}
-
-static void update_field_full(struct mail_index_update *update,
-                             enum mail_data_field field,
-                             const void *value, size_t size,
-                             size_t extra_space)
-{
-       int index;
-
-       index = mail_field_get_index(field);
-       i_assert(index >= 0);
-
-       update->updated_fields |= field;
-       update->field_sizes[index] = size;
-       update->field_extra_sizes[index] = extra_space;
-       update->fields[index] = p_malloc(update->pool, size);
-       memcpy(update->fields[index], value, size);
-}
-
-static void update_header_field(struct mail_index_update *update,
-                               enum mail_data_field field,
-                               const void *value, size_t size __attr_unused__)
-{
-       switch (field) {
-       case DATA_HDR_INTERNAL_DATE:
-               i_assert(size == sizeof(time_t));
-               update->data_hdr.internal_date = *((const time_t *) value);
-               break;
-       case DATA_HDR_VIRTUAL_SIZE:
-               i_assert(size == sizeof(uoff_t));
-               update->data_hdr.virtual_size = *((const uoff_t *) value);
-               break;
-       case DATA_HDR_HEADER_SIZE:
-               i_assert(size == sizeof(uoff_t));
-               update->data_hdr.header_size = *((const uoff_t *) value);
-               break;
-       case DATA_HDR_BODY_SIZE:
-               i_assert(size == sizeof(uoff_t));
-               update->data_hdr.body_size = *((const uoff_t *) value);
-               break;
-       default:
-                i_unreached();
-       }
-
-       update->updated_fields |= field;
-}
-
-void mail_index_update_field(struct mail_index_update *update,
-                            enum mail_data_field field,
-                            const char *value, size_t extra_space)
-{
-       update_field_full(update, field, value, strlen(value) + 1, extra_space);
-}
-
-void mail_index_update_field_raw(struct mail_index_update *update,
-                                enum mail_data_field field,
-                                const void *value, size_t size)
-{
-       if (field >= DATA_FIELD_LAST)
-               update_header_field(update, field, value, size);
-       else
-               update_field_full(update, field, value, size, 0);
-}
-
-struct header_update_context {
-       struct mail_index_update *update;
-       pool_t envelope_pool;
-       struct message_part_envelope_data *envelope;
-
-       message_header_callback_t *header_cb;
-       void *context;
-};
-
-static void update_header_cb(struct message_part *part,
-                            struct message_header_line *hdr, void *context)
-{
-       struct header_update_context *ctx = context;
-
-       if (part != NULL && part->parent != NULL)
-               return;
-
-       /* see if we can do anything with this field */
-       if (ctx->update->index->header->cache_fields & DATA_FIELD_ENVELOPE) {
-               if (ctx->envelope_pool == NULL) {
-                       ctx->envelope_pool =
-                               pool_alloconly_create("index envelope", 2048);
-               }
-               imap_envelope_parse_header(ctx->envelope_pool,
-                                          &ctx->envelope, hdr);
-       }
-
-       if (ctx->header_cb != NULL)
-               ctx->header_cb(part, hdr, ctx->context);
-}
-
-void mail_index_update_headers(struct mail_index_update *update,
-                              struct istream *input,
-                               enum mail_data_field cache_fields,
-                              message_header_callback_t *header_cb,
-                              void *context)
-{
-       struct header_update_context ctx;
-       struct message_part *part;
-       struct message_size hdr_size, body_size;
-       pool_t pool;
-       buffer_t *buf;
-       const char *value, *error;
-       size_t size;
-       uoff_t start_offset;
-
-       ctx.update = update;
-       ctx.envelope_pool = NULL;
-       ctx.envelope = NULL;
-       ctx.header_cb = header_cb;
-       ctx.context = context;
-
-       if (cache_fields == 0)
-                cache_fields = update->index->header->cache_fields;
-
-       if (IS_BODYSTRUCTURE_FIELD(cache_fields)) {
-               /* for body / bodystructure, we need need to
-                  fully parse the message. unless it's already parsed
-                  and cached. */
-               pool = pool_alloconly_create("index message parser", 2048);
-
-               value = update->index->lookup_field_raw(update->index,
-                                                       update->rec,
-                                                       DATA_FIELD_MESSAGEPART,
-                                                       &size);
-               if (value == NULL)
-                       part = NULL;
-               else {
-                       part = message_part_deserialize(pool, value, size,
-                                                       &error);
-                       if (part == NULL) {
-                               /* corrupted, rebuild it */
-                               index_set_corrupted(update->index,
-                                       "Corrupted cached MessagePart data "
-                                       "(%s)", error);
-                       }
-               }
-
-               start_offset = input->v_offset;
-
-               if (part == NULL) {
-                       part = message_parse(pool, input,
-                                            update_header_cb, &ctx);
-                       if ((part->flags & MESSAGE_PART_FLAG_HAS_NULS) != 0) {
-                               update->rec->index_flags |=
-                                       INDEX_MAIL_FLAG_HAS_NULS;
-                       } else {
-                               update->rec->index_flags |=
-                                       INDEX_MAIL_FLAG_HAS_NO_NULS;
-                       }
-               } else {
-                       /* cached, construct the bodystructure using it.
-                          also we need to parse the header.. */
-                       i_stream_seek(input, start_offset);
-                       message_parse_header(NULL, input, NULL,
-                                            update_header_cb, &ctx);
-               }
-
-               /* save sizes */
-               hdr_size = part->header_size;
-               body_size = part->body_size;
-
-               /* don't save both BODY + BODYSTRUCTURE since BODY can be
-                  generated from BODYSTRUCTURE. FIXME: However that takes
-                  CPU, maybe this should be configurable (I/O vs. CPU)? */
-               if ((cache_fields & DATA_FIELD_BODY) &&
-                   ((update->rec->data_fields | cache_fields) &
-                    DATA_FIELD_BODYSTRUCTURE) == 0) {
-                       t_push();
-                       i_stream_seek(input, start_offset);
-                       value = imap_part_get_bodystructure(pool, &part,
-                                                           input, FALSE);
-                       update->index->update_field(update, DATA_FIELD_BODY,
-                                                   value, 0);
-                       t_pop();
-               }
-
-               if (cache_fields & DATA_FIELD_BODYSTRUCTURE) {
-                       t_push();
-                       i_stream_seek(input, start_offset);
-                       value = imap_part_get_bodystructure(pool, &part,
-                                                           input, TRUE);
-                       update->index->update_field(update,
-                                                   DATA_FIELD_BODYSTRUCTURE,
-                                                   value, 0);
-                       t_pop();
-               }
-
-               if (cache_fields & DATA_FIELD_MESSAGEPART) {
-                       t_push();
-                       buf = buffer_create_dynamic(data_stack_pool, 2048,
-                                                   (size_t)-1);
-                       message_part_serialize(part, buf);
-
-                       value = buffer_get_data(buf, &size);
-                       update->index->update_field_raw(update,
-                                                       DATA_FIELD_MESSAGEPART,
-                                                       value, size);
-                       t_pop();
-               }
-
-               pool_unref(pool);
-       } else {
-               message_parse_header(NULL, input, &hdr_size,
-                                    update_header_cb, &ctx);
-
-               body_size.physical_size = input->v_limit - input->v_offset;
-               if (body_size.physical_size == 0)
-                        body_size.virtual_size = 0;
-               else if (update->data_hdr.virtual_size == 0)
-                       body_size.virtual_size = (uoff_t)-1;
-               else {
-                       body_size.virtual_size =
-                               update->data_hdr.virtual_size -
-                               hdr_size.virtual_size;
-               }
-       }
-
-       if (ctx.envelope != NULL) {
-               string_t *str;
-
-               t_push();
-               str = str_new(data_stack_pool, 2048);
-               imap_envelope_write_part_data(ctx.envelope, str);
-               update->index->update_field(update, DATA_FIELD_ENVELOPE,
-                                           str_c(str), 0);
-               t_pop();
-
-               pool_unref(ctx.envelope_pool);
-       }
-
-       /* update physical sizes */
-       update->index->update_field_raw(update, DATA_HDR_HEADER_SIZE,
-                                       &hdr_size.physical_size,
-                                       sizeof(hdr_size.physical_size));
-       update->index->update_field_raw(update, DATA_HDR_BODY_SIZE,
-                                       &body_size.physical_size,
-                                       sizeof(body_size.physical_size));
-
-       /* update virtual size if we know it */
-       if (body_size.virtual_size != (uoff_t)-1) {
-               uoff_t virtual_size;
-
-               virtual_size = hdr_size.virtual_size + body_size.virtual_size;
-               update->index->update_field_raw(update, DATA_HDR_VIRTUAL_SIZE,
-                                               &virtual_size,
-                                               sizeof(virtual_size));
-       }
-
-
-       /* update binary flags. */
-       if (hdr_size.virtual_size == hdr_size.physical_size)
-               update->rec->index_flags |= INDEX_MAIL_FLAG_BINARY_HEADER;
-       if (body_size.virtual_size == body_size.physical_size)
-               update->rec->index_flags |= INDEX_MAIL_FLAG_BINARY_BODY;
-}
index da28d9eda2cbc5018d914b06c96a6b8ccd17a75c..58c9a17c555183593b2e4cb1f0709169ac69d547 100644 (file)
@@ -6,8 +6,8 @@
 #include "file-set-size.h"
 #include "mmap-util.h"
 #include "mail-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 #include "mail-modifylog.h"
 #include "mail-custom-flags.h"
 
@@ -27,7 +27,12 @@ static int mmap_verify(struct mail_index *index)
                return FALSE;
        }
 
-       extra = (index->mmap_full_length - sizeof(struct mail_index_header)) %
+       /* keep the header set even if we fail, so we can update the flags */
+       hdr = index->mmap_base;
+       index->header = hdr;
+       index->header_size = hdr->header_size;
+
+       extra = (index->mmap_full_length - index->header_size) %
                sizeof(struct mail_index_record);
 
        if (extra != 0) {
@@ -39,25 +44,20 @@ static int mmap_verify(struct mail_index *index)
                (void)ftruncate(index->fd, (off_t)index->mmap_full_length);
        }
 
-       /* keep the header set even if we fail, so we can update the flags */
-       hdr = index->mmap_base;
-       index->header = hdr;
-
        if (hdr->used_file_size > index->mmap_full_length) {
                index_set_corrupted(index,
                                    "used_file_size larger than real file size "
-                                   "(%"PRIuUOFF_T" vs %"PRIuSIZE_T")",
+                                   "(%u vs %"PRIuSIZE_T")",
                                    hdr->used_file_size,
                                    index->mmap_full_length);
                return FALSE;
        }
 
-       if (hdr->used_file_size < sizeof(struct mail_index_header) ||
-           (hdr->used_file_size - sizeof(struct mail_index_header)) %
+       if (hdr->used_file_size < index->header_size ||
+           (hdr->used_file_size - index->header_size) %
            sizeof(struct mail_index_record) != 0) {
                index_set_corrupted(index, "Invalid used_file_size in header "
-                                   "(%"PRIuUOFF_T")",
-                                   hdr->used_file_size);
+                                   "(%u)", hdr->used_file_size);
                return FALSE;
        }
 
@@ -139,14 +139,12 @@ void mail_index_close(struct mail_index *index)
                                 PROT_READ|PROT_WRITE);
 #endif
                        index->header->flags |= index->set_flags;
-                       (void)msync(index->mmap_base,
-                                   sizeof(struct mail_index_header), MS_SYNC);
+                       (void)msync(index->mmap_base, index->header_size,
+                                   MS_SYNC);
                }
                index->set_flags = 0;
        }
 
-       index->set_cache_fields = 0;
-
        index->opened = FALSE;
        index->inconsistent = FALSE;
 
@@ -174,9 +172,9 @@ void mail_index_close(struct mail_index *index)
        }
        index->mmap_base = NULL;
 
-       if (index->data != NULL) {
-                mail_index_data_free(index->data);
-               index->data = NULL;
+       if (index->cache != NULL) {
+                mail_cache_free(index->cache);
+               index->cache = NULL;
        }
 
        if (index->modifylog != NULL) {
@@ -206,9 +204,6 @@ static int mail_index_sync_file(struct mail_index *index)
        for (i = 0; i < sizeof(fsync_fds)/sizeof(fsync_fds[0]); i++)
                fsync_fds[i] = -1;
 
-       if (!mail_index_data_sync_file(index->data, &fsync_fds[0]))
-               return FALSE;
-
        if (msync(index->mmap_base, index->mmap_used_length, MS_SYNC) < 0)
                return index_set_syscall_error(index, "msync()");
 
@@ -261,11 +256,6 @@ static void mail_index_update_header_changes(struct mail_index *index)
                index->header->flags |= index->set_flags;
                index->set_flags = 0;
        }
-
-       if (index->set_cache_fields != 0) {
-               index->header->cache_fields = index->set_cache_fields;
-               index->set_cache_fields = 0;
-       }
 }
 
 static int mail_index_write_header_changes(struct mail_index *index)
@@ -285,8 +275,7 @@ static int mail_index_write_header_changes(struct mail_index *index)
        mail_index_update_header_changes(index);
 
        if (!index->anon_mmap) {
-               if (msync(index->mmap_base,
-                         sizeof(struct mail_index_header), MS_SYNC) < 0) {
+               if (msync(index->mmap_base, index->header_size, MS_SYNC) < 0) {
                        index_set_syscall_error(index, "msync()");
                        failed = TRUE;
                }
@@ -305,6 +294,13 @@ static int mail_index_write_header_changes(struct mail_index *index)
 static int mail_index_lock_remove(struct mail_index *index)
 {
        enum mail_lock_type old_lock_type;
+       int ret = TRUE;
+
+       while (index->cache_later_locks > 0) {
+               if (!mail_cache_unlock(index->cache))
+                       ret = FALSE;
+               index->cache_later_locks--;
+       }
 
        if (!mail_index_wait_lock(index, F_UNLCK))
                return FALSE;
@@ -316,18 +312,15 @@ static int mail_index_lock_remove(struct mail_index *index)
                /* releasing shared lock. we may need to update some
                   flags in header. */
                unsigned int old_flags;
-               enum mail_data_field old_cache;
 
                old_flags = index->header->flags;
-               old_cache = index->header->cache_fields;
 
-               if ((old_flags | index->set_flags) != old_flags ||
-                   (old_cache | index->set_cache_fields) != old_cache)
+               if ((old_flags | index->set_flags) != old_flags)
                        return mail_index_write_header_changes(index);
        }
 
         debug_mprotect(index->mmap_base, index->mmap_full_length, index);
-       return TRUE;
+       return ret;
 }
 
 static int mail_index_lock_change(struct mail_index *index,
@@ -335,10 +328,14 @@ static int mail_index_lock_change(struct mail_index *index,
 {
        int ret, fd_lock_type;
 
-       /* shared -> exclusive isn't allowed without try_lock */
+       /* shared -> exclusive can deadlock */
        i_assert(try_lock || lock_type != MAIL_LOCK_EXCLUSIVE ||
                 index->lock_type != MAIL_LOCK_SHARED);
 
+       /* locking index when cache is locked can deadlock */
+       i_assert(try_lock || index->lock_type == MAIL_LOCK_EXCLUSIVE ||
+                !mail_cache_is_locked(index->cache));
+
        if (index->inconsistent) {
                /* index is in inconsistent state and nothing else than
                   free() is allowed for it. */
@@ -379,7 +376,7 @@ static int mail_index_lock_change(struct mail_index *index,
                return FALSE;
        }
 
-       if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
+       if (index->header->flags & MAIL_INDEX_HDR_FLAG_FSCK) {
                /* someone just partially updated the index, need to fsck it */
                if (lock_type == MAIL_LOCK_SHARED) {
                        /* we need exclusive lock so fsck()'s set_lock() won't
@@ -397,7 +394,7 @@ static int mail_index_lock_change(struct mail_index *index,
 
                /* check again, in case it was already fscked while we had
                   it unlocked for a while */
-               if (index->header->flags & MAIL_INDEX_FLAG_FSCK) {
+               if (index->header->flags & MAIL_INDEX_HDR_FLAG_FSCK) {
                        if (!index->fsck(index))
                                return FALSE;
                }
@@ -413,9 +410,8 @@ static int mail_index_lock_change(struct mail_index *index,
                   when the lock is released, the FSCK flag will also be
                   removed. */
                index->excl_lock_counter++;
-               index->header->flags |= MAIL_INDEX_FLAG_FSCK;
-               if (!mail_index_fmdatasync(index,
-                                          sizeof(struct mail_index_header))) {
+               index->header->flags |= MAIL_INDEX_HDR_FLAG_FSCK;
+               if (!mail_index_fmdatasync(index, index->header_size)) {
                        (void)index->set_lock(index, MAIL_LOCK_UNLOCK);
                        return FALSE;
                }
@@ -452,15 +448,14 @@ static int mail_index_lock_full(struct mail_index *index,
                        mail_modifylog_notify_lock_drop(index->modifylog);
 
                /* dropping exclusive lock (either unlock or to shared) */
-               keep_fsck = (index->set_flags & MAIL_INDEX_FLAG_FSCK) != 0;
+               keep_fsck = (index->set_flags & MAIL_INDEX_HDR_FLAG_FSCK) != 0;
                mail_index_update_header_changes(index);
 
                /* remove the FSCK flag only after successful fsync() */
                if (mail_index_sync_file(index) && !keep_fsck) {
-                       index->header->flags &= ~MAIL_INDEX_FLAG_FSCK;
+                       index->header->flags &= ~MAIL_INDEX_HDR_FLAG_FSCK;
                        if (!index->anon_mmap &&
-                           msync(index->mmap_base,
-                                 sizeof(struct mail_index_header),
+                           msync(index->mmap_base, index->header_size,
                                  MS_SYNC) < 0) {
                                /* we only failed to remove the fsck flag,
                                   so this isn't fatal. */
@@ -502,115 +497,6 @@ struct mail_index_header *mail_index_get_header(struct mail_index *index)
        return index->header;
 }
 
-const char *mail_index_lookup_field(struct mail_index *index,
-                                   struct mail_index_record *rec,
-                                   enum mail_data_field field)
-{
-       struct mail_index_data_record *datarec;
-
-       datarec = (rec->data_fields & field) == 0 ? NULL :
-               mail_index_data_lookup(index->data, rec, field);
-       if (datarec == NULL)
-               return NULL;
-
-       if (!mail_index_data_record_verify(index->data, datarec)) {
-               /* index is corrupted, it will be rebuilt */
-               return NULL;
-       }
-
-       return datarec->data;
-}
-
-const void *mail_index_lookup_field_raw(struct mail_index *index,
-                                       struct mail_index_record *rec,
-                                       enum mail_data_field field,
-                                       size_t *size)
-{
-       struct mail_index_data_record_header *datahdr;
-       struct mail_index_data_record *datarec;
-
-       if ((rec->data_fields & field) == 0) {
-               *size = 0;
-               return NULL;
-       }
-
-       if (field < DATA_FIELD_LAST) {
-               /* read data field */
-               datarec = mail_index_data_lookup(index->data, rec, field);
-               if (datarec == NULL) {
-                       *size = 0;
-                       return NULL;
-               }
-
-               *size = datarec->full_field_size;
-               return datarec->data;
-       }
-
-       /* read header field */
-       datahdr = mail_index_data_lookup_header(index->data, rec);
-       if (datahdr == NULL) {
-               *size = 0;
-               return NULL;
-       }
-
-       switch (field) {
-       case DATA_HDR_INTERNAL_DATE:
-               *size = sizeof(datahdr->internal_date);
-               return &datahdr->internal_date;
-       case DATA_HDR_VIRTUAL_SIZE:
-               *size = sizeof(datahdr->virtual_size);
-               return &datahdr->virtual_size;
-       case DATA_HDR_HEADER_SIZE:
-               *size = sizeof(datahdr->header_size);
-               return &datahdr->header_size;
-       case DATA_HDR_BODY_SIZE:
-               *size = sizeof(datahdr->body_size);
-               return &datahdr->body_size;
-       default:
-               *size = 0;
-               return NULL;
-       }
-}
-
-void mail_index_cache_fields_later(struct mail_index *index,
-                                  enum mail_data_field field)
-{
-       i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
-
-       field &= ~index->never_cache_fields;
-
-       /* first check if the field even could be in the file */
-       if ((index->set_cache_fields & field) != field) {
-               if ((index->header->cache_fields & field) == 0) {
-                       /* no, but make sure the future records will have it.
-                          we don't immediately mark the index to cache this
-                          field for old messages as some clients never ask
-                          the info again */
-                       index->set_cache_fields |= field;
-               } else {
-                       /* this is at least the second time it's being asked,
-                          make sure it'll be cached soon. */
-                       index->set_flags |= MAIL_INDEX_FLAG_CACHE_FIELDS;
-               }
-       }
-}
-
-time_t mail_get_internal_date(struct mail_index *index,
-                             struct mail_index_record *rec)
-{
-       const time_t *date;
-       size_t size;
-
-       date = index->lookup_field_raw(index, rec,
-                                      DATA_HDR_INTERNAL_DATE, &size);
-       if (date == NULL)
-               return (time_t)-1;
-       else {
-               i_assert(size == sizeof(*date));
-               return *date;
-       }
-}
-
 void mail_index_mark_flag_changes(struct mail_index *index,
                                  struct mail_index_record *rec,
                                  enum mail_flags old_flags,
@@ -691,8 +577,8 @@ int mail_index_expunge(struct mail_index *index,
        }
 
        if (index->header->messages_count == 0) {
-               /* all mail was deleted, truncate data file */
-               if (!mail_index_data_reset(index->data))
+               /* all mail was deleted, truncate cache file */
+               if (!mail_cache_truncate(index->cache))
                        return FALSE;
        }
 
@@ -759,12 +645,19 @@ static int mail_index_grow(struct mail_index *index)
        return TRUE;
 }
 
-struct mail_index_record *mail_index_append_begin(struct mail_index *index)
+struct mail_index_record *mail_index_append(struct mail_index *index)
 {
        struct mail_index_record *rec;
 
        i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
 
+       if (index->header->next_uid == MAX_ALLOWED_UID) {
+               index->set_flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
+               index_set_error(index, "Reached maximum UID in mailbox %s, "
+                               "rebuilding index", index->filepath);
+               return NULL;
+       }
+
        if (index->mmap_used_length == index->mmap_full_length) {
                if (!mail_index_grow(index))
                        return NULL;
@@ -774,48 +667,17 @@ struct mail_index_record *mail_index_append_begin(struct mail_index *index)
        i_assert(index->mmap_used_length + sizeof(struct mail_index_record) <=
                 index->mmap_full_length);
 
+       index->header->messages_count++;
+
        rec = (struct mail_index_record *) ((char *) index->mmap_base +
                                            index->mmap_used_length);
-       memset(rec, 0, sizeof(struct mail_index_record));
-
-       index->header->used_file_size += sizeof(struct mail_index_record);
-       index->mmap_used_length += sizeof(struct mail_index_record);
-
-       return rec;
-}
-
-int mail_index_append_end(struct mail_index *index,
-                         struct mail_index_record *rec)
-{
-       i_assert(rec->uid == 0);
-
-       if (index->header->next_uid == MAX_ALLOWED_UID) {
-               index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
-               index_set_error(index, "Reached maximum UID in mailbox %s, "
-                               "rebuilding index", index->filepath);
-               return FALSE;
-       }
-
-       index->header->messages_count++;
        rec->uid = index->header->next_uid++;
+       rec->msg_flags = 0;
 
-       return TRUE;
-}
-
-void mail_index_append_abort(struct mail_index *index,
-                            struct mail_index_record *rec)
-{
-       i_assert(rec->uid == 0);
+       index->header->used_file_size += sizeof(*rec);
+       index->mmap_used_length += sizeof(*rec);
 
-       if (INDEX_FILE_POSITION(index, rec) ==
-           index->header->used_file_size - sizeof(*rec)) {
-               /* we can just rollback */
-               index->header->used_file_size -= sizeof(*rec);
-               index->mmap_used_length -= sizeof(*rec);
-       } else {
-               /* mark it deleted */
-               (void)mail_index_expunge_record_range(index, rec, rec);
-       }
+       return rec;
 }
 
 enum mail_index_error mail_index_get_last_error(struct mail_index *index)
index bab2fa5d60d3ee247996be57d609cc63b2979bc3..1f83c0fea4e4202697fb637c10c7f249e39bcac7 100644 (file)
@@ -1,11 +1,13 @@
 #ifndef __MAIL_INDEX_H
 #define __MAIL_INDEX_H
 
+#include "byteorder.h"
 #include "file-dotlock.h"
 #include "message-parser.h"
 #include "imap-util.h"
 
-#define MAIL_INDEX_VERSION 3
+#define MAIL_INDEX_MAJOR_VERSION 3
+#define MAIL_INDEX_MINOR_VERSION 0
 
 #define INDEX_FILE_PREFIX ".imap.index"
 
@@ -31,57 +33,34 @@ enum mail_index_header_flag {
        /* Rebuild flag is set while index is being rebuilt or when
           some error is noticed in the index file. If this flag is set,
           the index shouldn't be used before rebuilding it. */
-       MAIL_INDEX_FLAG_REBUILD                 = 0x0001,
-       MAIL_INDEX_FLAG_FSCK                    = 0x0002,
-       MAIL_INDEX_FLAG_CACHE_FIELDS            = 0x0004,
-       MAIL_INDEX_FLAG_COMPRESS                = 0x0008,
-       MAIL_INDEX_FLAG_COMPRESS_DATA           = 0x0010,
-       MAIL_INDEX_FLAG_DIRTY_MESSAGES          = 0x0040,
-       MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS       = 0x0080,
-       MAIL_INDEX_FLAG_MAILDIR_NEW             = 0x0100
+       MAIL_INDEX_HDR_FLAG_FSCK                = NBO32_BIT0,
+       MAIL_INDEX_HDR_FLAG_REBUILD             = NBO32_BIT1,
+       MAIL_INDEX_HDR_FLAG_COMPRESS            = NBO32_BIT2,
+       MAIL_INDEX_HDR_FLAG_COMPRESS_CACHE      = NBO32_BIT3,
+       MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES      = NBO32_BIT4,
+       MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS   = NBO32_BIT5,
+       MAIL_INDEX_HDR_FLAG_MAILDIR_NEW         = NBO32_BIT6
 };
 
-enum mail_data_field {
-       DATA_FIELD_LOCATION             = 0x00000001,
-       DATA_FIELD_ENVELOPE             = 0x00000002,
-       DATA_FIELD_BODY                 = 0x00000004,
-       DATA_FIELD_BODYSTRUCTURE        = 0x00000008,
-       DATA_FIELD_MD5                  = 0x00000010,
-       DATA_FIELD_MESSAGEPART          = 0x00000020,
-
-       DATA_FIELD_LAST                 = 0x00000040,
-       DATA_FIELD_MAX_BITS             = 6,
-
-       /* separate from above, but in same bitmask */
-       DATA_HDR_INTERNAL_DATE          = 0x40000000,
-       DATA_HDR_VIRTUAL_SIZE           = 0x20000000,
-       DATA_HDR_HEADER_SIZE            = 0x10000000,
-       DATA_HDR_BODY_SIZE              = 0x08000000
-};
-
-#define IS_BODYSTRUCTURE_FIELD(field) \
-       (((field) & (DATA_FIELD_BODY | DATA_FIELD_BODYSTRUCTURE | \
-                    DATA_FIELD_MESSAGEPART)) != 0)
-
-enum mail_index_mail_flag {
+enum mail_index_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. */
-       INDEX_MAIL_FLAG_BINARY_HEADER   = 0x0001,
-       INDEX_MAIL_FLAG_BINARY_BODY     = 0x0002,
+       MAIL_INDEX_FLAG_BINARY_HEADER   = NBO32_BIT0,
+       MAIL_INDEX_FLAG_BINARY_BODY     = NBO32_BIT1,
 
        /* Mail flags have been changed in index, but not written into
           actual mailbox yet. */
-       INDEX_MAIL_FLAG_DIRTY           = 0x0004,
+       MAIL_INDEX_FLAG_DIRTY           = NBO32_BIT2,
 
        /* Maildir: Mail file is in new/ dir instead of cur/ */
-       INDEX_MAIL_FLAG_MAILDIR_NEW     = 0x0008,
+       MAIL_INDEX_FLAG_MAILDIR_NEW     = NBO32_BIT3,
 
        /* Mail header or body is known to contain NUL characters. */
-       INDEX_MAIL_FLAG_HAS_NULS        = 0x0010,
+       MAIL_INDEX_FLAG_HAS_NULS        = NBO32_BIT4,
        /* Mail header or body is known to not contain NUL characters. */
-       INDEX_MAIL_FLAG_HAS_NO_NULS     = 0x0020
+       MAIL_INDEX_FLAG_HAS_NO_NULS     = NBO32_BIT5
 };
 
 enum mail_lock_type {
@@ -122,75 +101,41 @@ typedef void mail_lock_notify_callback_t(enum mail_lock_notify_type notify_type,
                                         unsigned int secs_left, void *context);
 
 struct mail_index_header {
-       unsigned char compat_data[8];
-       /* 0 = version
-          1 = flags,
-          2 = sizeof(unsigned int),
-          3 = sizeof(time_t),
-          4 = sizeof(uoff_t),
-          5 = INDEX_ALIGN_SIZE */
+       /* major version is increased only when you can't have backwards
+          compatibility. minor version is increased when header size is
+          increased to contain new non-critical fields. */
+       uint8_t major_version;
+       uint8_t minor_version;
+       uint8_t header_size;
+       uint8_t reserved;
 
-       unsigned int indexid;
-       unsigned int sync_id; /* re-mmap() when changed, required only
-                                if file size is changed */
+       uint32_t indexid;
 
-       unsigned int flags;
-       unsigned int cache_fields;
+       uint32_t used_file_size;
+       uint32_t sync_id; /* re-mmap() when changed, required only
+                            if file size is shrinked */
 
-       uoff_t used_file_size;
+       uint32_t flags;
 
-       unsigned int uid_validity;
-       unsigned int next_uid;
+       uint32_t uid_validity;
+       uint32_t next_uid;
 
-       unsigned int messages_count;
-       unsigned int seen_messages_count;
-       unsigned int deleted_messages_count;
-       unsigned int last_nonrecent_uid;
+       uint32_t messages_count;
+       uint32_t seen_messages_count;
+       uint32_t deleted_messages_count;
+       uint32_t last_nonrecent_uid;
 
        /* these UIDs may not exist and may not even be unseen */
-       unsigned int first_unseen_uid_lowwater;
-       unsigned int first_deleted_uid_lowwater;
-};
-
-struct mail_index_data_header {
-       unsigned int indexid;
-       unsigned int reserved; /* for alignment mostly */
-
-       uoff_t used_file_size;
-       uoff_t deleted_space;
+       uint32_t first_unseen_uid_lowwater;
+       uint32_t first_deleted_uid_lowwater;
 };
 
 struct mail_index_record {
-       unsigned int uid;
-       unsigned int msg_flags;
-       unsigned int data_position; /* first bit must be 0 */
-
-       unsigned int index_flags; /* enum mail_index_mail_flag */
-       unsigned int data_fields; /* enum mail_data_field */
-};
-
-struct mail_index_data_record_header {
-       unsigned int data_size; /* including this header */
-
-       time_t internal_date;
-       uoff_t virtual_size;
-
-       uoff_t header_size;
-       uoff_t body_size;
-};
-
-struct mail_index_data_record {
-       unsigned int field; /* enum mail_data_field */
-       unsigned int full_field_size;
-       char data[INDEX_ALIGN_SIZE]; /* variable size */
+       uint32_t uid;
+       uint32_t msg_flags;
+       uint32_t data_offset;
 };
 
-#define SIZEOF_MAIL_INDEX_DATA \
-       (sizeof(struct mail_index_data_record) - INDEX_ALIGN_SIZE)
-
-#define DATA_RECORD_SIZE(rec) \
-        (SIZEOF_MAIL_INDEX_DATA + (rec)->full_field_size)
-
 struct mail_index {
        /* Note that opening same index twice in the same process is a bad
           idea since they share the same file locks. As soon one of the
@@ -274,34 +219,16 @@ struct mail_index {
                                                      unsigned int last_uid,
                                                      unsigned int *seq_r);
 
-       /* Find field from specified record, or NULL if it's not in index.
-          Makes sure that the field ends with \0. */
-       const char *(*lookup_field)(struct mail_index *index,
-                                   struct mail_index_record *rec,
-                                   enum mail_data_field field);
-
-       /* Find field from specified record, or NULL if it's not in index. */
-       const void *(*lookup_field_raw)(struct mail_index *index,
-                                       struct mail_index_record *rec,
-                                       enum mail_data_field field,
-                                       size_t *size);
-
-       /* Mark the fields to be cached later. If any of them is already
-          set in hdr->cache_fields, mark the caching to happen next time
-          index is opened. */
-       void (*cache_fields_later)(struct mail_index *index,
-                                  enum mail_data_field field);
-
        /* Open mail file and return it as mmap()ed IStream. If we fail,
           we return NULL and set deleted = TRUE if failure was because the
-          mail was just deleted (ie. not an error). internal_date is set
+          mail was just deleted (ie. not an error). received_date is set
           if it's non-NULL. */
        struct istream *(*open_mail)(struct mail_index *index,
                                     struct mail_index_record *rec,
-                                    time_t *internal_date, int *deleted);
+                                    time_t *received_date, int *deleted);
 
-       /* Returns internal date of message, or (time_t)-1 if error occured. */
-       time_t (*get_internal_date)(struct mail_index *index,
+       /* Returns received date of message, or (time_t)-1 if error occured. */
+       time_t (*get_received_date)(struct mail_index *index,
                                    struct mail_index_record *rec);
 
        /* Expunge mails from index. Modifylog is also updated. The
@@ -327,41 +254,8 @@ struct mail_index {
                            int external_change);
 
        /* Append a new record to index. The index must be exclusively
-          locked before calling this function. rec->uid is updated in
-          append_end(). */
-       struct mail_index_record *(*append_begin)(struct mail_index *index);
-       int (*append_end)(struct mail_index *index,
-                         struct mail_index_record *rec);
-       void (*append_abort)(struct mail_index *index,
-                            struct mail_index_record *rec);
-
-       /* Updating fields happens by calling update_begin(), one or more
-          update_field()s and finally update_end() which does the actual
-          updating. The index must be exclusively locked all this time.
-          update_begin() and update_field() functions cannot fail.
-
-          The extra_space parameter for update_field() specifies the amount
-          of extra empty space we should leave after the value, so that if
-          the field grows in future it could be expanded without copying it
-          to end of file. When the field already exists, the extra_space
-          is ignored.
-
-          The files may not actually be updated until after you've unlocked
-          the file. */
-       struct mail_index_update *
-               (*update_begin)(struct mail_index *index,
-                               struct mail_index_record *rec);
-       int (*update_end)(struct mail_index_update *update);
-       void (*update_abort)(struct mail_index_update *update);
-
-       void (*update_field)(struct mail_index_update *update,
-                            enum mail_data_field field,
-                            const char *value, size_t extra_space);
-       /* Just remember that full_field_size will be INDEX_ALIGNed, so
-          it may differer from the given size parameter. */
-       void (*update_field_raw)(struct mail_index_update *update,
-                                enum mail_data_field field,
-                                const void *value, size_t size);
+          locked before calling this function. */
+       struct mail_index_record *(*append)(struct mail_index *index);
 
        /* Returns the last error code. */
        enum mail_index_error (*get_last_error)(struct mail_index *index);
@@ -371,7 +265,7 @@ struct mail_index {
        const char *(*get_last_error_text)(struct mail_index *index);
 
 /* private: */
-       struct mail_index_data *data;
+       struct mail_cache *cache;
        struct mail_modify_log *modifylog;
        struct mail_custom_flags *custom_flags;
 
@@ -379,7 +273,6 @@ struct mail_index {
        char *filepath; /* index file path */
        char *mailbox_path; /* file/directory for mailbox location */
        char *control_dir; /* destination for control files */
-       enum mail_data_field default_cache_fields, never_cache_fields;
        unsigned int indexid;
        unsigned int sync_id;
 
@@ -418,6 +311,7 @@ struct mail_index {
        size_t mmap_full_length;
 
        struct mail_index_header *header;
+       size_t header_size;
 
         enum mail_lock_type lock_type;
        time_t file_sync_stamp;
@@ -429,7 +323,7 @@ struct mail_index {
        /* these fields are OR'ed to the fields in index header once we
           get around grabbing exclusive lock */
        unsigned int set_flags;
-       enum mail_data_field set_cache_fields;
+       unsigned int cache_later_locks;
 
        unsigned int anon_mmap:1;
        unsigned int mmap_invalidate:1;
@@ -444,6 +338,7 @@ struct mail_index {
        unsigned int mailbox_lock_timeout:1;
        unsigned int maildir_keep_new:1;
        unsigned int maildir_have_new:1;
+       unsigned int maildir_synced_once:1;
 };
 
 #ifdef DEV_T_STRUCT
@@ -456,7 +351,7 @@ struct mail_index {
    members.. */
 #define MAIL_INDEX_PRIVATE_FILL \
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
-       0, 0, 0, 0, 0, 0, { 0, 0, 0 }, 0, \
+       0, 0, 0, 0, { 0, 0, 0 }, 0, 0, 0, \
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, \
@@ -481,15 +376,6 @@ struct mail_index_record *mail_index_next(struct mail_index *index,
 struct mail_index_record *
 mail_index_lookup_uid_range(struct mail_index *index, unsigned int first_uid,
                            unsigned int last_uid, unsigned int *seq_r);
-const char *mail_index_lookup_field(struct mail_index *index,
-                                   struct mail_index_record *rec,
-                                   enum mail_data_field field);
-const void *mail_index_lookup_field_raw(struct mail_index *index,
-                                       struct mail_index_record *rec,
-                                       enum mail_data_field field,
-                                       size_t *size);
-void mail_index_cache_fields_later(struct mail_index *index,
-                                  enum mail_data_field field);
 int mail_index_expunge(struct mail_index *index,
                       struct mail_index_record *first_rec,
                       struct mail_index_record *last_rec,
@@ -499,46 +385,22 @@ int mail_index_update_flags(struct mail_index *index,
                            struct mail_index_record *rec,
                            unsigned int seq, enum mail_flags flags,
                            int external_change);
-struct mail_index_record *mail_index_append_begin(struct mail_index *index);
-int mail_index_append_end(struct mail_index *index,
-                         struct mail_index_record *rec);
-void mail_index_append_abort(struct mail_index *index,
-                            struct mail_index_record *rec);
-struct mail_index_update *
-mail_index_update_begin(struct mail_index *index,
-                       struct mail_index_record *rec);
-int mail_index_update_end(struct mail_index_update *update);
-void mail_index_update_abort(struct mail_index_update *update);
-void mail_index_update_field(struct mail_index_update *update,
-                            enum mail_data_field field,
-                            const char *value, size_t extra_space);
-void mail_index_update_field_raw(struct mail_index_update *update,
-                                enum mail_data_field field,
-                                const void *value, size_t size);
-time_t mail_get_internal_date(struct mail_index *index,
-                             struct mail_index_record *rec);
+struct mail_index_record *mail_index_append(struct mail_index *index);
 enum mail_index_error mail_index_get_last_error(struct mail_index *index);
 const char *mail_index_get_last_error_text(struct mail_index *index);
 
 /* INTERNAL: */
 void mail_index_init(struct mail_index *index, const char *dir);
 int mail_index_mmap_update(struct mail_index *index);
-void mail_index_init_header(struct mail_index *index,
-                           struct mail_index_header *hdr);
+void mail_index_init_header(struct mail_index_header *hdr);
 void mail_index_close(struct mail_index *index);
 int mail_index_fmdatasync(struct mail_index *index, size_t size);
 void mail_index_mark_flag_changes(struct mail_index *index,
                                  struct mail_index_record *rec,
                                  enum mail_flags old_flags,
                                  enum mail_flags new_flags);
-void mail_index_update_headers(struct mail_index_update *update,
-                              struct istream *input,
-                               enum mail_data_field cache_fields,
-                              message_header_callback_t *header_cb,
-                              void *context);
-int mail_index_update_cache(struct mail_index *index);
+int mail_index_rebuild(struct mail_index *index);
 int mail_index_compress(struct mail_index *index);
-int mail_index_compress_data(struct mail_index *index);
 int mail_index_truncate(struct mail_index *index);
 int mail_index_expunge_record_range(struct mail_index *index,
                                    struct mail_index_record *first_rec,
@@ -577,7 +439,7 @@ int mail_index_expunge_record_range(struct mail_index *index,
 /* record for given index */
 #define INDEX_RECORD_AT(index, idx) \
        ((struct mail_index_record *) \
-        ((char *) index->mmap_base + sizeof(struct mail_index_header)) + (idx))
+        ((char *) index->mmap_base + (index)->header_size) + (idx))
 
 /* returns the next record after last one */
 #define INDEX_END_RECORD(index) \
@@ -585,26 +447,28 @@ int mail_index_expunge_record_range(struct mail_index *index,
         ((char *) (index)->mmap_base + (index)->mmap_used_length))
 
 /* index number for uoff_t position */
-#define INDEX_POSITION_INDEX(pos) \
-       (((pos) - sizeof(struct mail_index_header)) / \
+#define INDEX_POSITION_INDEX(index, pos) \
+       (((pos) - (index)->header_size) / \
         sizeof(struct mail_index_record))
 
 /* index number for given record */
 #define INDEX_RECORD_INDEX(index, ptr) \
-       INDEX_POSITION_INDEX(INDEX_FILE_POSITION(index, ptr))
+       INDEX_POSITION_INDEX(index, INDEX_FILE_POSITION(index, ptr))
 
 /* mark the index corrupted */
 #define INDEX_MARK_CORRUPTED(index) \
-       STMT_START { (index)->set_flags |= MAIL_INDEX_FLAG_REBUILD; } STMT_END
+       STMT_START { \
+               (index)->set_flags |= MAIL_INDEX_HDR_FLAG_REBUILD; \
+       } STMT_END
 
 /* get number of records in mmaped index */
 #define MAIL_INDEX_RECORD_COUNT(index) \
-       ((index->mmap_used_length - sizeof(struct mail_index_header)) / \
+       ((index->mmap_used_length - (index)->header_size) / \
         sizeof(struct mail_index_record))
 
 /* minimum size for index file */
-#define INDEX_FILE_MIN_SIZE \
-       (sizeof(struct mail_index_header) + \
+#define INDEX_FILE_MIN_SIZE(index) \
+       ((index)->header_size + \
         INDEX_MIN_RECORDS_COUNT * sizeof(struct mail_index_record))
 
 /* enum mail_lock_type to fcntl() lock type */
index adc20f4b1aaca8fe94a7f057f3bc61a00e885ff6..22436bcf0d8aa4c9fa8a243ee62ccc5d16379139 100644 (file)
@@ -12,7 +12,6 @@ libindex_maildir_a_SOURCES = \
        maildir-clean.c \
        maildir-expunge.c \
        maildir-open.c \
-       maildir-rebuild.c \
        maildir-sync.c \
        maildir-uidlist.c \
        maildir-update-flags.c
index 6198c62bd2d69d3aedc500a659e009b1f25118f6..8af3fe27ee59d5d2761d49f3ef12e235ad3510c8 100644 (file)
@@ -1,51 +1,27 @@
-/* Copyright (C) 2002 Timo Sirainen */
+/* Copyright (C) 2002-2003 Timo Sirainen */
 
 #include "lib.h"
-#include "istream.h"
 #include "maildir-index.h"
-#include "mail-index-util.h"
+#include "mail-cache.h"
 
-#include <unistd.h>
-#include <fcntl.h>
-#include <sys/stat.h>
-
-static int maildir_record_update(struct mail_index *index,
-                                struct mail_index_update *update, int fd)
-{
-       struct istream *input;
-        enum mail_data_field cache_fields;
-
-       if (index->mail_read_mmaped) {
-               input = i_stream_create_mmap(fd, system_pool,
-                                            MAIL_MMAP_BLOCK_SIZE, 0, 0, FALSE);
-       } else {
-               input = i_stream_create_file(fd, system_pool,
-                                            MAIL_READ_BLOCK_SIZE, FALSE);
-       }
-
-       cache_fields = index->header->cache_fields & ~DATA_FIELD_LOCATION;
-       mail_index_update_headers(update, input, cache_fields, NULL, NULL);
-
-       i_stream_unref(input);
-       return TRUE;
-}
-
-static int maildir_index_append_fd(struct mail_index *index,
-                                  int fd, const char *fname, int new_dir)
+int maildir_index_append_file(struct mail_cache_transaction_ctx **trans_ctx,
+                             struct mail_index *index, const char *fname,
+                             int new_dir)
 {
        struct mail_index_record *rec;
-       struct mail_index_update *update;
-       struct stat st;
+        enum mail_index_record_flag index_flags;
        uoff_t virtual_size;
        const char *p;
-       int failed;
 
-       i_assert(fname != NULL);
+       i_assert(index->lock_type == MAIL_LOCK_EXCLUSIVE);
 
-       if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
-               return FALSE;
+       if (*trans_ctx == NULL) {
+               if (mail_cache_transaction_begin(index->cache,
+                                                TRUE, trans_ctx) <= 0)
+                       return FALSE;
+       }
 
-       rec = index->append_begin(index);
+       rec = index->append(index);
        if (rec == NULL)
                return FALSE;
 
@@ -53,7 +29,11 @@ static int maildir_index_append_fd(struct mail_index *index,
        rec->msg_flags = maildir_filename_get_flags(fname, 0);
        mail_index_mark_flag_changes(index, rec, 0, rec->msg_flags);
 
-       update = index->update_begin(index, rec);
+       /* always set index flags */
+       index_flags = new_dir ? MAIL_INDEX_FLAG_MAILDIR_NEW : 0;
+       if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_INDEX_FLAGS,
+                           &index_flags, sizeof(index_flags)))
+               return FALSE;
 
        /* set virtual size if found from file name */
        p = strstr(fname, ",W=");
@@ -65,66 +45,19 @@ static int maildir_index_append_fd(struct mail_index *index,
                        p++;
                }
 
-               if (*p == ':' || *p == ',' || *p == '\0') {
-                       index->update_field_raw(update, DATA_HDR_VIRTUAL_SIZE,
-                                               &virtual_size,
-                                               sizeof(virtual_size));
+               if (*p == ':' || *p == ',' || *p != '\0') {
+                       if (!mail_cache_add(*trans_ctx, rec,
+                                           MAIL_CACHE_VIRTUAL_FULL_SIZE,
+                                           &virtual_size,
+                                           sizeof(virtual_size)))
+                               return FALSE;
                }
        }
 
-       /* set internal date */
-       if (fd != -1 && fstat(fd, &st) == 0) {
-               index->update_field_raw(update, DATA_HDR_INTERNAL_DATE,
-                                       &st.st_mtime, sizeof(st.st_mtime));
-       }
-
-       /* set the location */
-       if (new_dir)
-               rec->index_flags |= INDEX_MAIL_FLAG_MAILDIR_NEW;
-       index->update_field(update, DATA_FIELD_LOCATION, fname,
-                           MAILDIR_LOCATION_EXTRA_SPACE);
-
-       /* parse the header and update record's fields */
-       failed = fd == -1 ? FALSE : !maildir_record_update(index, update, fd);
-
-       if (!index->update_end(update) || failed) {
-               index->append_abort(index, rec);
+       /* always set location */
+       if (!mail_cache_add(*trans_ctx, rec, MAIL_CACHE_LOCATION,
+                           fname, strlen(fname)+1))
                return FALSE;
-       }
-
-       return index->append_end(index, rec);
-}
-
-int maildir_index_append_file(struct mail_index *index, const char *dir,
-                             const char *fname, int new_dir)
-{
-       const char *path;
-       int fd, ret;
-
-       i_assert(index->lock_type != MAIL_LOCK_SHARED);
 
-       if ((index->header->cache_fields & ~DATA_FIELD_LOCATION) == 0) {
-               /* nothing cached, don't bother opening the file */
-               return maildir_index_append_fd(index, -1, fname, new_dir);
-       }
-
-       path = t_strconcat(dir, "/", fname, NULL);
-       fd = open(path, O_RDONLY);
-       if (fd == -1) {
-               if (errno == ENOENT) {
-                       /* it's not found because it's deleted or renamed.
-                          don't try to handle any error cases here, just
-                          save the thing and let the syncing handle it
-                          later */
-                       return maildir_index_append_fd(index, -1,
-                                                      fname, new_dir);
-               }
-
-               return index_file_set_syscall_error(index, path, "open()");
-       }
-
-       ret = maildir_index_append_fd(index, fd, fname, new_dir);
-       if (close(fd) < 0)
-               return index_file_set_syscall_error(index, path, "close()");
-       return ret;
+       return TRUE;
 }
index 08aa50853c244b269bb4251191b33ed67163ce85..0feee973f5402d44760184e022422cf8b0aaaefe 100644 (file)
@@ -12,8 +12,9 @@ static int maildir_expunge_mail_file(struct mail_index *index,
                                     const char **fname)
 {
        const char *path;
+       int new_dir;
 
-       *fname = maildir_get_location(index, rec);
+       *fname = maildir_get_location(index, rec, &new_dir);
        if (*fname == NULL)
                return -1;
 
@@ -23,7 +24,7 @@ static int maildir_expunge_mail_file(struct mail_index *index,
        if (index->next_dirty_flush != 0)
                index->next_dirty_flush = ioloop_time;
 
-       if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+       if (new_dir) {
                /* probably in new/ dir */
                path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
                if (unlink(path) == 0)
index 91cb6a55b7dda75fdb1b844c4f865066071100c3..ce40a8b8fc2ca89fcc8bda18b606d83da2009014 100644 (file)
@@ -6,8 +6,8 @@
 #include "hostpid.h"
 #include "str.h"
 #include "maildir-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 #include <sys/stat.h>
@@ -24,20 +24,39 @@ static int maildir_index_open(struct mail_index *index,
 }
 
 const char *maildir_get_location(struct mail_index *index,
-                                struct mail_index_record *rec)
+                                struct mail_index_record *rec, int *new_dir)
 {
        const char *fname, *new_fname;
 
-       fname = index->lookup_field(index, rec, DATA_FIELD_LOCATION);
+       if (new_dir != NULL)
+               *new_dir = FALSE;
+
+       if (index->new_filenames != NULL) {
+               /* this has the most up-to-date filename */
+               new_fname = hash_lookup(index->new_filenames,
+                                       POINTER_CAST(rec->uid));
+               if (new_fname != NULL) {
+                       if (*new_fname == '/') {
+                               new_fname++;
+                               if (new_dir != NULL)
+                                       *new_dir = TRUE;
+                       }
+                       return new_fname;
+               }
+       }
+
+       /* index file should give us at least the base name. */
+       fname = mail_cache_lookup_string_field(index->cache, rec,
+                                              MAIL_CACHE_LOCATION);
        if (fname == NULL) {
-               index_data_set_corrupted(index->data,
+               mail_cache_set_corrupted(index->cache,
                        "Missing location field for record %u", rec->uid);
+               return NULL;
        }
 
-       if (index->new_filenames != NULL) {
-               new_fname = hash_lookup(index->new_filenames, fname);
-               if (new_fname != NULL)
-                       return new_fname;
+       if (new_dir != NULL) {
+               *new_dir = (mail_cache_get_index_flags(index->cache, rec) &
+                           MAIL_INDEX_FLAG_MAILDIR_NEW) != 0;
        }
 
        return fname;
@@ -213,6 +232,31 @@ const char *maildir_filename_set_flags(const char *fname, enum mail_flags flags)
        return str_c(flags_str);
 }
 
+void maildir_index_update_filename(struct mail_index *index, unsigned int uid,
+                                  const char *fname, int new_dir)
+{
+       const char *new_fname, *old_fname;
+
+       if (index->new_filename_pool == NULL) {
+               index->new_filename_pool =
+                       pool_alloconly_create("Maildir filenames", 10240);
+       }
+       if (index->new_filenames == NULL) {
+               index->new_filenames =
+                       hash_create(system_pool, index->new_filename_pool, 0,
+                                   NULL, NULL);
+       }
+
+       t_push();
+       new_fname = !new_dir ? fname : t_strconcat("/", fname, NULL);
+       old_fname = hash_lookup(index->new_filenames, POINTER_CAST(uid));
+       if (old_fname == NULL || strcmp(old_fname, new_fname) != 0) {
+               hash_insert(index->new_filenames, POINTER_CAST(uid),
+                            p_strdup(index->new_filename_pool, new_fname));
+       }
+       t_pop();
+}
+
 struct mail_index *
 maildir_index_alloc(const char *maildir, const char *index_dir,
                    const char *control_dir)
@@ -247,18 +291,19 @@ static void maildir_index_free(struct mail_index *index)
        i_free(index);
 }
 
-static int maildir_get_internal_date_file(struct mail_index *index,
+static int maildir_get_received_date_file(struct mail_index *index,
                                          struct mail_index_record *rec,
                                          const char **fname, struct stat *st)
 {
        const char *path;
+       int new_dir;
 
        /* stat() gives it */
-       *fname = maildir_get_location(index, rec);
+       *fname = maildir_get_location(index, rec, &new_dir);
        if (*fname == NULL)
                return -1;
 
-       if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+       if (new_dir) {
                /* probably in new/ dir */
                path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
                if (stat(path, st) < 0 && errno != ENOENT) {
@@ -279,7 +324,7 @@ static int maildir_get_internal_date_file(struct mail_index *index,
        return TRUE;
 }
 
-static time_t maildir_get_internal_date(struct mail_index *index,
+static time_t maildir_get_received_date(struct mail_index *index,
                                        struct mail_index_record *rec)
 {
        struct stat st;
@@ -288,11 +333,12 @@ static time_t maildir_get_internal_date(struct mail_index *index,
        int ret, i, found;
 
        /* try getting it from cache */
-       date = mail_get_internal_date(index, rec);
-       if (date != (time_t)-1)
+       if (mail_cache_copy_fixed_field(index->cache, rec,
+                                       MAIL_CACHE_RECEIVED_DATE,
+                                       &date, sizeof(date)))
                return date;
 
-       ret = maildir_get_internal_date_file(index, rec, &fname, &st);
+       ret = maildir_get_received_date_file(index, rec, &fname, &st);
        for (i = 0; ret == 0 && i < 10; i++) {
                /* file is either renamed or deleted. sync the maildir and
                   see which one. if file appears to be renamed constantly,
@@ -305,7 +351,7 @@ static time_t maildir_get_internal_date(struct mail_index *index,
                        return (time_t)-1;
                }
 
-               ret = maildir_get_internal_date_file(index, rec, &fname, &st);
+               ret = maildir_get_received_date_file(index, rec, &fname, &st);
        }
 
        return st.st_mtime;
@@ -317,28 +363,18 @@ struct mail_index maildir_index = {
        mail_index_set_lock,
        mail_index_try_lock,
         mail_index_set_lock_notify_callback,
-       maildir_index_rebuild,
+       mail_index_rebuild,
        mail_index_fsck,
        maildir_index_sync,
        mail_index_get_header,
        mail_index_lookup,
        mail_index_next,
         mail_index_lookup_uid_range,
-       mail_index_lookup_field,
-       mail_index_lookup_field_raw,
-       mail_index_cache_fields_later,
        maildir_open_mail,
-       maildir_get_internal_date,
+       maildir_get_received_date,
        mail_index_expunge,
        maildir_index_update_flags,
-       mail_index_append_begin,
-       mail_index_append_end,
-       mail_index_append_abort,
-       mail_index_update_begin,
-       mail_index_update_end,
-       mail_index_update_abort,
-       mail_index_update_field,
-       mail_index_update_field_raw,
+       mail_index_append,
        mail_index_get_last_error,
        mail_index_get_last_error_text,
 
index 3d0d192427cf55c964bd7ab07da73276ffbe1ef2..d3f41420b4e220a92d6ed5ac1a1e707274a5b638 100644 (file)
@@ -1,13 +1,11 @@
 #ifndef __MAILDIR_INDEX_H
 #define __MAILDIR_INDEX_H
 
+struct mail_cache_transaction_ctx;
+
 #include <sys/time.h>
-#include <dirent.h>
 #include "mail-index.h"
 
-/* ":2,DFRST" - leave the 2 extra for other clients' additions */
-#define MAILDIR_LOCATION_EXTRA_SPACE 10
-
 /* How often to try to flush dirty flags. */
 #define MAILDIR_DIRTY_FLUSH_TIMEOUT (60*5)
 
@@ -21,20 +19,22 @@ int maildir_create_tmp(struct mail_index *index, const char *dir,
                       const char **path);
 
 const char *maildir_get_location(struct mail_index *index,
-                                struct mail_index_record *rec);
+                                struct mail_index_record *rec, int *new_dir);
 enum mail_flags maildir_filename_get_flags(const char *fname,
                                           enum mail_flags default_flags);
 const char *maildir_filename_set_flags(const char *fname,
                                       enum mail_flags flags);
+void maildir_index_update_filename(struct mail_index *index, unsigned int uid,
+                                  const char *fname, int new_dir);
 
-int maildir_index_rebuild(struct mail_index *index);
 int maildir_index_sync_readonly(struct mail_index *index,
                                const char *fname, int *found);
 int maildir_index_sync(struct mail_index *index, int minimal_sync,
                       enum mail_lock_type lock_type, int *changes);
 
-int maildir_index_append_file(struct mail_index *index, const char *dir,
-                             const char *fname, int new_dir);
+int maildir_index_append_file(struct mail_cache_transaction_ctx **trans_ctx,
+                             struct mail_index *index, const char *fname,
+                             int new_dir);
 int maildir_index_update_flags(struct mail_index *index,
                               struct mail_index_record *rec, unsigned int seq,
                               enum mail_flags flags, int external_change);
@@ -42,7 +42,7 @@ int maildir_try_flush_dirty_flags(struct mail_index *index, int force);
 
 struct istream *maildir_open_mail(struct mail_index *index,
                                  struct mail_index_record *rec,
-                                 time_t *internal_date, int *deleted);
+                                 time_t *received_date, int *deleted);
 
 int maildir_expunge_mail(struct mail_index *index,
                         struct mail_index_record *rec);
index c60e23fd55dd74c383fc75952405a149199d61cd..3d427b773876a8c7b0328b9742a60244db58b223 100644 (file)
@@ -3,8 +3,8 @@
 #include "lib.h"
 #include "istream.h"
 #include "maildir-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <unistd.h>
 #include <fcntl.h>
@@ -15,13 +15,13 @@ static int maildir_open_mail_file(struct mail_index *index,
                                  const char **fname, int *deleted)
 {
        const char *path;
-       int fd = -1;
+       int new_dir, fd = -1;
 
-       *fname = maildir_get_location(index, rec);
+       *fname = maildir_get_location(index, rec, &new_dir);
        if (*fname == NULL)
                return -1;
 
-       if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+       if (new_dir) {
                /* probably in new/ dir */
                path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
                fd = open(path, O_RDONLY);
@@ -50,7 +50,7 @@ static int maildir_open_mail_file(struct mail_index *index,
 
 struct istream *maildir_open_mail(struct mail_index *index,
                                  struct mail_index_record *rec,
-                                 time_t *internal_date, int *deleted)
+                                 time_t *received_date, int *deleted)
 {
        struct stat st;
        const char *fname;
@@ -84,13 +84,9 @@ struct istream *maildir_open_mail(struct mail_index *index,
                        return NULL;
        }
 
-       if (internal_date != NULL) {
-               *internal_date = mail_get_internal_date(index, rec);
-
-               if (*internal_date == (time_t)-1) {
-                       if (fstat(fd, &st) == 0)
-                               *internal_date = st.st_mtime;
-               }
+       if (received_date != NULL) {
+               if (fstat(fd, &st) == 0)
+                       *received_date = st.st_mtime;
        }
 
        if (index->mail_read_mmaped) {
index 6bba31c9edb092903518c78d75ec00688741e2a2..d24671c650c223ccf30ea2a59beb35a480941f89 100644 (file)
 #include "str.h"
 #include "maildir-index.h"
 #include "maildir-uidlist.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 #include <stdlib.h>
 enum maildir_file_action {
        MAILDIR_FILE_ACTION_EXPUNGE,
         MAILDIR_FILE_ACTION_UPDATE_FLAGS,
-       MAILDIR_FILE_ACTION_UPDATE_CONTENT,
        MAILDIR_FILE_ACTION_NEW,
        MAILDIR_FILE_ACTION_NONE,
 
@@ -236,6 +235,7 @@ struct maildir_sync_context {
        struct dirent *new_dent;
 
        struct maildir_uidlist *uidlist;
+        struct mail_cache_transaction_ctx *trans_ctx;
        unsigned int readonly_check:1;
        unsigned int flag_updates:1;
        unsigned int uidlist_rewrite:1;
@@ -276,41 +276,6 @@ static int maildir_cmp(const void *p1, const void *p2)
        return *s1 - *s2;
 }
 
-static void maildir_update_filename_memory(struct mail_index *index,
-                                          const char *fname)
-{
-       char *new_fname;
-
-       if (index->new_filename_pool == NULL) {
-               index->new_filename_pool =
-                       pool_alloconly_create("Maildir fname", 10240);
-       }
-       if (index->new_filenames == NULL) {
-               index->new_filenames =
-                       hash_create(system_pool, index->new_filename_pool, 0,
-                                   maildir_hash, maildir_cmp);
-       }
-
-       new_fname = p_strdup(index->new_filename_pool, fname);
-       hash_insert(index->new_filenames, new_fname, new_fname);
-}
-
-static int maildir_update_filename(struct maildir_sync_context *ctx,
-                                  struct mail_index_record *rec,
-                                  const char *new_fname)
-{
-       struct mail_index_update *update;
-
-       if (ctx->index->lock_type != MAIL_LOCK_EXCLUSIVE) {
-               maildir_update_filename_memory(ctx->index, new_fname);
-               return TRUE;
-       }
-
-       update = ctx->index->update_begin(ctx->index, rec);
-       ctx->index->update_field(update, DATA_FIELD_LOCATION, new_fname, 0);
-       return ctx->index->update_end(update);
-}
-
 static int maildir_update_flags(struct maildir_sync_context *ctx,
                                struct mail_index_record *rec,
                                unsigned int seq, const char *new_fname)
@@ -373,11 +338,6 @@ static int maildir_sync_open_uidlist(struct maildir_sync_context *ctx)
                        return FALSE;
                }
 
-               if (!index->rebuilding) {
-                       index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
-                       return FALSE;
-               }
-
                index->header->uid_validity = ctx->uidlist->uid_validity;
                i_assert(index->header->next_uid == 1);
        }
@@ -393,47 +353,6 @@ static int maildir_sync_open_uidlist(struct maildir_sync_context *ctx)
        return TRUE;
 }
 
-static int is_file_content_changed(struct mail_index *index,
-                                  struct mail_index_record *rec,
-                                  const char *dir, const char *fname)
-{
-#define DATA_HDR_SIZE (DATA_HDR_HEADER_SIZE | DATA_HDR_BODY_SIZE)
-       struct mail_index_data_record_header *data_hdr;
-       struct stat st;
-       const char *path;
-
-       if ((rec->data_fields & DATA_HDR_INTERNAL_DATE) == 0 &&
-           (rec->data_fields & DATA_HDR_SIZE) != DATA_HDR_SIZE) {
-               /* nothing in cache, we can't know if it's changed */
-               return FALSE;
-       }
-
-       t_push();
-       path = t_strdup_printf("%s/%s", dir, fname);
-
-       if (stat(path, &st) < 0) {
-               if (errno != ENOENT)
-                       index_file_set_syscall_error(index, path, "stat()");
-               t_pop();
-               return FALSE;
-       }
-       t_pop();
-
-       data_hdr = mail_index_data_lookup_header(index->data, rec);
-       if (data_hdr == NULL)
-               return FALSE;
-
-       if ((rec->data_fields & DATA_HDR_INTERNAL_DATE) != 0 &&
-           st.st_mtime != data_hdr->internal_date)
-               return TRUE;
-
-       if ((rec->data_fields & DATA_HDR_SIZE) == DATA_HDR_SIZE &&
-           (uoff_t)st.st_size != data_hdr->body_size + data_hdr->header_size)
-               return TRUE;
-
-       return FALSE;
-}
-
 static void uidlist_hash_get_filenames(void *key, void *value, void *context)
 {
        buffer_t *buf = context;
@@ -464,6 +383,53 @@ static int maildir_time_cmp(const void *p1, const void *p2)
        return t1 < t2 ? -1 : t1 > t2 ? 1 : 0;
 }
 
+static int maildir_full_sync_finish_new_mails(struct maildir_sync_context *ctx)
+{
+       const char *dir, **new_files;
+       buffer_t *buf;
+       unsigned int i;
+       int new_dir;
+
+       ctx->uidlist_rewrite = TRUE;
+
+       /* then there's the completely new mails. sort them by the filename
+          so we should get them to same order as they were created. */
+       buf = buffer_create_static_hard(ctx->pool,
+                                       ctx->new_count * sizeof(const char *));
+       hash_foreach(ctx->files, uidlist_hash_get_filenames, buf);
+       i_assert(buffer_get_used_size(buf) ==
+                ctx->new_count * sizeof(const char *));
+
+       new_files = buffer_get_modifyable_data(buf, NULL);
+       qsort(new_files, ctx->new_count, sizeof(const char *),
+             maildir_time_cmp);
+
+       if (!ctx->index->maildir_keep_new) {
+               dir = ctx->cur_dir;
+               new_dir = FALSE;
+       } else {
+               /* this is actually slightly wrong, because we don't really
+                  know if some of the new messages are in cur/ already.
+                  we could know that by saving it into buffer, but that'd
+                  require extra memory. luckily it doesn't really matter if
+                  we say it's in new/, but it's actually in cur/. we have
+                  to deal with such case anyway since another client might
+                  have just moved it. */
+               dir = ctx->new_dir;
+               new_dir = TRUE;
+               ctx->index->maildir_have_new = TRUE;
+       }
+
+       for (i = 0; i < ctx->new_count; i++) {
+               if (!maildir_index_append_file(&ctx->trans_ctx, ctx->index,
+                                              new_files[i], new_dir))
+                       return FALSE;
+       }
+       ctx->new_count = 0;
+
+       return TRUE;
+}
+
 static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
 {
        struct mail_index *index = ctx->index;
@@ -472,11 +438,10 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
        struct maildir_hash_rec *hash_rec;
        struct maildir_uidlist_rec uid_rec;
         enum maildir_file_action action;
-       const char *fname, **new_files, *dir;
+       const char *fname, *dir;
        void *orig_key, *orig_value;
-       unsigned int seq, first_seq, last_seq, uid, last_uid, i, new_flag;
+       unsigned int seq, first_seq, last_seq, uid, last_uid, new_flag;
        int new_dir, skip_next;
-       buffer_t *buf;
 
        if (ctx->new_count > 0) {
                /* new mails, either they're already in uidlist or we have
@@ -512,7 +477,7 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
                                return FALSE;
                }
 
-               fname = maildir_get_location(index, rec);
+               fname = maildir_get_location(index, rec, NULL);
                if (fname == NULL)
                        return FALSE;
 
@@ -536,20 +501,16 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
                    (ACTION(hash_rec) == MAILDIR_FILE_ACTION_UPDATE_FLAGS ||
                     ACTION(hash_rec) == MAILDIR_FILE_ACTION_NONE)) {
                        /* it's UID has changed. shouldn't happen. */
-                       i_warning("UID changed for %s/%s: %u -> %u",
-                                 index->mailbox_path, fname, uid, uid_rec.uid);
-                       hash_rec->action = MAILDIR_FILE_ACTION_UPDATE_CONTENT |
-                               (hash_rec->action & MAILDIR_FILE_FLAGS);
+                       index_set_corrupted(index,
+                                           "UID changed for %s/%s: %u -> %u",
+                                           index->mailbox_path, fname,
+                                           uid, uid_rec.uid);
+                       return FALSE;
                }
 
                action = hash_rec != NULL ?
                        ACTION(hash_rec) : MAILDIR_FILE_ACTION_NONE;
                switch (action) {
-               case MAILDIR_FILE_ACTION_UPDATE_CONTENT:
-                       hash_rec->action = MAILDIR_FILE_ACTION_NEW |
-                               (hash_rec->action & MAILDIR_FILE_FLAGS);
-                       ctx->new_count++;
-                       /* fall through */
                case MAILDIR_FILE_ACTION_EXPUNGE:
                        if (first_rec == NULL) {
                                first_rec = rec;
@@ -559,8 +520,10 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
                        last_seq = seq;
                        break;
                case MAILDIR_FILE_ACTION_UPDATE_FLAGS:
-                       if (!maildir_update_filename(ctx, rec, orig_key))
-                               return FALSE;
+                       new_dir = (hash_rec->action &
+                                  MAILDIR_FILE_FLAG_NEWDIR) != 0;
+                       maildir_index_update_filename(index, rec->uid,
+                                                     orig_key, new_dir);
                        if (!maildir_update_flags(ctx, rec, seq, fname))
                                return FALSE;
                        /* fall through */
@@ -651,8 +614,8 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
                                ctx->index->maildir_have_new = TRUE;
                        dir = new_flag != 0 ? ctx->new_dir : ctx->cur_dir;
 
-                       if (!maildir_index_append_file(index, dir, orig_key,
-                                                      new_flag != 0))
+                       if (!maildir_index_append_file(&ctx->trans_ctx, index,
+                                                      orig_key, new_flag != 0))
                                return FALSE;
                }
 
@@ -677,48 +640,13 @@ static int maildir_full_sync_finish(struct maildir_sync_context *ctx)
                index->last_uidlist_mtime = st.st_mtime;
        }
 
-       if (ctx->new_count == 0 || !INDEX_IS_UIDLIST_LOCKED(index)) {
-               /* all done (or can't do it since we don't have lock) */
-               return TRUE;
-       }
-
-       ctx->uidlist_rewrite = TRUE;
-
-       /* then there's the completely new mails. sort them by the filename
-          so we should get them to same order as they were created. */
-       buf = buffer_create_static_hard(ctx->pool,
-                                       ctx->new_count * sizeof(const char *));
-       hash_foreach(ctx->files, uidlist_hash_get_filenames, buf);
-       i_assert(buffer_get_used_size(buf) ==
-                ctx->new_count * sizeof(const char *));
-
-       new_files = buffer_get_modifyable_data(buf, NULL);
-       qsort(new_files, ctx->new_count, sizeof(const char *),
-             maildir_time_cmp);
-
-       if (!index->maildir_keep_new) {
-               dir = ctx->cur_dir;
-               new_dir = FALSE;
-       } else {
-               /* this is actually slightly wrong, because we don't really
-                  know if some of the new messages are in cur/ already.
-                  we could know that by saving it into buffer, but that'd
-                  require extra memory. luckily it doesn't really matter if
-                  we say it's in new/, but it's actually in cur/. we have
-                  to deal with such case anyway since another client might
-                  have just moved it. */
-               dir = ctx->new_dir;
-               new_dir = TRUE;
-               ctx->index->maildir_have_new = TRUE;
-       }
-
-       for (i = 0; i < ctx->new_count; i++) {
-               if (!maildir_index_append_file(index, dir,
-                                              new_files[i], new_dir))
-                       return FALSE;
-       }
+       if (ctx->new_count > 0 && INDEX_IS_UIDLIST_LOCKED(index))
+                maildir_full_sync_finish_new_mails(ctx);
 
-       ctx->new_count = 0;
+       /* all done (or can't do it since we don't have lock) */
+       ctx->index->maildir_synced_once = TRUE;
+       if (ctx->trans_ctx != NULL)
+               mail_cache_transaction_commit(ctx->trans_ctx);
        return TRUE;
 }
 
@@ -730,12 +658,12 @@ static int maildir_full_sync_init(struct maildir_sync_context *ctx,
        struct maildir_hash_rec *hash_rec;
        const char *fname;
        size_t size;
-       int have_new;
+       int new_dir, have_new;
 
-       /* FIXME: kludge. we want to have pointers to data file, so we must
-          make sure that it's base address doesn't change. this call makes
-          sure it's fully mmaped in memory even when we begin */
-       if (mail_index_data_get_mmaped(index->data, &size) == NULL)
+       /* kludge. we want to have pointers to data file, so we must make sure
+          that it's base address doesn't change. this call makes sure it's
+          fully mmaped in memory even when we begin */
+       if (mail_cache_get_mmaped(index->cache, &size) == NULL)
                return FALSE;
 
        if (index->header->messages_count >= INT_MAX/32) {
@@ -744,6 +672,19 @@ static int maildir_full_sync_init(struct maildir_sync_context *ctx,
                return FALSE;
        }
 
+       /* we're resyncing everything, so reset the filename hash */
+       if (index->new_filenames != NULL) {
+               hash_destroy(index->new_filenames);
+               index->new_filenames = NULL;
+       }
+
+       if (index->new_filename_pool != NULL)
+               p_clear(index->new_filename_pool);
+
+       /* reset synced-flag too, just in case something fails and we don't
+          have up-to-date new_filenames */
+       ctx->index->maildir_synced_once = FALSE;
+
        /* read current messages in index into hash */
        size = nearest_power(index->header->messages_count *
                             sizeof(struct maildir_hash_rec) + 1024);
@@ -757,15 +698,14 @@ static int maildir_full_sync_init(struct maildir_sync_context *ctx,
 
        rec = index->lookup(index, 1);
        while (rec != NULL) {
-               fname = maildir_get_location(index, rec);
+               fname = maildir_get_location(index, rec, &new_dir);
                if (fname == NULL)
                        return FALSE;
 
-               if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0)
+               if (new_dir)
                        have_new = TRUE;
 
-               if (!only_new ||
-                   (rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+               if (!only_new || new_dir) {
                        hash_rec = p_new(ctx->pool, struct maildir_hash_rec, 1);
                        hash_rec->rec = rec;
                        hash_rec->action = MAILDIR_FILE_ACTION_EXPUNGE;
@@ -841,20 +781,14 @@ static void uidlist_hash_fix_allocs(void *key, void *value, void *context)
 }
 
 static int maildir_full_sync_dir(struct maildir_sync_context *ctx,
-                                const char *dir, int new_dir,
-                                DIR *dirp, struct dirent *d)
+                                int new_dir, DIR *dirp, struct dirent *d)
 {
        struct maildir_hash_rec *hash_rec;
        void *orig_key, *orig_value;
-       int check_content_changes, newflag;
+       int newflag;
 
        newflag = new_dir ? MAILDIR_FILE_FLAG_NEWDIR : 0;
 
-       /* Do we want to check changes in file contents? This slows down
-          things as we need to do extra stat() for all files. */
-       check_content_changes = !ctx->readonly_check &&
-               getenv("MAILDIR_CHECK_CONTENT_CHANGES") != NULL;
-
        do {
                if (d->d_name[0] == '.')
                        continue;
@@ -889,25 +823,7 @@ static int maildir_full_sync_dir(struct maildir_sync_context *ctx,
                        continue;
                }
 
-               if (!new_dir && (hash_rec->rec->index_flags &
-                                INDEX_MAIL_FLAG_MAILDIR_NEW) != 0 &&
-                   ctx->index->lock_type == MAIL_LOCK_EXCLUSIVE) {
-                       /* mail was indexed in new/ but it has been
-                          moved to cur/ later */
-                       hash_rec->rec->index_flags &=
-                               ~INDEX_MAIL_FLAG_MAILDIR_NEW;
-               }
-
-               if (check_content_changes &&
-                   is_file_content_changed(ctx->index, hash_rec->rec,
-                                           dir, d->d_name)) {
-                       /* file content changed, treat it as new message */
-                       hash_rec->action =
-                               MAILDIR_FILE_ACTION_UPDATE_CONTENT | newflag;
-
-                       hash_insert(ctx->files, p_strdup(ctx->pool, d->d_name),
-                                   hash_rec);
-               } else if (strcmp(orig_key, d->d_name) != 0) {
+               if (strcmp(orig_key, d->d_name) != 0) {
                        hash_rec->action =
                                MAILDIR_FILE_ACTION_UPDATE_FLAGS | newflag;
 
@@ -969,8 +885,8 @@ static int maildir_full_sync_dirs(struct maildir_sync_context *ctx)
        }
 
        if (ctx->new_dent != NULL) {
-               if (!maildir_full_sync_dir(ctx, ctx->new_dir, TRUE,
-                                          ctx->new_dirp, ctx->new_dent))
+               if (!maildir_full_sync_dir(ctx, TRUE, ctx->new_dirp,
+                                          ctx->new_dent))
                        return FALSE;
                 ctx->new_dent = NULL;
        }
@@ -981,8 +897,7 @@ static int maildir_full_sync_dirs(struct maildir_sync_context *ctx)
                                                    "opendir()");
        }
 
-       failed = !maildir_full_sync_dir(ctx, ctx->cur_dir, FALSE,
-                                       dirp, readdir(dirp));
+       failed = !maildir_full_sync_dir(ctx, FALSE, dirp, readdir(dirp));
 
        if (closedir(dirp) < 0) {
                return index_file_set_syscall_error(ctx->index, ctx->cur_dir,
@@ -1000,8 +915,7 @@ static int maildir_sync_new_dir_full(struct maildir_sync_context *ctx)
        if (!maildir_full_sync_init(ctx, TRUE))
                return FALSE;
 
-       if (!maildir_full_sync_dir(ctx, ctx->new_dir, TRUE,
-                                  ctx->new_dirp, ctx->new_dent))
+       if (!maildir_full_sync_dir(ctx, TRUE, ctx->new_dirp, ctx->new_dent))
                return FALSE;
        ctx->new_dent = NULL;
 
@@ -1098,8 +1012,8 @@ static int maildir_sync_new_dir(struct maildir_sync_context *ctx,
                                ctx->index->maildir_have_new = TRUE;
 
                        t_push();
-                       if (!maildir_index_append_file(ctx->index, final_dir,
-                                                      d->d_name,
+                       if (!maildir_index_append_file(&ctx->trans_ctx,
+                                                      ctx->index, d->d_name,
                                                       !move_to_cur)) {
                                t_pop();
                                return FALSE;
@@ -1244,13 +1158,16 @@ static int maildir_full_sync_finish_readonly(struct maildir_sync_context *ctx)
        void *orig_key, *orig_value;
        const char *fname;
        unsigned int seq;
+       int new_dir;
 
-       if (index->lock_type != MAIL_LOCK_EXCLUSIVE || !ctx->flag_updates)
+       if (!ctx->flag_updates) {
+               ctx->index->maildir_synced_once = TRUE;
                return TRUE;
+       }
 
        rec = index->lookup(index, 1); seq = 1;
        while (rec != NULL) {
-               fname = maildir_get_location(index, rec);
+               fname = maildir_get_location(index, rec, NULL);
                if (fname == NULL)
                        return FALSE;
 
@@ -1261,15 +1178,21 @@ static int maildir_full_sync_finish_readonly(struct maildir_sync_context *ctx)
 
                if (hash_rec != NULL &&
                    ACTION(hash_rec) == MAILDIR_FILE_ACTION_UPDATE_FLAGS) {
-                       if (!maildir_update_filename(ctx, rec, orig_key))
-                               return FALSE;
-                       if (!maildir_update_flags(ctx, rec, seq, fname))
-                               return FALSE;
+                       new_dir = (hash_rec->action &
+                                  MAILDIR_FILE_FLAG_NEWDIR) != 0;
+                       maildir_index_update_filename(index, rec->uid,
+                                                     orig_key, new_dir);
+
+                       if (index->lock_type == MAIL_LOCK_EXCLUSIVE) {
+                               if (!maildir_update_flags(ctx, rec, seq, fname))
+                                       return FALSE;
+                       }
                }
 
                rec = index->next(index, rec); seq++;
        }
 
+       ctx->index->maildir_synced_once = TRUE;
        return TRUE;
 }
 
@@ -1281,13 +1204,19 @@ static int maildir_index_sync_context_readonly(struct maildir_sync_context *ctx)
 
        i_assert(index->lock_type != MAIL_LOCK_UNLOCK);
 
-       if (stat(ctx->cur_dir, &st) < 0) {
-               index_file_set_syscall_error(index, ctx->cur_dir, "stat()");
-               return FALSE;
-       }
+       if (!index->maildir_synced_once) {
+               /* we haven't synced yet in this session. do it */
+               cur_changed = TRUE;
+       } else {
+               if (stat(ctx->cur_dir, &st) < 0) {
+                       index_file_set_syscall_error(index, ctx->cur_dir,
+                                                    "stat()");
+                       return FALSE;
+               }
 
-       cur_changed = st.st_mtime != index->file_sync_stamp ||
-               index->maildir_cur_dirty != 0;
+               cur_changed = st.st_mtime != index->file_sync_stamp ||
+                       index->maildir_cur_dirty != 0;
+       }
 
        if (!cur_changed) {
                if (!index->maildir_have_new) {
@@ -1325,6 +1254,9 @@ static int maildir_index_sync_context_readonly(struct maildir_sync_context *ctx)
 
 static void maildir_index_sync_deinit(struct maildir_sync_context *ctx)
 {
+       // FIXME: remove new flags from cache if needed
+       if (ctx->trans_ctx != NULL)
+                mail_cache_transaction_end(ctx->trans_ctx);
        if (ctx->uidlist != NULL)
                maildir_uidlist_close(ctx->uidlist);
        if (ctx->files != NULL)
@@ -1347,14 +1279,6 @@ maildir_sync_context_new(struct mail_index *index)
 {
         struct maildir_sync_context *ctx;
 
-       if (index->new_filenames != NULL) {
-               hash_destroy(index->new_filenames);
-               index->new_filenames = NULL;
-       }
-
-       if (index->new_filename_pool != NULL)
-               p_clear(index->new_filename_pool);
-
        ctx = t_new(struct maildir_sync_context, 1);
        ctx->index = index;
        ctx->new_dir = t_strconcat(index->mailbox_path, "/new", NULL);
@@ -1374,7 +1298,7 @@ int maildir_index_sync_readonly(struct mail_index *index,
 
        ret = maildir_index_sync_context_readonly(ctx);
 
-       if (!ret)
+       if (!ret || ctx->files == NULL)
                *found = FALSE;
        else {
                hash_rec = hash_lookup(ctx->files, fname);
index 26398db0fc3364f0b8bd7669f47a9a18cd08c20a..f5d3052146aef08da005c68e09623952111da069 100644 (file)
@@ -155,7 +155,7 @@ static int maildir_uidlist_rewrite_fd(struct mail_index *index,
 
        rec = index->lookup(index, 1);
        while (rec != NULL) {
-               fname = maildir_get_location(index, rec);
+               fname = maildir_get_location(index, rec, NULL);
                if (fname == NULL)
                        return FALSE;
 
index 59847d297cfaaf356d89e90d0c1720f160d474b8..263c161337858d04a0c3135897d75493fb8ae8b6 100644 (file)
@@ -1,17 +1,55 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "hash.h"
 #include "ioloop.h"
 #include "maildir-index.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 
+static int update_filename(struct mail_index *index,
+                          struct mail_index_record *rec)
+{
+       const char *old_fname, *old_path, *new_fname, *new_path;
+       enum mail_index_record_flag flags;
+
+       old_fname = maildir_get_location(index, rec, NULL);
+       if (old_fname == NULL)
+               return -1;
+
+       flags = mail_cache_get_index_flags(index->cache, rec);
+
+       old_path = t_strconcat(index->mailbox_path,
+                              (flags & MAIL_INDEX_FLAG_MAILDIR_NEW) != 0 ?
+                              "/new/" : "/cur/", old_fname, NULL);
+
+       new_fname = maildir_filename_set_flags(old_fname, rec->msg_flags);
+       new_path = t_strconcat(index->mailbox_path, "/cur/", new_fname, NULL);
+
+       if (strcmp(old_path, new_path) == 0 ||
+           rename(old_path, new_path) == 0) {
+               flags &= ~(MAIL_INDEX_FLAG_DIRTY | MAIL_INDEX_FLAG_MAILDIR_NEW);
+               if (!mail_cache_update_index_flags(index->cache, rec, flags))
+                       return -1;
+               return 1;
+       } else {
+               if (errno != ENOENT && errno != EACCES &&
+                   !ENOSPACE(errno)) {
+                       index_set_error(index,
+                                       "rename(%s, %s) failed: %m",
+                                       old_path, new_path);
+                       return -1;
+               }
+               return 0;
+       }
+}
+
 int maildir_try_flush_dirty_flags(struct mail_index *index, int force)
 {
        struct mail_index_record *rec;
-       const char *old_fname, *old_path, *new_fname, *new_path;
-       int flag, dirty = FALSE;
+       int ret, dirty = FALSE;
 
        if (index->next_dirty_flush == 0 ||
            (ioloop_time < index->next_dirty_flush && !force))
@@ -20,44 +58,30 @@ int maildir_try_flush_dirty_flags(struct mail_index *index, int force)
        if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
                return FALSE;
 
+       ret = mail_cache_lock(index->cache, !force);
+       if (ret <= 0)
+               return ret == 0;
+        mail_cache_unlock_later(index->cache);
+
        rec = index->lookup(index, 1);
        while (rec != NULL) {
-               if ((rec->index_flags & INDEX_MAIL_FLAG_DIRTY) != 0) {
-                       old_fname = maildir_get_location(index, rec);
-                       if (old_fname == NULL)
-                               return FALSE;
-
-                       flag = (rec->index_flags &
-                               INDEX_MAIL_FLAG_MAILDIR_NEW) != 0;
-                       old_path = t_strconcat(index->mailbox_path,
-                                              flag ? "/new/" : "/cur/",
-                                              old_fname, NULL);
-
-                       new_fname = maildir_filename_set_flags(old_fname,
-                                                              rec->msg_flags);
-                       new_path = t_strconcat(index->mailbox_path,
-                                              "/cur/", new_fname, NULL);
-
-                       if (strcmp(old_path, new_path) == 0 ||
-                           rename(old_path, new_path) == 0)
-                                rec->index_flags &= ~INDEX_MAIL_FLAG_DIRTY;
-                       else {
+               if ((mail_cache_get_index_flags(index->cache, rec) &
+                    MAIL_INDEX_FLAG_DIRTY) != 0) {
+                       ret = update_filename(index, rec);
+                       if (ret < 0)
+                               break;
+                       if (ret == 0)
                                dirty = TRUE;
-                               if (errno != ENOENT && errno != EACCES &&
-                                   !ENOSPACE(errno)) {
-                                       index_set_error(index,
-                                               "rename(%s, %s) failed: %m",
-                                               old_path, new_path);
-                                       return FALSE;
-                               }
-                       }
                }
 
                rec = index->next(index, rec);
        }
 
+       if (ret < 0)
+               return FALSE;
+
        if (!dirty) {
-               index->header->flags &= ~MAIL_INDEX_FLAG_DIRTY_MESSAGES;
+               index->header->flags &= ~MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
                index->next_dirty_flush = 0;
        } else {
                index->next_dirty_flush =
@@ -88,13 +112,12 @@ static int handle_error(struct mail_index *index,
        return -1;
 }
 
-static int maildir_rename_mail_file(struct mail_index *index,
-                                   struct mail_index_record *rec,
+static int maildir_rename_mail_file(struct mail_index *index, int new_dir,
                                    const char *old_fname, const char *new_path)
 {
        const char *path;
 
-       if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+       if (new_dir) {
                /* probably in new/ dir */
                path = t_strconcat(index->mailbox_path, "/new/",
                                   old_fname, NULL);
@@ -117,14 +140,15 @@ static int maildir_rename_mail(struct mail_index *index,
                               enum mail_flags flags, const char **new_fname_r)
 {
        const char *old_fname, *new_fname, *new_path;
-       int i, ret, found;
+        enum mail_index_record_flag index_flags;
+       int i, ret, found, new_dir;
 
        new_fname = new_path = NULL;
 
        i = 0;
        do {
                /* we need to update the flags in the file name */
-               old_fname = maildir_get_location(index, rec);
+               old_fname = maildir_get_location(index, rec, &new_dir);
                if (old_fname == NULL)
                        return FALSE;
 
@@ -139,21 +163,17 @@ static int maildir_rename_mail(struct mail_index *index,
                if (strcmp(old_fname, new_fname) == 0)
                        ret = 1;
                else {
-                       ret = maildir_rename_mail_file(index, rec, old_fname,
-                                                      new_path);
+                       ret = maildir_rename_mail_file(index, new_dir,
+                                                      old_fname, new_path);
                        if (ret == -1)
                                return FALSE;
 
                        if (ret == 1) {
-                               if (index->maildir_keep_new &&
-                                   (rec->index_flags &
-                                    INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+                               if (index->maildir_keep_new && new_dir) {
                                        /* looks like we have some more space
                                           again, see if we could move mails
                                           from new/ to cur/ again */
                                        index->maildir_keep_new = FALSE;
-                                       rec->index_flags &=
-                                               ~INDEX_MAIL_FLAG_MAILDIR_NEW;
                                }
 
                                /* cur/ was updated, set it dirty-synced */
@@ -173,15 +193,26 @@ static int maildir_rename_mail(struct mail_index *index,
                i++;
        } while (i < 10 && ret == 0);
 
-       if (ret != 1) {
-               /* we couldn't actually rename() the file now.
-                  leave it's flags dirty so they get changed later. */
-               rec->index_flags |= INDEX_MAIL_FLAG_DIRTY;
-               index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES;
-               index->next_dirty_flush =
-                       ioloop_time + MAILDIR_DIRTY_FLUSH_TIMEOUT;
-               *new_fname_r = NULL;
+       if (ret == 1)
+               return TRUE;
+
+       /* we couldn't actually rename() the file now.
+          leave it's flags dirty so they get changed later. */
+       index_flags = mail_cache_get_index_flags(index->cache, rec);
+       if ((index_flags & MAIL_INDEX_FLAG_DIRTY) == 0) {
+               if (mail_cache_lock(index->cache, FALSE) <= 0)
+                       return FALSE;
+               mail_cache_unlock_later(index->cache);
+
+               index_flags |= MAIL_INDEX_FLAG_DIRTY;
+               mail_cache_update_index_flags(index->cache, rec, index_flags);
+
+               index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
        }
+
+       index->next_dirty_flush =
+               ioloop_time + MAILDIR_DIRTY_FLUSH_TIMEOUT;
+       *new_fname_r = NULL;
        return TRUE;
 }
 
@@ -189,7 +220,6 @@ int maildir_index_update_flags(struct mail_index *index,
                               struct mail_index_record *rec, unsigned int seq,
                               enum mail_flags flags, int external_change)
 {
-       struct mail_index_update *update;
        const char *new_fname;
        int failed = FALSE;
 
@@ -200,11 +230,8 @@ int maildir_index_update_flags(struct mail_index *index,
        }
 
        if (new_fname != NULL) {
-               /* update the filename in index */
-               update = index->update_begin(index, rec);
-               index->update_field(update, DATA_FIELD_LOCATION, new_fname, 0);
-               if (!index->update_end(update))
-                       failed = TRUE;
+               maildir_index_update_filename(index, rec->uid,
+                                             new_fname, FALSE);
        }
 
        if (!failed && !mail_index_update_flags(index, rec, seq, flags,
index 79965f8dda843725d3ca4f24f1c38fd8ec7f2066..5daf25da764eb2d0b6c482ed6cfd54ed528603c5 100644 (file)
@@ -12,7 +12,6 @@ libindex_mbox_a_SOURCES = \
        mbox-index.c \
        mbox-lock.c \
        mbox-open.c \
-       mbox-rebuild.c \
        mbox-rewrite.c \
        mbox-sync.c \
        mbox-sync-full.c
index e31626b29a4f881da1a8b6fb38d746dfca32ebdd..21cf82b84b33e878c6e756aca52ca8930af8ea0f 100644 (file)
@@ -7,19 +7,21 @@
 #include "md5.h"
 #include "mbox-index.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 static int mbox_index_append_next(struct mail_index *index,
+                                  struct mail_index_record *rec,
+                                 struct mail_cache_transaction_ctx *trans_ctx,
                                  struct istream *input)
 {
-       struct mail_index_record *rec;
-       struct mail_index_update *update;
         struct mbox_header_context ctx;
-       time_t internal_date;
+       enum mail_index_record_flag index_flags;
+       time_t received_date;
        uoff_t abs_start_offset, eoh_offset;
        const unsigned char *data;
        unsigned char md5_digest[16];
        size_t size, pos;
-       int ret, dirty;
+       int dirty;
 
        /* get the From-line */
        pos = 0;
@@ -40,14 +42,14 @@ static int mbox_index_append_next(struct mail_index *index,
                index_set_error(index, "Error indexing mbox file %s: "
                                "From-line not found where expected",
                                index->mailbox_path);
-               index->set_flags |= MAIL_INDEX_FLAG_FSCK;
+               index->set_flags |= MAIL_INDEX_HDR_FLAG_FSCK;
                return -1;
        }
 
        /* parse the From-line */
-       internal_date = mbox_from_parse_date(data + 5, size - 5);
-       if (internal_date == (time_t)-1)
-               internal_date = ioloop_time;
+       received_date = mbox_from_parse_date(data + 5, size - 5);
+       if (received_date == (time_t)-1)
+               received_date = ioloop_time;
 
        i_stream_skip(input, pos+1);
        abs_start_offset = input->start_offset + input->v_offset;
@@ -57,19 +59,16 @@ static int mbox_index_append_next(struct mail_index *index,
        mbox_skip_header(input);
        eoh_offset = input->v_offset;
 
-       /* add message to index */
-       rec = index->append_begin(index);
-       if (rec == NULL)
-               return -1;
-
-       update = index->update_begin(index, rec);
+       index_flags = 0;
 
-       index->update_field_raw(update, DATA_HDR_INTERNAL_DATE,
-                               &internal_date, sizeof(internal_date));
+       if (!mail_cache_add(trans_ctx, rec, MAIL_CACHE_RECEIVED_DATE,
+                           &received_date, sizeof(received_date)))
+               return -1;
 
-       /* location = offset to beginning of headers in message */
-       index->update_field_raw(update, DATA_FIELD_LOCATION,
-                               &abs_start_offset, sizeof(uoff_t));
+       /* location offset = beginning of headers in message */
+       if (!mail_cache_add(trans_ctx, rec, MAIL_CACHE_LOCATION_OFFSET,
+                           &abs_start_offset, sizeof(abs_start_offset)))
+               return -1;
 
        /* parse the header and cache wanted fields. get the message flags
           from Status and X-Status fields. temporarily limit the stream length
@@ -84,12 +83,12 @@ static int mbox_index_append_next(struct mail_index *index,
        i_stream_seek(input, abs_start_offset - input->start_offset);
 
        i_stream_set_read_limit(input, eoh_offset);
-       mail_index_update_headers(update, input, 0, mbox_header_cb, &ctx);
+       //FIXME:mail_index_update_headers(update, input, 0, mbox_header_cb, &ctx);
 
        i_stream_seek(input, input->v_limit);
        i_stream_set_read_limit(input, 0);
 
-       ret = 1;
+       dirty = ctx.content_length_broken;
        if (index->header->messages_count == 0 &&
            ctx.uid_validity != index->header->uid_validity) {
                /* UID validity is different */
@@ -103,66 +102,53 @@ static int mbox_index_append_next(struct mail_index *index,
                        /* we have to write it to mbox */
                        if (index->mbox_lock_type != MAIL_LOCK_EXCLUSIVE) {
                                /* try again */
-                               ret = 0;
-                       } else {
-                               index->header->flags |=
-                                       MAIL_INDEX_FLAG_DIRTY_MESSAGES;
-                               rec->index_flags |= INDEX_MAIL_FLAG_DIRTY;
+                               return 0;
                        }
+
+                       dirty = TRUE;
                }
        }
 
        if (ctx.uid >= index->header->next_uid) {
                /* X-UID header looks ok */
-               if (ret != 0)
-                       index->header->next_uid = ctx.uid;
-               dirty = ctx.content_length_broken;
+               index->header->next_uid = ctx.uid;
        } else if (!index->mailbox_readonly) {
                /* Write X-UID for it */
                dirty = TRUE;
        } else {
                /* save MD5 */
                md5_final(&ctx.md5, md5_digest);
-               index->update_field_raw(update, DATA_FIELD_MD5,
-                                       md5_digest, sizeof(md5_digest));
-               dirty = FALSE;
+
+               if (!mail_cache_add(trans_ctx, rec, MAIL_CACHE_MD5,
+                                   md5_digest, sizeof(md5_digest)))
+                       return -1;
        }
 
        if (dirty && !index->mailbox_readonly) {
                if (index->mbox_lock_type != MAIL_LOCK_EXCLUSIVE) {
                        /* try again */
-                       ret = 0;
-               } else {
-                       index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES;
-                       rec->index_flags |= INDEX_MAIL_FLAG_DIRTY;
+                       return 0;
                }
+
+               index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
+               index_flags |= MAIL_INDEX_FLAG_DIRTY;
        }
 
-       if (ret <= 0) {
-               index->update_abort(update);
-               index->append_abort(index, rec);
-       } else {
-               if (!index->update_end(update)) {
-                       index->append_abort(index, rec);
-                       ret = -1;
-               } else {
-                       /* save message flags */
-                       rec->msg_flags = ctx.flags;
-                       mail_index_mark_flag_changes(index, rec, 0,
-                                                    rec->msg_flags);
-                       ret = 1;
+       /* save message flags */
+       rec->msg_flags = ctx.flags;
+       mail_index_mark_flag_changes(index, rec, 0, rec->msg_flags);
 
-                       if (!index->append_end(index, rec))
-                               ret = -1;
-               }
-       }
+       if (!mail_cache_add(trans_ctx, rec, MAIL_CACHE_INDEX_FLAGS,
+                           &index_flags, sizeof(index_flags)))
+               return -1;
 
-       mbox_header_free_context(&ctx);
-       return ret;
+       return 1;
 }
 
-int mbox_index_append(struct mail_index *index, struct istream *input)
+int mbox_index_append_stream(struct mail_index *index, struct istream *input)
 {
+       struct mail_cache_transaction_ctx *trans_ctx;
+       struct mail_index_record *rec;
        uoff_t offset;
        int ret;
 
@@ -174,6 +160,9 @@ int mbox_index_append(struct mail_index *index, struct istream *input)
        if (!index->set_lock(index, MAIL_LOCK_EXCLUSIVE))
                return FALSE;
 
+       if (mail_cache_transaction_begin(index->cache, TRUE, &trans_ctx) <= 0)
+               return FALSE;
+
        do {
                offset = input->v_offset;
                if (input->start_offset + input->v_offset != 0) {
@@ -185,8 +174,9 @@ int mbox_index_append(struct mail_index *index, struct istream *input)
                                                "LF not found where expected",
                                                index->mailbox_path);
 
-                               index->set_flags |= MAIL_INDEX_FLAG_FSCK;
-                               return FALSE;
+                               index->set_flags |= MAIL_INDEX_HDR_FLAG_FSCK;
+                               ret = -1;
+                               break;
                        }
                }
 
@@ -195,8 +185,15 @@ int mbox_index_append(struct mail_index *index, struct istream *input)
                        break;
                }
 
+               /* add message to index */
+               rec = index->append(index);
+               if (rec == NULL) {
+                       ret = -1;
+                       break;
+               }
+
                t_push();
-               ret = mbox_index_append_next(index, input);
+               ret = mbox_index_append_next(index, rec, trans_ctx, input);
                t_pop();
 
                if (ret == 0) {
@@ -206,10 +203,18 @@ int mbox_index_append(struct mail_index *index, struct istream *input)
                }
        } while (ret > 0);
 
-       if (index->mbox_lock_type == MAIL_LOCK_EXCLUSIVE) {
+       if (ret >= 0 && index->mbox_lock_type == MAIL_LOCK_EXCLUSIVE) {
                /* Write missing X-IMAPbase and new/changed X-UID headers */
-               return mbox_index_rewrite(index);
+               if (!mbox_index_rewrite(index))
+                       ret = -1;
+       }
+
+       if (ret >= 0) {
+               if (!mail_cache_transaction_commit(trans_ctx))
+                       ret = -1;
        }
+       if (!mail_cache_transaction_end(trans_ctx))
+               ret = -1;
 
        return ret >= 0;
 }
index 57e9d6f7e017e67d64142f7f4280f108bc06a919..cc81c08c3121bb8b9364025fcffd6c0a66b26791 100644 (file)
@@ -6,8 +6,8 @@
 #include "mbox-index.h"
 #include "mbox-lock.h"
 #include "mail-index-util.h"
-#include "mail-index-data.h"
 #include "mail-custom-flags.h"
+#include "mail-cache.h"
 
 #include <stdlib.h>
 #include <unistd.h>
@@ -138,10 +138,6 @@ void mbox_header_init_context(struct mbox_header_context *ctx,
        ctx->content_length = (uoff_t)-1;
 }
 
-void mbox_header_free_context(struct mbox_header_context *ctx __attr_unused__)
-{
-}
-
 static enum mail_flags
 mbox_get_status_flags(const unsigned char *value, size_t len)
 {
@@ -694,52 +690,36 @@ int mbox_mail_get_location(struct mail_index *index,
                           struct mail_index_record *rec,
                           uoff_t *offset, uoff_t *hdr_size, uoff_t *body_size)
 {
-       struct mail_index_data_record_header *data_hdr;
-       const uoff_t *location;
-       size_t size;
-
        if (offset != NULL) {
-               location = index->lookup_field_raw(index, rec,
-                                                  DATA_FIELD_LOCATION, &size);
-               if (location == NULL) {
-                       index_data_set_corrupted(index->data,
+               if (!mail_cache_copy_fixed_field(index->cache, rec,
+                                                MAIL_CACHE_LOCATION_OFFSET,
+                                                offset, sizeof(*offset))) {
+                       mail_cache_set_corrupted(index->cache,
                                "Missing location field for record %u",
                                rec->uid);
                        return FALSE;
-               } else if (size != sizeof(uoff_t) || *location > OFF_T_MAX) {
-                       index_data_set_corrupted(index->data,
-                               "Invalid location field for record %u",
-                               rec->uid);
-                       return FALSE;
                }
-
-               *offset = *location;
        }
 
-       if (hdr_size != NULL || body_size != NULL) {
-               data_hdr = mail_index_data_lookup_header(index->data, rec);
-               if (data_hdr == NULL) {
-                       index_set_corrupted(index,
-                               "Missing data header for record %u", rec->uid);
-                       return FALSE;
-               }
-
-               if ((rec->data_fields & DATA_HDR_HEADER_SIZE) == 0) {
-                       index_set_corrupted(index,
+       if (hdr_size != NULL) {
+               if (!mail_cache_copy_fixed_field(index->cache, rec,
+                                                MAIL_CACHE_HEADER_SIZE,
+                                                hdr_size, sizeof(*hdr_size))) {
+                       mail_cache_set_corrupted(index->cache,
                                "Missing header size for record %u", rec->uid);
                        return FALSE;
                }
+       }
 
-               if ((rec->data_fields & DATA_HDR_BODY_SIZE) == 0) {
-                       index_set_corrupted(index,
+       if (body_size != NULL) {
+               if (!mail_cache_copy_fixed_field(index->cache, rec,
+                                                MAIL_CACHE_BODY_SIZE,
+                                                body_size,
+                                                sizeof(*body_size))) {
+                       mail_cache_set_corrupted(index->cache,
                                "Missing body size for record %u", rec->uid);
                        return FALSE;
                }
-
-               if (hdr_size != NULL)
-                       *hdr_size = data_hdr->header_size;
-               if (body_size != NULL)
-                       *body_size = data_hdr->body_size;
        }
 
        return TRUE;
@@ -805,8 +785,8 @@ static int mbox_index_expunge(struct mail_index *index,
        if (first_seq == 1) {
                /* Our message containing X-IMAPbase was deleted.
                   Get it back there. */
-               index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES |
-                       MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS;
+               index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES |
+                       MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS;
        }
        return TRUE;
 }
@@ -816,26 +796,51 @@ static int mbox_index_update_flags(struct mail_index *index,
                                   unsigned int seq, enum mail_flags flags,
                                   int external_change)
 {
+        enum mail_index_record_flag index_flags;
+
        if (!mail_index_update_flags(index, rec, seq, flags, external_change))
                return FALSE;
 
        if (!external_change) {
-               rec->index_flags |= INDEX_MAIL_FLAG_DIRTY;
-               index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES;
+               index_flags = mail_cache_get_index_flags(index->cache, rec);
+               if ((index_flags & MAIL_INDEX_FLAG_DIRTY) == 0) {
+                       if (mail_cache_lock(index->cache, FALSE) <= 0)
+                               return FALSE;
+                       mail_cache_unlock_later(index->cache);
+
+                       index_flags |= MAIL_INDEX_FLAG_DIRTY;
+                       mail_cache_update_index_flags(index->cache, rec,
+                                                     index_flags);
+
+                       index->header->flags |=
+                               MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES;
+               }
        }
        return TRUE;
 }
 
-static int mbox_index_append_end(struct mail_index *index,
-                                struct mail_index_record *rec)
+static struct mail_index_record *mbox_index_append(struct mail_index *index)
 {
-       if (!mail_index_append_end(index, rec))
-               return FALSE;
-
        /* update last_uid in X-IMAPbase */
-       index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES |
-               MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS;
-       return TRUE;
+       index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES |
+               MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS;
+
+       return mail_index_append(index);
+}
+
+static time_t mbox_get_received_date(struct mail_index *index,
+                                    struct mail_index_record *rec)
+{
+       time_t date;
+
+       if (mail_cache_copy_fixed_field(index->cache, rec,
+                                       MAIL_CACHE_RECEIVED_DATE,
+                                       &date, sizeof(date)))
+               return date;
+
+       mail_cache_set_corrupted(index->cache,
+               "Missing internal date for record %u", rec->uid);
+       return (time_t)-1;
 }
 
 struct mail_index mbox_index = {
@@ -844,28 +849,18 @@ struct mail_index mbox_index = {
        mbox_index_set_lock,
        mbox_index_try_lock,
         mail_index_set_lock_notify_callback,
-       mbox_index_rebuild,
+       mail_index_rebuild,
        mail_index_fsck,
        mbox_index_sync,
        mail_index_get_header,
        mail_index_lookup,
        mail_index_next,
         mail_index_lookup_uid_range,
-       mail_index_lookup_field,
-       mail_index_lookup_field_raw,
-       mail_index_cache_fields_later,
        mbox_open_mail,
-       mail_get_internal_date,
+       mbox_get_received_date,
        mbox_index_expunge,
        mbox_index_update_flags,
-       mail_index_append_begin,
-       mbox_index_append_end,
-       mail_index_append_abort,
-       mail_index_update_begin,
-       mail_index_update_end,
-       mail_index_update_abort,
-       mail_index_update_field,
-       mail_index_update_field_raw,
+       mbox_index_append,
        mail_index_get_last_error,
        mail_index_get_last_error_text,
 
index 7ebde0e74e188d019384ee3f43ed42527ef52b2f..52c5d75b5636642d0f11f35283e87a3bddb270f8 100644 (file)
@@ -35,7 +35,6 @@ void mbox_file_close_fd(struct mail_index *index);
 void mbox_header_init_context(struct mbox_header_context *ctx,
                              struct mail_index *index,
                              struct istream *input);
-void mbox_header_free_context(struct mbox_header_context *ctx);
 void mbox_header_cb(struct message_part *part,
                    struct message_header_line *hdr, void *context);
 void mbox_keywords_parse(const unsigned char *value, size_t len,
@@ -50,20 +49,19 @@ void mbox_skip_message(struct istream *input);
 int mbox_verify_end_of_body(struct istream *input, uoff_t end_offset);
 int mbox_mail_get_location(struct mail_index *index,
                           struct mail_index_record *rec,
-                          uoff_t *offset, uoff_t *hdr_size, uoff_t *body_size);
+                          uoff_t *offset, uoff_t *hdr_size, uoff_t *full_size);
 
 struct mail_index *
 mbox_index_alloc(const char *mbox_path, const char *index_dir,
                 const char *control_dir);
-int mbox_index_rebuild(struct mail_index *index);
 int mbox_index_sync(struct mail_index *index, int minimal_sync,
                    enum mail_lock_type lock_type, int *changes);
 int mbox_sync_full(struct mail_index *index);
 struct istream *mbox_open_mail(struct mail_index *index,
                               struct mail_index_record *rec,
-                              time_t *internal_date, int *deleted);
+                              time_t *received_date, int *deleted);
 
-int mbox_index_append(struct mail_index *index, struct istream *input);
+int mbox_index_append_stream(struct mail_index *index, struct istream *input);
 
 time_t mbox_from_parse_date(const unsigned char *msg, size_t size);
 const char *mbox_from_create(const char *sender, time_t time);
index 713b5fcab90b6363fd594f9ad7a7f68001f6c03c..0a23d54f2df7b3f9d5fe1942bc3f6c671c1cb8db 100644 (file)
@@ -3,7 +3,6 @@
 #include "lib.h"
 #include "istream.h"
 #include "mbox-index.h"
-#include "mail-index-data.h"
 #include "mail-index-util.h"
 
 #include <stdlib.h>
@@ -12,7 +11,7 @@
 
 struct istream *mbox_open_mail(struct mail_index *index,
                               struct mail_index_record *rec,
-                              time_t *internal_date, int *deleted)
+                              time_t *received_date, int *deleted)
 {
        struct istream *input;
        uoff_t offset, hdr_size, body_size;
@@ -32,8 +31,8 @@ struct istream *mbox_open_mail(struct mail_index *index,
        if (input == NULL)
                return NULL;
 
-       if (internal_date != NULL)
-               *internal_date = mail_get_internal_date(index, rec);
+       if (received_date != NULL)
+               *received_date = index->get_received_date(index, rec);
 
        i_assert(index->mbox_sync_counter == index->mbox_lock_counter);
 
diff --git a/src/lib-index/mbox/mbox-rebuild.c b/src/lib-index/mbox/mbox-rebuild.c
deleted file mode 100644 (file)
index 0e857a6..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-/* Copyright (C) 2002 Timo Sirainen */
-
-#include "lib.h"
-#include "mbox-index.h"
-#include "mail-index-data.h"
-#include "mail-index-util.h"
-
-#include <unistd.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-
-int mbox_index_rebuild(struct mail_index *index)
-{
-       struct stat st;
-
-       i_assert(index->lock_type != MAIL_LOCK_SHARED);
-
-       if (!mail_index_set_lock(index, MAIL_LOCK_EXCLUSIVE))
-               return FALSE;
-
-       /* reset the header */
-       mail_index_init_header(index, index->header);
-       index->mmap_used_length = index->header->used_file_size;
-
-       /* require these fields */
-       index->header->cache_fields |= DATA_FIELD_LOCATION |
-               DATA_FIELD_MESSAGEPART | DATA_FIELD_MD5;
-
-       /* update indexid, which also means that our state has completely
-          changed */
-       index->indexid = index->header->indexid;
-       if (index->opened)
-               index->inconsistent = TRUE;
-
-       if (!index->anon_mmap) {
-               if (msync(index->mmap_base,
-                         sizeof(struct mail_index_header), MS_SYNC) < 0)
-                       return index_set_syscall_error(index, "msync()");
-       }
-
-       /* reset data file */
-       if (!mail_index_data_reset(index->data))
-               return FALSE;
-
-       if (!mbox_sync_full(index))
-               return FALSE;
-
-       /* update sync stamp */
-       if (stat(index->mailbox_path, &st) < 0)
-               return mbox_set_syscall_error(index, "fstat()");
-
-       index->file_sync_stamp = st.st_mtime;
-
-       /* rebuild is complete - remove the flag */
-       index->header->flags &= ~(MAIL_INDEX_FLAG_REBUILD|MAIL_INDEX_FLAG_FSCK);
-       index->set_flags &= ~MAIL_INDEX_FLAG_REBUILD;
-       return TRUE;
-}
index d8c253966e49cfab54871914d412afc562fa5f53..6e2c8a2685b636d1e88b5b437e4ecb5673f6f447 100644 (file)
@@ -11,6 +11,7 @@
 #include "mbox-lock.h"
 #include "mail-index-util.h"
 #include "mail-custom-flags.h"
+#include "mail-cache.h"
 
 #include <stdio.h>
 #include <stdlib.h>
@@ -38,18 +39,25 @@ struct mbox_rewrite_context {
 };
 
 /* Remove dirty flag from all messages */
-static void reset_dirty_flags(struct mail_index *index)
+static int reset_dirty_flags(struct mail_index *index)
 {
        struct mail_index_record *rec;
+       enum mail_index_record_flag index_flags;
 
        rec = index->lookup(index, 1);
        while (rec != NULL) {
-               rec->index_flags &= ~INDEX_MAIL_FLAG_DIRTY;
+               index_flags = mail_cache_get_index_flags(index->cache, rec);
+               if ((index_flags & MAIL_INDEX_FLAG_DIRTY) != 0) {
+                       if (!mail_cache_update_index_flags(index->cache, rec, index_flags))
+                               return FALSE;
+               }
+
                rec = index->next(index, rec);
        }
 
-       index->header->flags &= ~(MAIL_INDEX_FLAG_DIRTY_MESSAGES |
-                                 MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS);
+       index->header->flags &= ~(MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES |
+                                 MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS);
+       return TRUE;
 }
 
 static int mbox_write(struct mail_index *index, struct istream *input,
@@ -553,7 +561,8 @@ static int dirty_flush(struct mail_index *index, uoff_t dirty_offset,
 }
 
 #define INDEX_DIRTY_FLAGS \
-        (MAIL_INDEX_FLAG_DIRTY_MESSAGES | MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS)
+       (MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES | \
+        MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS)
 
 int mbox_index_rewrite(struct mail_index *index)
 {
@@ -567,7 +576,7 @@ int mbox_index_rewrite(struct mail_index *index)
        uoff_t offset, hdr_size, body_size, dirty_offset, wanted_offset;
        const char *path;
        unsigned int seq;
-       int tmp_fd, failed, dirty_found, rewrite, no_locking;
+       int tmp_fd, failed, dirty, dirty_found, rewrite, no_locking;
 
        i_assert(!index->mailbox_readonly);
        i_assert(index->lock_type == MAIL_LOCK_UNLOCK ||
@@ -633,7 +642,7 @@ int mbox_index_rewrite(struct mail_index *index)
                return !failed;
        }
 
-       if (index->header->flags & MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS) {
+       if (index->header->flags & MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS) {
                /* need to update X-IMAPbase in first message */
                dirty_found = TRUE;
        } else {
@@ -649,7 +658,14 @@ int mbox_index_rewrite(struct mail_index *index)
        failed = FALSE; seq = 1;
        rec = index->lookup(index, 1);
        while (rec != NULL) {
-               if (dirty_found || (rec->index_flags & INDEX_MAIL_FLAG_DIRTY)) {
+               if (dirty_found)
+                       dirty = FALSE;
+               else {
+                       dirty = (mail_cache_get_index_flags(index->cache, rec) &
+                                MAIL_INDEX_FLAG_DIRTY) != 0;
+               }
+
+               if (dirty_found || dirty) {
                        /* get offset to beginning of mail headers */
                        if (!mbox_mail_get_location(index, rec, &offset,
                                                    &hdr_size, &body_size)) {
@@ -659,30 +675,27 @@ int mbox_index_rewrite(struct mail_index *index)
                        }
 
                        if (offset < input->v_offset) {
-                               index_set_corrupted(index,
-                                                   "Invalid message offset");
+                               mail_cache_set_corrupted(index->cache,
+                                       "Invalid message offset");
                                failed = TRUE;
                                break;
                        }
 
                        if (offset + hdr_size + body_size > input->v_size) {
-                               index_set_corrupted(index,
-                                                   "Invalid message size");
+                               mail_cache_set_corrupted(index->cache,
+                                       "Invalid message size");
                                failed = TRUE;
                                break;
                        }
-               }
 
-               if (!dirty_found &&
-                   (rec->index_flags & INDEX_MAIL_FLAG_DIRTY)) {
-                       /* first dirty message */
-                       dirty_found = TRUE;
-                       dirty_offset = offset;
+                       if (!dirty_found) {
+                               /* first dirty message */
+                               dirty_found = TRUE;
+                               dirty_offset = offset;
 
-                       i_stream_seek(input, dirty_offset);
-               }
+                               i_stream_seek(input, dirty_offset);
+                       }
 
-               if (dirty_found) {
                        /* write the From-line */
                        if (!mbox_write(index, input, output, offset)) {
                                failed = TRUE;
@@ -748,8 +761,10 @@ int mbox_index_rewrite(struct mail_index *index)
                }
        }
 
-       if (!failed)
-               reset_dirty_flags(index);
+       if (!failed) {
+               if (!reset_dirty_flags(index))
+                       failed = TRUE;
+       }
 
        i_stream_unref(input);
        o_stream_unref(output);
index 22314bff3375306d936d378dfa112329e4e69cf9..fce346ef3ef71017147b93c688808ff6214e239e 100644 (file)
@@ -8,11 +8,14 @@
 #include "mbox-index.h"
 #include "mbox-lock.h"
 #include "mail-index-util.h"
+#include "mail-cache.h"
 
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
 
+#if 0
+
 static void skip_line(struct istream *input)
 {
        const unsigned char *msg;
@@ -34,21 +37,25 @@ static int verify_header(struct mail_index *index,
                         struct mail_index_record *rec,
                         unsigned int uid, unsigned char current_digest[16])
 {
-       const unsigned char *old_digest;
+       const void *old_digest;
        size_t size;
 
-       /* MD5 sums must match */
-       old_digest = index->lookup_field_raw(index, rec, DATA_FIELD_MD5, &size);
-       if (old_digest == NULL)
+       if (uid != 0) {
+               /* X-UID header - no need to check more */
                return uid == rec->uid;
+       }
+
+       /* check if MD5 sums match */
+       if (!mail_cache_lookup_field(index->cache, rec, MAIL_CACHE_MD5,
+                                    &old_digest, &size))
+               return FALSE;
 
-       return size >= 16 && memcmp(old_digest, current_digest, 16) == 0 &&
-               (uid == 0 || uid == rec->uid);
+       return memcmp(old_digest, current_digest, 16) == 0;
 }
 
 static int mail_update_header_size(struct mail_index *index,
                                   struct mail_index_record *rec,
-                                  struct mail_index_update *update,
+                                  struct mail_cache_transaction_ctx *ctx,
                                   struct message_size *hdr_size)
 {
        const void *part_data;
@@ -105,14 +112,14 @@ static int mbox_check_uidvalidity(struct mail_index *index,
        if (uid_validity == index->header->uid_validity)
                return TRUE;
 
-       index->header->flags |= MAIL_INDEX_FLAG_DIRTY_MESSAGES |
-               MAIL_INDEX_FLAG_DIRTY_CUSTOMFLAGS;
+       index->header->flags |= MAIL_INDEX_HDR_FLAG_DIRTY_MESSAGES |
+               MAIL_INDEX_HDR_FLAG_DIRTY_CUSTOMFLAGS;
 
        if (uid_validity == 0) {
                /* X-IMAPbase header isn't written yet */
        } else {
                /* UID validity has changed - rebuild whole index */
-               index->set_flags |= MAIL_INDEX_FLAG_REBUILD;
+               index->set_flags |= MAIL_INDEX_HDR_FLAG_REBUILD;
                return FALSE;
        }
 
@@ -387,3 +394,9 @@ int mbox_sync_full(struct mail_index *index)
 
        return !failed;
 }
+#endif
+
+int mbox_sync_full(struct mail_index *index)
+{
+       // FIXME
+}
index 57a7fea3970ee0fe1e3414ec12e4babd29b8a4ad..f83634c644c9aa0932ac3ebdcbf3ce1b9177a78b 100644 (file)
@@ -141,7 +141,8 @@ int mbox_index_sync(struct mail_index *index, int minimal_sync __attr_unused__,
                        if (!mbox_lock_and_sync_full(index, data_lock_type))
                                return FALSE;
 
-                       if ((index->set_flags & MAIL_INDEX_FLAG_REBUILD) != 0) {
+                       if ((index->set_flags &
+                            MAIL_INDEX_HDR_FLAG_REBUILD) != 0) {
                                /* uidvalidity probably changed, rebuild */
                                if (!index->rebuild(index))
                                        return FALSE;
index 37e227314232bf505df877d04bed2b8535fe5b9e..7b46c86be79f2de6c26fca82d112a89b74a8e8e0 100644 (file)
@@ -34,7 +34,7 @@ static int index_expunge_seek_first(struct index_mailbox *ibox,
                                hdr->first_deleted_uid_lowwater);
 
                        /* fsck should be enough to fix it */
-                       ibox->index->set_flags |= MAIL_INDEX_FLAG_FSCK;
+                       ibox->index->set_flags |= MAIL_INDEX_HDR_FLAG_FSCK;
                        return FALSE;
                }
        } else {
@@ -144,7 +144,7 @@ struct mail *index_storage_expunge_fetch_next(struct mail_expunge_context *ctx)
        ctx->mail.mail.seq = ctx->seq;
        ctx->mail.mail.uid = ctx->rec->uid;
 
-       if (!index_mail_next(&ctx->mail, ctx->rec, ctx->seq)) {
+       if (index_mail_next(&ctx->mail, ctx->rec, ctx->seq, FALSE) < 0) {
                ctx->failed = TRUE;
                return NULL;
        }
index 34c71d169c71046ff0a1c63dc6a90d13fd073e00..89c1a3cbcdfc0feae45ed2a4c9c7ee036d60db64 100644 (file)
@@ -23,6 +23,7 @@ struct mail_fetch_context {
 struct mail_fetch_context *
 index_storage_fetch_init(struct mailbox *box,
                         enum mail_fetch_field wanted_fields,
+                        const char *const *wanted_headers,
                         const char *messageset, int uidset)
 {
        struct index_mailbox *ibox = (struct index_mailbox *) box;
@@ -41,7 +42,7 @@ index_storage_fetch_init(struct mailbox *box,
        ctx->ibox = ibox;
        ctx->index = ibox->index;
 
-       index_mail_init(ibox, &ctx->mail, wanted_fields, NULL);
+       index_mail_init(ibox, &ctx->mail, wanted_fields, wanted_headers);
        ctx->msgset_ctx = index_messageset_init(ibox, messageset, uidset, TRUE);
        return ctx;
 }
@@ -55,12 +56,14 @@ int index_storage_fetch_deinit(struct mail_fetch_context *ctx, int *all_found)
        if (all_found != NULL)
                *all_found = ret > 0;
 
+       if (ctx->ibox->fetch_mail.pool != NULL)
+               index_mail_deinit(&ctx->ibox->fetch_mail);
+       if (ctx->mail.pool != NULL)
+               index_mail_deinit(&ctx->mail);
+
        if (!index_storage_lock(ctx->ibox, ctx->old_lock))
                ret = -1;
 
-       if (ctx->ibox->fetch_mail.pool != NULL)
-               index_mail_deinit(&ctx->ibox->fetch_mail);
-       index_mail_deinit(&ctx->mail);
        i_free(ctx);
        return ret >= 0;
 }
@@ -72,17 +75,25 @@ struct mail *index_storage_fetch_next(struct mail_fetch_context *ctx)
 
        do {
                msgset_mail = index_messageset_next(ctx->msgset_ctx);
-               if (msgset_mail == NULL)
-                       return NULL;
+               if (msgset_mail == NULL) {
+                       ret = -1;
+                       break;
+               }
 
                ctx->mail.mail.seq = msgset_mail->client_seq;
                ctx->mail.mail.uid = msgset_mail->rec->uid;
 
                ret = index_mail_next(&ctx->mail, msgset_mail->rec,
-                                     msgset_mail->idx_seq);
+                                     msgset_mail->idx_seq, FALSE);
        } while (ret == 0);
 
-       return ret < 0 ? NULL : &ctx->mail.mail;
+       if (ret < 0) {
+               /* error or last record */
+               index_mail_deinit(&ctx->mail);
+               return NULL;
+       }
+
+       return &ctx->mail.mail;
 }
 
 static struct mail *
@@ -93,7 +104,7 @@ fetch_record(struct index_mailbox *ibox, struct mail_index_record *rec,
                index_mail_deinit(&ibox->fetch_mail);
 
        index_mail_init(ibox, &ibox->fetch_mail, wanted_fields, NULL);
-       if (index_mail_next(&ibox->fetch_mail, rec, idx_seq) <= 0)
+       if (index_mail_next(&ibox->fetch_mail, rec, idx_seq, FALSE) <= 0)
                return NULL;
 
        return &ibox->fetch_mail.mail;
index e4b2452696dbdb0a1a9dc4332a71e4e767a43611..b7454d1a7905a71eee7fb42e6bddf3ff58336e4d 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "istream.h"
 #include "str.h"
 #include "message-address.h"
@@ -11,6 +12,7 @@
 #include "mail-index.h"
 #include "mail-index-util.h"
 #include "mail-custom-flags.h"
+#include "mail-cache.h"
 #include "index-storage.h"
 #include "index-expunge.h"
 #include "index-mail.h"
@@ -24,19 +26,25 @@ static struct message_part *get_cached_parts(struct index_mail *mail)
        const char *error;
        size_t part_size;
 
-       part_data = mail->ibox->index->
-               lookup_field_raw(mail->ibox->index, mail->data.rec,
-                                DATA_FIELD_MESSAGEPART, &part_size);
-       if (part_data == NULL) {
-               mail->ibox->index->cache_fields_later(mail->ibox->index,
-                                                     DATA_FIELD_MESSAGEPART);
+       if ((mail->data.cached_fields & MAIL_CACHE_MESSAGEPART) == 0) {
+               mail_cache_mark_missing(mail->ibox->index->cache,
+                                       MAIL_CACHE_MESSAGEPART);
+               return NULL;
+       }
+
+       // FIXME: for non-multipart messages we could build it
+
+       if (!mail_cache_lookup_field(mail->ibox->index->cache, mail->data.rec,
+                                    MAIL_CACHE_MESSAGEPART,
+                                    &part_data, &part_size)) {
+               /* unexpected - must be an error */
                return NULL;
        }
 
        part = message_part_deserialize(mail->pool, part_data, part_size,
                                        &error);
        if (part == NULL) {
-               index_set_corrupted(mail->ibox->index,
+               mail_cache_set_corrupted(mail->ibox->index->cache,
                        "Corrupted cached message_part data (%s)", error);
                return NULL;
        }
@@ -53,103 +61,188 @@ static struct message_part *get_cached_parts(struct index_mail *mail)
        return part;
 }
 
-static char *get_cached_field(struct index_mail *mail,
-                             enum mail_data_field field)
+static char *get_cached_string(struct index_mail *mail,
+                              enum mail_cache_field field)
 {
        const char *ret;
 
-       ret = mail->ibox->index->lookup_field(mail->ibox->index,
-                                             mail->data.rec, field);
-       if (ret == NULL)
-               mail->ibox->index->cache_fields_later(mail->ibox->index, field);
+       if ((mail->data.cached_fields & field) == 0) {
+               mail_cache_mark_missing(mail->ibox->index->cache, field);
+               return NULL;
+       }
+
+       ret = mail_cache_lookup_string_field(mail->ibox->index->cache,
+                                            mail->data.rec, field);
        return p_strdup(mail->pool, ret);
 }
 
 static uoff_t get_cached_uoff_t(struct index_mail *mail,
-                               enum mail_data_field field,
-                               const char *field_name)
+                               enum mail_cache_field field)
 {
-       const uoff_t *uoff_p;
-       size_t size;
-
-       uoff_p = mail->ibox->index->
-               lookup_field_raw(mail->ibox->index, mail->data.rec,
-                                field, &size);
+       uoff_t uoff;
 
-       if (uoff_p == NULL)
-               mail->ibox->index->cache_fields_later(mail->ibox->index, field);
-       else if (size != sizeof(*uoff_p)) {
-               index_set_corrupted(mail->ibox->index,
-                                   "Corrupted cached %s", field_name);
-               uoff_p = NULL;
+       if (!mail_cache_copy_fixed_field(mail->ibox->index->cache,
+                                        mail->data.rec, field,
+                                        &uoff, sizeof(uoff))) {
+               mail_cache_mark_missing(mail->ibox->index->cache, field);
+               uoff = (uoff_t)-1;
        }
 
-       return uoff_p == NULL ? (uoff_t)-1 : *uoff_p;
+       return uoff;
 }
 
 static uoff_t get_cached_virtual_size(struct index_mail *mail)
 {
-       return get_cached_uoff_t(mail, DATA_HDR_VIRTUAL_SIZE, "virtual size");
+       return get_cached_uoff_t(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE);
 }
 
 static time_t get_cached_received_date(struct index_mail *mail)
 {
-       const time_t *time_p;
-       size_t size;
+       time_t t;
+
+       if (!mail_cache_copy_fixed_field(mail->ibox->index->cache,
+                                        mail->data.rec,
+                                        MAIL_CACHE_RECEIVED_DATE,
+                                        &t, sizeof(t))) {
+               mail_cache_mark_missing(mail->ibox->index->cache,
+                                       MAIL_CACHE_RECEIVED_DATE);
+               t = (time_t)-1;
+       }
 
-       time_p = mail->ibox->index->
-               lookup_field_raw(mail->ibox->index, mail->data.rec,
-                                DATA_HDR_INTERNAL_DATE, &size);
+       return t;
+}
 
-       if (time_p == NULL) {
-               mail->ibox->index->cache_fields_later(mail->ibox->index,
-                                                     DATA_HDR_INTERNAL_DATE);
-       } else if (size != sizeof(*time_p)) {
-               index_set_corrupted(mail->ibox->index,
-                                   "Corrupted cached received time");
-               time_p = NULL;
+static void get_cached_sent_date(struct index_mail *mail,
+                                struct mail_sent_date *sent_date)
+{
+       if (!mail_cache_copy_fixed_field(mail->ibox->index->cache,
+                                        mail->data.rec, MAIL_CACHE_SENT_DATE,
+                                        sent_date, sizeof(*sent_date))) {
+               mail_cache_mark_missing(mail->ibox->index->cache,
+                                       MAIL_CACHE_SENT_DATE);
+
+               sent_date->time = (time_t)-1;
+               sent_date->timezone = 0;
        }
+}
+
+static int index_mail_cache_transaction_begin(struct index_mail *mail)
+{
+       if (mail->ibox->trans_ctx != NULL)
+               return TRUE;
+
+       if (mail_cache_transaction_begin(mail->ibox->index->cache, TRUE,
+                                        &mail->ibox->trans_ctx) <= 0)
+               return FALSE;
+
+       mail->data.cached_fields =
+               mail_cache_get_fields(mail->ibox->index->cache,
+                                     mail->data.rec);
+       return TRUE;
+}
+
+static int index_mail_cache_can_add(struct index_mail *mail,
+                                   enum mail_cache_field field)
+{
+       if ((mail->data.cached_fields & field) != 0)
+               return FALSE;
+
+       // FIXME: check if we really want to cache this
+
+       if (!index_mail_cache_transaction_begin(mail))
+               return FALSE;
+
+       /* cached_fields may have changed, recheck */
+       if ((mail->data.cached_fields & field) != 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+static void index_mail_cache_add(struct index_mail *mail,
+                                enum mail_cache_field field,
+                                const void *data, size_t size)
+{
+       struct index_mailbox *ibox = mail->ibox;
 
-       return time_p == NULL ? (time_t)-1 : *time_p;
+        if (!index_mail_cache_can_add(mail, field))
+               return;
+
+       if (!mail_cache_add(ibox->trans_ctx, mail->data.rec,
+                           field, data, size))
+               mail_cache_transaction_rollback(ibox->trans_ctx);
+
+       mail->data.cached_fields |= field;
 }
 
 static int open_stream(struct index_mail *mail, uoff_t position)
 {
+       struct index_mail_data *data = &mail->data;
        int deleted;
 
-       if (mail->data.stream == NULL) {
-               mail->data.stream = mail->ibox->index->
-                       open_mail(mail->ibox->index, mail->data.rec,
-                                 &mail->data.received_date, &deleted);
+       if (data->stream == NULL) {
+               data->stream = mail->ibox->index->
+                       open_mail(mail->ibox->index, data->rec,
+                                 &data->received_date, &deleted);
+               data->deleted = deleted;
 
-               if (mail->data.stream == NULL)
+               if (data->stream == NULL)
                        return FALSE;
+
+               if (data->received_date != (time_t)-1) {
+                       index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
+                                            &data->received_date,
+                                            sizeof(data->received_date));
+               }
        }
 
        i_stream_seek(mail->data.stream, position);
        return TRUE;
 }
 
-static void prepend_cached_header(struct index_mail *mail, const char *name)
+static int find_wanted_headers(struct mail_cache *cache,
+                              const char *const wanted_headers[])
 {
-       struct cached_header *hdr;
+       const char *const *headers, *const *tmp, *const *tmp2;
+       int i;
 
-       hdr = p_new(mail->pool, struct cached_header, 1);
-       hdr->name = name;
-       hdr->name_len = strlen(name);
+       if (wanted_headers == NULL || *wanted_headers == NULL)
+               return -1;
 
-       hdr->next = mail->data.headers;
-       mail->data.headers = hdr;
+       for (i = MAIL_CACHE_HEADERS_COUNT-1; i >= 0; i--) {
+               headers = mail_cache_get_header_fields(cache, i);
+               if (headers == NULL)
+                       continue;
+
+               for (tmp = wanted_headers; *tmp != NULL; tmp++) {
+                       for (tmp2 = headers; *tmp2 != NULL; tmp2++) {
+                               if (strcasecmp(*tmp2, *tmp) == 0)
+                                       break;
+                       }
+
+                       if (*tmp2 == NULL)
+                               break;
+               }
+
+               if (*tmp == NULL)
+                       return i;
+       }
+
+       return -1;
 }
 
-void index_mail_init_parse_header(struct index_mail *mail)
+static struct cached_header *
+find_cached_header(struct index_mail *mail, const char *name, size_t len)
 {
-       const char *const *tmp;
+       struct cached_header *hdr;
 
-       if (mail->wanted_headers != NULL) {
-               for (tmp = mail->wanted_headers; *tmp != NULL; tmp++)
-                       prepend_cached_header(mail, *tmp);
+       for (hdr = mail->data.headers; hdr != NULL; hdr = hdr->next) {
+               if (len == hdr->name_len &&
+                   memcasecmp(hdr->name, name, len) == 0)
+                       return hdr;
        }
+
+       return NULL;
 }
 
 void index_mail_parse_header(struct message_part *part __attr_unused__,
@@ -175,66 +268,201 @@ void index_mail_parse_header(struct message_part *part __attr_unused__,
 
        if (hdr == NULL) {
                /* end of headers */
-               if (data->save_sent_time) {
+               if (data->save_sent_date) {
                        /* not found */
-                       data->sent_time = 0;
-                       data->sent_timezone = 0;
-                       data->save_sent_time = FALSE;
+                       data->sent_date.time = 0;
+                       data->sent_date.timezone = 0;
+                       data->save_sent_date = FALSE;
+               }
+               if (data->sent_date.time != (time_t)-1) {
+                       index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
+                                            &data->sent_date,
+                                            sizeof(data->sent_date));
+               }
+
+               /* mark parsed headers as fully saved */
+                cached_hdr = data->headers;
+               for (; cached_hdr != NULL; cached_hdr = cached_hdr->next) {
+                       if (cached_hdr->parsing) {
+                               cached_hdr->parsing = FALSE;
+                               cached_hdr->fully_saved = TRUE;
+                       }
                }
                return;
        }
 
-       if (data->save_sent_time && strcasecmp(hdr->name, "Date") == 0) {
+       if (data->save_sent_date && strcasecmp(hdr->name, "Date") == 0) {
                if (hdr->continues) {
                        hdr->use_full_value = TRUE;
                        return;
                }
                if (!message_date_parse(hdr->full_value, hdr->full_value_len,
-                                       &data->sent_time,
-                                       &data->sent_timezone)) {
+                                       &data->sent_date.time,
+                                       &data->sent_date.timezone)) {
                        /* 0 == parse error */
-                       data->sent_time = 0;
-                       data->sent_timezone = 0;
+                       data->sent_date.time = 0;
+                       data->sent_date.timezone = 0;
                }
-               data->save_sent_time = FALSE;
+               data->save_sent_date = FALSE;
        }
 
-        cached_hdr = data->headers;
-       while (cached_hdr != NULL) {
-               if (cached_hdr->name_len == hdr->name_len &&
-                   memcasecmp(hdr->name, hdr->name, hdr->name_len) == 0) {
-                       /* save only the first header */
-                       if (cached_hdr->value != NULL)
-                               break;
+       cached_hdr = find_cached_header(mail, hdr->name, hdr->name_len);
+       if (cached_hdr != NULL && !cached_hdr->fully_saved) {
+               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");
+       }
+}
 
-                       if (hdr->continues) {
-                               hdr->use_full_value = TRUE;
-                               break;
-                       }
+static struct cached_header *
+add_cached_header(struct index_mail *mail, const char *name)
+{
+       struct cached_header *hdr;
+
+       i_assert(*name != '\0');
+
+       hdr = find_cached_header(mail, name, strlen(name));
+       if (hdr != NULL)
+               return hdr;
+
+       hdr = p_new(mail->pool, struct cached_header, 1);
+       hdr->name = p_strdup(mail->pool, name);
+       hdr->name_len = strlen(name);
+
+       hdr->next = mail->data.headers;
+       mail->data.headers = hdr;
+
+       return hdr;
+}
+
+static const char *const *get_header_names(struct cached_header *hdr)
+{
+       const char *null = NULL;
+       buffer_t *buffer;
+
+       buffer = buffer_create_dynamic(data_stack_pool, 128, (size_t)-1);
+       for (; hdr != NULL; hdr = hdr->next)
+               buffer_append(buffer, &hdr->name, sizeof(const char *));
+       buffer_append(buffer, &null, sizeof(const char *));
+
+       return buffer_get_data(buffer, NULL);
+}
+
+static int find_unused_header_idx(struct mail_cache *cache)
+{
+       int i;
+
+       for (i = 0; i < MAIL_CACHE_HEADERS_COUNT; i++) {
+               if (mail_cache_get_header_fields(cache, i) == NULL)
+                       return i;
+       }
+       return -1;
+}
+
+void index_mail_parse_header_init(struct index_mail *mail,
+                                 const char *const *headers)
+{
+       struct cached_header *hdr;
+       const char *const *tmp;
 
-                       cached_hdr->value = p_strndup(mail->pool,
-                                                     hdr->full_value,
-                                                     hdr->full_value_len);
-                       break;
+       if (mail->data.header_data == NULL)
+               mail->data.header_data = str_new(mail->pool, 4096);
+
+       if (headers == NULL) {
+               /* parsing all headers */
+               for (hdr = mail->data.headers; hdr != NULL; hdr = hdr->next)
+                       hdr->parsing = TRUE;
+       } else {
+               for (hdr = mail->data.headers; hdr != NULL; hdr = hdr->next) {
+                       for (tmp = headers; *tmp != NULL; tmp++) {
+                               if (strcasecmp(*tmp, hdr->name) == 0)
+                                       hdr->parsing = TRUE;
+                       }
                }
-                cached_hdr = cached_hdr->next;
        }
 }
 
 static int parse_header(struct index_mail *mail)
 {
+       struct mail_cache *cache = mail->ibox->index->cache;
+       const char *const *headers, *const *tmp;
+       int idx;
+
        if (!open_stream(mail, 0))
                return FALSE;
 
-        index_mail_init_parse_header(mail);
+       if (mail->data.save_cached_headers) {
+               /* we want to save some of the headers. that means we'll have
+                  to save all the headers in that group. if we're creating a
+                  new group, save all the headers in previous group in it
+                  too. */
+               idx = mail->data.save_header_idx;
+               if (idx < 0) {
+                       /* can we reuse existing? */
+                       headers = get_header_names(mail->data.headers);
+                       idx = find_wanted_headers(cache, headers);
+                       if (idx >= 0)
+                               mail->data.save_header_idx = idx;
+               }
+               if (idx < 0) {
+                       idx = find_unused_header_idx(cache);
+                       idx--; /* include all previous headers too */
+               }
+
+               headers = idx < 0 ? NULL :
+                       mail_cache_get_header_fields(cache, idx);
+
+               if (headers != NULL) {
+                       for (tmp = headers; *tmp != NULL; tmp++)
+                               add_cached_header(mail, *tmp);
+               }
+       }
+
+       index_mail_parse_header_init(mail, NULL);
        message_parse_header(NULL, mail->data.stream, &mail->data.hdr_size,
                             index_mail_parse_header, mail);
        mail->data.parse_header = FALSE;
+       mail->data.headers_read = TRUE;
        mail->data.hdr_size_set = TRUE;
 
        return TRUE;
 }
 
+static int parse_cached_header(struct index_mail *mail, int idx)
+{
+       struct istream *istream;
+       const char *str, *const *idx_headers;
+
+       idx_headers = mail_cache_get_header_fields(mail->ibox->index->cache,
+                                                  idx);
+       i_assert(idx_headers != NULL);
+
+       str = mail_cache_lookup_string_field(mail->ibox->index->cache,
+                                            mail->data.rec,
+                                            mail_cache_header_fields[idx]);
+       if (str == NULL)
+               return FALSE;
+
+       t_push();
+       istream = i_stream_create_from_data(data_stack_pool, str, strlen(str));
+       index_mail_parse_header_init(mail, idx_headers);
+       message_parse_header(NULL, istream, NULL,
+                            index_mail_parse_header, mail);
+
+       i_stream_unref(istream);
+       t_pop();
+
+       if (idx == mail->data.header_idx)
+               mail->data.headers_read = TRUE;
+       return TRUE;
+}
+
 static const struct mail_full_flags *get_flags(struct mail *_mail)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
@@ -255,6 +483,9 @@ static const struct message_part *get_parts(struct mail *_mail)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
        struct index_mail_data *data = &mail->data;
+       buffer_t *buffer;
+       const void *buf_data;
+       size_t buf_size;
 
        if (data->parts != NULL)
                return data->parts;
@@ -268,7 +499,6 @@ static const struct message_part *get_parts(struct mail *_mail)
        if (!open_stream(mail, 0))
                return NULL;
 
-        index_mail_init_parse_header(mail);
        data->parts = message_parse(mail->pool, data->stream,
                                    index_mail_parse_header, mail);
 
@@ -281,6 +511,17 @@ static const struct message_part *get_parts(struct mail *_mail)
                _mail->has_no_nuls = TRUE;
        }
 
+       if (index_mail_cache_can_add(mail, MAIL_CACHE_MESSAGEPART)) {
+               t_push();
+               buffer = buffer_create_dynamic(data_stack_pool,
+                                              1024, (size_t)-1);
+               message_part_serialize(data->parts, buffer);
+
+               buf_data = buffer_get_data(buffer, &buf_size);
+               index_mail_cache_add(mail, MAIL_CACHE_MESSAGEPART,
+                                    &buf_data, buf_size);
+               t_pop();
+       }
        return data->parts;
 }
 
@@ -299,41 +540,51 @@ static time_t get_received_date(struct mail *_mail)
        }
 
        data->received_date = mail->ibox->index->
-               get_internal_date(mail->ibox->index, mail->data.rec);
+               get_received_date(mail->ibox->index, mail->data.rec);
+       if (data->received_date != (time_t)-1) {
+               index_mail_cache_add(mail, MAIL_CACHE_RECEIVED_DATE,
+                                    &data->received_date,
+                                    sizeof(data->received_date));
+       }
        return data->received_date;
 }
 
 static time_t get_date(struct mail *_mail, int *timezone)
 {
+       static const char *date_headers[] = { "Date", NULL };
        struct index_mail *mail = (struct index_mail *) _mail;
        struct index_mail_data *data = &mail->data;
-       const char *result;
+       int idx;
 
-       if (data->sent_time != (time_t)-1) {
+       if (data->sent_date.time != (time_t)-1) {
                if (timezone != NULL)
-                       *timezone = data->sent_timezone;
-               return data->sent_time;
+                       *timezone = data->sent_date.timezone;
+               return data->sent_date.time;
        }
 
-       if (data->parse_header || data->envelope == NULL) {
-               data->save_sent_time = TRUE;
-               parse_header(mail);
-       } else {
-               if (!imap_envelope_parse(data->envelope,
-                                        IMAP_ENVELOPE_DATE,
-                                        IMAP_ENVELOPE_RESULT_TYPE_STRING,
-                                        &result))
-                       return (time_t)-1;
+       if ((mail->wanted_fields & MAIL_FETCH_DATE) == 0)
+               get_cached_sent_date(mail, &data->sent_date);
+
+       if (data->sent_date.time == (time_t)-1) {
+               idx = data->parse_header ? -1 :
+                       find_wanted_headers(mail->ibox->index->cache,
+                                           date_headers);
+
+               data->save_sent_date = TRUE;
+               if (idx >= 0) {
+                       if (!parse_cached_header(mail, idx))
+                               idx = -1;
+               }
+               if (idx < 0)
+                       parse_header(mail);
 
-               if (!message_date_parse((const unsigned char *) result,
-                                       (size_t)-1, &data->sent_time,
-                                       &data->sent_timezone))
-                       data->sent_time = 0;
+               index_mail_cache_add(mail, MAIL_CACHE_SENT_DATE,
+                                    &data->sent_date, sizeof(data->sent_date));
        }
 
        if (timezone != NULL)
-               *timezone = data->sent_timezone;
-       return data->sent_time;
+               *timezone = data->sent_date.timezone;
+       return data->sent_date.time;
 }
 
 static int get_msgpart_sizes(struct index_mail *mail)
@@ -358,12 +609,15 @@ static int get_msgpart_sizes(struct index_mail *mail)
 
 static void get_binary_sizes(struct index_mail *mail)
 {
+       enum mail_index_record_flag index_flags;
        uoff_t size;
 
+       index_flags = mail_cache_get_index_flags(mail->ibox->index->cache,
+                                                mail->data. rec);
+
        if (!mail->data.hdr_size_set &&
-           (mail->data.rec->index_flags & INDEX_MAIL_FLAG_BINARY_HEADER)) {
-               size = get_cached_uoff_t(mail, DATA_HDR_HEADER_SIZE,
-                                        "header size");
+           (index_flags & MAIL_INDEX_FLAG_BINARY_HEADER) != 0) {
+               size = get_cached_uoff_t(mail, MAIL_CACHE_HEADER_SIZE);
                if (size != (uoff_t)-1) {
                        mail->data.hdr_size.physical_size =
                                mail->data.hdr_size.virtual_size = size;
@@ -372,8 +626,8 @@ static void get_binary_sizes(struct index_mail *mail)
        }
 
        if (!mail->data.body_size_set &&
-           (mail->data.rec->index_flags & INDEX_MAIL_FLAG_BINARY_BODY)) {
-               size = get_cached_uoff_t(mail, DATA_HDR_BODY_SIZE, "body size");
+           (index_flags & MAIL_INDEX_FLAG_BINARY_BODY) != 0) {
+               size = get_cached_uoff_t(mail, MAIL_CACHE_BODY_SIZE);
                if (size != (uoff_t)-1) {
                        mail->data.body_size.physical_size =
                                mail->data.body_size.virtual_size = size;
@@ -382,6 +636,20 @@ static void get_binary_sizes(struct index_mail *mail)
        }
 }
 
+static void index_mail_cache_add_sizes(struct index_mail *mail)
+{
+       if (mail->data.hdr_size_set) {
+               index_mail_cache_add(mail, MAIL_CACHE_HEADER_SIZE,
+                                    &mail->data.hdr_size.physical_size,
+                                    sizeof(uoff_t));
+       }
+       if (mail->data.body_size_set) {
+               index_mail_cache_add(mail, MAIL_CACHE_BODY_SIZE,
+                                    &mail->data.body_size.physical_size,
+                                    sizeof(uoff_t));
+       }
+}
+
 static uoff_t get_size(struct mail *_mail)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
@@ -440,48 +708,104 @@ static uoff_t get_size(struct mail *_mail)
                body_size = data->body_size.virtual_size;
                data->body_size_set = TRUE;
        }
-
        data->size = hdr_size + body_size;
+
+       index_mail_cache_add_sizes(mail);
+       index_mail_cache_add(mail, MAIL_CACHE_VIRTUAL_FULL_SIZE,
+                            &data->size, sizeof(data->size));
+
        return data->size;
 }
 
 static const char *get_header(struct mail *_mail, const char *field)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
-       struct index_mail_data *data = &mail->data;
        struct cached_header *hdr;
-       enum imap_envelope_field env_field = 0;
-       const char *str;
-       char *ret;
+       size_t field_len;
+       int idx;
 
-       for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
-               if (strcasecmp(hdr->name, field) == 0)
-                       return hdr->value;
+       field_len = strlen(field);
+       hdr = find_cached_header(mail, field, field_len);
+       if (hdr == NULL) {
+               /* not wanted initially, add it and check if we can
+                  get it from cache */
+               const char *headers[2];
+
+               hdr = add_cached_header(mail, field);
+
+               headers[0] = field; headers[1] = NULL;
+               idx = find_wanted_headers(mail->ibox->index->cache, headers);
+       } else {
+               idx = mail->data.header_idx;
        }
 
-       if (data->parse_header || data->envelope == NULL ||
-           !imap_envelope_get_field(field, &env_field)) {
-               /* if we have to parse the header, do it even if we could use
-                  envelope - envelope parsing would just slow up. */
-                prepend_cached_header(mail, field);
-               parse_header(mail);
+       if (!hdr->fully_saved) {
+               if (idx >= 0) {
+                       if (!parse_cached_header(mail, idx))
+                               idx = -1;
+               }
+               if (idx < 0) {
+                       mail->data.save_cached_headers = TRUE;
+                       parse_header(mail);
+               }
+
+               hdr = find_cached_header(mail, field, field_len);
+       }
+
+       return hdr->value_idx == 0 ? NULL :
+               t_strcut(str_c(mail->data.header_data) + hdr->value_idx, '\n');
+}
+
+static struct istream *get_headers(struct mail *_mail,
+                                  const char *const minimum_fields[])
+{
+       struct index_mail *mail = (struct index_mail *) _mail;
+       struct cached_header *hdr;
+       const char *const *tmp, *str;
+       int idx, all_exists, all_saved;
 
-               for (hdr = data->headers; hdr != NULL; hdr = hdr->next) {
-                       if (strcasecmp(hdr->name, field) == 0)
-                               return hdr->value;
+       i_assert(*minimum_fields != NULL);
+
+       all_exists = all_saved = TRUE;
+       for (tmp = minimum_fields; *tmp != NULL; tmp++) {
+               hdr = find_cached_header(mail, *tmp, strlen(*tmp));
+               if (hdr == NULL) {
+                       add_cached_header(mail, *tmp);
+                       all_exists = FALSE;
+               } else if (!hdr->fully_saved)
+                       all_saved = FALSE;
+       }
+
+       if (all_exists) {
+               if (all_saved) {
+                       return i_stream_create_from_data(mail->pool,
+                                       str_data(mail->data.header_data),
+                                       str_len(mail->data.header_data));
                }
 
-               return NULL;
+               idx = mail->data.header_idx;
        } else {
-               t_push();
-               if (!imap_envelope_parse(data->envelope, env_field,
-                                        IMAP_ENVELOPE_RESULT_TYPE_STRING,
-                                        &str))
-                       str = NULL;
-               ret = p_strdup(mail->pool, str);
-               t_pop();
-               return ret;
+               idx = find_wanted_headers(mail->ibox->index->cache,
+                                         get_header_names(mail->data.headers));
+       }
+
+       if (idx >= 0) {
+               /* everything should be cached */
+               str = mail_cache_lookup_string_field(mail->ibox->index->cache,
+                               mail->data.rec, mail_cache_header_fields[idx]);
+               if (str != NULL) {
+                       return i_stream_create_from_data(mail->pool,
+                                                        str, strlen(str));
+               }
+       }
+       if (idx < 0) {
+               mail->data.save_cached_headers = TRUE;
+               parse_header(mail);
        }
+
+       return i_stream_create_from_data(mail->pool,
+                                        str_data(mail->data.header_data),
+                                        str_len(mail->data.header_data));
 }
 
 static const struct message_address *
@@ -490,8 +814,6 @@ get_address(struct mail *_mail, const char *field)
        struct index_mail *mail = (struct index_mail *) _mail;
        const char *str;
 
-       /* don't bother with checking envelope - we're most likely
-          creating it */
        str = get_header(_mail, field);
        if (str == NULL)
                return NULL;
@@ -503,37 +825,16 @@ get_address(struct mail *_mail, const char *field)
 static const char *get_first_mailbox(struct mail *_mail, const char *field)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
-       struct index_mail_data *data = &mail->data;
-       enum imap_envelope_field env_field;
+       struct message_address *addr;
        const char *str;
-       const char *ret = NULL;
-
-       if (data->envelope != NULL &&
-           imap_envelope_get_field(field, &env_field)) {
-               /* prefer parsing envelope - faster than having to actually
-                  parse the header field */
-               t_push();
-               if (!imap_envelope_parse(data->envelope, env_field,
-                                       IMAP_ENVELOPE_RESULT_TYPE_FIRST_MAILBOX,
-                                       &str))
-                       str = NULL;
-               ret = p_strdup(mail->pool, str);
-               t_pop();
-       } else {
-               struct message_address *addr;
-
-               str = get_header(_mail, field);
-               if (str == NULL)
-                       return NULL;
 
-               addr = message_address_parse(mail->pool,
-                                            (const unsigned char *) str,
-                                            (size_t)-1, 1);
-               if (addr != NULL)
-                       ret = addr->mailbox;
-       }
+       str = get_header(_mail, field);
+       if (str == NULL)
+               return NULL;
 
-       return ret;
+       addr = message_address_parse(mail->pool, (const unsigned char *) str,
+                                    (size_t)-1, 1);
+       return addr != NULL ? addr->mailbox : NULL;
 }
 
 static struct istream *get_stream(struct mail *_mail,
@@ -574,6 +875,7 @@ static struct istream *get_stream(struct mail *_mail,
                *body_size = data->body_size;
        }
 
+       index_mail_cache_add_sizes(mail);
        i_stream_seek(data->stream, 0);
        return data->stream;
 }
@@ -582,14 +884,27 @@ static const char *get_special(struct mail *_mail, enum mail_fetch_field field)
 {
        struct index_mail *mail = (struct index_mail *) _mail;
        struct index_mail_data *data = &mail->data;
+       struct mail_cache *cache = mail->ibox->index->cache;
+       enum mail_cache_field cache_field;
        char *str;
+       int i, idx;
 
        switch (field) {
        case MAIL_FETCH_IMAP_BODY:
+               if ((data->cached_fields & MAIL_CACHE_BODY) &&
+                   data->body == NULL)
+                       data->body = get_cached_string(mail, MAIL_CACHE_BODY);
                if (data->body != NULL)
                        return data->body;
                /* fall through */
        case MAIL_FETCH_IMAP_BODYSTRUCTURE:
+               if ((data->cached_fields & MAIL_CACHE_BODYSTRUCTURE) &&
+                   data->bodystructure == NULL) {
+                       data->bodystructure =
+                               get_cached_string(mail,
+                                                 MAIL_CACHE_BODYSTRUCTURE);
+               }
+
                if (data->bodystructure != NULL) {
                        if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
                                return data->bodystructure;
@@ -600,9 +915,17 @@ static const char *get_special(struct mail *_mail, enum mail_fetch_field field)
                                imap_body_parse_from_bodystructure(
                                                        data->bodystructure));
                        t_pop();
+
+                       if (data->body == NULL) {
+                               mail_cache_set_corrupted(cache,
+                                       "Corrupted BODYSTRUCTURE");
+                       }
                        return data->body;
                }
 
+               if (!open_stream(mail, 0))
+                       return NULL;
+
                if (data->parts == NULL)
                        data->parts = get_cached_parts(mail);
 
@@ -612,6 +935,13 @@ static const char *get_special(struct mail *_mail, enum mail_fetch_field field)
                                field == MAIL_FETCH_IMAP_BODYSTRUCTURE));
                t_pop();
 
+               /* should never fail */
+               i_assert(str != NULL);
+
+               cache_field = field == MAIL_FETCH_IMAP_BODYSTRUCTURE ?
+                       MAIL_CACHE_BODYSTRUCTURE : MAIL_CACHE_BODY;
+               index_mail_cache_add(mail, cache_field, str, strlen(str)+1);
+
                if (field == MAIL_FETCH_IMAP_BODYSTRUCTURE)
                        data->bodystructure = str;
                else
@@ -621,15 +951,34 @@ static const char *get_special(struct mail *_mail, enum mail_fetch_field field)
                if (data->envelope != NULL)
                        return data->envelope;
 
-               if ((mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) == 0) {
-                       data->envelope = p_strdup(mail->pool,
-                               get_cached_field(mail, DATA_FIELD_ENVELOPE));
-                       if (data->envelope != NULL)
-                               return data->envelope;
+               if (data->parse_header) {
+                       data->save_envelope = TRUE;
+                       parse_header(mail);
+                       return data->envelope;
+               }
+
+               if (data->save_envelope) {
+                       /* it was in wanted_fields, header_idx should be
+                          correct */
+                       idx = data->header_idx;
+                       i_assert(idx >= 0);
+               } else {
+                       idx = find_wanted_headers(cache, imap_envelope_headers);
                }
 
                data->save_envelope = TRUE;
-               parse_header(mail);
+               if (idx >= 0) {
+                       if (!parse_cached_header(mail, idx))
+                               idx = -1;
+               }
+               if (idx < 0) {
+                       for (i = 0; imap_envelope_headers[i] != NULL; i++) {
+                               add_cached_header(mail,
+                                                 imap_envelope_headers[i]);
+                       }
+                       data->save_cached_headers = TRUE;
+                       parse_header(mail);
+               }
                return data->envelope;
        default:
                i_unreached();
@@ -646,6 +995,7 @@ static struct mail index_mail = {
        get_date,
        get_size,
        get_header,
+       get_headers,
        get_address,
        get_first_mailbox,
        get_stream,
@@ -662,7 +1012,10 @@ void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail,
        mail->mail = index_mail;
        mail->mail.box = &ibox->box;
 
-       mail->pool = pool_alloconly_create("index_mail", 4096);
+       mail->wanted_headers_idx =
+               find_wanted_headers(ibox->index->cache, wanted_headers);
+
+       mail->pool = pool_alloconly_create("index_mail", 16384);
        mail->ibox = ibox;
        mail->wanted_fields = wanted_fields;
        mail->wanted_headers = wanted_headers;
@@ -672,137 +1025,182 @@ void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail,
                ibox->mail_init(mail);
 }
 
+static void index_mail_close(struct index_mail *mail)
+{
+       struct index_mail_data *data = &mail->data;
+       const char *const *headers;
+
+       if (data->stream != NULL)
+               i_stream_unref(data->stream);
+
+       if (!data->save_cached_headers || !data->headers_read)
+               return;
+
+       /* save cached headers - FIXME: this breaks if fetch_uid() and
+          fetch/search are both accessing headers from same message.
+          index_mails should probably be shared.. */
+       if (!index_mail_cache_transaction_begin(mail))
+               return;
+
+       if (data->save_header_idx < 0) {
+               data->save_header_idx =
+                       find_unused_header_idx(mail->ibox->index->cache);
+               if (data->save_header_idx < 0)
+                       return;
+
+                headers = get_header_names(data->headers);
+               if (!mail_cache_set_header_fields(mail->ibox->trans_ctx,
+                                                 data->save_header_idx,
+                                                 headers))
+                       return;
+       }
+
+       mail_cache_add(mail->ibox->trans_ctx, data->rec,
+                      mail_cache_header_fields[data->save_header_idx],
+                      str_c(mail->data.header_data),
+                      str_len(mail->data.header_data)+1);
+       data->save_cached_headers = FALSE;
+}
+
 int index_mail_next(struct index_mail *mail, struct mail_index_record *rec,
-                   unsigned int idx_seq)
+                   unsigned int idx_seq, int delay_open)
 {
+       struct mail_index *index = mail->ibox->index;
        struct index_mail_data *data = &mail->data;
-       int ret, open_mail, parse_header, envelope_headers;
+        enum mail_index_record_flag index_flags;
+       int i, ret, open_mail, only_wanted_headers;
 
-       i_assert(mail->expunge_counter == mail->ibox->index->expunge_counter);
+       i_assert(mail->expunge_counter == index->expunge_counter);
 
        t_push();
 
-       /* close the old one */
-       if (data->stream != NULL)
-               i_stream_unref(data->stream);
-
+       index_mail_close(mail);
        memset(data, 0, sizeof(*data));
        p_clear(mail->pool);
 
-       mail->mail.has_nuls =
-               (rec->index_flags & INDEX_MAIL_FLAG_HAS_NULS) != 0;
+        data->cached_fields = mail_cache_get_fields(index->cache, rec);
+       index_flags = (data->cached_fields & MAIL_CACHE_INDEX_FLAGS) == 0 ? 0 :
+               mail_cache_get_index_flags(index->cache, rec);
+
+       mail->mail.has_nuls = (index_flags & MAIL_INDEX_FLAG_HAS_NULS) != 0;
        mail->mail.has_no_nuls =
-               (rec->index_flags & INDEX_MAIL_FLAG_HAS_NO_NULS) != 0;
+               (index_flags & MAIL_INDEX_FLAG_HAS_NO_NULS) != 0;
 
        data->rec = rec;
        data->idx_seq = idx_seq;
        data->size = (uoff_t)-1;
-       data->received_date = data->sent_time = (time_t)-1;
+       data->received_date = data->sent_date.time = (time_t)-1;
 
        /* if some wanted fields are cached, get them */
        if (mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS)
                data->parts = get_cached_parts(mail);
-       if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE)
-               data->envelope = get_cached_field(mail, DATA_FIELD_ENVELOPE);
        if (mail->wanted_fields & MAIL_FETCH_IMAP_BODY)
-               data->body = get_cached_field(mail, DATA_FIELD_BODY);
-       if (mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) {
+               data->body = get_cached_string(mail, MAIL_CACHE_BODY);
+       if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) ||
+           ((mail->wanted_fields & MAIL_FETCH_IMAP_BODY) &&
+            data->body == NULL)) {
                data->bodystructure =
-                       get_cached_field(mail, DATA_FIELD_BODYSTRUCTURE);
+                       get_cached_string(mail, MAIL_CACHE_BODYSTRUCTURE);
        }
        if (mail->wanted_fields & MAIL_FETCH_SIZE)
                data->size = get_cached_virtual_size(mail);
+       if (mail->wanted_fields & MAIL_FETCH_DATE)
+               get_cached_sent_date(mail, &data->sent_date);
 
        /* see if we have to parse the message */
-       open_mail = parse_header = FALSE;
+       open_mail = FALSE;
        if ((mail->wanted_fields & MAIL_FETCH_MESSAGE_PARTS) &&
            data->parts == NULL)
-               parse_header = TRUE;
-       else if ((mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) &&
-                data->envelope == NULL)
-               parse_header = TRUE;
+               data->parse_header = TRUE;
        else if ((mail->wanted_fields & MAIL_FETCH_IMAP_BODYSTRUCTURE) &&
                 data->bodystructure == NULL) {
                if (data->parts == NULL)
                        data->parts = get_cached_parts(mail);
-               /* FIXME: this isn't helping really.. */
                open_mail = TRUE;
-               parse_header = data->parts == NULL;
+               data->parse_header = data->parts == NULL;
        } 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);
                open_mail = TRUE;
-               parse_header = data->parts == NULL;
+               data->parse_header = data->parts == NULL;
        } else if (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
                                          MAIL_FETCH_STREAM_BODY))
                open_mail = TRUE;
-       else if ((mail->wanted_fields & MAIL_FETCH_SIZE) && data->size == 0)
-               open_mail = TRUE;
 
-       envelope_headers = FALSE;
-       if (!parse_header && mail->wanted_headers != NULL) {
-               const char *const *tmp;
-               enum imap_envelope_field env_field;
-
-               for (tmp = mail->wanted_headers; *tmp != NULL; tmp++) {
-                       if (imap_envelope_get_field(*tmp, &env_field))
-                               envelope_headers = TRUE;
-                       else {
-                               open_mail = TRUE;
-                               parse_header = TRUE;
-                               break;
-                       }
-               }
-       }
+       /* check headers */
+       if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE) {
+               for (i = 0; imap_envelope_headers[i] != NULL; i++) 
+                       add_cached_header(mail, imap_envelope_headers[i]);
+       } else if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
+                  data->sent_date.time == (time_t)-1)
+               add_cached_header(mail, "Date");
 
-       if (!parse_header && (mail->wanted_fields & MAIL_FETCH_DATE))
-               envelope_headers = TRUE;
+       only_wanted_headers = mail->data.headers == NULL;
+       if (mail->wanted_headers != NULL) {
+               const char *const *tmp;
 
-       if (!parse_header && envelope_headers &&
-           data->envelope == NULL) {
-               data->envelope =
-                       get_cached_field(mail, DATA_FIELD_ENVELOPE);
-               if (data->envelope == NULL)
-                       parse_header = TRUE;
+               for (tmp = mail->wanted_headers; *tmp != NULL; tmp++)
+                       add_cached_header(mail, *tmp);
        }
 
-       if (open_mail || parse_header) {
-               int deleted;
+       data->save_header_idx = -1;
+       if (data->headers != NULL &&
+           (mail->wanted_fields & (MAIL_FETCH_STREAM_HEADER |
+                                   MAIL_FETCH_STREAM_BODY)) == 0) {
+               /* we're not explicitly opening the file, caching the headers
+                  could be a good idea if they're not already cached */
+               if (only_wanted_headers) {
+                       /* no extra headers, we already know if it's indexed */
+                       data->header_idx = mail->wanted_headers_idx;
+               } else {
+                       const char *const *headers;
+
+                       headers = get_header_names(data->headers);
+                       data->header_idx =
+                               find_wanted_headers(index->cache, headers);
+               }
+               if (data->header_idx == -1 ||
+                   (data->cached_fields &
+                    mail_cache_header_fields[data->header_idx]) == 0) {
+                       data->save_cached_headers = TRUE;
+                       data->parse_header = TRUE;
+                       data->save_header_idx = data->header_idx;
+               }
+       } else {
+               data->header_idx = -1;
+       }
 
-               data->stream = mail->ibox->index->
-                       open_mail(mail->ibox->index, data->rec,
-                                 &data->received_date, &deleted);
-               if (data->stream == NULL)
-                       ret = deleted ? 0 : -1;
+       if ((open_mail || data->parse_header) && !delay_open) {
+               if (!open_stream(mail, 0))
+                       ret = data->deleted ? 0 : -1;
                else
                        ret = 1;
        } else {
-               if ((mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) &&
-                   data->received_date == (time_t)-1) {
+               if (mail->wanted_fields & MAIL_FETCH_RECEIVED_DATE) {
                        /* check this only after open_mail() */
                        data->received_date = get_cached_received_date(mail);
                }
-
-               if (mail->wanted_fields & MAIL_FETCH_DATE)
-                       data->save_sent_time = TRUE;
-               if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE)
-                       data->save_envelope = TRUE;
-
-               data->parse_header = parse_header;
                ret = 1;
        }
+
+       if ((mail->wanted_fields & MAIL_FETCH_DATE) &&
+           data->sent_date.time == (time_t)-1)
+               data->save_sent_date = TRUE;
+
+       if (mail->wanted_fields & MAIL_FETCH_IMAP_ENVELOPE)
+               data->save_envelope = TRUE;
+
        t_pop();
        return ret;
 }
 
 void index_mail_deinit(struct index_mail *mail)
 {
-       if (mail->data.stream != NULL) {
-               i_stream_unref(mail->data.stream);
-               mail->data.stream = NULL;
-       }
+       t_push();
+       index_mail_close(mail);
+       t_pop();
 
        pool_unref(mail->pool);
-       mail->pool = NULL;
+       memset(mail, 0, sizeof(*mail));
 }
index 2396b2b31e315f91df9f41487d8e4cc5278d20b9..826137aeaca9c628faffe52d799656ecf0f0436c 100644 (file)
@@ -2,6 +2,7 @@
 #define __INDEX_MAIL_H
 
 #include "message-size.h"
+#include "mail-cache.h"
 
 struct message_header_line;
 
@@ -9,18 +10,25 @@ struct cached_header {
        struct cached_header *next;
 
        size_t name_len;
-       const char *name, *value;
+       const char *name;
+       size_t value_idx; /* in header_data */
+
+       unsigned int parsing:1;
+       unsigned int fully_saved:1;
 };
 
 struct index_mail_data {
        struct mail_full_flags flags;
-       time_t received_date;
+       time_t date, received_date;
        uoff_t size;
 
-       time_t sent_time;
-       int sent_timezone;
+       enum mail_cache_field cached_fields;
+       struct mail_sent_date sent_date;
 
        struct cached_header *headers;
+       string_t *header_data;
+       int header_idx, save_header_idx;
+
        struct message_part *parts;
        const char *envelope, *body, *bodystructure;
         struct message_part_envelope_data *envelope_data;
@@ -32,10 +40,13 @@ struct index_mail_data {
         struct message_size hdr_size, body_size;
 
        unsigned int parse_header:1;
-       unsigned int save_sent_time:1;
+       unsigned int headers_read:1;
+       unsigned int save_cached_headers:1;
+       unsigned int save_sent_date:1;
        unsigned int save_envelope:1;
        unsigned int hdr_size_set:1;
        unsigned int body_size_set:1;
+       unsigned int deleted:1;
 };
 
 struct index_mail {
@@ -49,16 +60,18 @@ struct index_mail {
 
        enum mail_fetch_field wanted_fields;
        const char *const *wanted_headers;
+       int wanted_headers_idx;
 };
 
 void index_mail_init(struct index_mailbox *ibox, struct index_mail *mail,
                     enum mail_fetch_field wanted_fields,
                     const char *const wanted_headers[]);
 int index_mail_next(struct index_mail *mail, struct mail_index_record *rec,
-                   unsigned int idx_seq);
+                   unsigned int idx_seq, int delay_open);
 void index_mail_deinit(struct index_mail *mail);
 
-void index_mail_init_parse_header(struct index_mail *mail);
+void index_mail_parse_header_init(struct index_mail *mail,
+                                 const char *const *headers);
 void index_mail_parse_header(struct message_part *part,
                             struct message_header_line *hdr, void *context);
 
index 26d0b1a8c5d515222fd65bd64325e96173aeeec1..410df572c9d7c57113f1c5c932772dd7e3dcb01a 100644 (file)
@@ -8,7 +8,6 @@
 #include "message-body-search.h"
 #include "message-header-search.h"
 #include "imap-date.h"
-#include "imap-envelope.h"
 #include "index-storage.h"
 #include "index-messageset.h"
 #include "index-mail.h"
@@ -216,16 +215,17 @@ static int search_arg_match_cached(struct mail_search_context *ctx,
                                   enum mail_search_arg_type type,
                                   const char *value)
 {
-       time_t internal_date, search_time;
+       time_t date, search_time;
        uoff_t virtual_size, search_size;
+       int timezone_offset;
 
        switch (type) {
        /* internal dates */
        case SEARCH_BEFORE:
        case SEARCH_ON:
        case SEARCH_SINCE:
-               internal_date = ctx->mail->get_received_date(ctx->mail);
-               if (internal_date == (time_t)-1)
+               date = ctx->mail->get_received_date(ctx->mail);
+               if (date == (time_t)-1)
                        return -1;
 
                if (!imap_parse_date(value, &search_time))
@@ -233,12 +233,39 @@ static int search_arg_match_cached(struct mail_search_context *ctx,
 
                switch (type) {
                case SEARCH_BEFORE:
-                       return internal_date < search_time;
+                       return date < search_time;
                case SEARCH_ON:
-                       return internal_date >= search_time &&
-                               internal_date < search_time + 3600*24;
+                       return date >= search_time &&
+                               date < search_time + 3600*24;
                case SEARCH_SINCE:
-                       return internal_date >= search_time;
+                       return date >= search_time;
+               default:
+                       /* unreachable */
+                       break;
+               }
+
+       /* sent dates */
+       case SEARCH_SENTBEFORE:
+       case SEARCH_SENTON:
+       case SEARCH_SENTSINCE:
+               /* NOTE: RFC-3501 specifies that timezone is ignored
+                  in searches. date is returned as UTC, so change it. */
+               date = ctx->mail->get_date(ctx->mail, &timezone_offset);
+               if (date == (time_t)-1)
+                       return -1;
+               date += timezone_offset * 60;
+
+               if (!imap_parse_date(value, &search_time))
+                       return 0;
+
+               switch (type) {
+               case SEARCH_SENTBEFORE:
+                       return date < search_time;
+               case SEARCH_SENTON:
+                       return date >= search_time &&
+                               date < search_time + 3600*24;
+               case SEARCH_SENTSINCE:
+                       return date >= search_time;
                default:
                        /* unreachable */
                        break;
@@ -339,120 +366,6 @@ search_header_context(struct mail_search_context *ctx,
        return arg->context;
 }
 
-/* Returns >0 = matched, 0 = not matched, -1 = unknown */
-static int search_arg_match_envelope(struct mail_search_context *ctx,
-                                    struct mail_search_arg *arg)
-{
-       struct mail_index *index = ctx->ibox->index;
-       enum imap_envelope_field env_field;
-        struct header_search_context *hdr_search_ctx;
-       const char *envelope, *field;
-       int ret;
-
-       switch (arg->type) {
-       case SEARCH_SENTBEFORE:
-       case SEARCH_SENTON:
-       case SEARCH_SENTSINCE:
-                env_field = IMAP_ENVELOPE_DATE;
-               break;
-
-       case SEARCH_FROM:
-                env_field = IMAP_ENVELOPE_FROM;
-               break;
-       case SEARCH_TO:
-                env_field = IMAP_ENVELOPE_TO;
-               break;
-       case SEARCH_CC:
-                env_field = IMAP_ENVELOPE_CC;
-               break;
-       case SEARCH_BCC:
-                env_field = IMAP_ENVELOPE_BCC;
-               break;
-       case SEARCH_SUBJECT:
-                env_field = IMAP_ENVELOPE_SUBJECT;
-               break;
-
-       case SEARCH_IN_REPLY_TO:
-                env_field = IMAP_ENVELOPE_IN_REPLY_TO;
-               break;
-       case SEARCH_MESSAGE_ID:
-                env_field = IMAP_ENVELOPE_MESSAGE_ID;
-               break;
-       default:
-               return -1;
-       }
-
-       t_push();
-
-       /* get field from hopefully cached envelope */
-       envelope = index->lookup_field(index, ctx->imail.data.rec,
-                                      DATA_FIELD_ENVELOPE);
-       if (envelope != NULL) {
-               ret = imap_envelope_parse(envelope, env_field,
-                                         IMAP_ENVELOPE_RESULT_TYPE_STRING,
-                                         &field) ? 1 : -1;
-       } else {
-               index->cache_fields_later(index, DATA_FIELD_ENVELOPE);
-               field = NULL;
-               ret = -1;
-       }
-
-       if (ret != -1) {
-               switch (arg->type) {
-               case SEARCH_SENTBEFORE:
-               case SEARCH_SENTON:
-               case SEARCH_SENTSINCE:
-                       ret = search_sent(arg->type, arg->value.str,
-                                         (const unsigned char *) field,
-                                         (size_t)-1);
-                       break;
-               default:
-                       if (arg->value.str[0] == '\0') {
-                               /* we're just testing existence of the field.
-                                  assume it matches with non-NIL values. */
-                               ret = field != NULL ? 1 : 0;
-                               break;
-                       }
-
-                       if (field == NULL) {
-                               /* doesn't exist */
-                               ret = 0;
-                               break;
-                       }
-
-                       hdr_search_ctx = search_header_context(ctx, arg);
-                       if (hdr_search_ctx == NULL) {
-                               ret = 0;
-                               break;
-                       }
-
-                       ret = message_header_search(
-                                               (const unsigned char *) field,
-                                               strlen(field),
-                                               hdr_search_ctx) ? 1 : 0;
-               }
-       }
-       t_pop();
-       return ret;
-}
-
-static void search_envelope_arg(struct mail_search_arg *arg, void *context)
-{
-       struct mail_search_context *ctx = context;
-
-       switch (search_arg_match_envelope(ctx, arg)) {
-       case -1:
-               /* unknown */
-               break;
-       case 0:
-               ARG_SET_RESULT(arg, 0);
-               break;
-       default:
-               ARG_SET_RESULT(arg, 1);
-               break;
-       }
-}
-
 static void search_header_arg(struct mail_search_arg *arg, void *context)
 {
        struct search_header_context *ctx = context;
@@ -477,35 +390,8 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
                }
                return;
 
-       case SEARCH_FROM:
-               if (strcasecmp(ctx->hdr->name, "From") != 0)
-                       return;
-               break;
-       case SEARCH_TO:
-               if (strcasecmp(ctx->hdr->name, "To") != 0)
-                       return;
-               break;
-       case SEARCH_CC:
-               if (strcasecmp(ctx->hdr->name, "Cc") != 0)
-                       return;
-               break;
-       case SEARCH_BCC:
-               if (strcasecmp(ctx->hdr->name, "Bcc") != 0)
-                       return;
-               break;
-       case SEARCH_SUBJECT:
-               if (strcasecmp(ctx->hdr->name, "Subject") != 0)
-                       return;
-               break;
-       case SEARCH_IN_REPLY_TO:
-               if (strcasecmp(ctx->hdr->name, "In-Reply-To") != 0)
-                       return;
-               break;
-       case SEARCH_MESSAGE_ID:
-               if (strcasecmp(ctx->hdr->name, "Message-ID") != 0)
-                       return;
-               break;
        case SEARCH_HEADER:
+       case SEARCH_HEADER_ADDRESS:
                ctx->custom_header = TRUE;
 
                if (strcasecmp(ctx->hdr->name, arg->hdr_field_name) != 0)
@@ -532,8 +418,7 @@ static void search_header_arg(struct mail_search_arg *arg, void *context)
                hdr_search_ctx = search_header_context(ctx->index_context, arg);
                if (hdr_search_ctx == NULL)
                        ret = 0;
-               else if (arg->type == SEARCH_FROM || arg->type == SEARCH_TO ||
-                        arg->type == SEARCH_CC || arg->type == SEARCH_BCC) {
+               else if (arg->type == SEARCH_HEADER_ADDRESS) {
                        /* we have to match against normalized address */
                        struct message_address *addr;
                        string_t *str;
@@ -574,14 +459,8 @@ static void search_header_unmatch(struct mail_search_arg *arg,
                        ARG_SET_RESULT(arg, 0);
                }
                break;
-       case SEARCH_FROM:
-       case SEARCH_TO:
-       case SEARCH_CC:
-       case SEARCH_BCC:
-       case SEARCH_SUBJECT:
        case SEARCH_HEADER:
-       case SEARCH_IN_REPLY_TO:
-       case SEARCH_MESSAGE_ID:
+       case SEARCH_HEADER_ADDRESS:
                ARG_SET_RESULT(arg, 0);
                break;
        default:
@@ -605,15 +484,7 @@ static void search_header(struct message_part *part,
 
        index_mail_parse_header(part, hdr, ctx->index_context->mail);
 
-       if (ctx->custom_header ||
-           strcasecmp(hdr->name, "Date") == 0 ||
-           strcasecmp(hdr->name, "From") == 0 ||
-           strcasecmp(hdr->name, "To") == 0 ||
-           strcasecmp(hdr->name, "Cc") == 0 ||
-           strcasecmp(hdr->name, "Bcc") == 0 ||
-           strcasecmp(hdr->name, "Subject") == 0 ||
-           strcasecmp(hdr->name, "In-Reply-To") == 0 ||
-           strcasecmp(hdr->name, "Message-ID") == 0) {
+       if (ctx->custom_header || strcasecmp(hdr->name, "Date") == 0) {
                ctx->hdr = hdr;
 
                ctx->custom_header = FALSE;
@@ -649,17 +520,23 @@ static int search_arg_match_text(struct mail_search_arg *args,
                                 struct mail_search_context *ctx)
 {
        struct istream *input;
-       int have_headers, have_body, have_text;
+       const char *const *headers;
+       int have_headers, have_body;
 
        /* first check what we need to use */
-       mail_search_args_analyze(args, &have_headers, &have_body, &have_text);
-       if (!have_headers && !have_body && !have_text)
+       headers = mail_search_args_analyze(args, &have_headers, &have_body);
+       if (!have_headers && !have_body)
                return TRUE;
 
-       if (have_headers || have_text) {
+       if (have_headers) {
                struct search_header_context hdr_ctx;
 
-               input = ctx->mail->get_stream(ctx->mail, NULL, NULL);
+               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;
 
@@ -668,7 +545,7 @@ static int search_arg_match_text(struct mail_search_arg *args,
                hdr_ctx.custom_header = TRUE;
                hdr_ctx.args = args;
 
-               index_mail_init_parse_header(&ctx->imail);
+               index_mail_parse_header_init(&ctx->imail, headers);
                message_parse_header(NULL, input, NULL,
                                     search_header, &hdr_ctx);
        } else {
@@ -681,7 +558,7 @@ static int search_arg_match_text(struct mail_search_arg *args,
                i_stream_seek(input, hdr_size.physical_size);
        }
 
-       if (have_text || have_body) {
+       if (have_body) {
                struct search_body_context body_ctx;
 
                memset(&body_ctx, 0, sizeof(body_ctx));
@@ -967,6 +844,11 @@ int index_storage_search_deinit(struct mail_search_context *ctx)
                        ret = FALSE;
        }
 
+       if (ctx->ibox->fetch_mail.pool != NULL)
+               index_mail_deinit(&ctx->ibox->fetch_mail);
+       if (ctx->imail.pool != NULL)
+               index_mail_deinit(&ctx->imail);
+
        if (!index_storage_lock(ctx->ibox, MAIL_LOCK_UNLOCK))
                ret = FALSE;
 
@@ -978,18 +860,42 @@ int index_storage_search_deinit(struct mail_search_context *ctx)
        if (ctx->hdr_pool != NULL)
                pool_unref(ctx->hdr_pool);
 
-       if (ctx->ibox->fetch_mail.pool != NULL)
-               index_mail_deinit(&ctx->ibox->fetch_mail);
-        index_mail_deinit(&ctx->imail);
        i_free(ctx);
        return ret;
 }
 
+static int search_match_next(struct mail_search_context *ctx)
+{
+        struct mail_search_arg *arg;
+       int ret;
+
+       /* check the index matches first */
+       mail_search_args_reset(ctx->args);
+       ret = mail_search_args_foreach(ctx->args, search_index_arg, ctx);
+       if (ret >= 0)
+               return ret > 0;
+
+       /* next search only from cached arguments */
+       ret = mail_search_args_foreach(ctx->args, search_cached_arg, ctx);
+       if (ret >= 0)
+               return ret > 0;
+
+       /* open the mail file and check the rest */
+       if (!search_arg_match_text(ctx->args, ctx))
+               return FALSE;
+
+       for (arg = ctx->args; arg != NULL; arg = arg->next) {
+               if (arg->result != 1)
+                       return FALSE;
+       }
+
+       return TRUE;
+}
+
 struct mail *index_storage_search_next(struct mail_search_context *ctx)
 {
        const struct messageset_mail *msgset_mail;
-        struct mail_search_arg *arg;
-       int found, ret;
+       int ret;
 
        if (ctx->msgset_ctx == NULL) {
                /* initialization failed or didn't found any messages */
@@ -998,47 +904,35 @@ struct mail *index_storage_search_next(struct mail_search_context *ctx)
 
        do {
                msgset_mail = index_messageset_next(ctx->msgset_ctx);
-               if (msgset_mail == NULL)
-                       return NULL;
+               if (msgset_mail == NULL) {
+                       ret = -1;
+                       break;
+               }
 
                ctx->mail->seq = msgset_mail->client_seq;
                ctx->mail->uid = msgset_mail->rec->uid;
-               ret = index_mail_next(&ctx->imail, msgset_mail->rec,
-                                     msgset_mail->idx_seq);
-
-               if (ret < 0)
-                       return NULL;
-
-               if (ret == 0)
-                       found = FALSE;
-               else {
-                       mail_search_args_reset(ctx->args);
 
-                       t_push();
-
-                       mail_search_args_foreach(ctx->args, search_index_arg,
-                                                ctx);
-                       mail_search_args_foreach(ctx->args, search_cached_arg,
-                                                ctx);
-                       mail_search_args_foreach(ctx->args, search_envelope_arg,
-                                                ctx);
-                       found = search_arg_match_text(ctx->args, ctx);
+               ret = index_mail_next(&ctx->imail, msgset_mail->rec,
+                                     msgset_mail->idx_seq, TRUE);
+               if (ret <= 0) {
+                       if (ret < 0)
+                               break;
+                       continue;
+               }
 
-                       t_pop();
+               t_push();
+               ret = search_match_next(ctx);
+               t_pop();
 
-                       if (ctx->error != NULL)
-                               return NULL;
-               }
+               if (ctx->error != NULL)
+                       ret = -1;
+       } while (ret == 0);
 
-               if (found) {
-                       for (arg = ctx->args; arg != NULL; arg = arg->next) {
-                               if (arg->result != 1) {
-                                       found = FALSE;
-                                       break;
-                               }
-                       }
-               }
-       } while (!found);
+       if (ret < 0) {
+               /* error or last record */
+               index_mail_deinit(&ctx->imail);
+               return NULL;
+       }
 
        return ctx->mail;
 }
index f5d31b81d770422a41ce907e816ab78cadb80865..f805cdb5dd7a3abbe72e5dfe4913a14993e3ba8d 100644 (file)
@@ -149,20 +149,28 @@ void index_storage_destroy_unrefed(void)
        destroy_unrefed(TRUE);
 }
 
-static enum mail_data_field get_data_fields(const char *fields)
+static enum mail_cache_field get_cache_fields(const char *fields)
 {
+       static enum mail_cache_field field_masks[] = {
+               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[] = {
-               "Location",
-               "Envelope",
-               "Body",
-               "Bodystructure",
-               "MD5",
-               "MessagePart",
+               "sent_date",
+               "received_date",
+               "virtual_size",
+               "body",
+               "bodystructure",
+               "messagepart",
                NULL
        };
 
        const char *const *arr;
-       enum mail_data_field ret;
+       enum mail_cache_field ret;
        int i;
 
        if (fields == NULL || *fields == '\0')
@@ -175,7 +183,7 @@ static enum mail_data_field get_data_fields(const char *fields)
 
                for (i = 0; field_names[i] != NULL; i++) {
                        if (strcasecmp(field_names[i], *arr) == 0) {
-                               ret |= 1 << i;
+                               ret |= field_masks[i];
                                break;
                        }
                }
@@ -188,28 +196,28 @@ static enum mail_data_field get_data_fields(const char *fields)
        return ret;
 }
 
-static enum mail_data_field get_default_cache_fields(void)
+static enum mail_cache_field get_default_cache_fields(void)
 {
-       static enum mail_data_field ret = 0;
+       static enum mail_cache_field ret = 0;
        static int ret_set = FALSE;
 
        if (ret_set)
                return ret;
 
-       ret = get_data_fields(getenv("MAIL_CACHE_FIELDS"));
+       ret = get_cache_fields(getenv("MAIL_CACHE_FIELDS"));
        ret_set = TRUE;
        return ret;
 }
 
-static enum mail_data_field get_never_cache_fields(void)
+static enum mail_cache_field get_never_cache_fields(void)
 {
-       static enum mail_data_field ret = 0;
+       static enum mail_cache_field ret = 0;
        static int ret_set = FALSE;
 
        if (ret_set)
                return ret;
 
-       ret = get_data_fields(getenv("MAIL_NEVER_CACHE_FIELDS"));
+       ret = get_cache_fields(getenv("MAIL_NEVER_CACHE_FIELDS"));
        ret_set = TRUE;
        return ret;
 }
@@ -282,9 +290,16 @@ void index_storage_init_lock_notify(struct index_mailbox *ibox)
 int index_storage_lock(struct index_mailbox *ibox,
                       enum mail_lock_type lock_type)
 {
-       int ret;
+       int ret = TRUE;
 
        if (lock_type == MAIL_LOCK_UNLOCK) {
+               if (ibox->trans_ctx != NULL) {
+                       if (!mail_cache_transaction_commit(ibox->trans_ctx))
+                               ret = FALSE;
+                       if (!mail_cache_transaction_end(ibox->trans_ctx))
+                               ret = FALSE;
+                       ibox->trans_ctx = NULL;
+               }
                if (ibox->lock_type != MAILBOX_LOCK_UNLOCK)
                        return TRUE;
        } else {
@@ -295,7 +310,8 @@ int index_storage_lock(struct index_mailbox *ibox,
        /* we have to set/reset this every time, because the same index
           may be used by multiple IndexMailboxes. */
         index_storage_init_lock_notify(ibox);
-       ret = ibox->index->set_lock(ibox->index, lock_type);
+       if (!ibox->index->set_lock(ibox->index, lock_type))
+               ret = FALSE;
        ibox->index->set_lock_notify_callback(ibox->index, NULL, NULL);
 
        if (!ret)
@@ -337,14 +353,13 @@ index_storage_mailbox_init(struct mail_storage *storage, struct mailbox *box,
 
                if (!index->opened) {
                        /* open the index first */
-                       index->default_cache_fields =
-                               get_default_cache_fields();
-                       index->never_cache_fields =
-                               get_never_cache_fields();
-
                        if (!index->open(index, index_flags))
                                break;
 
+                       mail_cache_set_defaults(index->cache,
+                                               get_default_cache_fields(),
+                                               get_never_cache_fields());
+
                        if (INDEX_IS_IN_MEMORY(index) &&
                            storage->index_dir != NULL) {
                                storage->callbacks->notify_no(&ibox->box,
index 8fa90e39e6fb26a21faead9e2bf2a666ea86e8a5..43dce79d34fd6dc71574a5ae79639fe40f8210af 100644 (file)
@@ -21,6 +21,7 @@ struct index_mailbox {
 
        struct mail_index *index;
         enum mailbox_lock_type lock_type;
+       struct mail_cache_transaction_ctx *trans_ctx;
 
        struct timeout *autosync_to;
         struct index_autosync_file *autosync_files;
@@ -90,6 +91,7 @@ int index_storage_sync(struct mailbox *box, enum mail_sync_flags flags);
 struct mail_fetch_context *
 index_storage_fetch_init(struct mailbox *box,
                         enum mail_fetch_field wanted_fields,
+                        const char *const *wanted_headers,
                         const char *messageset, int uidset);
 int index_storage_fetch_deinit(struct mail_fetch_context *ctx, int *all_found);
 struct mail *index_storage_fetch_next(struct mail_fetch_context *ctx);
index c9f5d9346b27a50146fa1e9d019f77e98b93c7d5..aeae66d5fa6b327f298a0b70b371466727e8c24a 100644 (file)
@@ -31,12 +31,13 @@ static int maildir_hardlink_file(struct mail_index *index,
                                 const char **fname, const char *new_path)
 {
        const char *path;
+       int new_dir;
 
-       *fname = maildir_get_location(index, rec);
+       *fname = maildir_get_location(index, rec, &new_dir);
        if (*fname == NULL)
                return -1;
 
-       if ((rec->index_flags & INDEX_MAIL_FLAG_MAILDIR_NEW) != 0) {
+       if (new_dir) {
                /* probably in new/ dir */
                path = t_strconcat(index->mailbox_path, "/new/", *fname, NULL);
                if (link(path, new_path) == 0)
index ce10a32196dc9d3b25d936dae92ac3384b34af24..b10e1904589b955f7de55c52ba8d95449d7f9c35 100644 (file)
@@ -1,6 +1,7 @@
 /* Copyright (C) 2002 Timo Sirainen */
 
 #include "lib.h"
+#include "buffer.h"
 #include "mail-search.h"
 
 void mail_search_args_reset(struct mail_search_arg *args)
@@ -93,9 +94,11 @@ int mail_search_args_foreach(struct mail_search_arg *args,
        return result;
 }
 
-static void search_arg_analyze(struct mail_search_arg *arg, int *have_headers,
-                              int *have_body, int *have_text)
+static void
+search_arg_analyze(struct mail_search_arg *arg, buffer_t *headers,
+                  int *have_headers, int *have_body, int *have_text)
 {
+       static const char *date_hdr = "Date";
        struct mail_search_arg *subarg;
 
        if (arg->result != -1)
@@ -107,8 +110,9 @@ static void search_arg_analyze(struct mail_search_arg *arg, int *have_headers,
                subarg = arg->value.subargs;
                while (subarg != NULL) {
                        if (subarg->result == -1) {
-                               search_arg_analyze(subarg, have_headers,
-                                                  have_body, have_text);
+                               search_arg_analyze(subarg, headers,
+                                                  have_headers, have_body,
+                                                  have_text);
                        }
 
                        subarg = subarg->next;
@@ -117,14 +121,13 @@ static void search_arg_analyze(struct mail_search_arg *arg, int *have_headers,
        case SEARCH_SENTBEFORE:
        case SEARCH_SENTON:
        case SEARCH_SENTSINCE:
-       case SEARCH_FROM:
-       case SEARCH_TO:
-       case SEARCH_CC:
-       case SEARCH_BCC:
-       case SEARCH_SUBJECT:
-       case SEARCH_IN_REPLY_TO:
-       case SEARCH_MESSAGE_ID:
+               *have_headers = TRUE;
+               buffer_append(headers, &date_hdr, sizeof(const char *));
+               break;
        case SEARCH_HEADER:
+       case SEARCH_HEADER_ADDRESS:
+               buffer_append(headers, &arg->hdr_field_name,
+                             sizeof(const char *));
                *have_headers = TRUE;
                break;
        case SEARCH_BODY:
@@ -138,12 +141,25 @@ static void search_arg_analyze(struct mail_search_arg *arg, int *have_headers,
        }
 }
 
-void mail_search_args_analyze(struct mail_search_arg *args, int *have_headers,
-                             int *have_body, int *have_text)
+const char *const *
+mail_search_args_analyze(struct mail_search_arg *args,
+                        int *have_headers, int *have_body)
 {
-       *have_headers = *have_body = *have_text = FALSE;
+       const char *null = NULL;
+       buffer_t *headers;
+       int have_text;
 
-       for (; args != NULL; args = args->next)
-               search_arg_analyze(args, have_headers, have_body, have_text);
-}
+       *have_headers = *have_body = have_text = FALSE;
+
+       headers = buffer_create_dynamic(data_stack_pool, 128, (size_t)-1);
+       for (; args != NULL; args = args->next) {
+               search_arg_analyze(args, headers, have_headers,
+                                  have_body, &have_text);
+       }
 
+       if (!have_headers || have_text)
+               return NULL;
+
+       buffer_append(headers, &null, sizeof(const char *));
+       return buffer_get_data(headers, NULL);
+}
index a46def3fdca06d46a3a6f42eaf1c54c0111472dd..90a2c99baa04a94b3581cb8d288795fae598b1e1 100644 (file)
@@ -32,20 +32,12 @@ enum mail_search_arg_type {
        SEARCH_LARGER,
 
        /* headers */
-       SEARCH_FROM,
-       SEARCH_TO,
-       SEARCH_CC,
-       SEARCH_BCC,
-       SEARCH_SUBJECT,
        SEARCH_HEADER,
+       SEARCH_HEADER_ADDRESS,
 
        /* body */
        SEARCH_BODY,
-       SEARCH_TEXT,
-
-       /* our shortcuts for headers */
-        SEARCH_IN_REPLY_TO,
-        SEARCH_MESSAGE_ID
+       SEARCH_TEXT
 };
 
 struct mail_search_arg {
@@ -58,7 +50,7 @@ struct mail_search_arg {
        } value;
 
         void *context;
-       const char *hdr_field_name; /* for SEARCH_HEADER */
+       const char *hdr_field_name; /* for SEARCH_HEADER* */
        unsigned int not:1;
 
        int result; /* -1 = unknown, 0 = unmatched, 1 = matched */
@@ -82,9 +74,11 @@ int mail_search_args_foreach(struct mail_search_arg *args,
                             mail_search_foreach_callback_t callback,
                             void *context);
 
-/* Fills have_headers, have_body and have_text based on if such search
-   argument exists that needs to be checked. */
-void mail_search_args_analyze(struct mail_search_arg *args, int *have_headers,
-                             int *have_body, int *have_text);
+/* Fills have_headers and have_body based on if such search argument exists
+   that needs to be checked. Returns the headers that we're searching for, or
+   NULL if we're searching for TEXT. */
+const char *const *
+mail_search_args_analyze(struct mail_search_arg *args,
+                        int *have_headers, int *have_body);
 
 #endif
index 2e5e46b5632321e23d3ff9479bd65d34e177a3c5..8e60235db2ea9ea5478edaabbd4f6fe229b034c8 100644 (file)
@@ -267,6 +267,7 @@ struct mailbox {
        struct mail_fetch_context *
                (*fetch_init)(struct mailbox *box,
                              enum mail_fetch_field wanted_fields,
+                             const char *const *wanted_headers,
                              const char *messageset, int uidset);
        /* Deinitialize fetch request. all_found is set to TRUE if all of the
           fetched messages were found (ie. not just deleted). */
@@ -377,6 +378,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 the parsed address for given header field. */
        const struct message_address *(*get_address)(struct mail *mail,
index f66c7db80f268875455937dad6c56a3ed693582d..906a113223fd6ccb9a3ad1f5fa9b4145813bc2a4 100644 (file)
@@ -56,11 +56,13 @@ static void _auto_sync(struct mailbox *box, enum mailbox_sync_type sync_type,
 
 static struct mail_fetch_context *
 _fetch_init(struct mailbox *box, enum mail_fetch_field wanted_fields,
+           const char *const *wanted_headers,
            const char *messageset, int uidset)
 {
        struct proxy_mailbox *p = (struct proxy_mailbox *) box;
 
-       return p->box->fetch_init(p->box, wanted_fields, messageset, uidset);
+       return p->box->fetch_init(p->box, wanted_fields, wanted_headers,
+                                 messageset, uidset);
 }
 
 static struct mail *_fetch_uid(struct mailbox *box, unsigned int uid,
index bd8b79e6e798f17904f7df1686a974eb93341408..a269f794632c1f2b13a4ece915877a59b3eb5fa1 100644 (file)
@@ -62,7 +62,7 @@ static int init_mailbox(struct client *client)
        messageset = t_strdup_printf("1:%u", client->messages_count);
        for (i = 0; i < 2; i++) {
                ctx = client->mailbox->fetch_init(client->mailbox,
-                                                 MAIL_FETCH_SIZE,
+                                                 MAIL_FETCH_SIZE, NULL,
                                                  messageset, FALSE);
                if (ctx == NULL) {
                        client_send_storage_error(client);
index 05e4c4b9dec531201ca781549216ba37e8a13705..45b4ea226f41d69493b6d686633cdbf37068e67d 100644 (file)
@@ -261,7 +261,7 @@ static void fetch(struct client *client, unsigned int msgnum,
 
        ctx = client->mailbox->fetch_init(client->mailbox,
                                          MAIL_FETCH_STREAM_HEADER |
-                                         MAIL_FETCH_STREAM_BODY,
+                                         MAIL_FETCH_STREAM_BODY, NULL,
                                          dec2str(msgnum+1), FALSE);
        if (ctx == NULL) {
                client_send_storage_error(client);
@@ -345,7 +345,7 @@ static void list_uids(struct client *client, unsigned int message)
                t_strdup_printf("1:%u", client->messages_count) :
                t_strdup_printf("%u", message);
 
-       ctx = client->mailbox->fetch_init(client->mailbox, 0,
+       ctx = client->mailbox->fetch_init(client->mailbox, 0, NULL,
                                          messageset, FALSE);
        if (ctx == NULL) {
                client_send_storage_error(client);