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)
{
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;
}
}
}
-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,
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: {
}
}
+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;
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;
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:
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
/* 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) ||
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);
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) {
#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)
}
}
+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)
{
}
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);
}
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;
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;
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);
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,
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;
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
} 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));
}
}
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;
}
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)
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)
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);
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;
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;
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;
+ }
}
}
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,
/* 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;
}
/* 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 {
/* 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,
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;
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;
}
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,
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,
/* 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;
#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"
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,
/* 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,
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;
#ifndef MAIL_TYPES_H
#define MAIL_TYPES_H
+#define MAIL_GUID_128_SIZE 16
+
enum mail_flags {
MAIL_ANSWERED = 0x01,
MAIL_FLAGGED = 0x02,
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);
#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"
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);
- }
-}
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
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;
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,
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));
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));
}
#include <unistd.h>
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;
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);
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 *
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;
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);
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;
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;
#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)
#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"
}
}
+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);
- }
}
}
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 {
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;
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;
}
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 {
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 {
}
}
-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 ||
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;
}
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,
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[]);
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;
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)) {
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);
}
}
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);
#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"
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)
{
box->v.sync_notify(box, ctx->uid,
MAILBOX_SYNC_TYPE_EXPUNGE);
}
- mail_index_expunge(ctx->trans, ctx->seq);
ctx->changed = TRUE;
return 1;
}
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);
rec = mail_index_lookup(view, seq);
if (uid > rec->uid) {
/* expunged */
- mail_index_expunge(trans, seq);
+ maildir_index_expunge(ctx, seq);
goto again;
}
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);
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
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;
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,
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
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,
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);
+}
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;
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,
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,
#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"
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];
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);
+ }
+}
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;
}
/* 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"
{
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;
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;
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;