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

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

commit fa9d5b56356f376a71a448ed9f87f658c902a0bb
Author: Benoit TELLIER <btell...@linagora.com>
AuthorDate: Wed Oct 9 17:03:33 2024 +0200

    JAMES-2182 Partial implementation for shared folders in IMAP
    
    Several issues remains to address:
     - [ ] List + myrights needs to keep FQDN in the response
     - [ ] #user shall not include personal mailboxes
           filter them out?
           (post filtering?)
     - [ ] Using #user in mailbox name pattern shall be permitted
     - [ ] Remove duplicate results...
---
 .../imapmailbox/suite/ListingWithSharingTest.java  |  8 ++-
 .../james/imap/scripts/ListWithSharedMailbox.test  | 64 ++++++++++++++++++++--
 .../org/apache/james/imap/main/PathConverter.java  |  6 +-
 .../apache/james/imap/processor/ListProcessor.java | 39 +++++++++----
 .../james/imap/processor/StatusProcessor.java      |  9 +--
 5 files changed, 103 insertions(+), 23 deletions(-)

diff --git 
a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/ListingWithSharingTest.java
 
b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/ListingWithSharingTest.java
index 95eedde1d1..c496b3724f 100644
--- 
a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/ListingWithSharingTest.java
+++ 
b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/ListingWithSharingTest.java
@@ -33,8 +33,10 @@ import org.junit.jupiter.api.Test;
 
 public abstract class ListingWithSharingTest implements ImapTestConstants {
     public static final Username OTHER_USER_NAME = Username.of("Boby");
+    public static final Username YET_ANOTHER_USER_NAME = Username.of("Diana");
     public static final String OTHER_USER_PASSWORD = "password";
     public static final MailboxPath OTHER_USER_SHARED_MAILBOX = 
MailboxPath.forUser(OTHER_USER_NAME, "sharedMailbox");
+    public static final MailboxPath YET_ANOTHER_USER_SHARED_MAILBOX = 
MailboxPath.forUser(YET_ANOTHER_USER_NAME, "sharedMailbox");
     public static final MailboxPath OTHER_USER_SHARED_MAILBOX_CHILD = 
MailboxPath.forUser(OTHER_USER_NAME, "sharedMailbox.child");
 
     protected abstract ImapHostSystem createImapHostSystem();
@@ -57,8 +59,10 @@ public abstract class ListingWithSharingTest implements 
ImapTestConstants {
         scriptedTestProtocol
             .withMailbox(OTHER_USER_SHARED_MAILBOX)
             .withMailbox(OTHER_USER_SHARED_MAILBOX_CHILD)
-            .withRights(OTHER_USER_SHARED_MAILBOX, USER, 
MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("r"))
-            .withRights(OTHER_USER_SHARED_MAILBOX_CHILD, USER, 
MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("r"))
+            .withMailbox(YET_ANOTHER_USER_SHARED_MAILBOX)
+            .withRights(OTHER_USER_SHARED_MAILBOX, USER, 
MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("rl"))
+            .withRights(OTHER_USER_SHARED_MAILBOX_CHILD, USER, 
MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("rl"))
+            .withRights(YET_ANOTHER_USER_SHARED_MAILBOX, USER, 
MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("rl"))
             .withLocale(Locale.US)
             .run("ListWithSharedMailbox");
     }
diff --git 
a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListWithSharedMailbox.test
 
b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListWithSharedMailbox.test
index eba7f0dd17..29243651e5 100644
--- 
a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListWithSharedMailbox.test
+++ 
b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/ListWithSharedMailbox.test
@@ -17,16 +17,68 @@
 # under the License.                                           #
 ################################################################
 
-# Shouw that #private.Boby.sharedMailbox and it's child are not displayed
-
+# Can list other users delegated mailbox
 C: a0 LIST "" "*"
+SUB {
 S: \* LIST \(\\HasNoChildren\) \"\.\" \"INBOX\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\"
+}
 S: a0 OK LIST completed.
 
-C: a1 LIST "#private" "%"
-S: \* LIST \(\\HasNoChildren\) \"\.\" \"#private.INBOX\"
+C: a1 LIST "" "*" RETURN (MYRIGHTS)
+# TODO MYRIGHTS status response should keep FQDN
+SUB {
+S: \* LIST \(\\HasNoChildren\) \"\.\" \"INBOX\"
+S: \* MYRIGHTS \"INBOX\" \"aeiklprstwx\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: \* MYRIGHTS \"sharedMailbox\" \"lr\"
+S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\"
+S: \* MYRIGHTS \"sharedMailbox\" \"lr\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\"
+S: \* MYRIGHTS \"sharedMailbox.child\" \"lr\"
+}
 S: a1 OK LIST completed.
 
-C: a3 LIST "#private" sharedMailbox*
-S: a3 OK LIST completed.
+C: a2 LIST "" "*" RETURN (STATUS (UNSEEN RECENT))
+SUB {
+S: \* LIST \(\\HasNoChildren\) \"\.\" \"INBOX\"
+S: \* STATUS \"INBOX\" \(RECENT 0 UNSEEN 0\)
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: \* STATUS \"#user.diana.sharedMailbox\" \(RECENT 0 UNSEEN 0\)
+S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\"
+S: \* STATUS \"#user.boby.sharedMailbox\" \(RECENT 0 UNSEEN 0\)
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\"
+S: \* STATUS \"#user.boby.sharedMailbox.child\" \(RECENT 0 UNSEEN 0\)
+}
+S: a2 OK LIST completed.
+
+C: a3 STATUS #user.boby.sharedMailbox (UNSEEN RECENT)
+S: \* STATUS \"#user.boby.sharedMailbox\" \(RECENT 0 UNSEEN 0\)
+S: a3 OK STATUS completed.
+
+C: a4 LIST "#user.diana" "*"
+# TODO why double results
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: a4 OK LIST completed.
+
+C: a7 LIST "#user" "*"
+# TODO personal mailboxes shall not be included
+SUB {
+S: \* LIST \(\\HasNoChildren\) \"\.\" \"#private.INBOX\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\"
+S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\"
+S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\"
+}
+S: a7 OK LIST completed.
+
+# When looking up private namespace the shared mailboxes are not included
+C: a8 LIST "#private" "%"
+S: \* LIST \(\\HasNoChildren\) \"\.\" \"#private.INBOX\"
+S: a8 OK LIST completed.
+
+C: a9 LIST "#private" sharedMailbox*
+S: a9 OK LIST completed.
 
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java 
b/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
index 5ea5c5fe79..8a53728fa7 100644
--- a/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
+++ b/protocols/imap/src/main/java/org/apache/james/imap/main/PathConverter.java
@@ -94,7 +94,11 @@ public interface PathConverter {
                 String mailboxName = 
Joiner.on(session.getPathDelimiter()).join(Iterables.skip(mailboxPathParts, 1));
                 return new MailboxPath(MailboxConstants.USER_NAMESPACE, 
session.getUser(), sanitizeMailboxName(mailboxName));
             } else if (namespace.equalsIgnoreCase("#user")) {
-                Preconditions.checkArgument(mailboxPathParts.size() > 2, 
"Expecting at least 2 parts");
+                if (mailboxPathParts.size() == 1) {
+                    // May be generated by some List commands.
+                    String mailboxName = 
Joiner.on(session.getPathDelimiter()).join(Iterables.skip(mailboxPathParts, 1));
+                    return new MailboxPath(MailboxConstants.USER_NAMESPACE, 
null, sanitizeMailboxName(mailboxName));
+                }
                 String username = mailboxPathParts.get(USER);
                 String unescapedUsername = username.replace("__", ".")
                     .replace("_-", "_");
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
index aa6f19cd5e..a568665e50 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListProcessor.java
@@ -191,18 +191,11 @@ public class ListProcessor<T extends ListRequest> extends 
AbstractMailboxProcess
             return Mono.empty();
         }
 
-        // If the mailboxPattern is fully qualified, ignore the
-        // reference name.
-        String finalReferencename = request.getBaseReferenceName();
-        if (request.getMailboxPattern().charAt(0) == 
MailboxConstants.NAMESPACE_PREFIX_CHAR) {
-            finalReferencename = "";
-        }
         // Is the interpreted (combined) pattern relative?
         // Should the namespace section be returned or not?
-        boolean isRelative = ((finalReferencename + 
request.getMailboxPattern()).charAt(0) != 
MailboxConstants.NAMESPACE_PREFIX_CHAR);
+        boolean isRelative = ((request.getBaseReferenceName() + 
request.getMailboxPattern()).charAt(0) != 
MailboxConstants.NAMESPACE_PREFIX_CHAR);
 
-        MailboxQuery mailboxQuery = mailboxQuery(computeBasePath(session, 
finalReferencename, isRelative),
-            request.getMailboxPattern(), mailboxSession);
+        MailboxQuery mailboxQuery = 
mailboxQuery(request.getBaseReferenceName(), request.getMailboxPattern(), 
mailboxSession, session, isRelative);
 
         if (request.selectSubscribed()) {
             return processWithSubscribed(session, request, responder, 
mailboxSession, isRelative, mailboxQuery);
@@ -337,7 +330,33 @@ public class ListProcessor<T extends ListRequest> extends 
AbstractMailboxProcess
         return metaData.getResolvedAcls().getEntries().get(entryKey);
     }
 
-    private MailboxQuery mailboxQuery(MailboxPath basePath, String 
mailboxName, MailboxSession mailboxSession) {
+    private MailboxQuery mailboxQuery(String finalReferencename, String 
mailboxName, MailboxSession mailboxSession,
+                                      ImapSession session, boolean isRelative) 
{
+        if (finalReferencename.isEmpty()) {
+            if (mailboxName.equals("*")) {
+                return MailboxQuery.builder()
+                    .matchesAllMailboxNames()
+                    .build();
+            }
+            return MailboxQuery.builder()
+                .expression(new PrefixedRegex(
+                    "",
+                    ModifiedUtf7.decodeModifiedUTF7(mailboxName),
+                    mailboxSession.getPathDelimiter()))
+                .build();
+        }
+
+        MailboxPath basePath = computeBasePath(session, finalReferencename, 
isRelative);
+        if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE)
+            && basePath.getUser() == null) {
+            return MailboxQuery.builder()
+                .namespace(MailboxConstants.USER_NAMESPACE)
+                .expression(new PrefixedRegex(
+                    basePath.getName(),
+                    ModifiedUtf7.decodeModifiedUTF7(mailboxName),
+                    mailboxSession.getPathDelimiter()))
+                .build();
+        }
         if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE)
             && basePath.getUser().equals(mailboxSession.getUser())
             && basePath.getName().isEmpty()
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
index a4aafb278e..038d41e553 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/StatusProcessor.java
@@ -74,6 +74,7 @@ import reactor.core.publisher.Mono;
  */
 public class StatusProcessor extends AbstractMailboxProcessor<StatusRequest> 
