This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 151db97de33fbffb6924cfe19273de8433e1fc8b
Author: Benoit Tellier <[email protected]>
AuthorDate: Mon Mar 2 07:10:25 2020 +0100

    JAMES-3074 UidValidity sanitizing at the IMAP level
---
 .../parser/AbstractSelectionCommandParser.java     |  20 +---
 .../imap/decode/parser/ExamineCommandParser.java   |   4 +-
 .../imap/decode/parser/SelectCommandParser.java    |   4 +-
 .../request/AbstractMailboxSelectionRequest.java   | 125 ++++++++++++++++++++-
 .../james/imap/message/request/ExamineRequest.java |   3 +-
 .../james/imap/message/request/SelectRequest.java  |   3 +-
 .../imap/processor/AbstractSelectionProcessor.java |   9 +-
 .../james/imap/processor/ExamineProcessor.java     |   5 +-
 .../james/imap/processor/SelectProcessor.java      |   5 +-
 .../decode/parser/SelectCommandParserTest.java     |  17 ++-
 .../AbstractMailboxSelectionRequestTest.java       | 123 ++++++++++++++++++++
 11 files changed, 276 insertions(+), 42 deletions(-)

diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
index d183a29..5014107 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/AbstractSelectionCommandParser.java
@@ -32,8 +32,8 @@ import org.apache.james.imap.decode.ImapRequestLineReader;
 import 
org.apache.james.imap.decode.ImapRequestLineReader.StringMatcherCharacterValidator;
 import org.apache.james.imap.decode.base.AbstractImapCommandParser;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.mailbox.MessageUid;
-import org.apache.james.mailbox.model.UidValidity;
 
 public abstract class AbstractSelectionCommandParser extends 
AbstractImapCommandParser {
     private static final String CONDSTORE = 
ImapConstants.SUPPORTS_CONDSTORE.asString();
@@ -47,7 +47,7 @@ public abstract class AbstractSelectionCommandParser extends 
AbstractImapCommand
     protected ImapMessage decode(ImapRequestLineReader request, Tag tag, 
ImapSession session) throws DecodingException {
         final String mailboxName = request.mailbox();
         boolean condstore = false;
-        UidValidity lastKnownUidValidity = null;
+        ClientSpecifiedUidValidity lastKnownUidValidity = 
ClientSpecifiedUidValidity.UNKNOWN;
         Long knownModSeq = null;
         UidRange[] uidSet = null;
         UidRange[] knownUidSet = null;
@@ -81,7 +81,7 @@ public abstract class AbstractSelectionCommandParser extends 
AbstractImapCommand
                 // Consume enclosing paren
                 request.consumeChar('(');
                 long uidValidityAsNumber = request.number();
-                lastKnownUidValidity = 
sanitizeUidValidity(uidValidityAsNumber);
+                lastKnownUidValidity = 
ClientSpecifiedUidValidity.of(uidValidityAsNumber);
 
                 // Consume the SP
                 request.consumeChar(' ');
@@ -131,18 +131,6 @@ public abstract class AbstractSelectionCommandParser 
extends AbstractImapCommand
         return createRequest(mailboxName, condstore, lastKnownUidValidity, 
knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 
-    private UidValidity sanitizeUidValidity(long uidValidityAsNumber) {
-        if (UidValidity.isValid(uidValidityAsNumber)) {
-            return UidValidity.of(uidValidityAsNumber);
-        } else {
-            // The UidValidity cached by the client is invalid
-            // We know that the backend will regenerate it
-            // Hence we force the mismatch
-            // QRSYNC command will be ignored
-            return UidValidity.random();
-        }
-    }
-
     /**
      * Check if the {@link IdRange}'s are formatted like stated in the QRESYNC 
RFC.
      * 
@@ -220,5 +208,5 @@ public abstract class AbstractSelectionCommandParser 
extends AbstractImapCommand
     /**
      * Create a new {@link AbstractMailboxSelectionRequest} for the given 
arguments
      */
-    protected abstract AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long 
knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] 
knownSequenceSet, Tag tag);
+    protected abstract AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, ClientSpecifiedUidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag);
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
index b2bef7e..e711e9d 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/ExamineCommandParser.java
@@ -24,8 +24,8 @@ import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.ExamineRequest;
-import org.apache.james.mailbox.model.UidValidity;
 
 /**
  * Parse EXAMINE commands
@@ -37,7 +37,7 @@ public class ExamineCommandParser extends 
AbstractSelectionCommandParser {
     }
 
     @Override
-    protected AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long 
knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] 
knownSequenceSet, Tag tag) {
+    protected AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, ClientSpecifiedUidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         return new ExamineRequest(mailboxName, condstore, 
lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
index dd30d44..c9ad85a 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SelectCommandParser.java
@@ -24,8 +24,8 @@ import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.SelectRequest;
-import org.apache.james.mailbox.model.UidValidity;
 
 /**
  * Parse SELECT commands
@@ -36,7 +36,7 @@ public class SelectCommandParser extends 
AbstractSelectionCommandParser {
     }
 
     @Override
-    protected AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long 
knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] 
knownSequenceSet, Tag tag) {
+    protected AbstractMailboxSelectionRequest createRequest(String 
mailboxName, boolean condstore, ClientSpecifiedUidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         return new SelectRequest(mailboxName, condstore, lastKnownUidValidity, 
knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
index 468e2b0..bfdb30d 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequest.java
@@ -19,6 +19,8 @@
 
 package org.apache.james.imap.message.request;
 
+import java.util.Objects;
+
 import org.apache.james.imap.api.ImapCommand;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
@@ -26,29 +28,144 @@ import org.apache.james.imap.api.message.UidRange;
 import org.apache.james.imap.api.message.request.ImapRequest;
 import org.apache.james.mailbox.model.UidValidity;
 
+import com.google.common.base.Preconditions;
+
 /**
  * {@link ImapRequest} which selects a Mailbox. 
  * 
  * This supports also the <code>CONDSTORE</code> and the <code>QRESYNC</code> 
extension
  */
 public abstract class AbstractMailboxSelectionRequest extends 
