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/12/28 02:05:31 UTC

[james-project] 02/04: JAMES-3754 IMAP SEARCH for SaveDate extension

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 4cf94690e9731dbadc34afd5794fd4883ba3ca37
Author: Quan Tran <hq...@linagora.com>
AuthorDate: Thu Dec 8 13:35:46 2022 +0700

    JAMES-3754 IMAP SEARCH for SaveDate extension
---
 .../james/mpt/imapmailbox/suite/SelectedState.java |  6 ++
 .../apache/james/imap/scripts/SearchSaveDate.test  | 53 ++++++++++++++++++
 .../mpt/imapmailbox/jpa/JpaSelectedStateTest.java  |  6 ++
 .../james/imap/api/message/request/SearchKey.java  | 28 +++++++++-
 .../imap/decode/parser/SearchCommandParser.java    | 65 ++++++++++++++++++++++
 .../james/imap/processor/SearchProcessor.java      |  8 +++
 .../james/imap/processor/SearchProcessorTest.java  | 25 +++++++++
 7 files changed, 189 insertions(+), 2 deletions(-)

diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/SelectedState.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/SelectedState.java
index f699880cd7..9b49406980 100644
--- a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/SelectedState.java
+++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/SelectedState.java
@@ -255,4 +255,10 @@ public abstract class SelectedState implements ImapTestConstants {
             .withLocale(Locale.KOREA)
             .run("Namespace");
     }
+
+    @Test
+    public void testSearchSaveDate() throws Exception {
+        simpleScriptedTestProtocol
+            .run("SearchSaveDate");
+    }
 }
diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SearchSaveDate.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SearchSaveDate.test
new file mode 100644
index 0000000000..f14e857772
--- /dev/null
+++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SearchSaveDate.test
@@ -0,0 +1,53 @@
+################################################################
+# 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.                                           #
+################################################################
+C: f CREATE anothermailbox
+S: f OK \[MAILBOXID \(.+\)\] CREATE completed\.
+C: g APPEND anothermailbox {704+}
+C: Received: by 10.114.81.13 with HTTP; Sat, 2 Feb 2008 05:14:19 -0800 (PST)
+C: Message-ID: <f4...@mail.gmail.com>
+C: Date: Sat, 2 Feb 2008 13:14:19 +0000
+C: From: "Robert Burrell Donkin" <ro...@gmail.com>
+C: To: "James Developers List" <se...@james.apache.org>
+C: Subject: JCR -> trunk ...?
+C: MIME-Version: 1.0
+C: Content-Type: text/plain; charset=ISO-8859-1
+C: Content-Transfer-Encoding: 7bit
+C: Content-Disposition: inline
+C: Delivered-To: robertburrelldonkin@gmail.com
+C: 
+C: i'd like to copy james-jcr into trunk and add some example
+C: configurations. development can continue in the sandbox (or not) and
+C: merged in later (if necessary).
+C: 
+C: any objections?
+C: 
+C: - robert
+S: g OK (\[.+\] )?APPEND completed\.
+C: f SEARCH SAVEDON 28-Dec-2014
+S: \* SEARCH
+S: f OK SEARCH completed.
+C: g SEARCH SAVEDBEFORE 28-Dec-2014
+S: \* SEARCH
+S: g OK SEARCH completed.
+C: g SEARCH SAVEDSINCE 28-Dec-2014
+S: \* SEARCH 1 2 3 4
+S: g OK SEARCH completed.
+C: g SEARCH SAVEDATESUPPORTED
+S: \* SEARCH 1 2 3 4
+S: g OK SEARCH completed.
\ No newline at end of file
diff --git a/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/JpaSelectedStateTest.java b/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/JpaSelectedStateTest.java
index 6cdd380fb9..099c3d3d4d 100644
--- a/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/JpaSelectedStateTest.java
+++ b/mpt/impl/imap-mailbox/jpa/src/test/java/org/apache/james/mpt/imapmailbox/jpa/JpaSelectedStateTest.java
@@ -22,6 +22,7 @@ package org.apache.james.mpt.imapmailbox.jpa;
 import org.apache.james.mpt.api.ImapHostSystem;
 import org.apache.james.mpt.imapmailbox.jpa.host.JPAHostSystemExtension;
 import org.apache.james.mpt.imapmailbox.suite.SelectedState;
