changeset 15496:5d90e2aa1ba8

dsync: Use private modseqs to support syncing private flags in shared mailboxes.
author Timo Sirainen <tss@iki.fi>
date Fri, 04 Jan 2013 01:13:05 +0200
parents 69371578720f
children 5bb879b6e3f3
files src/doveadm/dsync/dsync-brain-mailbox.c src/doveadm/dsync/dsync-brain-mails.c src/doveadm/dsync/dsync-ibc-stream.c src/doveadm/dsync/dsync-mail.c src/doveadm/dsync/dsync-mail.h src/doveadm/dsync/dsync-mailbox-export.c src/doveadm/dsync/dsync-mailbox-export.h src/doveadm/dsync/dsync-mailbox-import.c src/doveadm/dsync/dsync-mailbox-import.h src/doveadm/dsync/dsync-mailbox-state.c src/doveadm/dsync/dsync-mailbox-state.h src/doveadm/dsync/dsync-mailbox.h src/doveadm/dsync/dsync-transaction-log-scan.c src/doveadm/dsync/dsync-transaction-log-scan.h
diffstat 14 files changed, 176 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/src/doveadm/dsync/dsync-brain-mailbox.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-brain-mailbox.c	Fri Jan 04 01:13:05 2013 +0200
@@ -140,17 +140,20 @@
 {
 	enum dsync_mailbox_exporter_flags exporter_flags = 0;
 	uint32_t last_common_uid, highest_wanted_uid;
-	uint64_t last_common_modseq;
+	uint64_t last_common_modseq, last_common_pvt_modseq;
 
 	i_assert(brain->log_scan == NULL);
 
 	last_common_uid = brain->mailbox_state.last_common_uid;
 	last_common_modseq = brain->mailbox_state.last_common_modseq;
+	last_common_pvt_modseq = brain->mailbox_state.last_common_pvt_modseq;
 	highest_wanted_uid = last_common_uid == 0 ?
 		(uint32_t)-1 : last_common_uid;
 	if (dsync_transaction_log_scan_init(brain->box->view,
+					    brain->box->view_pvt,
 					    highest_wanted_uid,
 					    last_common_modseq,
+					    last_common_pvt_modseq,
 					    &brain->log_scan) < 0) {
 		i_error("Failed to read transaction log for mailbox %s",
 			mailbox_get_vname(brain->box));
@@ -165,7 +168,7 @@
 
 	brain->box_exporter = brain->backup_recv ? NULL :
 		dsync_mailbox_export_init(brain->box, brain->log_scan,
-					  last_common_uid, last_common_modseq,
+					  last_common_uid,
 					  exporter_flags);
 	return 0;
 }
@@ -176,7 +179,7 @@
 	enum dsync_mailbox_import_flags import_flags = 0;
 	const struct dsync_mailbox_state *state;
 	uint32_t last_common_uid;
-	uint64_t last_common_modseq;
+	uint64_t last_common_modseq, last_common_pvt_modseq;
 
 	i_assert(brain->box_importer == NULL);
 	i_assert(brain->log_scan != NULL);
@@ -191,9 +194,11 @@
 	if (state != NULL) {
 		last_common_uid = state->last_common_uid;
 		last_common_modseq = state->last_common_modseq;
+		last_common_pvt_modseq = state->last_common_pvt_modseq;
 	} else {
 		last_common_uid = 0;
 		last_common_modseq = 0;
+		last_common_pvt_modseq = 0;
 	}
 
 	if (brain->guid_requests)
@@ -208,9 +213,11 @@
 	brain->box_importer = brain->backup_send ? NULL :
 		dsync_mailbox_import_init(brain->box, brain->log_scan,
 					  last_common_uid, last_common_modseq,
+					  last_common_pvt_modseq,
 					  remote_dsync_box->uid_next,
 					  remote_dsync_box->first_recent_uid,
 					  remote_dsync_box->highest_modseq,
+					  remote_dsync_box->highest_pvt_modseq,
 					  import_flags);
 }
 
@@ -228,13 +235,14 @@
 	}
 	if (brain->box_importer != NULL) {
 		uint32_t last_common_uid;
-		uint64_t last_common_modseq;
+		uint64_t last_common_modseq, last_common_pvt_modseq;
 		bool changes_during_sync;
 
 		i_assert(brain->failed);
 		(void)dsync_mailbox_import_deinit(&brain->box_importer,
 						  &last_common_uid,
 						  &last_common_modseq,
+						  &last_common_pvt_modseq,
 						  &changes_during_sync);
 	}
 	if (brain->log_scan != NULL)
