changeset 19634:98b116c28a95

pop3c: Preserve local cache even when server reorders messages.
author Timo Sirainen <timo.sirainen@dovecot.fi>
date Mon, 25 Jan 2016 22:33:29 +0200
parents 7c03eb7b0f67
children 3712091cf4d0
files src/lib-storage/index/pop3c/pop3c-sync.c
diffstat 1 files changed, 100 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-storage/index/pop3c/pop3c-sync.c	Mon Jan 25 21:57:35 2016 +0200
+++ b/src/lib-storage/index/pop3c/pop3c-sync.c	Mon Jan 25 22:33:29 2016 +0200
@@ -11,6 +11,12 @@
 #include "pop3c-storage.h"
 #include "pop3c-sync.h"
 
+struct pop3c_sync_msg {
+	uint32_t seq;
+	const char *uidl;
+};
+ARRAY_DEFINE_TYPE(pop3c_sync_msg, struct pop3c_sync_msg);
+
 int pop3c_sync_get_uidls(struct pop3c_mailbox *mbox)
 {
 	ARRAY_TYPE(const_string) uidls;
@@ -129,6 +135,48 @@
 }
 
 static void
+pop3c_get_local_msgs(pool_t pool, ARRAY_TYPE(pop3c_sync_msg) *local_msgs,
+		     uint32_t messages_count,
+		     struct mail_cache_view *cache_view,
+		     unsigned int cache_idx)
+{
+	string_t *str = t_str_new(128);
+	struct pop3c_sync_msg msg;
+	uint32_t seq;
+
+	memset(&msg, 0, sizeof(msg));
+	for (seq = 1; seq <= messages_count; seq++) {
+		str_truncate(str, 0);
+		if (mail_cache_lookup_field(cache_view, str, seq,
+					    cache_idx) > 0) {
+			msg.seq = seq;
+			msg.uidl = p_strdup(pool, str_c(str));
+			array_idx_set(local_msgs, seq-1, &msg);
+		}
+	}
+}
+
+static void
+pop3c_get_remote_msgs(ARRAY_TYPE(pop3c_sync_msg) *remote_msgs,
+		      struct pop3c_mailbox *mbox)
+{
+	struct pop3c_sync_msg *msg;
+	uint32_t seq;
+
+	for (seq = 1; seq <= mbox->msg_count; seq++) {
+		msg = array_append_space(remote_msgs);
+		msg->seq = seq;
+		msg->uidl = mbox->msg_uidls[seq-1];
+	}
+}
+
+static int pop3c_sync_msg_uidl_cmp(const struct pop3c_sync_msg *msg1,
+				   const struct pop3c_sync_msg *msg2)
+{
+	return null_strcmp(msg1->uidl, msg2->uidl);
+}
+
+static void
 pop3c_sync_messages(struct pop3c_mailbox *mbox,
 		    struct mail_index_view *sync_view,
 		    struct mail_index_transaction *sync_trans,
@@ -138,9 +186,12 @@
 		INDEX_STORAGE_CONTEXT(&mbox->box);
 	const struct mail_index_header *hdr;
 	struct mail_cache_transaction_ctx *cache_trans;
-	string_t *str;
-	uint32_t lseq, rseq, seq1, seq2, uid;
+	ARRAY_TYPE(pop3c_sync_msg) local_msgs, remote_msgs;
+	const struct pop3c_sync_msg *lmsg, *rmsg;
+	uint32_t seq1, seq2, next_uid;
+	unsigned int lidx, ridx, lcount, rcount;
 	unsigned int cache_idx = ibox->cache_fields[MAIL_CACHE_POP3_UIDL].idx;
+	pool_t pool;
 
 	i_assert(mbox->msg_uids == NULL);
 
@@ -153,38 +204,65 @@
 			&uid_validity, sizeof(uid_validity), TRUE);
 	}
 
