Browse code

[eas] BM-8229 Fix: load highest modseq from server and never use a value higher than that for our sync

Thomas Cataldo authored on 13/04/2018 09:22:53
Showing 6 changed files
... ...
@@ -61,6 +61,7 @@ import net.bluemind.imap.MailboxChanges;
61 61
 import net.bluemind.imap.SearchQuery;
62 62
 import net.bluemind.imap.StoreClient;
63 63
 import net.bluemind.imap.SyncData;
64
+import net.bluemind.imap.SyncStatus;
64 65
 import net.bluemind.mailbox.api.IMailboxes;
65 66
 import net.bluemind.mailbox.api.Mailbox;
66 67
 import net.bluemind.mailbox.api.Mailbox.Type;
... ...
@@ -108,11 +109,16 @@ public class EmailManager extends AbstractItemManager {
108 108
 			logger.warn("FilterType has changed");
109 109
 		}
110 110
 
111
-		long validity = store.getUidValidity(mailbox);
111
+		SyncStatus validity = store.getUidValidity(mailbox);
112 112
 		long modseq = state.version;
113 113
 		if (modseq == 0) {
114 114
 			modseq = 1L;
115 115
 		}
116
+		if (modseq > validity.highestModSeq) {
117
+			logger.warn("QRESYNC highestmodseq is {} and we wanted to sync from {}", validity.highestModSeq, modseq);
118
+			// force an empty sync instead re-downloading every deletes...
119
+			modseq = validity.highestModSeq;
120
+		}
116 121
 
117 122
 		long lowest = state.lowestUid;