@@ -248,7 +256,8 @@
 {
 	const enum mailbox_status_items status_items =
 		STATUS_UIDVALIDITY | STATUS_UIDNEXT | STATUS_MESSAGES |
-		STATUS_FIRST_RECENT_UID | STATUS_HIGHESTMODSEQ;
+		STATUS_FIRST_RECENT_UID | STATUS_HIGHESTMODSEQ |
+		STATUS_HIGHESTPVTMODSEQ;
 	const enum mailbox_metadata_items metadata_items =
 		MAILBOX_METADATA_CACHE_FIELDS | MAILBOX_METADATA_GUID;
 	struct mailbox_status status;
@@ -283,6 +292,7 @@
 	dsync_box_r->messages_count = status.messages;
 	dsync_box_r->first_recent_uid = status.first_recent_uid;
 	dsync_box_r->highest_modseq = status.highest_modseq;
+	dsync_box_r->highest_pvt_modseq = status.highest_pvt_modseq;
 	dsync_box_r->cache_fields = *metadata.cache_fields;
 	return 1;
 }
@@ -300,7 +310,8 @@
 	return state == NULL ||
 		state->last_uidvalidity != dsync_box->uid_validity ||
 		state->last_common_uid+1 != dsync_box->uid_next ||
-		state->last_common_modseq != dsync_box->highest_modseq;
+		state->last_common_modseq != dsync_box->highest_modseq ||
+		state->last_common_pvt_modseq != dsync_box->highest_pvt_modseq;
 }
 
 static int
@@ -406,6 +417,7 @@
 			   const struct dsync_mailbox *box2)
 {
 	return box1->highest_modseq != box2->highest_modseq ||
+		box1->highest_pvt_modseq != box2->highest_pvt_modseq ||
 		box1->messages_count != box2->messages_count ||
 		box1->uid_next != box2->uid_next ||
 		box1->uid_validity != box2->uid_validity ||
--- a/src/doveadm/dsync/dsync-brain-mails.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-brain-mails.c	Fri Jan 04 01:13:05 2013 +0200
@@ -165,10 +165,13 @@
 		state.last_common_uid = brain->local_dsync_box.uid_next-1;
 		state.last_common_modseq =
 			brain->local_dsync_box.highest_modseq;
+		state.last_common_pvt_modseq =
+			brain->local_dsync_box.highest_pvt_modseq;
 	} else {
 		if (dsync_mailbox_import_deinit(&brain->box_importer,
 						&state.last_common_uid,
 						&state.last_common_modseq,
+						&state.last_common_pvt_modseq,
 						&changes_during_sync) < 0) {
 			i_error("Importing mailbox %s failed",
 				mailbox_get_vname(brain->box));
--- a/src/doveadm/dsync/dsync-ibc-stream.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-ibc-stream.c	Fri Jan 04 01:13:05 2013 +0200
@@ -66,7 +66,7 @@
 	{ .name = "mailbox_state",
 	  .chr = 'S',
 	  .required_keys = "mailbox_guid last_uidvalidity last_common_uid "
-	  	"last_common_modseq"
+	  	"last_common_modseq last_common_pvt_modseq"
 	},
 	{ .name = "mailbox_tree_node",
 	  .chr = 'N',
@@ -81,14 +81,14 @@
 	},
 	{ .name = "mailbox",
 	  .chr = 'B',
-	  .required_keys = "mailbox_guid uid_validity uid_next "
-		"messages_count first_recent_uid highest_modseq",
+	  .required_keys = "mailbox_guid uid_validity uid_next messages_count "
+		"first_recent_uid highest_modseq highest_pvt_modseq",
 	  .optional_keys = "mailbox_lost cache_fields"
 	},
 	{ .name = "mail_change",
 	  .chr = 'C',
 	  .required_keys = "type uid",
-	  .optional_keys = "guid hdr_hash modseq save_timestamp "
+	  .optional_keys = "guid hdr_hash modseq pvt_modseq save_timestamp "
 	  	"add_flags remove_flags final_flags "
 	  	"keywords_reset keyword_changes"
 	},
@@ -642,6 +642,8 @@
 				    dec2str(state->last_common_uid));
 	dsync_serializer_encode_add(encoder, "last_common_modseq",
 				    dec2str(state->last_common_modseq));
+	dsync_serializer_encode_add(encoder, "last_common_pvt_modseq",
+				    dec2str(state->last_common_pvt_modseq));
 
 	dsync_serializer_encode_finish(&encoder, str);
 	dsync_ibc_stream_send_string(ibc, str);
@@ -682,6 +684,11 @@
 		dsync_ibc_input_error(ibc, decoder, "Invalid last_common_modseq");
 		return DSYNC_IBC_RECV_RET_TRYAGAIN;
 	}
