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 a2557d58c96f938f3e89176870eedabaa1ebeda1
Author: Benoit TELLIER <btell...@linagora.com>
AuthorDate: Wed Oct 9 14:53:43 2024 +0200

    JAMES-2182 PathConverter: username escaping for dots
    
    This common character conflicts with the path delimiter
    of the mailbox name and thus needs to be escaped.
---
 .../org/apache/james/imap/main/PathConverter.java  | 13 +++++++++--
 .../apache/james/imap/main/PathConverterTest.java  | 26 ++++++++++++++++++++++
 2 files changed, 37 insertions(+), 2 deletions(-)

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 3bbcbf469f..5ea5c5fe79 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
@@ -33,6 +33,8 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Splitter;
 import com.google.common.base.Strings;
 import com.google.common.collect.Iterables;
+import com.google.common.escape.Escaper;
+import com.google.common.escape.Escapers;
 
 public interface PathConverter {
     interface Factory {
@@ -50,6 +52,10 @@ public interface PathConverter {
     class Default implements PathConverter{
         private static final int NAMESPACE = 0;
         private static final int USER = 1;
+        public static final Escaper USERNAME_ESCAPER = Escapers.builder()
+            .addEscape('.', "__")
+            .addEscape('_', "_-")
+            .build();
 
         private final ImapSession session;
 
@@ -90,7 +96,9 @@ public interface PathConverter {
             } else if (namespace.equalsIgnoreCase("#user")) {
                 Preconditions.checkArgument(mailboxPathParts.size() > 2, 
"Expecting at least 2 parts");
                 String username = mailboxPathParts.get(USER);
-                Username user = Username.from(username, 
session.getUser().getDomainPart().map(Domain::asString));
+                String unescapedUsername = username.replace("__", ".")
+                    .replace("_-", "_");
+                Username user = Username.from(unescapedUsername, 
session.getUser().getDomainPart().map(Domain::asString));
                 String mailboxName = 
Joiner.on(session.getPathDelimiter()).join(Iterables.skip(mailboxPathParts, 2));
                 return new MailboxPath(MailboxConstants.USER_NAMESPACE, user, 
sanitizeMailboxName(mailboxName));
 
@@ -126,7 +134,8 @@ public interface PathConverter {
                     if (!sb.isEmpty()) {
                         sb.append(session.getPathDelimiter());
                     }
-                    sb.append(mailboxPath.getUser().getLocalPart());
+
+                    
sb.append(USERNAME_ESCAPER.escape(mailboxPath.getUser().getLocalPart()));
                 }
             }
             if (mailboxPath.getName() != null && 
!mailboxPath.getName().isEmpty()) {
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterTest.java
index f314f32949..8d864b4b26 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/main/PathConverterTest.java
@@ -35,6 +35,8 @@ import org.junit.jupiter.api.Test;
 class PathConverterTest {
 
     private static final Username USERNAME = Username.of("username");
+    private static final Username USERNAME_WITH_DOT = 
Username.of("username.with.dot");
+    private static final Username USERNAME_WITH_UNDERSCORE = 
Username.of("username_with_underscore");
     private static final Username USERNAME2 = Username.of("username2");
 
     private static final Username USERNAME_WITH_MAIL = 
Username.of("usern...@apache.org");
@@ -112,6 +114,18 @@ class PathConverterTest {
             .isEqualTo(MailboxPath.forUser(USERNAME2, "abc"));
     }
 
+    @Test
+    void buildFullPathShouldAcceptAbsoluteOtherUserPathWithDot() {
+        
assertThat(pathConverter.buildFullPath("#user.username__with__dot.abc"))
+            .isEqualTo(MailboxPath.forUser(USERNAME_WITH_DOT, "abc"));
+    }
+
+    @Test
+    void buildFullPathShouldAcceptAbsoluteOtherUserPathWithUnderscore() {
+        
assertThat(pathConverter.buildFullPath("#user.username_-with_-underscore.abc"))
+            .isEqualTo(MailboxPath.forUser(USERNAME_WITH_UNDERSCORE, "abc"));
+    }
+
     @Test
     void buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() {
         assertThat(pathConverter.buildFullPath("#user.username2.abc.def"))
@@ -136,6 +150,18 @@ class PathConverterTest {
             .isEqualTo("#user.username2.abc");
     }
 
+    @Test
+    void mailboxNameShouldEscapeDotInUsername() {
+        assertThat(pathConverter.mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_DOT, "abc"), mailboxSession))
+            .isEqualTo("#user.username__with__dot.abc");
+    }
+
+    @Test
+    void mailboxNameShouldEscapeUnderscoreInUsername() {
+        assertThat(pathConverter.mailboxName(RELATIVE, 
MailboxPath.forUser(USERNAME_WITH_UNDERSCORE, "abc"), mailboxSession))
+            .isEqualTo("#user.username_-with_-underscore.abc");
+    }
+
     @Test
     void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() {
         assertThat(pathConverter.mailboxName(RELATIVE, new 
MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession))


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

Reply via email to