changeset 6037:d911d943438e HEAD

Recent flag handling rewrite. Still not perfect with maildir.
author Timo Sirainen <tss@iki.fi>
date Mon, 16 Jul 2007 09:48:02 +0300
parents 5d3a941444f1
children cadb5b7cd919
files src/lib-index/mail-index-fsck.c src/lib-index/mail-index-map.c src/lib-index/mail-index-sync-update.c src/lib-index/mail-index-sync.c src/lib-index/mail-index-transaction-private.h src/lib-index/mail-index-transaction-view.c src/lib-index/mail-index-transaction.c src/lib-index/mail-index-view-sync.c src/lib-index/mail-index-view.c src/lib-index/mail-index.h src/lib-mail/mail-types.h src/lib-storage/index/cydir/cydir-save.c src/lib-storage/index/cydir/cydir-sync.c src/lib-storage/index/dbox/dbox-save.c src/lib-storage/index/dbox/dbox-sync.c src/lib-storage/index/index-mail.c src/lib-storage/index/index-status.c src/lib-storage/index/index-storage.c src/lib-storage/index/index-storage.h src/lib-storage/index/index-sync.c src/lib-storage/index/maildir/maildir-save.c src/lib-storage/index/maildir/maildir-storage.c src/lib-storage/index/maildir/maildir-sync-index.c src/lib-storage/index/maildir/maildir-sync.c src/lib-storage/index/maildir/maildir-uidlist.c src/lib-storage/index/maildir/maildir-uidlist.h src/lib-storage/index/mbox/mbox-sync-private.h src/lib-storage/index/mbox/mbox-sync.c src/lib/seq-range-array.c src/lib/seq-range-array.h src/util/idxview.c
diffstat 31 files changed, 329 insertions(+), 393 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-index/mail-index-fsck.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-fsck.c	Mon Jul 16 09:48:02 2007 +0300
@@ -72,11 +72,9 @@
 	hdr.log_file_seq = file_seq;
 
 	hdr.messages_count = 0;
-	hdr.recent_messages_count = 0;
 	hdr.seen_messages_count = 0;
 	hdr.deleted_messages_count = 0;
 
-	hdr.first_recent_uid_lowwater = 0;
 	hdr.first_unseen_uid_lowwater = 0;
 	hdr.first_deleted_uid_lowwater = 0;
 
@@ -88,16 +86,11 @@
 		}
 
 		hdr.messages_count++;
-		if ((rec->flags & MAIL_RECENT) != 0)
-			hdr.recent_messages_count++;
 		if ((rec->flags & MAIL_SEEN) != 0)
 			hdr.seen_messages_count++;
 		if ((rec->flags & MAIL_DELETED) != 0)
 			hdr.deleted_messages_count++;
 
-		if ((rec->flags & MAIL_RECENT) != 0 &&
-		    hdr.first_recent_uid_lowwater == 0)
-			hdr.first_recent_uid_lowwater = rec->uid;
 		if ((rec->flags & MAIL_SEEN) == 0 &&
 		    hdr.first_unseen_uid_lowwater == 0)
 			hdr.first_unseen_uid_lowwater = rec->uid;
@@ -115,12 +108,14 @@
 		hdr.next_uid = last_uid+1;
 	}
 
-	if (hdr.first_recent_uid_lowwater == 0)
-                hdr.first_recent_uid_lowwater = hdr.next_uid;
 	if (hdr.first_unseen_uid_lowwater == 0)
                 hdr.first_unseen_uid_lowwater = hdr.next_uid;
 	if (hdr.first_deleted_uid_lowwater == 0)
                 hdr.first_deleted_uid_lowwater = hdr.next_uid;
+	if (hdr.first_recent_uid > hdr.next_uid)
+		hdr.first_recent_uid = hdr.next_uid;
+	if (hdr.first_recent_uid == 0)
+		hdr.first_recent_uid = 1;
 
         CHECK(log_file_seq, !=);
         CHECK(log_file_head_offset, !=);
@@ -128,13 +123,12 @@
 
 	CHECK(uid_validity, !=);
         CHECK(messages_count, !=);
-        CHECK(recent_messages_count, !=);
         CHECK(seen_messages_count, !=);
         CHECK(deleted_messages_count, !=);
 
-        CHECK(first_recent_uid_lowwater, <);
         CHECK(first_unseen_uid_lowwater, <);
 	CHECK(first_deleted_uid_lowwater, <);
+	CHECK(first_recent_uid, !=);
 
 	map->hdr = hdr;
 	return 1;
--- a/src/lib-index/mail-index-map.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-map.c	Mon Jul 16 09:48:02 2007 +0300
@@ -301,11 +301,11 @@
 	if (hdr->next_uid == 0)
 		return 0;
 