+	value = dsync_deserializer_decode_get(decoder, "last_common_pvt_modseq");
+	if (str_to_uint64(value, &state_r->last_common_pvt_modseq) < 0) {
+		dsync_ibc_input_error(ibc, decoder, "Invalid last_common_pvt_modseq");
+		return DSYNC_IBC_RECV_RET_TRYAGAIN;
+	}
 	return DSYNC_IBC_RECV_RET_OK;
 }
 
@@ -1002,6 +1009,8 @@
 				    dec2str(dsync_box->first_recent_uid));
 	dsync_serializer_encode_add(encoder, "highest_modseq",
 				    dec2str(dsync_box->highest_modseq));
+	dsync_serializer_encode_add(encoder, "highest_pvt_modseq",
+				    dec2str(dsync_box->highest_pvt_modseq));
 
 	value = get_cache_fields(ibc, dsync_box);
 	if (value != NULL)
@@ -1113,6 +1122,11 @@
 		dsync_ibc_input_error(ibc, decoder, "Invalid highest_modseq");
 		return DSYNC_IBC_RECV_RET_TRYAGAIN;
 	}
+	value = dsync_deserializer_decode_get(decoder, "highest_pvt_modseq");
+	if (str_to_uint64(value, &box->highest_pvt_modseq) < 0) {
+		dsync_ibc_input_error(ibc, decoder, "Invalid highest_pvt_modseq");
+		return DSYNC_IBC_RECV_RET_TRYAGAIN;
+	}
 
 	p_array_init(&box->cache_fields, pool, 32);
 	if (dsync_deserializer_decode_try(decoder, "cache_fields", &value)) {
@@ -1164,6 +1178,10 @@
 		dsync_serializer_encode_add(encoder, "modseq",
 					    dec2str(change->modseq));
 	}
+	if (change->pvt_modseq != 0) {
+		dsync_serializer_encode_add(encoder, "pvt_modseq",
+					    dec2str(change->pvt_modseq));
+	}
 	if (change->save_timestamp != 0) {
 		dsync_serializer_encode_add(encoder, "save_timestamp",
 					    dec2str(change->save_timestamp));
@@ -1252,6 +1270,11 @@
 		dsync_ibc_input_error(ibc, decoder, "Invalid modseq");
 		return DSYNC_IBC_RECV_RET_TRYAGAIN;
 	}
+	if (dsync_deserializer_decode_try(decoder, "pvt_modseq", &value) &&
+	    str_to_uint64(value, &change->pvt_modseq) < 0) {
+		dsync_ibc_input_error(ibc, decoder, "Invalid pvt_modseq");
+		return DSYNC_IBC_RECV_RET_TRYAGAIN;
+	}
 	if (dsync_deserializer_decode_try(decoder, "save_timestamp", &value) &&
 	    str_to_time(value, &change->save_timestamp) < 0) {
 		dsync_ibc_input_error(ibc, decoder, "Invalid save_timestamp");
--- a/src/doveadm/dsync/dsync-mail.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mail.c	Fri Jan 04 01:13:05 2013 +0200
@@ -73,6 +73,7 @@
 	}
 	dest_r->hdr_hash = p_strdup(pool, src->hdr_hash);
 	dest_r->modseq = src->modseq;
+	dest_r->pvt_modseq = src->pvt_modseq;
 	dest_r->save_timestamp = src->save_timestamp;
 
 	dest_r->add_flags = src->add_flags;
--- a/src/doveadm/dsync/dsync-mail.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mail.h	Fri Jan 04 01:13:05 2013 +0200
@@ -49,6 +49,9 @@
 
 	/* Message's current modseq (saves, flag changes) */
 	uint64_t modseq;
+	/* Message's current private modseq (for private flags in
+	   shared mailboxes, otherwise 0) */
+	uint64_t pvt_modseq;
 	/* Message's save timestamp (saves) */
 	time_t save_timestamp;
 
@@ -57,6 +60,7 @@
 	/* Flags added/removed since last sync, and final flags containing
 	   flags that exist now but haven't changed */
 	uint8_t add_flags, remove_flags, final_flags;
+	uint8_t add_pvt_flags, remove_pvt_flags;
 	/* Remove all keywords before applying changes. This is used only with
 	   old transaction logs, new ones never reset keywords (just explicitly
 	   remove unwanted keywords) */
--- a/src/doveadm/dsync/dsync-mailbox-export.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-export.c	Fri Jan 04 01:13:05 2013 +0200
@@ -21,7 +21,6 @@
 	struct mailbox *box;
 	struct dsync_transaction_log_scan *log_scan;
 	uint32_t last_common_uid;