+	pool = pool_alloconly_create(MEMPOOL_GROWING"pop3c sync", 10240);
+	p_array_init(&local_msgs, pool, hdr->messages_count);
+	pop3c_get_local_msgs(pool, &local_msgs, hdr->messages_count,
+			     cache_view, cache_idx);
+	p_array_init(&remote_msgs, pool, mbox->msg_count);
+	pop3c_get_remote_msgs(&remote_msgs, mbox);
+
+	/* sort the messages by UIDLs, because some servers reorder messages */
+	array_sort(&local_msgs, pop3c_sync_msg_uidl_cmp);
+	array_sort(&remote_msgs, pop3c_sync_msg_uidl_cmp);
+
 	/* skip over existing messages with matching UIDLs and expunge the ones
-	   that no longer exist in remote. */
+	   that no longer exist in remote. (+1 to avoid malloc(0) assert) */
 	mbox->msg_uids = i_new(uint32_t, mbox->msg_count + 1);
-	str = t_str_new(128);
-	for (lseq = rseq = 1; lseq <= hdr->messages_count && rseq <= mbox->msg_count; lseq++) {
-		str_truncate(str, 0);
-		if (mail_cache_lookup_field(cache_view, str, lseq,
-					    cache_idx) > 0 &&
-		    strcmp(str_c(str), mbox->msg_uidls[rseq-1]) == 0) {
+	cache_trans = mail_cache_get_transaction(cache_view, sync_trans);
+
+	lmsg = array_get(&local_msgs, &lcount);
+	rmsg = array_get(&remote_msgs, &rcount);
+	next_uid = hdr->next_uid;
+	lidx = ridx = 0;
+	while (lidx < lcount || ridx < rcount) {
+		uint32_t lseq = lidx < lcount ? lmsg[lidx].seq : 0;
+		uint32_t rseq = ridx < rcount ? rmsg[ridx].seq : 0;
+		int ret;
+
+		if (lidx >= lcount)
+			ret = 1;
+		else if (ridx >= rcount)
+			ret = -1;
+		else
+			ret = strcmp(lmsg[lidx].uidl, rmsg[ridx].uidl);
+		if (ret < 0) {
+			/* message expunged in remote */
+			mail_index_expunge(sync_trans, lseq);
+			lidx++;
+		} else if (ret > 0) {
+			/* new message in remote */
+			i_assert(mbox->msg_uids[rseq-1] == 0);
+			mbox->msg_uids[rseq-1] = next_uid;
+			mail_index_append(sync_trans, next_uid++, &lseq);
+			mail_cache_add(cache_trans, lseq, cache_idx,
+				       rmsg[ridx].uidl,
+				       strlen(rmsg[ridx].uidl)+1);
+			ridx++;
+		} else {
 			/* UIDL matched */
+			i_assert(mbox->msg_uids[rseq-1] == 0);
 			mail_index_lookup_uid(sync_view, lseq,
 					      &mbox->msg_uids[rseq-1]);
-			rseq++;
-		} else {
-			mail_index_expunge(sync_trans, lseq);
+			lidx++;
+			ridx++;
 		}
 	}
-	/* add the rest of the messages in POP3 mailbox to index */
-	cache_trans = mail_cache_get_transaction(cache_view, sync_trans);
-	uid = hdr->next_uid;
-	for (; rseq <= mbox->msg_count; rseq++) {
-		mbox->msg_uids[rseq-1] = uid;
-		mail_index_append(sync_trans, uid++, &lseq);
-		mail_cache_add(cache_trans, lseq, cache_idx,
-			       mbox->msg_uidls[rseq-1],
-			       strlen(mbox->msg_uidls[rseq-1])+1);
-	}
 
 	/* mark the newly seen messages as recent */
 	if (mail_index_lookup_seq_range(sync_view, hdr->first_recent_uid,
 					hdr->next_uid, &seq1, &seq2))
 		mailbox_recent_flags_set_seqs(&mbox->box, sync_view, seq1, seq2);
+	pool_unref(&pool);
 }
 
 int pop3c_sync(struct pop3c_mailbox *mbox)