You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/11/22 07:11:48 UTC

[james-project] 03/12: JAMES-3858 Add FETCH item for EMAILID and THREADID

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 6a4fa30410c5fcb1a45711e6aed70eb3b0a706be
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Thu Nov 17 11:02:14 2022 +0700

    JAMES-3858 Add FETCH item for EMAILID and THREADID
---
 .../org/apache/james/imap/api/ImapConstants.java   |  2 ++
 .../apache/james/imap/api/message/FetchData.java   |  3 ++
 .../imap/decode/parser/FetchCommandParser.java     |  6 ++++
 .../james/imap/encode/FetchResponseEncoder.java    | 25 +++++++++++++++-
 .../james/imap/message/response/FetchResponse.java | 16 ++++++++++-
 .../imap/processor/AbstractMailboxProcessor.java   |  4 +--
 .../james/imap/processor/StoreProcessor.java       |  6 ++--
 .../imap/processor/fetch/FetchResponseBuilder.java | 33 +++++++++++++++++++++-
 .../encode/FetchResponseEncoderEnvelopeTest.java   |  2 +-
 .../FetchResponseEncoderNoExtensionsTest.java      |  8 +++---
 .../imap/encode/FetchResponseEncoderTest.java      |  6 ++--
 11 files changed, 95 insertions(+), 16 deletions(-)

diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
index fb90dc3359..211c4c9d1d 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/ImapConstants.java
@@ -253,4 +253,6 @@ public interface ImapConstants {
     String STORAGE_QUOTA_RESOURCE = "STORAGE";
 
     String MESSAGE_QUOTA_RESOURCE = "MESSAGE";
+    String EMAILID = "EMAILID";
+    String THREADID = "THREADID";
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/FetchData.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/FetchData.java
index 45c93bc920..50cf6159b2 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/FetchData.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/FetchData.java
@@ -102,6 +102,9 @@ public class FetchData {
         BODY,
         BODY_STRUCTURE,
         MODSEQ,
+        // https://www.rfc-editor.org/rfc/rfc8474.html#section-5.3
+        EMAILID,
+        THREADID
     }
 
     public static Builder builder() {
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/FetchCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/FetchCommandParser.java
index 84eed8c212..3f11db1465 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/FetchCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/FetchCommandParser.java
@@ -20,11 +20,13 @@ package org.apache.james.imap.decode.parser;
 
 import static org.apache.james.imap.api.message.FetchData.Item.BODY;
 import static org.apache.james.imap.api.message.FetchData.Item.BODY_STRUCTURE;
+import static org.apache.james.imap.api.message.FetchData.Item.EMAILID;
 import static org.apache.james.imap.api.message.FetchData.Item.ENVELOPE;
 import static org.apache.james.imap.api.message.FetchData.Item.FLAGS;
 import static org.apache.james.imap.api.message.FetchData.Item.INTERNAL_DATE;
 import static org.apache.james.imap.api.message.FetchData.Item.MODSEQ;
 import static org.apache.james.imap.api.message.FetchData.Item.SIZE;
+import static org.apache.james.imap.api.message.FetchData.Item.THREADID;
 import static org.apache.james.imap.api.message.FetchData.Item.UID;
 
 import java.util.List;
@@ -185,6 +187,10 @@ public class FetchCommandParser extends AbstractUidCommandParser {
                 return fetch.add(BodyFetchElement.createRFC822Text(), false);
             case "MODSEQ":
                 return fetch.fetch(MODSEQ);
+            case "EMAILID":
+                return fetch.fetch(EMAILID);
+            case "THREADID":
+                return fetch.fetch(THREADID);
             default:
                 throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Invalid fetch attribute: " + name);
         }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
index 80ce91485e..c67980610c 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/FetchResponseEncoder.java
@@ -37,6 +37,8 @@ import org.apache.james.imap.message.response.FetchResponse.Structure;
 import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -81,7 +83,8 @@ public class FetchResponseEncoder implements ImapResponseEncoder<FetchResponse>
         encodeBodyStructure(composer, fetchResponse.getBodyStructure());
         encodeUid(composer, fetchResponse);
         encodeBodyElements(composer, fetchResponse.getElements());
-
+        encodeEmailId(composer, fetchResponse);
+        encodeThreadId(composer, fetchResponse);
         composer.closeParen().end();
     }
 
@@ -295,6 +298,26 @@ public class FetchResponseEncoder implements ImapResponseEncoder<FetchResponse>
         }
     }
 