-	uint64_t last_common_modseq;
 
 	struct mailbox_transaction_context *trans;
 	struct mail_search_context *search_ctx;
@@ -99,6 +98,7 @@
 	i_assert((change->add_flags & change->remove_flags) == 0);
 
 	change->modseq = mail_get_modseq(mail);
+	change->pvt_modseq = mail_get_pvt_modseq(mail);
 	change->final_flags = mail_get_flags(mail) & MAIL_FLAGS_NONRECENT;
 
 	keywords = mail_get_keywords(mail);
@@ -416,7 +416,6 @@
 dsync_mailbox_export_init(struct mailbox *box,
 			  struct dsync_transaction_log_scan *log_scan,
 			  uint32_t last_common_uid,
-			  uint64_t last_common_modseq,
 			  enum dsync_mailbox_exporter_flags flags)
 {
 	struct dsync_mailbox_exporter *exporter;
@@ -429,7 +428,6 @@
 	exporter->box = box;
 	exporter->log_scan = log_scan;
 	exporter->last_common_uid = last_common_uid;
-	exporter->last_common_modseq = last_common_modseq;
 	exporter->auto_export_mails =
 		(flags & DSYNC_MAILBOX_EXPORTER_FLAG_AUTO_EXPORT_MAILS) != 0;
 	exporter->mails_have_guids =
--- a/src/doveadm/dsync/dsync-mailbox-export.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-export.h	Fri Jan 04 01:13:05 2013 +0200
@@ -10,7 +10,6 @@
 dsync_mailbox_export_init(struct mailbox *box,
 			  struct dsync_transaction_log_scan *log_scan,
 			  uint32_t last_common_uid,
-			  uint64_t last_common_modseq,
 			  enum dsync_mailbox_exporter_flags flags);
 const struct dsync_mail_change *
 dsync_mailbox_export_next(struct dsync_mailbox_exporter *exporter);
--- a/src/doveadm/dsync/dsync-mailbox-import.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-import.c	Fri Jan 04 01:13:05 2013 +0200
@@ -40,10 +40,10 @@
 	pool_t pool;
 	struct mailbox *box;
 	uint32_t last_common_uid;
-	uint64_t last_common_modseq;
+	uint64_t last_common_modseq, last_common_pvt_modseq;
 	uint32_t remote_uid_next;
 	uint32_t remote_first_recent_uid;
-	uint64_t remote_highest_modseq;
+	uint64_t remote_highest_modseq, remote_highest_pvt_modseq;
 
 	struct mailbox_transaction_context *trans, *ext_trans;
 	struct mail_search_context *search_ctx;
@@ -70,7 +70,7 @@
 	unsigned int mail_request_idx;
 
 	uint32_t prev_uid, next_local_seq, local_uid_next;
-	uint64_t local_initial_highestmodseq;
+	uint64_t local_initial_highestmodseq, local_initial_highestpvtmodseq;
 
 	unsigned int failed:1;
 	unsigned int last_common_uid_found:1;
@@ -112,9 +112,11 @@
 			  struct dsync_transaction_log_scan *log_scan,
 			  uint32_t last_common_uid,
 			  uint64_t last_common_modseq,
+			  uint64_t last_common_pvt_modseq,
 			  uint32_t remote_uid_next,
 			  uint32_t remote_first_recent_uid,
 			  uint64_t remote_highest_modseq,
+			  uint64_t remote_highest_pvt_modseq,
 			  enum dsync_mailbox_import_flags flags)
 {
 	const enum mailbox_transaction_flags ext_trans_flags =
@@ -132,11 +134,13 @@
 	importer->box = box;
 	importer->last_common_uid = last_common_uid;
 	importer->last_common_modseq = last_common_modseq;
+	importer->last_common_pvt_modseq = last_common_pvt_modseq;
 	importer->last_common_uid_found =
 		last_common_uid != 0 || last_common_modseq != 0;
 	importer->remote_uid_next = remote_uid_next;
 	importer->remote_first_recent_uid = remote_first_recent_uid;
 	importer->remote_highest_modseq = remote_highest_modseq;
+	importer->remote_highest_pvt_modseq = remote_highest_pvt_modseq;
 
 	hash_table_create(&importer->import_guids, pool, 0, str_hash, strcmp);
 	hash_table_create_direct(&importer->import_uids, pool, 0);
@@ -162,11 +166,12 @@
 	importer->revert_local_changes =
 		(flags & DSYNC_MAILBOX_IMPORT_FLAG_REVERT_LOCAL_CHANGES) != 0;
 
-	mailbox_get_open_status(importer->box,
-				STATUS_UIDNEXT | STATUS_HIGHESTMODSEQ,
+	mailbox_get_open_status(importer->box, STATUS_UIDNEXT |
+				STATUS_HIGHESTMODSEQ | STATUS_HIGHESTPVTMODSEQ,
 				&status);
 	importer->local_uid_next = status.uidnext;
 	importer->local_initial_highestmodseq = status.highest_modseq;
+	importer->local_initial_highestpvtmodseq = status.highest_pvt_modseq;
 	dsync_mailbox_import_search_init(importer);
 
 	importer->local_changes = dsync_transaction_log_scan_get_hash(log_scan);
@@ -428,27 +433,40 @@
 static void
 merge_flags(uint32_t local_final, uint32_t local_add, uint32_t local_remove,
 	    uint32_t remote_final, uint32_t remote_add, uint32_t remote_remove,
-	    bool prefer_remote,
+	    uint32_t pvt_mask, bool prefer_remote, bool prefer_pvt_remote,
 	    uint32_t *change_add_r, uint32_t *change_remove_r)
 {
 	uint32_t combined_add, combined_remove, conflict_flags;
-	uint32_t local_wanted, remote_wanted;
+	uint32_t local_wanted, remote_wanted, conflict_pvt_flags;
 
 	/* resolve conflicts */
 	conflict_flags = local_add & remote_remove;
 	if (conflict_flags != 0) {
+		conflict_pvt_flags = conflict_flags & pvt_mask;
+		conflict_flags &= ~pvt_mask;
 		if (prefer_remote)
 			local_add &= ~conflict_flags;
 		else
 			remote_remove &= ~conflict_flags;
+		if (prefer_pvt_remote)
+			local_add &= ~conflict_pvt_flags;
+		else
+			remote_remove &= ~conflict_pvt_flags;
 	}
-	conflict_flags = local_remove & remote_add;
+	conflict_flags = (local_remove & remote_add) & ~pvt_mask;
 	if (conflict_flags != 0) {
+		conflict_pvt_flags = conflict_flags & pvt_mask;
+		conflict_flags &= ~pvt_mask;
 		if (prefer_remote)
 			local_remove &= ~conflict_flags;
 		else
 			remote_add &= ~conflict_flags;
+		if (prefer_pvt_remote)
+			local_remove &= ~conflict_pvt_flags;
+		else
+			remote_add &= ~conflict_pvt_flags;
 	}
+	
 	combined_add = local_add|remote_add;
 	combined_remove = local_remove|remote_remove;
 	i_assert((combined_add & combined_remove) == 0);
@@ -459,10 +477,15 @@
 
 	conflict_flags = local_wanted ^ remote_wanted;
 	if (conflict_flags != 0) {
-		if (prefer_remote)
+		if (prefer_remote && prefer_pvt_remote)
 			local_wanted = remote_wanted;
-		/*else
-			remote_wanted = local_wanted;*/
+		else if (prefer_remote && !prefer_pvt_remote) {
+			local_wanted = (local_wanted & pvt_mask) |
+				(remote_wanted & ~pvt_mask);
+		} else if (!prefer_remote && prefer_pvt_remote) {
+			local_wanted = (local_wanted & ~pvt_mask) |
+				(remote_wanted & pvt_mask);
+		}
 	}
 
 	*change_add_r = local_wanted & ~local_final;
@@ -609,7 +632,8 @@
 	for (i = 0; i < array_size; i++) {
 		merge_flags(local_final[i], local_add[i], local_remove[i],
 			    remote_final[i], remote_add[i], remote_remove[i],
-			    prefer_remote, &change_add[i], &change_remove[i]);
+			    0, prefer_remote, prefer_remote,
+			    &change_add[i], &change_remove[i]);
 		if (change_add[i] != 0) {
 			keywords_append(&add_keywords, &all_keywords,
 					change_add[i], i*32);
@@ -673,6 +697,7 @@
 	mail_update_flags(mail, MODIFY_REPLACE,
 			  change->add_flags | change->final_flags);
 	mail_update_modseq(mail, change->modseq);
+	mail_update_pvt_modseq(mail, change->pvt_modseq);
 }
 
 static void
@@ -684,7 +709,7 @@
 	uint32_t change_add, change_remove;
 	ARRAY_TYPE(const_string) local_keyword_changes = ARRAY_INIT;
 	struct mail *mail;
-	bool prefer_remote;
+	bool prefer_remote, prefer_pvt_remote;
 
 	i_assert((change->add_flags & change->remove_flags) == 0);
 
@@ -723,11 +748,19 @@
 		   they become unsynced. */
 		prefer_remote = !importer->master_brain;
 	}
+	if (mail_get_pvt_modseq(mail) < change->pvt_modseq)
+		prefer_pvt_remote = TRUE;
+	else if (mail_get_pvt_modseq(mail) > change->pvt_modseq)
+		prefer_pvt_remote = FALSE;
+	else
+		prefer_pvt_remote = !importer->master_brain;
 
 	/* merge flags */
 	merge_flags(mail_get_flags(mail), local_add, local_remove,
 		    change->final_flags, change->add_flags, change->remove_flags,
-		    prefer_remote, &change_add, &change_remove);
+		    mailbox_get_private_flags_mask(mail->box),
+		    prefer_remote, prefer_pvt_remote,
+		    &change_add, &change_remove);
 
 	if (change_add != 0)
 		mail_update_flags(mail, MODIFY_ADD, change_add);
@@ -738,6 +771,7 @@
 	merge_keywords(mail, &local_keyword_changes, &change->keyword_changes,
 		       prefer_remote);
 	mail_update_modseq(mail, change->modseq);
+	mail_update_pvt_modseq(mail, change->pvt_modseq);
 }
 
 static void
