You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2024/10/07 06:36:14 UTC
(james-project) branch master updated: JAMES-3945 Subaddressing - case sensitivity and subfolders (#2435)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new 256c89dec7 JAMES-3945 Subaddressing - case sensitivity and subfolders (#2435)
256c89dec7 is described below
commit 256c89dec70a8aa4db0a807b42b2602b4716cc20
Author: florentos17 <fa...@linagora.com>
AuthorDate: Mon Oct 7 08:36:09 2024 +0200
JAMES-3945 Subaddressing - case sensitivity and subfolders (#2435)
---
.../model/search/ExactNameCaseInsensitive.java | 70 ++++++++++++++
.../search/PrefixedWildcardCaseInsensitive.java | 69 ++++++++++++++
.../model/search/ExactNameCaseInsensitiveTest.java | 88 +++++++++++++++++
.../PrefixedWildcardCaseInsensitiveTest.java | 105 +++++++++++++++++++++
.../james/mailets/configuration/Constants.java | 10 +-
.../apache/james/mailets/SubAddressingTest.java | 102 +++++++++++++++++++-
.../james/transport/mailets/SubAddressing.java | 49 ++++++++--
.../james/transport/mailets/SubAddressingTest.java | 76 ++++++++-------
8 files changed, 513 insertions(+), 56 deletions(-)
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitive.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitive.java
new file mode 100644
index 0000000000..8acced2857
--- /dev/null
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitive.java
@@ -0,0 +1,70 @@
+/****************************************************************
+ * 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.mailbox.model.search;
+
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
+public class ExactNameCaseInsensitive implements MailboxNameExpression {
+
+ private final String name;
+
+ public ExactNameCaseInsensitive(String name) {
+ Preconditions.checkNotNull(name);
+ this.name = name;
+ }
+
+ @Override
+ public boolean isExpressionMatch(String mailboxName) {
+ Preconditions.checkNotNull(mailboxName);
+ return name.equalsIgnoreCase(mailboxName);
+ }
+
+ @Override
+ public String getCombinedName() {
+ return name;
+ }
+
+ @Override
+ public boolean isWild() {
+ return false;
+ }
+
+ @Override
+ public MailboxNameExpression includeChildren() {
+ return new PrefixedWildcardCaseInsensitive(name);
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof ExactNameCaseInsensitive) {
+ ExactNameCaseInsensitive exactName = (ExactNameCaseInsensitive) o;
+
+ return Objects.equals(this.name, exactName.name);
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(name);
+ }
+}
diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitive.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitive.java
new file mode 100644
index 0000000000..85e490d775
--- /dev/null
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitive.java
@@ -0,0 +1,69 @@
+/****************************************************************
+ * 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.mailbox.model.search;
+
+import java.util.Objects;
+
+import com.google.common.base.Preconditions;
+
+public class PrefixedWildcardCaseInsensitive implements MailboxNameExpression {
+
+ private final String prefix;
+
+ public PrefixedWildcardCaseInsensitive(String prefix) {
+ Preconditions.checkNotNull(prefix);
+ this.prefix = prefix;
+ }
+
+ @Override
+ public boolean isExpressionMatch(String name) {
+ return name.toLowerCase().startsWith(prefix.toLowerCase());
+ }
+
+ @Override
+ public String getCombinedName() {
+ return prefix + FREEWILDCARD;
+ }
+
+ @Override
+ public boolean isWild() {
+ return true;
+ }
+
+ @Override
+ public MailboxNameExpression includeChildren() {
+ return this;
+ }
+
+ @Override
+ public final boolean equals(Object o) {
+ if (o instanceof PrefixedWildcardCaseInsensitive) {
+ PrefixedWildcardCaseInsensitive that = (PrefixedWildcardCaseInsensitive) o;
+
+ return Objects.equals(this.prefix, that.prefix);
+ }
+ return false;
+ }
+
+ @Override
+ public final int hashCode() {
+ return Objects.hash(prefix);
+ }
+}
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitiveTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitiveTest.java
new file mode 100644
index 0000000000..1c46759d40
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/ExactNameCaseInsensitiveTest.java
@@ -0,0 +1,88 @@
+/****************************************************************
+ * 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.mailbox.model.search;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class ExactNameCaseInsensitiveTest {
+ public static final String NAME = "toto";
+ public static final String NAME_DIFFERENT_CASE_1 = "Toto";
+ public static final String NAME_DIFFERENT_CASE_2 = "TOTO";
+
+ @Test
+ void shouldMatchBeanContract() {
+ EqualsVerifier.forClass(ExactNameCaseInsensitive.class)
+ .verify();
+ }
+
+ @Test
+ void constructorShouldThrowOnNullName() {
+ assertThatThrownBy(() -> new ExactNameCaseInsensitive(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ void isWildShouldReturnFalse() {
+ assertThat(new ExactNameCaseInsensitive(NAME).isWild())
+ .isFalse();
+ }
+
+ @Test
+ void getCombinedNameShouldReturnName() {
+ assertThat(new ExactNameCaseInsensitive(NAME).getCombinedName())
+ .isEqualTo(NAME);
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenName() {
+ assertThat(new ExactNameCaseInsensitive(NAME).isExpressionMatch(NAME))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameDifferentCase1() {
+ assertThat(new ExactNameCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_1))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameDifferentCase2() {
+ assertThat(new ExactNameCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_2))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnFalseWhenOtherValue() {
+ assertThat(new ExactNameCaseInsensitive(NAME).isExpressionMatch("other"))
+ .isFalse();
+ }
+
+ @Test
+ void isExpressionMatchShouldThrowOnNullValue() {
+ assertThatThrownBy(() -> new ExactNameCaseInsensitive(NAME).isExpressionMatch(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+}
\ No newline at end of file
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitiveTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitiveTest.java
new file mode 100644
index 0000000000..597632c91c
--- /dev/null
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/search/PrefixedWildcardCaseInsensitiveTest.java
@@ -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. *
+ ****************************************************************/
+
+package org.apache.james.mailbox.model.search;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+import org.junit.jupiter.api.Test;
+
+import nl.jqno.equalsverifier.EqualsVerifier;
+
+public class PrefixedWildcardCaseInsensitiveTest {
+ public static final String NAME = "toto";
+ public static final String NAME_DIFFERENT_CASE_1 = "Toto";
+ public static final String NAME_DIFFERENT_CASE_2 = "TOTO";
+
+ @Test
+ void shouldMatchBeanContract() {
+ EqualsVerifier.forClass(PrefixedWildcardCaseInsensitive.class)
+ .verify();
+ }
+
+ @Test
+ void constructorShouldThrowOnNullName() {
+ assertThatThrownBy(() -> new PrefixedWildcardCaseInsensitive(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+
+ @Test
+ void isWildShouldReturnTrue() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isWild())
+ .isTrue();
+ }
+
+ @Test
+ void getCombinedNameShouldReturnName() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).getCombinedName())
+ .isEqualTo(NAME + MailboxNameExpression.FREEWILDCARD);
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenName() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameWithDifferentCase1() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_1))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameWithDifferentCase2() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_2))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameAndPostfix() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME + "any"))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameWithDifferentCase1AndPostfix() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_1 + "any"))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnTrueWhenNameWithDifferentCase2AndPostfix() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(NAME_DIFFERENT_CASE_2 + "any"))
+ .isTrue();
+ }
+
+ @Test
+ void isExpressionMatchShouldReturnFalseWhenOtherValue() {
+ assertThat(new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch("other"))
+ .isFalse();
+ }
+
+ @Test
+ void isExpressionMatchShouldThrowOnNullValue() {
+ assertThatThrownBy(() -> new PrefixedWildcardCaseInsensitive(NAME).isExpressionMatch(null))
+ .isInstanceOf(NullPointerException.class);
+ }
+}
\ No newline at end of file
diff --git a/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/Constants.java b/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/Constants.java
index c5bdaf4b13..4e2ebcc1f2 100644
--- a/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/Constants.java
+++ b/server/mailet/integration-testing/src/main/java/org/apache/james/mailets/configuration/Constants.java
@@ -42,9 +42,9 @@ public class Constants {
public static final String DEFAULT_DOMAIN = "james.org";
public static final String LOCALHOST_IP = "127.0.0.1";
public static final String PASSWORD = "secret";
- public static final String FROM = "user@" + DEFAULT_DOMAIN;
- public static final String FROM2 = "user4@" + DEFAULT_DOMAIN;
- public static final String RECIPIENT = "user2@" + DEFAULT_DOMAIN;
- public static final String ALIAS = "user2alias@" + DEFAULT_DOMAIN;
- public static final String RECIPIENT2 = "user3@" + DEFAULT_DOMAIN;
+ public static final String FROM = "from@" + DEFAULT_DOMAIN;
+ public static final String FROM2 = "from2@" + DEFAULT_DOMAIN;
+ public static final String RECIPIENT = "recipient@" + DEFAULT_DOMAIN;
+ public static final String ALIAS = "recipientalias@" + DEFAULT_DOMAIN;
+ public static final String RECIPIENT2 = "recipient2@" + DEFAULT_DOMAIN;
}
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
index 55f656cc00..fa1225cd1f 100644
--- a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/SubAddressingTest.java
@@ -27,6 +27,7 @@ import static org.apache.james.mailets.configuration.Constants.FROM2;
import static org.apache.james.mailets.configuration.Constants.LOCALHOST_IP;
import static org.apache.james.mailets.configuration.Constants.PASSWORD;
import static org.apache.james.mailets.configuration.Constants.RECIPIENT;
+import static org.apache.james.mailets.configuration.Constants.RECIPIENT2;
import static org.apache.james.mailets.configuration.Constants.awaitAtMostOneMinute;
import java.io.File;
@@ -55,6 +56,8 @@ import org.junit.jupiter.api.io.TempDir;
class SubAddressingTest {
private static final String TARGETED_MAILBOX = "any";
+ private static final String TARGETED_MAILBOX_LOWER = TARGETED_MAILBOX;
+ private static final String TARGETED_MAILBOX_UPPER = "ANY";
@RegisterExtension
public TestIMAPClient testIMAPClient = new TestIMAPClient();
@@ -78,6 +81,7 @@ class SubAddressingTest {
DataProbe dataProbe = jamesServer.getProbe(DataProbeImpl.class);
dataProbe.addDomain(DEFAULT_DOMAIN);
dataProbe.addUser(RECIPIENT, PASSWORD);
+ dataProbe.addUser(RECIPIENT2, PASSWORD);
dataProbe.addUser(FROM, PASSWORD);
dataProbe.addUser(FROM2, PASSWORD);
@@ -100,7 +104,95 @@ class SubAddressingTest {
// do not create mailbox
- sendSubAddressedMail();
+ sendSubAddressedMail(TARGETED_MAILBOX);
+ awaitSubAddressedMail(MailboxConstants.INBOX);
+ }
+
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInINBOXWhenSpecifiedFolderExistsForAnotherUser(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailbox for recipient 1
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX + " " + "anyone" + " p");
+
+ // send to recipient 2
+ messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+ .authenticate(FROM, PASSWORD)
+ .sendMessage(FROM, "recipient2+" + TARGETED_MAILBOX + "@" + DEFAULT_DOMAIN);
+
+ testIMAPClient
+ .connect(LOCALHOST_IP, jamesServer.getProbe(ImapGuiceProbe.class).getImapPort())
+ .login(RECIPIENT2, PASSWORD)
+ .select(MailboxConstants.INBOX)
+ .awaitMessage(awaitAtMostOneMinute);
+ }
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInSpecifiedFolderWhenItExistsInUpperCase(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailbox
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_UPPER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_UPPER + " " + "anyone" + " p");
+
+ sendSubAddressedMail(TARGETED_MAILBOX_LOWER);
+ awaitSubAddressedMail(TARGETED_MAILBOX_UPPER);
+ }
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInSpecifiedFolderWhenItExistsInLowerCase(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailbox
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_LOWER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_LOWER + " " + "anyone" + " p");
+
+ sendSubAddressedMail(TARGETED_MAILBOX_UPPER);
+ awaitSubAddressedMail(TARGETED_MAILBOX_LOWER);
+ }
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInSpecifiedFolderWithCorrectLowerCaseWhenSeveralCasesExist(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailboxes
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_LOWER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_LOWER + " " + "anyone" + " p");
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_UPPER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_UPPER + " " + "anyone" + " p");
+
+ sendSubAddressedMail(TARGETED_MAILBOX_LOWER);
+ awaitSubAddressedMail(TARGETED_MAILBOX_LOWER);
+ }
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInSpecifiedFolderWithCorrectUpperCaseWhenSeveralCasesExist(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailboxes
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_LOWER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_LOWER + " " + "anyone" + " p");
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_UPPER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_UPPER + " " + "anyone" + " p");
+
+ sendSubAddressedMail(TARGETED_MAILBOX_UPPER);
+ awaitSubAddressedMail(TARGETED_MAILBOX_UPPER);
+ }
+
+ @Test
+ void subAddressedEmailShouldBeDeliveredInINBOXWhenSpecifiedFolderExistsWithCorrectCaseButNoRightAndOtherCaseButRight(@TempDir File temporaryFolder) throws Exception {
+ setup(temporaryFolder);
+
+ // create mailbox with incorrect case and give posting right
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_LOWER);
+ testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX_LOWER + " " + "anyone" + " p");
+
+ // create mailbox with correct case but don't give posting right
+ testIMAPClient.sendCommand("CREATE " + TARGETED_MAILBOX_UPPER);
+
+ sendSubAddressedMail(TARGETED_MAILBOX_UPPER);
awaitSubAddressedMail(MailboxConstants.INBOX);
}
@@ -114,7 +206,7 @@ class SubAddressingTest {
// do not give posting rights
testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX + " " + "anyone" + " -p");
- sendSubAddressedMail();
+ sendSubAddressedMail(TARGETED_MAILBOX);
awaitSubAddressedMail(MailboxConstants.INBOX);
}
@@ -127,14 +219,14 @@ class SubAddressingTest {
// give posting rights for anyone
testIMAPClient.sendCommand("SETACL " + TARGETED_MAILBOX + " " + "anyone" + " +p");
- sendSubAddressedMail();
+ sendSubAddressedMail(TARGETED_MAILBOX);
awaitSubAddressedMail(TARGETED_MAILBOX);
}
- private void sendSubAddressedMail() throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
+ private void sendSubAddressedMail(String targetMailbox) throws IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException {
messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
.authenticate(FROM, PASSWORD)
- .sendMessage(FROM, "user2+" + TARGETED_MAILBOX + "@" + DEFAULT_DOMAIN);
+ .sendMessage(FROM,"recipient+" + targetMailbox + "@" + DEFAULT_DOMAIN);
}
private void awaitSubAddressedMail(String expectedMailbox) throws IOException {
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
index 41c4e1119f..f3d1c5c1c6 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/SubAddressing.java
@@ -21,6 +21,9 @@ package org.apache.james.transport.mailets;
import static org.apache.james.mailbox.MessageManager.MailboxMetaData.RecentMode.IGNORE;
+import java.util.Comparator;
+import java.util.Optional;
+
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.mail.MessagingException;
@@ -36,7 +39,10 @@ import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.exception.MailboxNotFoundException;
import org.apache.james.mailbox.exception.UnsupportedRightException;
import org.apache.james.mailbox.model.MailboxACL;
+import org.apache.james.mailbox.model.MailboxMetaData;
import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.search.ExactNameCaseInsensitive;
+import org.apache.james.mailbox.model.search.MailboxQuery;
import org.apache.james.user.api.UsersRepository;
import org.apache.james.user.api.UsersRepositoryException;
import org.apache.mailet.Mail;
@@ -83,33 +89,56 @@ public class SubAddressing extends GenericMailet {
public void service(Mail mail) throws MessagingException {
mail.getRecipients().forEach(recipient ->
recipient.getLocalPartDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER)
- .ifPresent(Throwing.consumer(targetFolder -> postIfHasRight(mail, recipient, targetFolder))));
+ .ifPresent(Throwing.consumer(targetFolder -> postIfHasRight(
+ mail,
+ recipient.stripDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER),
+ getPathWithCorrectCase(recipient, targetFolder)))));
+ }
+
+ private Optional<MailboxPath> getPathWithCorrectCase(MailAddress recipient, String targetFolder) throws UsersRepositoryException, MailboxException {
+ Username recipientUsername = usersRepository.getUsername(recipient);
+ MailboxSession session = mailboxManager.createSystemSession(recipientUsername);
+
+ Comparator<MailboxPath> exactMatchFirst = Comparator.comparing(mailboxPath -> mailboxPath.getName().equals(targetFolder) ? 0 : 1);
+
+ return mailboxManager.search(
+ MailboxQuery.privateMailboxesBuilder(session).expression(new ExactNameCaseInsensitive(targetFolder)).build(),
+ session)
+ .toStream()
+ .map(MailboxMetaData::getPath)
+ .sorted(exactMatchFirst)
+ .findFirst()
+ .or(() -> {
+ LOG.info("{}'s subfolder `{}` was tried to be addressed but it does not exist", recipient, targetFolder);
+ return Optional.empty();
+ });
}
- private void postIfHasRight(Mail mail, MailAddress recipient, String targetFolder) throws UsersRepositoryException, MailboxException {
- if (hasPostRight(mail, recipient, targetFolder)) {
- StorageDirective.builder().targetFolders(ImmutableList.of(targetFolder)).build()
+ private void postIfHasRight(Mail mail, MailAddress recipient, Optional<MailboxPath> targetFolderPath) throws UsersRepositoryException, MailboxException {
+ if (hasPostRight(mail, recipient, targetFolderPath)) {
+ StorageDirective.builder().targetFolders(ImmutableList.of(targetFolderPath.get().getName())).build()
.encodeAsAttributes(usersRepository.getUsername(recipient))
.forEach(mail::setAttribute);
} else {
LOG.info("{} tried to address {}'s subfolder `{}` but they did not have the right to",
- mail.getMaybeSender().toString(), recipient.stripDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER), targetFolder);
+ mail.getMaybeSender().toString(), recipient, targetFolderPath);
}
}
- private Boolean hasPostRight(Mail mail, MailAddress recipient, String targetFolder) throws MailboxException, UsersRepositoryException {
+ private Boolean hasPostRight(Mail mail, MailAddress recipient, Optional<MailboxPath> targetFolderPath) throws MailboxException, UsersRepositoryException {
try {
- return resolvePostRight(retrieveMailboxACL(recipient, targetFolder), mail.getMaybeSender(), recipient);
+ return targetFolderPath.isPresent() && resolvePostRight(retrieveMailboxACL(recipient, targetFolderPath.get()), mail.getMaybeSender(), recipient);
} catch (MailboxNotFoundException e) {
- LOG.info("{}'s subfolder `{}` was tried to be addressed but it does not exist", recipient.stripDetails(UsersRepository.LOCALPART_DETAIL_DELIMITER), targetFolder);
+ LOG.info("{}'s subfolder `{}` was tried to be addressed but it does not exist", recipient, targetFolderPath);
return false;
}
}
- private MailboxACL retrieveMailboxACL(MailAddress recipient, String targetFolder) throws MailboxException, UsersRepositoryException {
+ private MailboxACL retrieveMailboxACL(MailAddress recipient, MailboxPath targetFolderPath) throws MailboxException, UsersRepositoryException {
Username recipientUsername = usersRepository.getUsername(recipient);
MailboxSession session = mailboxManager.createSystemSession(recipientUsername);
- return mailboxManager.getMailbox(MailboxPath.forUser(recipientUsername, targetFolder), session)
+
+ return mailboxManager.getMailbox(targetFolderPath, session)
.getMetaData(IGNORE, session, MessageManager.MailboxMetaData.FetchGroup.NO_COUNT)
.getACL();
}
diff --git a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
index c7b160f480..3307083262 100644
--- a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
+++ b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/SubAddressingTest.java
@@ -52,15 +52,15 @@ import org.junit.jupiter.api.Test;
public class SubAddressingTest {
private static final String SENDER1 = "sender1@localhost";
- private static final String SENDER2 = "sender2@localhost";
private static final String RECIPIENT = "recipient@localhost";
private static final String TARGET = "targetfolder";
- private static final String UNEXISTING_TARGET = "unexistingfolder";
+ private static final String TARGET_UPPERCASE = "Targetfolder";
+ private static final String TARGET_UNEXISTING = "unexistingfolder";
private MailboxManager mailboxManager;
private SubAddressing testee;
private UsersRepository usersRepository;
- private Username sender1Username;
+ private Username senderUsername;
private Username recipientUsername;
private MailboxSession recipientSession;
private MailboxId targetMailboxId;
@@ -71,7 +71,7 @@ public class SubAddressingTest {
usersRepository = MemoryUsersRepository.withVirtualHosting(NO_DOMAIN_LIST);
recipientUsername = usersRepository.getUsername(new MailAddress(RECIPIENT));
- sender1Username = usersRepository.getUsername(new MailAddress(SENDER1));
+ senderUsername = usersRepository.getUsername(new MailAddress(SENDER1));
recipientSession = mailboxManager.createSystemSession(recipientUsername);
targetMailboxId = mailboxManager.createMailbox(
@@ -83,73 +83,78 @@ public class SubAddressingTest {
@Test
void shouldNotAddStorageDirectiveWhenTargetMailboxDoesNotExist() throws Exception {
- Mail mail = mailBuilder(UNEXISTING_TARGET).sender(SENDER1).build();
+ Mail mail = mailBuilder(TARGET_UNEXISTING).sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
assertThat(mail.attributes().map(this::unbox))
- .doesNotContain(Pair.of(recipient, UNEXISTING_TARGET));
+ .doesNotContain(Pair.of(recipient, TARGET_UNEXISTING));
}
@Test
- void shouldNotAddStorageDirectiveWhenNobodyHasRight() throws Exception {
- removePostRightForKey(MailboxACL.ANYONE_KEY);
+ void shouldAddStorageDirectiveWhenTargetMailboxExistsButLowerCase() throws Exception {
+ givePostRightForKey(MailboxACL.ANYONE_KEY);
- Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+ Mail mail = mailBuilder(TARGET_UPPERCASE).sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
assertThat(mail.attributes().map(this::unbox))
- .doesNotContain(Pair.of(recipient, TARGET));
+ .containsOnly(Pair.of(recipient, TARGET));
}
-
@Test
- void shouldAddStorageDirectiveWhenAnyoneHasRight() throws Exception {
- givePostRightForKey(MailboxACL.ANYONE_KEY);
+ void shouldAddStorageDirectiveWhenTargetMailboxExistsButUpperCase() throws Exception {
+ targetMailboxId = mailboxManager.createMailbox(
+ MailboxPath.forUser(recipientUsername, "Target"), recipientSession).get();
- Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+ MailboxACL.ACLCommand command = MailboxACL.command()
+ .key(MailboxACL.ANYONE_KEY)
+ .rights(MailboxACL.Right.Post)
+ .asAddition();
+ mailboxManager.applyRightsCommand(targetMailboxId, command, recipientSession);
+
+ Mail mail = mailBuilder("target").sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
assertThat(mail.attributes().map(this::unbox))
- .containsOnly(Pair.of(recipient, TARGET));
+ .containsOnly(Pair.of(recipient, "Target"));
}
- //@Disabled
@Test
- void shouldAddStorageDirectiveWhenSenderIsWhiteListed() throws Exception {
- // whitelist sender 1 and send from sender 1
- removePostRightForKey(MailboxACL.ANYONE_KEY);
- givePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
+ void shouldAddStorageDirectiveWhenTargetFolderIsASubFolder() throws Exception {
+ givePostRightForKey(MailboxACL.ANYONE_KEY);
- Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
+ MailboxPath newPath = MailboxPath.forUser(recipientUsername, "folder" + recipientSession.getPathDelimiter() + TARGET);
+ mailboxManager.renameMailbox(targetMailboxId, newPath, recipientSession).getFirst().getMailboxId();
+
+ Mail mail = mailBuilder("folder" + recipientSession.getPathDelimiter() + TARGET).sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
assertThat(mail.attributes().map(this::unbox))
- .containsOnly(Pair.of(recipient, TARGET));
+ .containsOnly(Pair.of(recipient, "folder" + recipientSession.getPathDelimiter() + TARGET));
}
@Test
- void shouldNotAddStorageDirectiveWhenSenderIsNotWhiteListed() throws Exception {
- // whitelist sender 1 and send from sender 2
- removePostRightForKey(MailboxACL.ANYONE_KEY);
- givePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
+ void shouldAddStorageDirectiveWhenTargetFolderIsASubFolderWithDifferentCase() throws Exception {
+ givePostRightForKey(MailboxACL.ANYONE_KEY);
+
+ MailboxPath newPath = MailboxPath.forUser(recipientUsername, "Folder" + recipientSession.getPathDelimiter() + TARGET_UPPERCASE);
+ mailboxManager.renameMailbox(targetMailboxId, newPath, recipientSession).getFirst().getMailboxId();
- Mail mail = mailBuilder(TARGET).sender(SENDER2).build();
+ Mail mail = mailBuilder("folder" + recipientSession.getPathDelimiter() + TARGET).sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
assertThat(mail.attributes().map(this::unbox))
- .doesNotContain(Pair.of(recipient, TARGET));
+ .containsOnly(Pair.of(recipient, "Folder" + recipientSession.getPathDelimiter() + TARGET_UPPERCASE));
}
@Test
- void shouldNotAddStorageDirectiveWhenSenderIsBlackListed() throws Exception {
- // blacklist sender 1 and send from sender 1
- givePostRightForKey(MailboxACL.ANYONE_KEY);
- givePostRightForKey(MailboxACL.EntryKey.createNegativeUserEntryKey(sender1Username));
+ void shouldNotAddStorageDirectiveWhenNobodyHasRight() throws Exception {
+ removePostRightForKey(MailboxACL.ANYONE_KEY);
Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
testee.service(mail);
@@ -159,13 +164,12 @@ public class SubAddressingTest {
.doesNotContain(Pair.of(recipient, TARGET));
}
+
@Test
- void shouldAddStorageDirectiveWhenSenderIsNotBlackListed() throws Exception {
- // blacklist sender 1 and send from sender 2
+ void shouldAddStorageDirectiveWhenAnyoneHasRight() throws Exception {
givePostRightForKey(MailboxACL.ANYONE_KEY);
- removePostRightForKey(MailboxACL.EntryKey.createUserEntryKey(sender1Username));
- Mail mail = mailBuilder(TARGET).sender(SENDER2).build();
+ Mail mail = mailBuilder(TARGET).sender(SENDER1).build();
testee.service(mail);
AttributeName recipient = AttributeName.of("DeliveryPaths_recipient@localhost");
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org