-	if (hdr->recent_messages_count > hdr->messages_count ||
-	    hdr->seen_messages_count > hdr->messages_count ||
+	if (hdr->seen_messages_count > hdr->messages_count ||
 	    hdr->deleted_messages_count > hdr->messages_count)
 		return 0;
-	if (hdr->first_recent_uid_lowwater > hdr->next_uid ||
+	if (hdr->first_recent_uid == 0 ||
+	    hdr->first_recent_uid > hdr->next_uid ||
 	    hdr->first_unseen_uid_lowwater > hdr->next_uid ||
 	    hdr->first_deleted_uid_lowwater > hdr->next_uid)
 		return 0;
@@ -638,6 +638,7 @@
 	hdr->indexid = index->indexid;
 	hdr->log_file_seq = 1;
 	hdr->next_uid = 1;
+	hdr->first_recent_uid = 1;
 }
 
 struct mail_index_map *mail_index_map_alloc(struct mail_index *index)
--- a/src/lib-index/mail-index-sync-update.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-sync-update.c	Mon Jul 16 09:48:02 2007 +0300
@@ -93,26 +93,6 @@
 				uint8_t old_flags, uint8_t new_flags,
 				const char **error_r)
 {
-	if (((old_flags ^ new_flags) & MAIL_RECENT) != 0) {
-		/* different recent-flag */
-		if ((old_flags & MAIL_RECENT) == 0) {
-			hdr->recent_messages_count++;
-			if (hdr->recent_messages_count > hdr->messages_count) {
-				*error_r = "Recent counter wrong";
-				return -1;
-			}
-		} else {
-			if (hdr->recent_messages_count == 0 ||
-			    hdr->recent_messages_count > hdr->messages_count) {
-				*error_r = "Recent counter wrong";
-				return -1;
-			}
-
-			if (--hdr->recent_messages_count == 0)
-				hdr->first_recent_uid_lowwater = hdr->next_uid;
-		}
-	}
-
 	if (((old_flags ^ new_flags) & MAIL_SEEN) != 0) {
 		/* different seen-flag */
 		if ((old_flags & MAIL_SEEN) != 0) {
@@ -169,9 +149,6 @@
 mail_index_header_update_lowwaters(struct mail_index_header *hdr,
 				   const struct mail_index_record *rec)
 {
-	if ((rec->flags & MAIL_RECENT) != 0 &&
-	    rec->uid < hdr->first_recent_uid_lowwater)
-		hdr->first_recent_uid_lowwater = rec->uid;
 	if ((rec->flags & MAIL_SEEN) == 0 &&
 	    rec->uid < hdr->first_unseen_uid_lowwater)
 		hdr->first_unseen_uid_lowwater = rec->uid;
@@ -335,7 +312,7 @@
         flag_mask = ~u->remove_flags;
 
 	if (((u->add_flags | u->remove_flags) &
-	     (MAIL_SEEN | MAIL_DELETED | MAIL_RECENT)) == 0) {
+	     (MAIL_SEEN | MAIL_DELETED)) == 0) {
 		/* we're not modifying any counted/lowwatered flags */
 		for (idx = seq1-1; idx < seq2; idx++) {
 			rec = MAIL_INDEX_MAP_IDX(view->map, idx);
@@ -626,7 +603,7 @@
 void mail_index_map_check(struct mail_index_map *map)
 {
 	const struct mail_index_header *hdr = &map->hdr;
-	unsigned int i, del = 0, recent = 0, seen = 0;
+	unsigned int i, del = 0, seen = 0;
 
 	i_assert(hdr->messages_count == map->records_count);
 	for (i = 0; i < map->records_count; i++) {
@@ -638,17 +615,12 @@
 			i_assert(rec->uid >= hdr->first_deleted_uid_lowwater);
 			del++;
 		}
-		if (rec->flags & MAIL_RECENT) {
-			i_assert(rec->uid >= hdr->first_recent_uid_lowwater);
-			recent++;
-		}
 		if (rec->flags & MAIL_SEEN)
 			seen++;
 		else
 			i_assert(rec->uid >= hdr->first_unseen_uid_lowwater);
 	}
 	i_assert(del == hdr->deleted_messages_count);
-	i_assert(recent == hdr->recent_messages_count);
 	i_assert(seen == hdr->seen_messages_count);
 }
 #endif
--- a/src/lib-index/mail-index-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -15,6 +15,7 @@
 	struct mail_index *index;
 	struct mail_index_view *view;
 	struct mail_index_transaction *sync_trans, *ext_trans;
+	enum mail_index_sync_flags flags;
 
 	const struct mail_transaction_header *hdr;
 	const void *data;
@@ -170,27 +171,6 @@
 	return 0;
 }
 
-static int mail_index_sync_add_recent_updates(struct mail_index_sync_ctx *ctx)
-{
-	const struct mail_index_record *rec;
-	uint32_t seq, messages_count;
-	bool seen_recent = FALSE;
-
-	messages_count = mail_index_view_get_messages_count(ctx->view);
-	for (seq = 1; seq <= messages_count; seq++) {
-		if (mail_index_lookup(ctx->view, seq, &rec) < 0)
-			return -1;
-
-		if ((rec->flags & MAIL_RECENT) != 0) {
-			seen_recent = TRUE;
-			mail_index_update_flags(ctx->sync_trans, rec->uid,
-						MODIFY_REMOVE, MAIL_RECENT);
-		}
-	}
-
-	return 0;
-}
-
 static void
 mail_index_sync_update_mailbox_pos(struct mail_index_sync_ctx *ctx)
 {
@@ -205,8 +185,7 @@
 }
 
 static int
-mail_index_sync_read_and_sort(struct mail_index_sync_ctx *ctx,
-			      enum mail_index_sync_flags flags)
+mail_index_sync_read_and_sort(struct mail_index_sync_ctx *ctx)
 {
 	struct mail_index_transaction *sync_trans = ctx->sync_trans;
 	struct mail_index_sync_list *synclist;
@@ -215,17 +194,12 @@
 	int ret;
 
 	if ((ctx->view->map->hdr.flags & MAIL_INDEX_HDR_FLAG_HAVE_DIRTY) &&
-	    (flags & MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY) != 0) {
+	    (ctx->flags & MAIL_INDEX_SYNC_FLAG_FLUSH_DIRTY) != 0) {
 		/* show dirty flags as flag updates */
 		if (mail_index_sync_add_dirty_updates(ctx) < 0)
 			return -1;
 	}
 
-	if ((flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0) {
-		if (mail_index_sync_add_recent_updates(ctx) < 0)
-			return -1;
-	}
-
 	/* read all transactions from log into a transaction in memory.
 	   skip the external ones, they're already synced to mailbox and
 	   included in our view */
@@ -287,7 +261,7 @@
 		     enum mail_index_sync_flags flags,
 		     uint32_t log_file_seq, uoff_t log_file_offset)
 {
-	if (hdr->recent_messages_count > 0 &&
+	if (hdr->first_recent_uid < hdr->next_uid &&
 	    (flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0)
 		return TRUE;
 
@@ -385,6 +359,7 @@
 	ctx->index = index;
 	ctx->last_tail_seq = hdr->log_file_seq;
 	ctx->last_tail_offset = hdr->log_file_tail_offset;
+	ctx->flags = flags;
 
 	ctx->view = mail_index_view_open(index);
 
@@ -410,7 +385,7 @@
 
 	/* we need to have all the transactions sorted to optimize
 	   caller's mailbox access patterns */
-	if (mail_index_sync_read_and_sort(ctx, flags) < 0) {
+	if (mail_index_sync_read_and_sort(ctx) < 0) {
                 mail_index_sync_rollback(&ctx);
 		return -1;
 	}
@@ -599,7 +574,7 @@
 {
         struct mail_index_sync_ctx *ctx = *_ctx;
 	struct mail_index *index = ctx->index;
-	uint32_t seq, diff;
+	uint32_t seq, diff, next_uid;
 	uoff_t offset;
 	bool want_rotate;
 	int ret = 0;
@@ -612,6 +587,14 @@
 		(void)mail_cache_compress(index->cache, ctx->ext_trans);
 	}
 
+	next_uid = mail_index_transaction_get_next_uid(ctx->ext_trans);
+	if ((ctx->flags & MAIL_INDEX_SYNC_FLAG_DROP_RECENT) != 0 &&
+	    index->map->hdr.first_recent_uid < next_uid) {
+		mail_index_update_header(ctx->ext_trans,
+			offsetof(struct mail_index_header, first_recent_uid),
+			&next_uid, sizeof(next_uid), FALSE);
+	}
+
 	if (mail_index_transaction_commit(&ctx->ext_trans, &seq, &offset) < 0) {
 		mail_index_sync_end(&ctx);
 		return -1;
--- a/src/lib-index/mail-index-transaction-private.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-transaction-private.h	Mon Jul 16 09:48:02 2007 +0300
@@ -89,6 +89,7 @@
 void mail_index_transaction_unref(struct mail_index_transaction **t);
 
 void mail_index_transaction_sort_appends(struct mail_index_transaction *t);
+uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t);
 
 bool mail_index_seq_array_lookup(const ARRAY_TYPE(seq_array) *array,
 				 uint32_t seq, unsigned int *idx_r);
--- a/src/lib-index/mail-index-transaction-view.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-transaction-view.c	Mon Jul 16 09:48:02 2007 +0300
@@ -42,23 +42,16 @@
 	struct mail_index_view_transaction *tview =
                 (struct mail_index_view_transaction *)view;
 	const struct mail_index_header *hdr;
-	const struct mail_index_record *recs;
-	unsigned int count;
+	uint32_t next_uid;
 
 	/* FIXME: header counters may not be correct */
 	hdr = tview->super->get_header(view);
 
-	if (array_is_created(&tview->t->appends)) {
-		/* update next_uid from appends, if UIDs have been given yet */
-		mail_index_transaction_sort_appends(tview->t);
-
-		recs = array_get(&tview->t->appends, &count);
-		if (count > 0 && recs[count-1].uid != 0) {
-			i_assert(recs[count-1].uid >= hdr->next_uid);
-			tview->hdr = *hdr;
-			tview->hdr.next_uid = recs[count-1].uid + 1;
-			hdr = &tview->hdr;
-		}
+	next_uid = mail_index_transaction_get_next_uid(tview->t);
+	if (next_uid != hdr->next_uid) {
+		tview->hdr = *hdr;
+		tview->hdr.next_uid = next_uid;
+		hdr = &tview->hdr;
 	}
 	return hdr;
 }
--- a/src/lib-index/mail-index-transaction.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-transaction.c	Mon Jul 16 09:48:02 2007 +0300
@@ -424,6 +424,40 @@
 	t->appends_nonsorted = FALSE;
 }
 
+uint32_t mail_index_transaction_get_next_uid(struct mail_index_transaction *t)
+{
+	const struct mail_index_header *hdr;
+	const struct mail_index_record *recs;
+	unsigned int count, offset;
+	uint32_t next_uid;
+
+	next_uid = t->view->map->hdr.next_uid;
+	if (array_is_created(&t->appends)) {
+		/* get next_uid from appends if they have UIDs */
+		mail_index_transaction_sort_appends(t);
+
+		recs = array_get(&t->appends, &count);
+		if (count > 0 && recs[count-1].uid != 0) {
+			i_assert(recs[count-1].uid >= next_uid);
+			next_uid = recs[count-1].uid + 1;
+		}
+	}
+
+	/* see if it's been updated in pre/post header changes */
+	offset = offsetof(struct mail_index_header, next_uid);
+	if (t->post_hdr_mask[offset] != 0) {
+		hdr = (const void *)t->post_hdr_change;
+		if (hdr->next_uid > next_uid)
+			next_uid = hdr->next_uid;
+	}
+	if (t->pre_hdr_mask[offset] != 0) {
+		hdr = (const void *)t->pre_hdr_change;
+		if (hdr->next_uid > next_uid)
+			next_uid = hdr->next_uid;
+	}
+	return next_uid;
+}
+
 static int _mail_index_transaction_commit(struct mail_index_transaction *t,
 					  uint32_t *log_file_seq_r,
 					  uoff_t *log_file_offset_r)
--- a/src/lib-index/mail-index-view-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-view-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -466,7 +466,7 @@
 
 #define FLAG_UPDATE_IS_INTERNAL(u) \
 	((((u)->add_flags | (u)->remove_flags) & \
-	  ~(MAIL_INDEX_MAIL_FLAG_DIRTY | MAIL_RECENT)) == 0)
+	  ~MAIL_INDEX_MAIL_FLAG_DIRTY) == 0)
 
 static bool
 mail_index_view_sync_get_rec(struct mail_index_view_sync_ctx *ctx,
--- a/src/lib-index/mail-index-view.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index-view.c	Mon Jul 16 09:48:02 2007 +0300
@@ -339,8 +339,6 @@
 
 	*seq_r = 0;
 
-	if ((flags_mask & MAIL_RECENT) != 0 && (flags & MAIL_RECENT) != 0)
-		LOW_UPDATE(view->map->hdr.first_recent_uid_lowwater);
 	if ((flags_mask & MAIL_SEEN) != 0 && (flags & MAIL_SEEN) == 0)
 		LOW_UPDATE(view->map->hdr.first_unseen_uid_lowwater);
 	if ((flags_mask & MAIL_DELETED) != 0 && (flags & MAIL_DELETED) != 0)
--- a/src/lib-index/mail-index.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-index/mail-index.h	Mon Jul 16 09:48:02 2007 +0300
@@ -64,12 +64,12 @@
 	uint32_t next_uid;
 
 	uint32_t messages_count;
-	uint32_t recent_messages_count;
+	uint32_t unused_old_recent_messages_count;
 	uint32_t seen_messages_count;
 	uint32_t deleted_messages_count;
 
-	/* these UIDs may not exist and may not even be unseen */
-	uint32_t first_recent_uid_lowwater;
+	uint32_t first_recent_uid;
+	/* these UIDs may not exist and may not even be unseen/deleted */
 	uint32_t first_unseen_uid_lowwater;
 	uint32_t first_deleted_uid_lowwater;
 
--- a/src/lib-mail/mail-types.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-mail/mail-types.h	Mon Jul 16 09:48:02 2007 +0300
@@ -9,7 +9,8 @@
 	MAIL_DRAFT	= 0x10,
 	MAIL_RECENT	= 0x20,
 
-	MAIL_FLAGS_MASK = 0x3f
+	MAIL_FLAGS_MASK = 0x3f,
+	MAIL_FLAGS_NONRECENT = (MAIL_FLAGS_MASK ^ MAIL_RECENT)
 };
 
 enum modify_type {
--- a/src/lib-storage/index/cydir/cydir-save.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/cydir/cydir-save.c	Mon Jul 16 09:48:02 2007 +0300
@@ -112,7 +112,7 @@
 	t_pop();
 
 	/* add to index */
-	save_flags = (flags & ~MAIL_RECENT) | MAIL_RECENT;
+	save_flags = flags & ~MAIL_RECENT;
 	mail_index_append(ctx->trans, 0, &ctx->seq);
 	mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
 				save_flags);
--- a/src/lib-storage/index/cydir/cydir-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/cydir/cydir-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -77,6 +77,17 @@
 			return -1;
 	}
 
+	/* mark the newly seen messages as recent */
+	if (mail_index_lookup_uid_range(ctx->sync_view, hdr->first_recent_uid,
+					hdr->next_uid, &seq1, &seq2) < 0) {
+		mail_storage_set_index_error(&ctx->mbox->ibox);
+		return -1;
+	}
+	if (seq1 != 0) {
+		index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->sync_view,
+					     seq1, seq2);
+	}
+
 	while (ret > 0 &&
 	       (ret = mail_index_sync_next(ctx->index_sync_ctx,
 					   &sync_rec)) > 0) {
--- a/src/lib-storage/index/dbox/dbox-save.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-save.c	Mon Jul 16 09:48:02 2007 +0300
@@ -219,7 +219,7 @@
 	ctx->mail_offset = ctx->file->output->offset;
 
 	/* add to index */
-	save_flags = (flags & ~MAIL_RECENT) | MAIL_RECENT;
+	save_flags = flags & ~MAIL_RECENT;
 	mail_index_append(ctx->trans, 0, &ctx->seq);
 	mail_index_update_flags(ctx->trans, ctx->seq, MODIFY_REPLACE,
 				save_flags);
--- a/src/lib-storage/index/dbox/dbox-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/dbox/dbox-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -74,23 +74,6 @@
 	return 0;
 }
 
-static int dbox_update_recent_flags(struct dbox_sync_context *ctx,
-				    uint32_t seq1, uint32_t seq2)
-{
-	uint32_t seq;
-	const struct mail_index_record *rec;
-
-	for (seq = seq1; seq <= seq2; seq++) {
-		if (mail_index_lookup(ctx->sync_view, seq, &rec) < 0) {
-			mail_storage_set_index_error(&ctx->mbox->ibox);
-			return -1;
-		}
-		if ((rec->flags & MAIL_RECENT) != 0)
-			index_mailbox_set_recent(&ctx->mbox->ibox, seq);
-	}
-	return 0;
-}
-
 static int dbox_sync_add(struct dbox_sync_context *ctx,
 			 const struct mail_index_sync_rec *sync_rec)
 {
@@ -124,10 +107,6 @@
 	case MAIL_INDEX_SYNC_TYPE_FLAGS:
 		dbox_sync_rec.value.flags.add = sync_rec->add_flags;
 		dbox_sync_rec.value.flags.remove = sync_rec->remove_flags;
-		if ((sync_rec->remove_flags & MAIL_RECENT) != 0) {
-			if (dbox_update_recent_flags(ctx, seq1, seq2) < 0)
-				return -1;
-		}
 		break;
 	case MAIL_INDEX_SYNC_TYPE_KEYWORD_ADD:
 	case MAIL_INDEX_SYNC_TYPE_KEYWORD_REMOVE:
--- a/src/lib-storage/index/index-mail.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/index-mail.c	Mon Jul 16 09:48:02 2007 +0300
@@ -115,7 +115,7 @@
 	struct index_mail *mail = (struct index_mail *) _mail;
 	struct index_mail_data *data = &mail->data;
 
-	data->flags = data->rec->flags & MAIL_FLAGS_MASK;
+	data->flags = data->rec->flags & MAIL_FLAGS_NONRECENT;
 	if (index_mailbox_is_recent(mail->ibox, data->seq))
 		data->flags |= MAIL_RECENT;
 
@@ -1110,7 +1110,7 @@
 	struct index_mail *imail = (struct index_mail *)mail;
 
 	mail_index_update_flags(imail->trans->trans, mail->seq, modify_type,
-				flags & MAIL_FLAGS_MASK);
+				flags & MAIL_FLAGS_NONRECENT);
 	return 0;
 }
 
--- a/src/lib-storage/index/index-status.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/index-status.c	Mon Jul 16 09:48:02 2007 +0300
@@ -14,7 +14,7 @@
 	/* we can get most of the status items without any trouble */
 	hdr = mail_index_get_header(ibox->view);
 	status_r->messages = hdr->messages_count;
-	status_r->recent = ibox->synced_recent_count;
+	status_r->recent = ibox->recent_flags_count;
 	status_r->unseen =
 		hdr->messages_count - hdr->seen_messages_count;
 	status_r->uidvalidity = hdr->uid_validity;
--- a/src/lib-storage/index/index-storage.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/index-storage.c	Mon Jul 16 09:48:02 2007 +0300
@@ -432,8 +432,8 @@
 	index_mailbox_check_remove_all(ibox);
 	if (ibox->index != NULL)
 		index_storage_unref(ibox->index);
-	if (ibox->recent_flags != NULL)
-		buffer_free(ibox->recent_flags);
+	if (array_is_created(&ibox->recent_flags))
+		array_free(&ibox->recent_flags);
 	i_free(ibox->cache_fields);
 
 	pool_unref(box->pool);
--- a/src/lib-storage/index/index-storage.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/index-storage.h	Mon Jul 16 09:48:02 2007 +0300
@@ -33,8 +33,6 @@
 	struct mail_cache *cache;
 	struct mail_vfuncs *mail_vfuncs;
 
-	bool (*is_recent)(struct index_mailbox *ibox, uint32_t uid);
-
 	uint32_t md5hdr_ext_idx;
 
 	struct timeout *notify_to;
@@ -52,16 +50,15 @@
 	struct mail_cache_field *cache_fields;
 	unsigned int mail_cache_min_mail_count;
 
-	buffer_t *recent_flags;
-	uint32_t recent_flags_start_seq, recent_flags_count;
+	ARRAY_TYPE(seq_range) recent_flags;
+	uint32_t recent_flags_prev_uid;
+	uint32_t recent_flags_count;
 
-	uint32_t synced_recent_count;
 	time_t sync_last_check;
 
 	unsigned int readonly:1;
 	unsigned int keep_recent:1;
 	unsigned int keep_locked:1;
-	unsigned int recent_flags_synced:1;
 	unsigned int sent_diskspace_warning:1;
 	unsigned int sent_readonly_flags_warning:1;
 	unsigned int notify_pending:1;
@@ -117,10 +114,11 @@
 void index_keywords_free(struct mailbox_transaction_context *t,
 			 struct mail_keywords *keywords);
 
-void index_mailbox_set_recent(struct index_mailbox *ibox, uint32_t seq);
-bool index_mailbox_is_recent(struct index_mailbox *ibox, uint32_t seq);
-
-unsigned int index_storage_get_recent_count(struct mail_index_view *view);
+void index_mailbox_set_recent_uid(struct index_mailbox *ibox, uint32_t uid);
+void index_mailbox_set_recent_seq(struct index_mailbox *ibox,
+				  struct mail_index_view *view,
+				  uint32_t seq1, uint32_t seq2);
+bool index_mailbox_is_recent(struct index_mailbox *ibox, uint32_t uid);
 
 void index_mailbox_check_add(struct index_mailbox *ibox,
 			     const char *path);
--- a/src/lib-storage/index/index-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/index-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -19,114 +19,54 @@
 	bool failed;
 };
 
-void index_mailbox_set_recent(struct index_mailbox *ibox, uint32_t seq)
+void index_mailbox_set_recent_uid(struct index_mailbox *ibox, uint32_t uid)
 {
-	unsigned char *p;
-	size_t dest_idx;
+	if (uid <= ibox->recent_flags_prev_uid) {
+		i_assert(seq_range_exists(&ibox->recent_flags, uid));
+		return;
+	}
+	ibox->recent_flags_prev_uid = uid;
 
-	i_assert(seq != 0);
+	seq_range_array_add(&ibox->recent_flags, 64, uid);
+	ibox->recent_flags_count++;
+}
 
-	if (ibox->recent_flags_start_seq == 0) {
-		ibox->recent_flags = buffer_create_dynamic(default_pool, 128);
-		ibox->recent_flags_start_seq = seq;
-	} else if (seq < ibox->recent_flags_start_seq) {
-		dest_idx = ibox->recent_flags_start_seq - seq;
-		buffer_copy(ibox->recent_flags, dest_idx,
-			    ibox->recent_flags, 0, (size_t)-1);
-		memset(buffer_get_modifiable_data(ibox->recent_flags, NULL),
-		       0, dest_idx);
-		ibox->recent_flags_start_seq = seq;
-	}
+void index_mailbox_set_recent_seq(struct index_mailbox *ibox,
+				  struct mail_index_view *view,
+				  uint32_t seq1, uint32_t seq2)
+{
+	uint32_t uid;
+	int ret;
 
-	p = buffer_get_space_unsafe(ibox->recent_flags,
-				    seq - ibox->recent_flags_start_seq, 1);
-	if (*p == 0) {
-		ibox->recent_flags_count++;
-		*p = 1;
+	for (; seq1 <= seq2; seq1++) {
+		ret = mail_index_lookup_uid(view, seq1, &uid);
+		i_assert(ret == 0);
+		index_mailbox_set_recent_uid(ibox, uid);
 	}
 }
 
-bool index_mailbox_is_recent(struct index_mailbox *ibox, uint32_t seq)
+bool index_mailbox_is_recent(struct index_mailbox *ibox, uint32_t uid)
 {
-	const unsigned char *data;
-	size_t size;
-	uint32_t idx;
-
-	if (seq < ibox->recent_flags_start_seq ||
-	    ibox->recent_flags_start_seq == 0)
-		return FALSE;
-
-	idx = seq - ibox->recent_flags_start_seq;
-	data = buffer_get_data(ibox->recent_flags, &size);
-	return idx < size ? data[idx] : FALSE;
+	return array_is_created(&ibox->recent_flags) &&
+		seq_range_exists(&ibox->recent_flags, uid);
 }
 
 static void index_mailbox_expunge_recent(struct index_mailbox *ibox,
 					 uint32_t seq1, uint32_t seq2)
 {
-	const unsigned char *data;
-	size_t size;
-	uint32_t i, idx, count, move;
-
-	if (ibox->recent_flags_start_seq == 0) {
-		/* no recent flags */
-		return;
-	}
-
-	if (seq2 < ibox->recent_flags_start_seq) {
-		/* expunging messages before recent flags, just modify
-		   the recent start position */
-		ibox->recent_flags_start_seq -= seq2 - seq1 + 1;
-		return;
-	}
-
-	if (seq1 < ibox->recent_flags_start_seq) {
-		move = ibox->recent_flags_start_seq - seq1;
-		seq1 = ibox->recent_flags_start_seq;
-	} else {
-		move = 0;
-	}
+	uint32_t uid;
+	int ret;
 
-	idx = seq1 - ibox->recent_flags_start_seq;
-	count = seq2 - seq1 + 1;
-
-	data = buffer_get_data(ibox->recent_flags, &size);
-	if (idx < size) {
-		if (idx + count > size)
-			count = size - idx;
-
-		for (i = 0; i < count; i++) {
-			if (data[idx+i])
-				ibox->recent_flags_count--;
-		}
-
-		buffer_copy(ibox->recent_flags, idx,
-			    ibox->recent_flags, idx + count, (size_t)-1);
-		buffer_write_zero(ibox->recent_flags, size - count, count);
-
-		buffer_set_used_size(ibox->recent_flags, size - count);
-	}
-        ibox->recent_flags_start_seq -= move;
-}
-
-static int index_mailbox_update_recent(struct index_mailbox *ibox,
-				       uint32_t seq1, uint32_t seq2)
-{
-	const struct mail_index_record *rec;
+	if (!array_is_created(&ibox->recent_flags))
+		return;
 
 	for (; seq1 <= seq2; seq1++) {
-		if (mail_index_lookup(ibox->view, seq1, &rec) < 0) {
-			mail_storage_set_index_error(ibox);
-			return -1;
-		}
+		ret = mail_index_lookup_uid(ibox->view, seq1, &uid);
+		i_assert(ret == 0);
 
-		if ((rec->flags & MAIL_RECENT) != 0 ||
-		    (ibox->is_recent != NULL &&
-		     ibox->is_recent(ibox, rec->uid)))
-                        index_mailbox_set_recent(ibox, seq1);
+		if (seq_range_array_remove(&ibox->recent_flags, uid))
+			ibox->recent_flags_count--;
 	}
-
-	return 0;
 }
 
 struct mailbox_sync_context *
@@ -158,11 +98,6 @@
 		return &ctx->ctx;
 	}
 
-	if (!ibox->recent_flags_synced) {
-		ibox->recent_flags_synced = TRUE;
-                index_mailbox_update_recent(ibox, 1, ctx->messages_count);
-	}
-
 	if ((flags & MAILBOX_SYNC_FLAG_NO_EXPUNGES) == 0) {
 		mail_index_view_sync_get_expunges(ctx->sync_ctx,
 						  &ctx->expunges);
@@ -299,21 +234,32 @@
 	struct index_mailbox_sync_context *ctx =
 		(struct index_mailbox_sync_context *)_ctx;
 	struct index_mailbox *ibox = ctx->ibox;
-	uint32_t messages_count;
+	const struct mail_index_header *hdr;
+	uint32_t seq1, seq2;
 	int ret = ctx->failed ? -1 : 0;
 
 	if (ctx->sync_ctx != NULL)
 		mail_index_view_sync_end(&ctx->sync_ctx);
 
+	if (ibox->keep_recent) {
+		/* mailbox syncing didn't necessarily update our recent state */
+		hdr = mail_index_get_header(ibox->view);
+		if (hdr->first_recent_uid > ibox->recent_flags_prev_uid) {
+			if (mail_index_lookup_uid_range(ibox->view,
+							hdr->first_recent_uid,
+							hdr->next_uid,
+							&seq1, &seq2) < 0) {
+				mail_storage_set_index_error(ctx->ibox);
+				return -1;
+			}
+			if (seq1 != 0) {
+				index_mailbox_set_recent_seq(ibox, ibox->view,
+							     seq1, seq2);
+			}
+		}
+	}
+
 	if (ret == 0) {
-		messages_count = mail_index_view_get_messages_count(ibox->view);
-		if (messages_count != ctx->messages_count) {
-			index_mailbox_update_recent(ibox,
-						    ctx->messages_count+1,
-						    messages_count);
-		}
-		ibox->synced_recent_count = ibox->recent_flags_count;
-
 		ret = status_items == 0 ? 0 :
 			index_storage_get_status_locked(ctx->ibox, status_items,
 							status_r);
--- a/src/lib-storage/index/maildir/maildir-save.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-save.c	Mon Jul 16 09:48:02 2007 +0300
@@ -608,9 +608,6 @@
 		*t->ictx.last_saved_uid = first_uid + ctx->files_count - 1;
 	}
 
-	flags = MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
-		MAILDIR_UIDLIST_REC_FLAG_RECENT;
-
 	/* move them into new/ and/or cur/ */
 	ret = 0;
 	ctx->moving = TRUE;
@@ -636,6 +633,9 @@
 			t_push();
 			newdir = maildir_get_updated_filename(ctx, mf, &dest);
 
+			flags = MAILDIR_UIDLIST_REC_FLAG_RECENT;
+			if (newdir)
+				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
 			ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
 							dest, flags);
 			i_assert(ret > 0);
--- a/src/lib-storage/index/maildir/maildir-storage.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-storage.c	Mon Jul 16 09:48:02 2007 +0300
@@ -415,13 +415,6 @@
 	return 0;
 }
 