@@ -1138,6 +1172,9 @@
 		(void)mailbox_enable(importer->box, MAILBOX_FEATURE_CONDSTORE);
 		mailbox_save_set_min_modseq(save_ctx, change->modseq);
 	}
+	/* FIXME: if there already are private flags, they get lost because
+	   saving can't handle updating private index. they get added on the
+	   next sync though. if this is fixed here, set min_pvt_modseq also. */
 }
 
 static int
@@ -1409,6 +1446,7 @@
 		I_MIN(importer->last_common_uid+1,
 		      importer->remote_first_recent_uid);
 	update.min_highest_modseq = importer->remote_highest_modseq;
+	update.min_highest_pvt_modseq = importer->remote_highest_pvt_modseq;
 
 	if (mailbox_update(importer->box, &update) < 0) {
 		i_error("Mailbox update failed to mailbox %s: %s",
@@ -1477,6 +1515,7 @@
 int dsync_mailbox_import_deinit(struct dsync_mailbox_importer **_importer,
 				uint32_t *last_common_uid_r,
 				uint64_t *last_common_modseq_r,
+				uint64_t *last_common_pvt_modseq_r,
 				bool *changes_during_sync_r)
 {
 	struct dsync_mailbox_importer *importer = *_importer;
@@ -1517,14 +1556,16 @@
 		array_free(&importer->mail_requests);
 
 	*last_common_uid_r = importer->last_common_uid;
-	if (!*changes_during_sync_r)
+	if (!*changes_during_sync_r) {
 		*last_common_modseq_r = importer->remote_highest_modseq;
-	else {
+		*last_common_pvt_modseq_r = importer->remote_highest_pvt_modseq;
+	} else {
 		/* local changes occurred during dsync. we exported changes up
 		   to local_initial_highestmodseq, so all of the changes have
 		   happened after it. we want the next run to see those changes,
 		   so return it as the last common modseq */
 		*last_common_modseq_r = importer->local_initial_highestmodseq;
+		*last_common_pvt_modseq_r = importer->local_initial_highestpvtmodseq;
 	}
 
 	ret = importer->failed ? -1 : 0;
--- a/src/doveadm/dsync/dsync-mailbox-import.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-import.h	Fri Jan 04 01:13:05 2013 +0200
@@ -18,9 +18,11 @@
 			  struct dsync_transaction_log_scan *log_scan,
 			  uint32_t last_common_uid,
 			  uint64_t last_common_modseq,
+			  uint64_t last_common_pvt_modseq,
 			  uint32_t remote_uid_next,
 			  uint32_t remote_first_recent_uid,
 			  uint64_t remote_highest_modseq,
+			  uint64_t remote_highest_pvt_modseq,
 			  enum dsync_mailbox_import_flags flags);
 void dsync_mailbox_import_change(struct dsync_mailbox_importer *importer,
 				 const struct dsync_mail_change *change);
@@ -32,6 +34,7 @@
 int dsync_mailbox_import_deinit(struct dsync_mailbox_importer **importer,
 				uint32_t *last_common_uid_r,
 				uint64_t *last_common_modseq_r,
+				uint64_t *last_common_pvt_modseq_r,
 				bool *changes_during_sync_r);
 
 #endif
--- a/src/doveadm/dsync/dsync-mailbox-state.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-state.c	Fri Jan 04 01:13:05 2013 +0200
@@ -40,6 +40,8 @@
 		put_uint32(buf, state->last_common_uid);
 		put_uint32(buf, state->last_common_modseq & 0xffffffffU);
 		put_uint32(buf, state->last_common_modseq >> 32);
+		put_uint32(buf, state->last_common_pvt_modseq & 0xffffffffU);
+		put_uint32(buf, state->last_common_pvt_modseq >> 32);
 		if (buf->used % 3 == 0) {
 			crc = crc32_data_more(crc, buf->data, buf->used);
 			base64_encode(buf->data, buf->used, output);
@@ -93,6 +95,9 @@
 		state->last_common_modseq =
 			get_uint32(data + GUID_128_SIZE + 8) |
 			(uint64_t)get_uint32(data + GUID_128_SIZE + 12) << 32;
+		state->last_common_pvt_modseq =
+			get_uint32(data + GUID_128_SIZE + 16) |
+			(uint64_t)get_uint32(data + GUID_128_SIZE + 20) << 32;
 		guid_p = state->mailbox_guid;
 		hash_table_insert(states, guid_p, state);
 	}
--- a/src/doveadm/dsync/dsync-mailbox-state.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox-state.h	Fri Jan 04 01:13:05 2013 +0200
@@ -8,6 +8,7 @@
 	uint32_t last_uidvalidity;
 	uint32_t last_common_uid;
 	uint64_t last_common_modseq;
+	uint64_t last_common_pvt_modseq;
 };
 ARRAY_DEFINE_TYPE(dsync_mailbox_state, struct dsync_mailbox_state);
 HASH_TABLE_DEFINE_TYPE(dsync_mailbox_state, uint8_t *, struct dsync_mailbox_state *);
--- a/src/doveadm/dsync/dsync-mailbox.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-mailbox.h	Fri Jan 04 01:13:05 2013 +0200
@@ -10,7 +10,7 @@
 	bool mailbox_lost;
 
 	uint32_t uid_validity, uid_next, messages_count, first_recent_uid;
-	uint64_t highest_modseq;
+	uint64_t highest_modseq, highest_pvt_modseq;
 	ARRAY_TYPE(mailbox_cache_field) cache_fields;
 };
 
--- a/src/doveadm/dsync/dsync-transaction-log-scan.c	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-transaction-log-scan.c	Fri Jan 04 01:13:05 2013 +0200
@@ -105,11 +105,11 @@
 }
 
 static void
