Mercurial > dovecot > core-2.2
changeset 7629:bad3a811a148 HEAD
Added QRESYNC support.
line wrap: on
line diff
--- a/src/imap/client.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/client.h Sat Mar 15 16:24:26 2008 +0200 @@ -92,6 +92,7 @@ unsigned int destroyed:1; unsigned int handling_input:1; unsigned int syncing:1; + unsigned int selecting:1; unsigned int input_skip_line:1; /* skip all the data until we've found a new line */ };
--- a/src/imap/cmd-close.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-close.c Sat Mar 15 16:24:26 2008 +0200 @@ -9,6 +9,8 @@ struct client *client = cmd->client; struct mailbox *mailbox = client->mailbox; struct mail_storage *storage; + struct mailbox_status status; + bool show_highestmodseq; if (!client_verify_open_mailbox(cmd)) return TRUE; @@ -16,15 +18,25 @@ storage = mailbox_get_storage(mailbox); client->mailbox = NULL; + show_highestmodseq = + (cmd->client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0; + if (!imap_expunge(mailbox, NULL)) client_send_untagged_storage_error(client, storage); - else if (mailbox_sync(mailbox, 0, 0, NULL) < 0) + else if (mailbox_sync(mailbox, 0, !show_highestmodseq ? 0 : + STATUS_HIGHESTMODSEQ, &status) < 0) client_send_untagged_storage_error(client, storage); if (mailbox_close(&mailbox) < 0) client_send_untagged_storage_error(client, storage); client_update_mailbox_flags(client, NULL); - client_send_tagline(cmd, "OK Close completed."); + if (!show_highestmodseq) + client_send_tagline(cmd, "OK Close completed."); + else { + client_send_tagline(cmd, t_strdup_printf( + "OK [HIGHESTMODSEQ %llu] Close completed.", + (unsigned long long)status.highest_modseq)); + } return TRUE; }
--- a/src/imap/cmd-copy.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-copy.c Sat Mar 15 16:24:26 2008 +0200 @@ -109,7 +109,7 @@ if (!client_verify_mailbox_name(cmd, mailbox, TRUE, FALSE)) return TRUE; - search_arg = imap_search_get_arg(cmd, messageset, cmd->uid); + search_arg = imap_search_get_seqset(cmd, messageset, cmd->uid); if (search_arg == NULL) return TRUE;
--- a/src/imap/cmd-enable.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-enable.c Sat Mar 15 16:24:26 2008 +0200 @@ -24,6 +24,11 @@ client_enable(cmd->client, MAILBOX_FEATURE_CONDSTORE); str_append(reply, " CONDSTORE"); } + else if (strcmp(str, "QRESYNC") == 0) { + client_enable(cmd->client, MAILBOX_FEATURE_QRESYNC | + MAILBOX_FEATURE_CONDSTORE); + str_append(reply, " QRESYNC"); + } } if (str_len(reply) > 9) client_send_line(cmd->client, str_c(reply));
--- a/src/imap/cmd-expunge.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-expunge.c Sat Mar 15 16:24:26 2008 +0200 @@ -5,6 +5,29 @@ #include "imap-search.h" #include "imap-expunge.h" +static bool cmd_expunge_callback(struct client_command_context *cmd) +{ + struct mailbox_status status; + + if (cmd->client->sync_seen_deletes && !cmd->uid) { + /* Outlook workaround: session 1 set \Deleted flag and + session 2 tried to expunge without having seen it yet. + expunge again. */ + return cmd_expunge(cmd); + } + + if ((cmd->client->enabled_features & MAILBOX_FEATURE_QRESYNC) == 0) + client_send_tagline(cmd, "OK Expunge completed."); + else { + mailbox_get_status(cmd->client->mailbox, + STATUS_HIGHESTMODSEQ, &status); + client_send_tagline(cmd, t_strdup_printf( + "OK [HIGHESTMODSEQ %llu] Expunge completed.", + (unsigned long long)status.highest_modseq)); + } + return TRUE; +} + bool cmd_uid_expunge(struct client_command_context *cmd) { struct client *client = cmd->client; @@ -24,13 +47,13 @@ return TRUE; } - search_arg = imap_search_get_arg(cmd, uidset, TRUE); + search_arg = imap_search_get_seqset(cmd, uidset, TRUE); if (search_arg == NULL) return TRUE; if (imap_expunge(client->mailbox, search_arg)) { - return cmd_sync(cmd, 0, IMAP_SYNC_FLAG_SAFE, - "OK Expunge completed."); + return cmd_sync_callback(cmd, 0, IMAP_SYNC_FLAG_SAFE, + cmd_expunge_callback); } else { client_send_storage_error(cmd, mailbox_get_storage(client->mailbox)); @@ -38,19 +61,6 @@ } } -static bool cmd_expunge_callback(struct client_command_context *cmd) -{ - if (cmd->client->sync_seen_deletes) { - /* Outlook workaround: session 1 set \Deleted flag and - session 2 tried to expunge without having seen it yet. - expunge again. */ - return cmd_expunge(cmd); - } - - client_send_tagline(cmd, "OK Expunge completed."); - return TRUE; -} - bool cmd_expunge(struct client_command_context *cmd) { struct client *client = cmd->client;
--- a/src/imap/cmd-fetch.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-fetch.c Sat Mar 15 16:24:26 2008 +0200 @@ -69,20 +69,34 @@ } static bool -fetch_add_unchanged_since(struct imap_fetch_context *ctx, uint64_t modseq) +fetch_parse_modifier(struct imap_fetch_context *ctx, + const char *name, const struct imap_arg **args) { - struct mail_search_arg *search_arg; + unsigned long long num; - search_arg = p_new(ctx->cmd->pool, struct mail_search_arg, 1); - search_arg->type = SEARCH_MODSEQ; - search_arg->value.modseq = - p_new(ctx->cmd->pool, struct mail_search_modseq, 1); - search_arg->value.modseq->modseq = modseq; + if (strcmp(name, "CHANGEDSINCE") == 0) { + if ((*args)->type != IMAP_ARG_ATOM) { + client_send_command_error(ctx->cmd, + "Invalid CHANGEDSINCE modseq."); + return FALSE; + } + num = strtoull(imap_arg_string(*args), NULL, 10); + *args += 1; + return imap_fetch_add_unchanged_since(ctx, num); + } + if (strcmp(name, "VANISHED") == 0 && ctx->cmd->uid) { + if ((ctx->client->enabled_features & + MAILBOX_FEATURE_QRESYNC) == 0) { + client_send_command_error(ctx->cmd, + "QRESYNC not enabled"); + return FALSE; + } + ctx->send_vanished = TRUE; + return TRUE; + } - search_arg->next = ctx->search_args->next; - ctx->search_args->next = search_arg; - - return imap_fetch_init_handler(ctx, "MODSEQ", NULL); + client_send_command_error(ctx->cmd, "Unknown FETCH modifier"); + return FALSE; } static bool @@ -90,26 +104,24 @@ const struct imap_arg *args) { const char *name; - unsigned long long num; - for (; args->type != IMAP_ARG_EOL; args++) { - if (args->type != IMAP_ARG_ATOM || - args[1].type != IMAP_ARG_ATOM) { + while (args->type != IMAP_ARG_EOL) { + if (args->type != IMAP_ARG_ATOM) { client_send_command_error(ctx->cmd, "FETCH modifiers contain non-atoms."); return FALSE; } - name = IMAP_ARG_STR(args); - if (strcasecmp(name, "CHANGEDSINCE") == 0) { - args++; - num = strtoull(imap_arg_string(args), NULL, 10); - if (!fetch_add_unchanged_since(ctx, num)) - return FALSE; - } else { - client_send_command_error(ctx->cmd, - "Unknown FETCH modifier"); + name = t_str_ucase(IMAP_ARG_STR(args)); + args++; + if (!fetch_parse_modifier(ctx, name, &args)) return FALSE; - } + } + if (ctx->send_vanished && + (ctx->search_args->next == NULL || + ctx->search_args->next->type != SEARCH_MODSEQ)) { + client_send_command_error(ctx->cmd, + "VANISHED used without CHANGEDSINCE"); + return FALSE; } return TRUE; } @@ -150,15 +162,11 @@ static bool cmd_fetch_continue(struct client_command_context *cmd) { struct imap_fetch_context *ctx = cmd->context; - int ret; - if ((ret = imap_fetch(ctx)) == 0) { + if (imap_fetch_more(ctx) == 0) { /* unfinished */ return FALSE; } - if (ret < 0) - ctx->failed = TRUE; - return cmd_fetch_finish(ctx); } @@ -168,7 +176,6 @@ const struct imap_arg *args; struct mail_search_arg *search_arg; const char *messageset; - int ret; if (!client_read_args(cmd, 0, 0, &args)) return FALSE; @@ -185,11 +192,11 @@ return TRUE; } - search_arg = imap_search_get_arg(cmd, messageset, cmd->uid); + search_arg = imap_search_get_anyset(cmd, messageset, cmd->uid); if (search_arg == NULL) return TRUE; - ctx = imap_fetch_init(cmd); + ctx = imap_fetch_init(cmd, cmd->client->mailbox); if (ctx == NULL) return TRUE; ctx->search_args = search_arg; @@ -201,17 +208,15 @@ return TRUE; } - imap_fetch_begin(ctx); - if ((ret = imap_fetch(ctx)) == 0) { - /* unfinished */ - cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; + if (imap_fetch_begin(ctx) == 0) { + if (imap_fetch_more(ctx) == 0) { + /* unfinished */ + cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; - cmd->func = cmd_fetch_continue; - cmd->context = ctx; - return FALSE; + cmd->func = cmd_fetch_continue; + cmd->context = ctx; + return FALSE; + } } - if (ret < 0) - ctx->failed = TRUE; - return cmd_fetch_finish(ctx); }
--- a/src/imap/cmd-search.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-search.c Sat Mar 15 16:24:26 2008 +0200 @@ -5,6 +5,7 @@ #include "str.h" #include "commands.h" #include "mail-search.h" +#include "mail-search-build.h" #include "imap-search.h" #define OUTBUF_SIZE 65536
--- a/src/imap/cmd-select.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-select.c Sat Mar 15 16:24:26 2008 +0200 @@ -1,79 +1,283 @@ /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ #include "common.h" +#include "seq-range-array.h" #include "commands.h" +#include "mail-search.h" +#include "imap-messageset.h" +#include "imap-fetch.h" #include "imap-sync.h" -bool cmd_select_full(struct client_command_context *cmd, bool readonly) -{ - struct client *client = cmd->client; +#include <stdlib.h> + +struct imap_select_context { + struct client_command_context *cmd; struct mail_storage *storage; struct mailbox *box; - struct mailbox_status status; - enum mailbox_open_flags open_flags = 0; - const struct imap_arg *args, *list_args; - const char *mailbox, *str; + + struct imap_fetch_context *fetch_ctx; + + uint32_t qresync_uid_validity; + uint64_t qresync_modseq; + ARRAY_TYPE(seq_range) qresync_known_uids; + ARRAY_TYPE(uint32_t) qresync_sample_seqset; + ARRAY_TYPE(uint32_t) qresync_sample_uidset; +}; + +static int select_qresync_get_uids(struct imap_select_context *ctx, + const ARRAY_TYPE(seq_range) *seqset, + const ARRAY_TYPE(seq_range) *uidset) +{ + const struct seq_range *seq_range, *uid_range; + struct seq_range_iter seq_iter; + unsigned int i, seq_count, uid_count, diff, n = 0; + uint32_t seq; + + /* change all n:m ranges to n,m and store the results */ + seq_range = array_get(seqset, &seq_count); + uid_range = array_get(uidset, &uid_count); + + seq_range_array_iter_init(&seq_iter, seqset); + i_array_init(&ctx->qresync_sample_uidset, uid_count); + i_array_init(&ctx->qresync_sample_seqset, uid_count); + for (i = 0; i < uid_count; i++) { + if (!seq_range_array_iter_nth(&seq_iter, n++, &seq)) + return -1; + array_append(&ctx->qresync_sample_uidset, + &uid_range[i].seq1, 1); + array_append(&ctx->qresync_sample_seqset, &seq, 1); - /* <mailbox> [(CONDSTORE)] */ - if (!client_read_args(cmd, 0, 0, &args)) + diff = uid_range[i].seq2 - uid_range[i].seq1; + if (diff > 0) { + n += diff - 1; + if (!seq_range_array_iter_nth(&seq_iter, n++, &seq)) + return -1; + + array_append(&ctx->qresync_sample_uidset, + &uid_range[i].seq2, 1); + array_append(&ctx->qresync_sample_seqset, &seq, 1); + } + } + if (seq_range_array_iter_nth(&seq_iter, n, &seq)) + return -1; + return 0; +} + +static bool +select_parse_qresync(struct imap_select_context *ctx, + const struct imap_arg *args) +{ + ARRAY_TYPE(seq_range) seqset, uidset; + unsigned int count; + + if ((ctx->cmd->client->enabled_features & + MAILBOX_FEATURE_QRESYNC) == 0) { + client_send_command_error(ctx->cmd, "QRESYNC not enabled"); return FALSE; + } + if (args->type != IMAP_ARG_LIST) { + client_send_command_error(ctx->cmd, + "QRESYNC parameters missing"); + return FALSE; + } + args = IMAP_ARG_LIST_ARGS(args); + for (count = 0; args[count].type != IMAP_ARG_EOL; count++) ; - if (!IMAP_ARG_TYPE_IS_STRING(args[0].type)) { - client_send_command_error(cmd, "Invalid arguments."); + if (count < 2 || count > 4 || + args[0].type != IMAP_ARG_ATOM || + args[1].type != IMAP_ARG_ATOM || + (count > 2 && args[2].type != IMAP_ARG_ATOM) || + (count > 3 && args[3].type != IMAP_ARG_LIST)) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC parameters"); return FALSE; } - mailbox = IMAP_ARG_STR(&args[0]); + ctx->qresync_uid_validity = + strtoul(IMAP_ARG_STR_NONULL(&args[0]), NULL, 10); + ctx->qresync_modseq = + strtoull(IMAP_ARG_STR_NONULL(&args[1]), NULL, 10); + if (count > 2) { + i_array_init(&ctx->qresync_known_uids, 64); + if (imap_messageset_parse(&ctx->qresync_known_uids, + IMAP_ARG_STR_NONULL(&args[2])) < 0) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC known-uids"); + return FALSE; + } + } else { + i_array_init(&ctx->qresync_known_uids, 64); + seq_range_array_add_range(&ctx->qresync_known_uids, + 1, (uint32_t)-1); + } + if (count > 3) { + args = IMAP_ARG_LIST_ARGS(&args[3]); + if (args[0].type != IMAP_ARG_ATOM || + args[1].type != IMAP_ARG_ATOM || + args[2].type != IMAP_ARG_EOL) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC known set parameters"); + return FALSE; + } + t_array_init(&seqset, 32); + if (imap_messageset_parse(&seqset, + IMAP_ARG_STR_NONULL(&args[0])) < 0) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC known-sequence-set"); + return FALSE; + } + t_array_init(&uidset, 32); + if (imap_messageset_parse(&uidset, + IMAP_ARG_STR_NONULL(&args[1])) < 0) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC known-uid-set"); + return FALSE; + } + if (select_qresync_get_uids(ctx, &seqset, &uidset) < 0) { + client_send_command_error(ctx->cmd, + "Invalid QRESYNC sets"); + return FALSE; + } + } + return TRUE; +} + +static bool +select_parse_options(struct imap_select_context *ctx, + const struct imap_arg *args) +{ + const char *name; + + while (args->type != IMAP_ARG_EOL) { + if (args->type != IMAP_ARG_ATOM) { + client_send_command_error(ctx->cmd, + "SELECT options contain non-atoms."); + return FALSE; + } + name = t_str_ucase(IMAP_ARG_STR(args)); + args++; - if (args[1].type == IMAP_ARG_LIST) { - list_args = IMAP_ARG_LIST_ARGS(&args[1]); - for (; list_args->type != IMAP_ARG_EOL; list_args++) { - str = imap_arg_string(list_args); - if (str != NULL && strcasecmp(str, "CONDSTORE") == 0) { - client_enable(client, - MAILBOX_FEATURE_CONDSTORE); - } + if (strcmp(name, "CONDSTORE") == 0) + client_enable(ctx->cmd->client, + MAILBOX_FEATURE_CONDSTORE); + else if (strcmp(name, "QRESYNC") == 0) { + if (!select_parse_qresync(ctx, args)) + return FALSE; + args++; + } else { + client_send_command_error(ctx->cmd, + "Unknown FETCH modifier"); + return FALSE; + } + } + return TRUE; +} + +static void select_context_free(struct imap_select_context *ctx) +{ + if (array_is_created(&ctx->qresync_known_uids)) + array_free(&ctx->qresync_known_uids); + if (array_is_created(&ctx->qresync_sample_seqset)) + array_free(&ctx->qresync_sample_seqset); + if (array_is_created(&ctx->qresync_sample_uidset)) + array_free(&ctx->qresync_sample_uidset); +} + +static void cmd_select_finish(struct imap_select_context *ctx, int ret) +{ + if (ret < 0) { + if (ctx->box != NULL) + mailbox_close(&ctx->box); + client_send_storage_error(ctx->cmd, ctx->storage); + ctx->cmd->client->mailbox = NULL; + } else { + client_send_tagline(ctx->cmd, mailbox_is_readonly(ctx->box) ? + "OK [READ-ONLY] Select completed." : + "OK [READ-WRITE] Select completed."); + } + ctx->cmd->client->selecting = FALSE; + select_context_free(ctx); +} + +static bool cmd_select_continue(struct client_command_context *cmd) +{ + struct imap_select_context *ctx = cmd->context; + int ret; + + if ((ret = imap_fetch_more(ctx->fetch_ctx)) == 0) { + /* unfinished */ + return FALSE; + } + + ret = imap_fetch_deinit(ctx->fetch_ctx); + cmd_select_finish(ctx, ret); + return TRUE; +} + +static int select_qresync(struct imap_select_context *ctx) +{ + struct imap_fetch_context *fetch_ctx; + struct mail_search_arg *search_arg; + + search_arg = p_new(ctx->cmd->pool, struct mail_search_arg, 1); + search_arg->type = SEARCH_UIDSET; + search_arg->value.seqset = ctx->qresync_known_uids; + + fetch_ctx = imap_fetch_init(ctx->cmd, ctx->box); + if (fetch_ctx == NULL) + return -1; + + fetch_ctx->search_args = search_arg; + fetch_ctx->send_vanished = TRUE; + fetch_ctx->qresync_sample_seqset = &ctx->qresync_sample_seqset; + fetch_ctx->qresync_sample_uidset = &ctx->qresync_sample_uidset; + + if (!imap_fetch_add_unchanged_since(fetch_ctx, ctx->qresync_modseq) || + !imap_fetch_init_handler(fetch_ctx, "UID", NULL) || + !imap_fetch_init_handler(fetch_ctx, "FLAGS", NULL) || + !imap_fetch_init_handler(fetch_ctx, "MODSEQ", NULL)) { + (void)imap_fetch_deinit(fetch_ctx); + return -1; + } + + if (imap_fetch_begin(fetch_ctx) == 0) { + if (imap_fetch_more(fetch_ctx) == 0) { + /* unfinished */ + ctx->fetch_ctx = fetch_ctx; + ctx->cmd->state = CLIENT_COMMAND_STATE_WAIT_OUTPUT; + + ctx->cmd->func = cmd_select_continue; + ctx->cmd->context = ctx; + return FALSE; } } - if (client->mailbox != NULL) { - box = client->mailbox; - client->mailbox = NULL; + return imap_fetch_deinit(fetch_ctx); +} - storage = mailbox_get_storage(box); - if (mailbox_close(&box) < 0) - client_send_untagged_storage_error(client, storage); - } - - storage = client_find_storage(cmd, &mailbox); - if (storage == NULL) - return TRUE; +static int +select_open(struct imap_select_context *ctx, const char *mailbox, bool readonly) +{ + struct client *client = ctx->cmd->client; + struct mailbox_status status; + enum mailbox_open_flags open_flags = 0; if (readonly) open_flags |= MAILBOX_OPEN_READONLY | MAILBOX_OPEN_KEEP_RECENT; - - box = mailbox_open(storage, mailbox, NULL, open_flags); - if (box == NULL) { - client_send_storage_error(cmd, storage); - return TRUE; - } + ctx->box = mailbox_open(ctx->storage, mailbox, NULL, open_flags); + if (ctx->box == NULL) + return -1; if (client->enabled_features != 0) - mailbox_enable(box, client->enabled_features); - if (mailbox_sync(box, MAILBOX_SYNC_FLAG_FULL_READ, + mailbox_enable(ctx->box, client->enabled_features); + if (mailbox_sync(ctx->box, MAILBOX_SYNC_FLAG_FULL_READ, STATUS_MESSAGES | STATUS_RECENT | STATUS_FIRST_UNSEEN_SEQ | STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_KEYWORDS | - STATUS_HIGHESTMODSEQ, &status) < 0) { - client_send_storage_error(cmd, storage); - mailbox_close(&box); - return TRUE; - } + STATUS_HIGHESTMODSEQ, &status) < 0) + return -1; - /* set client's mailbox only after getting status to make sure - we're not sending any expunge/exists replies too early to client */ - client->mailbox = box; + client->mailbox = ctx->box; client->select_counter++; - client->messages_count = status.messages; client->recent_count = status.recent; client->uidvalidity = status.uidvalidity; @@ -109,9 +313,65 @@ (unsigned long long)status.highest_modseq)); } - client_send_tagline(cmd, mailbox_is_readonly(box) ? - "OK [READ-ONLY] Select completed." : - "OK [READ-WRITE] Select completed."); + if (ctx->qresync_uid_validity == status.uidvalidity) { + if (select_qresync(ctx) < 0) + return -1; + } + return 0; +} + +bool cmd_select_full(struct client_command_context *cmd, bool readonly) +{ + struct client *client = cmd->client; + struct mailbox *box; + struct imap_select_context *ctx; + const struct imap_arg *args; + const char *mailbox; + int ret; + + /* <mailbox> [(optional parameters)] */ + if (!client_read_args(cmd, 0, 0, &args)) + return FALSE; + + if (!IMAP_ARG_TYPE_IS_STRING(args[0].type)) { + client_send_command_error(cmd, "Invalid arguments."); + return FALSE; + } + mailbox = IMAP_ARG_STR(&args[0]); + + ctx = p_new(cmd->pool, struct imap_select_context, 1); + ctx->cmd = cmd; + ctx->storage = client_find_storage(cmd, &mailbox); + if (ctx->storage == NULL) + return TRUE; + + if (args[1].type == IMAP_ARG_LIST) { + if (!select_parse_options(ctx, IMAP_ARG_LIST_ARGS(&args[1]))) { + select_context_free(ctx); + return TRUE; + } + } + + if (client->selecting) { + client_send_tagline(cmd, "Mailbox is already being selected"); + return TRUE; + } + client->selecting = TRUE; + + if (client->mailbox != NULL) { + box = client->mailbox; + client->mailbox = NULL; + + if (mailbox_close(&box) < 0) { + client_send_untagged_storage_error(client, + mailbox_get_storage(box)); + } + /* CLOSED response is required by QRESYNC */ + client_send_line(client, "* OK [CLOSED]"); + } + + ret = select_open(ctx, mailbox, readonly); + cmd_select_finish(ctx, ret); return TRUE; }
--- a/src/imap/cmd-store.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/cmd-store.c Sat Mar 15 16:24:26 2008 +0200 @@ -142,7 +142,7 @@ if (!store_parse_args(&ctx, args)) return TRUE; - search_arg = imap_search_get_arg(cmd, ctx.messageset, cmd->uid); + search_arg = imap_search_get_seqset(cmd, ctx.messageset, cmd->uid); if (search_arg == NULL) return TRUE;
--- a/src/imap/imap-fetch.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/imap-fetch.c Sat Mar 15 16:24:26 2008 +0200 @@ -9,6 +9,8 @@ #include "message-send.h" #include "message-size.h" #include "imap-date.h" +#include "mail-search.h" +#include "mail-search-build.h" #include "commands.h" #include "imap-fetch.h" #include "imap-util.h" @@ -81,7 +83,8 @@ return handler->init(ctx, name, args); } -struct imap_fetch_context *imap_fetch_init(struct client_command_context *cmd) +struct imap_fetch_context * +imap_fetch_init(struct client_command_context *cmd, struct mailbox *box) { struct client *client = cmd->client; struct imap_fetch_context *ctx; @@ -94,7 +97,7 @@ ctx = p_new(cmd->pool, struct imap_fetch_context, 1); ctx->client = client; ctx->cmd = cmd; - ctx->box = client->mailbox; + ctx->box = box; ctx->cur_str = str_new(default_pool, 8192); ctx->all_headers_buf = buffer_create_dynamic(cmd->pool, 128); @@ -105,6 +108,23 @@ return ctx; } +bool imap_fetch_add_unchanged_since(struct imap_fetch_context *ctx, + uint64_t modseq) +{ + struct mail_search_arg *search_arg; + + search_arg = p_new(ctx->cmd->pool, struct mail_search_arg, 1); + search_arg->type = SEARCH_MODSEQ; + search_arg->value.modseq = + p_new(ctx->cmd->pool, struct mail_search_modseq, 1); + search_arg->value.modseq->modseq = modseq; + + search_arg->next = ctx->search_args->next; + ctx->search_args->next = search_arg; + + return imap_fetch_init_handler(ctx, "MODSEQ", NULL); +} + #undef imap_fetch_add_handler void imap_fetch_add_handler(struct imap_fetch_context *ctx, bool buffered, bool want_deinit, @@ -153,11 +173,152 @@ } } -void imap_fetch_begin(struct imap_fetch_context *ctx) +static void +expunges_drop_known(struct imap_fetch_context *ctx, struct mail *mail, + ARRAY_TYPE(seq_range) *expunges) +{ + const uint32_t *seqs, *uids; + unsigned int i, count; + + seqs = array_get(ctx->qresync_sample_seqset, &count); + uids = array_idx(ctx->qresync_sample_uidset, 0); + i_assert(array_count(ctx->qresync_sample_uidset) == count); + i_assert(count > 0); + + /* FIXME: we could do removals from the middle as well */ + for (i = 0; i < count; i++) { + mail_set_seq(mail, seqs[i]); + if (uids[i] != mail->uid) + break; + } + if (i > 0) + seq_range_array_remove_range(expunges, 1, uids[i-1]); +} + +static int get_expunges_fallback(struct imap_fetch_context *ctx, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunges) +{ + struct mailbox_transaction_context *trans; + struct mail_search_arg search_arg; + struct mail_search_context *search_ctx; + struct mail *mail; + const struct seq_range *uid_range; + struct mailbox_status status; + unsigned int i, count; + uint32_t next_uid; + int ret = 0; + + uid_range = array_get(uids, &count); + i_assert(count > 0); + i = 0; + next_uid = uid_range[0].seq1; + + /* search UIDs in given range */ + memset(&search_arg, 0, sizeof(search_arg)); + search_arg.type = SEARCH_UIDSET; + i_array_init(&search_arg.value.seqset, array_count(uids)); + array_append_array(&search_arg.value.seqset, uids); + + trans = mailbox_transaction_begin(ctx->box, 0); + mail = mail_alloc(trans, 0, NULL); + search_ctx = mailbox_search_init(trans, NULL, &search_arg, NULL); + while (mailbox_search_next(search_ctx, mail) > 0) { + if (mail->uid == next_uid) { + if (next_uid < uid_range[i].seq2) + next_uid++; + else if (++i < count) + next_uid = uid_range[++i].seq1; + else + break; + } else { + /* next_uid .. mail->uid-1 are expunged */ + i_assert(mail->uid > next_uid); + while (mail->uid > uid_range[i].seq2) { + seq_range_array_add_range(expunges, next_uid, + uid_range[i].seq2); + i++; + i_assert(i < count); + next_uid = uid_range[i].seq1; + } + if (next_uid != mail->uid) { + seq_range_array_add_range(expunges, next_uid, + mail->uid - 1); + } + if (uid_range[i].seq2 == mail->uid) + next_uid = uid_range[++i].seq1; + else + next_uid = mail->uid + 1; + } + } + if (i < count) { + i_assert(next_uid <= uid_range[i].seq2); + seq_range_array_add_range(expunges, next_uid, + uid_range[i].seq2); + i++; + } + for (; i < count; i++) { + seq_range_array_add_range(expunges, uid_range[i].seq1, + uid_range[i].seq2); + } + + mailbox_get_status(ctx->box, STATUS_UIDNEXT, &status); + seq_range_array_remove_range(expunges, status.uidnext, (uint32_t)-1); + + if (mailbox_search_deinit(&search_ctx) < 0) + ret = -1; + + if (ret == 0 && ctx->qresync_sample_seqset != NULL) + expunges_drop_known(ctx, mail, expunges); + + mail_free(&mail); + (void)mailbox_transaction_commit(&trans); + return ret; +} + +static int +imap_fetch_send_vanished(struct imap_fetch_context *ctx) +{ + const struct mail_search_arg *uidarg = ctx->search_args; + const struct mail_search_arg *modseqarg = uidarg->next; + const ARRAY_TYPE(seq_range) *uids = &uidarg->value.seqset; + uint64_t modseq = modseqarg->value.modseq->modseq; + ARRAY_TYPE(seq_range) expunges; + string_t *str; + int ret = 0; + + i_array_init(&expunges, array_count(uids)); + if (!mailbox_get_expunged_uids(ctx->box, modseq, uids, &expunges)) { + /* return all expunged UIDs */ + if (get_expunges_fallback(ctx, uids, &expunges) < 0) { + array_clear(&expunges); + ret = -1; + } + } + if (array_count(&expunges) > 0) { + str = str_new(default_pool, 128); + str_append(str, "* VANISHED (EARLIER) "); + imap_write_seq_range(str, &expunges); + str_append(str, "\r\n"); + o_stream_send(ctx->client->output, str_data(str), str_len(str)); + str_free(&str); + } + array_free(&expunges); + return ret; +} + +int imap_fetch_begin(struct imap_fetch_context *ctx) { const void *null = NULL; const void *data; + if (ctx->send_vanished) { + if (imap_fetch_send_vanished(ctx) < 0) { + ctx->failed = TRUE; + return -1; + } + } + if (ctx->flags_update_seen) { if (mailbox_is_readonly(ctx->box)) ctx->flags_update_seen = FALSE; @@ -186,8 +347,12 @@ ctx->select_counter = ctx->client->select_counter; ctx->mail = mail_alloc(ctx->trans, ctx->fetch_data, ctx->all_headers_ctx); + + /* Delayed uidset -> seqset conversion. VANISHED needs the uidset. */ + mail_search_args_init(ctx->search_args, ctx->box, TRUE); ctx->search_ctx = mailbox_search_init(ctx->trans, NULL, ctx->search_args, NULL); + return 0; } static int imap_fetch_flush_buffer(struct imap_fetch_context *ctx) @@ -233,7 +398,7 @@ return 0; } -static int imap_fetch_more(struct imap_fetch_context *ctx) +static int imap_fetch_more_int(struct imap_fetch_context *ctx) { struct client *client = ctx->client; const struct imap_fetch_context_handler *handlers; @@ -350,14 +515,16 @@ return 1; } -int imap_fetch(struct imap_fetch_context *ctx) +int imap_fetch_more(struct imap_fetch_context *ctx) { int ret; i_assert(ctx->client->output_lock == NULL || ctx->client->output_lock == ctx->cmd); - ret = imap_fetch_more(ctx); + ret = imap_fetch_more_int(ctx); + if (ret < 0) + ctx->failed = TRUE; if (ctx->line_partial) { /* nothing can be sent until FETCH is finished */ ctx->client->output_lock = ctx->cmd;
--- a/src/imap/imap-fetch.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/imap-fetch.h Sat Mar 15 16:24:26 2008 +0200 @@ -53,6 +53,9 @@ bool skip_cr; int (*cont_handler)(struct imap_fetch_context *ctx); + const ARRAY_TYPE(uint32_t) *qresync_sample_seqset; + const ARRAY_TYPE(uint32_t) *qresync_sample_uidset; + ARRAY_TYPE(keywords) tmp_keywords; unsigned int select_counter; @@ -67,6 +70,7 @@ unsigned int line_partial:1; unsigned int line_finished:1; unsigned int partial_fetch:1; + unsigned int send_vanished:1; unsigned int failed:1; }; @@ -91,13 +95,17 @@ (imap_fetch_handler_t *)handler, context) #endif -struct imap_fetch_context *imap_fetch_init(struct client_command_context *cmd); +struct imap_fetch_context * +imap_fetch_init(struct client_command_context *cmd, struct mailbox *box); int imap_fetch_deinit(struct imap_fetch_context *ctx); bool imap_fetch_init_handler(struct imap_fetch_context *ctx, const char *name, const struct imap_arg **args); -void imap_fetch_begin(struct imap_fetch_context *ctx); -int imap_fetch(struct imap_fetch_context *ctx); +bool imap_fetch_add_unchanged_since(struct imap_fetch_context *ctx, + uint64_t modseq); + +int imap_fetch_begin(struct imap_fetch_context *ctx); +int imap_fetch_more(struct imap_fetch_context *ctx); bool fetch_body_section_init(struct imap_fetch_context *ctx, const char *name, const struct imap_arg **args);
--- a/src/imap/imap-search.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/imap-search.c Sat Mar 15 16:24:26 2008 +0200 @@ -77,39 +77,48 @@ } static int -imap_search_get_uidset_arg(pool_t pool, struct mailbox *box, const char *uidset, +imap_search_get_uidset_arg(struct client_command_context *cmd, + const char *uidset, struct mail_search_arg **arg_r, const char **error_r) { struct mail_search_arg *arg; - arg = p_new(pool, struct mail_search_arg, 1); + arg = p_new(cmd->pool, struct mail_search_arg, 1); arg->type = SEARCH_UIDSET; - p_array_init(&arg->value.seqset, pool, 16); + p_array_init(&arg->value.seqset, cmd->pool, 16); if (imap_messageset_parse(&arg->value.seqset, uidset) < 0) { *error_r = "Invalid uidset"; return -1; } - mail_search_args_init(arg, box, TRUE); *arg_r = arg; return 0; } struct mail_search_arg * -imap_search_get_arg(struct client_command_context *cmd, - const char *set, bool uid) +imap_search_get_seqset(struct client_command_context *cmd, + const char *set, bool uid) +{ + struct mail_search_arg *search_arg; + + search_arg = imap_search_get_anyset(cmd, set, uid); + if (uid && search_arg != NULL) + mail_search_args_init(search_arg, cmd->client->mailbox, TRUE); + return search_arg; +} + +struct mail_search_arg * +imap_search_get_anyset(struct client_command_context *cmd, + const char *set, bool uid) { struct mail_search_arg *search_arg = NULL; const char *error = NULL; int ret; - if (!uid) { + if (!uid) ret = imap_search_get_msgset_arg(cmd, set, &search_arg, &error); - } else { - ret = imap_search_get_uidset_arg(cmd->pool, - cmd->client->mailbox, set, - &search_arg, &error); - } + else + ret = imap_search_get_uidset_arg(cmd, set, &search_arg, &error); if (ret < 0) { client_send_command_error(cmd, error); return NULL;
--- a/src/imap/imap-search.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/imap-search.h Sat Mar 15 16:24:26 2008 +0200 @@ -11,7 +11,10 @@ const struct imap_arg *args, const char **error_r); struct mail_search_arg * -imap_search_get_arg(struct client_command_context *cmd, - const char *set, bool uid); +imap_search_get_seqset(struct client_command_context *cmd, + const char *set, bool uid); +struct mail_search_arg * +imap_search_get_anyset(struct client_command_context *cmd, + const char *set, bool uid); #endif
--- a/src/imap/imap-sync.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/imap/imap-sync.c Sat Mar 15 16:24:26 2008 +0200 @@ -29,6 +29,7 @@ struct mailbox_sync_rec sync_rec; ARRAY_TYPE(keywords) tmp_keywords; + ARRAY_TYPE(seq_range) expunges; uint32_t seq; unsigned int messages_count; @@ -56,6 +57,9 @@ ctx->messages_count = client->messages_count; i_array_init(&ctx->tmp_keywords, client->keywords.announce_count + 8); + if ((client->enabled_features & MAILBOX_FEATURE_QRESYNC) != 0) + i_array_init(&ctx->expunges, 128); + client_send_mailbox_flags(client, FALSE); return ctx; } @@ -66,6 +70,8 @@ int ret; mail_free(&ctx->mail); + if (array_is_created(&ctx->expunges)) + array_free(&ctx->expunges); if (mailbox_sync_deinit(&ctx->sync_ctx, STATUS_UIDVALIDITY | STATUS_MESSAGES | STATUS_RECENT, &status) < 0 || @@ -144,6 +150,53 @@ return client_send_line(ctx->client, str_c(str)); } +static void imap_sync_vanished(struct imap_sync_context *ctx) +{ + const struct seq_range *seqs; + unsigned int i, count; + string_t *line; + uint32_t seq, prev_uid, start_uid; + bool comma = FALSE; + + /* Convert expunge sequences to UIDs and send them in VANISHED line. */ + seqs = array_get(&ctx->expunges, &count); + if (count == 0) + return; + + line = t_str_new(256); + str_append(line, "* VANISHED "); + for (i = 0; i < count; i++) { + prev_uid = start_uid = 0; + for (seq = seqs[i].seq1; seq <= seqs[i].seq2; seq++) { + mail_set_seq(ctx->mail, seq); + if (prev_uid + 1 != ctx->mail->uid) { + if (start_uid != 0) { + if (!comma) + comma = TRUE; + else + str_append_c(line, ','); + str_printfa(line, "%u", start_uid); + if (start_uid != prev_uid) { + str_printfa(line, ":%u", + prev_uid); + } + } + start_uid = ctx->mail->uid; + } + prev_uid = ctx->mail->uid; + } + if (!comma) + comma = TRUE; + else + str_append_c(line, ','); + str_printfa(line, "%u", start_uid); + if (start_uid != prev_uid) + str_printfa(line, ":%u", prev_uid); + } + str_append(line, "\r\n"); + o_stream_send(ctx->client->output, str_data(line), str_len(line)); +} + int imap_sync_more(struct imap_sync_context *ctx) { string_t *str; @@ -170,6 +223,10 @@ ctx->sync_rec.seq2 = ctx->messages_count; } + /* EXPUNGEs must come last */ + i_assert(!array_is_created(&ctx->expunges) || + array_count(&ctx->expunges) == 0 || + ctx->sync_rec.type == MAILBOX_SYNC_TYPE_EXPUNGE); switch (ctx->sync_rec.type) { case MAILBOX_SYNC_TYPE_FLAGS: if (ctx->seq == 0) @@ -184,6 +241,16 @@ } break; case MAILBOX_SYNC_TYPE_EXPUNGE: + if (array_is_created(&ctx->expunges)) { + /* Use a single VANISHED line */ + seq_range_array_add_range(&ctx->expunges, + ctx->sync_rec.seq1, + ctx->sync_rec.seq2); + ctx->messages_count -= + ctx->sync_rec.seq2 - + ctx->sync_rec.seq1 + 1; + break; + } if (ctx->seq == 0) ctx->seq = ctx->sync_rec.seq2; ret = 1; @@ -228,6 +295,8 @@ ctx->seq = 0; } + if (array_is_created(&ctx->expunges)) + imap_sync_vanished(ctx); return ret; }
--- a/src/lib-index/mail-index-modseq.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-index/mail-index-modseq.c Sat Mar 15 16:24:26 2008 +0200 @@ -21,14 +21,6 @@ METADATA_MODSEQ_IDX_KEYWORD_START }; -struct mail_index_modseq_header { - /* highest used modseq */ - uint64_t highest_modseq; - /* last tracked log file position */ - uint32_t log_seq; - uint32_t log_offset; -}; - struct metadata_modseqs { ARRAY_TYPE(modseqs) modseqs; }; @@ -608,3 +600,16 @@ array_free(&mmap->metadata_modseqs); i_free(mmap); } + +bool mail_index_modseq_get_log_offset(struct mail_index_view *view, + uint64_t modseq, uint32_t *log_seq_r, + uoff_t *log_offset_r) +{ + if (view->map->hdr.indexid >= (modseq >> 32)) { + /* invalid modseq or created for an earlier index */ + return FALSE; + } + *log_seq_r = (modseq >> 32) - view->map->hdr.indexid; + *log_offset_r = modseq & 0xffffffff; + return TRUE; +}
--- a/src/lib-index/mail-index-modseq.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-index/mail-index-modseq.h Sat Mar 15 16:24:26 2008 +0200 @@ -9,6 +9,14 @@ struct mail_index_map_modseq; struct mail_index_sync_map_ctx; +struct mail_index_modseq_header { + /* highest used modseq */ + uint64_t highest_modseq; + /* last tracked log file position */ + uint32_t log_seq; + uint32_t log_offset; +}; + void mail_index_modseq_init(struct mail_index *index); void mail_index_modseq_enable(struct mail_index *index); @@ -41,4 +49,8 @@ void mail_index_map_modseq_free(struct mail_index_map_modseq *mmap); +bool mail_index_modseq_get_log_offset(struct mail_index_view *view, + uint64_t modseq, uint32_t *log_seq_r, + uoff_t *log_offset_r); + #endif
--- a/src/lib-storage/index/cydir/cydir-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/cydir/cydir-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -431,6 +431,7 @@ index_keywords_create, index_keywords_free, index_storage_get_uids, + index_storage_get_expunged_uids, index_mail_alloc, index_header_lookup_init, index_header_lookup_deinit,
--- a/src/lib-storage/index/dbox/dbox-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/dbox/dbox-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -680,6 +680,7 @@ index_keywords_create, index_keywords_free, index_storage_get_uids, + index_storage_get_expunged_uids, dbox_mail_alloc, index_header_lookup_init, index_header_lookup_deinit,
--- a/src/lib-storage/index/index-fetch.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/index-fetch.c Sat Mar 15 16:24:26 2008 +0200 @@ -1,6 +1,8 @@ /* Copyright (c) 2002-2008 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "array.h" +#include "mail-index-modseq.h" #include "index-storage.h" #include "index-mail.h" @@ -12,3 +14,77 @@ mail_index_lookup_seq_range(ibox->view, uid1, uid2, seq1_r, seq2_r); } + +bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t modseq, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunged_uids) +{ +#define EXPUNGE_MASK (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXTERNAL) + struct index_mailbox *ibox = (struct index_mailbox *)box; + struct mail_transaction_log_view *log_view; + const struct mail_index_header *hdr; + const struct mail_transaction_header *thdr; + const struct mail_transaction_expunge *rec, *end; + const struct seq_range *uid_range; + unsigned int count; + const void *tdata; + uint32_t prev_seq, log_seq, min_uid, max_uid; + uoff_t prev_offset, log_offset; + bool reset; + + if (!mail_index_modseq_get_log_offset(ibox->view, modseq, + &log_seq, &log_offset)) + return FALSE; + if (log_seq > ibox->view->log_file_head_seq || + (log_seq == ibox->view->log_file_head_seq && + log_offset >= ibox->view->log_file_head_offset)) { + /* we haven't seen this high expunges at all */ + return TRUE; + } + + hdr = mail_index_get_header(ibox->view); + log_view = mail_transaction_log_view_open(ibox->index->log); + /* we can't trust user-given log offsets, so we have to start reading + from the beginning of the log. */ + if (mail_transaction_log_view_set(log_view, log_seq, 0, + ibox->view->log_file_head_seq, + ibox->view->log_file_head_offset, + &reset) <= 0) { + mail_transaction_log_view_close(&log_view); + return FALSE; + } + + /* do only minimal range checks while adding the UIDs. */ + uid_range = array_get(uids, &count); + i_assert(count > 0); + min_uid = uid_range[0].seq1; + max_uid = uid_range[count-1].seq2; + + while (mail_transaction_log_view_next(log_view, &thdr, &tdata) > 0) { + if ((thdr->type & EXPUNGE_MASK) != EXPUNGE_MASK) + continue; + + mail_transaction_log_view_get_prev_pos(log_view, + &prev_seq, &prev_offset); + if (prev_seq < log_seq || + (prev_offset <= log_offset && prev_seq == log_seq)) { + /* still too old expunge. note that + prev_offset==log_offset is also skipped. */ + continue; + } + + rec = tdata; + end = rec + thdr->size / sizeof(*rec); + for (; rec != end; rec++) { + if (!(rec->uid1 > max_uid || rec->uid2 < min_uid)) { + seq_range_array_add_range(expunged_uids, + rec->uid1, rec->uid2); + } + } + } + + /* remove UIDs not in the wanted UIDs range */ + seq_range_array_remove_invert_range(expunged_uids, uids); + mail_transaction_log_view_close(&log_view); + return TRUE; +}
--- a/src/lib-storage/index/index-storage.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/index-storage.h Sat Mar 15 16:24:26 2008 +0200 @@ -146,6 +146,9 @@ struct mailbox_status *status_r); void index_storage_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2, uint32_t *seq1_r, uint32_t *seq2_r); +bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t modseq, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunged_uids); struct mailbox_header_lookup_ctx * index_header_lookup_init(struct mailbox *box, const char *const headers[]);
--- a/src/lib-storage/index/maildir/maildir-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/maildir/maildir-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -1038,6 +1038,7 @@ index_keywords_create, index_keywords_free, index_storage_get_uids, + index_storage_get_expunged_uids, index_mail_alloc, index_header_lookup_init, index_header_lookup_deinit,
--- a/src/lib-storage/index/mbox/mbox-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/mbox/mbox-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -988,6 +988,7 @@ index_keywords_create, index_keywords_free, index_storage_get_uids, + index_storage_get_expunged_uids, index_mail_alloc, index_header_lookup_init, index_header_lookup_deinit,
--- a/src/lib-storage/index/raw/raw-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/index/raw/raw-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -288,6 +288,7 @@ index_keywords_create, index_keywords_free, index_storage_get_uids, + index_storage_get_expunged_uids, index_mail_alloc, index_header_lookup_init, index_header_lookup_deinit,
--- a/src/lib-storage/mail-storage-private.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/mail-storage-private.h Sat Mar 15 16:24:26 2008 +0200 @@ -125,6 +125,9 @@ void (*get_uids)(struct mailbox *box, uint32_t uid1, uint32_t uid2, uint32_t *seq1_r, uint32_t *seq2_r); + bool (*get_expunged_uids)(struct mailbox *box, uint64_t modseq, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunged_uids); struct mail * (*mail_alloc)(struct mailbox_transaction_context *t,
--- a/src/lib-storage/mail-storage.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/mail-storage.c Sat Mar 15 16:24:26 2008 +0200 @@ -608,6 +608,13 @@ box->v.get_uids(box, uid1, uid2, seq1_r, seq2_r); } +bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t modseq, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunged_uids) +{ + return box->v.get_expunged_uids(box, modseq, uids, expunged_uids); +} + struct mailbox_header_lookup_ctx * mailbox_header_lookup_init(struct mailbox *box, const char *const headers[]) {
--- a/src/lib-storage/mail-storage.h Sat Mar 15 15:24:45 2008 +0200 +++ b/src/lib-storage/mail-storage.h Sat Mar 15 16:24:26 2008 +0200 @@ -3,6 +3,7 @@ struct message_size; +#include "seq-range-array.h" #include "file-lock.h" #include "mail-types.h" #include "mail-error.h" @@ -60,6 +61,8 @@ enum mailbox_feature { /* Enable tracking modsequences */ MAILBOX_FEATURE_CONDSTORE = 0x01, + /* Enable tracking expunge modsequences */ + MAILBOX_FEATURE_QRESYNC = 0x02 }; enum mailbox_status_items { @@ -354,6 +357,12 @@ /* Convert uid range to sequence range. */ void mailbox_get_uids(struct mailbox *box, uint32_t uid1, uint32_t uid2, uint32_t *seq1_r, uint32_t *seq2_r); +/* Get list of UIDs expunged after modseq and within the given range. + UIDs that have been expunged after the last mailbox sync aren't returned. + Returns TRUE if ok, FALSE if modseq is lower than we can check for. */ +bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t modseq, + const ARRAY_TYPE(seq_range) *uids, + ARRAY_TYPE(seq_range) *expunged_uids); /* Initialize header lookup for given headers. */ struct mailbox_header_lookup_ctx *
--- a/src/util/idxview.c Sat Mar 15 15:24:45 2008 +0200 +++ b/src/util/idxview.c Sat Mar 15 16:24:26 2008 +0200 @@ -7,6 +7,8 @@ #include "file-lock.h" #include "mail-index-private.h" #include "mail-cache-private.h" +#include "mail-cache-private.h" +#include "mail-index-modseq.h" #include <stdio.h> #include <stdlib.h> @@ -89,6 +91,14 @@ printf("header\n"); printf(" - last_dirty_flush_stamp = %s\n", unixdate2str(hdr->last_dirty_flush_stamp)); + } else if (strcmp(ext->name, "modseq") == 0) { + const struct mail_index_modseq_header *hdr = data; + + printf("header\n"); + printf(" - highest_modseq = %llu\n", + (unsigned long long)hdr->highest_modseq); + printf(" - log_seq ...... = %u\n", hdr->log_seq); + printf(" - log_offset ... = %u\n", hdr->log_offset); } else { printf("header ........ = %s\n", binary_to_hex(data, ext->hdr_size));