-static bool maildir_is_recent(struct index_mailbox *ibox, uint32_t uid)
-{
-	struct maildir_mailbox *mbox = (struct maildir_mailbox *)ibox;
-
-	return maildir_uidlist_is_recent(mbox->uidlist, uid);
-}
-
 static void maildir_lock_touch_timeout(struct maildir_mailbox *mbox)
 {
 	(void)maildir_uidlist_lock_touch(mbox->uidlist);
@@ -458,7 +451,6 @@
 	mbox->ibox.box.pool = pool;
 	mbox->ibox.storage = &storage->storage;
 	mbox->ibox.mail_vfuncs = &maildir_mail_vfuncs;
-	mbox->ibox.is_recent = maildir_is_recent;
 	mbox->ibox.index = index;
 
 	mbox->storage = storage;
--- a/src/lib-storage/index/maildir/maildir-sync-index.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-sync-index.c	Mon Jul 16 09:48:02 2007 +0300
@@ -27,7 +27,7 @@
 	enum mail_flags flags;
 	ARRAY_TYPE(keyword_indexes) keywords;
 
-	uint32_t seq, uid;
+	uint32_t seq, uid, last_nonrecent_seq, last_nonrecent_uid;
 
 	bool changed;
 };
@@ -133,8 +133,7 @@
 		i_assert(ret > 0);
 	}
 
