From: Timo Sirainen Date: Tue, 14 Jul 2009 02:24:27 +0000 (-0400) Subject: Keep track of expunged messages' GUIDs and expose them via mailbox_get_expunges(). X-Git-Tag: 2.0.alpha1~428 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=0c909e3461607eadcd66f4eac69b7f34e37fccf1;p=thirdparty%2Fdovecot%2Fcore.git Keep track of expunged messages' GUIDs and expose them via mailbox_get_expunges(). The message GUIDs are stored in expunge records to transaction log. Before doing the final expunge, Maildir and dbox verify that the GUID in expunge request matches the current actual GUID. The GUID is stored in 128 bit field. If the real GUID isn't 128 bit, the bits are taken from SHA1 of the GUID. --HG-- branch : HEAD --- diff --git a/src/imap/imap-fetch.c b/src/imap/imap-fetch.c index 31fa3c9d11..e30b80d18b 100644 --- a/src/imap/imap-fetch.c +++ b/src/imap/imap-fetch.c @@ -255,6 +255,18 @@ static int get_expunges_fallback(struct imap_fetch_context *ctx, return ret; } +static void +mailbox_expunge_to_range(const ARRAY_TYPE(mailbox_expunge_rec) *input, + ARRAY_TYPE(seq_range) *output) +{ + const struct mailbox_expunge_rec *expunges; + unsigned int i, count; + + expunges = array_get(input, &count); + for (i = 0; i < count; i++) + seq_range_array_add(output, 0, expunges[i].uid); +} + static int imap_fetch_send_vanished(struct imap_fetch_context *ctx) { @@ -262,27 +274,32 @@ imap_fetch_send_vanished(struct imap_fetch_context *ctx) 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; + ARRAY_TYPE(mailbox_expunge_rec) expunges; + ARRAY_TYPE(seq_range) expunges_range; string_t *str; int ret = 0; i_array_init(&expunges, array_count(uids)); - if (!mailbox_get_expunged_uids(ctx->box, modseq, uids, &expunges)) { + i_array_init(&expunges_range, array_count(uids)); + if (mailbox_get_expunges(ctx->box, modseq, uids, &expunges)) + mailbox_expunge_to_range(&expunges, &expunges_range); + else { /* return all expunged UIDs */ - if (get_expunges_fallback(ctx, uids, &expunges) < 0) { - array_clear(&expunges); + if (get_expunges_fallback(ctx, uids, &expunges_range) < 0) { + array_clear(&expunges_range); ret = -1; } } - if (array_count(&expunges) > 0) { + if (array_count(&expunges_range) > 0) { str = str_new(default_pool, 128); str_append(str, "* VANISHED (EARLIER) "); - imap_write_seq_range(str, &expunges); + imap_write_seq_range(str, &expunges_range); str_append(str, "\r\n"); o_stream_send(ctx->client->output, str_data(str), str_len(str)); str_free(&str); } array_free(&expunges); + array_free(&expunges_range); return ret; } diff --git a/src/lib-index/mail-index-sync-update.c b/src/lib-index/mail-index-sync-update.c index 329b33aeb6..e49082fd34 100644 --- a/src/lib-index/mail-index-sync-update.c +++ b/src/lib-index/mail-index-sync-update.c @@ -236,44 +236,36 @@ sync_expunge_call_handlers(struct mail_index_sync_map_ctx *ctx, } } -static int -sync_expunge(const struct mail_transaction_expunge *e, unsigned int count, - struct mail_index_sync_map_ctx *ctx) +static void +sync_expunge(struct mail_index_sync_map_ctx *ctx, uint32_t uid1, uint32_t uid2) { struct mail_index_map *map = ctx->view->map; struct mail_index_record *rec; uint32_t seq_count, seq, seq1, seq2; - unsigned int i; - - for (i = 0; i < count; i++, e++) { - if (!mail_index_lookup_seq_range(ctx->view, e->uid1, e->uid2, - &seq1, &seq2)) { - /* everything expunged already */ - continue; - } - sync_expunge_call_handlers(ctx, seq1, seq2); - - map = mail_index_sync_get_atomic_map(ctx); - for (seq = seq1; seq <= seq2; seq++) { - rec = MAIL_INDEX_MAP_IDX(map, seq-1); - mail_index_sync_header_update_counts(ctx, rec->uid, - rec->flags, 0, - FALSE); - } + if (!mail_index_lookup_seq_range(ctx->view, uid1, uid2, &seq1, &seq2)) { + /* everything expunged already */ + return; + } - /* @UNSAFE */ - memmove(MAIL_INDEX_MAP_IDX(map, seq1-1), - MAIL_INDEX_MAP_IDX(map, seq2), - (map->rec_map->records_count - seq2) * - map->hdr.record_size); + sync_expunge_call_handlers(ctx, seq1, seq2); - seq_count = seq2 - seq1 + 1; - map->rec_map->records_count -= seq_count; - map->hdr.messages_count -= seq_count; - mail_index_modseq_expunge(ctx->modseq_ctx, seq1, seq2); + map = mail_index_sync_get_atomic_map(ctx); + for (seq = seq1; seq <= seq2; seq++) { + rec = MAIL_INDEX_MAP_IDX(map, seq-1); + mail_index_sync_header_update_counts(ctx, rec->uid, rec->flags, + 0, FALSE); } - return 1; + + /* @UNSAFE */ + memmove(MAIL_INDEX_MAP_IDX(map, seq1-1), + MAIL_INDEX_MAP_IDX(map, seq2), + (map->rec_map->records_count - seq2) * map->hdr.record_size); + + seq_count = seq2 - seq1 + 1; + map->rec_map->records_count -= seq_count; + map->hdr.messages_count -= seq_count; + mail_index_modseq_expunge(ctx->modseq_ctx, seq1, seq2); } void mail_index_sync_write_seq_update(struct mail_index_sync_map_ctx *ctx, @@ -484,7 +476,21 @@ int mail_index_sync_record(struct mail_index_sync_map_ctx *ctx, break; } end = CONST_PTR_OFFSET(data, hdr->size); - ret = sync_expunge(rec, end - rec, ctx); + for (; rec != end; rec++) + sync_expunge(ctx, rec->uid1, rec->uid2); + break; + } + case MAIL_TRANSACTION_EXPUNGE_GUID: + case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: { + const struct mail_transaction_expunge_guid *rec = data, *end; + + if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { + /* this is simply a request for expunge */ + break; + } + end = CONST_PTR_OFFSET(data, hdr->size); + for (; rec != end; rec++) + sync_expunge(ctx, rec->uid, rec->uid); break; } case MAIL_TRANSACTION_FLAG_UPDATE: { diff --git a/src/lib-index/mail-index-sync.c b/src/lib-index/mail-index-sync.c index d6378705f2..9fdc594471 100644 --- a/src/lib-index/mail-index-sync.c +++ b/src/lib-index/mail-index-sync.c @@ -41,6 +41,17 @@ static void mail_index_sync_add_expunge(struct mail_index_sync_ctx *ctx) } } +static void mail_index_sync_add_expunge_guid(struct mail_index_sync_ctx *ctx) +{ + const struct mail_transaction_expunge_guid *e = ctx->data; + size_t i, size = ctx->hdr->size / sizeof(*e); + + for (i = 0; i < size; i++) { + mail_index_expunge_guid(ctx->sync_trans, e[i].uid, + e[i].guid_128); + } +} + static void mail_index_sync_add_flag_update(struct mail_index_sync_ctx *ctx) { const struct mail_transaction_flag_update *u = ctx->data; @@ -129,6 +140,9 @@ static bool mail_index_sync_add_transaction(struct mail_index_sync_ctx *ctx) case MAIL_TRANSACTION_EXPUNGE: mail_index_sync_add_expunge(ctx); break; + case MAIL_TRANSACTION_EXPUNGE_GUID: + mail_index_sync_add_expunge_guid(ctx); + break; case MAIL_TRANSACTION_FLAG_UPDATE: mail_index_sync_add_flag_update(ctx); break; @@ -512,6 +526,7 @@ static bool mail_index_sync_view_have_any(struct mail_index_view *view, to be synced, but cache syncing relies on tail offsets being updated. */ case MAIL_TRANSACTION_EXPUNGE: + case MAIL_TRANSACTION_EXPUNGE_GUID: case MAIL_TRANSACTION_FLAG_UPDATE: case MAIL_TRANSACTION_KEYWORD_UPDATE: case MAIL_TRANSACTION_KEYWORD_RESET: @@ -550,11 +565,12 @@ void mail_index_sync_get_offsets(struct mail_index_sync_ctx *ctx, static void mail_index_sync_get_expunge(struct mail_index_sync_rec *rec, - const struct mail_transaction_expunge *exp) + const struct mail_transaction_expunge_guid *exp) { rec->type = MAIL_INDEX_SYNC_TYPE_EXPUNGE; - rec->uid1 = exp->uid1; - rec->uid2 = exp->uid2; + rec->uid1 = exp->uid; + rec->uid2 = exp->uid; + memcpy(rec->guid_128, exp->guid_128, sizeof(rec->guid_128)); } static void @@ -605,6 +621,8 @@ bool mail_index_sync_next(struct mail_index_sync_ctx *ctx, /* FIXME: replace with a priority queue so we don't have to go through the whole list constantly. and remember to make sure that keyword resets are sent before adds! */ + /* FIXME: pretty ugly to do this for expunges, which isn't even a + seq_range. */ sync_list = array_get_modifiable(&ctx->sync_list, &count); for (i = 0; i < count; i++) { if (!array_is_created(sync_list[i].array) || @@ -641,7 +659,7 @@ bool mail_index_sync_next(struct mail_index_sync_ctx *ctx, if (sync_list[i].array == (void *)&sync_trans->expunges) { mail_index_sync_get_expunge(sync_rec, - (const struct mail_transaction_expunge *)uid_range); + (const struct mail_transaction_expunge_guid *)uid_range); } else if (sync_list[i].array == (void *)&sync_trans->updates) { mail_index_sync_get_update(sync_rec, (const struct mail_transaction_flag_update *)uid_range); diff --git a/src/lib-index/mail-index-transaction-export.c b/src/lib-index/mail-index-transaction-export.c index e2a2dc7851..bcafb36a82 100644 --- a/src/lib-index/mail-index-transaction-export.c +++ b/src/lib-index/mail-index-transaction-export.c @@ -380,7 +380,7 @@ void mail_index_transaction_export(struct mail_index_transaction *t, if ((t->flags & MAIL_INDEX_TRANSACTION_FLAG_EXTERNAL) != 0) change_mask |= MAIL_INDEX_SYNC_TYPE_EXPUNGE; log_append_buffer(&ctx, t->expunges.arr.buffer, - MAIL_TRANSACTION_EXPUNGE); + MAIL_TRANSACTION_EXPUNGE_GUID); } if (t->post_hdr_changed) { diff --git a/src/lib-index/mail-index-transaction-finish.c b/src/lib-index/mail-index-transaction-finish.c index 0e2cf4be6e..88e5229e72 100644 --- a/src/lib-index/mail-index-transaction-finish.c +++ b/src/lib-index/mail-index-transaction-finish.c @@ -6,6 +6,26 @@ #include "mail-index-modseq.h" #include "mail-index-transaction-private.h" +int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1, + const struct mail_transaction_expunge_guid *e2) +{ + if (e1->uid < e2->uid) + return -1; + else if (e1->uid > e2->uid) + return 1; + else + return 0; +} + +void mail_index_transaction_sort_expunges(struct mail_index_transaction *t) +{ + if (!t->expunges_nonsorted) + return; + + array_sort(&t->expunges, mail_transaction_expunge_guid_cmp); + t->expunges_nonsorted = FALSE; +} + static void ext_reset_update_atomic(struct mail_index_transaction *t, uint32_t ext_id, uint32_t expected_reset_id) @@ -312,6 +332,31 @@ static void keyword_updates_convert_to_uids(struct mail_index_transaction *t) } } +static void expunges_convert_to_uids(struct mail_index_transaction *t) +{ + struct mail_transaction_expunge_guid *expunges; + unsigned int src, dest, count; + + if (!array_is_created(&t->expunges)) + return; + + mail_index_transaction_sort_expunges(t); + + expunges = array_get_modifiable(&t->expunges, &count); + if (count == 0) + return; + + /* convert uids and drop duplicates */ + expunges[0].uid = mail_index_transaction_get_uid(t, expunges[0].uid); + for (src = dest = 1; src < count; src++) { + expunges[dest].uid = + mail_index_transaction_get_uid(t, expunges[src].uid); + if (expunges[dest-1].uid != expunges[dest].uid) + dest++; + } + array_delete(&t->expunges, dest, count-dest); +} + static void mail_index_transaction_convert_to_uids(struct mail_index_transaction *t) { @@ -330,8 +375,7 @@ mail_index_transaction_convert_to_uids(struct mail_index_transaction *t) } keyword_updates_convert_to_uids(t); - - mail_index_convert_to_uid_ranges(t, &t->expunges); + expunges_convert_to_uids(t); mail_index_convert_to_uid_ranges(t, (void *)&t->updates); mail_index_convert_to_uid_ranges(t, &t->keyword_resets); } diff --git a/src/lib-index/mail-index-transaction-private.h b/src/lib-index/mail-index-transaction-private.h index 64340ccc0a..a9fe061ced 100644 --- a/src/lib-index/mail-index-transaction-private.h +++ b/src/lib-index/mail-index-transaction-private.h @@ -37,14 +37,14 @@ struct mail_index_transaction { struct mail_index_view *view; /* NOTE: If you add anything new, remember to update - mail_index_transaction_reset() to reset it. */ + mail_index_transaction_reset_v() to reset it. */ ARRAY_DEFINE(appends, struct mail_index_record); uint32_t first_new_seq, last_new_seq; uint32_t highest_append_uid; /* lowest/highest sequence that updates flags/keywords */ uint32_t min_flagupdate_seq, max_flagupdate_seq; - ARRAY_TYPE(seq_range) expunges; + ARRAY_DEFINE(expunges, struct mail_transaction_expunge_guid); ARRAY_DEFINE(updates, struct mail_transaction_flag_update); size_t last_update_idx; @@ -77,6 +77,7 @@ struct mail_index_transaction { unsigned int sync_transaction:1; unsigned int appends_nonsorted:1; + unsigned int expunges_nonsorted:1; unsigned int drop_unnecessary_flag_updates:1; unsigned int pre_hdr_changed:1; unsigned int post_hdr_changed:1; @@ -104,6 +105,7 @@ void mail_index_transaction_unref(struct mail_index_transaction **t); void mail_index_transaction_reset_v(struct mail_index_transaction *t); void mail_index_transaction_sort_appends(struct mail_index_transaction *t); +void mail_index_transaction_sort_expunges(struct mail_index_transaction *t); uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t); void mail_index_transaction_set_log_updates(struct mail_index_transaction *t); void mail_index_update_day_headers(struct mail_index_transaction *t); @@ -117,6 +119,8 @@ mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, int mail_index_transaction_finish(struct mail_index_transaction *t); void mail_index_transaction_export(struct mail_index_transaction *t, struct mail_transaction_log_append_ctx *append_ctx); +int mail_transaction_expunge_guid_cmp(const struct mail_transaction_expunge_guid *e1, + const struct mail_transaction_expunge_guid *e2); unsigned int mail_index_transaction_get_flag_update_pos(struct mail_index_transaction *t, unsigned int left_idx, diff --git a/src/lib-index/mail-index-transaction-update.c b/src/lib-index/mail-index-transaction-update.c index 09b543138c..64dc0fe96c 100644 --- a/src/lib-index/mail-index-transaction-update.c +++ b/src/lib-index/mail-index-transaction-update.c @@ -92,6 +92,7 @@ void mail_index_transaction_reset_v(struct mail_index_transaction *t) memset(t->post_hdr_mask, 0, sizeof(t->post_hdr_mask)); t->appends_nonsorted = FALSE; + t->expunges_nonsorted = FALSE; t->drop_unnecessary_flag_updates = FALSE; t->pre_hdr_changed = FALSE; t->post_hdr_changed = FALSE; @@ -273,6 +274,18 @@ mail_index_expunge_last_append(struct mail_index_transaction *t, uint32_t seq) void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq) { + static uint8_t null_guid[MAIL_GUID_128_SIZE] = + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + mail_index_expunge_guid(t, seq, null_guid); +} + +void mail_index_expunge_guid(struct mail_index_transaction *t, uint32_t seq, + const uint8_t guid_128[MAIL_GUID_128_SIZE]) +{ + const struct mail_transaction_expunge_guid *expunges; + struct mail_transaction_expunge_guid *expunge; + unsigned int count; + i_assert(seq > 0); if (seq >= t->first_new_seq) { /* we can handle only the last append. otherwise we'd have to @@ -283,8 +296,18 @@ void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq) } else { t->log_updates = TRUE; - /* expunges is a sorted array of {seq1, seq2, ..}, .. */ - seq_range_array_add(&t->expunges, 128, seq); + /* ignore duplicates here. drop them when commiting. */ + if (!array_is_created(&t->expunges)) + i_array_init(&t->expunges, 64); + else if (!t->expunges_nonsorted) { + /* usually expunges are added in increasing order. */ + expunges = array_get(&t->expunges, &count); + if (count > 0 && seq < expunges[count-1].uid) + t->expunges_nonsorted = TRUE; + } + expunge = array_append_space(&t->expunges); + expunge->uid = seq; + memcpy(expunge->guid_128, guid_128, sizeof(expunge->guid_128)); } } diff --git a/src/lib-index/mail-index-transaction-view.c b/src/lib-index/mail-index-transaction-view.c index 1d501b11bc..cbfdc66713 100644 --- a/src/lib-index/mail-index-transaction-view.c +++ b/src/lib-index/mail-index-transaction-view.c @@ -124,8 +124,7 @@ tview_lookup_full(struct mail_index_view *view, uint32_t seq, rec = tview->super->lookup_full(view, seq, map_r, expunged_r); rec = tview_apply_flag_updates(tview, rec, seq); - if (array_is_created(&tview->t->expunges) && - seq_range_exists(&tview->t->expunges, seq)) + if (mail_index_transaction_is_expunged(tview->t, seq)) *expunged_r = TRUE; return rec; } diff --git a/src/lib-index/mail-index-transaction.c b/src/lib-index/mail-index-transaction.c index 110b5e2e47..881882f87e 100644 --- a/src/lib-index/mail-index-transaction.c +++ b/src/lib-index/mail-index-transaction.c @@ -22,8 +22,17 @@ mail_index_transaction_get_view(struct mail_index_transaction *t) bool mail_index_transaction_is_expunged(struct mail_index_transaction *t, uint32_t seq) { - return array_is_created(&t->expunges) && - seq_range_exists(&t->expunges, seq); + struct mail_transaction_expunge_guid key; + + if (!array_is_created(&t->expunges)) + return FALSE; + + if (t->expunges_nonsorted) + mail_index_transaction_sort_expunges(t); + + key.uid = seq; + return array_bsearch(&t->expunges, &key, + mail_transaction_expunge_guid_cmp) != NULL; } void mail_index_transaction_ref(struct mail_index_transaction *t) diff --git a/src/lib-index/mail-index-view-sync.c b/src/lib-index/mail-index-view-sync.c index 5044c74204..b434544be9 100644 --- a/src/lib-index/mail-index-view-sync.c +++ b/src/lib-index/mail-index-view-sync.c @@ -123,6 +123,20 @@ view_sync_add_expunge_range(ARRAY_TYPE(seq_range) *dest, seq_range_array_add_range(dest, src[i].seq1, src[i].seq2); } +static void +view_sync_add_expunge_guids(ARRAY_TYPE(seq_range) *dest, + const struct mail_transaction_expunge_guid *src, + size_t src_size) +{ + unsigned int i, src_count; + + i_assert(src_size % sizeof(*src) == 0); + + src_count = src_size / sizeof(*src); + for (i = 0; i < src_count; i++) + seq_range_array_add(dest, 0, src[i].uid); +} + static int view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx, unsigned int *expunge_count_r) @@ -139,14 +153,17 @@ view_sync_get_expunges(struct mail_index_view_sync_ctx *ctx, mail_transaction_log_view_mark(view->log_view); while ((ret = mail_transaction_log_view_next(view->log_view, &hdr, &data)) > 0) { - if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) == 0) - continue; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { - /* this is simply a request for expunge */ + /* skip expunge requests */ continue; } - - view_sync_add_expunge_range(&ctx->expunges, data, hdr->size); + if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { + view_sync_add_expunge_guids(&ctx->expunges, + data, hdr->size); + } else if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { + view_sync_add_expunge_range(&ctx->expunges, + data, hdr->size); + } } mail_transaction_log_view_rewind(view->log_view); @@ -161,7 +178,7 @@ static bool have_existing_expunges(struct mail_index_view *view, uint32_t seq1, seq2; range_end = CONST_PTR_OFFSET(range, size); - for (; range < range_end; range++) { + for (; range != range_end; range++) { if (mail_index_lookup_seq_range(view, range->seq1, range->seq2, &seq1, &seq2)) return TRUE; @@ -169,6 +186,22 @@ static bool have_existing_expunges(struct mail_index_view *view, return FALSE; } +static bool +have_existing_guid_expunge(struct mail_index_view *view, + const struct mail_transaction_expunge_guid *expunges, + size_t size) +{ + const struct mail_transaction_expunge_guid *expunges_end; + uint32_t seq; + + expunges_end = CONST_PTR_OFFSET(expunges, size); + for (; expunges != expunges_end; expunges++) { + if (mail_index_lookup_seq(view, expunges->uid, &seq)) + return TRUE; + } + return FALSE; +} + static bool view_sync_have_expunges(struct mail_index_view *view) { const struct mail_transaction_header *hdr; @@ -180,17 +213,22 @@ static bool view_sync_have_expunges(struct mail_index_view *view) while ((ret = mail_transaction_log_view_next(view->log_view, &hdr, &data)) > 0) { - if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) == 0) - continue; if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { - /* this is simply a request for expunge */ + /* skip expunge requests */ continue; } - - /* we have an expunge. see if it still exists. */ - if (have_existing_expunges(view, data, hdr->size)) { - have_expunges = TRUE; - break; + if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { + /* we have an expunge. see if it still exists. */ + if (have_existing_expunges(view, data, hdr->size)) { + have_expunges = TRUE; + break; + } + } else if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0) { + /* we have an expunge. see if it still exists. */ + if (have_existing_guid_expunge(view, data, hdr->size)) { + have_expunges = TRUE; + break; + } } } @@ -616,7 +654,8 @@ mail_index_view_sync_want(struct mail_index_view_sync_ctx *ctx, mail_transaction_log_view_get_prev_pos(view->log_view, &seq, &offset); next_offset = offset + sizeof(*hdr) + hdr->size; - if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) != 0 && + if ((hdr->type & (MAIL_TRANSACTION_EXPUNGE | + MAIL_TRANSACTION_EXPUNGE_GUID)) != 0 && (hdr->type & MAIL_TRANSACTION_EXTERNAL) != 0) { if ((ctx->flags & MAIL_INDEX_VIEW_SYNC_FLAG_NOEXPUNGES) != 0) { i_assert(!LOG_IS_BEFORE(seq, offset, @@ -688,10 +727,13 @@ mail_index_view_sync_get_next_transaction(struct mail_index_view_sync_ctx *ctx) /* Apply transaction to view's mapping if needed (meaning we didn't just re-map the view to head mapping). */ if (ctx->sync_map_update && !synced_to_map) { - if ((hdr->type & MAIL_TRANSACTION_EXPUNGE) == 0) T_BEGIN { - ret = mail_index_sync_record(&ctx->sync_map_ctx, - hdr, ctx->data); - } T_END; + if ((hdr->type & (MAIL_TRANSACTION_EXPUNGE | + MAIL_TRANSACTION_EXPUNGE_GUID)) == 0) { + T_BEGIN { + ret = mail_index_sync_record(&ctx->sync_map_ctx, + hdr, ctx->data); + } T_END; + } if (ret < 0) return -1; } diff --git a/src/lib-index/mail-index.h b/src/lib-index/mail-index.h index c1679fa2e8..b525430ef9 100644 --- a/src/lib-index/mail-index.h +++ b/src/lib-index/mail-index.h @@ -165,6 +165,9 @@ struct mail_index_sync_rec { /* MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD, .._REMOVE: */ unsigned int keyword_idx; + + /* MAIL_INDEX_SYNC_TYPE_EXPUNGE: */ + uint8_t guid_128[MAIL_GUID_128_SIZE]; }; enum mail_index_view_sync_type { @@ -400,6 +403,9 @@ void mail_index_append_assign_uids(struct mail_index_transaction *t, /* Expunge record from index. Note that this doesn't affect sequence numbers until transaction is committed and mailbox is synced. */ void mail_index_expunge(struct mail_index_transaction *t, uint32_t seq); +/* Like mail_index_expunge(), but also write message GUID to transaction log. */ +void mail_index_expunge_guid(struct mail_index_transaction *t, uint32_t seq, + const uint8_t guid_128[MAIL_GUID_128_SIZE]); /* Update flags in index. */ void mail_index_update_flags(struct mail_index_transaction *t, uint32_t seq, enum modify_type modify_type, diff --git a/src/lib-index/mail-transaction-log-append.c b/src/lib-index/mail-transaction-log-append.c index e3f7d4965f..95629c4b0e 100644 --- a/src/lib-index/mail-transaction-log-append.c +++ b/src/lib-index/mail-transaction-log-append.c @@ -20,7 +20,8 @@ void mail_transaction_log_append_add(struct mail_transaction_log_append_ctx *ctx memset(&hdr, 0, sizeof(hdr)); hdr.type = type; - if (type == MAIL_TRANSACTION_EXPUNGE) + if (type == MAIL_TRANSACTION_EXPUNGE || + type == MAIL_TRANSACTION_EXPUNGE_GUID) hdr.type |= MAIL_TRANSACTION_EXPUNGE_PROT; if (ctx->external) hdr.type |= MAIL_TRANSACTION_EXTERNAL; diff --git a/src/lib-index/mail-transaction-log-file.c b/src/lib-index/mail-transaction-log-file.c index 407735e11f..b2d8147595 100644 --- a/src/lib-index/mail-transaction-log-file.c +++ b/src/lib-index/mail-transaction-log-file.c @@ -822,6 +822,7 @@ mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr, switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT: + case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT: if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* ignore expunge requests */ break; diff --git a/src/lib-index/mail-transaction-log-view.c b/src/lib-index/mail-transaction-log-view.c index 59c5dda7aa..0c0e88a535 100644 --- a/src/lib-index/mail-transaction-log-view.c +++ b/src/lib-index/mail-transaction-log-view.c @@ -417,6 +417,15 @@ log_view_is_record_valid(struct mail_transaction_log_file *file, } rec_type &= ~MAIL_TRANSACTION_EXPUNGE_PROT; } + if ((hdr->type & MAIL_TRANSACTION_EXPUNGE_GUID) != 0) { + if (rec_type != (MAIL_TRANSACTION_EXPUNGE_GUID | + MAIL_TRANSACTION_EXPUNGE_PROT)) { + mail_transaction_log_file_set_corrupted(file, + "expunge guid record missing protection mask"); + return FALSE; + } + rec_type &= ~MAIL_TRANSACTION_EXPUNGE_PROT; + } if (rec_size == 0) { mail_transaction_log_file_set_corrupted(file, @@ -441,6 +450,13 @@ log_view_is_record_valid(struct mail_transaction_log_file *file, array_create_from_buffer(&uids, &uid_buf, sizeof(struct mail_transaction_expunge)); break; + case MAIL_TRANSACTION_EXPUNGE_GUID: + if ((rec_size % sizeof(struct mail_transaction_expunge_guid)) != 0) { + mail_transaction_log_file_set_corrupted(file, + "Invalid expunge guid record size"); + ret = FALSE; + } + break; case MAIL_TRANSACTION_FLAG_UPDATE: buffer_create_const_data(&uid_buf, data, rec_size); array_create_from_buffer(&uids, &uid_buf, @@ -615,7 +631,9 @@ int mail_transaction_log_view_next(struct mail_transaction_log_view *view, /* drop expunge protection */ if ((hdr->type & MAIL_TRANSACTION_TYPE_MASK) == - (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT)) + (MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT) || + (hdr->type & MAIL_TRANSACTION_TYPE_MASK) == + (MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT)) view->tmp_hdr.type = hdr->type & ~MAIL_TRANSACTION_EXPUNGE_PROT; else view->tmp_hdr.type = hdr->type; diff --git a/src/lib-index/mail-transaction-log.h b/src/lib-index/mail-transaction-log.h index 3abb9d1126..3e43340fe3 100644 --- a/src/lib-index/mail-transaction-log.h +++ b/src/lib-index/mail-transaction-log.h @@ -1,8 +1,7 @@ #ifndef MAIL_TRANSACTION_LOG_H #define MAIL_TRANSACTION_LOG_H -struct mail_index; -struct mail_index_transaction; +#include "mail-index.h" #define MAIL_TRANSACTION_LOG_SUFFIX ".log" @@ -39,6 +38,7 @@ enum mail_transaction_type { MAIL_TRANSACTION_KEYWORD_UPDATE = 0x00000400, MAIL_TRANSACTION_KEYWORD_RESET = 0x00000800, MAIL_TRANSACTION_EXT_ATOMIC_INC = 0x00001000, + MAIL_TRANSACTION_EXPUNGE_GUID = 0x00002000, MAIL_TRANSACTION_TYPE_MASK = 0x0000ffff, @@ -49,7 +49,7 @@ enum mail_transaction_type { /* since we'll expunge mails based on data read from transaction log, try to avoid the possibility of corrupted transaction log expunging - messages. this value is ORed to the actual MAIL_TRANSACTION_EXPUNGE + messages. this value is ORed to the actual MAIL_TRANSACTION_EXPUNGE* flag. if it's not present, assume corrupted log. */ MAIL_TRANSACTION_EXPUNGE_PROT = 0x0000cd90, @@ -65,6 +65,10 @@ struct mail_transaction_header { struct mail_transaction_expunge { uint32_t uid1, uid2; }; +struct mail_transaction_expunge_guid { + uint32_t uid; + uint8_t guid_128[MAIL_GUID_128_SIZE]; +}; struct mail_transaction_flag_update { uint32_t uid1, uid2; diff --git a/src/lib-mail/mail-types.h b/src/lib-mail/mail-types.h index 4703c79436..9796dcdeb7 100644 --- a/src/lib-mail/mail-types.h +++ b/src/lib-mail/mail-types.h @@ -1,6 +1,8 @@ #ifndef MAIL_TYPES_H #define MAIL_TYPES_H +#define MAIL_GUID_128_SIZE 16 + enum mail_flags { MAIL_ANSWERED = 0x01, MAIL_FLAGGED = 0x02, diff --git a/src/lib-storage/index/dbox/dbox-file-fix.c b/src/lib-storage/index/dbox/dbox-file-fix.c index cfb0cca19d..b13dcddfb3 100644 --- a/src/lib-storage/index/dbox/dbox-file-fix.c +++ b/src/lib-storage/index/dbox/dbox-file-fix.c @@ -165,7 +165,7 @@ dbox_file_fix_write_stream(struct dbox_file *file, uoff_t start_offset, bool pre, write_header, have_guid; struct message_size body; struct istream *body_input; - uint8_t guid_128[16]; + uint8_t guid_128[MAIL_GUID_128_SIZE]; int ret; i_stream_seek(file->input, 0); diff --git a/src/lib-storage/index/dbox/dbox-file.c b/src/lib-storage/index/dbox/dbox-file.c index abbdb993a9..7a0d54fd8a 100644 --- a/src/lib-storage/index/dbox/dbox-file.c +++ b/src/lib-storage/index/dbox/dbox-file.c @@ -12,7 +12,6 @@ #include "mkdir-parents.h" #include "fdatasync-path.h" #include "eacces-error.h" -#include "sha1.h" #include "str.h" #include "dbox-storage.h" #include "dbox-file.h" @@ -1029,23 +1028,3 @@ void dbox_msg_header_fill(struct dbox_message_header *dbox_msg_hdr, sizeof(dbox_msg_hdr->message_size_hex)); dbox_msg_hdr->save_lf = '\n'; } - -void dbox_get_guid_128(const char *input, buffer_t *output) -{ - unsigned char sha1_sum[SHA1_RESULTLEN]; - - buffer_set_used_size(output, 0); - if (strlen(input) != DBOX_GUID_BIN_LEN*2 || - hex_to_binary(input, output) < 0 || - output->used != DBOX_GUID_BIN_LEN) { - /* not 128bit hex. use a hash of it instead. */ - buffer_set_used_size(output, 0); - sha1_get_digest(input, strlen(input), sha1_sum); -#if SHA1_RESULTLEN < DBOX_GUID_BIN_LEN -# error not possible -#endif - buffer_append(output, - sha1_sum + SHA1_RESULTLEN - DBOX_GUID_BIN_LEN, - DBOX_GUID_BIN_LEN); - } -} diff --git a/src/lib-storage/index/dbox/dbox-file.h b/src/lib-storage/index/dbox/dbox-file.h index d7bf8d3d5e..6bba0b498e 100644 --- a/src/lib-storage/index/dbox/dbox-file.h +++ b/src/lib-storage/index/dbox/dbox-file.h @@ -206,6 +206,5 @@ int dbox_create_fd(struct dbox_storage *storage, const char *path); int dbox_file_header_write(struct dbox_file *file, struct ostream *output); int dbox_file_read_mail_header(struct dbox_file *file, uoff_t *physical_size_r); int dbox_file_metadata_skip_header(struct dbox_file *file); -void dbox_get_guid_128(const char *input, buffer_t *output); #endif diff --git a/src/lib-storage/index/dbox/dbox-map.c b/src/lib-storage/index/dbox/dbox-map.c index 3602ef05ee..eb37edd6a3 100644 --- a/src/lib-storage/index/dbox/dbox-map.c +++ b/src/lib-storage/index/dbox/dbox-map.c @@ -504,7 +504,7 @@ int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx, const ARRAY_TYPE(uint32_t) *map_uids, int diff) { struct dbox_map *map = ctx->map; - const uint32_t *uids; + const uint32_t *uidp; unsigned int i, count; const void *data; uint32_t seq; @@ -514,13 +514,14 @@ int dbox_map_update_refcounts(struct dbox_map_transaction_context *ctx, if (ctx->trans == NULL) return -1; - uids = array_get(map_uids, &count); + count = array_count(map_uids); for (i = 0; i < count; i++) { - if (!mail_index_lookup_seq(map->view, uids[i], &seq)) { + uidp = array_idx(map_uids, i); + if (!mail_index_lookup_seq(map->view, *uidp, &seq)) { /* we can't refresh map here since view has a transaction open. */ dbox_map_set_corrupted(map, - "refcount update lost map_uid=%u", uids[i]); + "refcount update lost map_uid=%u", *uidp); return -1; } mail_index_lookup_ext(map->view, seq, map->ref_ext_id, diff --git a/src/lib-storage/index/dbox/dbox-save.c b/src/lib-storage/index/dbox/dbox-save.c index fc71c6ea2b..cd47ef3b18 100644 --- a/src/lib-storage/index/dbox/dbox-save.c +++ b/src/lib-storage/index/dbox/dbox-save.c @@ -174,10 +174,9 @@ int dbox_save_continue(struct mail_save_context *_ctx) static void dbox_save_write_metadata(struct dbox_save_context *ctx) { struct dbox_metadata_header metadata_hdr; - uint8_t guid_128[16]; + uint8_t guid_128[MAIL_GUID_128_SIZE]; const char *guid; string_t *str; - buffer_t *guid_buf; uoff_t vsize; memset(&metadata_hdr, 0, sizeof(metadata_hdr)); @@ -195,16 +194,10 @@ static void dbox_save_write_metadata(struct dbox_save_context *ctx) str_printfa(str, "%c%llx\n", DBOX_METADATA_VIRTUAL_SIZE, (unsigned long long)vsize); - /* we can use user-given GUID if - a) we're not saving to a multi-file, - b) it's 128 bit hex-encoded */ guid = ctx->ctx.guid; - if (ctx->ctx.guid != NULL && ctx->cur_file->single_mbox == NULL) { - guid_buf = buffer_create_dynamic(pool_datastack_create(), - sizeof(guid_128)); - dbox_get_guid_128(guid, guid_buf); - memcpy(guid_128, guid_buf->data, sizeof(guid_128)); - } else { + if (ctx->ctx.guid != NULL) + mail_generate_guid_128_hash(guid, guid_128); + else { mail_generate_guid_128(guid_128); guid = binary_to_hex(guid_128, sizeof(guid_128)); } diff --git a/src/lib-storage/index/dbox/dbox-storage-rebuild.c b/src/lib-storage/index/dbox/dbox-storage-rebuild.c index 993fc6b46d..a223b6872f 100644 --- a/src/lib-storage/index/dbox/dbox-storage-rebuild.c +++ b/src/lib-storage/index/dbox/dbox-storage-rebuild.c @@ -17,7 +17,7 @@ #include struct dbox_rebuild_msg { - uint8_t guid_128[DBOX_GUID_BIN_LEN]; + uint8_t guid_128[MAIL_GUID_128_SIZE]; uint32_t file_id; uint32_t offset; uint32_t size; @@ -60,7 +60,7 @@ static unsigned int guid_hash(const void *p) const uint8_t *s = p; unsigned int i, g, h = 0; - for (i = 0; i < DBOX_GUID_BIN_LEN; i++) { + for (i = 0; i < MAIL_GUID_128_SIZE; i++) { h = (h << 4) + s[i]; if ((g = h & 0xf0000000UL)) { h = h ^ (g >> 24); @@ -72,7 +72,7 @@ static unsigned int guid_hash(const void *p) static int guid_cmp(const void *p1, const void *p2) { - return memcmp(p1, p2, DBOX_GUID_BIN_LEN); + return memcmp(p1, p2, MAIL_GUID_128_SIZE); } static struct dbox_storage_rebuild_context * @@ -133,7 +133,6 @@ static int rebuild_add_file(struct dbox_storage_rebuild_context *ctx, const char *fname, *guid; struct dbox_rebuild_msg *rec; uint32_t file_id; - buffer_t *guid_buf; uoff_t offset, prev_offset, size; bool last, expunged, first, fixed = FALSE; int ret = 0; @@ -154,9 +153,6 @@ static int rebuild_add_file(struct dbox_storage_rebuild_context *ctx, ctx->msgs_unsorted = TRUE; ctx->prev_file_id = file_id; - guid_buf = buffer_create_dynamic(pool_datastack_create(), - DBOX_GUID_BIN_LEN); - file = dbox_file_init_multi(ctx->storage, file_id); prev_offset = 0; dbox_file_seek_rewind(file); @@ -198,16 +194,15 @@ static int rebuild_add_file(struct dbox_storage_rebuild_context *ctx, ret = 0; break; } - dbox_get_guid_128(guid, guid_buf); rec = p_new(ctx->pool, struct dbox_rebuild_msg, 1); rec->file_id = file_id; rec->offset = offset; rec->size = file->input->v_offset - offset; - memcpy(rec->guid_128, guid_buf->data, sizeof(rec->guid_128)); + mail_generate_guid_128_hash(guid, rec->guid_128); array_append(&ctx->msgs, &rec, 1); - if (hash_table_lookup(ctx->guid_hash, guid_buf->data) != NULL) { + if (hash_table_lookup(ctx->guid_hash, rec->guid_128) != NULL) { /* duplicate. save this as a refcount=0 to map, so it will eventually be deleted. */ rec->seen_zero_ref_in_map = TRUE; diff --git a/src/lib-storage/index/dbox/dbox-storage.c b/src/lib-storage/index/dbox/dbox-storage.c index a4c08e7da8..76e8a00d5b 100644 --- a/src/lib-storage/index/dbox/dbox-storage.c +++ b/src/lib-storage/index/dbox/dbox-storage.c @@ -176,7 +176,7 @@ dbox_mailbox_alloc(struct mail_storage *storage, struct mailbox_list *list, sizeof(struct dbox_index_header), 0, 0); mbox->guid_ext_id = mail_index_ext_register(mbox->ibox.index, "guid", - 0, DBOX_GUID_BIN_LEN, 1); + 0, MAIL_GUID_128_SIZE, 1); mbox->maildir_uidlist = maildir_uidlist_init_readonly(&mbox->ibox); return &mbox->ibox.box; diff --git a/src/lib-storage/index/dbox/dbox-storage.h b/src/lib-storage/index/dbox/dbox-storage.h index 8544790f33..2b671e2039 100644 --- a/src/lib-storage/index/dbox/dbox-storage.h +++ b/src/lib-storage/index/dbox/dbox-storage.h @@ -20,7 +20,6 @@ #define DBOX_MAIL_FILE_MULTI_FORMAT DBOX_MAIL_FILE_MULTI_PREFIX"%u" #define DBOX_MAIL_FILE_UID_FORMAT DBOX_MAIL_FILE_UID_PREFIX"%u" #define DBOX_MAIL_FILE_BROKEN_COPY_SUFFIX ".broken" -#define DBOX_GUID_BIN_LEN (128/8) /* How often to scan for stale temp files (based on dir's atime) */ #define DBOX_TMP_SCAN_SECS (8*60*60) diff --git a/src/lib-storage/index/dbox/dbox-sync-file.c b/src/lib-storage/index/dbox/dbox-sync-file.c index 948452373d..fe0f017181 100644 --- a/src/lib-storage/index/dbox/dbox-sync-file.c +++ b/src/lib-storage/index/dbox/dbox-sync-file.c @@ -5,6 +5,7 @@ #include "istream.h" #include "ostream.h" #include "str.h" +#include "hex-binary.h" #include "dbox-storage.h" #include "dbox-file.h" #include "dbox-map.h" @@ -268,22 +269,62 @@ dbox_sync_file_move_if_needed(struct dbox_file *file, } } +static int +dbox_sync_verify_expunge_guid(struct dbox_sync_context *ctx, + const struct dbox_sync_expunge *expunge) +{ + const void *data; + uint32_t uid; + + mail_index_lookup_uid(ctx->sync_view, expunge->seq, &uid); + mail_index_lookup_ext(ctx->sync_view, expunge->seq, + ctx->mbox->guid_ext_id, &data, NULL); + if (mail_guid_128_is_empty(expunge->guid_128) || + memcmp(data, expunge->guid_128, MAIL_GUID_128_SIZE) == 0) + return 0; + + mail_storage_set_critical(&ctx->mbox->storage->storage, + "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", + ctx->mbox->ibox.box.vname, uid, + binary_to_hex(data, MAIL_GUID_128_SIZE), + binary_to_hex(expunge->guid_128, MAIL_GUID_128_SIZE)); + return -1; +} + +static int +dbox_sync_verify_expunge_guids(struct dbox_sync_context *ctx, + const struct dbox_sync_file_entry *entry) +{ + const struct dbox_sync_expunge *expunges; + unsigned int i, count; + + expunges = array_get(&entry->expunges, &count); + for (i = 0; i < count; i++) { + if (dbox_sync_verify_expunge_guid(ctx, &expunges[i]) < 0) + return -1; + } + return 0; +} + static void dbox_sync_mark_expunges(struct dbox_sync_context *ctx, - const ARRAY_TYPE(seq_range) *seqs) + const struct dbox_sync_file_entry *entry) { struct mailbox *box = &ctx->mbox->ibox.box; - struct seq_range_iter iter; - unsigned int i; - uint32_t seq, uid; - - seq_range_array_iter_init(&iter, seqs); i = 0; - while (seq_range_array_iter_nth(&iter, i++, &seq)) { - mail_index_expunge(ctx->trans, seq); - if (box->v.sync_notify != NULL) { - mail_index_lookup_uid(ctx->sync_view, seq, &uid); + const struct dbox_sync_expunge *expunges; + unsigned int i, count; + const void *data; + uint32_t uid; + + expunges = array_get(&entry->expunges, &count); + for (i = 0; i < count; i++) { + mail_index_lookup_uid(ctx->sync_view, expunges[i].seq, &uid); + mail_index_lookup_ext(ctx->sync_view, expunges[i].seq, + ctx->mbox->guid_ext_id, &data, NULL); + mail_index_expunge_guid(ctx->trans, expunges[i].seq, data); + + if (box->v.sync_notify != NULL) box->v.sync_notify(box, uid, MAILBOX_SYNC_TYPE_EXPUNGE); - } } } @@ -297,14 +338,17 @@ int dbox_sync_file(struct dbox_sync_context *ctx, file = entry->file_id != 0 ? dbox_file_init_multi(mbox->storage, entry->file_id) : dbox_file_init_single(mbox, entry->uid); - if (!array_is_created(&entry->expunge_map_uids)) { + if (!array_is_created(&entry->expunges)) { /* no expunges - we want to move it */ dbox_sync_file_move_if_needed(file, entry); + } else if (dbox_sync_verify_expunge_guids(ctx, entry) < 0) { + /* guid mismatches, see if index rebuilding helps */ + ret = 0; } else if (entry->uid != 0) { /* single-message file, we can unlink it */ if ((ret = dbox_sync_file_unlink(file)) == 0) { /* file was lost, delete it */ - dbox_sync_mark_expunges(ctx, &entry->expunge_seqs); + dbox_sync_mark_expunges(ctx, entry); ret = 1; } } else { @@ -314,10 +358,10 @@ int dbox_sync_file(struct dbox_sync_context *ctx, FALSE); } if (dbox_map_update_refcounts(ctx->map_trans, - &entry->expunge_map_uids, -1) < 0) + (void *)&entry->expunges, -1) < 0) ret = -1; else - dbox_sync_mark_expunges(ctx, &entry->expunge_seqs); + dbox_sync_mark_expunges(ctx, entry); } dbox_file_unref(&file); return ret; diff --git a/src/lib-storage/index/dbox/dbox-sync.c b/src/lib-storage/index/dbox/dbox-sync.c index 343d026256..e1026b1953 100644 --- a/src/lib-storage/index/dbox/dbox-sync.c +++ b/src/lib-storage/index/dbox/dbox-sync.c @@ -40,6 +40,7 @@ static int dbox_sync_add_seq(struct dbox_sync_context *ctx, uint32_t seq) { struct dbox_sync_file_entry *entry, lookup_entry; + struct dbox_sync_expunge *expunge; uint32_t map_uid; uoff_t offset; int ret; @@ -75,14 +76,16 @@ static int dbox_sync_add_seq(struct dbox_sync_context *ctx, } if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { - if (!array_is_created(&entry->expunge_map_uids)) { - p_array_init(&entry->expunge_map_uids, ctx->pool, - lookup_entry.uid != 0 ? 1 : 3); - p_array_init(&entry->expunge_seqs, ctx->pool, + if (!array_is_created(&entry->expunges)) { + p_array_init(&entry->expunges, ctx->pool, lookup_entry.uid != 0 ? 1 : 3); } - seq_range_array_add(&entry->expunge_seqs, 0, seq); - array_append(&entry->expunge_map_uids, &map_uid, 1); + + expunge = array_append_space(&entry->expunges); + expunge->map_uid = map_uid; + expunge->seq = seq; + memcpy(expunge->guid_128, sync_rec->guid_128, + sizeof(expunge->guid_128)); if (entry->file_id != 0) ctx->have_storage_expunges = TRUE; } else { diff --git a/src/lib-storage/index/dbox/dbox-sync.h b/src/lib-storage/index/dbox/dbox-sync.h index 86ca29ccfb..9757038f4b 100644 --- a/src/lib-storage/index/dbox/dbox-sync.h +++ b/src/lib-storage/index/dbox/dbox-sync.h @@ -11,13 +11,20 @@ enum dbox_sync_flags { DBOX_SYNC_FLAG_NO_PURGE = 0x08 }; +struct dbox_sync_expunge { + /* keep map_uid first, so we can just cast it to + dbox_map_update_refcounts() */ + uint32_t map_uid; + uint32_t seq; + uint8_t guid_128[MAIL_GUID_128_SIZE]; +}; + struct dbox_sync_file_entry { uint32_t uid, file_id; unsigned int move_from_alt:1; unsigned int move_to_alt:1; - ARRAY_TYPE(seq_range) expunge_seqs; - ARRAY_TYPE(uint32_t) expunge_map_uids; + ARRAY_DEFINE(expunges, struct dbox_sync_expunge); }; struct dbox_sync_context { diff --git a/src/lib-storage/index/index-fetch.c b/src/lib-storage/index/index-fetch.c index 58718122b5..a82a3d8795 100644 --- a/src/lib-storage/index/index-fetch.c +++ b/src/lib-storage/index/index-fetch.c @@ -39,23 +39,59 @@ void index_storage_get_uid_range(struct mailbox *box, } } -bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t modseq, - const ARRAY_TYPE(seq_range) *uids, - ARRAY_TYPE(seq_range) *expunged_uids) +static void +add_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges, + const struct mail_transaction_expunge *src, size_t src_size, + const ARRAY_TYPE(seq_range) *uids_filter) +{ + const struct mail_transaction_expunge *end; + struct mailbox_expunge_rec *expunge; + uint32_t uid; + + end = src + src_size / sizeof(*src); + for (; src != end; src++) { + for (uid = src->uid1; uid < src->uid2; uid++) { + if (seq_range_exists(uids_filter, uid)) { + expunge = array_append_space(expunges); + expunge->uid = uid; + } + } + } +} + +static void +add_guid_expunges(ARRAY_TYPE(mailbox_expunge_rec) *expunges, + const struct mail_transaction_expunge_guid *src, + size_t src_size, const ARRAY_TYPE(seq_range) *uids_filter) +{ + const struct mail_transaction_expunge_guid *end; + struct mailbox_expunge_rec *expunge; + + end = src + src_size / sizeof(*src); + for (; src != end; src++) { + if (seq_range_exists(uids_filter, src->uid)) { + expunge = array_append_space(expunges); + expunge->uid = src->uid; + memcpy(expunge->guid_128, src->guid_128, + sizeof(expunge->guid_128)); + } + } +} + +bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges) { #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_transaction_header *thdr; - const struct mail_transaction_expunge *rec, *end; - const struct seq_range *uid_range; - unsigned int count; const void *tdata; - uint32_t log_seq, min_uid, max_uid; + uint32_t log_seq; uoff_t log_offset; bool reset; - if (!mail_index_modseq_get_next_log_offset(ibox->view, modseq, + if (!mail_index_modseq_get_next_log_offset(ibox->view, prev_modseq, &log_seq, &log_offset)) return FALSE; if (log_seq > ibox->view->log_file_head_seq || @@ -74,28 +110,22 @@ bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t modseq, 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) + if ((thdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { + /* skip expunge requests */ 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); - } + } + switch (thdr->type) { + case MAIL_TRANSACTION_EXPUNGE: + add_expunges(expunges, tdata, thdr->size, uids_filter); + break; + case MAIL_TRANSACTION_EXPUNGE_GUID: + add_guid_expunges(expunges, tdata, thdr->size, + uids_filter); + break; } } - /* remove UIDs not in the wanted UIDs range */ - seq_range_array_intersect(expunged_uids, uids); mail_transaction_log_view_close(&log_view); return TRUE; } diff --git a/src/lib-storage/index/index-mail.c b/src/lib-storage/index/index-mail.c index 4804084b30..9c6eda4c36 100644 --- a/src/lib-storage/index/index-mail.c +++ b/src/lib-storage/index/index-mail.c @@ -1446,8 +1446,16 @@ void index_mail_update_keywords(struct mail *mail, enum modify_type modify_type, void index_mail_expunge(struct mail *mail) { struct index_mail *imail = (struct index_mail *)mail; + const char *value; + uint8_t guid_128[MAIL_GUID_128_SIZE]; - mail_index_expunge(imail->trans->trans, mail->seq); + if (mail_get_special(mail, MAIL_FETCH_GUID, &value) < 0) + mail_index_expunge(imail->trans->trans, mail->seq); + else { + mail_generate_guid_128_hash(value, guid_128); + mail_index_expunge_guid(imail->trans->trans, + mail->seq, guid_128); + } } void index_mail_set_cache_corrupted(struct mail *mail, diff --git a/src/lib-storage/index/index-storage.h b/src/lib-storage/index/index-storage.h index d6c845e8eb..e66a0aef61 100644 --- a/src/lib-storage/index/index-storage.h +++ b/src/lib-storage/index/index-storage.h @@ -146,9 +146,9 @@ void index_storage_get_seq_range(struct mailbox *box, void index_storage_get_uid_range(struct mailbox *box, const ARRAY_TYPE(seq_range) *seqs, ARRAY_TYPE(seq_range) *uids); -bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t modseq, - const ARRAY_TYPE(seq_range) *uids, - ARRAY_TYPE(seq_range) *expunged_uids); +bool index_storage_get_expunged_uids(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges); struct mailbox_header_lookup_ctx * index_header_lookup_init(struct mailbox *box, const char *const headers[]); diff --git a/src/lib-storage/index/index-sync-changes.c b/src/lib-storage/index/index-sync-changes.c index 3ba8623fe7..c89e935682 100644 --- a/src/lib-storage/index/index-sync-changes.c +++ b/src/lib-storage/index/index-sync-changes.c @@ -73,21 +73,26 @@ void index_sync_changes_delete_to(struct index_sync_changes_context *ctx, static bool index_sync_changes_have_expunges(struct index_sync_changes_context *ctx, - unsigned int count) + unsigned int count, + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]) { const struct mail_index_sync_rec *syncs; unsigned int i; syncs = array_idx(&ctx->syncs, 0); for (i = 0; i < count; i++) { - if (syncs[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) + if (syncs[i].type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { + memcpy(expunged_guid_128, syncs[i].guid_128, + MAIL_GUID_128_SIZE); return TRUE; + } } return FALSE; } void index_sync_changes_read(struct index_sync_changes_context *ctx, - uint32_t uid, bool *sync_expunge_r) + uint32_t uid, bool *sync_expunge_r, + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]) { struct mail_index_sync_rec *sync_rec = &ctx->sync_rec; uint32_t seq1, seq2; @@ -103,8 +108,11 @@ void index_sync_changes_read(struct index_sync_changes_context *ctx, sync_rec->type != MAIL_INDEX_SYNC_TYPE_APPEND) { array_append(&ctx->syncs, sync_rec, 1); - if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) + if (sync_rec->type == MAIL_INDEX_SYNC_TYPE_EXPUNGE) { *sync_expunge_r = TRUE; + memcpy(expunged_guid_128, sync_rec->guid_128, + MAIL_GUID_128_SIZE); + } } if (!mail_index_sync_next(ctx->index_sync_ctx, sync_rec)) { @@ -145,7 +153,8 @@ void index_sync_changes_read(struct index_sync_changes_context *ctx, if (!*sync_expunge_r && orig_count > 0) { *sync_expunge_r = - index_sync_changes_have_expunges(ctx, orig_count); + index_sync_changes_have_expunges(ctx, orig_count, + expunged_guid_128); } } diff --git a/src/lib-storage/index/index-sync-changes.h b/src/lib-storage/index/index-sync-changes.h index 956348edf6..d62cb202a9 100644 --- a/src/lib-storage/index/index-sync-changes.h +++ b/src/lib-storage/index/index-sync-changes.h @@ -14,7 +14,8 @@ void index_sync_changes_delete_to(struct index_sync_changes_context *ctx, uint32_t last_uid); void index_sync_changes_read(struct index_sync_changes_context *ctx, - uint32_t uid, bool *sync_expunge_r); + uint32_t uid, bool *sync_expunge_r, + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]); bool index_sync_changes_have(struct index_sync_changes_context *ctx); uint32_t index_sync_changes_get_next_uid(struct index_sync_changes_context *ctx); diff --git a/src/lib-storage/index/maildir/maildir-sync-index.c b/src/lib-storage/index/maildir/maildir-sync-index.c index cd5f595b87..1915e2af0f 100644 --- a/src/lib-storage/index/maildir/maildir-sync-index.c +++ b/src/lib-storage/index/maildir/maildir-sync-index.c @@ -3,6 +3,7 @@ #include "lib.h" #include "ioloop.h" #include "array.h" +#include "hex-binary.h" #include "maildir-storage.h" #include "index-sync-changes.h" #include "maildir-uidlist.h" @@ -39,6 +40,54 @@ maildir_sync_get_keywords_sync_ctx(struct maildir_index_sync_context *ctx) return ctx->keywords_sync_ctx; } +static void +maildir_index_expunge(struct maildir_index_sync_context *ctx, uint32_t seq) +{ + enum maildir_uidlist_rec_flag flags; + uint8_t guid_128[MAIL_GUID_128_SIZE]; + const char *fname; + uint32_t uid; + + mail_index_lookup_uid(ctx->view, seq, &uid); + if (maildir_uidlist_lookup(ctx->mbox->uidlist, uid, + &flags, &fname) <= 0) + memset(guid_128, 0, sizeof(guid_128)); + else T_BEGIN { + mail_generate_guid_128_hash(t_strcut(fname, ':'), + guid_128); + } T_END; + + mail_index_expunge_guid(ctx->trans, ctx->seq, guid_128); +} + +static bool +maildir_expunge_is_valid_guid(struct maildir_index_sync_context *ctx, + const char *filename, + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]) +{ + uint8_t guid_128[MAIL_GUID_128_SIZE]; + + if (mail_guid_128_is_empty(expunged_guid_128)) { + /* no GUID associated with expunge */ + return TRUE; + } + + T_BEGIN { + mail_generate_guid_128_hash(t_strcut(filename, ':'), + guid_128); + } T_END; + + if (memcmp(guid_128, expunged_guid_128, sizeof(guid_128)) == 0) + return TRUE; + + mail_storage_set_critical(&ctx->mbox->storage->storage, + "Mailbox %s: Expunged GUID mismatch for UID %u: %s vs %s", + ctx->mbox->ibox.box.vname, ctx->uid, + binary_to_hex(guid_128, sizeof(guid_128)), + binary_to_hex(expunged_guid_128, MAIL_GUID_128_SIZE)); + return FALSE; +} + static int maildir_expunge(struct maildir_mailbox *mbox, const char *path, struct maildir_index_sync_context *ctx) { @@ -49,7 +98,6 @@ static int maildir_expunge(struct maildir_mailbox *mbox, const char *path, box->v.sync_notify(box, ctx->uid, MAILBOX_SYNC_TYPE_EXPUNGE); } - mail_index_expunge(ctx->trans, ctx->seq); ctx->changed = TRUE; return 1; } @@ -401,6 +449,7 @@ int maildir_sync_index(struct maildir_index_sync_context *ctx, int ret = 0; time_t time_before_sync; struct stat st; + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]; bool expunged, full_rescan = FALSE; i_assert(!mbox->syncing_commit); @@ -485,7 +534,7 @@ int maildir_sync_index(struct maildir_index_sync_context *ctx, rec = mail_index_lookup(view, seq); if (uid > rec->uid) { /* expunged */ - mail_index_expunge(trans, seq); + maildir_index_expunge(ctx, seq); goto again; } @@ -497,12 +546,16 @@ int maildir_sync_index(struct maildir_index_sync_context *ctx, continue; } - index_sync_changes_read(ctx->sync_changes, rec->uid, &expunged); + index_sync_changes_read(ctx->sync_changes, rec->uid, &expunged, + expunged_guid_128); if (expunged) { + if (!maildir_expunge_is_valid_guid(ctx, filename, + expunged_guid_128)) + continue; if (maildir_file_do(mbox, ctx->uid, maildir_expunge, ctx) >= 0) { /* successful expunge */ - mail_index_expunge(trans, seq); + maildir_index_expunge(ctx, seq); } if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0) maildir_sync_notify(ctx->maildir_sync_ctx); @@ -551,7 +604,7 @@ int maildir_sync_index(struct maildir_index_sync_context *ctx, if (!partial) { /* expunge the rest */ for (seq++; seq <= hdr->messages_count; seq++) - mail_index_expunge(trans, seq); + maildir_index_expunge(ctx, seq); } /* add \Recent flags. use updated view so it contains newly diff --git a/src/lib-storage/index/mbox/mbox-sync.c b/src/lib-storage/index/mbox/mbox-sync.c index 3605028890..741f0aef2e 100644 --- a/src/lib-storage/index/mbox/mbox-sync.c +++ b/src/lib-storage/index/mbox/mbox-sync.c @@ -169,12 +169,15 @@ mbox_sync_read_next_mail(struct mbox_sync_context *sync_ctx, static void mbox_sync_read_index_syncs(struct mbox_sync_context *sync_ctx, uint32_t uid, bool *sync_expunge_r) { + uint8_t expunged_guid_128[MAIL_GUID_128_SIZE]; + if (uid == 0 || sync_ctx->index_reset) { /* nothing for this or the future ones */ uid = (uint32_t)-1; } - index_sync_changes_read(sync_ctx->sync_changes, uid, sync_expunge_r); + index_sync_changes_read(sync_ctx->sync_changes, uid, sync_expunge_r, + expunged_guid_128); if (sync_ctx->mbox->ibox.backend_readonly) { /* we can't expunge anything from read-only mboxes */ *sync_expunge_r = FALSE; diff --git a/src/lib-storage/mail-storage-private.h b/src/lib-storage/mail-storage-private.h index 424df38bcd..b002d5c782 100644 --- a/src/lib-storage/mail-storage-private.h +++ b/src/lib-storage/mail-storage-private.h @@ -163,9 +163,9 @@ struct mailbox_vfuncs { void (*get_uid_range)(struct mailbox *box, const ARRAY_TYPE(seq_range) *seqs, ARRAY_TYPE(seq_range) *uids); - bool (*get_expunged_uids)(struct mailbox *box, uint64_t modseq, - const ARRAY_TYPE(seq_range) *uids, - ARRAY_TYPE(seq_range) *expunged_uids); + bool (*get_expunges)(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges); bool (*get_virtual_uid)(struct mailbox *box, const char *backend_mailbox, uint32_t backend_uidvalidity, @@ -428,10 +428,13 @@ void mail_storage_set_internal_error(struct mail_storage *storage); bool mail_storage_set_error_from_errno(struct mail_storage *storage); const char *mail_generate_guid_string(void); -void mail_generate_guid_128(uint8_t guid[16]); +void mail_generate_guid_128(uint8_t guid[MAIL_GUID_128_SIZE]); +void mail_generate_guid_128_hash(const char *input, + uint8_t guid[MAIL_GUID_128_SIZE]); int mail_set_aborted(struct mail *mail); void mail_set_expunged(struct mail *mail); void mailbox_set_deleted(struct mailbox *box); +bool mail_guid_128_is_empty(const uint8_t guid_128[MAIL_GUID_128_SIZE]); bool mailbox_guid_is_empty(const uint8_t guid[MAILBOX_GUID_SIZE]); #endif diff --git a/src/lib-storage/mail-storage.c b/src/lib-storage/mail-storage.c index 172722ca9b..2898212d46 100644 --- a/src/lib-storage/mail-storage.c +++ b/src/lib-storage/mail-storage.c @@ -709,11 +709,12 @@ void mailbox_get_uid_range(struct mailbox *box, box->v.get_uid_range(box, seqs, uids); } -bool mailbox_get_expunged_uids(struct mailbox *box, uint64_t modseq, - const ARRAY_TYPE(seq_range) *uids, - ARRAY_TYPE(seq_range) *expunged_uids) +bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges) { - return box->v.get_expunged_uids(box, modseq, uids, expunged_uids); + return box->v.get_expunges(box, prev_modseq, + uids_filter, expunges); } bool mailbox_get_virtual_uid(struct mailbox *box, const char *backend_mailbox, @@ -1072,13 +1073,18 @@ void mailbox_set_deleted(struct mailbox *box) box->mailbox_deleted = TRUE; } -bool mailbox_guid_is_empty(const uint8_t guid[MAILBOX_GUID_SIZE]) +bool mail_guid_128_is_empty(const uint8_t guid_128[MAIL_GUID_128_SIZE]) { unsigned int i; for (i = 0; i < MAILBOX_GUID_SIZE; i++) { - if (guid[i] != 0) + if (guid_128[i] != 0) return FALSE; } return TRUE; } + +bool mailbox_guid_is_empty(const uint8_t guid[MAILBOX_GUID_SIZE]) +{ + return mail_guid_128_is_empty(guid); +} diff --git a/src/lib-storage/mail-storage.h b/src/lib-storage/mail-storage.h index af2080aa76..7f8ecc49f4 100644 --- a/src/lib-storage/mail-storage.h +++ b/src/lib-storage/mail-storage.h @@ -170,7 +170,7 @@ struct mail_save_context; struct mailbox; struct mailbox_transaction_context; -#define MAILBOX_GUID_SIZE 16 +#define MAILBOX_GUID_SIZE MAIL_GUID_128_SIZE struct mailbox_status { uint32_t messages; uint32_t recent; @@ -204,6 +204,15 @@ struct mailbox_sync_rec { enum mailbox_sync_type type; }; +struct mailbox_expunge_rec { + /* IMAP UID */ + uint32_t uid; + /* 128 bit GUID. If the actual GUID has a different size, this + contains last bits of its SHA1 sum. */ + uint8_t guid_128[MAIL_GUID_128_SIZE]; +}; +ARRAY_DEFINE_TYPE(mailbox_expunge_rec, struct mailbox_expunge_rec); + enum mail_lookup_abort { /* Perform everything no matter what it takes */ MAIL_LOOKUP_ABORT_NEVER = 0, @@ -424,12 +433,13 @@ void mailbox_get_seq_range(struct mailbox *box, uint32_t uid1, uint32_t uid2, void mailbox_get_uid_range(struct mailbox *box, const ARRAY_TYPE(seq_range) *seqs, ARRAY_TYPE(seq_range) *uids); -/* 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); +/* Get list of messages' that have been expunged after prev_modseq and that + exist in uids_filter 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 (but expunged_uids is still set as best as it can). */ +bool mailbox_get_expunges(struct mailbox *box, uint64_t prev_modseq, + const ARRAY_TYPE(seq_range) *uids_filter, + ARRAY_TYPE(mailbox_expunge_rec) *expunges); /* If box is a virtual mailbox, look up UID for the given backend message. Returns TRUE if found, FALSE if not. */ bool mailbox_get_virtual_uid(struct mailbox *box, const char *backend_mailbox, diff --git a/src/lib-storage/mail.c b/src/lib-storage/mail.c index ddc3d05c18..65e6dc8823 100644 --- a/src/lib-storage/mail.c +++ b/src/lib-storage/mail.c @@ -2,7 +2,10 @@ #include "lib.h" #include "ioloop.h" +#include "buffer.h" +#include "hex-binary.h" #include "crc32.h" +#include "sha1.h" #include "hostpid.h" #include "mail-storage-private.h" @@ -237,7 +240,7 @@ const char *mail_generate_guid_string(void) pid, my_hostname); } -void mail_generate_guid_128(uint8_t guid[16]) +void mail_generate_guid_128(uint8_t guid[MAIL_GUID_128_SIZE]) { static struct timespec ts = { 0, 0 }; static uint8_t guid_static[8]; @@ -276,3 +279,25 @@ void mail_generate_guid_128(uint8_t guid[16]) guid[7] = (ts.tv_sec & 0xff000000) >> 24; memcpy(guid + 8, guid_static, 8); } + +void mail_generate_guid_128_hash(const char *input, + uint8_t guid[MAIL_GUID_128_SIZE]) +{ + unsigned char sha1_sum[SHA1_RESULTLEN]; + buffer_t buf; + + buffer_create_data(&buf, guid, MAIL_GUID_128_SIZE); + if (strlen(input) != MAIL_GUID_128_SIZE*2 || + hex_to_binary(input, &buf) < 0 || + buf.used != MAIL_GUID_128_SIZE) { + /* not 128bit hex. use a hash of it instead. */ + buffer_set_used_size(&buf, 0); + sha1_get_digest(input, strlen(input), sha1_sum); +#if SHA1_RESULTLEN < DBOX_GUID_BIN_LEN +# error not possible +#endif + buffer_append(&buf, + sha1_sum + SHA1_RESULTLEN - MAIL_GUID_128_SIZE, + MAIL_GUID_128_SIZE); + } +} diff --git a/src/lib-storage/test-mailbox.c b/src/lib-storage/test-mailbox.c index 4d75204763..53ba8cc7a6 100644 --- a/src/lib-storage/test-mailbox.c +++ b/src/lib-storage/test-mailbox.c @@ -192,9 +192,9 @@ test_mailbox_get_uid_range(struct mailbox *box ATTR_UNUSED, static bool test_mailbox_get_expunged_uids(struct mailbox *box ATTR_UNUSED, - uint64_t modseq ATTR_UNUSED, - const ARRAY_TYPE(seq_range) *uids ATTR_UNUSED, - ARRAY_TYPE(seq_range) *expunged_uids ATTR_UNUSED) + uint64_t prev_modseq ATTR_UNUSED, + const ARRAY_TYPE(seq_range) *uids_filter ATTR_UNUSED, + ARRAY_TYPE(mailbox_expunge_rec) *expunges ATTR_UNUSED) { return FALSE; } diff --git a/src/util/logview.c b/src/util/logview.c index 0a74883a8d..6acbab07c4 100644 --- a/src/util/logview.c +++ b/src/util/logview.c @@ -1,6 +1,7 @@ /* Copyright (c) 2007-2009 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "hex-binary.h" #include "mail-index-private.h" #include "mail-transaction-log.h" @@ -54,6 +55,7 @@ mail_transaction_header_has_modseq(const struct mail_transaction_header *hdr) { switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) { case MAIL_TRANSACTION_EXPUNGE | MAIL_TRANSACTION_EXPUNGE_PROT: + case MAIL_TRANSACTION_EXPUNGE_GUID | MAIL_TRANSACTION_EXPUNGE_PROT: if ((hdr->type & MAIL_TRANSACTION_EXTERNAL) == 0) { /* ignore expunge requests */ break; @@ -75,6 +77,9 @@ static const char *log_record_type(unsigned int type) case MAIL_TRANSACTION_EXPUNGE|MAIL_TRANSACTION_EXPUNGE_PROT: name = "expunge"; break; + case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: + name = "expunge-guid"; + break; case MAIL_TRANSACTION_APPEND: name = "append"; break; @@ -248,6 +253,16 @@ static void log_record_print(const struct mail_transaction_header *hdr, printf("\n"); break; } + case MAIL_TRANSACTION_EXPUNGE_GUID|MAIL_TRANSACTION_EXPUNGE_PROT: { + const struct mail_transaction_expunge_guid *exp = data; + + for (; size > 0; size -= sizeof(*exp), exp++) { + printf(" - %u (guid ", exp->uid); + print_data(exp->guid_128, sizeof(exp->guid_128)); + printf(")\n"); + } + break; + } case MAIL_TRANSACTION_APPEND: { const struct mail_index_record *rec = data;