-log_add_expunge_guid(struct dsync_transaction_log_scan *ctx, const void *data,
+log_add_expunge_guid(struct dsync_transaction_log_scan *ctx,
+		     struct mail_index_view *view, const void *data,
 		     const struct mail_transaction_header *hdr)
 {
 	const struct mail_transaction_expunge_guid *rec = data, *end;
-	struct mail_index_view *view = ctx->view;
 	struct dsync_mail_change *change;
 	uint32_t seq;
 	bool external;
@@ -271,7 +271,7 @@
 
 static void
 log_add_modseq_update(struct dsync_transaction_log_scan *ctx, const void *data,
-		      const struct mail_transaction_header *hdr)
+		      const struct mail_transaction_header *hdr, bool pvt_scan)
 {
 	const struct mail_transaction_modseq_update *rec = data, *end;
 	struct dsync_mail_change *change;
@@ -292,16 +292,21 @@
 
 		modseq = rec->modseq_low32 |
 			((uint64_t)rec->modseq_high32 << 32);
-		if (change->modseq < modseq)
-			change->modseq = modseq;
+		if (!pvt_scan) {
+			if (change->modseq < modseq)
+				change->modseq = modseq;
+		} else {
+			if (change->pvt_modseq < modseq)
+				change->pvt_modseq = modseq;
+		}
 	}
 }
 
 static int
 dsync_log_set(struct dsync_transaction_log_scan *ctx,
+	      struct mail_index_view *view, bool pvt_scan,
 	      struct mail_transaction_log_view *log_view, uint64_t modseq)
 {
-	struct mail_index_view *view = ctx->view;
 	uint32_t log_seq;
 	uoff_t log_offset;
 	bool reset;
@@ -320,35 +325,25 @@
 	}
 	if (ret == 0) {
 		/* return everything we've got */
-		ctx->returned_all_changes = TRUE;
+		if (!pvt_scan)
+			ctx->returned_all_changes = TRUE;
 		return mail_transaction_log_view_set_all(log_view);
 	}
 	return ret < 0 ? -1 : 0;
 }
 