-	uflags &= (MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
-		   MAILDIR_UIDLIST_REC_FLAG_RECENT);
+	uflags &= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
 	maildir_uidlist_sync_remove(ctx->uidlist_sync_ctx, filename);
 	ret = maildir_uidlist_sync_next(ctx->uidlist_sync_ctx,
 					filename, uflags);
@@ -155,9 +154,15 @@
 	struct mail_index_sync_ctx *sync_ctx;
 	struct mail_index_view *view;
 	struct mail_index_transaction *trans;
+	enum mail_index_sync_flags sync_flags;
+
+	sync_flags = 0;
+	/* don't drop recent messages if we're saving messages */
+	if (!mbox->ibox.keep_recent && maildir_sync_ctx != NULL)
+		sync_flags |= MAIL_INDEX_SYNC_FLAG_DROP_RECENT;
 
 	if (mail_index_sync_begin(mbox->ibox.index, &sync_ctx, &view, &trans,
-				  (uint32_t)-1, (uoff_t)-1, 0) <= 0) {
+				  (uint32_t)-1, (uoff_t)-1, sync_flags) <= 0) {
 		mail_storage_set_index_error(&mbox->ibox);
 		return -1;
 	}
@@ -179,6 +184,46 @@
 	return 0;
 }
 
+static int maildir_sync_recent_state(struct maildir_index_sync_context *ctx)
+{
+	const struct mail_index_header *hdr;
+	uint32_t seq1, seq2, first_recent_uid;
+
+	/* see what index thinks is the lowest recent sequence */
+	hdr = mail_index_get_header(ctx->view);
+	if (mail_index_lookup_uid_range(ctx->view, hdr->first_recent_uid,
+					hdr->next_uid, &seq1, &seq2) < 0) {
+		mail_storage_set_index_error(&ctx->mbox->ibox);
+		return -1;
+	}
+
+	if (seq1 == 0 && hdr->first_recent_uid >= ctx->last_nonrecent_uid + 1) {
+		/* nothing new */
+		return 0;
+	}
+
+	if (ctx->last_nonrecent_seq >= seq1) {
+		/* we've seen newer non-recent messages. */
+		seq1 = ctx->last_nonrecent_seq + 1;
+	}
+
+	if (seq2 < ctx->seq)
+		seq2 = ctx->seq - 1;
+
+	if (seq1 <= seq2) {
+		index_mailbox_set_recent_seq(&ctx->mbox->ibox, ctx->view,
+					     seq1, seq2);
+	}
+
+	if (ctx->mbox->ibox.keep_recent &&
+	    hdr->first_recent_uid < ctx->last_nonrecent_uid + 1) {
+		first_recent_uid = ctx->last_nonrecent_uid + 1;
+		mail_index_update_header(ctx->trans,
+			offsetof(struct mail_index_header, first_recent_uid),
+			&first_recent_uid, sizeof(first_recent_uid), FALSE);
+	}
+	return 0;
+}
 int maildir_sync_index_finish(struct maildir_index_sync_context **_ctx,
 			      bool failed, bool cancel)
 {
@@ -191,6 +236,8 @@
 	if (ret < 0 || cancel)
 		mail_index_sync_rollback(&ctx->sync_ctx);
 	else {
+		maildir_sync_recent_state(ctx);
+
 		/* Set syncing_commit=TRUE so that if any sync callbacks try
 		   to access mails which got lost (eg. expunge callback trying
 		   to open the file which was just unlinked) we don't try to
@@ -291,17 +338,16 @@
 		   at all even for newly seen mails */
 		ctx->flags &= ~mbox->private_flags_mask;
 
-		if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
-		    (uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0 &&
-		    (uflags & MAILDIR_UIDLIST_REC_FLAG_MOVED) == 0) {
-			/* mail is recent for next session as well */
-			ctx->flags |= MAIL_RECENT;
-		}
-
 	__again:
 		ctx->seq = ++seq;
 		ctx->uid = uid;
 
+		if ((uflags & MAILDIR_UIDLIST_REC_FLAG_RECENT) == 0 &&
+		    (uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) == 0) {
+			ctx->last_nonrecent_seq = seq;
+			ctx->last_nonrecent_uid = uid;
+		}
+
 		if (seq > hdr->messages_count) {
 			if (uid < hdr_next_uid) {
 				maildir_handle_uid_insertion(ctx, uflags,
@@ -356,7 +402,7 @@
 			if (maildir_file_do(mbox, ctx->uid,
 					    maildir_expunge, ctx) >= 0) {
 				/* successful expunge */
-				mail_index_expunge(trans, ctx->seq);
+				mail_index_expunge(trans, seq);
 			}
 			if ((++changes % MAILDIR_SLOW_MOVE_COUNT) == 0)
 				maildir_sync_notify(ctx->maildir_sync_ctx);
@@ -366,20 +412,9 @@
 		/* the private flags are stored only in indexes, keep them */
 		ctx->flags |= rec->flags & mbox->private_flags_mask;
 
-		if ((rec->flags & MAIL_RECENT) != 0) {
-			index_mailbox_set_recent(&mbox->ibox, seq);
-			if (mbox->ibox.keep_recent) {
-				ctx->flags |= MAIL_RECENT;
-			} else {
-				mail_index_update_flags(trans, seq,
-							MODIFY_REMOVE,
-							MAIL_RECENT);
-			}
-		}
-
 		if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NONSYNCED) != 0) {
 			/* partial syncing */
-			if ((ctx->flags & MAIL_RECENT) != 0) {
+			if ((uflags & MAILDIR_UIDLIST_REC_FLAG_NEW_DIR) != 0) {
 				/* we last saw this mail in new/, but it's
 				   not there anymore. possibly expunged,
 				   make sure. */
@@ -403,15 +438,9 @@
 			continue;
 		}
 
-		if ((ctx->flags & ~MAIL_RECENT) !=
-		    (rec->flags & (MAIL_FLAGS_MASK^MAIL_RECENT))) {
+		if (ctx->flags != (rec->flags & MAIL_FLAGS_NONRECENT)) {
 			mail_index_update_flags(trans, seq, MODIFY_REPLACE,
 						ctx->flags);
-		} else if ((ctx->flags & MAIL_RECENT) == 0 &&
-			   (rec->flags & MAIL_RECENT) != 0) {
-			/* just remove recent flag */
-			mail_index_update_flags(trans, seq, MODIFY_REMOVE,
-						MAIL_RECENT);
 		}
 
 		/* update keywords if they have changed */
--- a/src/lib-storage/index/maildir/maildir-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -453,16 +453,15 @@
 				/* someone else moved it already */
 				dir_changed = TRUE;
 				move_count++;
-				flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED;
+				flags |= MAILDIR_UIDLIST_REC_FLAG_MOVED |
+					MAILDIR_UIDLIST_REC_FLAG_RECENT;
 			} else if (ENOSPACE(errno) || errno == EACCES) {
 				/* not enough disk space / read-only maildir,
 				   leave here */
-				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
-					MAILDIR_UIDLIST_REC_FLAG_RECENT;
+				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
 				move_new = FALSE;
 			} else {
-				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
-					MAILDIR_UIDLIST_REC_FLAG_RECENT;
+				flags |= MAILDIR_UIDLIST_REC_FLAG_NEW_DIR;
 				mail_storage_set_critical(storage,
 					"rename(%s, %s) failed: %m",
 					str_c(src), str_c(dest));
@@ -645,6 +644,22 @@
 	}
 }
 
+static bool move_recent_messages(struct maildir_sync_context *ctx)
+{
+	const struct mail_index_header *hdr;
+
+	if (ctx->mbox->ibox.keep_recent)
+		return FALSE;
+
+	(void)maildir_uidlist_refresh(ctx->mbox->uidlist);
+
+	/* if there are files in new/, we'll need to move them. we'll check
+	   this by checking if we have any recent messages */
+	hdr = mail_index_get_header(ctx->mbox->ibox.view);
+	return hdr->first_recent_uid <
+		maildir_uidlist_get_next_uid(ctx->mbox->uidlist);
+}
+
 static int maildir_sync_context(struct maildir_sync_context *ctx, bool forced,
 				bool sync_last_commit)
 {
@@ -659,8 +674,11 @@
 					     &new_changed, &cur_changed) < 0)
 			return -1;
 
-		if (!new_changed && !cur_changed)
-			return 1;
+		if (!new_changed && !cur_changed) {
+			if (!move_recent_messages(ctx))
+				return 1;
+			new_changed = TRUE;
+		}
 	} else {
 		new_changed = cur_changed = TRUE;
 	}
--- a/src/lib-storage/index/maildir/maildir-uidlist.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.c	Mon Jul 16 09:48:02 2007 +0300
@@ -84,7 +84,6 @@
 	unsigned int version;
 	unsigned int uid_validity, next_uid, prev_read_uid, last_seen_uid;
 	unsigned int read_records_count;
-	uint32_t first_recent_uid;
 	uoff_t last_read_offset;
 	string_t *hdr_extensions;
 
@@ -247,14 +246,6 @@
 	i_free(uidlist);
 }
 
-static void
-maildir_uidlist_mark_recent(struct maildir_uidlist *uidlist, uint32_t uid)
-{
-	if (uidlist->first_recent_uid == 0 ||
-	    uid < uidlist->first_recent_uid)
-		uidlist->first_recent_uid = uid;
-}
-
 static int maildir_uid_cmp(const void *p1, const void *p2)
 {
 	const struct maildir_uidlist_rec *const *rec1 = p1, *const *rec2 = p2;
@@ -738,50 +729,6 @@
 	return NULL;
 }
 
-bool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid)
-{
-	enum maildir_uidlist_rec_flag flags;
-
-	if (uidlist->first_recent_uid == 0 || uid < uidlist->first_recent_uid)
-		return FALSE;
-
-	if (maildir_uidlist_lookup(uidlist, uid, &flags) == NULL)
-		return FALSE;
-
-	i_assert(uidlist->first_recent_uid != uid ||
-		 (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0);
-	return (flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0;
-}
-
-uint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist)
-{
-	struct maildir_uidlist_rec *const *recs;
-	unsigned int idx, count;
-	uint32_t recent_count;
-
-	if (!uidlist->initial_sync) {
-		/* we haven't synced yet, trust index */
-		const struct mail_index_header *hdr;
-
-		hdr = mail_index_get_header(uidlist->mbox->ibox.view);
-		return hdr->recent_messages_count;
-	}
-
-	/* all recent messages were in new/ dir, so even if we did only
-	   a partial sync we should know all the recent messages. */
-
-	if (uidlist->first_recent_uid == 0)
-		return 0;
-
-	recs = array_get(&uidlist->records, &count);
-	maildir_uidlist_lookup_rec(uidlist, uidlist->first_recent_uid, &idx);
-	for (recent_count = 0; idx < count; idx++) {
-		if ((recs[idx]->flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0)
-			recent_count++;
-	}
-	return recent_count;
-}
-
 uint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist)
 {
 	return uidlist->uid_validity;
@@ -1110,10 +1057,6 @@
 		uidlist->change_counter++;
 	}
 
-	if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
-	    rec->uid != (uint32_t)-1)
-		maildir_uidlist_mark_recent(uidlist, rec->uid);
-
 	rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
 	rec->filename = p_strdup(uidlist->record_pool, filename);
 	hash_insert(uidlist->files, rec->filename, rec);