+    private void encodeEmailId(ImapResponseComposer composer, FetchResponse fetchResponse) throws IOException {
+        final MessageId emailId = fetchResponse.getEmailId();
+        if (emailId != null) {
+            composer.message(ImapConstants.EMAILID);
+            composer.openParen();
+            composer.message(emailId.serialize());
+            composer.closeParen();
+        }
+    }
+    private void encodeThreadId(ImapResponseComposer composer, FetchResponse fetchResponse) throws IOException {
+        final ThreadId threadId = fetchResponse.getThreadId();
+        if (threadId != null) {
+            composer.message(ImapConstants.THREADID);
+            composer.openParen();
+            composer.message(threadId.serialize());
+            composer.closeParen();
+        }
+    }
+
+
     private void encodeFlags(ImapResponseComposer composer, FetchResponse fetchResponse) throws IOException {
         final Flags flags = fetchResponse.getFlags();
         if (flags != null) {
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
index b909af977a..3a66268fde 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/FetchResponse.java
@@ -30,6 +30,8 @@ import org.apache.james.imap.message.Literal;
 import org.apache.james.mailbox.MessageSequenceNumber;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
 
 public final class FetchResponse implements ImapResponseMessage {
     private final MessageSequenceNumber messageNumber;
@@ -42,8 +44,10 @@ public final class FetchResponse implements ImapResponseMessage {
     private final Structure body;
     private final Structure bodystructure;
     private final ModSeq modSeq;
+    private final MessageId emailId;
+    private final ThreadId threadId;
 
-    public FetchResponse(MessageSequenceNumber messageNumber, Flags flags, MessageUid uid, ModSeq modSeq, Date internalDate, Long size, Envelope envelope, Structure body, Structure bodystructure, List<BodyElement> elements) {
+    public FetchResponse(MessageSequenceNumber messageNumber, Flags flags, MessageUid uid, ModSeq modSeq, Date internalDate, Long size, Envelope envelope, Structure body, Structure bodystructure, List<BodyElement> elements, MessageId emailId, ThreadId threadId) {
         this.messageNumber = messageNumber;
         this.flags = flags;
         this.uid = uid;
@@ -54,6 +58,8 @@ public final class FetchResponse implements ImapResponseMessage {
         this.body = body;
         this.bodystructure = bodystructure;
         this.modSeq = modSeq;
+        this.emailId = emailId;
+        this.threadId = threadId;
     }
 
     /**
@@ -155,6 +161,14 @@ public final class FetchResponse implements ImapResponseMessage {
         return modSeq;
     }
 
+    public MessageId getEmailId() {
+        return emailId;
+    }
+
+    public ThreadId getThreadId() {
+        return threadId;
+    }
+
     /**
      * Describes the message structure.
      */
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
index 811383a093..5c4be73ce8 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMailboxProcessor.java
@@ -265,9 +265,9 @@ public abstract class AbstractMailboxProcessor<R extends ImapRequest> extends Ab
                     // Check if we also need to return the MODSEQ in the response. This is true if CONDSTORE or
                     // if QRESYNC was enabled, and the mailbox supports the permant storage of mod-sequences
                     if (condstoreEnabled || qresyncEnabled) {
-                        response = new FetchResponse(msn, flags, uidOut, mr.getModSeq(), null, null, null, null, null, null);
+                        response = new FetchResponse(msn, flags, uidOut, mr.getModSeq(), null, null, null, null, null, null, null, null);
                     } else {
-                        response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null, null, null);
+                        response = new FetchResponse(msn, flags, uidOut, null, null, null, null, null, null, null, null, null);
                     }
                     responder.respond(response);
                     return null;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
index 0c97d83816..00cb177af5 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/StoreProcessor.java
@@ -312,14 +312,14 @@ public class StoreProcessor extends AbstractMailboxProcessor<StoreRequest> {
         if (unchangedSince != -1 || qresyncEnabled || condstoreEnabled) {
             if (silent) {
                 // We need to return an FETCH response which contains the mod-sequence of the message even if FLAGS.SILENT was used
-                return new FetchResponse(msn, null, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
+                return new FetchResponse(msn, null, resultUid, modSeqs.get(uid), null, null, null, null, null, null, null, null);
             } else {
                 // Use a FETCH response which contains the mod-sequence and the flags
-                return new FetchResponse(msn, resultFlags, resultUid, modSeqs.get(uid), null, null, null, null, null, null);
+                return new FetchResponse(msn, resultFlags, resultUid, modSeqs.get(uid), null, null, null, null, null, null, null, null);
             }
         } else {
             // Use a FETCH response which only contains the flags as no CONDSTORE was used
-            return new FetchResponse(msn, resultFlags, resultUid, null, null, null, null, null, null, null);
+            return new FetchResponse(msn, resultFlags, resultUid, null, null, null, null, null, null, null, null, null);
         }
     }
 
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
index 9b78f318d9..a33c2421bc 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/fetch/FetchResponseBuilder.java
@@ -48,9 +48,11 @@ import org.apache.james.mailbox.exception.MessageRangeException;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.Content;
 import org.apache.james.mailbox.model.Header;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
 import org.apache.james.mailbox.model.MessageResult;
 import org.apache.james.mailbox.model.MimePath;
+import org.apache.james.mailbox.model.ThreadId;
 
 import reactor.core.publisher.Mono;
 
@@ -58,6 +60,8 @@ public final class FetchResponseBuilder {
     private final EnvelopeBuilder envelopeBuilder;
 
     private MessageSequenceNumber msn;
+    private MessageId messageId;
+    private ThreadId threadId;
     private MessageUid uid;
     private Flags flags;
     private Date internalDate;
@@ -74,6 +78,8 @@ public final class FetchResponseBuilder {
 
     public void reset(MessageSequenceNumber msn) {
         this.msn = msn;
+        messageId = null;
+        threadId = null;
         uid = null;
         flags = null;
         internalDate = null;
@@ -84,6 +90,14 @@ public final class FetchResponseBuilder {
         modSeq = null;
     }
 
+    public void setMessageId(MessageId messageId) {
+        this.messageId = messageId;
+    }
+
+    public void setThreadId(ThreadId threadId) {
+        this.threadId = threadId;
+    }
+
     public void setUid(MessageUid resultUid) {
         this.uid = resultUid;
     }
@@ -98,7 +112,7 @@ public final class FetchResponseBuilder {
     }
 
     public FetchResponse build() {
-        return new FetchResponse(msn, flags, uid, modSeq, internalDate, size, envelope, body, bodystructure, elements);
+        return new FetchResponse(msn, flags, uid, modSeq, internalDate, size, envelope, body, bodystructure, elements, messageId, threadId);
     }
 
     public Mono<FetchResponse> build(FetchData fetch, MessageResult result, MessageManager mailbox, SelectedMailbox selectedMailbox, MailboxSession mailboxSession) throws MessageRangeException, MailboxException {
@@ -152,6 +166,9 @@ public final class FetchResponseBuilder {
 
             addUid(fetch, resultUid);
 
+            addThreadId(fetch, result.getThreadId());
+            addMessageId(fetch, result.getMessageId());
+
             addModSeq(fetch, result.getModSeq());
 
             // FLAGS response
@@ -170,6 +187,20 @@ public final class FetchResponseBuilder {
         }
     }
 
+    private void addMessageId(FetchData fetch, MessageId messageId) {
+        // EMAILID response
+        if (fetch.contains(Item.EMAILID)) {
+            setMessageId(messageId);
+        }
+    }
+
+    private void addThreadId(FetchData fetch, ThreadId threadId) {
+        // THREADID response
+        if (fetch.contains(Item.THREADID)) {
+            setThreadId(threadId);
+        }
+    }
+
     private void addModSeq(FetchData fetch, ModSeq modSeq) {
         if (fetch.contains(Item.MODSEQ)) {
             long changedSince = fetch.getChangedSince();
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
index 9244671d74..e42dadd105 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderEnvelopeTest.java
@@ -97,7 +97,7 @@ public class FetchResponseEncoderEnvelopeTest {
         subject = null;
         to = null;
 
-        message = new FetchResponse(MSN, null, null, null, null, null, envelope, null, null, null);
+        message = new FetchResponse(MSN, null, null, null, null, null, envelope, null, null, null, null, null);
         encoder = new FetchResponseEncoder(false);
     }
 
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
index 4e0a4fc752..0bd013a916 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderNoExtensionsTest.java
@@ -58,7 +58,7 @@ class FetchResponseEncoderNoExtensionsTest {
     @Test
     void testShouldEncodeFlagsResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, flags, null, null, null, null,
-                null, null, null, null);
+                null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted))\r\n");
     }
@@ -66,7 +66,7 @@ class FetchResponseEncoderNoExtensionsTest {
     @Test
     void testShouldEncodeUidResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, null, MessageUid.of(72), null,
-                null, null, null, null, null, null);
+                null, null, null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n");
 
@@ -75,7 +75,7 @@ class FetchResponseEncoderNoExtensionsTest {
     @Test
     void testShouldEncodeAllResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null,
-                null, null, null, null, null, null);
+                null, null, null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted) UID 72)\r\n");
 
@@ -84,7 +84,7 @@ class FetchResponseEncoderNoExtensionsTest {
     @Test
     void testShouldNotAddExtensionsWithEncodingBodyStructure() throws Exception {
         FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null,
-                null, null, null, null, stubStructure, null);
+                null, null, null, null, stubStructure, null, null, null);
         final Map<String, String> parameters = new HashMap<>();
         parameters.put("CHARSET", "US-ASCII");
         final List<String> parameterList = new ArrayList<>();
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
index 7c803cb274..395ebb44ad 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/encode/FetchResponseEncoderTest.java
@@ -52,7 +52,7 @@ class FetchResponseEncoderTest  {
     @Test
     void testShouldEncodeFlagsResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, flags, null, null, null, null,
-                null, null, null, null);
+                null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted))\r\n");
 
@@ -62,7 +62,7 @@ class FetchResponseEncoderTest  {
     @Test
     void testShouldEncodeUidResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, null, MessageUid.of(72), null,
-                null, null, null, null, null, null); 
+                null, null, null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (UID 72)\r\n");
 
@@ -72,7 +72,7 @@ class FetchResponseEncoderTest  {
     @Test
     void testShouldEncodeAllResponse() throws Exception {
         FetchResponse message = new FetchResponse(MSN, flags, MessageUid.of(72), null,
-                null, null, null, null, null, null);
+                null, null, null, null, null, null, null, null);
         encoder.encode(message, composer);
         assertThat(writer.getString()).isEqualTo("* 100 FETCH (FLAGS (\\Deleted) UID 72)\r\n");
         


---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org