AbstractImapRequest {
+    public interface ClientSpecifiedUidValidity {
+        ClientSpecifiedUidValidity UNKNOWN = new ClientSpecifiedUidValidity() {
+            @Override
+            public boolean isUnknown() {
+                return true;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return false;
+            }
+
+            @Override
+            public String toString() {
+                return "UidValidity{UNKNOWN}";
+            }
+        };
+
+        static ClientSpecifiedUidValidity of(long value) {
+            if (UidValidity.isValid(value)) {
+                return valid(UidValidity.of(value));
+            }
+            return invalid(value);
+        }
+
+        class Invalid implements ClientSpecifiedUidValidity {
+            private final long invalidUidValidity;
+
+            public Invalid(long invalidUidValidity) {
+                
Preconditions.checkArgument(!UidValidity.isValid(invalidUidValidity), "Need to 
supply an invalid value");
+                this.invalidUidValidity = invalidUidValidity;
+            }
+
+            @Override
+            public boolean isUnknown() {
+                return false;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return false;
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Invalid) {
+                    Invalid invalid = (Invalid) o;
+
+                    return Objects.equals(this.invalidUidValidity, 
invalid.invalidUidValidity);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(invalidUidValidity);
+            }
+
+            @Override
+            public String toString() {
+                return String.format("Invalid UidValidity{%d}", 
invalidUidValidity);
+            }
+        }
 
+        class Valid implements ClientSpecifiedUidValidity {
+            private final UidValidity uidValidity;
+
+            public Valid(UidValidity uidValidity) {
+                this.uidValidity = uidValidity;
+            }
+
+            @Override
+            public boolean isUnknown() {
+                return false;
+            }
+
+            @Override
+            public boolean correspondsTo(UidValidity uidValidity) {
+                return this.uidValidity.equals(uidValidity);
+            }
+
+            @Override
+            public final boolean equals(Object o) {
+                if (o instanceof Valid) {
+                    Valid valid = (Valid) o;
+
+                    return Objects.equals(this.uidValidity, valid.uidValidity);
+                }
+                return false;
+            }
+
+            @Override
+            public final int hashCode() {
+                return Objects.hash(uidValidity);
+            }
+
+            @Override
+            public String toString() {
+                return String.format("UidValidity{%d}", uidValidity.asLong());
+            }
+        }
+
+        static ClientSpecifiedUidValidity invalid(long invalidValue) {
+            return new Invalid(invalidValue);
+        }
+
+        static ClientSpecifiedUidValidity valid(UidValidity uidValidity) {
+            return new Valid(uidValidity);
+        }
+
+        boolean isUnknown();
+
+        boolean correspondsTo(UidValidity uidValidity);
+    }
 
     private final String mailboxName;
     private final boolean condstore;
-    private final UidValidity lastKnownUidValidity;
+    private final ClientSpecifiedUidValidity lastKnownUidValidity;
     private final Long knownModSeq;
     private final UidRange[] uidSet;
     private final UidRange[] knownUidSet;
     private final IdRange[] knownSequenceSet;
 
-    public AbstractMailboxSelectionRequest(ImapCommand command, String 
mailboxName, boolean condstore, UidValidity lastKnownUidValidity, Long 
knownModSeq, UidRange[] uidSet, UidRange[] knownUidSet, IdRange[] 
knownSequenceSet, Tag tag) {
+    public AbstractMailboxSelectionRequest(ImapCommand command, String 
mailboxName, boolean condstore, ClientSpecifiedUidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(tag, command);
         this.mailboxName = mailboxName;
         this.condstore = condstore;
         this.lastKnownUidValidity = lastKnownUidValidity;
         this.knownModSeq = knownModSeq;
-        if ((lastKnownUidValidity == null && knownModSeq != null) || 
(this.lastKnownUidValidity != null && knownModSeq == null)) {
+        if ((lastKnownUidValidity.isUnknown() && knownModSeq != null) || (! 
lastKnownUidValidity.isUnknown() && knownModSeq == null)) {
             throw new IllegalArgumentException();
         }
         this.uidSet = uidSet;
@@ -80,7 +197,7 @@ public abstract class AbstractMailboxSelectionRequest 
extends AbstractImapReques
      * 
      * @return lastKnownUidValidity
      */
-    public final UidValidity getLastKnownUidValidity() {
+    public final ClientSpecifiedUidValidity getLastKnownUidValidity() {
         return lastKnownUidValidity;
     }
     
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
index a3989e8..208c0d9 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/ExamineRequest.java
@@ -22,10 +22,9 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
-import org.apache.james.mailbox.model.UidValidity;
 
 public class ExamineRequest extends AbstractMailboxSelectionRequest {
-    public ExamineRequest(String mailboxName, boolean condstore, UidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    public ExamineRequest(String mailboxName, boolean condstore, 
ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] 
uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.EXAMINE_COMMAND, mailboxName, condstore, 
lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
index b8bf1a3..d7b98ad 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/message/request/SelectRequest.java
@@ -22,10 +22,9 @@ import org.apache.james.imap.api.ImapConstants;
 import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
-import org.apache.james.mailbox.model.UidValidity;
 
 public class SelectRequest extends AbstractMailboxSelectionRequest {
-    public SelectRequest(String mailboxName, boolean condstore, UidValidity 
lastKnownUidValidity, Long knownModSeq, UidRange[] uidSet, UidRange[] 
knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
+    public SelectRequest(String mailboxName, boolean condstore, 
ClientSpecifiedUidValidity lastKnownUidValidity, Long knownModSeq, UidRange[] 
uidSet, UidRange[] knownUidSet, IdRange[] knownSequenceSet, Tag tag) {
         super(ImapConstants.SELECT_COMMAND, mailboxName, condstore, 
lastKnownUidValidity, knownModSeq, uidSet, knownUidSet, knownSequenceSet, tag);
     }
 }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
index e243fdf..11edf91 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractSelectionProcessor.java
@@ -38,6 +38,7 @@ import org.apache.james.imap.api.process.SearchResUtil;
 import org.apache.james.imap.api.process.SelectedMailbox;
 import org.apache.james.imap.main.PathConverter;
 import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.response.ExistsResponse;
 import org.apache.james.imap.message.response.RecentResponse;
 import org.apache.james.imap.processor.base.SelectedMailboxImpl;
@@ -98,7 +99,7 @@ abstract class AbstractSelectionProcessor<R extends 
AbstractMailboxSelectionRequ
 
     private void respond(ImapSession session, MailboxPath fullMailboxPath, 
AbstractMailboxSelectionRequest request, Responder responder) throws 
MailboxException, MessageRangeException {
 
-        UidValidity lastKnownUidValidity = request.getLastKnownUidValidity();
+        ClientSpecifiedUidValidity lastKnownUidValidity = 
request.getLastKnownUidValidity();
         Long modSeq = request.getKnownModSeq();
         IdRange[] knownSequences = request.getKnownSequenceSet();
         UidRange[] knownUids = request.getKnownUidSet();
@@ -112,7 +113,7 @@ abstract class AbstractSelectionProcessor<R extends 
AbstractMailboxSelectionRequ
         //    Resynchronization parameter to SELECT/EXAMINE command is 
specified
         //    and the client hasn't issued "ENABLE QRESYNC" in the current
         //    connection.
-        if (lastKnownUidValidity != null && 
!EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC))
 {
+        if (!lastKnownUidValidity.isUnknown() && 
!EnableProcessor.getEnabledCapabilities(session).contains(ImapConstants.SUPPORTS_QRESYNC))
 {
             taggedBad(request, responder, 
HumanReadableText.QRESYNC_NOT_ENABLED);
             return;
         }
@@ -154,8 +155,8 @@ abstract class AbstractSelectionProcessor<R extends 
AbstractMailboxSelectionRequ
         // 
         // If the mailbox does not store the mod-sequence in a permanent way 
its needed to not process the QRESYNC paramters
         // The same is true if none are given ;)
-        if (metaData.isModSeqPermanent() && lastKnownUidValidity != null) {
-            if (lastKnownUidValidity == metaData.getUidValidity()) {
+        if (metaData.isModSeqPermanent() && !lastKnownUidValidity.isUnknown()) 
{
+            if (lastKnownUidValidity.correspondsTo(metaData.getUidValidity())) 
{
                 
                 final MailboxManager mailboxManager = getMailboxManager();
                 final MailboxSession mailboxSession = 
session.getMailboxSession();
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
index 0468251..5486524 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/ExamineProcessor.java
@@ -20,7 +20,6 @@
 package org.apache.james.imap.processor;
 
 import java.io.Closeable;
-import java.util.Optional;
 
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
@@ -29,7 +28,6 @@ import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.message.request.ExamineRequest;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.events.EventBus;
-import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
 
@@ -49,8 +47,7 @@ public class ExamineProcessor extends 
AbstractSelectionProcessor<ExamineRequest>
             .addContext("knownModseq", request.getKnownModSeq())
             .addContext("knownUids", 
UidRange.toString(request.getKnownUidSet()))
             .addContext("knownIdRange", 
IdRange.toString(request.getKnownSequenceSet()))
-            .addContext("lastKnownUidValidity", 
Optional.ofNullable(request.getLastKnownUidValidity())
-                .map(UidValidity::asLong))
+            .addContext("lastKnownUidValidity", 
request.getLastKnownUidValidity())
             .addContext("uidSet", UidRange.toString(request.getUidSet()))
             .build();
     }
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
index 12a67aa..6d9bb18 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/SelectProcessor.java
@@ -20,7 +20,6 @@
 package org.apache.james.imap.processor;
 
 import java.io.Closeable;
-import java.util.Optional;
 
 import org.apache.james.imap.api.message.IdRange;
 import org.apache.james.imap.api.message.UidRange;
@@ -29,7 +28,6 @@ import org.apache.james.imap.api.process.ImapProcessor;
 import org.apache.james.imap.message.request.SelectRequest;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.events.EventBus;
-import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.metrics.api.MetricFactory;
 import org.apache.james.util.MDCBuilder;
 
@@ -49,8 +47,7 @@ public class SelectProcessor extends 
AbstractSelectionProcessor<SelectRequest> {
             .addContext("knownModseq", message.getKnownModSeq())
             .addContext("knownUids", 
UidRange.toString(message.getKnownUidSet()))
             .addContext("knownIdRange", 
IdRange.toString(message.getKnownSequenceSet()))
-            .addContext("lastKnownUidValidity", 
Optional.ofNullable(message.getLastKnownUidValidity())
-                .map(UidValidity::asLong))
+            .addContext("lastKnownUidValidity", 
message.getLastKnownUidValidity())
             .addContext("uidSet", UidRange.toString(message.getUidSet()))
             .build();
     }
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
index 683385b..6d73240 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/decode/parser/SelectCommandParserTest.java
@@ -30,7 +30,10 @@ import org.apache.james.imap.api.Tag;
 import org.apache.james.imap.api.message.response.StatusResponseFactory;
 import org.apache.james.imap.api.process.ImapSession;
 import org.apache.james.imap.decode.ImapRequestStreamLineReader;
+import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest;
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
 import org.apache.james.imap.message.request.SelectRequest;
+import org.apache.james.mailbox.model.UidValidity;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 
@@ -48,8 +51,18 @@ class SelectCommandParserTest {
 
         SelectRequest selectRequest = (SelectRequest) testee.decode(request, 
new Tag("0001"), mock(ImapSession.class));
 
-        assertThat(selectRequest.getLastKnownUidValidity().isValid())
-            .isTrue();
+        assertThat(selectRequest.getLastKnownUidValidity())
+            .isEqualTo(ClientSpecifiedUidValidity.invalid(0));
+    }
+
+    @Test
+    void decodeShouldParseValidUidValidity() throws Exception {
+        ImapRequestStreamLineReader request = toRequest("mailbox (QRESYNC (18 
42))\n");
+
+        SelectRequest selectRequest = (SelectRequest) testee.decode(request, 
new Tag("0001"), mock(ImapSession.class));
+
+        assertThat(selectRequest.getLastKnownUidValidity())
+            .isEqualTo(ClientSpecifiedUidValidity.valid(UidValidity.of(18)));
     }
 
     private ImapRequestStreamLineReader toRequest(String input) {
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
new file mode 100644
index 0000000..8e50036
--- /dev/null
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/message/request/AbstractMailboxSelectionRequestTest.java
@@ -0,0 +1,123 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.imap.message.request;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import 
org.apache.james.imap.message.request.AbstractMailboxSelectionRequest.ClientSpecifiedUidValidity;
+import org.apache.james.mailbox.model.UidValidity;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Nested;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+class AbstractMailboxSelectionRequestTest {
+    private static final ClientSpecifiedUidValidity INVALID = 
ClientSpecifiedUidValidity.invalid(0);
+    private static final UidValidity UID_VALIDITY = UidValidity.of(42);
+    private static final ClientSpecifiedUidValidity VALID = 
ClientSpecifiedUidValidity.valid(UID_VALIDITY);
+
+    @Nested
+    class Unknown {
+        @Test
+        void isUnknownShouldReturnTrue() {
+            assertThat(ClientSpecifiedUidValidity.UNKNOWN.isUnknown())
+                .isTrue();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalse() {
+            
assertThat(ClientSpecifiedUidValidity.UNKNOWN.correspondsTo(UID_VALIDITY))
+                .isFalse();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(ClientSpecifiedUidValidity.UNKNOWN.toString())
+                .isEqualTo("UidValidity{UNKNOWN}");
+        }
+    }
+
+    @Nested
+    class Invalid {
+        @Test
+        void shouldMatchBeanContract() {
+            EqualsVerifier.forClass(ClientSpecifiedUidValidity.Invalid.class)
+                .verify();
+        }
+
+        @Test
+        void invalidShouldThrowOnValidValue() {
+            assertThatThrownBy(() -> ClientSpecifiedUidValidity.invalid(42))
+                .isInstanceOf(IllegalArgumentException.class);
+        }
+
+        @Test
+        void isUnknownShouldReturnFalse() {
+            assertThat(INVALID.isUnknown())
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalse() {
+            assertThat(INVALID.correspondsTo(UID_VALIDITY))
+                .isFalse();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(INVALID.toString())
+                .isEqualTo("Invalid UidValidity{0}");
+        }
+    }
+
+    @Nested
+    class Valid {
+        @Test
+        void shouldMatchBeanContract() {
+            EqualsVerifier.forClass(ClientSpecifiedUidValidity.Valid.class)
+                .verify();
+        }
+
+        @Test
+        void isUnknownShouldReturnFalse() {
+            assertThat(VALID.isUnknown())
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnFalseWhenDifferent() {
+            assertThat(VALID.correspondsTo(UidValidity.of(40)))
+                .isFalse();
+        }
+
+        @Test
+        void correspondsToShouldReturnTrueWhenSame() {
+            assertThat(VALID.correspondsTo(UID_VALIDITY))
+                .isTrue();
+        }
+
+        @Test
+        void toStringShouldBeInformative() {
+            assertThat(VALID.toString())
+                .isEqualTo("UidValidity{42}");
+        }
+    }
+}
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to