+import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 public class JpaSelectedStateTest extends SelectedState {
@@ -56,4 +57,9 @@ public class JpaSelectedStateTest extends SelectedState {
     @Override
     public void testUidUS() {
     }
+
+    @Override
+    @Disabled("SEARCH save date just return empty result for JPA")
+    public void testSearchSaveDate() {
+    }
 }
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchKey.java b/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchKey.java
index c052cca700..6da831c86f 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchKey.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/api/message/request/SearchKey.java
@@ -42,6 +42,10 @@ import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_OLDE
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_ON;
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_OR;
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_RECENT;
+import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SAVEDATESUPPORTED;
+import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SAVEDBEFORE;
+import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SAVEDON;
+import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SAVEDSINCE;
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SEEN;
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SENTBEFORE;
 import static org.apache.james.imap.api.message.request.SearchKey.Type.TYPE_SENTON;
@@ -121,7 +125,11 @@ public final class SearchKey {
         TYPE_OLDER,
         TYPE_MODSEQ,
         TYPE_THREADID,
-        TYPE_EMAILID
+        TYPE_EMAILID,
+        TYPE_SAVEDBEFORE,
+        TYPE_SAVEDON,
+        TYPE_SAVEDSINCE,
+        TYPE_SAVEDATESUPPORTED
     }
 
     private static final SearchKey UNSEEN = new SearchKey(TYPE_UNSEEN, null, null, 0, null, null, null, null, -1, -1, null, null);
@@ -282,10 +290,18 @@ public final class SearchKey {
         return new SearchKey(TYPE_ON, date, null, 0, null, null, null, null, -1, -1, null, null);
     }
 
+    public static SearchKey buildSavedOn(DayMonthYear date) {
+        return new SearchKey(TYPE_SAVEDON, date, null, 0, null, null, null, null, -1, -1, null, null);
+    }
+
     public static SearchKey buildSentBefore(DayMonthYear date) {
         return new SearchKey(TYPE_SENTBEFORE, date, null, 0, null, null, null, null, -1, -1, null, null);
     }
 
+    public static SearchKey buildSavedBefore(DayMonthYear date) {
+        return new SearchKey(TYPE_SAVEDBEFORE, date, null, 0, null, null, null, null, -1, -1, null, null);
+    }
+
     public static SearchKey buildSentOn(DayMonthYear date) {
         return new SearchKey(TYPE_SENTON, date, null, 0, null, null, null, null, -1, -1, null, null);
     }
@@ -298,6 +314,14 @@ public final class SearchKey {
         return new SearchKey(TYPE_SINCE, date, null, 0, null, null, null, null, -1, -1, null, null);
     }
 
+    public static SearchKey buildSavedSince(DayMonthYear date) {
+        return new SearchKey(TYPE_SAVEDSINCE, date, null, 0, null, null, null, null, -1, -1, null, null);
+    }
+
+    public static SearchKey buildSaveDateSupported() {
+        return new SearchKey(TYPE_SAVEDATESUPPORTED, null, null, 0, null, null, null, null, -1, -1, null, null);
+    }
+
     // FIELD VALUE
     public static SearchKey buildHeader(String name, String value) {
         return new SearchKey(TYPE_HEADER, null, null, 0, name, value, null, null, -1, -1, null, null);
@@ -393,7 +417,7 @@ public final class SearchKey {
      * Gets a date value to be search upon.
      * 
      * @return the date when: TYPE_BEFORE, TYPE_ON,
-     *         TYPE_SENTBEFORE, TYPE_SENTON, TYPE_SENTSINCE, TYPE_SINCE, otherwise null
+     *         TYPE_SENTBEFORE, TYPE_SENTON, TYPE_SENTSINCE, TYPE_SINCE, TYPE_SAVEBEFORE, TYPE_SAVEON, TYPE_SAVESINCE otherwise null
      */
     public DayMonthYear getDate() {
         return date;
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SearchCommandParser.java b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SearchCommandParser.java
index e4e898d330..c5a80e46cd 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SearchCommandParser.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/decode/parser/SearchCommandParser.java
@@ -291,6 +291,8 @@ public class SearchCommandParser extends AbstractUidCommandParser {
             return smaller(request);
         case 'U':
             return subject(request, charset);
+        case 'A':
+            return saved(request);
         default:
             throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Unknown search key");
         }
@@ -332,6 +334,26 @@ public class SearchCommandParser extends AbstractUidCommandParser {
         }
     }
 
+    private SearchKey saved(ImapRequestLineReader request) throws DecodingException {
+        nextIsV(request);
+        nextIsE(request);
+        nextIsD(request);
+
+        final int next = consumeAndCap(request);
+        switch (next) {
+            case 'A':
+                return saveDateSupported(request);
+            case 'B':
+                return savedBefore(request);
+            case 'O':
+                return savedOn(request);
+            case 'S':
+                return savedSince(request);
+            default:
+                throw new DecodingException(HumanReadableText.ILLEGAL_ARGUMENTS, "Unknown search key");
+        }
+    }
+
     private SearchKey o(ImapSession session, ImapRequestLineReader request, Context context) throws DecodingException {
         final int next = consumeAndCap(request);
         switch (next) {
@@ -668,6 +690,16 @@ public class SearchCommandParser extends AbstractUidCommandParser {
         return result;
     }
 
+    private SearchKey savedBefore(ImapRequestLineReader request) throws DecodingException {
+        nextIsE(request);
+        nextIsF(request);
+        nextIsO(request);
+        nextIsR(request);
+        nextIsE(request);
+        nextIsSpace(request);
+        return SearchKey.buildSavedBefore(request.date());
+    }
+
     private SearchKey sentSince(ImapRequestLineReader request) throws DecodingException {
         final SearchKey result;
         nextIsI(request);
@@ -680,6 +712,30 @@ public class SearchCommandParser extends AbstractUidCommandParser {
         return result;
     }
 
+    private SearchKey savedSince(ImapRequestLineReader request) throws DecodingException {
+        nextIsI(request);
+        nextIsN(request);
+        nextIsC(request);
+        nextIsE(request);
+        nextIsSpace(request);
+        return SearchKey.buildSavedSince(request.date());
+    }
+
+    private SearchKey saveDateSupported(ImapRequestLineReader request) throws DecodingException {
+        nextIsT(request);
+        nextIsE(request);
+        nextIsS(request);
+        nextIsU(request);
+        nextIsP(request);
+        nextIsP(request);
+        nextIsO(request);
+        nextIsR(request);
+        nextIsT(request);
+        nextIsE(request);
+        nextIsD(request);
+        return SearchKey.buildSaveDateSupported();
+    }
+
     private SearchKey since(ImapRequestLineReader request) throws DecodingException {
         final SearchKey result;
         nextIsN(request);
@@ -700,6 +756,12 @@ public class SearchCommandParser extends AbstractUidCommandParser {
         return result;
     }
 
+    private SearchKey savedOn(ImapRequestLineReader request) throws DecodingException {
+        nextIsN(request);
+        nextIsSpace(request);
+        return SearchKey.buildSavedOn(request.date());
+    }
+
     private SearchKey before(ImapRequestLineReader request) throws DecodingException {
         final SearchKey result;
         nextIsF(request);
@@ -912,6 +974,9 @@ public class SearchCommandParser extends AbstractUidCommandParser {
         nextIs(request, 'L', 'l');
     }
 
+    private void nextIsP(ImapRequestLineReader request) throws DecodingException {
+        nextIs(request, 'P', 'p');
+    }
 
     private void nextIsV(ImapRequestLineReader request) throws DecodingException {
         nextIs(request, 'V', 'v');
diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
index eb3ea375f2..3ab4073b44 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java
@@ -380,6 +380,14 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp
             return SearchQuery.threadId(ThreadId.fromBaseMessageId(getMailboxManager().getMessageIdFactory().fromString(key.getThreadId())));
         case TYPE_EMAILID:
             return SearchQuery.hasMessageId(getMailboxManager().getMessageIdFactory().fromString(key.getMessageId()));
+        case TYPE_SAVEDBEFORE:
+            return SearchQuery.saveDateBefore(date.toDate(), DateResolution.Day);
+        case TYPE_SAVEDON:
+            return SearchQuery.saveDateOn(date.toDate(), DateResolution.Day);
+        case TYPE_SAVEDSINCE:
+            return SearchQuery.or(SearchQuery.saveDateOn(date.toDate(), DateResolution.Day), SearchQuery.saveDateAfter(date.toDate(), DateResolution.Day));
+        case TYPE_SAVEDATESUPPORTED:
+            return SearchQuery.saveDateSupported();
         default:
             LOGGER.warn("Ignoring unknown search key {}", type);
             return SearchQuery.all();
diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
index 477e3c64ac..8658605fde 100644
--- a/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
+++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/SearchProcessorTest.java
@@ -238,6 +238,12 @@ public class SearchProcessorTest {
                 .internalDateBefore(getDate(DAY, MONTH, YEAR), DateResolution.Day));
     }
 
+    @Test
+    void testSAVEDBEFORE() throws Exception {
+        expectsGetSelectedMailbox();
+        check(SearchKey.buildSavedBefore(DAY_MONTH_YEAR), SearchQuery.saveDateBefore(getDate(DAY, MONTH, YEAR), DateResolution.Day));
+    }
+
     @Test
     void testBODY() throws Exception {
         expectsGetSelectedMailbox();
@@ -326,6 +332,12 @@ public class SearchProcessorTest {
                 DAY, MONTH, YEAR), DateResolution.Day));
     }
 
+    @Test
+    void testSAVEDON() throws Exception {
+        expectsGetSelectedMailbox();
+        check(SearchKey.buildSavedOn(DAY_MONTH_YEAR), SearchQuery.saveDateOn(getDate(DAY, MONTH, YEAR), DateResolution.Day));
+    }
+
     @Test
     void testAND() throws Exception {
         expectsGetSelectedMailbox();
@@ -390,6 +402,19 @@ public class SearchProcessorTest {
                 .internalDateAfter(getDate(DAY, MONTH, YEAR), DateResolution.Day)));
     }
 
+    @Test
+    void testSAVEDSINCE() throws Exception {
+        expectsGetSelectedMailbox();
+        check(SearchKey.buildSavedSince(DAY_MONTH_YEAR), SearchQuery.or(SearchQuery.saveDateOn(getDate(DAY, MONTH, YEAR), DateResolution.Day),
+            SearchQuery.saveDateAfter(getDate(DAY, MONTH, YEAR), DateResolution.Day)));
+    }
+
+    @Test
+    void testSAVEDATESUPPORTED() throws Exception {
+        expectsGetSelectedMailbox();
+        check(SearchKey.buildSaveDateSupported(), SearchQuery.saveDateSupported());
+    }
+
     @Test
     void testSMALLER() throws Exception {
         expectsGetSelectedMailbox();


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