implements CapabilityImplementingProcessor {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(StatusProcessor.class);
+    public static final boolean RELATIVE = true;
 
 
     private final PathConverter.Factory pathConverterFactory;
@@ -125,7 +126,7 @@ public class StatusProcessor extends 
AbstractMailboxProcessor<StatusRequest> imp
 
     Mono<MailboxStatusResponse> sendStatus(MessageManager mailbox, 
StatusDataItems statusDataItems, Responder responder, ImapSession session, 
MailboxSession mailboxSession) {
         return retrieveMetadata(mailbox, statusDataItems, mailboxSession)
-            .flatMap(metaData -> computeStatusResponse(mailbox, 
statusDataItems, metaData, mailboxSession)
+            .flatMap(metaData -> computeStatusResponse(mailbox, 
statusDataItems, metaData, mailboxSession, session)
                 .doOnNext(response -> {
                     // Enable CONDSTORE as this is a CONDSTORE enabling command
                     if (response.getHighestModSeq() != null) {
@@ -164,8 +165,8 @@ public class StatusProcessor extends 
AbstractMailboxProcessor<StatusRequest> imp
     private Mono<MailboxStatusResponse> computeStatusResponse(MessageManager 
mailbox,
                                                               StatusDataItems 
statusDataItems,
                                                               
MessageManager.MailboxMetaData metaData,
-                                                              MailboxSession 
session) {
-        return iterateMailbox(statusDataItems, mailbox, session)
+                                                              MailboxSession 
mailboxSession, ImapSession session) {
+        return iterateMailbox(statusDataItems, mailbox, mailboxSession)
             .map(maybeIterationResult -> {
                 Optional<Long> appendLimit = appendLimit(statusDataItems);
                 Long messages = messages(statusDataItems, metaData);
@@ -180,7 +181,7 @@ public class StatusProcessor extends 
AbstractMailboxProcessor<StatusRequest> imp
                     maybeIterationResult.flatMap(result -> 
result.getSize(statusDataItems)).orElse(null),
                     maybeIterationResult.flatMap(result -> 
result.getDeleted(statusDataItems)).orElse(null),
                     maybeIterationResult.flatMap(result -> 
result.getDeletedStorage(statusDataItems)).orElse(null),
-                    messages, recent, uidNext, highestModSeq, uidValidity, 
unseen, mailbox.getMailboxPath().getName(), mailboxId);
+                    messages, recent, uidNext, highestModSeq, uidValidity, 
unseen, pathConverterFactory.forSession(session).mailboxName(RELATIVE, 
mailbox.getMailboxPath(), mailboxSession), mailboxId);
             });
     }
 


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

Reply via email to