@@ -1165,6 +1108,9 @@
 			return 0;
 		}
 
+		/* probably was in new/ and now we're seeing it in cur/.
+		   remove new/moved flags so if this happens again we'll know
+		   to check for duplicates. */
 		rec->flags &= ~(MAILDIR_UIDLIST_REC_FLAG_NEW_DIR |
 				MAILDIR_UIDLIST_REC_FLAG_MOVED);
 	} else {
@@ -1179,15 +1125,13 @@
 			rec->uid = (uint32_t)-1;
 			ctx->new_files_count++;
 			ctx->changed = TRUE;
+			/* didn't exist in uidlist, it's recent */
+			flags |= MAILDIR_UIDLIST_REC_FLAG_RECENT;
 		}
 
 		array_append(&ctx->records, &rec, 1);
 	}
 
-	if ((flags & MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0 &&
-	    rec->uid != (uint32_t)-1)
-		maildir_uidlist_mark_recent(uidlist, rec->uid);
-
 	rec->flags = (rec->flags | flags) & ~MAILDIR_UIDLIST_REC_FLAG_NONSYNCED;
 	rec->filename = p_strdup(ctx->record_pool, filename);
 	hash_insert(ctx->files, rec->filename, rec);
@@ -1268,12 +1212,6 @@
 		i_assert(recs[dest]->uid == (uint32_t)-1);
 		recs[dest]->uid = ctx->uidlist->next_uid++;
 		recs[dest]->flags &= ~MAILDIR_UIDLIST_REC_FLAG_MOVED;
-
-		if ((recs[dest]->flags &
-		     MAILDIR_UIDLIST_REC_FLAG_RECENT) != 0) {
-			maildir_uidlist_mark_recent(ctx->uidlist,
-						    recs[dest]->uid);
-		}
 	}
 
 	ctx->new_files_count = 0;