-int dsync_transaction_log_scan_init(struct mail_index_view *view,
-				    uint32_t highest_wanted_uid,
-				    uint64_t modseq,
-				    struct dsync_transaction_log_scan **scan_r)
+static int
+dsync_log_scan(struct dsync_transaction_log_scan *ctx,
+	       struct mail_index_view *view, uint64_t modseq, bool pvt_scan)
 {
-	struct dsync_transaction_log_scan *ctx;
 	struct mail_transaction_log_view *log_view;
 	const struct mail_transaction_header *hdr;
 	const void *data;
 	uint32_t file_seq, max_seq;
 	uoff_t file_offset, max_offset;
-	pool_t pool;
-
-	pool = pool_alloconly_create(MEMPOOL_GROWING"dsync transaction log scan",
-				     10240);
-	ctx = p_new(pool, struct dsync_transaction_log_scan, 1);
-	ctx->pool = pool;
-	hash_table_create_direct(&ctx->changes, pool, 0);
-	ctx->view = view;
-	ctx->highest_wanted_uid = highest_wanted_uid;
 
 	log_view = mail_transaction_log_view_open(view->index->log);
-	if (dsync_log_set(ctx, log_view, modseq) < 0) {
+	if (dsync_log_set(ctx, view, pvt_scan, log_view, modseq) < 0) {
 		mail_transaction_log_view_close(&log_view);
 		return -1;
 	}
@@ -376,10 +371,12 @@
 
 		switch (hdr->type & MAIL_TRANSACTION_TYPE_MASK) {
 		case MAIL_TRANSACTION_EXPUNGE:
-			log_add_expunge(ctx, data, hdr);
+			if (!pvt_scan)
+				log_add_expunge(ctx, data, hdr);
 			break;
 		case MAIL_TRANSACTION_EXPUNGE_GUID:
-			log_add_expunge_guid(ctx, data, hdr);
+			if (!pvt_scan)
+				log_add_expunge_guid(ctx, view, data, hdr);
 			break;
 		case MAIL_TRANSACTION_FLAG_UPDATE:
 			log_add_flag_update(ctx, data, hdr);
@@ -393,15 +390,43 @@
 			} T_END;
 			break;
 		case MAIL_TRANSACTION_MODSEQ_UPDATE:
-			log_add_modseq_update(ctx, data, hdr);
+			log_add_modseq_update(ctx, data, hdr, pvt_scan);
 			break;
 		}
 	}
 
