From 065486260eff3c7dd89cc50b0dd30df4c078eb92 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 16:43:05 +0200 Subject: [PATCH 01/37] [REFACTORING] drop unused logger --- .../message/response/UnpooledStatusResponseFactory.java | 6 ------ 1 file changed, 6 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/UnpooledStatusResponseFactory.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/UnpooledStatusResponseFactory.java index 189848e9dce..41e2e78937e 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/UnpooledStatusResponseFactory.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/UnpooledStatusResponseFactory.java @@ -26,16 +26,10 @@ import org.apache.james.imap.api.message.response.StatusResponse.ResponseCode; import org.apache.james.imap.api.message.response.StatusResponse.Type; import org.apache.james.imap.api.message.response.StatusResponseFactory; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class UnpooledStatusResponseFactory extends AbstractStatusResponseFactory implements StatusResponseFactory { - - private static final Logger LOGGER = LoggerFactory.getLogger(UnpooledStatusResponseFactory.class); - @Override protected StatusResponse createResponse(Type type, Tag tag, ImapCommand command, HumanReadableText displayTextKey, ResponseCode code) { return new ImmutableStatusResponse(type, tag, command, displayTextKey, code); } - } From becc897fe3bc5d17d4a2f5c973f0ba50140ed882 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:06:08 +0200 Subject: [PATCH 02/37] JAMES-2182 Remove ImapSession::supportMultipleNamespaces --- .../james/imap/api/process/ImapSession.java | 7 +-- .../james/imap/encode/FakeImapSession.java | 5 -- .../imap/processor/NamespaceProcessor.java | 7 +-- .../processor/NamespaceProcessorTest.java | 49 ------------------- .../imapserver/netty/NettyImapSession.java | 5 -- 5 files changed, 3 insertions(+), 70 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java index bcb4f968c83..fa6b5085517 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/ImapSession.java @@ -237,12 +237,7 @@ default boolean backpressureNeeded(Runnable restoreBackpressure) { * Pop the current {@link ImapLineHandler} */ void popLineHandler(); - - /** - * Return true if multiple namespaces are supported - */ - boolean supportMultipleNamespaces(); - + /** * Return true if SSL is required when Authenticating */ diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java index 690e320d293..ed312cf4639 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/FakeImapSession.java @@ -196,11 +196,6 @@ public boolean isTLSActive() { return false; } - @Override - public boolean supportMultipleNamespaces() { - return false; - } - @Override public boolean isCompressionActive() { return false; diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java index 4d708b571d9..2f3a4a5679c 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java @@ -73,9 +73,6 @@ protected Mono processRequestReactive(NamespaceRequest request, ImapSessio private List buildPersonalNamespaces(MailboxSession mailboxSession, ImapSession session) { final List personalSpaces = new ArrayList<>(); String personal = ""; - if (session.supportMultipleNamespaces()) { - personal = mailboxSession.getPersonalSpace(); - } personalSpaces.add(new NamespaceResponse.Namespace(personal, mailboxSession.getPathDelimiter())); return personalSpaces; } @@ -83,7 +80,7 @@ private List buildPersonalNamespaces(MailboxSession private List buildOtherUsersSpaces(MailboxSession mailboxSession, ImapSession session) { final String otherUsersSpace = mailboxSession.getOtherUsersSpace(); final List otherUsersSpaces; - if (session.supportMultipleNamespaces() == false || otherUsersSpace == null) { + if (otherUsersSpace == null) { otherUsersSpaces = null; } else { otherUsersSpaces = new ArrayList<>(1); @@ -95,7 +92,7 @@ private List buildOtherUsersSpaces(MailboxSession m private List buildSharedNamespaces(MailboxSession mailboxSession, ImapSession session) { List sharedNamespaces = null; final Collection sharedSpaces = mailboxSession.getSharedSpaces(); - if (session.supportMultipleNamespaces() && !sharedSpaces.isEmpty()) { + if (!sharedSpaces.isEmpty()) { sharedNamespaces = new ArrayList<>(sharedSpaces.size()); for (String space : sharedSpaces) { sharedNamespaces.add(new NamespaceResponse.Namespace(space, mailboxSession.getPathDelimiter())); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java index 96958a3a518..27551c665c0 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java @@ -83,55 +83,6 @@ void setUp() { imapSession.setMailboxSession(mailboxSession); } - @Test - void testNamespaceResponseShouldContainPersonalAndUserSpaces() { - when(imapSession.supportMultipleNamespaces()).thenReturn(true); - - when(mailboxSession.getPersonalSpace()).thenReturn(PERSONAL_PREFIX); - when(mailboxSession.getOtherUsersSpace()).thenReturn(USERS_PREFIX); - when(mailboxSession.getSharedSpaces()).thenReturn(new ArrayList<>()); - when(mailboxSession.getPathDelimiter()).thenReturn(MailboxConstants.DEFAULT_DELIMITER); - - when(imapSession.getState()).thenReturn(ImapSessionState.AUTHENTICATED); - when(statusResponseStub.taggedOk(any(Tag.class), any(ImapCommand.class), any(HumanReadableText.class))) - .thenReturn(mock(StatusResponse.class)); - - final NamespaceResponse response = buildResponse(null); - final Responder responderMock = mock(Responder.class); - - subject.doProcess(namespaceRequest, responderMock, imapSession).block(); - - verify(responderMock, times(1)).respond(response); - verify(responderMock, times(1)).respond(any(StatusResponse.class)); - verifyNoMoreInteractions(responderMock); - } - - @Test - void testNamespaceResponseShouldContainSharedSpaces() { - when(imapSession.supportMultipleNamespaces()).thenReturn(true); - - when(mailboxSession.getPersonalSpace()).thenReturn(PERSONAL_PREFIX); - when(mailboxSession.getOtherUsersSpace()).thenReturn(USERS_PREFIX); - when(mailboxSession.getSharedSpaces()).thenReturn(Arrays.asList(SHARED_PREFIX)); - when(mailboxSession.getPathDelimiter()).thenReturn(MailboxConstants.DEFAULT_DELIMITER); - - when(imapSession.getState()).thenReturn(ImapSessionState.AUTHENTICATED); - when(statusResponseStub.taggedOk(any(Tag.class), any(ImapCommand.class), any(HumanReadableText.class))) - .thenReturn(mock(StatusResponse.class)); - - final List sharedSpaces = new ArrayList<>(); - sharedSpaces.add(new NamespaceResponse.Namespace(SHARED_PREFIX, MailboxConstants.DEFAULT_DELIMITER)); - final NamespaceResponse response = buildResponse(sharedSpaces); - - final Responder responderMock = mock(Responder.class); - - subject.doProcess(namespaceRequest, responderMock, imapSession).block(); - - verify(responderMock, times(1)).respond(response); - verify(responderMock, times(1)).respond(any(StatusResponse.class)); - verifyNoMoreInteractions(responderMock); - } - private NamespaceResponse buildResponse(List sharedSpaces) { final List personalSpaces = new ArrayList<>(); diff --git a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java index 3f2bc05f17e..ff61f4e6922 100644 --- a/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java +++ b/server/protocols/protocols-imap4/src/main/java/org/apache/james/imapserver/netty/NettyImapSession.java @@ -310,11 +310,6 @@ public Optional getSSLSession() { .map(SSLEngine::getSession); } - @Override - public boolean supportMultipleNamespaces() { - return false; - } - @Override public boolean isCompressionActive() { return channel.pipeline().get(ZLIB_DECODER) != null; From 9ee964c42391080a0c7fb00ac59155a9e2b68782 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:16:55 +0200 Subject: [PATCH 03/37] JAMES-2182 Inline namespace related concerns out of the mailbox session We do not need those IMAP concerns onto the storage layer. We will later come up with alternative implementation for namespace customization. --- .../apache/james/mailbox/MailboxSession.java | 55 ------------------- .../imap/processor/NamespaceProcessor.java | 20 +++---- 2 files changed, 10 insertions(+), 65 deletions(-) diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSession.java b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSession.java index 1080ea07256..1901e3a55c5 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSession.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/MailboxSession.java @@ -19,8 +19,6 @@ package org.apache.james.mailbox; -import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -29,7 +27,6 @@ import java.util.Optional; import org.apache.james.core.Username; -import org.apache.james.mailbox.model.MailboxConstants; import com.google.common.base.MoreObjects; @@ -100,9 +97,6 @@ public enum SessionType { User } - private final Collection sharedSpaces; - private final String otherUsersSpace; - private final String personalSpace; private final SessionId sessionId; private final Username userName; private final Optional loggedInUser; @@ -113,21 +107,9 @@ public enum SessionType { public MailboxSession(SessionId sessionId, Username userName, Optional loggedInUser, List localePreferences, char pathSeparator, SessionType type) { - this(sessionId, userName, loggedInUser, localePreferences, new ArrayList<>(), null, pathSeparator, type); - } - - public MailboxSession(SessionId sessionId, Username userName, Optional loggedInUser, - List localePreferences, List sharedSpaces, String otherUsersSpace, char pathSeparator, SessionType type) { this.sessionId = sessionId; this.userName = userName; - this.otherUsersSpace = otherUsersSpace; - this.sharedSpaces = sharedSpaces; this.type = type; - if (otherUsersSpace == null && (sharedSpaces == null || sharedSpaces.isEmpty())) { - this.personalSpace = ""; - } else { - this.personalSpace = MailboxConstants.USER_NAMESPACE; - } this.localePreferences = localePreferences; this.attributes = new HashMap<>(); @@ -173,43 +155,6 @@ public List getLocalePreferences() { return localePreferences; } - /** - * Gets the personal namespace for the current session.
- * Note that though servers may offer multiple personal namespaces, support - * is not offered through this API. This decision may be revised if - * reasonable use cases emerge. - * - * @return Personal Namespace, not null - */ - public String getPersonalSpace() { - return personalSpace; - } - - /** - * Gets the other users namespace for the current session.
- * Note that though servers may offer multiple other users namespaces, - * support is not offered through this API. This decision may be revised if - * reasonable use cases emerge. - * - * @return Other Users Namespace or null when there is non available - */ - public String getOtherUsersSpace() { - return otherUsersSpace; - } - - /** - * Iterates the Shared Namespaces available for the current - * session. - * - * @return not null though possibly empty - */ - public Collection getSharedSpaces() { - return sharedSpaces; - } - /** * Return the stored attributes for this {@link MailboxSession}. */ diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java index 2f3a4a5679c..03ad9e5f52b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java @@ -53,11 +53,11 @@ public NamespaceProcessor(MailboxManager mailboxManager, StatusResponseFactory f @Override protected Mono processRequestReactive(NamespaceRequest request, ImapSession session, Responder responder) { - final MailboxSession mailboxSession = session.getMailboxSession(); - final List personalNamespaces = buildPersonalNamespaces(mailboxSession, session); - final List otherUsersNamespaces = buildOtherUsersSpaces(mailboxSession, session); - final List sharedNamespaces = buildSharedNamespaces(mailboxSession, session); - final NamespaceResponse response = new NamespaceResponse(personalNamespaces, otherUsersNamespaces, sharedNamespaces); + MailboxSession mailboxSession = session.getMailboxSession(); + List personalNamespaces = buildPersonalNamespaces(mailboxSession); + List otherUsersNamespaces = buildOtherUsersSpaces(mailboxSession); + List sharedNamespaces = buildSharedNamespaces(mailboxSession); + NamespaceResponse response = new NamespaceResponse(personalNamespaces, otherUsersNamespaces, sharedNamespaces); responder.respond(response); return unsolicitedResponses(session, responder, false) .then(Mono.fromRunnable(() -> okComplete(request, responder))); @@ -70,15 +70,15 @@ protected Mono processRequestReactive(NamespaceRequest request, ImapSessio * not null * @return personal namespaces, not null */ - private List buildPersonalNamespaces(MailboxSession mailboxSession, ImapSession session) { + private List buildPersonalNamespaces(MailboxSession mailboxSession) { final List personalSpaces = new ArrayList<>(); String personal = ""; personalSpaces.add(new NamespaceResponse.Namespace(personal, mailboxSession.getPathDelimiter())); return personalSpaces; } - private List buildOtherUsersSpaces(MailboxSession mailboxSession, ImapSession session) { - final String otherUsersSpace = mailboxSession.getOtherUsersSpace(); + private List buildOtherUsersSpaces(MailboxSession mailboxSession) { + final String otherUsersSpace = null; final List otherUsersSpaces; if (otherUsersSpace == null) { otherUsersSpaces = null; @@ -89,9 +89,9 @@ private List buildOtherUsersSpaces(MailboxSession m return otherUsersSpaces; } - private List buildSharedNamespaces(MailboxSession mailboxSession, ImapSession session) { + private List buildSharedNamespaces(MailboxSession mailboxSession) { List sharedNamespaces = null; - final Collection sharedSpaces = mailboxSession.getSharedSpaces(); + final Collection sharedSpaces = new ArrayList<>(); if (!sharedSpaces.isEmpty()) { sharedNamespaces = new ArrayList<>(sharedSpaces.size()); for (String space : sharedSpaces) { From bd127c2e2f943a951b1cfb3c3a38f9319c9e456d Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:22:05 +0200 Subject: [PATCH 04/37] JAMES-2182 NamespaceResponse: rely on collection rather than list --- .../imap/encode/NamespaceResponseEncoder.java | 14 ++++++-------- .../imap/message/response/NamespaceResponse.java | 15 ++++++++------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java index 0d045837f38..6a65c45e6fc 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java @@ -19,6 +19,7 @@ package org.apache.james.imap.encode; import java.io.IOException; +import java.util.Collection; import java.util.List; import org.apache.james.imap.api.ImapConstants; @@ -39,17 +40,14 @@ public void encode(NamespaceResponse response, ImapResponseComposer composer) th composer.untagged(); composer.commandName(ImapConstants.NAMESPACE_COMMAND); - final List personal = response.getPersonal(); - encode(personal, composer); - final List users = response.getUsers(); - encode(users, composer); - final List shared = response.getShared(); - encode(shared, composer); + encode(response.getPersonal(), composer); + encode(response.getUsers(), composer); + encode(response.getShared(), composer); composer.end(); } - private void encode(List namespaces, ImapResponseComposer composer) throws IOException { + private void encode(Collection namespaces, ImapResponseComposer composer) throws IOException { if (namespaces == null || namespaces.isEmpty()) { composer.nil(); } else { @@ -66,7 +64,7 @@ private void encode(Namespace namespace, ImapResponseComposer composer) throws I String prefix = namespace.getPrefix(); String delimiter = Character.toString(namespace.getDelimiter()); - if (prefix.length() > 0) { + if (!prefix.isEmpty()) { prefix = prefix + delimiter; } composer.quote(prefix); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java index 6c20bd019a6..837afecdc78 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.james.imap.message.response; +import java.util.Collection; import java.util.List; import java.util.Objects; @@ -27,11 +28,11 @@ * Describes a NAMESPACE response. */ public class NamespaceResponse implements ImapResponseMessage { - private final List personal; - private final List users; - private final List shared; + private final Collection personal; + private final Collection users; + private final Collection shared; - public NamespaceResponse(List personal, List users, List shared) { + public NamespaceResponse(Collection personal, Collection users, Collection shared) { this.personal = personal; this.users = users; this.shared = shared; @@ -42,7 +43,7 @@ public NamespaceResponse(List personal, List users, List getPersonal() { + public Collection getPersonal() { return personal; } @@ -51,7 +52,7 @@ public List getPersonal() { * * @return possibly null */ - public List getShared() { + public Collection getShared() { return shared; } @@ -60,7 +61,7 @@ public List getShared() { * * @return possibly null */ - public List getUsers() { + public Collection getUsers() { return users; } From 5419e3f09aefe067a9001964d7f95b3090d040a7 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:23:37 +0200 Subject: [PATCH 05/37] JAMES-2182 NamespaceProcessorTest: remove the empty test class --- .../processor/NamespaceProcessorTest.java | 96 ------------------- 1 file changed, 96 deletions(-) delete mode 100644 protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java deleted file mode 100644 index 27551c665c0..00000000000 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/NamespaceProcessorTest.java +++ /dev/null @@ -1,96 +0,0 @@ -/**************************************************************** - * 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.processor; - -import static org.apache.james.imap.ImapFixture.TAG; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.List; - -import org.apache.james.imap.api.ImapCommand; -import org.apache.james.imap.api.ImapSessionState; -import org.apache.james.imap.api.Tag; -import org.apache.james.imap.api.display.HumanReadableText; -import org.apache.james.imap.api.message.response.StatusResponse; -import org.apache.james.imap.api.message.response.StatusResponseFactory; -import org.apache.james.imap.api.process.ImapProcessor.Responder; -import org.apache.james.imap.api.process.ImapSession; -import org.apache.james.imap.encode.FakeImapSession; -import org.apache.james.imap.message.request.NamespaceRequest; -import org.apache.james.imap.message.response.NamespaceResponse; -import org.apache.james.mailbox.MailboxManager; -import org.apache.james.mailbox.MailboxSession; -import org.apache.james.mailbox.model.MailboxConstants; -import org.apache.james.metrics.tests.RecordingMetricFactory; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.mockito.stubbing.Answer; - -import reactor.core.publisher.Mono; - -class NamespaceProcessorTest { - private static final String SHARED_PREFIX = "SharedPrefix"; - private static final String USERS_PREFIX = "UsersPrefix"; - private static final String PERSONAL_PREFIX = "PersonalPrefix"; - - NamespaceProcessor subject; - StatusResponseFactory statusResponseStub; - ImapSession imapSession; - MailboxSession mailboxSession; - NamespaceRequest namespaceRequest; - Collection sharedSpaces; - MailboxManager mailboxManagerStub; - - @BeforeEach - void setUp() { - sharedSpaces = new ArrayList<>(); - statusResponseStub = mock(StatusResponseFactory.class); - mailboxManagerStub = mock(MailboxManager.class); - when(mailboxManagerStub.manageProcessing(any(), any())).thenAnswer((Answer) invocation -> { - Object[] args = invocation.getArguments(); - return (Mono) args[0]; - }); - subject = new NamespaceProcessor(mailboxManagerStub, statusResponseStub, new RecordingMetricFactory()); - imapSession = spy(new FakeImapSession()); - mailboxSession = mock(MailboxSession.class); - - namespaceRequest = new NamespaceRequest(TAG); - imapSession.setMailboxSession(mailboxSession); - } - - private NamespaceResponse buildResponse(List sharedSpaces) { - - final List personalSpaces = new ArrayList<>(); - personalSpaces.add(new NamespaceResponse.Namespace(PERSONAL_PREFIX, MailboxConstants.DEFAULT_DELIMITER)); - final List otherUsersSpaces = new ArrayList<>(); - otherUsersSpaces.add(new NamespaceResponse.Namespace(USERS_PREFIX, MailboxConstants.DEFAULT_DELIMITER)); - - return new NamespaceResponse(personalSpaces, otherUsersSpaces, sharedSpaces); - } -} - From 84f2ee67269b033d17e89252f03fbfe41ec6280a Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:25:32 +0200 Subject: [PATCH 06/37] JAMES-2182 Inject NamespaceSupplier into its processor Guice implementors can then very easily overwrite the namespace definition --- .../imap/processor/DefaultProcessor.java | 2 +- .../imap/processor/NamespaceProcessor.java | 56 ++++------------- .../imap/processor/NamespaceSupplier.java | 61 +++++++++++++++++++ .../modules/protocols/IMAPServerModule.java | 2 + 4 files changed, 75 insertions(+), 46 deletions(-) create mode 100644 protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java index 7671f54f666..08a87d0dc9c 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java @@ -86,7 +86,7 @@ public static ImapProcessor createDefaultProcessor(ImapProcessor chainEndProcess builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper)); builder.add(new SearchProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory)); - builder.add(new NamespaceProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new NamespaceProcessor(mailboxManager, statusResponseFactory, metricFactory, new NamespaceSupplier.Default())); builder.add(new FetchProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new StartTLSProcessor(statusResponseFactory)); builder.add(new UnselectProcessor(mailboxManager, statusResponseFactory, metricFactory)); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java index 03ad9e5f52b..1541a9ec860 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceProcessor.java @@ -20,8 +20,6 @@ import static org.apache.james.imap.api.ImapConstants.SUPPORTS_NAMESPACES; -import java.util.ArrayList; -import java.util.Collection; import java.util.List; import jakarta.inject.Inject; @@ -46,59 +44,27 @@ public class NamespaceProcessor extends AbstractMailboxProcessor implements CapabilityImplementingProcessor { private static final List CAPS = ImmutableList.of(SUPPORTS_NAMESPACES); + private final NamespaceSupplier namespaceSupplier; + + @Inject - public NamespaceProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) { + public NamespaceProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, NamespaceSupplier namespaceSupplier) { super(NamespaceRequest.class, mailboxManager, factory, metricFactory); + this.namespaceSupplier = namespaceSupplier; } @Override protected Mono processRequestReactive(NamespaceRequest request, ImapSession session, Responder responder) { MailboxSession mailboxSession = session.getMailboxSession(); - List personalNamespaces = buildPersonalNamespaces(mailboxSession); - List otherUsersNamespaces = buildOtherUsersSpaces(mailboxSession); - List sharedNamespaces = buildSharedNamespaces(mailboxSession); - NamespaceResponse response = new NamespaceResponse(personalNamespaces, otherUsersNamespaces, sharedNamespaces); - responder.respond(response); - return unsolicitedResponses(session, responder, false) - .then(Mono.fromRunnable(() -> okComplete(request, responder))); - } - /** - * Builds personal namespaces from the session. - * - * @param mailboxSession - * not null - * @return personal namespaces, not null - */ - private List buildPersonalNamespaces(MailboxSession mailboxSession) { - final List personalSpaces = new ArrayList<>(); - String personal = ""; - personalSpaces.add(new NamespaceResponse.Namespace(personal, mailboxSession.getPathDelimiter())); - return personalSpaces; - } + NamespaceResponse response = new NamespaceResponse(namespaceSupplier.personalNamespaces(mailboxSession), + namespaceSupplier.otherUsersNamespaces(mailboxSession), + namespaceSupplier.sharedNamespaces(mailboxSession)); - private List buildOtherUsersSpaces(MailboxSession mailboxSession) { - final String otherUsersSpace = null; - final List otherUsersSpaces; - if (otherUsersSpace == null) { - otherUsersSpaces = null; - } else { - otherUsersSpaces = new ArrayList<>(1); - otherUsersSpaces.add(new NamespaceResponse.Namespace(otherUsersSpace, mailboxSession.getPathDelimiter())); - } - return otherUsersSpaces; - } + responder.respond(response); - private List buildSharedNamespaces(MailboxSession mailboxSession) { - List sharedNamespaces = null; - final Collection sharedSpaces = new ArrayList<>(); - if (!sharedSpaces.isEmpty()) { - sharedNamespaces = new ArrayList<>(sharedSpaces.size()); - for (String space : sharedSpaces) { - sharedNamespaces.add(new NamespaceResponse.Namespace(space, mailboxSession.getPathDelimiter())); - } - } - return sharedNamespaces; + return unsolicitedResponses(session, responder, false) + .then(Mono.fromRunnable(() -> okComplete(request, responder))); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java new file mode 100644 index 00000000000..90fe3c91ad0 --- /dev/null +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java @@ -0,0 +1,61 @@ +package org.apache.james.imap.processor; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.james.imap.message.response.NamespaceResponse; +import org.apache.james.imap.message.response.NamespaceResponse.Namespace; +import org.apache.james.mailbox.MailboxSession; + +import com.google.common.collect.ImmutableList; + +public interface NamespaceSupplier { + class Default implements NamespaceSupplier { + @Override + public Collection personalNamespaces(MailboxSession session) { + return ImmutableList.of(new NamespaceResponse.Namespace("", session.getPathDelimiter())); + } + + @Override + public Collection otherUsersNamespaces(MailboxSession session) { + return ImmutableList.of(); + } + + @Override + public Collection sharedNamespaces(MailboxSession session) { + return ImmutableList.of(); + } + } + + /** + * Gets the personal namespace for the current session.
+ * Note that though servers may offer multiple personal namespaces, support + * is not offered through this API. This decision may be revised if + * reasonable use cases emerge. + * + * @return Personal Namespace, not null + */ + Collection personalNamespaces(MailboxSession session); + + /** + * Gets the other users namespace for the current session.
+ * Note that though servers may offer multiple other users namespaces, + * support is not offered through this API. This decision may be revised if + * reasonable use cases emerge. + * + * @return Other Users Namespace or null when there is non available + */ + Collection otherUsersNamespaces(MailboxSession session); + + /** + * Iterates the Shared Namespaces available for the current + * session. + * + * @return not null though possibly empty + */ + Collection sharedNamespaces(MailboxSession session); +} diff --git a/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java b/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java index afa81f402c8..d5f33d874a2 100644 --- a/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java +++ b/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java @@ -56,6 +56,7 @@ import org.apache.james.imap.processor.CapabilityProcessor; import org.apache.james.imap.processor.DefaultProcessor; import org.apache.james.imap.processor.EnableProcessor; +import org.apache.james.imap.processor.NamespaceSupplier; import org.apache.james.imap.processor.PermitEnableCapabilityProcessor; import org.apache.james.imap.processor.SelectProcessor; import org.apache.james.imap.processor.StatusProcessor; @@ -104,6 +105,7 @@ protected void configure() { bind(SelectProcessor.class).in(Scopes.SINGLETON); bind(StatusProcessor.class).in(Scopes.SINGLETON); bind(EnableProcessor.class).in(Scopes.SINGLETON); + bind(NamespaceSupplier.class).to(NamespaceSupplier.Default.class).in(Scopes.SINGLETON); bind(MailboxTyper.class).to(DefaultMailboxTyper.class).in(Scopes.SINGLETON); Multibinder.newSetBinder(binder(), GuiceProbe.class).addBinding().to(ImapGuiceProbe.class); From 6ad4025f35ae906821689a37a93d8a7ab8725d77 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 09:31:16 +0200 Subject: [PATCH 07/37] JAMES-2182 ListProcessor: fix checkstyles --- .../java/org/apache/james/imap/processor/ListProcessor.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a74fb8c23f2..2875497ce43 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 @@ -138,7 +138,7 @@ protected Mono processRequestReactive(T request, ImapSession session, Resp } private Mono respond(ImapSession session, Responder responder, T request, MailboxSession mailboxSession) { - if (request.getMailboxPattern().length() == 0) { + if (request.getMailboxPattern().isEmpty()) { return Mono.fromRunnable(() -> respondNamespace(request.getBaseReferenceName(), responder, mailboxSession)); } else { return respondMailboxList(request, session, responder, mailboxSession); @@ -166,7 +166,7 @@ private void respondNamespace(String referenceName, Responder responder, Mailbox } private String computeReferenceRoot(String referenceName, MailboxSession mailboxSession) { - if (referenceName.length() > 0 && referenceName.charAt(0) == MailboxConstants.NAMESPACE_PREFIX_CHAR) { + if (!referenceName.isEmpty() && referenceName.charAt(0) == MailboxConstants.NAMESPACE_PREFIX_CHAR) { // A qualified reference name - get the root element int firstDelimiter = referenceName.indexOf(mailboxSession.getPathDelimiter()); if (firstDelimiter == -1) { From a79b7f4a6d06593eb340cc9bd9f51eeb854261c6 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 11:24:22 +0200 Subject: [PATCH 08/37] JAMES-2182 PathConverter: transform into an interface and inject This will allow customization of the IMAP folder naming strategy. --- .../apache/james/imap/main/PathConverter.java | 108 ++++++++++-------- .../imap/processor/AbstractAuthProcessor.java | 22 ++-- .../AbstractMessageRangeProcessor.java | 7 +- .../processor/AbstractSelectionProcessor.java | 7 +- .../james/imap/processor/AppendProcessor.java | 7 +- .../imap/processor/AuthenticateProcessor.java | 5 +- .../james/imap/processor/CopyProcessor.java | 5 +- .../james/imap/processor/CreateProcessor.java | 7 +- .../imap/processor/DefaultProcessor.java | 50 ++++---- .../imap/processor/DeleteACLProcessor.java | 6 +- .../james/imap/processor/DeleteProcessor.java | 7 +- .../imap/processor/ExamineProcessor.java | 5 +- .../james/imap/processor/GetACLProcessor.java | 7 +- .../imap/processor/GetMetadataProcessor.java | 8 +- .../imap/processor/GetQuotaRootProcessor.java | 6 +- .../james/imap/processor/ListProcessor.java | 11 +- .../imap/processor/ListRightsProcessor.java | 7 +- .../james/imap/processor/LoginProcessor.java | 5 +- .../james/imap/processor/MoveProcessor.java | 5 +- .../imap/processor/MyRightsProcessor.java | 7 +- .../james/imap/processor/RenameProcessor.java | 7 +- .../imap/processor/ReplaceProcessor.java | 7 +- .../james/imap/processor/SelectProcessor.java | 5 +- .../james/imap/processor/SetACLProcessor.java | 7 +- .../imap/processor/SetMetadataProcessor.java | 7 +- .../james/imap/processor/StatusProcessor.java | 7 +- .../imap/processor/SubscribeProcessor.java | 7 +- .../imap/processor/UnsubscribeProcessor.java | 7 +- .../james/imap/processor/XListProcessor.java | 9 +- .../james/imap/main/PathConverterTest.java | 2 +- .../AbstractSelectionProcessorTest.java | 3 +- .../imap/processor/CopyProcessorTest.java | 3 +- .../processor/DeleteACLProcessorTest.java | 3 +- .../imap/processor/GetACLProcessorTest.java | 3 +- .../processor/GetAnnotationProcessorTest.java | 3 +- .../processor/GetQuotaRootProcessorTest.java | 3 +- .../processor/ListRightsProcessorTest.java | 3 +- .../imap/processor/MoveProcessorTest.java | 5 +- .../imap/processor/SelectProcessorTest.java | 4 +- .../imap/processor/SetACLProcessorTest.java | 3 +- .../processor/SetMetadataProcessorTest.java | 3 +- .../modules/protocols/IMAPServerModule.java | 2 + .../imapserver/netty/IMAPServerTest.java | 1 + 43 files changed, 246 insertions(+), 150 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 ca5a7cbfd97..4e7828b62cc 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 @@ -32,69 +32,83 @@ import com.google.common.base.Strings; import com.google.common.collect.Iterables; -public class PathConverter { - - private static final int NAMESPACE = 0; +public interface PathConverter { + interface Factory { + PathConverter.Factory DEFAULT = new PathConverter.Factory.Default(); + + class Default implements Factory { + public PathConverter forSession(ImapSession session) { + return new PathConverter.Default(session); + } + } - public static PathConverter forSession(ImapSession session) { - return new PathConverter(session); + PathConverter forSession(ImapSession session); } - private final ImapSession session; + class Default implements PathConverter{ + private static final int NAMESPACE = 0; - private PathConverter(ImapSession session) { - this.session = session; - } - - public MailboxPath buildFullPath(String mailboxName) { - if (Strings.isNullOrEmpty(mailboxName)) { - return buildRelativePath(""); + public static PathConverter forSession(ImapSession session) { + return new PathConverter.Default(session); } - if (isAbsolute(mailboxName)) { - return buildAbsolutePath(mailboxName); - } else { - return buildRelativePath(mailboxName); + + private final ImapSession session; + + private Default(ImapSession session) { + this.session = session; } - } - private boolean isAbsolute(String mailboxName) { - Preconditions.checkArgument(!Strings.isNullOrEmpty(mailboxName)); - return mailboxName.charAt(0) == MailboxConstants.NAMESPACE_PREFIX_CHAR; - } + public MailboxPath buildFullPath(String mailboxName) { + if (Strings.isNullOrEmpty(mailboxName)) { + return buildRelativePath(""); + } + if (isAbsolute(mailboxName)) { + return buildAbsolutePath(mailboxName); + } else { + return buildRelativePath(mailboxName); + } + } - private MailboxPath buildRelativePath(String mailboxName) { - return buildMailboxPath(MailboxConstants.USER_NAMESPACE, session.getUserName(), mailboxName); - } + private boolean isAbsolute(String mailboxName) { + Preconditions.checkArgument(!Strings.isNullOrEmpty(mailboxName)); + return mailboxName.charAt(0) == MailboxConstants.NAMESPACE_PREFIX_CHAR; + } - private MailboxPath buildAbsolutePath(String absolutePath) { - char pathDelimiter = session.getMailboxSession().getPathDelimiter(); - List mailboxPathParts = Splitter.on(pathDelimiter).splitToList(absolutePath); - String namespace = mailboxPathParts.get(NAMESPACE); - String mailboxName = Joiner.on(pathDelimiter).join(Iterables.skip(mailboxPathParts, 1)); - return buildMailboxPath(namespace, retrieveUserName(namespace), mailboxName); - } + private MailboxPath buildRelativePath(String mailboxName) { + return buildMailboxPath(MailboxConstants.USER_NAMESPACE, session.getUserName(), mailboxName); + } - private Username retrieveUserName(String namespace) { - if (namespace.equals(MailboxConstants.USER_NAMESPACE)) { - return session.getUserName(); + private MailboxPath buildAbsolutePath(String absolutePath) { + char pathDelimiter = session.getMailboxSession().getPathDelimiter(); + List mailboxPathParts = Splitter.on(pathDelimiter).splitToList(absolutePath); + String namespace = mailboxPathParts.get(NAMESPACE); + String mailboxName = Joiner.on(pathDelimiter).join(Iterables.skip(mailboxPathParts, 1)); + return buildMailboxPath(namespace, retrieveUserName(namespace), mailboxName); } - throw new DeniedAccessOnSharedMailboxException(); - } - private MailboxPath buildMailboxPath(String namespace, Username user, String mailboxName) { - if (!namespace.equals(MailboxConstants.USER_NAMESPACE)) { + private Username retrieveUserName(String namespace) { + if (namespace.equals(MailboxConstants.USER_NAMESPACE)) { + return session.getUserName(); + } throw new DeniedAccessOnSharedMailboxException(); } - return new MailboxPath(namespace, user, sanitizeMailboxName(mailboxName)); - } - private String sanitizeMailboxName(String mailboxName) { - // use uppercase for INBOX - // See IMAP-349 - if (mailboxName.equalsIgnoreCase(MailboxConstants.INBOX)) { - return MailboxConstants.INBOX; + private MailboxPath buildMailboxPath(String namespace, Username user, String mailboxName) { + if (!namespace.equals(MailboxConstants.USER_NAMESPACE)) { + throw new DeniedAccessOnSharedMailboxException(); + } + return new MailboxPath(namespace, user, sanitizeMailboxName(mailboxName)); + } + + private String sanitizeMailboxName(String mailboxName) { + // use uppercase for INBOX + // See IMAP-349 + if (mailboxName.equalsIgnoreCase(MailboxConstants.INBOX)) { + return MailboxConstants.INBOX; + } + return mailboxName; } - return mailboxName; } + MailboxPath buildFullPath(String mailboxName); } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java index 508198c34a2..17ac2f13c67 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java @@ -56,14 +56,17 @@ public abstract class AbstractAuthProcessor extends Abstr private static final int MAX_FAILURES = 3; private ImapConfiguration imapConfiguration; + private final PathConverter.Factory pathConverterFactory; + @FunctionalInterface protected interface MailboxSessionAuthWithDelegationSupplier { MailboxSession get() throws MailboxException; } public AbstractAuthProcessor(Class acceptableClass, MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(acceptableClass, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -81,14 +84,13 @@ protected void doAuth(AuthenticationAttempt authenticationAttempt, ImapSession s authFailure = true; } if (!authFailure) { - final MailboxManager mailboxManager = getMailboxManager(); try { - final MailboxSession mailboxSession = mailboxManager.authenticate(authenticationAttempt.getAuthenticationId(), + final MailboxSession mailboxSession = getMailboxManager().authenticate(authenticationAttempt.getAuthenticationId(), authenticationAttempt.getPassword()) .withoutDelegation(); session.authenticated(); session.setMailboxSession(mailboxSession); - provisionInbox(session, mailboxManager, mailboxSession); + provisionInbox(session, getMailboxManager(), mailboxSession); AuditTrail.entry() .username(() -> mailboxSession.getUser().asString()) .sessionId(() -> session.sessionId().asString()) @@ -135,8 +137,8 @@ protected void doAuthWithDelegation(MailboxSessionAuthWithDelegationSupplier mai ImapSession session, ImapRequest request, Responder responder, Username authenticateUser, Username delegatorUser) { try { - final MailboxManager mailboxManager = getMailboxManager(); - final MailboxSession mailboxSession = mailboxSessionSupplier.get(); + MailboxManager mailboxManager = getMailboxManager(); + MailboxSession mailboxSession = mailboxSessionSupplier.get(); session.authenticated(); session.setMailboxSession(mailboxSession); AuditTrail.entry() @@ -179,7 +181,7 @@ protected void doAuthWithDelegation(MailboxSessionAuthWithDelegationSupplier mai } protected void provisionInbox(ImapSession session, MailboxManager mailboxManager, MailboxSession mailboxSession) throws MailboxException { - final MailboxPath inboxPath = PathConverter.forSession(session).buildFullPath(MailboxConstants.INBOX); + MailboxPath inboxPath = pathConverterFactory.forSession(session).buildFullPath(MailboxConstants.INBOX); if (Mono.from(mailboxManager.mailboxExists(inboxPath, mailboxSession)).block()) { LOGGER.debug("INBOX exists. No need to create it."); } else { @@ -194,7 +196,7 @@ protected void provisionInbox(ImapSession session, MailboxManager mailboxManager private void provisionMailbox(String mailbox, ImapSession session, MailboxManager mailboxManager, MailboxSession mailboxSession) throws MailboxException { - var mailboxPath = PathConverter.forSession(session).buildFullPath(mailbox); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailbox); if (Mono.from(mailboxManager.mailboxExists(mailboxPath, mailboxSession)).block()) { LOGGER.debug("{} exists. No need to create it.", mailbox); return; @@ -215,8 +217,8 @@ protected void manageFailureCount(ImapSession session, ImapRequest request, Resp } protected void manageFailureCount(ImapSession session, ImapRequest request, Responder responder, HumanReadableText failed) { - final Integer currentNumberOfFailures = (Integer) session.getAttribute(ATTRIBUTE_NUMBER_OF_FAILURES); - final int failures; + Integer currentNumberOfFailures = (Integer) session.getAttribute(ATTRIBUTE_NUMBER_OF_FAILURES); + int failures; if (currentNumberOfFailures == null) { failures = 1; } else { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMessageRangeProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMessageRangeProcessor.java index 5a36239ba1f..4fac7fc59f9 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMessageRangeProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractMessageRangeProcessor.java @@ -52,9 +52,12 @@ public abstract class AbstractMessageRangeProcessor extends AbstractMailboxProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractMessageRangeProcessor.class); + private final PathConverter.Factory pathConverterFactory; + public AbstractMessageRangeProcessor(Class acceptableClass, MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(acceptableClass, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } protected abstract Flux process(MailboxId targetMailbox, @@ -66,7 +69,7 @@ protected abstract Flux process(MailboxId targetMailbox, @Override protected Mono processRequestReactive(R request, ImapSession session, Responder responder) { - MailboxPath targetMailbox = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + MailboxPath targetMailbox = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); MailboxSession mailboxSession = session.getMailboxSession(); return Mono.from(getMailboxManager().mailboxExists(targetMailbox, mailboxSession)) 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 fb5153ff08c..6655204291c 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 @@ -82,13 +82,16 @@ abstract class AbstractSelectionProcessor CAPS = ImmutableList.of(ImapConstants.SUPPORTS_QRESYNC, ImapConstants.SUPPORTS_CONDSTORE); private final StatusResponseFactory statusResponseFactory; + + private final PathConverter.Factory pathConverterFactory; private final boolean openReadOnly; private final EventBus eventBus; - public AbstractSelectionProcessor(Class acceptableClass, MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, boolean openReadOnly, + public AbstractSelectionProcessor(Class acceptableClass, MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, PathConverter.Factory pathConverterFactory, boolean openReadOnly, MetricFactory metricFactory, EventBus eventBus) { super(acceptableClass, mailboxManager, statusResponseFactory, metricFactory); this.statusResponseFactory = statusResponseFactory; + this.pathConverterFactory = pathConverterFactory; this.openReadOnly = openReadOnly; this.eventBus = eventBus; @@ -97,7 +100,7 @@ public AbstractSelectionProcessor(Class acceptableClass, MailboxManager mailb @Override protected Mono processRequestReactive(R request, ImapSession session, Responder responder) { String mailboxName = request.getMailboxName(); - MailboxPath fullMailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName); + MailboxPath fullMailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName); return respond(session, fullMailboxPath, request, responder) .onErrorResume(MailboxNotFoundException.class, e -> { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java index ce0ce416ea2..494ed0e18cc 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AppendProcessor.java @@ -63,9 +63,12 @@ public class AppendProcessor extends AbstractMailboxProcessor imp private ImmutableList capabilities = ImmutableList.of(); + private final PathConverter.Factory pathConverterFactory; + @Inject - public AppendProcessor(MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, MetricFactory metricFactory) { + public AppendProcessor(MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(AppendRequest.class, mailboxManager, statusResponseFactory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -88,7 +91,7 @@ protected Mono processRequestReactive(AppendRequest request, ImapSession s final Content messageIn = request.getMessage().asMailboxContent(); final Date datetime = request.getDatetime(); final Flags flags = request.getFlags(); - final MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName); + final MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName); final MailboxManager mailboxManager = getMailboxManager(); session.stopDetectingCommandInjection(); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java index df500fe0bb9..89eaa5877ce 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AuthenticateProcessor.java @@ -36,6 +36,7 @@ import org.apache.james.imap.api.message.request.ImapRequest; import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.AuthenticateRequest; import org.apache.james.imap.message.request.IRAuthenticateRequest; import org.apache.james.imap.message.response.AuthenticateResponse; @@ -70,8 +71,8 @@ public class AuthenticateProcessor extends AbstractAuthProcessor { @Inject public CopyProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { - super(CopyRequest.class, mailboxManager, factory, metricFactory); + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { + super(CopyRequest.class, mailboxManager, factory, metricFactory, pathConverterFactory); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java index e4a4c248836..99fc531c353 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java @@ -41,17 +41,20 @@ public class CreateProcessor extends AbstractMailboxProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(CreateProcessor.class); + private final PathConverter.Factory pathConverterFactory; + @Inject public CreateProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(CreateRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override protected Mono processRequestReactive(CreateRequest request, ImapSession session, Responder responder) { MailboxManager mailboxManager = getMailboxManager(); - return Mono.fromCallable(() -> PathConverter.forSession(session).buildFullPath(request.getMailboxName())) + return Mono.fromCallable(() -> pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName())) .flatMap(mailboxPath -> Mono.from(mailboxManager.createMailboxReactive(mailboxPath, session.getMailboxSession())) .flatMap(mailboxId -> unsolicitedResponses(session, responder, false) .then(Mono.fromRunnable(() -> okComplete(request, StatusResponse.ResponseCode.mailboxId(mailboxId), responder)))) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java index 08a87d0dc9c..f96689a2e48 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java @@ -30,6 +30,7 @@ import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.api.process.MailboxTyper; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.processor.base.AbstractProcessor; import org.apache.james.imap.processor.base.ImapResponseMessageProcessor; import org.apache.james.imap.processor.fetch.FetchProcessor; @@ -55,6 +56,7 @@ public static ImapProcessor createDefaultProcessor(ImapProcessor chainEndProcess QuotaManager quotaManager, QuotaRootResolver quotaRootResolver, MetricFactory metricFactory) { + PathConverter.Factory pathConverterFactory = PathConverter.Factory.DEFAULT; ImmutableList.Builder builder = ImmutableList.builder(); CapabilityProcessor capabilityProcessor = new CapabilityProcessor(mailboxManager, statusResponseFactory, metricFactory); @@ -63,51 +65,51 @@ public static ImapProcessor createDefaultProcessor(ImapProcessor chainEndProcess builder.add(capabilityProcessor); builder.add(new IdProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new CheckProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new LoginProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new RenameProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new DeleteProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new CreateProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new LoginProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new RenameProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new DeleteProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new CreateProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); builder.add(new CloseProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new UnsubscribeProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory)); - builder.add(new SubscribeProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory)); - builder.add(new CopyProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new AuthenticateProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new UnsubscribeProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new SubscribeProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new CopyProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new AuthenticateProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); builder.add(new ExpungeProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new ReplaceProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new ExamineProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory)); - builder.add(new AppendProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new ReplaceProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new ExamineProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new AppendProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); builder.add(new StoreProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new NoopProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new IdleProcessor(mailboxManager, statusResponseFactory, metricFactory)); - StatusProcessor statusProcessor = new StatusProcessor(mailboxManager, statusResponseFactory, metricFactory); + StatusProcessor statusProcessor = new StatusProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory); builder.add(statusProcessor); builder.add(new LSubProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory)); - builder.add(new XListProcessor(mailboxManager, statusResponseFactory, mailboxTyper, metricFactory, subscriptionManager)); - builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper)); + builder.add(new XListProcessor(mailboxManager, statusResponseFactory, mailboxTyper, metricFactory, subscriptionManager, pathConverterFactory)); + builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper, pathConverterFactory)); builder.add(new SearchProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory)); + builder.add(new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory, pathConverterFactory)); builder.add(new NamespaceProcessor(mailboxManager, statusResponseFactory, metricFactory, new NamespaceSupplier.Default())); builder.add(new FetchProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new StartTLSProcessor(statusResponseFactory)); builder.add(new UnselectProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(new CompressProcessor(statusResponseFactory)); - builder.add(new GetACLProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new SetACLProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new DeleteACLProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new ListRightsProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new MyRightsProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new GetACLProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new SetACLProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new DeleteACLProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new ListRightsProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new MyRightsProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); EnableProcessor enableProcessor = new EnableProcessor(mailboxManager, statusResponseFactory, metricFactory, capabilityProcessor); builder.add(enableProcessor); builder.add(new GetQuotaProcessor(mailboxManager, statusResponseFactory, quotaManager, quotaRootResolver, metricFactory)); builder.add(new SetQuotaProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new GetQuotaRootProcessor(mailboxManager, statusResponseFactory, quotaRootResolver, quotaManager, metricFactory)); + builder.add(new GetQuotaRootProcessor(mailboxManager, statusResponseFactory, quotaRootResolver, quotaManager, metricFactory, pathConverterFactory)); builder.add(new ImapResponseMessageProcessor()); if (mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Move)) { - builder.add(new MoveProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new MoveProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); } if (mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Annotation)) { - builder.add(new SetMetadataProcessor(mailboxManager, statusResponseFactory, metricFactory)); - builder.add(new GetMetadataProcessor(mailboxManager, statusResponseFactory, metricFactory)); + builder.add(new SetMetadataProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); + builder.add(new GetMetadataProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory)); } ImmutableList processors = builder.build(); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteACLProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteACLProcessor.java index d0c0f33cb85..f34624337da 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteACLProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteACLProcessor.java @@ -57,10 +57,12 @@ public class DeleteACLProcessor extends AbstractMailboxProcessor CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_ACL); + private final PathConverter.Factory pathConverterFactory; @Inject - public DeleteACLProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) { + public DeleteACLProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(DeleteACLRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -69,7 +71,7 @@ protected Mono processRequestReactive(DeleteACLRequest request, ImapSessio final MailboxSession mailboxSession = session.getMailboxSession(); MailboxName mailboxName = request.getMailboxName(); MailboxACL.EntryKey entryKey = request.getEntryKey(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName.asString()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName.asString()); return checkLookupRight(request, responder, mailboxManager, mailboxSession, mailboxPath) .filter(FunctionalUtils.identityPredicate()) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java index 2007ffdd173..040d8c3f2b6 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DeleteProcessor.java @@ -43,15 +43,18 @@ public class DeleteProcessor extends AbstractMailboxProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(DeleteProcessor.class); + private final PathConverter.Factory pathConverterFactory; + @Inject - public DeleteProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) { + public DeleteProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(DeleteRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override protected Mono processRequestReactive(DeleteRequest request, ImapSession session, Responder responder) { MailboxManager mailboxManager = getMailboxManager(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); SelectedMailbox selected = session.getSelected(); return deselect(session, selected, mailboxPath) 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 d765c6f9eb1..6a03f55db80 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 @@ -28,6 +28,7 @@ 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.main.PathConverter; import org.apache.james.imap.message.request.ExamineRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.metrics.api.MetricFactory; @@ -37,8 +38,8 @@ public class ExamineProcessor extends AbstractSelectionProcessor @Inject public ExamineProcessor(MailboxManager mailboxManager, EventBus eventBus, StatusResponseFactory statusResponseFactory, - MetricFactory metricFactory) { - super(ExamineRequest.class, mailboxManager, statusResponseFactory, true, metricFactory, eventBus); + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { + super(ExamineRequest.class, mailboxManager, statusResponseFactory, pathConverterFactory, true, metricFactory, eventBus); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java index 11badd0c4f2..0b770ab3be0 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetACLProcessor.java @@ -61,10 +61,13 @@ public class GetACLProcessor extends AbstractMailboxProcessor imp private static final List CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_ACL); + private final PathConverter.Factory pathConverterFactory; + @Inject public GetACLProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(GetACLRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -73,7 +76,7 @@ protected Mono processRequestReactive(GetACLRequest request, ImapSession s MailboxSession mailboxSession = session.getMailboxSession(); MailboxName mailboxName = request.getMailboxName(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName.asString()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName.asString()); return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) .flatMap(Throwing.function(mailbox -> { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetMetadataProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetMetadataProcessor.java index 22fda735724..d8218a5a6d5 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetMetadataProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetMetadataProcessor.java @@ -65,10 +65,13 @@ public class GetMetadataProcessor extends AbstractMailboxProcessor capabilities; + private final PathConverter.Factory pathConverterFactory; + @Inject public GetMetadataProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(GetMetadataRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; this.capabilities = computeCapabilities(); } @@ -89,7 +92,8 @@ protected Mono processRequestReactive(GetMetadataRequest request, ImapSess String mailboxName = request.getMailboxName(); Optional maxsize = request.getMaxsize(); - return getMailboxAnnotations(session, request.getKeys(), request.getDepth(), PathConverter.forSession(session).buildFullPath(mailboxName)) + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName); + return getMailboxAnnotations(session, request.getKeys(), request.getDepth(), mailboxPath) .collectList() .flatMap(mailboxAnnotations -> Mono.fromCallable(() -> getMaxSizeValue(mailboxAnnotations, maxsize)) .flatMap(maximumOversizedSize -> Mono.fromRunnable(() -> respond(request, responder, mailboxName, mailboxAnnotations, maxsize, maximumOversizedSize))) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java index d176577f882..79bd8e75b3b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/GetQuotaRootProcessor.java @@ -60,13 +60,15 @@ public class GetQuotaRootProcessor extends AbstractMailboxProcessor processRequestReactive(GetQuotaRootRequest request, ImapSes final MailboxSession mailboxSession = session.getMailboxSession(); final MailboxManager mailboxManager = getMailboxManager(); - final MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + final MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); // First check mailbox exists return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) 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 2875497ce43..bc2e347ad8d 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 @@ -90,20 +90,23 @@ public class ListProcessor extends AbstractMailboxProcess private final StatusProcessor statusProcessor; protected final MailboxTyper mailboxTyper; + private final PathConverter.Factory pathConverterFactory; + @Inject public ListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, SubscriptionManager subscriptionManager, - StatusProcessor statusProcessor, MailboxTyper mailboxTyper) { - this((Class) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper); + StatusProcessor statusProcessor, MailboxTyper mailboxTyper, PathConverter.Factory pathConverterFactory) { + this((Class) ListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper, pathConverterFactory); } public ListProcessor(Class clazz, MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, SubscriptionManager subscriptionManager, - StatusProcessor statusProcessor, MailboxTyper mailboxTyper) { + StatusProcessor statusProcessor, MailboxTyper mailboxTyper, PathConverter.Factory pathConverterFactory) { super(clazz, mailboxManager, factory, metricFactory); this.subscriptionManager = subscriptionManager; this.statusProcessor = statusProcessor; this.mailboxTyper = mailboxTyper; + this.pathConverterFactory = pathConverterFactory; } @Override @@ -360,7 +363,7 @@ private MailboxPath computeBasePath(ImapSession session, String finalReferencena if (isRelative) { return MailboxPath.forUser(session.getUserName(), decodedName); } else { - return PathConverter.forSession(session).buildFullPath(decodedName); + return pathConverterFactory.forSession(session).buildFullPath(decodedName); } } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListRightsProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListRightsProcessor.java index 7c2c599006b..a4d750ab7ea 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ListRightsProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ListRightsProcessor.java @@ -59,10 +59,13 @@ public class ListRightsProcessor extends AbstractMailboxProcessor CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_ACL); + private final PathConverter.Factory pathConverterFactory; + @Inject public ListRightsProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(ListRightsRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -70,7 +73,7 @@ protected Mono processRequestReactive(ListRightsRequest request, ImapSessi MailboxManager mailboxManager = getMailboxManager(); MailboxSession mailboxSession = session.getMailboxSession(); MailboxName mailboxName = request.getMailboxName(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName.asString()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName.asString()); return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) .doOnNext(Throwing.consumer(mailbox -> { diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java index f435d8ec974..b4d20915f92 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/LoginProcessor.java @@ -28,6 +28,7 @@ import org.apache.james.imap.api.message.Capability; import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.LoginRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.metrics.api.MetricFactory; @@ -45,8 +46,8 @@ public class LoginProcessor extends AbstractAuthProcessor implemen private static final Logger LOGGER = LoggerFactory.getLogger(LoginProcessor.class); @Inject - public LoginProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) { - super(LoginRequest.class, mailboxManager, factory, metricFactory); + public LoginProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { + super(LoginRequest.class, mailboxManager, factory, metricFactory, pathConverterFactory); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/MoveProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/MoveProcessor.java index 018264cead5..6b66e9f937a 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/MoveProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/MoveProcessor.java @@ -29,6 +29,7 @@ import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.api.process.SelectedMailbox; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.MoveRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; @@ -47,8 +48,8 @@ public class MoveProcessor extends AbstractMessageRangeProcessor im @Inject public MoveProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { - super(MoveRequest.class, mailboxManager, factory, metricFactory); + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { + super(MoveRequest.class, mailboxManager, factory, metricFactory, pathConverterFactory); moveCapabilitySupported = mailboxManager.hasCapability(MailboxManager.MailboxCapabilities.Move); } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/MyRightsProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/MyRightsProcessor.java index ea2fc84d617..5baa4d9d6b8 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/MyRightsProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/MyRightsProcessor.java @@ -58,9 +58,12 @@ public class MyRightsProcessor extends AbstractMailboxProcessor private static final List CAPABILITIES = Collections.singletonList(ImapConstants.SUPPORTS_ACL); + private final PathConverter.Factory pathConverterFactory; + @Inject - public MyRightsProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory) { + public MyRightsProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(MyRightsRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -69,7 +72,7 @@ protected Mono processRequestReactive(MyRightsRequest request, ImapSession MailboxSession mailboxSession = session.getMailboxSession(); MailboxName mailboxName = request.getMailboxName(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName.asString()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName.asString()); return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) .doOnNext(Throwing.consumer(mailbox -> { Rfc4314Rights myRights = mailboxManager.myRights(mailbox.getMailboxEntity(), mailboxSession); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/RenameProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/RenameProcessor.java index 30b4c050433..eb61e4bcd12 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/RenameProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/RenameProcessor.java @@ -45,16 +45,19 @@ public class RenameProcessor extends AbstractMailboxProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(RenameProcessor.class); + private final PathConverter.Factory pathConverterFactory; + @Inject public RenameProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(RenameRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override protected Mono processRequestReactive(RenameRequest request, ImapSession session, Responder responder) { try { - PathConverter pathConverter = PathConverter.forSession(session); + PathConverter pathConverter = pathConverterFactory.forSession(session); MailboxPath existingPath = pathConverter.buildFullPath(request.getExistingName()); MailboxPath newPath = pathConverter.buildFullPath(request.getNewName()); MailboxManager mailboxManager = getMailboxManager(); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/ReplaceProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/ReplaceProcessor.java index 74db0d3f146..757aea38a3f 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/ReplaceProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/ReplaceProcessor.java @@ -68,8 +68,11 @@ public class ReplaceProcessor extends AbstractMailboxProcessor i private static final Logger LOGGER = LoggerFactory.getLogger(ReplaceProcessor.class); private static final ImmutableList CAPABILITIES = ImmutableList.of(Capability.of("REPLACE")); - public ReplaceProcessor(MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, MetricFactory metricFactory) { + private final PathConverter.Factory pathConverterFactory; + + public ReplaceProcessor(MailboxManager mailboxManager, StatusResponseFactory statusResponseFactory, MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(ReplaceRequest.class, mailboxManager, statusResponseFactory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -83,7 +86,7 @@ protected Mono processRequestReactive(ReplaceRequest request, ImapSession final Content messageIn = request.getMessage().asMailboxContent(); final Date datetime = request.getDatetime(); final Flags flags = request.getFlags(); - final MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName); + final MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName); final MailboxManager mailboxManager = getMailboxManager(); session.stopDetectingCommandInjection(); 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 721932fce11..fdfd4960e68 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 @@ -28,6 +28,7 @@ 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.main.PathConverter; import org.apache.james.imap.message.request.SelectRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.metrics.api.MetricFactory; @@ -37,8 +38,8 @@ public class SelectProcessor extends AbstractSelectionProcessor { @Inject public SelectProcessor(MailboxManager mailboxManager, EventBus eventBus, StatusResponseFactory statusResponseFactory, - MetricFactory metricFactory) { - super(SelectRequest.class, mailboxManager, statusResponseFactory, false, metricFactory, eventBus); + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { + super(SelectRequest.class, mailboxManager, statusResponseFactory, pathConverterFactory, false, metricFactory, eventBus); } @Override diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SetACLProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetACLProcessor.java index ace668d01a1..660686c288f 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SetACLProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetACLProcessor.java @@ -58,10 +58,13 @@ public class SetACLProcessor extends AbstractMailboxProcessor imp private static final List CAPABILITIES = ImmutableList.of(ImapConstants.SUPPORTS_ACL); + private final PathConverter.Factory pathConverterFactory; + @Inject public SetACLProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(SetACLRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -69,7 +72,7 @@ protected Mono processRequestReactive(SetACLRequest request, ImapSession s final MailboxManager mailboxManager = getMailboxManager(); final MailboxSession mailboxSession = session.getMailboxSession(); MailboxName mailboxName = request.getMailboxName(); - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(mailboxName.asString()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailboxName.asString()); return checkLookupRight(request, responder, mailboxManager, mailboxSession, mailboxPath) .filter(FunctionalUtils.identityPredicate()) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SetMetadataProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetMetadataProcessor.java index d47f87c097e..a3249960f5b 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SetMetadataProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SetMetadataProcessor.java @@ -57,10 +57,13 @@ public class SetMetadataProcessor extends AbstractMailboxProcessor capabilities; + private final PathConverter.Factory pathConverterFactory; + @Inject public SetMetadataProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(SetMetadataRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; this.capabilities = computeCapabilities(); } @@ -83,7 +86,7 @@ protected Mono processRequestReactive(SetMetadataRequest request, ImapSess final String mailboxName = request.getMailboxName(); return Mono.from(mailboxManager.updateAnnotationsReactive( - PathConverter.forSession(session).buildFullPath(mailboxName), + pathConverterFactory.forSession(session).buildFullPath(mailboxName), mailboxSession, request.getMailboxAnnotations())) .then(Mono.fromRunnable(() -> okComplete(request, responder)).then()) .doOnEach(logOnError(MailboxException.class, 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 59314fcd682..a4aafb278e8 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 @@ -75,12 +75,15 @@ public class StatusProcessor extends AbstractMailboxProcessor implements CapabilityImplementingProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(StatusProcessor.class); + + private final PathConverter.Factory pathConverterFactory; private ImapConfiguration imapConfiguration; @Inject public StatusProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(StatusRequest.class, mailboxManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override @@ -96,7 +99,7 @@ public List getImplementedCapabilities(ImapSession session) { @Override protected Mono processRequestReactive(StatusRequest request, ImapSession session, Responder responder) { - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); MailboxSession mailboxSession = session.getMailboxSession(); StatusDataItems statusDataItems = request.getStatusDataItems(); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SubscribeProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SubscribeProcessor.java index 096beefee15..eb52896eddb 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SubscribeProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SubscribeProcessor.java @@ -42,15 +42,18 @@ public class SubscribeProcessor extends AbstractSubscriptionProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(SubscribeProcessor.class); + private final PathConverter.Factory pathConverterFactory; + @Inject public SubscribeProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(SubscribeRequest.class, mailboxManager, subscriptionManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override protected Mono doProcessRequest(SubscribeRequest request, ImapSession session, Responder responder) { - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); MailboxSession mailboxSession = session.getMailboxSession(); return Mono.from(getSubscriptionManager().subscribeReactive(mailboxPath, mailboxSession)) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/UnsubscribeProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/UnsubscribeProcessor.java index c39ab1b263f..8f6ffdcdf3a 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/UnsubscribeProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/UnsubscribeProcessor.java @@ -43,15 +43,18 @@ public class UnsubscribeProcessor extends AbstractSubscriptionProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(UnsubscribeProcessor.class); + private final PathConverter.Factory pathConverterFactory; + @Inject public UnsubscribeProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(UnsubscribeRequest.class, mailboxManager, subscriptionManager, factory, metricFactory); + this.pathConverterFactory = pathConverterFactory; } @Override protected Mono doProcessRequest(UnsubscribeRequest request, ImapSession session, Responder responder) { - MailboxPath mailboxPath = PathConverter.forSession(session).buildFullPath(request.getMailboxName()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); MailboxSession mailboxSession = session.getMailboxSession(); return Mono.from(getSubscriptionManager().unsubscribeReactive(mailboxPath, mailboxSession)) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java index 3ec8b19b2c7..a7eddc25e31 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/XListProcessor.java @@ -31,6 +31,7 @@ import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.api.process.MailboxType; import org.apache.james.imap.api.process.MailboxTyper; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.ListRequest; import org.apache.james.imap.message.request.XListRequest; import org.apache.james.imap.message.response.XListResponse; @@ -51,13 +52,13 @@ public class XListProcessor extends ListProcessor implements Capab @Inject public XListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MetricFactory metricFactory, - SubscriptionManager subscriptionManager) { - this(mailboxManager, factory, null, metricFactory, subscriptionManager); + SubscriptionManager subscriptionManager, PathConverter.Factory pathConverterFactory) { + this(mailboxManager, factory, null, metricFactory, subscriptionManager, pathConverterFactory); } public XListProcessor(MailboxManager mailboxManager, StatusResponseFactory factory, MailboxTyper mailboxTyper, - MetricFactory metricFactory, SubscriptionManager subscriptionManager) { - super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, null, mailboxTyper); + MetricFactory metricFactory, SubscriptionManager subscriptionManager, PathConverter.Factory pathConverterFactory) { + super(XListRequest.class, mailboxManager, factory, metricFactory, subscriptionManager, null, mailboxTyper, pathConverterFactory); } @Override 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 e6bc9b75c4b..93d2970bd95 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 @@ -44,7 +44,7 @@ class PathConverterTest { void setUp() { imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USERNAME); - pathConverter = PathConverter.forSession(imapSession); + pathConverter = PathConverter.Factory.DEFAULT.forSession(imapSession); imapSession.setMailboxSession(mailboxSession); } diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/AbstractSelectionProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/AbstractSelectionProcessorTest.java index 314a37f9dd6..67f886a2fe9 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/AbstractSelectionProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/AbstractSelectionProcessorTest.java @@ -30,6 +30,7 @@ import org.apache.james.imap.api.message.UidRange; import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.SelectedMailbox; +import org.apache.james.imap.main.PathConverter; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MessageUid; import org.apache.james.metrics.api.MetricFactory; @@ -45,7 +46,7 @@ public void setUp() { EventBus eventBus = null; StatusResponseFactory statusResponseFactory = null; MetricFactory metricFactory = null; - testee = new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory); + testee = new SelectProcessor(mailboxManager, eventBus, statusResponseFactory, metricFactory, PathConverter.Factory.DEFAULT); } @Test diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java index 0fd12e94455..66a18ba3970 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/CopyProcessorTest.java @@ -40,6 +40,7 @@ import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.api.process.SelectedMailbox; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.CopyRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; @@ -86,7 +87,7 @@ void setUp() { imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USERNAME); - testee = new CopyProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory()); + testee = new CopyProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); imapSession.authenticated(); imapSession.setMailboxSession(mailboxSession); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/DeleteACLProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/DeleteACLProcessorTest.java index 88233e5a50a..4f3a1dbdb68 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/DeleteACLProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/DeleteACLProcessorTest.java @@ -33,6 +33,7 @@ import org.apache.james.imap.api.message.response.ImapResponseMessage; import org.apache.james.imap.api.process.ImapProcessor.Responder; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.MailboxName; import org.apache.james.imap.message.request.DeleteACLRequest; import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; @@ -81,7 +82,7 @@ void setUp() throws Exception { Object[] args = invocation.getArguments(); return (Mono) args[0]; }); - subject = new DeleteACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory()); + subject = new DeleteACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USER_1); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java index 7968cea29b8..339cb3369c1 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetACLProcessorTest.java @@ -36,6 +36,7 @@ import org.apache.james.imap.api.message.response.ImapResponseMessage; import org.apache.james.imap.api.process.ImapProcessor.Responder; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.MailboxName; import org.apache.james.imap.message.request.GetACLRequest; import org.apache.james.imap.message.response.ACLResponse; @@ -83,7 +84,7 @@ public void setUp() throws Exception { Object[] args = invocation.getArguments(); return (Mono) args[0]; }); - subject = new GetACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory()); + subject = new GetACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USER_1); MessageManager messageManager = mock(MessageManager.class); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java index 9d15f391b3b..4a1453b3e36 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetAnnotationProcessorTest.java @@ -43,6 +43,7 @@ import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.GetMetadataRequest; import org.apache.james.imap.message.request.GetMetadataRequest.Depth; import org.apache.james.imap.message.response.MetadataResponse; @@ -126,7 +127,7 @@ void setUp() { MockitoAnnotations.initMocks(this); initAndMockData(); - processor = new GetMetadataProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory()); + processor = new GetMetadataProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); } @Test diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetQuotaRootProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetQuotaRootProcessorTest.java index be0fa6b84cd..131360dd246 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/GetQuotaRootProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/GetQuotaRootProcessorTest.java @@ -42,6 +42,7 @@ import org.apache.james.imap.api.message.response.StatusResponse; import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.GetQuotaRootRequest; import org.apache.james.imap.message.response.QuotaResponse; import org.apache.james.imap.message.response.QuotaRootResponse; @@ -103,7 +104,7 @@ void setUp() throws Exception { .thenReturn(Mono.just(messageManager)); when(messageManager.getMailboxEntity()).thenReturn(mock(Mailbox.class)); testee = new GetQuotaRootProcessor(mockedMailboxManager, - statusResponseFactory, mockedQuotaRootResolver, mockedQuotaManager, new RecordingMetricFactory()); + statusResponseFactory, mockedQuotaRootResolver, mockedQuotaManager, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); } @Test diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/ListRightsProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/ListRightsProcessorTest.java index 839efe4d6ff..c069d7b4e30 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/ListRightsProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/ListRightsProcessorTest.java @@ -37,6 +37,7 @@ import org.apache.james.imap.api.message.response.ImapResponseMessage; import org.apache.james.imap.api.process.ImapProcessor.Responder; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.MailboxName; import org.apache.james.imap.message.request.ListRightsRequest; import org.apache.james.imap.message.response.ListRightsResponse; @@ -90,7 +91,7 @@ public void setUp() throws Exception { Object[] args = invocation.getArguments(); return (Mono) args[0]; }); - subject = new ListRightsProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory()); + subject = new ListRightsProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USER_1); MessageManager messageManager = mock(MessageManager.class); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java index d5d315c67db..9e7f4cf9f3d 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/MoveProcessorTest.java @@ -41,6 +41,7 @@ import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.api.process.SelectedMailbox; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.MoveRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxManager.MailboxCapabilities; @@ -88,7 +89,7 @@ public void setUp() { mailboxSession = MailboxSessionUtil.create(USERNAME); when(mockMailboxManager.hasCapability(eq(MailboxCapabilities.Move))).thenReturn(true); - testee = new MoveProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory()); + testee = new MoveProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); verify(mockMailboxManager).hasCapability(MailboxCapabilities.Move); imapSession.authenticated(); @@ -103,7 +104,7 @@ void getImplementedCapabilitiesShouldContainMoveWhenSupportedByMailboxManager() @Test void getImplementedCapabilitiesShouldNotContainMoveWhenUnSupportedByMailboxManager() { when(mockMailboxManager.hasCapability(eq(MailboxCapabilities.Move))).thenReturn(false); - assertThat(new MoveProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory()) + assertThat(new MoveProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT) .getImplementedCapabilities(null)).isEmpty(); } diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/SelectProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/SelectProcessorTest.java index ddf910c2c17..a0e339baf05 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/SelectProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/SelectProcessorTest.java @@ -36,6 +36,7 @@ import org.apache.james.imap.encode.base.ImapResponseComposerImpl; import org.apache.james.imap.encode.main.DefaultImapEncoderFactory; import org.apache.james.imap.encode.main.DefaultLocalizer; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.main.ResponseEncoder; import org.apache.james.imap.message.request.AbstractMailboxSelectionRequest; import org.apache.james.imap.message.request.SelectRequest; @@ -68,7 +69,8 @@ void setUp() throws Exception { testee = new SelectProcessor(mailboxManager, integrationResources.getEventBus(), new UnpooledStatusResponseFactory(), - new RecordingMetricFactory()); + new RecordingMetricFactory(), + PathConverter.Factory.DEFAULT); mailboxSession = mailboxManager.createSystemSession(Username.of("bob")); mailboxManager.createMailbox(MailboxPath.inbox(BOB), mailboxSession); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/SetACLProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetACLProcessorTest.java index 207f72624ce..354b8dd5627 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/SetACLProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetACLProcessorTest.java @@ -34,6 +34,7 @@ import org.apache.james.imap.api.message.response.ImapResponseMessage; import org.apache.james.imap.api.process.ImapProcessor.Responder; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.MailboxName; import org.apache.james.imap.message.request.SetACLRequest; import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; @@ -88,7 +89,7 @@ void setUp() throws Exception { Object[] args = invocation.getArguments(); return (Mono) args[0]; }); - subject = new SetACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory()); + subject = new SetACLProcessor(mailboxManager, statusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USER_1); MessageManager messageManager = mock(MessageManager.class); diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/SetMetadataProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetMetadataProcessorTest.java index a61bca7656e..4a7c580a1a6 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/SetMetadataProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/SetMetadataProcessorTest.java @@ -40,6 +40,7 @@ import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.SetMetadataRequest; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; @@ -103,7 +104,7 @@ private void initAndMockData() { void setUp() { MockitoAnnotations.initMocks(this); initAndMockData(); - processor = new SetMetadataProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory()); + processor = new SetMetadataProcessor(mockMailboxManager, mockStatusResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); } @Test diff --git a/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java b/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java index d5f33d874a2..617f75630f2 100644 --- a/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java +++ b/server/container/guice/protocols/imap/src/main/java/org/apache/james/modules/protocols/IMAPServerModule.java @@ -50,6 +50,7 @@ import org.apache.james.imap.encode.main.DefaultImapEncoderFactory; import org.apache.james.imap.encode.main.DefaultLocalizer; import org.apache.james.imap.main.DefaultImapDecoderFactory; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.response.UnpooledStatusResponseFactory; import org.apache.james.imap.processor.AuthenticateProcessor; import org.apache.james.imap.processor.CapabilityImplementingProcessor; @@ -106,6 +107,7 @@ protected void configure() { bind(StatusProcessor.class).in(Scopes.SINGLETON); bind(EnableProcessor.class).in(Scopes.SINGLETON); bind(NamespaceSupplier.class).to(NamespaceSupplier.Default.class).in(Scopes.SINGLETON); + bind(PathConverter.Factory.class).to(PathConverter.Factory.Default.class).in(Scopes.SINGLETON); bind(MailboxTyper.class).to(DefaultMailboxTyper.class).in(Scopes.SINGLETON); Multibinder.newSetBinder(binder(), GuiceProbe.class).addBinding().to(ImapGuiceProbe.class); diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java index c39d54c0322..76f8a4d0afd 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java @@ -21,6 +21,7 @@ import static jakarta.mail.Flags.Flag.ANSWERED; import static jakarta.mail.Folder.READ_WRITE; +import static org.apache.james.jmap.JMAPTestingConstants.BOB; import static org.apache.james.jmap.JMAPTestingConstants.LOCALHOST_IP; import static org.apache.james.jwt.OidcTokenFixture.INTROSPECTION_RESPONSE; import static org.apache.james.mailbox.MessageManager.FlagsUpdateMode.REPLACE; From e32080dddbc5a5ed5a1499a4c0c2a232dd9af47d Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 11:30:03 +0200 Subject: [PATCH 09/37] JAMES-2182 PathConverter: Add the reverse transformation MailboxPath -> mailboxname - Centralize in one place the knowledge of encoding MailboxPath into IMAP --- .../apache/james/imap/main/PathConverter.java | 33 +++++++++++++++++++ .../processor/AbstractMailboxProcessor.java | 29 ---------------- .../james/imap/processor/ListProcessor.java | 6 ++-- 3 files changed, 36 insertions(+), 32 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 4e7828b62cc..042c4b2ae12 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 @@ -108,7 +108,40 @@ private String sanitizeMailboxName(String mailboxName) { } return mailboxName; } + + /** + * Joins the elements of a mailboxPath together and returns them as a string + */ + private String joinMailboxPath(MailboxPath mailboxPath, char delimiter) { + StringBuilder sb = new StringBuilder(); + if (mailboxPath.getNamespace() != null && !mailboxPath.getNamespace().equals("")) { + sb.append(mailboxPath.getNamespace()); + } + if (mailboxPath.getUser() != null && !mailboxPath.getUser().equals("")) { + if (sb.length() > 0) { + sb.append(delimiter); + } + sb.append(mailboxPath.getUser().asString()); + } + if (mailboxPath.getName() != null && !mailboxPath.getName().equals("")) { + if (sb.length() > 0) { + sb.append(delimiter); + } + sb.append(mailboxPath.getName()); + } + return sb.toString(); + } + + public String mailboxName(boolean relative, MailboxPath path, char delimiter) { + if (relative) { + return path.getName(); + } else { + return joinMailboxPath(path, delimiter); + } + } } MailboxPath buildFullPath(String mailboxName); + + String mailboxName(boolean relative, MailboxPath path, char delimiter); } 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 42278c46455..439444f15ca 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 @@ -363,36 +363,7 @@ protected Mono processRequestReactive(R request, ImapSession session, Resp .then(); } - /** - * Joins the elements of a mailboxPath together and returns them as a string - */ - private String joinMailboxPath(MailboxPath mailboxPath, char delimiter) { - StringBuilder sb = new StringBuilder(); - if (mailboxPath.getNamespace() != null && !mailboxPath.getNamespace().equals("")) { - sb.append(mailboxPath.getNamespace()); - } - if (mailboxPath.getUser() != null && !mailboxPath.getUser().equals("")) { - if (sb.length() > 0) { - sb.append(delimiter); - } - sb.append(mailboxPath.getUser().asString()); - } - if (mailboxPath.getName() != null && !mailboxPath.getName().equals("")) { - if (sb.length() > 0) { - sb.append(delimiter); - } - sb.append(mailboxPath.getName()); - } - return sb.toString(); - } - protected String mailboxName(boolean relative, MailboxPath path, char delimiter) { - if (relative) { - return path.getName(); - } else { - return joinMailboxPath(path, delimiter); - } - } protected MailboxManager getMailboxManager() { return mailboxManager; 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 bc2e347ad8d..8657d8c4947 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 @@ -224,7 +224,7 @@ private Mono processWithoutSubscribed(ImapSession session, T request, Resp responder.respond( createResponse(metaData.inferiors(), metaData.getSelectability(), - mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()), + pathConverterFactory.forSession(session).mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()), metaData.getHierarchyDelimiter(), mailboxType, isSubscribed.test(metaData.getPath()))); @@ -287,7 +287,7 @@ private Pair buildListResponse(ListRequest listReques .map(mailboxMetaData -> ListResponse.builder() .returnSubscribed(RETURN_SUBSCRIBED) .forMetaData(mailboxMetaData) - .name(mailboxName(relative, subscribed, mailboxMetaData.getHierarchyDelimiter())) + .name(pathConverterFactory.forSession(session).mailboxName(relative, subscribed, mailboxMetaData.getHierarchyDelimiter())) .returnNonExistent(!RETURN_NON_EXISTENT) .mailboxType(getMailboxType(listRequest, session, mailboxMetaData.getPath()))) .orElseGet(() -> ListResponse.builder().nonExitingSubscribedMailbox(subscribed)) @@ -311,7 +311,7 @@ private List> listRecursiveMatch(ImapSession ses MailboxMetaData metaData = pair.getValue(); ListResponse listResponse = ListResponse.builder() .forMetaData(metaData) - .name(mailboxName(relative, metaData.getPath(), metaData.getHierarchyDelimiter())) + .name(pathConverterFactory.forSession(session).mailboxName(relative, metaData.getPath(), metaData.getHierarchyDelimiter())) .childInfos(ListResponse.ChildInfo.SUBSCRIBED) .returnSubscribed(allSubscribedSearch.contains(pair.getKey())) .mailboxType(getMailboxType(listRequest, session, metaData.getPath())) From 5bf1a43989e83d2a0e99983354d8a46960832bf1 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 12:00:09 +0200 Subject: [PATCH 10/37] JAMES-2182 PathConverter: change arguments of mailboxName method --- .../java/org/apache/james/imap/main/PathConverter.java | 7 ++++--- .../org/apache/james/imap/processor/ListProcessor.java | 6 +++--- 2 files changed, 7 insertions(+), 6 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 042c4b2ae12..74bfc7335a6 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 @@ -23,6 +23,7 @@ import org.apache.james.core.Username; import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxPath; @@ -132,16 +133,16 @@ private String joinMailboxPath(MailboxPath mailboxPath, char delimiter) { return sb.toString(); } - public String mailboxName(boolean relative, MailboxPath path, char delimiter) { + public String mailboxName(boolean relative, MailboxPath path, MailboxSession session) { if (relative) { return path.getName(); } else { - return joinMailboxPath(path, delimiter); + return joinMailboxPath(path, session.getPathDelimiter()); } } } MailboxPath buildFullPath(String mailboxName); - String mailboxName(boolean relative, MailboxPath path, char delimiter); + String mailboxName(boolean relative, MailboxPath path, MailboxSession session); } 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 8657d8c4947..aa6f19cd5e4 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 @@ -224,7 +224,7 @@ private Mono processWithoutSubscribed(ImapSession session, T request, Resp responder.respond( createResponse(metaData.inferiors(), metaData.getSelectability(), - pathConverterFactory.forSession(session).mailboxName(isRelative, metaData.getPath(), metaData.getHierarchyDelimiter()), + pathConverterFactory.forSession(session).mailboxName(isRelative, metaData.getPath(), mailboxSession), metaData.getHierarchyDelimiter(), mailboxType, isSubscribed.test(metaData.getPath()))); @@ -287,7 +287,7 @@ private Pair buildListResponse(ListRequest listReques .map(mailboxMetaData -> ListResponse.builder() .returnSubscribed(RETURN_SUBSCRIBED) .forMetaData(mailboxMetaData) - .name(pathConverterFactory.forSession(session).mailboxName(relative, subscribed, mailboxMetaData.getHierarchyDelimiter())) + .name(pathConverterFactory.forSession(session).mailboxName(relative, subscribed, session.getMailboxSession())) .returnNonExistent(!RETURN_NON_EXISTENT) .mailboxType(getMailboxType(listRequest, session, mailboxMetaData.getPath()))) .orElseGet(() -> ListResponse.builder().nonExitingSubscribedMailbox(subscribed)) @@ -311,7 +311,7 @@ private List> listRecursiveMatch(ImapSession ses MailboxMetaData metaData = pair.getValue(); ListResponse listResponse = ListResponse.builder() .forMetaData(metaData) - .name(pathConverterFactory.forSession(session).mailboxName(relative, metaData.getPath(), metaData.getHierarchyDelimiter())) + .name(pathConverterFactory.forSession(session).mailboxName(relative, metaData.getPath(), mailboxSession)) .childInfos(ListResponse.ChildInfo.SUBSCRIBED) .returnSubscribed(allSubscribedSearch.contains(pair.getKey())) .mailboxType(getMailboxType(listRequest, session, metaData.getPath())) From 16e8ef7f1a4549a8a4a576fa2e8cf5e6497702a3 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 12:03:57 +0200 Subject: [PATCH 11/37] JAMES-2182 PathConverter: only mailboxes belonging to the user can be relative --- .../src/main/java/org/apache/james/imap/main/PathConverter.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 74bfc7335a6..7c62d6166fa 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 @@ -134,7 +134,7 @@ private String joinMailboxPath(MailboxPath mailboxPath, char delimiter) { } public String mailboxName(boolean relative, MailboxPath path, MailboxSession session) { - if (relative) { + if (relative && path.belongsTo(session)) { return path.getName(); } else { return joinMailboxPath(path, session.getPathDelimiter()); From d96933c7836149b1d790432b6a180d5bdd5dc35e Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 14:13:12 +0200 Subject: [PATCH 12/37] JAMES-2182 PathConverter: more unit tests --- .../apache/james/imap/main/PathConverter.java | 4 -- .../james/imap/main/PathConverterTest.java | 38 +++++++++++++++++++ 2 files changed, 38 insertions(+), 4 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 7c62d6166fa..09702031d1e 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 @@ -49,10 +49,6 @@ public PathConverter forSession(ImapSession session) { class Default implements PathConverter{ private static final int NAMESPACE = 0; - public static PathConverter forSession(ImapSession session) { - return new PathConverter.Default(session); - } - private final ImapSession session; private Default(ImapSession session) { 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 93d2970bd95..b98c995532a 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 @@ -34,7 +34,9 @@ class PathConverterTest { private static final Username USERNAME = Username.of("username"); + private static final Username USERNAME2 = Username.of("username2"); private static final char PATH_DELIMITER = '.'; + public static final boolean RELATIVE = true; private FakeImapSession imapSession; private MailboxSession mailboxSession; @@ -105,4 +107,40 @@ void buildFullPathShouldDenyMailboxPathNotBelongingToTheUser() { assertThatThrownBy(() -> pathConverter.buildFullPath("#any")) .isInstanceOf(DeniedAccessOnSharedMailboxException.class); } + + @Test + void mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, MailboxPath.forUser(USERNAME, "abc"), mailboxSession)) + .isEqualTo("abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, MailboxPath.forUser(USERNAME2, "abc"), mailboxSession)) + .isEqualTo("#private.username2.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, new MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession)) + .isEqualTo("#Shared.marketing.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME, "abc"), mailboxSession)) + .isEqualTo("#private.username.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME2, "abc"), mailboxSession)) + .isEqualTo("#private.username2.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, new MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession)) + .isEqualTo("#Shared.marketing.abc"); + } } From c3df25b2b6d0d330e56c93e045b2b6151afb08e4 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 14:32:21 +0200 Subject: [PATCH 13/37] JAMES-2182 PathConverter: handle encoding for mailboxes belonging to others --- .../imap/scripts/ListWithSharedMailbox.test | 2 +- .../apache/james/imap/scripts/Namespace.test | 2 +- .../apache/james/imap/main/PathConverter.java | 53 +++++++++++-------- .../imap/processor/NamespaceSupplier.java | 2 +- .../james/imap/main/PathConverterTest.java | 18 +++++-- 5 files changed, 49 insertions(+), 28 deletions(-) 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 2d01957135a..eba7f0dd17d 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 @@ -24,7 +24,7 @@ S: \* LIST \(\\HasNoChildren\) \"\.\" \"INBOX\" S: a0 OK LIST completed. C: a1 LIST "#private" "%" -S: \* LIST \(\\HasNoChildren\) \"\.\" \"#private.imapuser.INBOX\" +S: \* LIST \(\\HasNoChildren\) \"\.\" \"#private.INBOX\" S: a1 OK LIST completed. C: a3 LIST "#private" sharedMailbox* diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Namespace.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Namespace.test index 42cc371ccb1..bb67da2bca7 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Namespace.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Namespace.test @@ -17,6 +17,6 @@ # under the License. # ################################################################ C: A001 NAMESPACE -S: \* NAMESPACE \(\((\"\"|\"#private\.\") \".\"\)\) NIL NIL +S: \* NAMESPACE \(\((\"\"|\"#private\.\") \".\"\)\) \(\(\"#user.\" \".\"\)\) NIL S: A001 OK NAMESPACE 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 09702031d1e..151e503f393 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 @@ -21,6 +21,7 @@ import java.util.List; +import org.apache.james.core.Domain; import org.apache.james.core.Username; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.mailbox.MailboxSession; @@ -48,6 +49,7 @@ public PathConverter forSession(ImapSession session) { class Default implements PathConverter{ private static final int NAMESPACE = 0; + private static final int USER = 1; private final ImapSession session; @@ -72,29 +74,29 @@ private boolean isAbsolute(String mailboxName) { } private MailboxPath buildRelativePath(String mailboxName) { - return buildMailboxPath(MailboxConstants.USER_NAMESPACE, session.getUserName(), mailboxName); + return new MailboxPath(MailboxConstants.USER_NAMESPACE, session.getUserName(), sanitizeMailboxName(mailboxName)); } private MailboxPath buildAbsolutePath(String absolutePath) { - char pathDelimiter = session.getMailboxSession().getPathDelimiter(); - List mailboxPathParts = Splitter.on(pathDelimiter).splitToList(absolutePath); - String namespace = mailboxPathParts.get(NAMESPACE); - String mailboxName = Joiner.on(pathDelimiter).join(Iterables.skip(mailboxPathParts, 1)); - return buildMailboxPath(namespace, retrieveUserName(namespace), mailboxName); + MailboxSession mailboxSession = session.getMailboxSession(); + return asMailboxPath(Splitter.on(mailboxSession.getPathDelimiter()).splitToList(absolutePath), mailboxSession); } - private Username retrieveUserName(String namespace) { - if (namespace.equals(MailboxConstants.USER_NAMESPACE)) { - return session.getUserName(); - } - throw new DeniedAccessOnSharedMailboxException(); - } + private MailboxPath asMailboxPath(List mailboxPathParts, MailboxSession session) { + String namespace = mailboxPathParts.get(NAMESPACE); + if (namespace.equalsIgnoreCase("#private")) { + 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"); + String username = mailboxPathParts.get(USER); + Username user = Username.from(username, 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)); - private MailboxPath buildMailboxPath(String namespace, Username user, String mailboxName) { - if (!namespace.equals(MailboxConstants.USER_NAMESPACE)) { + } else { throw new DeniedAccessOnSharedMailboxException(); } - return new MailboxPath(namespace, user, sanitizeMailboxName(mailboxName)); } private String sanitizeMailboxName(String mailboxName) { @@ -109,20 +111,27 @@ private String sanitizeMailboxName(String mailboxName) { /** * Joins the elements of a mailboxPath together and returns them as a string */ - private String joinMailboxPath(MailboxPath mailboxPath, char delimiter) { + private String joinMailboxPath(MailboxPath mailboxPath, MailboxSession session) { StringBuilder sb = new StringBuilder(); if (mailboxPath.getNamespace() != null && !mailboxPath.getNamespace().equals("")) { - sb.append(mailboxPath.getNamespace()); + if (mailboxPath.getNamespace().equalsIgnoreCase(MailboxConstants.USER_NAMESPACE) + && !mailboxPath.belongsTo(session)) { + sb.append("#user"); + } else { + sb.append(mailboxPath.getNamespace()); + } } if (mailboxPath.getUser() != null && !mailboxPath.getUser().equals("")) { - if (sb.length() > 0) { - sb.append(delimiter); + if (!mailboxPath.belongsTo(session)) { + if (sb.length() > 0) { + sb.append(session.getPathDelimiter()); + } + sb.append(mailboxPath.getUser().asString()); } - sb.append(mailboxPath.getUser().asString()); } if (mailboxPath.getName() != null && !mailboxPath.getName().equals("")) { if (sb.length() > 0) { - sb.append(delimiter); + sb.append(session.getPathDelimiter()); } sb.append(mailboxPath.getName()); } @@ -133,7 +142,7 @@ public String mailboxName(boolean relative, MailboxPath path, MailboxSession ses if (relative && path.belongsTo(session)) { return path.getName(); } else { - return joinMailboxPath(path, session.getPathDelimiter()); + return joinMailboxPath(path, session); } } } diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java index 90fe3c91ad0..c706266cef8 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java @@ -19,7 +19,7 @@ public Collection personalNamespaces(MailboxSession session) { @Override public Collection otherUsersNamespaces(MailboxSession session) { - return ImmutableList.of(); + return ImmutableList.of(new NamespaceResponse.Namespace("#user", session.getPathDelimiter())); } @Override 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 b98c995532a..eedc447b975 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 @@ -102,6 +102,18 @@ void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() { .isEqualTo(MailboxPath.forUser(USERNAME, mailboxName)); } + @Test + void buildFullPathShouldAcceptAbsoluteOtherUserPath() { + assertThat(pathConverter.buildFullPath("#user.username2.abc")) + .isEqualTo(MailboxPath.forUser(USERNAME2, "abc")); + } + + @Test + void buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() { + assertThat(pathConverter.buildFullPath("#user.username2.abc.def")) + .isEqualTo(MailboxPath.forUser(USERNAME2, "abc.def")); + } + @Test void buildFullPathShouldDenyMailboxPathNotBelongingToTheUser() { assertThatThrownBy(() -> pathConverter.buildFullPath("#any")) @@ -117,7 +129,7 @@ void mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() { @Test void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() { assertThat(pathConverter.mailboxName(RELATIVE, MailboxPath.forUser(USERNAME2, "abc"), mailboxSession)) - .isEqualTo("#private.username2.abc"); + .isEqualTo("#user.username2.abc"); } @Test @@ -129,13 +141,13 @@ void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() { @Test void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() { assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME, "abc"), mailboxSession)) - .isEqualTo("#private.username.abc"); + .isEqualTo("#private.abc"); } @Test void mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() { assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME2, "abc"), mailboxSession)) - .isEqualTo("#private.username2.abc"); + .isEqualTo("#user.username2.abc"); } @Test From 66f6b8028535cfd3d018b33f0fafc78327a8ad08 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 14:39:27 +0200 Subject: [PATCH 14/37] JAMES-2182 PathConverter: handle virtual hosting As we can only access resources within a single domain the domain is implicit and thus do not need to be repeated in the IMAP wording. --- .../apache/james/imap/main/PathConverter.java | 12 +- .../james/imap/main/PathConverterTest.java | 114 ++++++++++++++++++ 2 files changed, 120 insertions(+), 6 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 151e503f393..3bbcbf469fa 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 @@ -113,7 +113,7 @@ private String sanitizeMailboxName(String mailboxName) { */ private String joinMailboxPath(MailboxPath mailboxPath, MailboxSession session) { StringBuilder sb = new StringBuilder(); - if (mailboxPath.getNamespace() != null && !mailboxPath.getNamespace().equals("")) { + if (mailboxPath.getNamespace() != null && !mailboxPath.getNamespace().isEmpty()) { if (mailboxPath.getNamespace().equalsIgnoreCase(MailboxConstants.USER_NAMESPACE) && !mailboxPath.belongsTo(session)) { sb.append("#user"); @@ -121,16 +121,16 @@ private String joinMailboxPath(MailboxPath mailboxPath, MailboxSession session) sb.append(mailboxPath.getNamespace()); } } - if (mailboxPath.getUser() != null && !mailboxPath.getUser().equals("")) { + if (mailboxPath.getUser() != null) { if (!mailboxPath.belongsTo(session)) { - if (sb.length() > 0) { + if (!sb.isEmpty()) { sb.append(session.getPathDelimiter()); } - sb.append(mailboxPath.getUser().asString()); + sb.append(mailboxPath.getUser().getLocalPart()); } } - if (mailboxPath.getName() != null && !mailboxPath.getName().equals("")) { - if (sb.length() > 0) { + if (mailboxPath.getName() != null && !mailboxPath.getName().isEmpty()) { + if (!sb.isEmpty()) { sb.append(session.getPathDelimiter()); } sb.append(mailboxPath.getName()); 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 eedc447b975..f314f32949b 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 @@ -29,12 +29,16 @@ import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxPath; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; class PathConverterTest { private static final Username USERNAME = Username.of("username"); private static final Username USERNAME2 = Username.of("username2"); + + private static final Username USERNAME_WITH_MAIL = Username.of("username@apache.org"); + private static final Username USERNAME2_WITH_MAIL = Username.of("username2@apache.org"); private static final char PATH_DELIMITER = '.'; public static final boolean RELATIVE = true; @@ -155,4 +159,114 @@ void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() { assertThat(pathConverter.mailboxName(!RELATIVE, new MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession)) .isEqualTo("#Shared.marketing.abc"); } + @Nested + class WithEmail{ + @BeforeEach + void setUp() { + imapSession = new FakeImapSession(); + mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_MAIL); + pathConverter = PathConverter.Factory.DEFAULT.forSession(imapSession); + imapSession.setMailboxSession(mailboxSession); + } + + @Test + void buildFullPathShouldAcceptNull() { + assertThat(pathConverter.buildFullPath(null)) + .isEqualTo(new MailboxPath("", USERNAME_WITH_MAIL, "")); + } + + @Test + void buildPathShouldAcceptEmpty() { + assertThat(pathConverter.buildFullPath("")) + .isEqualTo(new MailboxPath("", USERNAME_WITH_MAIL, "")); + } + + @Test + void buildPathShouldAcceptRelativeMailboxName() { + String mailboxName = "mailboxName"; + assertThat(pathConverter.buildFullPath(mailboxName)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, mailboxName)); + } + + @Test + void buildFullPathShouldAcceptUserNamespace() { + assertThat(pathConverter.buildFullPath(MailboxConstants.USER_NAMESPACE)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, "")); + } + + @Test + void buildFullPathShouldAcceptUserNamespaceAndDelimiter() { + assertThat(pathConverter.buildFullPath(MailboxConstants.USER_NAMESPACE + PATH_DELIMITER)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, "")); + } + + @Test + void buildFullPathShouldAcceptFullAbsoluteUserPath() { + String mailboxName = "mailboxName"; + assertThat(pathConverter.buildFullPath(MailboxConstants.USER_NAMESPACE + PATH_DELIMITER + mailboxName)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, mailboxName)); + } + + @Test + void buildFullPathShouldAcceptRelativePathWithSubFolder() { + String mailboxName = "mailboxName" + PATH_DELIMITER + "subFolder"; + assertThat(pathConverter.buildFullPath(mailboxName)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, mailboxName)); + } + + @Test + void buildFullPathShouldAcceptAbsoluteUserPathWithSubFolder() { + String mailboxName = "mailboxName.subFolder"; + assertThat(pathConverter.buildFullPath(MailboxConstants.USER_NAMESPACE + PATH_DELIMITER + mailboxName)) + .isEqualTo(MailboxPath.forUser(USERNAME_WITH_MAIL, mailboxName)); + } + + @Test + void buildFullPathShouldAcceptAbsoluteOtherUserPath() { + assertThat(pathConverter.buildFullPath("#user.username2.abc")) + .isEqualTo(MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc")); + } + + @Test + void buildFullPathShouldAcceptAbsoluteOtherUserPathWithSubfolder() { + assertThat(pathConverter.buildFullPath("#user.username2.abc.def")) + .isEqualTo(MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc.def")); + } + + @Test + void mailboxNameShouldReturnNameOnlyWhenRelativeAndUserMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxSession)) + .isEqualTo("abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxSession)) + .isEqualTo("#user.username2.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenRelativeAndSharedMailbox() { + assertThat(pathConverter.mailboxName(RELATIVE, new MailboxPath("#Shared", Username.of("marketing@apache.org"), "abc"), mailboxSession)) + .isEqualTo("#Shared.marketing.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndUserMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME_WITH_MAIL, "abc"), mailboxSession)) + .isEqualTo("#private.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndOtherUserMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, MailboxPath.forUser(USERNAME2_WITH_MAIL, "abc"), mailboxSession)) + .isEqualTo("#user.username2.abc"); + } + + @Test + void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() { + assertThat(pathConverter.mailboxName(!RELATIVE, new MailboxPath("#Shared", Username.of("marketing@apache.org"), "abc"), mailboxSession)) + .isEqualTo("#Shared.marketing.abc"); + } + } } From ad0b90ad2abc3d36d73ef71df4ca322f8588d9e7 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 14:53:43 +0200 Subject: [PATCH 15/37] 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. --- .../apache/james/imap/main/PathConverter.java | 13 ++++++++-- .../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 3bbcbf469fa..5ea5c5fe796 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.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 PathConverter forSession(ImapSession session) { 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 @@ private MailboxPath asMailboxPath(List mailboxPathParts, MailboxSession } 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 @@ private String joinMailboxPath(MailboxPath mailboxPath, MailboxSession session) 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 f314f32949b..8d864b4b26f 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 @@ 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("username@apache.org"); @@ -112,6 +114,18 @@ void buildFullPathShouldAcceptAbsoluteOtherUserPath() { .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 @@ void mailboxNameShouldReturnFQDNWhenRelativeAndOtherUserMailbox() { .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)) From 08b59b46df6b5dce0acb41b8fae78504dff0362e Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 17:03:33 +0200 Subject: [PATCH 16/37] 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... --- .../suite/ListingWithSharingTest.java | 8 ++- .../imap/scripts/ListWithSharedMailbox.test | 64 +++++++++++++++++-- .../apache/james/imap/main/PathConverter.java | 6 +- .../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 95eedde1d18..c496b3724f2 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 @@ 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 void testListWithSharedMailboxUS() throws Exception { 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 eba7f0dd17d..29243651e5f 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 5ea5c5fe796..8a53728fa78 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 @@ private MailboxPath asMailboxPath(List mailboxPathParts, MailboxSession 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 aa6f19cd5e4..a568665e50d 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 @@ private Mono respondMailboxList(T request, ImapSession session, 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 @@ private MailboxACL.Rfc4314Rights getRfc4314Rights(MailboxSession mailboxSession, 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 a4aafb278e8..038d41e5538 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 @@ */ public class StatusProcessor extends AbstractMailboxProcessor 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 @@ private Mono sendStatus(MailboxPath mailboxPath, StatusDa Mono 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 @@ private RecentMode computeRecentMode(StatusDataItems statusDataItems) { private Mono 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 appendLimit = appendLimit(statusDataItems); Long messages = messages(statusDataItems, metaData); @@ -180,7 +181,7 @@ private Mono computeStatusResponse(MessageManager mailbox 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); }); } From 97678617775d91feda091775e4105bb99915ba70 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 21:38:45 +0200 Subject: [PATCH 17/37] JAMES-2182 MailboxManager mailbox search for specific other user --- .../james/mailbox/MailboxManagerTest.java | 26 +++++++++++++++++++ .../mailbox/store/StoreMailboxManager.java | 12 +++++++-- .../imap/scripts/ListWithSharedMailbox.test | 4 --- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java index f189c1a3a0a..528dea7b8ca 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java @@ -1199,6 +1199,32 @@ void searchShouldCombinePrivateAndDelegatedMailboxes() throws Exception { .extracting(MailboxMetaData::getPath) .containsOnly(inbox1, inbox2); } + + @Test + void searchShouldAllowListingAnotherUserMailbox() throws Exception { + assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL)); + MailboxSession session1 = mailboxManager.createSystemSession(USER_1); + MailboxSession session2 = mailboxManager.createSystemSession(USER_2); + MailboxPath inbox1 = MailboxPath.inbox(session1); + MailboxPath inbox2 = MailboxPath.inbox(session2); + mailboxManager.createMailbox(inbox1, session1); + mailboxManager.createMailbox(inbox2, session2); + mailboxManager.setRights(inbox1, + MailboxACL.EMPTY.apply(MailboxACL.command() + .forUser(USER_2) + .rights(MailboxACL.Right.Read, MailboxACL.Right.Lookup) + .asAddition()), + session1); + + MailboxQuery mailboxQuery = MailboxQuery.builder() + .userAndNamespaceFrom(inbox1) + .matchesAllMailboxNames() + .build(); + + assertThat(mailboxManager.search(mailboxQuery, session2).toStream()) + .extracting(MailboxMetaData::getPath) + .containsExactly(inbox1); + } @Test void searchShouldAllowUserFiltering() throws Exception { diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java index c3415c04917..37e68ce6bb7 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java @@ -808,14 +808,22 @@ private Mono retrieveCounters(MessageMapper messageMapper, Mail private Flux searchMailboxes(MailboxQuery mailboxQuery, MailboxSession session, Right right) { MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session); - Flux baseMailboxes = mailboxMapper - .findMailboxWithPathLike(toSingleUserQuery(mailboxQuery, session)); + Flux baseMailboxes = getBaseMailboxes(mailboxMapper, mailboxQuery, session); Flux delegatedMailboxes = getDelegatedMailboxes(mailboxMapper, mailboxQuery, right, session) .filter(Throwing.predicate(mailbox -> storeRightManager.hasRight(mailbox, right, session))) .filter(mailbox -> !mailbox.getUser().equals(session.getUser())); return Flux.concat(baseMailboxes, delegatedMailboxes); } + private Flux getBaseMailboxes(MailboxMapper mailboxMapper,MailboxQuery mailboxQuery, MailboxSession session) { + if (mailboxQuery.isPrivateMailboxes(session) + || mailboxQuery.getNamespace().isEmpty() && mailboxQuery.getUser().isEmpty()) { + return mailboxMapper.findMailboxWithPathLike(toSingleUserQuery(mailboxQuery, session)); + } else { + return Flux.empty(); + } + } + private Flux accessibleMailboxIds(MultimailboxesSearchQuery.Namespace namespace, Right right, MailboxSession session) { MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session); Flux baseMailboxes = mailboxMapper 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 29243651e5f..c462bb7bc0b 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 @@ -59,15 +59,11 @@ 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\" From 38c81f6832dcc2e0b78e57431548b01a2c5f1cf0 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 21:53:36 +0200 Subject: [PATCH 18/37] JAMES-2182 ListProcessor: handle split in reference between #user and the actual user --- .../imap/scripts/ListWithSharedMailbox.test | 7 ++++++ .../james/imap/processor/ListProcessor.java | 23 ++++++++++++++++--- 2 files changed, 27 insertions(+), 3 deletions(-) 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 c462bb7bc0b..aecbbdad52e 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 @@ -78,3 +78,10 @@ S: a8 OK LIST completed. C: a9 LIST "#private" sharedMailbox* S: a9 OK LIST completed. +C: a10 LIST "#user" "diana.*" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\" +S: a10 OK LIST completed. + +C: a11 LIST "#user" "diana." +S: a11 OK LIST completed. + 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 a568665e50d..4642620d44a 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 @@ -36,6 +36,7 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; +import org.apache.james.core.Username; import org.apache.james.imap.api.display.HumanReadableText; import org.apache.james.imap.api.display.ModifiedUtf7; import org.apache.james.imap.api.message.Capability; @@ -332,6 +333,7 @@ private MailboxACL.Rfc4314Rights getRfc4314Rights(MailboxSession mailboxSession, private MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, MailboxSession mailboxSession, ImapSession session, boolean isRelative) { + String decodedMailboxName = ModifiedUtf7.decodeModifiedUTF7(mailboxName); if (finalReferencename.isEmpty()) { if (mailboxName.equals("*")) { return MailboxQuery.builder() @@ -341,7 +343,7 @@ private MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, return MailboxQuery.builder() .expression(new PrefixedRegex( "", - ModifiedUtf7.decodeModifiedUTF7(mailboxName), + decodedMailboxName, mailboxSession.getPathDelimiter())) .build(); } @@ -349,11 +351,26 @@ private MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, MailboxPath basePath = computeBasePath(session, finalReferencename, isRelative); if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE) && basePath.getUser() == null) { + + int separatorPosition = decodedMailboxName.indexOf(mailboxSession.getPathDelimiter()); + if (separatorPosition >= 0) { + // interpret first part as the user + Username username = Username.of(decodedMailboxName.substring(0, separatorPosition)); + return MailboxQuery.builder() + .namespace(MailboxConstants.USER_NAMESPACE) + .username(username) + .expression(new PrefixedRegex( + basePath.getName(), + decodedMailboxName.substring(separatorPosition + 1), + mailboxSession.getPathDelimiter())) + .build(); + } + return MailboxQuery.builder() .namespace(MailboxConstants.USER_NAMESPACE) .expression(new PrefixedRegex( basePath.getName(), - ModifiedUtf7.decodeModifiedUTF7(mailboxName), + decodedMailboxName, mailboxSession.getPathDelimiter())) .build(); } @@ -372,7 +389,7 @@ private MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, .userAndNamespaceFrom(basePath) .expression(new PrefixedRegex( basePath.getName(), - ModifiedUtf7.decodeModifiedUTF7(mailboxName), + decodedMailboxName, mailboxSession.getPathDelimiter())) .build(); } From 4b43fa0dd041ea7cd31204d9c674b1e4628017aa Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 21:57:52 +0200 Subject: [PATCH 19/37] JAMES-2182 ListProcessor: extract ListRequest -> MailboxQuery convertion to PathConverter This allows customization... --- .../apache/james/imap/main/PathConverter.java | 79 +++++++++++++++++++ .../james/imap/processor/ListProcessor.java | 79 +------------------ 2 files changed, 81 insertions(+), 77 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 8a53728fa78..4eabd621b0e 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 @@ -23,10 +23,14 @@ import org.apache.james.core.Domain; import org.apache.james.core.Username; +import org.apache.james.imap.api.display.ModifiedUtf7; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.model.MailboxConstants; import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mailbox.model.search.MailboxQuery; +import org.apache.james.mailbox.model.search.PrefixedRegex; +import org.apache.james.mailbox.model.search.Wildcard; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; @@ -158,9 +162,84 @@ public String mailboxName(boolean relative, MailboxPath path, MailboxSession ses return joinMailboxPath(path, session); } } + + public MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, ImapSession session) { + MailboxSession mailboxSession = session.getMailboxSession(); + String decodedMailboxName = ModifiedUtf7.decodeModifiedUTF7(mailboxName); + if (finalReferencename.isEmpty()) { + if (mailboxName.equals("*")) { + return MailboxQuery.builder() + .matchesAllMailboxNames() + .build(); + } + return MailboxQuery.builder() + .expression(new PrefixedRegex( + "", + decodedMailboxName, + mailboxSession.getPathDelimiter())) + .build(); + } + + MailboxPath basePath = computeBasePath(session, finalReferencename); + if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE) + && basePath.getUser() == null) { + + int separatorPosition = decodedMailboxName.indexOf(mailboxSession.getPathDelimiter()); + if (separatorPosition >= 0) { + // interpret first part as the user + Username username = Username.of(decodedMailboxName.substring(0, separatorPosition)); + return MailboxQuery.builder() + .namespace(MailboxConstants.USER_NAMESPACE) + .username(username) + .expression(new PrefixedRegex( + basePath.getName(), + decodedMailboxName.substring(separatorPosition + 1), + mailboxSession.getPathDelimiter())) + .build(); + } + + return MailboxQuery.builder() + .namespace(MailboxConstants.USER_NAMESPACE) + .expression(new PrefixedRegex( + basePath.getName(), + decodedMailboxName, + mailboxSession.getPathDelimiter())) + .build(); + } + if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE) + && basePath.getUser().equals(mailboxSession.getUser()) + && basePath.getName().isEmpty() + && mailboxName.equals("*")) { + + return MailboxQuery.builder() + .userAndNamespaceFrom(basePath) + .expression(Wildcard.INSTANCE) + .build(); + } + + return MailboxQuery.builder() + .userAndNamespaceFrom(basePath) + .expression(new PrefixedRegex( + basePath.getName(), + decodedMailboxName, + mailboxSession.getPathDelimiter())) + .build(); + } + + private MailboxPath computeBasePath(ImapSession session, String finalReferencename) { + String decodedName = ModifiedUtf7.decodeModifiedUTF7(finalReferencename); + boolean isRelative = !finalReferencename.startsWith("#"); + if (isRelative) { + return MailboxPath.forUser(session.getUserName(), decodedName); + } else { + return buildFullPath(decodedName); + } + } } MailboxPath buildFullPath(String mailboxName); String mailboxName(boolean relative, MailboxPath path, MailboxSession session); + + MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, ImapSession session); } 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 4642620d44a..fcb1a48aa60 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 @@ -36,7 +36,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; -import org.apache.james.core.Username; import org.apache.james.imap.api.display.HumanReadableText; import org.apache.james.imap.api.display.ModifiedUtf7; import org.apache.james.imap.api.message.Capability; @@ -62,8 +61,6 @@ import org.apache.james.mailbox.model.MailboxMetaData; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.search.MailboxQuery; -import org.apache.james.mailbox.model.search.PrefixedRegex; -import org.apache.james.mailbox.model.search.Wildcard; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.util.MDCBuilder; import org.apache.james.util.ReactorUtils; @@ -196,7 +193,8 @@ private Mono respondMailboxList(T request, ImapSession session, // Should the namespace section be returned or not? boolean isRelative = ((request.getBaseReferenceName() + request.getMailboxPattern()).charAt(0) != MailboxConstants.NAMESPACE_PREFIX_CHAR); - MailboxQuery mailboxQuery = mailboxQuery(request.getBaseReferenceName(), request.getMailboxPattern(), mailboxSession, session, isRelative); + MailboxQuery mailboxQuery = pathConverterFactory.forSession(session) + .mailboxQuery(request.getBaseReferenceName(), request.getMailboxPattern(), session); if (request.selectSubscribed()) { return processWithSubscribed(session, request, responder, mailboxSession, isRelative, mailboxQuery); @@ -331,79 +329,6 @@ private MailboxACL.Rfc4314Rights getRfc4314Rights(MailboxSession mailboxSession, return metaData.getResolvedAcls().getEntries().get(entryKey); } - private MailboxQuery mailboxQuery(String finalReferencename, String mailboxName, MailboxSession mailboxSession, - ImapSession session, boolean isRelative) { - String decodedMailboxName = ModifiedUtf7.decodeModifiedUTF7(mailboxName); - if (finalReferencename.isEmpty()) { - if (mailboxName.equals("*")) { - return MailboxQuery.builder() - .matchesAllMailboxNames() - .build(); - } - return MailboxQuery.builder() - .expression(new PrefixedRegex( - "", - decodedMailboxName, - mailboxSession.getPathDelimiter())) - .build(); - } - - MailboxPath basePath = computeBasePath(session, finalReferencename, isRelative); - if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE) - && basePath.getUser() == null) { - - int separatorPosition = decodedMailboxName.indexOf(mailboxSession.getPathDelimiter()); - if (separatorPosition >= 0) { - // interpret first part as the user - Username username = Username.of(decodedMailboxName.substring(0, separatorPosition)); - return MailboxQuery.builder() - .namespace(MailboxConstants.USER_NAMESPACE) - .username(username) - .expression(new PrefixedRegex( - basePath.getName(), - decodedMailboxName.substring(separatorPosition + 1), - mailboxSession.getPathDelimiter())) - .build(); - } - - return MailboxQuery.builder() - .namespace(MailboxConstants.USER_NAMESPACE) - .expression(new PrefixedRegex( - basePath.getName(), - decodedMailboxName, - mailboxSession.getPathDelimiter())) - .build(); - } - if (basePath.getNamespace().equals(MailboxConstants.USER_NAMESPACE) - && basePath.getUser().equals(mailboxSession.getUser()) - && basePath.getName().isEmpty() - && mailboxName.equals("*")) { - - return MailboxQuery.builder() - .userAndNamespaceFrom(basePath) - .expression(Wildcard.INSTANCE) - .build(); - } - - return MailboxQuery.builder() - .userAndNamespaceFrom(basePath) - .expression(new PrefixedRegex( - basePath.getName(), - decodedMailboxName, - mailboxSession.getPathDelimiter())) - .build(); - } - - private MailboxPath computeBasePath(ImapSession session, String finalReferencename, boolean isRelative) { - String decodedName = ModifiedUtf7.decodeModifiedUTF7(finalReferencename); - if (isRelative) { - return MailboxPath.forUser(session.getUserName(), decodedName); - } else { - return pathConverterFactory.forSession(session).buildFullPath(decodedName); - } - } - - /** * retrieve mailboxType for specified mailboxPath using provided * MailboxTyper From 77d2756b3a2e4125478f4d916b482c83106d0283 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 22:13:26 +0200 Subject: [PATCH 20/37] JAMES-2182 Only user folder may be special use --- .../imapmailbox/suite/ListingWithSharingTest.java | 2 ++ .../james/imap/scripts/ListWithSharedMailbox.test | 15 +++++++++++++++ .../imap/api/process/DefaultMailboxTyper.java | 9 ++++++--- 3 files changed, 23 insertions(+), 3 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 c496b3724f2..9273f41b2af 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 @@ -60,9 +60,11 @@ public void testListWithSharedMailboxUS() throws Exception { .withMailbox(OTHER_USER_SHARED_MAILBOX) .withMailbox(OTHER_USER_SHARED_MAILBOX_CHILD) .withMailbox(YET_ANOTHER_USER_SHARED_MAILBOX) + .withMailbox(MailboxPath.forUser(OTHER_USER_NAME, "Sent")) .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")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "Sent"), 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 aecbbdad52e..73214414b4e 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 @@ -24,6 +24,7 @@ S: \* LIST \(\\HasNoChildren\) \"\.\" \"INBOX\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\" S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" } S: a0 OK LIST completed. @@ -38,6 +39,8 @@ S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\" S: \* MYRIGHTS \"sharedMailbox\" \"lr\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\" S: \* MYRIGHTS \"sharedMailbox.child\" \"lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" +S: \* MYRIGHTS \"Sent\" \"lr\" } S: a1 OK LIST completed. @@ -51,6 +54,8 @@ 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: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" +S: \* STATUS \"#user.boby.Sent\" \(RECENT 0 UNSEEN 0\) } S: a2 OK LIST completed. @@ -67,6 +72,7 @@ SUB { S: \* LIST \(\\HasNoChildren\) \".\" \"#user.diana.sharedMailbox\" S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" } S: a7 OK LIST completed. @@ -85,3 +91,12 @@ S: a10 OK LIST completed. C: a11 LIST "#user" "diana." S: a11 OK LIST completed. +C: a12 LIST "" "*" RETURN (SPECIAL-USE) +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: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" +} +S: a12 OK LIST completed. \ No newline at end of file diff --git a/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java b/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java index 3e68724157a..4aefd0f395e 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/api/process/DefaultMailboxTyper.java @@ -43,9 +43,12 @@ public DefaultMailboxTyper() { @Override public MailboxType getMailboxType(ImapSession session, MailboxPath path) { - return Role.from(path.getName()) - .flatMap(this::asMailboxType) - .orElse(MailboxType.OTHER); + if (path.belongsTo(session.getMailboxSession())) { + return Role.from(path.getName()) + .flatMap(this::asMailboxType) + .orElse(MailboxType.OTHER); + } + return MailboxType.OTHER; } private Optional asMailboxType(Role role) { From 7a906f50791bd2ddf3183011dc62b3fea95834ec Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 22:23:10 +0200 Subject: [PATCH 21/37] JAMES-2182 List myrights response should preserve namespace --- .../james/imap/scripts/ListWithSharedMailbox.test | 9 ++++----- .../org/apache/james/imap/main/PathConverter.java | 15 ++++++++++----- .../james/imap/processor/ListProcessor.java | 9 +++++---- 3 files changed, 19 insertions(+), 14 deletions(-) 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 73214414b4e..b2c54b29115 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 @@ -29,18 +29,17 @@ S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" S: a0 OK LIST completed. 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: \* MYRIGHTS \"#user.diana.sharedMailbox\" \"lr\" S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\" -S: \* MYRIGHTS \"sharedMailbox\" \"lr\" +S: \* MYRIGHTS \"#user.boby.sharedMailbox\" \"lr\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\" -S: \* MYRIGHTS \"sharedMailbox.child\" \"lr\" +S: \* MYRIGHTS \"#user.boby.sharedMailbox.child\" \"lr\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" -S: \* MYRIGHTS \"Sent\" \"lr\" +S: \* MYRIGHTS \"#user.boby.Sent\" \"lr\" } S: a1 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 4eabd621b0e..731583221a4 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 @@ -46,11 +46,17 @@ interface Factory { class Default implements Factory { public PathConverter forSession(ImapSession session) { + return new PathConverter.Default(session.getMailboxSession()); + } + + public PathConverter forSession(MailboxSession session) { return new PathConverter.Default(session); } } PathConverter forSession(ImapSession session); + + PathConverter forSession(MailboxSession session); } class Default implements PathConverter{ @@ -61,10 +67,10 @@ class Default implements PathConverter{ .addEscape('_', "_-") .build(); - private final ImapSession session; + private final MailboxSession mailboxSession; - private Default(ImapSession session) { - this.session = session; + private Default(MailboxSession mailboxSession) { + this.mailboxSession = mailboxSession; } public MailboxPath buildFullPath(String mailboxName) { @@ -84,11 +90,10 @@ private boolean isAbsolute(String mailboxName) { } private MailboxPath buildRelativePath(String mailboxName) { - return new MailboxPath(MailboxConstants.USER_NAMESPACE, session.getUserName(), sanitizeMailboxName(mailboxName)); + return new MailboxPath(MailboxConstants.USER_NAMESPACE, mailboxSession.getUser(), sanitizeMailboxName(mailboxName)); } private MailboxPath buildAbsolutePath(String absolutePath) { - MailboxSession mailboxSession = session.getMailboxSession(); return asMailboxPath(Splitter.on(mailboxSession.getPathDelimiter()).splitToList(absolutePath), mailboxSession); } 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 fcb1a48aa60..ee3cce3edce 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 @@ -222,7 +222,7 @@ private Mono processWithoutSubscribed(ImapSession session, T request, Resp isSubscribed.test(metaData.getPath()))); } }) - .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData)) + .doOnNext(metaData -> respondMyRights(request, responder, mailboxSession, metaData, isRelative)) .concatMap(metaData -> request.getStatusDataItems().map(statusDataItems -> statusProcessor.sendStatus(retrieveMessageManager(metaData, mailboxSession), statusDataItems, responder, session, mailboxSession)).orElse(Mono.empty())) .then(); } @@ -242,7 +242,7 @@ private Mono processWithSubscribed(ImapSession session, T request, Respond .map(tuple -> getListResponseForSelectSubscribed(session, tuple.getT1(), tuple.getT2(), request, mailboxSession, isRelative, mailboxQuery)) .flatMapIterable(list -> list) .doOnNext(pathAndResponse -> responder.respond(pathAndResponse.getMiddle())) - .doOnNext(pathAndResponse -> pathAndResponse.getRight().ifPresent(mailboxMetaData -> respondMyRights(request, responder, mailboxSession, mailboxMetaData))) + .doOnNext(pathAndResponse -> pathAndResponse.getRight().ifPresent(mailboxMetaData -> respondMyRights(request, responder, mailboxSession, mailboxMetaData, isRelative))) .concatMap(pathAndResponse -> sendStatusWhenSubscribed(session, request, responder, mailboxSession, pathAndResponse)) .then(); } @@ -313,9 +313,10 @@ private List> listRecursiveMatch(ImapSession ses .collect(Collectors.toList()); } - private void respondMyRights(T request, Responder responder, MailboxSession mailboxSession, MailboxMetaData metaData) { + private void respondMyRights(T request, Responder responder, MailboxSession mailboxSession, MailboxMetaData metaData, boolean isRelative) { if (request.getReturnOptions().contains(ListRequest.ListReturnOption.MYRIGHTS)) { - MailboxName mailboxName = new MailboxName(metaData.getPath().getName()); + MailboxName mailboxName = new MailboxName(pathConverterFactory.forSession(mailboxSession) + .mailboxName(isRelative, metaData.getPath(), mailboxSession)); MyRightsResponse myRightsResponse = new MyRightsResponse(mailboxName, getRfc4314Rights(mailboxSession, metaData)); responder.respond(myRightsResponse); } From 6cee7a8c6080b8ccc19c6289c1e95e1eb6e63676 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Wed, 9 Oct 2024 22:51:55 +0200 Subject: [PATCH 22/37] JAMES-2182 Fix checkstyles --- .../imap/encode/NamespaceResponseEncoder.java | 1 - .../apache/james/imap/main/PathConverter.java | 2 +- .../message/response/NamespaceResponse.java | 1 - .../processor/AbstractMailboxProcessor.java | 1 - .../imap/processor/NamespaceSupplier.java | 21 +++++++++++++++++-- .../james/imap/main/PathConverterTest.java | 3 ++- .../imapserver/netty/IMAPServerTest.java | 1 - 7 files changed, 22 insertions(+), 8 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java b/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java index 6a65c45e6fc..e02f6926b0d 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/encode/NamespaceResponseEncoder.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.util.Collection; -import java.util.List; import org.apache.james.imap.api.ImapConstants; import org.apache.james.imap.message.response.NamespaceResponse; 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 731583221a4..b8bb7093c15 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 @@ -59,7 +59,7 @@ public PathConverter forSession(MailboxSession session) { PathConverter forSession(MailboxSession session); } - class Default implements 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() diff --git a/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java b/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java index 837afecdc78..879d3bb86e0 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/message/response/NamespaceResponse.java @@ -19,7 +19,6 @@ package org.apache.james.imap.message.response; import java.util.Collection; -import java.util.List; import java.util.Objects; import org.apache.james.imap.api.message.response.ImapResponseMessage; 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 439444f15ca..5c2d999ab6b 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 @@ -60,7 +60,6 @@ import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MessageRangeException; import org.apache.james.mailbox.exception.OverQuotaException; -import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MessageRange; import org.apache.james.mailbox.model.MessageRange.Type; import org.apache.james.metrics.api.MetricFactory; diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java index c706266cef8..3b55ae55820 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/NamespaceSupplier.java @@ -1,8 +1,25 @@ +/**************************************************************** + * 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.processor; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import org.apache.james.imap.message.response.NamespaceResponse; import org.apache.james.imap.message.response.NamespaceResponse.Namespace; 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 8d864b4b26f..428d391861f 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 @@ -185,8 +185,9 @@ void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() { assertThat(pathConverter.mailboxName(!RELATIVE, new MailboxPath("#Shared", Username.of("marketing"), "abc"), mailboxSession)) .isEqualTo("#Shared.marketing.abc"); } + @Nested - class WithEmail{ + class WithEmail { @BeforeEach void setUp() { imapSession = new FakeImapSession(); diff --git a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java index 76f8a4d0afd..c39d54c0322 100644 --- a/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java +++ b/server/protocols/protocols-imap4/src/test/java/org/apache/james/imapserver/netty/IMAPServerTest.java @@ -21,7 +21,6 @@ import static jakarta.mail.Flags.Flag.ANSWERED; import static jakarta.mail.Folder.READ_WRITE; -import static org.apache.james.jmap.JMAPTestingConstants.BOB; import static org.apache.james.jmap.JMAPTestingConstants.LOCALHOST_IP; import static org.apache.james.jwt.OidcTokenFixture.INTROSPECTION_RESPONSE; import static org.apache.james.mailbox.MessageManager.FlagsUpdateMode.REPLACE; From b882b7dc546c0931bf3e89ce132c3f187719603c Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 09:27:00 +0200 Subject: [PATCH 23/37] JAMES-2182 LSUB for delegated accounts --- .../mailbox/model/search/PrefixedRegex.java | 22 +++++- .../model/search/PrefixedRegexTest.java | 4 +- .../imap/scripts/ListWithSharedMailbox.test | 73 ++++++++++++++++++- .../imap/processor/DefaultProcessor.java | 2 +- .../james/imap/processor/LSubProcessor.java | 12 ++- .../imap/processor/LSubProcessorTest.java | 3 +- 6 files changed, 105 insertions(+), 11 deletions(-) diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedRegex.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedRegex.java index 1040391c354..d9a65dd58e8 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedRegex.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedRegex.java @@ -41,8 +41,18 @@ public PrefixedRegex(String prefix, String regex, char pathDelimiter) { @Override public boolean isExpressionMatch(String name) { - return name.startsWith(prefix) - && regexMatching(name.substring(prefix.length())); + if (name.startsWith(prefix)) { + String nameSubstring = sanitizeSubstring(name.substring(prefix.length())); + return regexMatching(nameSubstring); + } + return false; + } + + private String sanitizeSubstring(String name) { + if (!name.isEmpty() && name.charAt(0) == pathDelimiter) { + return name.substring(1); + } + return name; } private boolean regexMatching(String name) { @@ -133,4 +143,12 @@ public final boolean equals(Object o) { public final int hashCode() { return Objects.hash(prefix, regex, pathDelimiter); } + + @Override + public String toString() { + return "PrefixedRegex{" + + "prefix='" + prefix + '\'' + + ", regex='" + regex + '\'' + + '}'; + } } diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedRegexTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedRegexTest.java index 95a2681c587..03340f532f3 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedRegexTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedRegexTest.java @@ -224,12 +224,12 @@ void isExpressionMatchShouldMatchFolderWhenMatching() { } @Test - void isExpressionMatchShouldReturnFalseWhenNameBeginsWithDelimiter() { + void isExpressionMatchShouldReturnTrueWhenNameBeginsWithDelimiter() { PrefixedRegex testee = new PrefixedRegex(EMPTY_PREFIX, "mailbox", PATH_DELIMITER); boolean actual = testee.isExpressionMatch(".mailbox"); - assertThat(actual).isFalse(); + assertThat(actual).isTrue(); } @Test 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 b2c54b29115..ba08ab81edd 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 @@ -98,4 +98,75 @@ S: \* LIST \(\\HasChildren\) \".\" \"#user.boby.sharedMailbox\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.sharedMailbox.child\" S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.Sent\" } -S: a12 OK LIST completed. \ No newline at end of file +S: a12 OK LIST completed. + +C: b01 SUBSCRIBE INBOX +S: b01 OK SUBSCRIBE completed. +C: b02 SUBSCRIBE #user.diana.sharedMailbox +S: b02 OK SUBSCRIBE completed. +C: b03 SUBSCRIBE #user.boby.sharedMailbox +S: b03 OK SUBSCRIBE completed. +C: b04 SUBSCRIBE #user.boby.sharedMailbox.child +S: b04 OK SUBSCRIBE completed. +C: b05 SUBSCRIBE #user.boby.Sent +S: b05 OK SUBSCRIBE completed. + +C: b06 LSUB "" "*" +SUB { +S: \* LSUB \(\) \"\.\" \"INBOX\" +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LSUB \(\) \".\" \"#user.boby.Sent\" +} +S: b06 OK LSUB completed. + +C: b07 LSUB "#user" "*" +SUB { +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LSUB \(\) \".\" \"#user.boby.Sent\" +} +S: b07 OK LSUB completed. + +C: b08 LSUB "#user.diana" "*" +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: b08 OK LSUB completed. + +C: b09 LSUB "" "#user.*" +SUB { +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox\" +S: \* LSUB \(\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LSUB \(\) \".\" \"#user.boby.Sent\" +} +S: b09 OK LSUB completed. + +C: b10 LSUB "" "#user.diana.*" +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: b10 OK LSUB completed. + +C: b11 LSUB "#user" "diana.*" +S: \* LSUB \(\) \".\" \"#user.diana.sharedMailbox\" +S: b11 OK LSUB completed. + +C: b12 LIST "" "*" RETURN (SUBSCRIBED) +SUB { +S: \* LIST \(\\HasNoChildren \\Subscribed\) \"\.\" \"INBOX\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.diana.sharedMailbox\" +S: \* LIST \(\\HasChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.Sent\" +} +S: b12 OK LIST completed. + +C: b13 LIST (SUBSCRIBED) "" "*"RETURN (SUBSCRIBED) +SUB { +S: \* LIST \(\\HasNoChildren \\Subscribed\) \"\.\" \"INBOX\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.diana.sharedMailbox\" +S: \* LIST \(\\HasChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.Sent\" +} +S: b13 OK LIST completed. \ No newline at end of file diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java index f96689a2e48..a69c2133a32 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java @@ -83,7 +83,7 @@ public static ImapProcessor createDefaultProcessor(ImapProcessor chainEndProcess builder.add(new IdleProcessor(mailboxManager, statusResponseFactory, metricFactory)); StatusProcessor statusProcessor = new StatusProcessor(mailboxManager, statusResponseFactory, metricFactory, pathConverterFactory); builder.add(statusProcessor); - builder.add(new LSubProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory)); + builder.add(new LSubProcessor(mailboxManager, subscriptionManager, statusResponseFactory, metricFactory, pathConverterFactory)); builder.add(new XListProcessor(mailboxManager, statusResponseFactory, mailboxTyper, metricFactory, subscriptionManager, pathConverterFactory)); builder.add(new ListProcessor<>(mailboxManager, statusResponseFactory, metricFactory, subscriptionManager, statusProcessor, mailboxTyper, pathConverterFactory)); builder.add(new SearchProcessor(mailboxManager, statusResponseFactory, metricFactory)); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/LSubProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/LSubProcessor.java index a7f779f7c1c..581b0472e09 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/LSubProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/LSubProcessor.java @@ -29,6 +29,7 @@ import org.apache.james.imap.api.display.ModifiedUtf7; import org.apache.james.imap.api.message.response.StatusResponseFactory; import org.apache.james.imap.api.process.ImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.LsubRequest; import org.apache.james.imap.message.response.LSubResponse; import org.apache.james.mailbox.MailboxManager; @@ -36,7 +37,6 @@ import org.apache.james.mailbox.SubscriptionManager; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.SubscriptionException; -import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.search.MailboxNameExpression; import org.apache.james.mailbox.model.search.PrefixedRegex; import org.apache.james.metrics.api.MetricFactory; @@ -50,14 +50,17 @@ public class LSubProcessor extends AbstractMailboxProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(LSubProcessor.class); + public static final boolean RELATIVE = true; private final SubscriptionManager subscriptionManager; + private final PathConverter.Factory pathConverterFactory; @Inject public LSubProcessor(MailboxManager mailboxManager, SubscriptionManager subscriptionManager, StatusResponseFactory factory, - MetricFactory metricFactory) { + MetricFactory metricFactory, PathConverter.Factory pathConverterFactory) { super(LsubRequest.class, mailboxManager, factory, metricFactory); this.subscriptionManager = subscriptionManager; + this.pathConverterFactory = pathConverterFactory; } @Override @@ -76,8 +79,9 @@ protected Mono processRequestReactive(LsubRequest request, ImapSession ses private Mono listSubscriptions(ImapSession session, Responder responder, String referenceName, String mailboxName) { MailboxSession mailboxSession = session.getMailboxSession(); try { + PathConverter pathConverter = pathConverterFactory.forSession(session); Mono> mailboxesMono = Flux.from(subscriptionManager.subscriptionsReactive(mailboxSession)) - .map(MailboxPath::getName) + .map(path -> pathConverter.mailboxName(RELATIVE, path, mailboxSession)) .collectList(); String decodedMailName = ModifiedUtf7.decodeModifiedUTF7(referenceName); @@ -90,7 +94,7 @@ private Mono listSubscriptions(ImapSession session, Responder responder, S return mailboxesMono.doOnNext(mailboxes -> { Collection mailboxResponses = new ArrayList<>(); for (String mailbox : mailboxes) { - respond(responder, expression, mailbox, true, mailboxes, mailboxResponses, mailboxSession.getPathDelimiter()); + respond(responder, expression, mailbox, RELATIVE, mailboxes, mailboxResponses, mailboxSession.getPathDelimiter()); } }).then(); } catch (SubscriptionException e) { diff --git a/protocols/imap/src/test/java/org/apache/james/imap/processor/LSubProcessorTest.java b/protocols/imap/src/test/java/org/apache/james/imap/processor/LSubProcessorTest.java index d24da477ec5..410ffe71b14 100644 --- a/protocols/imap/src/test/java/org/apache/james/imap/processor/LSubProcessorTest.java +++ b/protocols/imap/src/test/java/org/apache/james/imap/processor/LSubProcessorTest.java @@ -38,6 +38,7 @@ import org.apache.james.imap.api.process.ImapProcessor; import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.encode.FakeImapSession; +import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.LsubRequest; import org.apache.james.imap.message.response.LSubResponse; import org.apache.james.mailbox.MailboxManager; @@ -101,7 +102,7 @@ void setUp() throws Exception { Object[] args = invocation.getArguments(); return (Mono) args[0]; }); - processor = new LSubProcessor(mailboxManager, manager, serverResponseFactory, new RecordingMetricFactory()); + processor = new LSubProcessor(mailboxManager, manager, serverResponseFactory, new RecordingMetricFactory(), PathConverter.Factory.DEFAULT); session.setMailboxSession(mailboxSession); } From 7199458eacc564a7f98be7d7cc22d03e050c6360 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 09:51:04 +0200 Subject: [PATCH 24/37] JAMES-2182 Allow Read only selects --- .../imap/scripts/ListWithSharedMailbox.test | 13 ++++++++++++- .../processor/AbstractSelectionProcessor.java | 19 ++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) 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 ba08ab81edd..fcd2af70900 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 @@ -169,4 +169,15 @@ S: \* LIST \(\\HasChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox\" S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox.child\" S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.Sent\" } -S: b13 OK LIST completed. \ No newline at end of file +S: b13 OK LIST completed. + +C: c01 SELECT #user.diana.sharedMailbox +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* 0 EXISTS +S: \* 0 RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: c01 OK \[READ-ONLY\] SELECT completed. \ No newline at end of file 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 6655204291c..a5df7d2e355 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 @@ -60,6 +60,7 @@ import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxNotFoundException; +import org.apache.james.mailbox.model.MailboxACL; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MessageRange; @@ -401,13 +402,25 @@ private Mono selectMailbox(MailboxPath mailboxPath, ImapSession return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) .flatMap(Throwing.function(mailbox -> selectMailbox(session, responder, mailbox, currentMailbox) .flatMap(Throwing.function(sessionMailbox -> - mailbox.getMetaDataReactive(recentMode(!openReadOnly), mailboxSession, EnumSet.of(MailboxMetaData.Item.FirstUnseen, MailboxMetaData.Item.HighestModSeq, MailboxMetaData.Item.NextUid, MailboxMetaData.Item.MailboxCounters)) + mailbox.getMetaDataReactive(recentMode(!openReadOnly, mailbox, mailboxSession), mailboxSession, EnumSet.of(MailboxMetaData.Item.FirstUnseen, MailboxMetaData.Item.HighestModSeq, MailboxMetaData.Item.NextUid, MailboxMetaData.Item.MailboxCounters)) .doOnNext(next -> addRecent(next, sessionMailbox)))))); } - private MailboxMetaData.RecentMode recentMode(boolean reset) { + private MailboxMetaData.RecentMode recentMode(boolean reset, MessageManager mailbox, MailboxSession session) { if (reset) { - return RESET; + try { + if (getMailboxManager().myRights(mailbox.getMailboxEntity(), session).contains(MailboxACL.Right.Write)) { + return RESET; + } + // https://datatracker.ietf.org/doc/html/rfc3501#section-6.3.1 + // If the client is not permitted to modify the mailbox but is + // permitted read access, the mailbox is selected as read-only, and + // the server MUST prefix the text of the tagged OK response to + // SELECT with the "[READ-ONLY]" response code. + return RETRIEVE; + } catch (MailboxException e) { + return RESET; + } } return RETRIEVE; } From f845583c5ec7aa497ec1028a80c4297e62b93970 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 09:52:48 +0200 Subject: [PATCH 25/37] [REFACTORING] CreateProcessor: inline unneeded flatMap --- .../james/imap/processor/CreateProcessor.java | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java index 99fc531c353..377f5726f04 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java @@ -30,6 +30,7 @@ import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.exception.TooLongMailboxNameException; +import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.util.MDCBuilder; import org.apache.james.util.ReactorUtils; @@ -54,23 +55,23 @@ public CreateProcessor(MailboxManager mailboxManager, StatusResponseFactory fact protected Mono processRequestReactive(CreateRequest request, ImapSession session, Responder responder) { MailboxManager mailboxManager = getMailboxManager(); - return Mono.fromCallable(() -> pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName())) - .flatMap(mailboxPath -> Mono.from(mailboxManager.createMailboxReactive(mailboxPath, session.getMailboxSession())) - .flatMap(mailboxId -> unsolicitedResponses(session, responder, false) - .then(Mono.fromRunnable(() -> okComplete(request, StatusResponse.ResponseCode.mailboxId(mailboxId), responder)))) - .onErrorResume(MailboxExistsException.class, e -> { - no(request, responder, HumanReadableText.MAILBOX_EXISTS); - return ReactorUtils.logAsMono(() -> LOGGER.debug("Create failed for mailbox {} as it already exists", mailboxPath, e)); - }) - .onErrorResume(TooLongMailboxNameException.class, e -> { - taggedBad(request, responder, HumanReadableText.FAILURE_MAILBOX_NAME); - return ReactorUtils.logAsMono(() -> LOGGER.debug("The mailbox name length is over limit: {}", mailboxPath.getName(), e)); - }) - .onErrorResume(TooLongMailboxNameException.class, e -> { - no(request, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); - return ReactorUtils.logAsMono(() -> LOGGER.error("Create failed for mailbox {}", mailboxPath, e)); - }) - .then()); + MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); + return Mono.from(mailboxManager.createMailboxReactive(mailboxPath, session.getMailboxSession())) + .flatMap(mailboxId -> unsolicitedResponses(session, responder, false) + .then(Mono.fromRunnable(() -> okComplete(request, StatusResponse.ResponseCode.mailboxId(mailboxId), responder)))) + .onErrorResume(MailboxExistsException.class, e -> { + no(request, responder, HumanReadableText.MAILBOX_EXISTS); + return ReactorUtils.logAsMono(() -> LOGGER.debug("Create failed for mailbox {} as it already exists", mailboxPath, e)); + }) + .onErrorResume(TooLongMailboxNameException.class, e -> { + taggedBad(request, responder, HumanReadableText.FAILURE_MAILBOX_NAME); + return ReactorUtils.logAsMono(() -> LOGGER.debug("The mailbox name length is over limit: {}", mailboxPath.getName(), e)); + }) + .onErrorResume(TooLongMailboxNameException.class, e -> { + no(request, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); + return ReactorUtils.logAsMono(() -> LOGGER.error("Create failed for mailbox {}", mailboxPath, e)); + }) + .then(); } @Override From 75b29e88e1ebb1714abc780d8a8a762704581332 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 09:58:09 +0200 Subject: [PATCH 26/37] [REFACTORING] SystemMessageProcessor: remove unneeded fields --- .../org/apache/james/imap/processor/DefaultProcessor.java | 2 +- .../apache/james/imap/processor/SystemMessageProcessor.java | 6 +----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java index a69c2133a32..c47c7a2eac6 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/DefaultProcessor.java @@ -60,7 +60,7 @@ public static ImapProcessor createDefaultProcessor(ImapProcessor chainEndProcess ImmutableList.Builder builder = ImmutableList.builder(); CapabilityProcessor capabilityProcessor = new CapabilityProcessor(mailboxManager, statusResponseFactory, metricFactory); - builder.add(new SystemMessageProcessor(mailboxManager)); + builder.add(new SystemMessageProcessor()); builder.add(new LogoutProcessor(mailboxManager, statusResponseFactory, metricFactory)); builder.add(capabilityProcessor); builder.add(new IdProcessor(mailboxManager, statusResponseFactory, metricFactory)); diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SystemMessageProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SystemMessageProcessor.java index 7a28514923c..117fe475d6a 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SystemMessageProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SystemMessageProcessor.java @@ -24,7 +24,6 @@ import org.apache.james.imap.api.process.ImapSession; import org.apache.james.imap.message.request.SystemMessage; import org.apache.james.imap.processor.base.AbstractProcessor; -import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; import org.apache.james.util.MDCBuilder; import org.slf4j.Logger; @@ -38,12 +37,9 @@ public class SystemMessageProcessor extends AbstractProcessor { private static final Logger LOGGER = LoggerFactory.getLogger(SystemMessageProcessor.class); - private final MailboxManager mailboxManager; - @Inject - public SystemMessageProcessor(MailboxManager mailboxManager) { + public SystemMessageProcessor() { super(SystemMessage.class); - this.mailboxManager = mailboxManager; } @Override From a319c8f538bede131e82a1c46dc8f8b78693b0b2 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 10:02:10 +0200 Subject: [PATCH 27/37] [REFACTORING] Tests for UNSUBSCRIBE --- .../james/imap/scripts/ListWithSharedMailbox.test | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 fcd2af70900..6378cf5be5a 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 @@ -171,6 +171,18 @@ S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.Sent\" } S: b13 OK LIST completed. +C: b14 UNSUBSCRIBE #user.diana.sharedMailbox +S: b14 OK UNSUBSCRIBE completed. + +C: b15 LIST (SUBSCRIBED) "" "*"RETURN (SUBSCRIBED) +SUB { +S: \* LIST \(\\HasNoChildren \\Subscribed\) \"\.\" \"INBOX\" +S: \* LIST \(\\HasChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.sharedMailbox.child\" +S: \* LIST \(\\HasNoChildren \\Subscribed\) \".\" \"#user.boby.Sent\" +} +S: b15 OK LIST completed. + C: c01 SELECT #user.diana.sharedMailbox S: \* OK \[MAILBOXID \(.*\)\] Ok S: \* FLAGS \(.*\) From 554dba78cd39c0418829822f1ebc1ea5684cd217 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 10 Oct 2024 20:37:13 +0200 Subject: [PATCH 28/37] JAMES-2182 PathConverter: use MailboxSession where more convenient --- .../james/imap/processor/AbstractAuthProcessor.java | 8 ++++---- .../apache/james/imap/processor/StatusProcessor.java | 6 +++--- .../org/apache/james/imap/main/PathConverterTest.java | 10 ++-------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java index 17ac2f13c67..d37017eaf30 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/AbstractAuthProcessor.java @@ -185,18 +185,18 @@ protected void provisionInbox(ImapSession session, MailboxManager mailboxManager if (Mono.from(mailboxManager.mailboxExists(inboxPath, mailboxSession)).block()) { LOGGER.debug("INBOX exists. No need to create it."); } else { - provisionMailbox(DefaultMailboxes.INBOX, session, mailboxManager, mailboxSession); + provisionMailbox(DefaultMailboxes.INBOX, mailboxManager, mailboxSession); if (imapConfiguration.isProvisionDefaultMailboxes()) { for (String mailbox : DefaultMailboxes.DEFAULT_MAILBOXES) { - provisionMailbox(mailbox, session, mailboxManager, mailboxSession); + provisionMailbox(mailbox, mailboxManager, mailboxSession); } } } } - private void provisionMailbox(String mailbox, ImapSession session, MailboxManager mailboxManager, + private void provisionMailbox(String mailbox, MailboxManager mailboxManager, MailboxSession mailboxSession) throws MailboxException { - MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(mailbox); + MailboxPath mailboxPath = pathConverterFactory.forSession(mailboxSession).buildFullPath(mailbox); if (Mono.from(mailboxManager.mailboxExists(mailboxPath, mailboxSession)).block()) { LOGGER.debug("{} exists. No need to create it.", mailbox); return; 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 038d41e5538..d1f81143215 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 @@ -126,7 +126,7 @@ private Mono sendStatus(MailboxPath mailboxPath, StatusDa Mono sendStatus(MessageManager mailbox, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) { return retrieveMetadata(mailbox, statusDataItems, mailboxSession) - .flatMap(metaData -> computeStatusResponse(mailbox, statusDataItems, metaData, mailboxSession, session) + .flatMap(metaData -> computeStatusResponse(mailbox, statusDataItems, metaData, mailboxSession) .doOnNext(response -> { // Enable CONDSTORE as this is a CONDSTORE enabling command if (response.getHighestModSeq() != null) { @@ -165,7 +165,7 @@ private RecentMode computeRecentMode(StatusDataItems statusDataItems) { private Mono computeStatusResponse(MessageManager mailbox, StatusDataItems statusDataItems, MessageManager.MailboxMetaData metaData, - MailboxSession mailboxSession, ImapSession session) { + MailboxSession mailboxSession) { return iterateMailbox(statusDataItems, mailbox, mailboxSession) .map(maybeIterationResult -> { Optional appendLimit = appendLimit(statusDataItems); @@ -181,7 +181,7 @@ private Mono computeStatusResponse(MessageManager mailbox 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, pathConverterFactory.forSession(session).mailboxName(RELATIVE, mailbox.getMailboxPath(), mailboxSession), mailboxId); + messages, recent, uidNext, highestModSeq, uidValidity, unseen, pathConverterFactory.forSession(mailboxSession).mailboxName(RELATIVE, mailbox.getMailboxPath(), mailboxSession), mailboxId); }); } 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 428d391861f..02a9a12807e 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 @@ -23,7 +23,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import org.apache.james.core.Username; -import org.apache.james.imap.encode.FakeImapSession; import org.apache.james.mailbox.MailboxSession; import org.apache.james.mailbox.MailboxSessionUtil; import org.apache.james.mailbox.model.MailboxConstants; @@ -44,16 +43,13 @@ class PathConverterTest { private static final char PATH_DELIMITER = '.'; public static final boolean RELATIVE = true; - private FakeImapSession imapSession; private MailboxSession mailboxSession; private PathConverter pathConverter; @BeforeEach void setUp() { - imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USERNAME); - pathConverter = PathConverter.Factory.DEFAULT.forSession(imapSession); - imapSession.setMailboxSession(mailboxSession); + pathConverter = PathConverter.Factory.DEFAULT.forSession(mailboxSession); } @Test @@ -190,10 +186,8 @@ void mailboxNameShouldReturnFQDNWhenNotRelativeAndSharedMailbox() { class WithEmail { @BeforeEach void setUp() { - imapSession = new FakeImapSession(); mailboxSession = MailboxSessionUtil.create(USERNAME_WITH_MAIL); - pathConverter = PathConverter.Factory.DEFAULT.forSession(imapSession); - imapSession.setMailboxSession(mailboxSession); + pathConverter = PathConverter.Factory.DEFAULT.forSession(mailboxSession); } @Test From 233f20c958f1dab8bca082762c509886713426c2 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Fri, 11 Oct 2024 09:02:42 +0200 Subject: [PATCH 29/37] JAMES-2182 Fix InMemorySecurityTest --- .../james/imap/processor/CreateProcessor.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java index 377f5726f04..5103cca2cd9 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/CreateProcessor.java @@ -28,9 +28,9 @@ import org.apache.james.imap.main.PathConverter; import org.apache.james.imap.message.request.CreateRequest; import org.apache.james.mailbox.MailboxManager; +import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.exception.TooLongMailboxNameException; -import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.metrics.api.MetricFactory; import org.apache.james.util.MDCBuilder; import org.apache.james.util.ReactorUtils; @@ -55,21 +55,21 @@ public CreateProcessor(MailboxManager mailboxManager, StatusResponseFactory fact protected Mono processRequestReactive(CreateRequest request, ImapSession session, Responder responder) { MailboxManager mailboxManager = getMailboxManager(); - MailboxPath mailboxPath = pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName()); - return Mono.from(mailboxManager.createMailboxReactive(mailboxPath, session.getMailboxSession())) + return Mono.fromCallable(() -> pathConverterFactory.forSession(session).buildFullPath(request.getMailboxName())) + .flatMap(mailboxPath -> Mono.from(mailboxManager.createMailboxReactive(mailboxPath, session.getMailboxSession()))) .flatMap(mailboxId -> unsolicitedResponses(session, responder, false) .then(Mono.fromRunnable(() -> okComplete(request, StatusResponse.ResponseCode.mailboxId(mailboxId), responder)))) .onErrorResume(MailboxExistsException.class, e -> { no(request, responder, HumanReadableText.MAILBOX_EXISTS); - return ReactorUtils.logAsMono(() -> LOGGER.debug("Create failed for mailbox {} as it already exists", mailboxPath, e)); + return ReactorUtils.logAsMono(() -> LOGGER.debug("Create failed for mailbox {} as it already exists", request.getMailboxName(), e)); }) .onErrorResume(TooLongMailboxNameException.class, e -> { taggedBad(request, responder, HumanReadableText.FAILURE_MAILBOX_NAME); - return ReactorUtils.logAsMono(() -> LOGGER.debug("The mailbox name length is over limit: {}", mailboxPath.getName(), e)); + return ReactorUtils.logAsMono(() -> LOGGER.debug("The mailbox name length is over limit: {}", request.getMailboxName(), e)); }) - .onErrorResume(TooLongMailboxNameException.class, e -> { + .onErrorResume(MailboxException.class, e -> { no(request, responder, HumanReadableText.GENERIC_FAILURE_DURING_PROCESSING); - return ReactorUtils.logAsMono(() -> LOGGER.error("Create failed for mailbox {}", mailboxPath, e)); + return ReactorUtils.logAsMono(() -> LOGGER.error("Create failed for mailbox {}", request.getMailboxName(), e)); }) .then(); } From 631e9d1fe2a0de03eb59933d6b0115912bc71459 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Fri, 11 Oct 2024 18:40:45 +0200 Subject: [PATCH 30/37] JAMES-2182 Base test suite regarding IMAP right enforcements --- .../apache/james/mpt/api/ImapHostSystem.java | 2 + .../mpt/script/ImapScriptedTestProtocol.java | 17 +++ .../james/mpt/host/JamesImapHostSystem.java | 27 ++++ .../suite/IMAPSharingAccessTest.java | 143 ++++++++++++++++++ .../james/imap/scripts/SharingAccessL.test | 82 ++++++++++ .../james/imap/scripts/SharingAccessLR.test | 104 +++++++++++++ .../james/imap/scripts/SharingAccessLRA.test | 106 +++++++++++++ .../james/imap/scripts/SharingAccessLRI.test | 105 +++++++++++++ .../james/imap/scripts/SharingAccessLRK.test | 108 +++++++++++++ .../james/imap/scripts/SharingAccessLRS.test | 106 +++++++++++++ .../james/imap/scripts/SharingAccessLRT.test | 106 +++++++++++++ .../james/imap/scripts/SharingAccessLRTE.test | 108 +++++++++++++ .../james/imap/scripts/SharingAccessLRW.test | 106 +++++++++++++ .../james/imap/scripts/SharingAccessLRX.test | 105 +++++++++++++ .../cyrus/host/CyrusHostSystem.java | 5 + .../InMemoryIMAPSharingAccessTest.java | 42 +++++ 16 files changed, 1272 insertions(+) create mode 100644 mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/IMAPSharingAccessTest.java create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test create mode 100644 mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test create mode 100644 mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/InMemoryIMAPSharingAccessTest.java diff --git a/mpt/core/src/main/java/org/apache/james/mpt/api/ImapHostSystem.java b/mpt/core/src/main/java/org/apache/james/mpt/api/ImapHostSystem.java index 44980e5e0ce..4b246a0a948 100644 --- a/mpt/core/src/main/java/org/apache/james/mpt/api/ImapHostSystem.java +++ b/mpt/core/src/main/java/org/apache/james/mpt/api/ImapHostSystem.java @@ -31,6 +31,8 @@ public interface ImapHostSystem extends HostSystem { void createMailbox(MailboxPath mailboxPath) throws Exception; + void fillMailbox(MailboxPath mailboxPath) throws Exception; + void setQuotaLimits(QuotaCountLimit maxMessageQuota, QuotaSizeLimit maxStorageQuota) throws Exception; void grantRights(MailboxPath mailboxPath, Username userName, MailboxACL.Rfc4314Rights rights) throws Exception; diff --git a/mpt/core/src/main/java/org/apache/james/mpt/script/ImapScriptedTestProtocol.java b/mpt/core/src/main/java/org/apache/james/mpt/script/ImapScriptedTestProtocol.java index 5a5f8b34d79..c58f63d3607 100644 --- a/mpt/core/src/main/java/org/apache/james/mpt/script/ImapScriptedTestProtocol.java +++ b/mpt/core/src/main/java/org/apache/james/mpt/script/ImapScriptedTestProtocol.java @@ -40,6 +40,19 @@ public void prepare(ImapHostSystem system) throws Exception { } } + private static class FillMailbox implements PrepareCommand { + final MailboxPath mailboxPath; + + FillMailbox(MailboxPath mailboxPath) { + this.mailboxPath = mailboxPath; + } + + @Override + public void prepare(ImapHostSystem system) throws Exception { + system.fillMailbox(mailboxPath); + } + } + private static class CreateRights implements PrepareCommand { final MailboxPath mailboxPath; @@ -67,6 +80,10 @@ public ImapScriptedTestProtocol withMailbox(MailboxPath mailboxPath) { return withPreparedCommand(new CreateMailbox(mailboxPath)); } + public ImapScriptedTestProtocol withFilledMailbox(MailboxPath mailboxPath) { + return withPreparedCommand(new FillMailbox(mailboxPath)); + } + public ImapScriptedTestProtocol withRights(MailboxPath mailboxPath, Username username, MailboxACL.Rfc4314Rights rights) { return withPreparedCommand(new CreateRights(mailboxPath, username, rights)); } diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/host/JamesImapHostSystem.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/host/JamesImapHostSystem.java index eb48f6fe0cf..538c1760980 100644 --- a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/host/JamesImapHostSystem.java +++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/host/JamesImapHostSystem.java @@ -19,6 +19,8 @@ package org.apache.james.mpt.host; +import java.util.stream.IntStream; + import org.apache.commons.configuration2.HierarchicalConfiguration; import org.apache.commons.configuration2.ex.ConfigurationException; import org.apache.commons.configuration2.plist.PropertyListConfiguration; @@ -37,6 +39,8 @@ import org.apache.james.mailbox.Authorizator; import org.apache.james.mailbox.MailboxManager; import org.apache.james.mailbox.MailboxSession; +import org.apache.james.mailbox.MessageManager; +import org.apache.james.mailbox.exception.MailboxExistsException; import org.apache.james.mailbox.model.MailboxACL; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mpt.api.Continuation; @@ -46,6 +50,8 @@ import org.apache.james.mpt.imapmailbox.GrantRightsOnHost; import org.apache.james.user.memory.MemoryUsersRepository; +import com.github.fge.lambdas.Throwing; + public abstract class JamesImapHostSystem implements ImapHostSystem, GrantRightsOnHost { private static final DomainList NO_DOMAIN_LIST = null; @@ -108,6 +114,27 @@ public void createMailbox(MailboxPath mailboxPath) throws Exception { mailboxManager.endProcessingRequest(mailboxSession); } + @Override + public void fillMailbox(MailboxPath mailboxPath) throws Exception { + MailboxManager mailboxManager = getMailboxManager(); + MailboxSession mailboxSession = mailboxManager.createSystemSession(mailboxPath.getUser()); + mailboxManager.startProcessingRequest(mailboxSession); + + try { + mailboxManager.createMailbox(mailboxPath, mailboxSession); + } catch (MailboxExistsException e) { + // ignore + } + MessageManager mailbox = mailboxManager.getMailbox(mailboxPath, mailboxSession); + + IntStream.range(0, 10) + .forEach(Throwing.intConsumer(i -> + mailbox.appendMessage(MessageManager.AppendCommand.builder() + .build("Subject: " + mailboxPath.getName() + " " + i + "\r\n\r\nBODY " + i + "\r\n"), mailboxSession))); + + mailboxManager.endProcessingRequest(mailboxSession); + } + @Override public void grantRights(MailboxPath mailboxPath, Username username, MailboxACL.Rfc4314Rights rights) throws Exception { MailboxManager mailboxManager = getMailboxManager(); diff --git a/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/IMAPSharingAccessTest.java b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/IMAPSharingAccessTest.java new file mode 100644 index 00000000000..9f4b67cf97d --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/java/org/apache/james/mpt/imapmailbox/suite/IMAPSharingAccessTest.java @@ -0,0 +1,143 @@ +/**************************************************************** + * 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.mpt.imapmailbox.suite; + +import java.util.Locale; + +import org.apache.james.core.Username; +import org.apache.james.mailbox.model.MailboxACL; +import org.apache.james.mailbox.model.MailboxPath; +import org.apache.james.mpt.api.ImapHostSystem; +import org.apache.james.mpt.imapmailbox.ImapTestConstants; +import org.apache.james.mpt.imapmailbox.suite.base.BasicImapCommands; +import org.apache.james.mpt.script.ImapScriptedTestProtocol; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public abstract class IMAPSharingAccessTest implements ImapTestConstants { + public static final Username OTHER_USER_NAME = Username.of("Boby"); + public static final String OTHER_USER_PASSWORD = "password"; + + protected abstract ImapHostSystem createImapHostSystem(); + + private ImapHostSystem system; + private ImapScriptedTestProtocol scriptedTestProtocol; + + @BeforeEach + public void setUp() throws Exception { + system = createImapHostSystem(); + scriptedTestProtocol = new ImapScriptedTestProtocol("/org/apache/james/imap/scripts/", system) + .withUser(USER, PASSWORD) + .withUser(OTHER_USER_NAME, OTHER_USER_PASSWORD) + .withFilledMailbox(MailboxPath.inbox(USER)) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-l")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-l"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("l")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lr")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lr"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lr")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrs")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrs"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrs")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrw")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrw"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrw")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lri")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lri"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lri")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrk")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrk"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrx")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrx"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrx")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrt")) // todo + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrt"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrt")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrte")) // todo + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lrte"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrte")) + .withFilledMailbox(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lra")) + .withRights(MailboxPath.forUser(OTHER_USER_NAME, "mailbox-lra"), USER, MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lra")); + BasicImapCommands.welcome(scriptedTestProtocol); + BasicImapCommands.authenticate(scriptedTestProtocol); + } + + @Test + public void testMailboxL() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessL"); + } + + @Test + public void testMailboxLR() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLR"); + } + + @Test + public void testMailboxLRS() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRS"); + } + + @Test + public void testMailboxLRK() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRK"); + } + + @Test + public void testMailboxLRX() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRX"); + } + + @Test + public void testMailboxLRA() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRA"); + } + + @Test + public void testMailboxLRI() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRI"); + } + + @Test + public void testMailboxLRW() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRW"); + } + + @Test + public void testMailboxLRT() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRT"); + } + + @Test + public void testMailboxLRTE() throws Exception { + scriptedTestProtocol + .withLocale(Locale.US) + .run("SharingAccessLRTE"); + } +} diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test new file mode 100644 index 00000000000..60d9f64d04d --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test @@ -0,0 +1,82 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-l +S: \* MYRIGHTS \"#user.boby.mailbox-l\" \"l\" +S: a1 OK MYRIGHTS completed. + +# TODO should have had failed +C: a2 STATUS #user.boby.mailbox-l (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-l\" \(MESSAGES 0\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-l +S: a4 NO COPY processing failed. + +C: a5 DELETE #user.boby.mailbox-l +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-l imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-l. + +C: a7 CREATE #user.boby.mailbox-l.evev +S: a7 NO CREATE processing failed. + +# TODO should have had failed +C: a3 SELECT #user.boby.mailbox-l +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[PERMANENTFLAGS\] No permanent flags permitted +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-ONLY\] SELECT completed. + + diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test new file mode 100644 index 00000000000..1e6e2a4595b --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test @@ -0,0 +1,104 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lr +S: \* MYRIGHTS \"#user.boby.mailbox-lr\" \"lr\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lr (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lr\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-lr +S: a4 NO COPY processing failed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lr +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lr imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lr. + +C: a7 CREATE #user.boby.mailbox-lr.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lr +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-ONLY\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: F12 NO STORE failed. Save failed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: F13 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test new file mode 100644 index 00000000000..49975b13c02 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test @@ -0,0 +1,106 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lra +S: \* MYRIGHTS \"#user.boby.mailbox-lra\" \"alr\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lra (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lra\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-lra +S: a4 NO COPY processing failed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lra +S: a5 NO DELETE failed. No such mailbox. + +# TODO if I have 'a' right I shall be able to administer! +# org.apache.james.mailbox.exception.InsufficientRightsException: Setting ACL is only permitted to the owner of the mailbox +C: a5 SETACL #user.boby.mailbox-lra imapuser lra +S: a5 NO SETACL processing failed. + +C: a7 CREATE #user.boby.mailbox-lra.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lra +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-ONLY\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: F12 NO STORE failed. Save failed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: F13 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test new file mode 100644 index 00000000000..4a6e71579e1 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test @@ -0,0 +1,105 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lri +S: \* MYRIGHTS \"#user.boby.mailbox-lri\" \"ilr\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lri (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lri\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-lri +S: a4 OK .* COPY completed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lri +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lri imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lri. + +C: a7 CREATE #user.boby.mailbox-lri.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lri +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-WRITE\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +# TODO Insert is not flags +C: F12 STORE 1 +FLAGS (\Deleted) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) +S: F12 OK STORE completed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: F13 OK STORE completed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F14 OK STORE completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test new file mode 100644 index 00000000000..34a583572e0 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test @@ -0,0 +1,108 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrk +S: \* MYRIGHTS \"#user.boby.mailbox-lrk\" \"klr\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrk (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrk\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-lrk +S: a4 NO COPY processing failed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lrk +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrk imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrk. + +# TODO +# - MUST be able to create mailboxes +# - When a new mailbox is created, it SHOULD inherit the ACL from the parent mailbox (if one exists) in the defined hierarchy. +# CF https://github.com/giz-berlin/james-project/commit/9541be2da2a2da9a5bc5343e20ef152c5b473727#diff-78bbaf3505920ad8d1c2dfb95f2cbc4de2ed01c358645ad50e9b531483e3c25aR342 +C: a7 CREATE #user.boby.mailbox-lrk.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrk +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-ONLY\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: F12 NO STORE failed. Save failed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: F13 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test new file mode 100644 index 00000000000..add7607bf1a --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test @@ -0,0 +1,106 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrs +S: \* MYRIGHTS \"#user.boby.mailbox-lrs\" \"lrs\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrs (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrs\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +# TODO WTF I do not have 'i' right but I can copy? +C: a4 COPY 1:* #user.boby.mailbox-lrs +S: a4 OK .* COPY completed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lrs +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrs imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrs. + +C: a7 CREATE #user.boby.mailbox-lrs.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrs +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-WRITE\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +# TODO I shall only be able to manipulate \\Seen... +C: F12 STORE 1 +FLAGS (\Deleted) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) +S: F12 OK STORE completed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: F13 OK STORE completed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F14 OK STORE completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test new file mode 100644 index 00000000000..4dab79df207 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test @@ -0,0 +1,106 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrt +S: \* MYRIGHTS \"#user.boby.mailbox-lrt\" \"lrt\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrt (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrt\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +# TODO WTF I do not have 'i' right but I can copy? +C: a4 COPY 1:* #user.boby.mailbox-lrt +S: a4 OK .* COPY completed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lrt +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrt imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrt. + +C: a7 CREATE #user.boby.mailbox-lrt.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrt +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-WRITE\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) +S: F12 OK STORE completed. + +# TODO I shall only be able to manipulate \\Deleted... +C: F13 STORE 1 +FLAGS (\Seen) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: F13 OK STORE completed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F14 OK STORE completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test new file mode 100644 index 00000000000..30c9d77e9aa --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test @@ -0,0 +1,108 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrte +S: \* MYRIGHTS \"#user.boby.mailbox-lrte\" \"elrt\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrte (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrte\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +# TODO WTF I do not have 'i' right but I can copy? +C: a4 COPY 1:* #user.boby.mailbox-lrte +S: a4 OK .* COPY completed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lrte +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrte imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrte. + +C: a7 CREATE #user.boby.mailbox-lrte.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrte +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-WRITE\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) +S: F12 OK STORE completed. + +# TODO I shall only be able to manipulate \\Deleted... +C: F13 STORE 1 +FLAGS (\Seen) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: F13 OK STORE completed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F14 OK STORE completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: \* 1 EXPUNGE +S: \* 19 RECENT +S: F15 OK EXPUNGE completed. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test new file mode 100644 index 00000000000..c7fb43fa2bc --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test @@ -0,0 +1,106 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrw +S: \* MYRIGHTS \"#user.boby.mailbox-lrw\" \"lrw\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrw (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrw\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +# TODO WTF I do not have 'i' right but I can copy? +C: a4 COPY 1:* #user.boby.mailbox-lrw +S: a4 OK .* COPY completed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +C: a5 DELETE #user.boby.mailbox-lrw +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrw imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrw. + +C: a7 CREATE #user.boby.mailbox-lrw.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrw +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-WRITE\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +# TODO I shall only be able to manipulate \\Seen... +C: F12 STORE 1 +FLAGS (\Deleted) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) +S: F12 OK STORE completed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: F13 OK STORE completed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F14 OK STORE completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test new file mode 100644 index 00000000000..78548086e76 --- /dev/null +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test @@ -0,0 +1,105 @@ +################################################################ +# 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. # +################################################################ + +# Can list other users delegated mailbox +C: a0 LIST "" "*" +SUB { +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-l\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lr\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrs\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrw\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lri\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrx\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrk\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrt\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lrte\" +S: \* LIST \(\\HasNoChildren\) \".\" \"#user.boby.mailbox-lra\" +S: \* LIST \(\\HasNoChildren\) \".\" \"INBOX\" +} +S: a0 OK LIST completed. + +C: a1 MYRIGHTS #user.boby.mailbox-lrx +S: \* MYRIGHTS \"#user.boby.mailbox-lrx\" \"lrx\" +S: a1 OK MYRIGHTS completed. + +C: a2 STATUS #user.boby.mailbox-lrx (MESSAGES) +S: \* STATUS \"#user.boby.mailbox-lrx\" \(MESSAGES 10\) +S: a2 OK STATUS completed. + +# Ensure we cannot write in the mailbox +C: a4 SELECT INBOX +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a4 OK \[READ-WRITE\] SELECT completed. +C: a4 COPY 1:* #user.boby.mailbox-lrx +S: a4 NO COPY processing failed. +C: a6 UNSELECT +S: a6 OK UNSELECT completed. + +# TODO x right shall be enough to delete! +C: a5 DELETE #user.boby.mailbox-lrx +S: a5 NO DELETE failed. No such mailbox. + +C: a5 SETACL #user.boby.mailbox-lrx imapuser lra +S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrx. + +C: a7 CREATE #user.boby.mailbox-lrx.evev +S: a7 NO CREATE processing failed. + +C: a3 SELECT #user.boby.mailbox-lrx +S: \* OK \[MAILBOXID \(.*\)\] Ok +S: \* FLAGS \(.*\) +S: \* .* EXISTS +S: \* .* RECENT +S: \* OK \[UIDVALIDITY .*\] UIDs valid +S: \* OK \[UNSEEN 1\] MailboxMessage 1 is first unseen +S: \* OK \[PERMANENTFLAGS .*\] Limited +S: \* OK \[HIGHESTMODSEQ .*\] Highest +S: \* OK \[UIDNEXT .*\] Predicted next UID +S: a3 OK \[READ-ONLY\] SELECT completed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F12 STORE 1 +FLAGS (\Deleted) +S: F12 NO STORE failed. Save failed. + +C: F13 STORE 1 +FLAGS (\Seen) +S: F13 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F14 STORE 1 +FLAGS (\Flagged) +S: F14 NO STORE failed. Save failed. + +C: F11 FETCH 1 FLAGS +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) +S: F11 OK FETCH completed. + +C: F15 EXPUNGE +S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/cyrus/src/test/java/org/apache/james/mpt/imapmailbox/cyrus/host/CyrusHostSystem.java b/mpt/impl/imap-mailbox/cyrus/src/test/java/org/apache/james/mpt/imapmailbox/cyrus/host/CyrusHostSystem.java index 5572dcb3318..ebda38d9f3e 100644 --- a/mpt/impl/imap-mailbox/cyrus/src/test/java/org/apache/james/mpt/imapmailbox/cyrus/host/CyrusHostSystem.java +++ b/mpt/impl/imap-mailbox/cyrus/src/test/java/org/apache/james/mpt/imapmailbox/cyrus/host/CyrusHostSystem.java @@ -134,6 +134,11 @@ public void executeProtocolSession(ProtocolSession protocolSession) { } } + @Override + public void fillMailbox(MailboxPath mailboxPath) throws Exception { + throw new NotImplementedException("not implemented"); + } + @Override public void setQuotaLimits(QuotaCountLimit maxMessageQuota, QuotaSizeLimit maxStorageQuota) throws Exception { throw new NotImplementedException("not implemented"); diff --git a/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/InMemoryIMAPSharingAccessTest.java b/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/InMemoryIMAPSharingAccessTest.java new file mode 100644 index 00000000000..c1cc489dd42 --- /dev/null +++ b/mpt/impl/imap-mailbox/inmemory/src/test/java/org/apache/james/mpt/imapmailbox/inmemory/InMemoryIMAPSharingAccessTest.java @@ -0,0 +1,42 @@ +/**************************************************************** + * 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.mpt.imapmailbox.inmemory; + +import org.apache.james.mpt.api.ImapHostSystem; +import org.apache.james.mpt.imapmailbox.inmemory.host.InMemoryHostSystem; +import org.apache.james.mpt.imapmailbox.suite.IMAPSharingAccessTest; +import org.junit.jupiter.api.BeforeEach; + +public class InMemoryIMAPSharingAccessTest extends IMAPSharingAccessTest { + private ImapHostSystem system; + + @Override + @BeforeEach + public void setUp() throws Exception { + system = new InMemoryHostSystem(); + system.beforeTest(); + super.setUp(); + } + + @Override + protected ImapHostSystem createImapHostSystem() { + return system; + } +} From 67f82fbb3b8d3fe84d6ca8da93392bbf474c3848 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 10:12:59 +0200 Subject: [PATCH 31/37] JAMES-2182 Fix rights for CREATE --- .../james/mailbox/MailboxManagerTest.java | 76 +++++++++++++++++++ .../mailbox/store/StoreMailboxManager.java | 31 ++++++-- .../mailbox/store/StoreRightManager.java | 14 ++++ .../james/mpt/host/ExternalHostSystem.java | 5 ++ .../james/imap/scripts/SharingAccessLRK.test | 6 +- 5 files changed, 120 insertions(+), 12 deletions(-) diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java index 528dea7b8ca..8384acd1ebd 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java @@ -271,6 +271,82 @@ void hasInboxShouldBeTrueWhenINBOXIsCreated() throws Exception { assertThat(mailboxId.get()).isEqualTo(retrievedMailbox.getId()); } + @Test + void shareeShouldBeAbleToCreateMailbox() throws Exception { + // GIVEN USER1 shared his INBOX + session = mailboxManager.createSystemSession(USER_1); + MailboxPath mailboxPath = MailboxPath.inbox(session); + mailboxManager.createMailbox(mailboxPath, session); + mailboxManager.applyRightsCommand(mailboxPath, + MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_2)) + .rights(MailboxACL.Rfc4314Rights.of(ImmutableList.of(MailboxACL.Right.Lookup, + MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) + .asAddition(), session); + + // When USER_2 create a mailbox child + MailboxSession session2 = mailboxManager.createSystemSession(USER_2); + MailboxPath childPath = MailboxPath.inbox(session).child("child", session2.getPathDelimiter()); + mailboxManager.createMailbox(childPath, session2); + + // Then child path inherit rights + assertThat(mailboxManager.getMailbox(childPath, session) + .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) + .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); + } + + @Test + void shareeShouldBeAbleToCreateMailboxChildren() throws Exception { + // GIVEN USER1 shared his INBOX + session = mailboxManager.createSystemSession(USER_1); + MailboxPath mailboxPath = MailboxPath.inbox(session); + mailboxManager.createMailbox(mailboxPath, session); + mailboxManager.applyRightsCommand(mailboxPath, + MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_2)) + .rights(MailboxACL.Rfc4314Rights.of(ImmutableList.of(MailboxACL.Right.Lookup, + MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) + .asAddition(), session); + + // When USER_2 create a mailbox child + MailboxSession session2 = mailboxManager.createSystemSession(USER_2); + MailboxPath childPath = MailboxPath.inbox(session) + .child("child", session2.getPathDelimiter()) + .child("anotherkid", session2.getPathDelimiter()); + mailboxManager.createMailbox(childPath, session2); + + // Then child path inherit rights + assertThat(mailboxManager.getMailbox(childPath, session) + .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) + .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); + } + + @Test + void shareeShouldBeAbleToCreateMailboxChildrenIntermediatePaths() throws Exception { + // GIVEN USER1 shared his INBOX + session = mailboxManager.createSystemSession(USER_1); + MailboxPath mailboxPath = MailboxPath.inbox(session); + mailboxManager.createMailbox(mailboxPath, session); + mailboxManager.applyRightsCommand(mailboxPath, + MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_2)) + .rights(MailboxACL.Rfc4314Rights.of(ImmutableList.of(MailboxACL.Right.Lookup, + MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) + .asAddition(), session); + + // When USER_2 create a mailbox child + MailboxSession session2 = mailboxManager.createSystemSession(USER_2); + MailboxPath intermediatePath = MailboxPath.inbox(session) + .child("child", session2.getPathDelimiter()); + MailboxPath childPath = intermediatePath.child("anotherkid", session2.getPathDelimiter()); + mailboxManager.createMailbox(childPath, session2); + + // Then child path inherit rights + assertThat(mailboxManager.getMailbox(intermediatePath, session) + .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) + .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); + } + @Test void creatingMixedCaseINBOXShouldCreateItAsINBOX() throws Exception { session = mailboxManager.createSystemSession(USER_1); diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java index 37e68ce6bb7..5e7d20f951d 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java @@ -339,7 +339,7 @@ public Optional createMailbox(MailboxPath mailboxPath, CreateOption c public Mono createMailboxReactive(MailboxPath mailboxPath, CreateOption createOption, MailboxSession mailboxSession) { LOGGER.debug("createMailbox {}", mailboxPath); - return assertMailboxPathBelongToUserReactive(mailboxSession, mailboxPath) + return assertCanCreateReactive(mailboxSession, mailboxPath) .then(doCreateMailboxReactive(mailboxPath, createOption, mailboxSession)); } @@ -429,15 +429,32 @@ private Mono performConcurrentMailboxCreation(MailboxSession mailboxS LOGGER.info("{} mailbox was created concurrently", mailboxPath.asString()); return Mono.empty(); }) - .flatMap(any -> createSubscriptionIfNeeded(mailboxPath, createOption, mailboxSession).thenReturn(any)); + .flatMap(any -> createSubscriptionIfNeeded(mailboxPath, createOption, mailboxSession).thenReturn(any)) + .flatMap(any -> inheritRightsReactive(mailboxSession, mailboxPath).thenReturn(any)); } - private Mono assertMailboxPathBelongToUserReactive(MailboxSession mailboxSession, MailboxPath mailboxPath) { - if (!mailboxPath.belongsTo(mailboxSession)) { - return Mono.error(new InsufficientRightsException("mailboxPath '" + mailboxPath.asString() + "'" - + " does not belong to user '" + mailboxSession.getUser().asString() + "'")); + + private Mono assertCanCreateReactive(MailboxSession session, MailboxPath path) { + if (path.belongsTo(session)) { + return Mono.empty(); } - return Mono.empty(); + + return nearestExistingParent(session, path) + .filterWhen(parent -> hasRightReactive(parent, Right.CreateMailbox, session)) + .switchIfEmpty(Mono.error(new InsufficientRightsException("user '" + session.getUser().asString() + "' is not allowed to create the mailbox '" + path.asString() + "'"))) + .then(); + } + + private Mono nearestExistingParent(MailboxSession session, MailboxPath path) { + return Flux.fromIterable(path.getParents(session.getPathDelimiter()).reversed()) + .filterWhen(parent -> mailboxExists(parent, session)) + .next(); + } + + private Mono inheritRightsReactive(MailboxSession mailboxSession, MailboxPath path) { + return nearestExistingParent(mailboxSession, path) + .flatMap(parent -> Mono.from(listRightsReactive(parent, mailboxSession))) + .flatMap(acl -> storeRightManager.setRightsReactiveWithoutAccessControl(path, acl, mailboxSession)); } @Override diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java index 47faddf9a06..5eab78dc2f2 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java @@ -335,4 +335,18 @@ static MailboxACL filteredForSession(Mailbox mailbox, MailboxACL acl, MailboxSes } return new MailboxACL(ImmutableMap.of(userAsKey, rights)); } + + /** Sets an ACL for a mailbox *WITHOUT* checking if the user of current session is allowed to do so. + * We need this when creating a mailbox, to copy the ACL of the parent mailbox for all users. + */ + public Mono setRightsReactiveWithoutAccessControl(MailboxPath mailboxPath, MailboxACL mailboxACL, MailboxSession session) { + try { + assertSharesBelongsToUserDomain(mailboxPath.getUser(), mailboxACL.getEntries()); + } catch (DifferentDomainException e) { + return Mono.error(e); + } + MailboxMapper mapper = mailboxSessionMapperFactory.getMailboxMapper(session); + return mapper.findMailboxByPath(mailboxPath) + .flatMap(Throwing.>function(mailbox -> setRights(mailboxACL, mapper, mailbox, session)).sneakyThrow()); + } } diff --git a/mpt/core/src/main/java/org/apache/james/mpt/host/ExternalHostSystem.java b/mpt/core/src/main/java/org/apache/james/mpt/host/ExternalHostSystem.java index dbc1b133855..ac45ae29e1f 100644 --- a/mpt/core/src/main/java/org/apache/james/mpt/host/ExternalHostSystem.java +++ b/mpt/core/src/main/java/org/apache/james/mpt/host/ExternalHostSystem.java @@ -126,4 +126,9 @@ public void setQuotaLimits(QuotaCountLimit maxMessageQuota, QuotaSizeLimit maxSt public void grantRights(MailboxPath mailboxPath, Username userName, MailboxACL.Rfc4314Rights rights) throws Exception { throw new NotImplementedException("Not implemented"); } + + @Override + public void fillMailbox(MailboxPath mailboxPath) throws Exception { + throw new NotImplementedException("Not implemented"); + } } diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test index 34a583572e0..57da76bf116 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test @@ -65,12 +65,8 @@ S: a5 NO DELETE failed. No such mailbox. C: a5 SETACL #user.boby.mailbox-lrk imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrk. -# TODO -# - MUST be able to create mailboxes -# - When a new mailbox is created, it SHOULD inherit the ACL from the parent mailbox (if one exists) in the defined hierarchy. -# CF https://github.com/giz-berlin/james-project/commit/9541be2da2a2da9a5bc5343e20ef152c5b473727#diff-78bbaf3505920ad8d1c2dfb95f2cbc4de2ed01c358645ad50e9b531483e3c25aR342 C: a7 CREATE #user.boby.mailbox-lrk.evev -S: a7 NO CREATE processing failed. +S: a7 OK \[MAILBOXID \(.*\)\] CREATE completed. C: a3 SELECT #user.boby.mailbox-lrk S: \* OK \[MAILBOXID \(.*\)\] Ok From 48aa43a1a30ccbc2de01069059ff992823f78e76 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 10:28:19 +0200 Subject: [PATCH 32/37] JAMES-2182 Fix rights for DELETE --- .../james/mailbox/MailboxManagerTest.java | 34 +++++--- .../mailbox/store/StoreMailboxManager.java | 20 +++-- .../james/imap/scripts/SharingAccessL.test | 2 +- .../james/imap/scripts/SharingAccessLR.test | 2 +- .../james/imap/scripts/SharingAccessLRA.test | 2 +- .../james/imap/scripts/SharingAccessLRI.test | 2 +- .../james/imap/scripts/SharingAccessLRK.test | 2 +- .../james/imap/scripts/SharingAccessLRS.test | 2 +- .../james/imap/scripts/SharingAccessLRT.test | 2 +- .../james/imap/scripts/SharingAccessLRTE.test | 2 +- .../james/imap/scripts/SharingAccessLRW.test | 2 +- .../james/imap/scripts/SharingAccessLRX.test | 8 +- .../contract/MailboxSetMethodContract.scala | 80 ++++++++++++++++++- 13 files changed, 131 insertions(+), 29 deletions(-) diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java index 8384acd1ebd..3f851fa6e71 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java @@ -273,7 +273,7 @@ void hasInboxShouldBeTrueWhenINBOXIsCreated() throws Exception { @Test void shareeShouldBeAbleToCreateMailbox() throws Exception { - // GIVEN USER1 shared his INBOX + assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL)); session = mailboxManager.createSystemSession(USER_1); MailboxPath mailboxPath = MailboxPath.inbox(session); mailboxManager.createMailbox(mailboxPath, session); @@ -284,12 +284,10 @@ void shareeShouldBeAbleToCreateMailbox() throws Exception { MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) .asAddition(), session); - // When USER_2 create a mailbox child MailboxSession session2 = mailboxManager.createSystemSession(USER_2); MailboxPath childPath = MailboxPath.inbox(session).child("child", session2.getPathDelimiter()); mailboxManager.createMailbox(childPath, session2); - // Then child path inherit rights assertThat(mailboxManager.getMailbox(childPath, session) .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); @@ -297,7 +295,7 @@ void shareeShouldBeAbleToCreateMailbox() throws Exception { @Test void shareeShouldBeAbleToCreateMailboxChildren() throws Exception { - // GIVEN USER1 shared his INBOX + assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL)); session = mailboxManager.createSystemSession(USER_1); MailboxPath mailboxPath = MailboxPath.inbox(session); mailboxManager.createMailbox(mailboxPath, session); @@ -308,14 +306,12 @@ void shareeShouldBeAbleToCreateMailboxChildren() throws Exception { MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) .asAddition(), session); - // When USER_2 create a mailbox child MailboxSession session2 = mailboxManager.createSystemSession(USER_2); MailboxPath childPath = MailboxPath.inbox(session) .child("child", session2.getPathDelimiter()) .child("anotherkid", session2.getPathDelimiter()); mailboxManager.createMailbox(childPath, session2); - // Then child path inherit rights assertThat(mailboxManager.getMailbox(childPath, session) .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); @@ -323,7 +319,7 @@ void shareeShouldBeAbleToCreateMailboxChildren() throws Exception { @Test void shareeShouldBeAbleToCreateMailboxChildrenIntermediatePaths() throws Exception { - // GIVEN USER1 shared his INBOX + assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL)); session = mailboxManager.createSystemSession(USER_1); MailboxPath mailboxPath = MailboxPath.inbox(session); mailboxManager.createMailbox(mailboxPath, session); @@ -334,19 +330,37 @@ void shareeShouldBeAbleToCreateMailboxChildrenIntermediatePaths() throws Excepti MailboxACL.Right.Read, MailboxACL.Right.CreateMailbox))) .asAddition(), session); - // When USER_2 create a mailbox child MailboxSession session2 = mailboxManager.createSystemSession(USER_2); MailboxPath intermediatePath = MailboxPath.inbox(session) .child("child", session2.getPathDelimiter()); MailboxPath childPath = intermediatePath.child("anotherkid", session2.getPathDelimiter()); mailboxManager.createMailbox(childPath, session2); - // Then child path inherit rights assertThat(mailboxManager.getMailbox(intermediatePath, session) .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_2))) .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("lrk")); } + @Test + void shareeShouldBeAbleToDeleteMailbox() throws Exception { + assumeTrue(mailboxManager.hasCapability(MailboxCapabilities.ACL)); + session = mailboxManager.createSystemSession(USER_1); + MailboxPath mailboxPath = MailboxPath.forUser(USER_1, "child"); + mailboxManager.createMailbox(mailboxPath, session); + mailboxManager.applyRightsCommand(mailboxPath, + MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_2)) + .rights(MailboxACL.Rfc4314Rights.of(ImmutableList.of(MailboxACL.Right.Lookup, + MailboxACL.Right.Read, MailboxACL.Right.DeleteMailbox))) + .asAddition(), session); + + MailboxSession session2 = mailboxManager.createSystemSession(USER_2); + mailboxManager.deleteMailbox(mailboxPath, session2); + + assertThatThrownBy(() -> mailboxManager.getMailbox(mailboxPath, session)) + .isInstanceOf(MailboxNotFoundException.class); + } + @Test void creatingMixedCaseINBOXShouldCreateItAsINBOX() throws Exception { session = mailboxManager.createSystemSession(USER_1); @@ -2195,7 +2209,7 @@ void user2ShouldNotBeAbleToDeleteUser1Mailbox() throws Exception { mailboxManager.createMailbox(inbox, sessionUser1); assertThatThrownBy(() -> mailboxManager.deleteMailbox(inbox, sessionUser2)) - .isInstanceOf(MailboxNotFoundException.class); + .isInstanceOf(InsufficientRightsException.class); } diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java index 5e7d20f951d..6f9548fe259 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java @@ -460,10 +460,10 @@ private Mono inheritRightsReactive(MailboxSession mailboxSession, MailboxP @Override public void deleteMailbox(final MailboxPath mailboxPath, final MailboxSession session) throws MailboxException { LOGGER.info("deleteMailbox {}", mailboxPath); - assertIsOwner(session, mailboxPath); MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session); mailboxMapper.execute(() -> block(mailboxMapper.findMailboxByPath(mailboxPath) + .filterWhen(mailbox -> assertCanDeleteReactive(session, mailbox.generateAssociatedPath()).thenReturn(true)) .flatMap(mailbox -> doDeleteMailbox(mailboxMapper, mailbox, session)) .switchIfEmpty(Mono.error(() -> new MailboxNotFoundException(mailboxPath))))); } @@ -497,18 +497,28 @@ public Mono deleteMailboxReactive(MailboxId mailboxId, MailboxSession s @Override public Mono deleteMailboxReactive(MailboxPath mailboxPath, MailboxSession session) { LOGGER.info("deleteMailbox {}", mailboxPath); - if (!mailboxPath.belongsTo(session)) { - LOGGER.info("Mailbox {} does not belong to {}", mailboxPath.asString(), session.getUser().asString()); - return Mono.error(new MailboxNotFoundException(mailboxPath.asString())); - } MailboxMapper mailboxMapper = mailboxSessionMapperFactory.getMailboxMapper(session); return mailboxMapper.executeReactive(mailboxMapper.findMailboxByPath(mailboxPath) + .filterWhen(mailbox -> assertCanDeleteReactive(session, mailbox.generateAssociatedPath()).thenReturn(true)) .flatMap(mailbox -> doDeleteMailbox(mailboxMapper, mailbox, session)) .switchIfEmpty(Mono.error(() -> new MailboxNotFoundException(mailboxPath)))) .then(); } + private Mono assertCanDeleteReactive(MailboxSession session, MailboxPath path) { + if (path.belongsTo(session)) { + return Mono.empty(); + } + return Mono.from(hasRightReactive(path, Right.DeleteMailbox, session)) + .flatMap(hasRight -> { + if (hasRight) { + return Mono.empty(); + } + return Mono.error(new InsufficientRightsException("user '" + session.getUser().asString() + "' is not allowed to delete the mailbox '" + path.asString() + "'")); + }); + } + private Mono doDeleteMailbox(MailboxMapper mailboxMapper, Mailbox mailbox, MailboxSession session) { MessageMapper messageMapper = mailboxSessionMapperFactory.getMessageMapper(session); diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test index 60d9f64d04d..004cb21dbdb 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test @@ -59,7 +59,7 @@ C: a4 COPY 1:* #user.boby.mailbox-l S: a4 NO COPY processing failed. C: a5 DELETE #user.boby.mailbox-l -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-l imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-l. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test index 1e6e2a4595b..8d7212a5eab 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLR.test @@ -60,7 +60,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lr -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lr imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lr. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test index 49975b13c02..c08191f1325 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test @@ -60,7 +60,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lra -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. # TODO if I have 'a' right I shall be able to administer! # org.apache.james.mailbox.exception.InsufficientRightsException: Setting ACL is only permitted to the owner of the mailbox diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test index 4a6e71579e1..fc1bf6cdd6b 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test @@ -60,7 +60,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lri -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lri imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lri. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test index 57da76bf116..ea62d95e0dc 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRK.test @@ -60,7 +60,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lrk -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lrk imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrk. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test index add7607bf1a..867f624b357 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test @@ -61,7 +61,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lrs -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lrs imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrs. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test index 4dab79df207..d972b0da754 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test @@ -61,7 +61,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lrt -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lrt imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrt. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test index 30c9d77e9aa..9bf858fc3c3 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test @@ -61,7 +61,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lrte -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lrte imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrte. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test index c7fb43fa2bc..4056b27f716 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test @@ -61,7 +61,7 @@ C: a6 UNSELECT S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lrw -S: a5 NO DELETE failed. No such mailbox. +S: a5 NO DELETE processing failed. C: a5 SETACL #user.boby.mailbox-lrw imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrw. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test index 78548086e76..e3f31d9291f 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test @@ -59,9 +59,6 @@ S: a4 NO COPY processing failed. C: a6 UNSELECT S: a6 OK UNSELECT completed. -# TODO x right shall be enough to delete! -C: a5 DELETE #user.boby.mailbox-lrx -S: a5 NO DELETE failed. No such mailbox. C: a5 SETACL #user.boby.mailbox-lrx imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrx. @@ -102,4 +99,7 @@ S: \* 1 FETCH \(FLAGS \(\\Recent\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE -S: F15 NO EXPUNGE failed. Mailbox is read only. \ No newline at end of file +S: F15 NO EXPUNGE failed. Mailbox is read only. + +C: a5 DELETE #user.boby.mailbox-lrx +S: a5 OK DELETE completed. \ No newline at end of file diff --git a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala index 5e9a9c3455d..8c36e10f0ea 100644 --- a/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala +++ b/server/protocols/jmap-rfc-8621-integration-tests/jmap-rfc-8621-integration-tests-common/src/main/scala/org/apache/james/jmap/rfc8621/contract/MailboxSetMethodContract.scala @@ -2116,7 +2116,7 @@ trait MailboxSetMethodContract { val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path) server.getProbe(classOf[ACLProbeImpl]) - .replaceRights(path, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read, Right.CreateMailbox)) + .replaceRights(path, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read)) val request = s""" |{ @@ -2173,6 +2173,84 @@ trait MailboxSetMethodContract { |}""".stripMargin) } + @Test + def mailboxSetShouldCreateChildMailboxWhenSharedParentMailboxWithCreateRight(server: GuiceJamesServer): Unit = { + val path = MailboxPath.forUser(ANDRE, "mailbox") + val mailboxId: MailboxId = server.getProbe(classOf[MailboxProbeImpl]).createMailbox(path) + + server.getProbe(classOf[ACLProbeImpl]) + .replaceRights(path, BOB.asString, new MailboxACL.Rfc4314Rights(Right.Lookup, Right.Read, Right.CreateMailbox)) + val request = + s""" + |{ + | "using": [ "urn:ietf:params:jmap:core", "urn:ietf:params:jmap:mail" ], + | "methodCalls": [ + | [ + | "Mailbox/set", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "create": { + | "C42": { + | "name": "childMailbox", + | "parentId":"${mailboxId.serialize}" + | } + | } + | }, + | "c1" + | ] + | ] + |} + |""".stripMargin + + val response = `given` + .header(ACCEPT.toString, ACCEPT_RFC8621_VERSION_HEADER) + .body(request) + .when + .post + .`then` + .log().ifValidationFails() + .statusCode(SC_OK) + .contentType(JSON) + .extract + .body + .asString + + assertThatJson(response) + .whenIgnoringPaths("methodResponses[0][1].newState", "methodResponses[0][1].oldState") + .isEqualTo( + s"""{ + | "sessionState": "${SESSION_STATE.value}", + | "methodResponses": [[ + | "Mailbox/set", + | { + | "accountId": "29883977c13473ae7cb7678ef767cbfbaffc8a44a6e463d971d23a65c1dc4af6", + | "created":{ + | "C42":{ + | "id":"3", + | "isSubscribed":true, + | "myRights":{ + | "mayAddItems":true, + | "mayCreateChild":true, + | "mayDelete":true, + | "mayReadItems":true, + | "mayRemoveItems":true, + | "mayRename":true, + | "maySetKeywords":true, + | "maySetSeen":true, + | "maySubmit":true + | }, + | "sortOrder":1000, + | "totalEmails":0, + | "totalThreads":0, + | "unreadEmails":0, + | "unreadThreads":0 + | } + | } + | }, + | "c1"]] + |}""".stripMargin) + } + @Test @Tag(CategoryTags.BASIC_FEATURE) def deleteShouldSucceedWhenMailboxExists(server: GuiceJamesServer): Unit = { From 8a93a83b4af139e37015c43550aecaf9924ad1a3 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 10:47:45 +0200 Subject: [PATCH 33/37] JAMES-2182 Fix rights for SETACL --- .../james/mailbox/MailboxManagerTest.java | 20 +++++++++++++++++++ .../mailbox/store/StoreRightManager.java | 7 ++++--- .../james/imap/scripts/SharingAccessLRA.test | 4 +--- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java index 3f851fa6e71..bbe3df11e69 100644 --- a/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java +++ b/mailbox/api/src/test/java/org/apache/james/mailbox/MailboxManagerTest.java @@ -3134,6 +3134,26 @@ void setRightsShouldThrowOnDeletedMailbox() throws Exception { @Test void setRightsByIdShouldThrowWhenNotOwner() throws Exception { + MailboxPath mailboxPath = MailboxPath.forUser(USER_2, "mailbox"); + MailboxId id = mailboxManager.createMailbox(mailboxPath, session2).get(); + mailboxManager.setRights(id, MailboxACL.EMPTY.apply(MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_1)) + .rights(new MailboxACL.Rfc4314Rights(MailboxACL.Right.Lookup, MailboxACL.Right.Administer, MailboxACL.Right.Read)) + .asAddition()), session2); + + mailboxManager.setRights(id, MailboxACL.EMPTY.apply( + MailboxACL.command() + .key(MailboxACL.EntryKey.createUserEntryKey(USER_1)) + .rights(MailboxACL.FULL_RIGHTS) + .asAddition()), session); + + assertThat(mailboxManager.getMailbox(mailboxPath, session2) + .getMailboxEntity().getACL().getEntries().get(MailboxACL.EntryKey.createUserEntryKey(USER_1))) + .isEqualTo(MailboxACL.Rfc4314Rights.fromSerializedRfc4314Rights("aeiklprstwx")); + } + + @Test + void setRightsByIdShouldThrowWhenNotAdministrator() throws Exception { MailboxId id = mailboxManager.createMailbox(MailboxPath.forUser(USER_2, "mailbox"), session2).get(); mailboxManager.setRights(id, MailboxACL.EMPTY.apply(MailboxACL.command() .key(MailboxACL.EntryKey.createUserEntryKey(USER_1)) diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java index 5eab78dc2f2..1a22ae328ed 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreRightManager.java @@ -275,10 +275,11 @@ public void setRights(MailboxPath mailboxPath, MailboxACL mailboxACL, MailboxSes private void assertHaveAccessTo(Mailbox mailbox, MailboxSession session) throws InsufficientRightsException, MailboxNotFoundException { if (!mailbox.generateAssociatedPath().belongsTo(session)) { - if (mailbox.getACL().getEntries().containsKey(EntryKey.createUserEntryKey(session.getUser()))) { - throw new InsufficientRightsException("Setting ACL is only permitted to the owner of the mailbox"); - } else { + Rfc4314Rights acl = mailbox.getACL().getEntries().get(EntryKey.createUserEntryKey(session.getUser())); + if (acl == null) { throw new MailboxNotFoundException(mailbox.getMailboxId()); + } else if (!acl.contains(Right.Administer)) { + throw new InsufficientRightsException("Setting ACL is only permitted to the owner and admins of the mailbox"); } } } diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test index c08191f1325..5b98e9458a6 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRA.test @@ -62,10 +62,8 @@ S: a6 OK UNSELECT completed. C: a5 DELETE #user.boby.mailbox-lra S: a5 NO DELETE processing failed. -# TODO if I have 'a' right I shall be able to administer! -# org.apache.james.mailbox.exception.InsufficientRightsException: Setting ACL is only permitted to the owner of the mailbox C: a5 SETACL #user.boby.mailbox-lra imapuser lra -S: a5 NO SETACL processing failed. +S: a5 OK SETACL completed. C: a7 CREATE #user.boby.mailbox-lra.evev S: a7 NO CREATE processing failed. From c935acf9ee5edbb228695080396dcd00ec242d6b Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 16:28:41 +0200 Subject: [PATCH 34/37] JAMES-2182 Fix rights for APPEND, MOVE, COPY --- .../james/mailbox/store/StoreMessageManager.java | 13 +++++++++++++ .../apache/james/imap/scripts/SharingAccessLRS.test | 5 +++-- .../apache/james/imap/scripts/SharingAccessLRT.test | 5 +++-- .../james/imap/scripts/SharingAccessLRTE.test | 7 ++++--- .../apache/james/imap/scripts/SharingAccessLRW.test | 5 +++-- .../apache/james/imap/scripts/SharingAccessLRX.test | 3 ++- 6 files changed, 28 insertions(+), 10 deletions(-) diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java index 79f3b522453..61e5fe4a415 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java @@ -63,6 +63,7 @@ import org.apache.james.mailbox.MetadataWithMailboxId; import org.apache.james.mailbox.ModSeq; import org.apache.james.mailbox.events.MailboxIdRegistrationKey; +import org.apache.james.mailbox.exception.InsufficientRightsException; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.ReadOnlyException; import org.apache.james.mailbox.exception.UnsupportedRightException; @@ -408,6 +409,9 @@ private Mono appendMessage(Content msgIn, Date internalDate, final if (!isWriteable(mailboxSession)) { throw new ReadOnlyException(getMailboxPath()); } + if (!storeRightManager.myRights(mailbox, mailboxSession).contains(MailboxACL.Right.Insert)) { + throw new InsufficientRightsException("Append messages requires 'i' right"); + } try (InputStream contentStream = msgIn.getInputStream(); UnsynchronizedFilterInputStream bufferedContentStream = UnsynchronizedBufferedInputStream.builder() @@ -733,6 +737,9 @@ public Flux copyTo(MessageRange set, StoreMessageManager toMailbox if (!toMailbox.isWriteable(session)) { return Flux.error(new ReadOnlyException(toMailbox.getMailboxPath())); } + if (!storeRightManager.myRights(toMailbox.mailbox, session).contains(MailboxACL.Right.Insert)) { + return Flux.error(new InsufficientRightsException("Append messages requires 'i' right")); + } //TODO lock the from mailbox too, in a non-deadlocking manner - how? return Flux.from(locker.executeReactiveWithLockReactive(toMailbox.getMailboxPath(), copy(set, toMailbox, session) @@ -747,9 +754,15 @@ public Flux moveTo(MessageRange set, StoreMessageManager toMailbox if (!isWriteable(session)) { return Flux.error(new ReadOnlyException(toMailbox.getMailboxPath())); } + if (!storeRightManager.myRights(mailbox, session).contains(MailboxACL.Right.PerformExpunge)) { + return Flux.error(new InsufficientRightsException("Deleting messages requires 'e' right")); + } if (!toMailbox.isWriteable(session)) { return Flux.error(new ReadOnlyException(toMailbox.getMailboxPath())); } + if (!storeRightManager.myRights(toMailbox.mailbox, session).contains(MailboxACL.Right.Insert)) { + return Flux.error(new InsufficientRightsException("Append messages requires 'i' right")); + } //TODO lock the from mailbox too, in a non-deadlocking manner - how? return Flux.from(locker.executeReactiveWithLockReactive(toMailbox.getMailboxPath(), move(set, toMailbox, session) diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test index 867f624b357..d2bb291817f 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test @@ -54,9 +54,10 @@ S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited S: \* OK \[HIGHESTMODSEQ .*\] Highest S: \* OK \[UIDNEXT .*\] Predicted next UID S: a4 OK \[READ-WRITE\] SELECT completed. -# TODO WTF I do not have 'i' right but I can copy? + C: a4 COPY 1:* #user.boby.mailbox-lrs -S: a4 OK .* COPY completed. +S: a4 NO COPY processing failed. + C: a6 UNSELECT S: a6 OK UNSELECT completed. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test index d972b0da754..579e7905ff3 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test @@ -54,9 +54,10 @@ S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited S: \* OK \[HIGHESTMODSEQ .*\] Highest S: \* OK \[UIDNEXT .*\] Predicted next UID S: a4 OK \[READ-WRITE\] SELECT completed. -# TODO WTF I do not have 'i' right but I can copy? + C: a4 COPY 1:* #user.boby.mailbox-lrt -S: a4 OK .* COPY completed. +S: a4 NO COPY processing failed. + C: a6 UNSELECT S: a6 OK UNSELECT completed. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test index 9bf858fc3c3..eef28d57b9a 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test @@ -54,9 +54,10 @@ S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited S: \* OK \[HIGHESTMODSEQ .*\] Highest S: \* OK \[UIDNEXT .*\] Predicted next UID S: a4 OK \[READ-WRITE\] SELECT completed. -# TODO WTF I do not have 'i' right but I can copy? + C: a4 COPY 1:* #user.boby.mailbox-lrte -S: a4 OK .* COPY completed. +S: a4 NO COPY processing failed. + C: a6 UNSELECT S: a6 OK UNSELECT completed. @@ -104,5 +105,5 @@ S: F11 OK FETCH completed. C: F15 EXPUNGE S: \* 1 EXPUNGE -S: \* 19 RECENT +S: \* 9 RECENT S: F15 OK EXPUNGE completed. \ No newline at end of file diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test index 4056b27f716..569879821dc 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test @@ -54,9 +54,10 @@ S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited S: \* OK \[HIGHESTMODSEQ .*\] Highest S: \* OK \[UIDNEXT .*\] Predicted next UID S: a4 OK \[READ-WRITE\] SELECT completed. -# TODO WTF I do not have 'i' right but I can copy? + C: a4 COPY 1:* #user.boby.mailbox-lrw -S: a4 OK .* COPY completed. +S: a4 NO COPY processing failed. + C: a6 UNSELECT S: a6 OK UNSELECT completed. diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test index e3f31d9291f..3c1bb1107c9 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRX.test @@ -54,12 +54,13 @@ S: \* OK \[PERMANENTFLAGS \(.*\)\] Limited S: \* OK \[HIGHESTMODSEQ .*\] Highest S: \* OK \[UIDNEXT .*\] Predicted next UID S: a4 OK \[READ-WRITE\] SELECT completed. + C: a4 COPY 1:* #user.boby.mailbox-lrx S: a4 NO COPY processing failed. + C: a6 UNSELECT S: a6 OK UNSELECT completed. - C: a5 SETACL #user.boby.mailbox-lrx imapuser lra S: a5 NO SETACL You need the Administer right to perform command SETACL on mailbox #user.boby.mailbox-lrx. From f1946c9ba28929ee47e974efef49e6280f764537 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 16:37:06 +0200 Subject: [PATCH 35/37] JAMES-2182 Fix rights for SELECT, STATUS --- .../apache/james/imap/scripts/SharingAccessL.test | 15 ++------------- .../processor/AbstractSelectionProcessor.java | 9 ++++++++- .../james/imap/processor/StatusProcessor.java | 9 +++++++++ 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test index 004cb21dbdb..63df2847d44 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessL.test @@ -38,10 +38,8 @@ C: a1 MYRIGHTS #user.boby.mailbox-l S: \* MYRIGHTS \"#user.boby.mailbox-l\" \"l\" S: a1 OK MYRIGHTS completed. -# TODO should have had failed C: a2 STATUS #user.boby.mailbox-l (MESSAGES) -S: \* STATUS \"#user.boby.mailbox-l\" \(MESSAGES 0\) -S: a2 OK STATUS completed. +S: a2 NO STATUS failed. Status failed. # Ensure we cannot write in the mailbox C: a4 SELECT INBOX @@ -67,16 +65,7 @@ S: a5 NO SETACL You need the Administer right to perform command SETACL on mailb C: a7 CREATE #user.boby.mailbox-l.evev S: a7 NO CREATE processing failed. -# TODO should have had failed C: a3 SELECT #user.boby.mailbox-l -S: \* OK \[MAILBOXID \(.*\)\] Ok -S: \* FLAGS \(.*\) -S: \* .* EXISTS -S: \* .* RECENT -S: \* OK \[UIDVALIDITY .*\] UIDs valid -S: \* OK \[PERMANENTFLAGS\] No permanent flags permitted -S: \* OK \[HIGHESTMODSEQ .*\] Highest -S: \* OK \[UIDNEXT .*\] Predicted next UID -S: a3 OK \[READ-ONLY\] SELECT completed. +S: a3 NO SELECT failed. 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 a5df7d2e355..cd32c1ad30d 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 @@ -58,6 +58,7 @@ import org.apache.james.mailbox.MessageManager.MailboxMetaData; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; +import org.apache.james.mailbox.exception.InsufficientRightsException; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.model.MailboxACL; @@ -109,7 +110,7 @@ protected Mono processRequestReactive(R request, ImapSession session, Resp return ReactorUtils.logAsMono(() -> LOGGER.debug("Select failed as mailbox does not exist {}", mailboxName, e)); }) .onErrorResume(MailboxException.class, e -> { - no(request, responder, HumanReadableText.SELECT); + no(request, responder, HumanReadableText.FAILED); return ReactorUtils.logAsMono(() -> LOGGER.error("Select failed for mailbox {}", mailboxName, e)); }); } @@ -400,6 +401,12 @@ private Mono selectMailbox(MailboxPath mailboxPath, ImapSession final SelectedMailbox currentMailbox = session.getSelected(); return Mono.from(mailboxManager.getMailboxReactive(mailboxPath, mailboxSession)) + .handle(Throwing.biConsumer((mailbox, sink) -> { + if (mailboxManager.hasRight(mailbox.getMailboxEntity(), MailboxACL.Right.Read, mailboxSession)) { + sink.next(mailbox); + } + sink.error(new InsufficientRightsException("'r' right is needed to select a mailbox")); + })) .flatMap(Throwing.function(mailbox -> selectMailbox(session, responder, mailbox, currentMailbox) .flatMap(Throwing.function(sessionMailbox -> mailbox.getMetaDataReactive(recentMode(!openReadOnly, mailbox, mailboxSession), mailboxSession, EnumSet.of(MailboxMetaData.Item.FirstUnseen, MailboxMetaData.Item.HighestModSeq, MailboxMetaData.Item.NextUid, MailboxMetaData.Item.MailboxCounters)) 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 d1f81143215..361fc8f5b03 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 @@ -44,10 +44,12 @@ import org.apache.james.mailbox.MessageManager.MailboxMetaData.RecentMode; import org.apache.james.mailbox.MessageUid; import org.apache.james.mailbox.ModSeq; +import org.apache.james.mailbox.exception.InsufficientRightsException; import org.apache.james.mailbox.exception.MailboxException; import org.apache.james.mailbox.exception.MailboxNotFoundException; import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData; import org.apache.james.mailbox.model.FetchGroup; +import org.apache.james.mailbox.model.MailboxACL; import org.apache.james.mailbox.model.MailboxId; import org.apache.james.mailbox.model.MailboxPath; import org.apache.james.mailbox.model.MessageRange; @@ -59,6 +61,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; import reactor.core.publisher.Flux; @@ -121,6 +124,12 @@ protected Mono processRequestReactive(StatusRequest request, ImapSession s private Mono sendStatus(MailboxPath mailboxPath, StatusDataItems statusDataItems, Responder responder, ImapSession session, MailboxSession mailboxSession) { return Mono.from(getMailboxManager().getMailboxReactive(mailboxPath, mailboxSession)) + .handle(Throwing.biConsumer((mailbox, sink) -> { + if (getMailboxManager().hasRight(mailbox.getMailboxEntity(), MailboxACL.Right.Read, mailboxSession)) { + sink.next(mailbox); + } + sink.error(new InsufficientRightsException("'r' right is needed to status a mailbox")); + })) .flatMap(mailbox -> sendStatus(mailbox, statusDataItems, responder, session, mailboxSession)); } From 0fe3a6fe0cf21d237ca7af91390698ee932268e0 Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Thu, 17 Oct 2024 17:07:37 +0200 Subject: [PATCH 36/37] JAMES-2182 Fix rights for STORE --- .../mailbox/store/StoreMessageManager.java | 77 +++++++++++++------ .../james/imap/scripts/SharingAccessLRI.test | 12 +-- .../james/imap/scripts/SharingAccessLRS.test | 11 +-- .../james/imap/scripts/SharingAccessLRT.test | 9 +-- .../james/imap/scripts/SharingAccessLRTE.test | 9 +-- .../james/imap/scripts/SharingAccessLRW.test | 11 +-- .../processor/AbstractSelectionProcessor.java | 3 +- .../james/imap/processor/StatusProcessor.java | 3 +- 8 files changed, 76 insertions(+), 59 deletions(-) diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java index 61e5fe4a415..699974a7afe 100644 --- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java +++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMessageManager.java @@ -679,12 +679,43 @@ private void trimFlags(Flags flags, MailboxSession session) { } - @Override - public Map setFlags(final Flags flags, final FlagsUpdateMode flagsUpdateMode, final MessageRange set, MailboxSession mailboxSession) throws MailboxException { + public Optional ensureFlagsWrite(Flags flags, FlagsUpdateMode flagsUpdateMode, MailboxSession mailboxSession) { + MailboxACL.Rfc4314Rights myRights = storeRightManager.myRights(mailbox, mailboxSession); - if (!isWriteable(mailboxSession)) { - throw new ReadOnlyException(getMailboxPath()); + if (flagsUpdateMode.equals(FlagsUpdateMode.REPLACE)) { + if (!myRights.contains(MailboxACL.Right.Write, MailboxACL.Right.WriteSeenFlag, MailboxACL.Right.DeleteMessages)) { + return Optional.of(new InsufficientRightsException("'stw' rights are needed to reset flags")); + } + return Optional.empty(); + } + + if (flags.contains(Flag.SEEN) && !myRights.contains(MailboxACL.Right.WriteSeenFlag)) { + return Optional.of(new InsufficientRightsException("'s' right is needed to modify seen flag")); + } + + if (flags.contains(Flag.DELETED) && !myRights.contains(MailboxACL.Right.DeleteMessages)) { + return Optional.of(new InsufficientRightsException("'t' right is needed to modify deleted flag")); + } + + boolean hasOtherFlagChanges = flags.getUserFlags().length > 0 + || flags.contains(Flag.FLAGGED) + || flags.contains(Flag.DRAFT) + || flags.contains(Flag.ANSWERED) + || flags.contains(Flag.RECENT); + + if (hasOtherFlagChanges && !myRights.contains(MailboxACL.Right.Write)) { + return Optional.of(new InsufficientRightsException("'w' right is needed to modify arbitrary flags")); } + return Optional.empty(); + } + + @Override + public Map setFlags(Flags flags, FlagsUpdateMode flagsUpdateMode, MessageRange set, MailboxSession mailboxSession) throws MailboxException { + + ensureFlagsWrite(flags, flagsUpdateMode, mailboxSession) + .ifPresent(Throwing.consumer(e -> { + throw e; + }).sneakyThrow()); trimFlags(flags, mailboxSession); @@ -709,25 +740,25 @@ public Map setFlags(final Flags flags, final FlagsUpdateMode @Override public Publisher> setFlagsReactive(Flags flags, FlagsUpdateMode flagsUpdateMode, MessageRange set, MailboxSession mailboxSession) { - if (!isWriteable(mailboxSession)) { - return Mono.error(new ReadOnlyException(getMailboxPath())); - } - - trimFlags(flags, mailboxSession); - - MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession); - - return messageMapper.executeReactive(messageMapper.updateFlagsReactive(getMailboxEntity(), new FlagsUpdateCalculator(flags, flagsUpdateMode), set)) - .flatMap(updatedFlags -> eventBus.dispatch(EventFactory.flagsUpdated() - .randomEventId() - .mailboxSession(mailboxSession) - .mailbox(getMailboxEntity()) - .updatedFlags(updatedFlags) - .build(), - new MailboxIdRegistrationKey(mailbox.getMailboxId())) - .thenReturn(updatedFlags.stream().collect(ImmutableMap.toImmutableMap( - UpdatedFlags::getUid, - UpdatedFlags::getNewFlags)))); + return ensureFlagsWrite(flags, flagsUpdateMode, mailboxSession) + .map(Mono::>error) + .orElseGet(() -> { + trimFlags(flags, mailboxSession); + + MessageMapper messageMapper = mapperFactory.getMessageMapper(mailboxSession); + + return messageMapper.executeReactive(messageMapper.updateFlagsReactive(getMailboxEntity(), new FlagsUpdateCalculator(flags, flagsUpdateMode), set)) + .flatMap(updatedFlags -> eventBus.dispatch(EventFactory.flagsUpdated() + .randomEventId() + .mailboxSession(mailboxSession) + .mailbox(getMailboxEntity()) + .updatedFlags(updatedFlags) + .build(), + new MailboxIdRegistrationKey(mailbox.getMailboxId())) + .thenReturn(updatedFlags.stream().collect(ImmutableMap.toImmutableMap( + UpdatedFlags::getUid, + UpdatedFlags::getNewFlags)))); + }); } /** diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test index fc1bf6cdd6b..860b3038f6a 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRI.test @@ -84,21 +84,17 @@ C: F11 FETCH 1 FLAGS S: \* 1 FETCH \(FLAGS \(\\Recent\)\) S: F11 OK FETCH completed. -# TODO Insert is not flags C: F12 STORE 1 +FLAGS (\Deleted) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) -S: F12 OK STORE completed. +S: F12 NO STORE failed. Save failed. C: F13 STORE 1 +FLAGS (\Seen) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) -S: F13 OK STORE completed. +S: F13 NO STORE failed. Save failed. C: F14 STORE 1 +FLAGS (\Flagged) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) -S: F14 OK STORE completed. +S: F14 NO STORE failed. Save failed. C: F11 FETCH 1 FLAGS -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Recent\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test index d2bb291817f..f6e06a156eb 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRS.test @@ -86,21 +86,18 @@ C: F11 FETCH 1 FLAGS S: \* 1 FETCH \(FLAGS \(\\Recent\)\) S: F11 OK FETCH completed. -# TODO I shall only be able to manipulate \\Seen... C: F12 STORE 1 +FLAGS (\Deleted) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) -S: F12 OK STORE completed. +S: F12 NO STORE failed. Save failed. C: F13 STORE 1 +FLAGS (\Seen) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Recent \\Seen\)\) S: F13 OK STORE completed. C: F14 STORE 1 +FLAGS (\Flagged) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) -S: F14 OK STORE completed. +S: F14 NO STORE failed. Save failed. C: F11 FETCH 1 FLAGS -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Recent \\Seen\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test index 579e7905ff3..be41d2cac40 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRT.test @@ -90,17 +90,14 @@ C: F12 STORE 1 +FLAGS (\Deleted) S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) S: F12 OK STORE completed. -# TODO I shall only be able to manipulate \\Deleted... C: F13 STORE 1 +FLAGS (\Seen) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) -S: F13 OK STORE completed. +S: F13 NO STORE failed. Save failed. C: F14 STORE 1 +FLAGS (\Flagged) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) -S: F14 OK STORE completed. +S: F14 NO STORE failed. Save failed. C: F11 FETCH 1 FLAGS -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test index eef28d57b9a..892cdaab8f9 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRTE.test @@ -90,17 +90,14 @@ C: F12 STORE 1 +FLAGS (\Deleted) S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) S: F12 OK STORE completed. -# TODO I shall only be able to manipulate \\Deleted... C: F13 STORE 1 +FLAGS (\Seen) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) -S: F13 OK STORE completed. +S: F13 NO STORE failed. Save failed. C: F14 STORE 1 +FLAGS (\Flagged) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) -S: F14 OK STORE completed. +S: F14 NO STORE failed. Save failed. C: F11 FETCH 1 FLAGS -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test index 569879821dc..cb96dcf0d26 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/SharingAccessLRW.test @@ -86,21 +86,18 @@ C: F11 FETCH 1 FLAGS S: \* 1 FETCH \(FLAGS \(\\Recent\)\) S: F11 OK FETCH completed. -# TODO I shall only be able to manipulate \\Seen... C: F12 STORE 1 +FLAGS (\Deleted) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent\)\) -S: F12 OK STORE completed. +S: F12 NO STORE failed. Save failed. C: F13 STORE 1 +FLAGS (\Seen) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Recent \\Seen\)\) -S: F13 OK STORE completed. +S: F13 NO STORE failed. Save failed. C: F14 STORE 1 +FLAGS (\Flagged) -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Flagged \\Recent\)\) S: F14 OK STORE completed. C: F11 FETCH 1 FLAGS -S: \* 1 FETCH \(FLAGS \(\\Deleted \\Flagged \\Recent \\Seen\)\) +S: \* 1 FETCH \(FLAGS \(\\Flagged \\Recent\)\) S: F11 OK FETCH completed. C: F15 EXPUNGE 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 cd32c1ad30d..1ca548e6992 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 @@ -404,8 +404,9 @@ private Mono selectMailbox(MailboxPath mailboxPath, ImapSession .handle(Throwing.biConsumer((mailbox, sink) -> { if (mailboxManager.hasRight(mailbox.getMailboxEntity(), MailboxACL.Right.Read, mailboxSession)) { sink.next(mailbox); + } else { + sink.error(new InsufficientRightsException("'r' right is needed to select a mailbox")); } - sink.error(new InsufficientRightsException("'r' right is needed to select a mailbox")); })) .flatMap(Throwing.function(mailbox -> selectMailbox(session, responder, mailbox, currentMailbox) .flatMap(Throwing.function(sessionMailbox -> 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 361fc8f5b03..bade56b99b7 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 @@ -127,8 +127,9 @@ private Mono sendStatus(MailboxPath mailboxPath, StatusDa .handle(Throwing.biConsumer((mailbox, sink) -> { if (getMailboxManager().hasRight(mailbox.getMailboxEntity(), MailboxACL.Right.Read, mailboxSession)) { sink.next(mailbox); + } else { + sink.error(new InsufficientRightsException("'r' right is needed to status a mailbox")); } - sink.error(new InsufficientRightsException("'r' right is needed to status a mailbox")); })) .flatMap(mailbox -> sendStatus(mailbox, statusDataItems, responder, session, mailboxSession)); } From ff0482193b5e4fb900f3d6189332ff6d13113b3d Mon Sep 17 00:00:00 2001 From: Benoit TELLIER Date: Tue, 22 Oct 2024 14:27:48 +0200 Subject: [PATCH 37/37] JAMES-2128 Ensure creating #user is forbidden --- .../java/org/apache/james/mailbox/model/MailboxPath.java | 6 ++++-- .../resources/org/apache/james/imap/scripts/Create.test | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java index d43641de5de..24abfdad667 100644 --- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java +++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/MailboxPath.java @@ -154,7 +154,9 @@ public String getName() { } public boolean belongsTo(MailboxSession mailboxSession) { - return user.equals(mailboxSession.getUser()); + return Optional.ofNullable(user) + .map(mailboxSession.getUser()::equals) + .orElse(false); } public MailboxPath child(String childName, char delimiter) { @@ -255,7 +257,7 @@ boolean hasEmptyNameInHierarchy(char pathDelimiter) { } public String asString() { - return namespace + ":" + user.asString() + ":" + name; + return namespace + ":" + Optional.ofNullable(user).map(Username::asString).orElse("") + ":" + name; } public String asEscapedString() { diff --git a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Create.test b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Create.test index 4f47c8b1fb4..438e4d890f8 100644 --- a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Create.test +++ b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/Create.test @@ -73,6 +73,9 @@ S: \* LIST \(\\HasChildren\) "." "one" S: \* LIST \(\\HasNoChildren\) "." "one.two" S: 21 OK LIST completed. +C: 22 CREATE #user +S: 22 NO CREATE processing failed. + # Cleanup C: a1 DELETE test1.subfolder1 S: a1 OK DELETE completed.