--- a/src/lib-storage/index/maildir/maildir-uidlist.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/maildir/maildir-uidlist.h	Mon Jul 16 09:48:02 2007 +0300
@@ -53,10 +53,6 @@
 const char *
 maildir_uidlist_lookup_ext(struct maildir_uidlist *uidlist, uint32_t uid,
 			   enum maildir_uidlist_rec_ext_key key);
-/* Returns TRUE if mail with given UID is recent. */
-bool maildir_uidlist_is_recent(struct maildir_uidlist *uidlist, uint32_t uid);
-/* Returns number of recent messages. */
-uint32_t maildir_uidlist_get_recent_count(struct maildir_uidlist *uidlist);
 
 uint32_t maildir_uidlist_get_uid_validity(struct maildir_uidlist *uidlist);
 uint32_t maildir_uidlist_get_next_uid(struct maildir_uidlist *uidlist);
--- a/src/lib-storage/index/mbox/mbox-sync-private.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync-private.h	Mon Jul 16 09:48:02 2007 +0300
@@ -131,6 +131,7 @@
 
 	uint32_t prev_msg_uid, next_uid, idx_next_uid;
 	uint32_t seq, idx_seq, need_space_seq;
+	uint32_t last_nonrecent_idx_seq, last_nonrecent_uid;
 	off_t expunged_space, space_diff;
 
 	unsigned int dest_first_mail:1;