-	ctx->last_log_seq = file_seq;
-	ctx->last_log_offset = file_offset;
+	if (!pvt_scan) {
+		ctx->last_log_seq = file_seq;
+		ctx->last_log_offset = file_offset;
+	}
+	mail_transaction_log_view_close(&log_view);
+	return 0;
+}
 
-	mail_transaction_log_view_close(&log_view);
+int dsync_transaction_log_scan_init(struct mail_index_view *view,
+				    struct mail_index_view *pvt_view,
+				    uint32_t highest_wanted_uid,
+				    uint64_t modseq, uint64_t pvt_modseq,
+				    struct dsync_transaction_log_scan **scan_r)
+{
+	struct dsync_transaction_log_scan *ctx;
+	pool_t pool;
+
+	pool = pool_alloconly_create(MEMPOOL_GROWING"dsync transaction log scan",
+				     10240);
+	ctx = p_new(pool, struct dsync_transaction_log_scan, 1);
+	ctx->pool = pool;
+	hash_table_create_direct(&ctx->changes, pool, 0);
+	ctx->view = view;
+	ctx->highest_wanted_uid = highest_wanted_uid;
+
+	if (dsync_log_scan(ctx, view, modseq, FALSE) < 0)
+		return -1;
+	if (pvt_view != NULL) {
+		if (dsync_log_scan(ctx, pvt_view, pvt_modseq, TRUE) < 0)
+			return -1;
+	}
+
 	*scan_r = ctx;
 	return 0;
 }
--- a/src/doveadm/dsync/dsync-transaction-log-scan.h	Fri Jan 04 01:12:24 2013 +0200
+++ b/src/doveadm/dsync/dsync-transaction-log-scan.h	Fri Jan 04 01:13:05 2013 +0200
@@ -8,8 +8,9 @@
 struct dsync_transaction_log_scan;
 
 int dsync_transaction_log_scan_init(struct mail_index_view *view,
+				    struct mail_index_view *pvt_view,
 				    uint32_t highest_wanted_uid,
-				    uint64_t modseq,
+				    uint64_t modseq, uint64_t pvt_modseq,
 				    struct dsync_transaction_log_scan **scan_r);
 HASH_TABLE_TYPE(dsync_uid_mail_change)
 dsync_transaction_log_scan_get_hash(struct dsync_transaction_log_scan *scan);