118 123
 		if (lowest == 0) {
... ...
@@ -120,7 +126,7 @@ public class EmailManager extends AbstractItemManager {
120 120
 			lowest = store.getFirstUid();
121 121
 		}
122 122
 
123
-		SyncData sd = new SyncData(validity, modseq, state.highestUid, lowest);
123
+		SyncData sd = new SyncData(validity.uidValidity, modseq, state.highestUid, lowest);
124 124
 		MailboxChanges changes = store.sync(mailbox, sd);
125 125
 
126 126
 		if (logger.isDebugEnabled()) {
... ...
@@ -32,8 +32,8 @@ public class QResyncTests extends LoggedTestCase {
32 32
 	}
33 33
 
34 34
 	public void testGetUidValidity() {
35
-		long validity = sc.getUidValidity("INBOX");
36
-		assertTrue(validity > 0);
35
+		SyncStatus validity = sc.getUidValidity("INBOX");
36
+		assertTrue(validity.uidValidity > 0);
37 37
 		System.out.println("validity: " + validity);
38 38
 	}
39 39
 
... ...
@@ -42,8 +42,8 @@ public class QResyncTests extends LoggedTestCase {
42 42
 		// ensure 1 mail in box before first full sync
43 43
 		int added = sc.append("INBOX", getRfc822Message(), new FlagsList());
44 44
 		assertTrue(added > 0);
45
-		long validity = sc.getUidValidity("INBOX");
46
-		SyncData sd = new SyncData(validity);
45
+		SyncStatus validity = sc.getUidValidity("INBOX");
46
+		SyncData sd = new SyncData(validity.uidValidity);
47 47
 		MailboxChanges changes = sc.sync("INBOX", sd);
48 48
 		assertNotNull(changes);
49 49
 		long modseq = changes.modseq;
... ...
@@ -56,7 +56,7 @@ public class QResyncTests extends LoggedTestCase {
56 56
 		// add one mail and do an incremental sync
57 57
 		int addedAfterSync = sc.append("INBOX", getRfc822Message(), new FlagsList());
58 58
 		assertEquals(changes.highestUid + 1, addedAfterSync);
59
-		sd = new SyncData(validity, modseq, changes.highestUid, 0);
59
+		sd = new SyncData(validity.uidValidity, modseq, changes.highestUid, 0);
60 60
 		System.out.println("**** +1 mail (uid " + addedAfterSync + ") ****");
61 61
 		MailboxChanges afterOneAdd = sc.sync("INBOX", sd);
62 62
 		assertTrue(afterOneAdd.modseq > modseq);
... ...
@@ -72,7 +72,7 @@ public class QResyncTests extends LoggedTestCase {
72 72
 		assertTrue(flagged);
73 73
 
74 74
 		System.out.println("**** marked 1 as read ****");
75
-		sd = new SyncData(validity, afterOneAdd.modseq, afterOneAdd.highestUid, 0);
75
+		sd = new SyncData(validity.uidValidity, afterOneAdd.modseq, afterOneAdd.highestUid, 0);
76 76
 		MailboxChanges flagChange = sc.sync("INBOX", sd);
77 77
 		assertTrue(flagChange.modseq > afterOneAdd.modseq);
78 78
 		assertEquals(1, flagChange.updated.size());
... ...
@@ -87,7 +87,7 @@ public class QResyncTests extends LoggedTestCase {
87 87
 		assertTrue(deleted);
88 88
 
89 89
 		System.out.println("**** flagged 2 as deleted ****");
90
-		sd = new SyncData(validity, flagChange.modseq, flagChange.highestUid, 0);
90
+		sd = new SyncData(validity.uidValidity, flagChange.modseq, flagChange.highestUid, 0);
91 91
 		MailboxChanges twoDeletes = sc.sync("INBOX", sd);
92 92
 		// ensure we already flag as deleted before expunge
93 93
 		assertEquals(2, twoDeletes.deleted.size());
... ...
@@ -105,8 +105,8 @@ public class QResyncTests extends LoggedTestCase {
105 105
 		// ensure 1 mail in box before first full sync
106 106
 		int added = sc.append("INBOX", getRfc822Message(), new FlagsList());
107 107
 		assertTrue(added > 0);
108
-		long validity = sc.getUidValidity("INBOX");
109
-		SyncData sd = new SyncData(validity, 1, 1, 0);
108
+		SyncStatus validity = sc.getUidValidity("INBOX");
109
+		SyncData sd = new SyncData(validity.uidValidity, 1, 1, 0);
110 110
 		MailboxChanges changes = sc.sync("INBOX", sd);
111 111
 		assertNotNull(changes);
112 112
 		long modseq = changes.modseq;
... ...
@@ -397,7 +397,7 @@ public class StoreClient implements AutoCloseable {
397 397
 		return cs.enable(capability, otherCapabilities);
398 398
 	}
399 399
 
400
-	public long getUidValidity(String mailbox) {
400
+	public SyncStatus getUidValidity(String mailbox) {
401 401
 		return cs.getUidValidity(mailbox);
402 402
 	}
403 403
 
404 404
new file mode 100644
... ...
@@ -0,0 +1,38 @@
0
+/* BEGIN LICENSE
1
+  * Copyright © Blue Mind SAS, 2012-2018
2
+  *
3
+  * This file is part of BlueMind. BlueMind is a messaging and collaborative
4
+  * solution.
5
+  *
6
+  * This program is free software; you can redistribute it and/or modify
7
+  * it under the terms of either the GNU Affero General Public License as
8
+  * published by the Free Software Foundation (version 3 of the License).
9
+  *
10
+  * This program is distributed in the hope that it will be useful,
11
+  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
+  *
14
+  * See LICENSE.txt
15
+  * END LICENSE
16
+  */
17
+package net.bluemind.imap;
18
+
19
+import com.google.common.base.MoreObjects;
20
+
21
+public class SyncStatus {
22
+
23
+	public final long uidValidity;
24
+	public final long highestModSeq;
25
+
26
+	public SyncStatus(long uidValidity, long modSeq) {
27
+		this.uidValidity = uidValidity;
28
+		this.highestModSeq = modSeq;
29
+	}
30
+
31
+	public String toString() {
32
+		return MoreObjects.toStringHelper(SyncStatus.class)//
33
+				.add("uidvalidity", uidValidity).add("highestmodseq", highestModSeq)//
34
+				.toString();
35
+	}
36
+
37
+}
... ...
@@ -20,25 +20,29 @@ package net.bluemind.imap.command;
20 20
 
21 21
 import java.util.List;
22 22
 
23
+import net.bluemind.imap.SyncStatus;
23 24
 import net.bluemind.imap.impl.IMAPResponse;
24 25
 
25
-public class UidValidityCommand extends SimpleCommand<Long> {
26
+public class UidValidityCommand extends SimpleCommand<SyncStatus> {
26 27
 
27 28
 	private String mailbox;
28 29
 
29 30
 	public UidValidityCommand(String mailbox) {
30
-		super("STATUS " + toUtf7(mailbox) + " (UIDVALIDITY)");
31
+		super("STATUS " + toUtf7(mailbox) + " (UIDVALIDITY HIGHESTMODSEQ)");
31 32
 		this.mailbox = mailbox;
32 33
 	}
33 34
 
34 35
 	@Override
35 36
 	public void responseReceived(List<IMAPResponse> rs) {
36
-		data = 0L;
37 37
 		if (isOk(rs)) {
38
-			// * STATUS inbox (UIDVALIDITY 1394648781)
38
+			// * STATUS inbox (UIDVALIDITY 1394648781 HIGHESTMODSEQ 1234)
39 39
 			String unseen = rs.get(0).getPayload();
40
-			int s = unseen.lastIndexOf(' ');
41
-			data = Long.parseLong(unseen.substring(s + 1, unseen.length() - 1));
40
+			int paren = unseen.indexOf("(UIDVALIDITY");
41
+			String fetched = unseen.substring(paren, unseen.length() - 1);
42
+			String[] splitted = fetched.split(" ");
43
+			long uidValidity = Long.parseLong(splitted[1]);
44
+			long modSeq = Long.parseLong(splitted[3]);
45
+			data = new SyncStatus(uidValidity, modSeq);
42 46
 		} else {
43 47
 			for (IMAPResponse ir : rs) {
44 48
 				logger.error("{}: {}", mailbox, ir.getPayload());
... ...
@@ -56,6 +56,7 @@ import net.bluemind.imap.QuotaInfo;
56 56
 import net.bluemind.imap.SearchQuery;
57 57
 import net.bluemind.imap.Summary;
58 58
 import net.bluemind.imap.SyncData;
59
+import net.bluemind.imap.SyncStatus;
59 60
 import net.bluemind.imap.TaggedResult;
60 61
 import net.bluemind.imap.command.AppendCommand;
61 62
 import net.bluemind.imap.command.CapabilityCommand;
... ...
@@ -450,7 +451,7 @@ public final class ClientSupport {
450 450
 		return run(new EnableCommand(capability, otherCapabilities));
451 451
 	}
452 452
 
453
-	public long getUidValidity(String mailbox) {
453
+	public SyncStatus getUidValidity(String mailbox) {
454 454
 		return run(new UidValidityCommand(mailbox));
455 455
 	}
456 456