--- a/src/lib-storage/index/mbox/mbox-sync.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib-storage/index/mbox/mbox-sync.c	Mon Jul 16 09:48:02 2007 +0300
@@ -357,7 +357,7 @@
 	struct mailbox *box = &sync_ctx->mbox->ibox.box;
 	uint8_t mbox_flags;
 
-	mbox_flags = mail->flags & MAIL_FLAGS_MASK;
+	mbox_flags = mail->flags & MAIL_FLAGS_NONRECENT;
 
 	if (mail_ctx->dirty)
 		mbox_flags |= MAIL_INDEX_MAIL_FLAG_DIRTY;
@@ -384,7 +384,7 @@
 		enum mailbox_sync_type sync_type;
 
 		memset(&idx_mail, 0, sizeof(idx_mail));
-		idx_mail.flags = rec->flags;
+		idx_mail.flags = rec->flags & ~MAIL_RECENT;
 
 		/* get old keywords */
 		t_push();
@@ -404,35 +404,23 @@
 		if (sync_type != 0 && box->v.sync_notify != NULL)
 			box->v.sync_notify(box, rec->uid, sync_type);
 
-#define SYNC_FLAGS (MAIL_RECENT | MAIL_INDEX_MAIL_FLAG_DIRTY)
 		if ((idx_mail.flags & MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
 			/* flags are dirty. ignore whatever was in the mbox,
-			   but update recent/dirty flag states if needed. */
-			mbox_flags &= SYNC_FLAGS;
-			mbox_flags |= idx_mail.flags & ~SYNC_FLAGS;
+			   but update dirty flag state if needed. */
+			mbox_flags &= ~MAIL_INDEX_MAIL_FLAG_DIRTY;
+			mbox_flags |=
+				idx_mail.flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY;
 			if (sync_ctx->delay_writes)
 				mbox_flags |= MAIL_INDEX_MAIL_FLAG_DIRTY;
-		} else {
-			/* keep index's internal flags */
-			mbox_flags &= MAIL_FLAGS_MASK | SYNC_FLAGS;
-			mbox_flags |= idx_mail.flags &
-				~(MAIL_FLAGS_MASK | SYNC_FLAGS);
 		}
 
-		if ((idx_mail.flags & ~SYNC_FLAGS) !=
-		    (mbox_flags & ~SYNC_FLAGS)) {
+		if ((idx_mail.flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY) !=
+		    (mbox_flags & ~MAIL_INDEX_MAIL_FLAG_DIRTY)) {
 			/* flags other than recent/dirty have changed */
 			mail_index_update_flags(sync_ctx->t, sync_ctx->idx_seq,
 						MODIFY_REPLACE, mbox_flags);
 		} else {
 			if (((idx_mail.flags ^ mbox_flags) &
-			     MAIL_RECENT) != 0) {
-				/* drop recent flag (it can only be dropped) */
-				mail_index_update_flags(sync_ctx->t,
-					sync_ctx->idx_seq,
-					MODIFY_REMOVE, MAIL_RECENT);
-			}
-			if (((idx_mail.flags ^ mbox_flags) &
 			     MAIL_INDEX_MAIL_FLAG_DIRTY) != 0) {
 				/* dirty flag state changed */
 				bool dirty = (mbox_flags &
@@ -458,11 +446,9 @@
 		}
 	}
 
-	if (mail_ctx->recent &&
-	    (rec == NULL || (rec->flags & MAIL_INDEX_MAIL_FLAG_DIRTY) == 0 ||
-	     (rec->flags & MAIL_RECENT) != 0)) {
-		index_mailbox_set_recent(&sync_ctx->mbox->ibox,
-					 sync_ctx->idx_seq);
+	if (!mail_ctx->recent) {
+		sync_ctx->last_nonrecent_idx_seq = sync_ctx->idx_seq;
+		sync_ctx->last_nonrecent_uid = mail->uid;
 	}
 
 	/* update from_offsets, but not if we're going to rewrite this message.
@@ -926,7 +912,21 @@
 	if (index_sync_changes_have(sync_ctx->sync_changes))
 		return 1;
 
+	if (sync_ctx->hdr->first_recent_uid <= next_uid &&
+	    !sync_ctx->mbox->ibox.keep_recent) {
+		/* we'll need to rewrite Status: O headers */
+		return 1;
+	}
+
 	uid = index_sync_changes_get_next_uid(sync_ctx->sync_changes);
+
+	if (sync_ctx->hdr->first_recent_uid < sync_ctx->hdr->next_uid &&
+	    (uid > sync_ctx->hdr->first_recent_uid || uid == 0) &&
+	    !sync_ctx->mbox->ibox.keep_recent) {
+		/* we'll need to rewrite Status: O headers */
+		uid = sync_ctx->hdr->first_recent_uid;
+	}
+
 	if (uid != 0) {
 		/* we can skip forward to next record which needs updating. */
 		if (uid != next_uid) {
@@ -1331,6 +1331,7 @@
 static int mbox_sync_update_index_header(struct mbox_sync_context *sync_ctx)
 {
 	const struct stat *st;
+	uint32_t first_recent_uid;
 
 	st = i_stream_stat(sync_ctx->file_input, FALSE);
 	if (st == NULL) {
@@ -1415,12 +1416,53 @@
 			&sync_size, sizeof(sync_size), TRUE);
 	}
 
+	first_recent_uid = !sync_ctx->mbox->ibox.keep_recent ? 0 :
+		sync_ctx->last_nonrecent_uid + 1;
+	if (sync_ctx->hdr->first_recent_uid < first_recent_uid) {
+		mail_index_update_header(sync_ctx->t,
+			offsetof(struct mail_index_header, first_recent_uid),
+			&first_recent_uid, sizeof(first_recent_uid), FALSE);
+	}
+
 	sync_ctx->mbox->mbox_dirty_stamp = st->st_mtime;
 	sync_ctx->mbox->mbox_dirty_size = st->st_size;
 
 	return 0;
 }
 
+static int mbox_sync_recent_state(struct mbox_sync_context *sync_ctx)
+{
+	uint32_t seq1, seq2;
+
+	/* see what index thinks is the lowest recent sequence */
+	if (mail_index_lookup_uid_range(sync_ctx->sync_view,
+					sync_ctx->hdr->first_recent_uid,
+					sync_ctx->hdr->next_uid,
+					&seq1, &seq2) < 0) {
+		mail_storage_set_index_error(&sync_ctx->mbox->ibox);
+		return -1;
+	}
+
+	if (seq1 == 0 && sync_ctx->idx_seq <= 1) {
+		/* nothing new */
+		return 0;
+	}
+
+	if (sync_ctx->last_nonrecent_idx_seq >= seq1) {
+		/* we've seen newer non-recent messages. */
+		seq1 = sync_ctx->last_nonrecent_idx_seq + 1;
+	}
+
+	if (seq2 < sync_ctx->idx_seq)
+		seq2 = sync_ctx->idx_seq - 1;
+
+	if (seq1 <= seq2) {
+		index_mailbox_set_recent_seq(&sync_ctx->mbox->ibox,
+					     sync_ctx->sync_view, seq1, seq2);
+	}
+	return 0;
+}
+
 static void mbox_sync_restart(struct mbox_sync_context *sync_ctx)
 {
 	sync_ctx->base_uid_validity = 0;
@@ -1511,6 +1553,8 @@
 		partial = FALSE;
 	}
 
+	if (mbox_sync_recent_state(sync_ctx) < 0)
+		return -1;
 	if (mbox_sync_handle_eof_updates(sync_ctx, &mail_ctx) < 0)
 		return -1;
 
@@ -1664,6 +1708,13 @@
 		return ret;
 	}
 
+	if (!mbox->ibox.keep_recent) {
+		/* see if we need to drop recent flags */
+		sync_ctx.hdr = mail_index_get_header(sync_view);
+		if (sync_ctx.hdr->first_recent_uid < sync_ctx.hdr->next_uid)
+			changed = 1;
+	}
+
 	if (!changed && !mail_index_sync_have_more(index_sync_ctx)) {
 		/* nothing to do */
 	__nothing_to_do:
--- a/src/lib/seq-range-array.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib/seq-range-array.c	Mon Jul 16 09:48:02 2007 +0300
@@ -100,22 +100,22 @@
 	}
 }
 
-void seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq)
+bool seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq)
 {
 	struct seq_range *data, value;
 	unsigned int idx, left_idx, right_idx, count;
 
 	if (!array_is_created(array))
-		return;
+		return FALSE;
 
 	data = array_get_modifiable(array, &count);
 	if (count == 0)
-		return;
+		return FALSE;
 
 	/* quick checks */
 	if (seq > data[count-1].seq2 || seq < data[0].seq1) {
 		/* outside the range */
-		return;
+		return FALSE;
 	}
 	if (data[count-1].seq2 == seq) {
 		/* shrink last range */
@@ -123,7 +123,7 @@
 			data[count-1].seq2--;
 		else
 			array_delete(array, count-1, 1);
-		return;
+		return TRUE;
 	}
 	if (data[0].seq1 == seq) {
 		/* shrink up first range */
@@ -131,7 +131,7 @@
 			data[0].seq1++;
 		else
 			array_delete(array, 0, 1);
-		return;
+		return TRUE;
 	}
 
 	/* somewhere in the middle, array is sorted so find it with
@@ -166,9 +166,10 @@
 
 				array_insert(array, idx + 1, &value, 1);
 			}
-			break;
+			return TRUE;
 		}
 	}
+	return FALSE;
 }
 
 void seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array,
--- a/src/lib/seq-range-array.h	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/lib/seq-range-array.h	Mon Jul 16 09:48:02 2007 +0300
@@ -9,7 +9,7 @@
 
 void seq_range_array_add(ARRAY_TYPE(seq_range) *array, unsigned int init_count,
 			 uint32_t seq);
-void seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq);
+bool seq_range_array_remove(ARRAY_TYPE(seq_range) *array, uint32_t seq);
 void seq_range_array_remove_range(ARRAY_TYPE(seq_range) *array,
 				  uint32_t seq1, uint32_t seq2);
 bool seq_range_exists(const ARRAY_TYPE(seq_range) *array, uint32_t seq);
--- a/src/util/idxview.c	Mon Jul 16 07:52:50 2007 +0300
+++ b/src/util/idxview.c	Mon Jul 16 09:48:02 2007 +0300
@@ -62,10 +62,9 @@
 	printf("uid validity = %u\n", hdr.uid_validity);
 	printf("next uid = %u\n", hdr.next_uid);
 	printf("messages count = %u\n", hdr.messages_count);
-	printf("recent messages count = %u\n", hdr.recent_messages_count);
 	printf("seen messages count = %u\n", hdr.seen_messages_count);
 	printf("deleted messages count = %u\n", hdr.deleted_messages_count);
-	printf("first recent uid lowwater = %u\n", hdr.first_recent_uid_lowwater);
+	printf("first recent uid = %u\n", hdr.first_recent_uid);
 	printf("first unseen uid lowwater = %u\n", hdr.first_unseen_uid_lowwater);
 	printf("first deleted uid lowwater = %u\n", hdr.first_deleted_uid_lowwater);
 	printf("log file seq = %u\n", hdr.log_file_seq);