You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2024/04/16 09:04:02 UTC
(james-project) branch master updated: JAMES-4029 Fix infinite loop with Bounce + forward
This is an automated email from the ASF dual-hosted git repository.
rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new ad58064c1b JAMES-4029 Fix infinite loop with Bounce + forward
ad58064c1b is described below
commit ad58064c1bcd53ed82fc2e3080d20b4d0e7843eb
Author: Benoit TELLIER <bt...@linagora.com>
AuthorDate: Mon Apr 15 20:54:24 2024 +0200
JAMES-4029 Fix infinite loop with Bounce + forward
---
.../mailets/ForwardBounceLoopIntegrationTest.java | 156 +++++++++++++++++++++
.../mailets/redirect/ProcessRedirectNotify.java | 7 +
2 files changed, 163 insertions(+)
diff --git a/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/ForwardBounceLoopIntegrationTest.java b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/ForwardBounceLoopIntegrationTest.java
new file mode 100644
index 0000000000..75d421438f
--- /dev/null
+++ b/server/mailet/integration-testing/src/test/java/org/apache/james/mailets/ForwardBounceLoopIntegrationTest.java
@@ -0,0 +1,156 @@
+/****************************************************************
+ * 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.mailets;
+
+import static org.apache.james.mailets.configuration.CommonProcessors.ERROR_REPOSITORY;
+import static org.apache.james.mailets.configuration.Constants.DEFAULT_DOMAIN;
+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.MailetConfiguration.TO_TRANSPORT;
+import static org.apache.james.mailets.configuration.ProcessorConfiguration.STATE_BOUNCES;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.File;
+
+import org.apache.james.core.Username;
+import org.apache.james.mailets.configuration.CommonProcessors;
+import org.apache.james.mailets.configuration.MailetConfiguration;
+import org.apache.james.mailets.configuration.MailetContainer;
+import org.apache.james.mailets.configuration.ProcessorConfiguration;
+import org.apache.james.modules.protocols.SmtpGuiceProbe;
+import org.apache.james.rrt.lib.Mapping;
+import org.apache.james.rrt.lib.MappingSource;
+import org.apache.james.transport.mailets.Bounce;
+import org.apache.james.transport.mailets.ToProcessor;
+import org.apache.james.transport.matchers.All;
+import org.apache.james.transport.matchers.RecipientIs;
+import org.apache.james.utils.DataProbeImpl;
+import org.apache.james.utils.FilteringManagementProbeImpl;
+import org.apache.james.utils.GuiceProbe;
+import org.apache.james.utils.MailRepositoryProbeImpl;
+import org.apache.james.utils.SMTPMessageSender;
+import org.apache.james.utils.SpoolerProbe;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.io.TempDir;
+import org.testcontainers.shaded.org.awaitility.Awaitility;
+
+import com.google.inject.multibindings.Multibinder;
+
+public class ForwardBounceLoopIntegrationTest {
+
+ private static final Username SENDER = Username.of("sender@" + DEFAULT_DOMAIN);
+ private static final Username ALICE = Username.of("alice@" + DEFAULT_DOMAIN);
+ private static final Username BOB = Username.of("bob@" + DEFAULT_DOMAIN);
+ private static final Username CEDRIC = Username.of("cedric@" + DEFAULT_DOMAIN);
+ private TemporaryJamesServer jamesServer;
+ private MailRepositoryProbeImpl mailRepositoryProbe;
+
+ @RegisterExtension
+ public SMTPMessageSender messageSender = new SMTPMessageSender(DEFAULT_DOMAIN);
+ private DataProbeImpl dataProbe;
+
+ private void setUp(File temporaryFolder, MailetContainer.Builder mailetConfiguration) throws Exception {
+ jamesServer = TemporaryJamesServer.builder()
+ .withOverrides(binder -> Multibinder.newSetBinder(binder, GuiceProbe.class).addBinding().to(FilteringManagementProbeImpl.class))
+ .withMailetContainer(mailetConfiguration)
+ .build(temporaryFolder);
+
+ jamesServer.start();
+
+ dataProbe = jamesServer.getProbe(DataProbeImpl.class);
+ dataProbe.addDomain(DEFAULT_DOMAIN);
+
+ dataProbe.addUser(SENDER.asString(), PASSWORD);
+ dataProbe.addUser(ALICE.asString(), PASSWORD);
+ dataProbe.addUser(BOB.asString(), PASSWORD);
+ dataProbe.addUser(CEDRIC.asString(), PASSWORD);
+
+ mailRepositoryProbe = jamesServer.getProbe(MailRepositoryProbeImpl.class);
+ }
+
+ @AfterEach
+ void tearDown() {
+ jamesServer.shutdown();
+ }
+
+ @Test
+ void dsnBounceWithForwardShouldNotLeadToALoop(@TempDir File temporaryFolder) throws Exception {
+ setUp(temporaryFolder, MailetContainer.builder()
+ .putProcessor(ProcessorConfiguration.root()
+ .addMailet(MailetConfiguration.builder()
+ .matcher(RecipientIs.class)
+ .matcherCondition(CEDRIC.asString())
+ .mailet(ToProcessor.class)
+ .addProperty("processor", STATE_BOUNCES))
+ .addMailet(TO_TRANSPORT))
+ .putProcessor(CommonProcessors.error())
+ .putProcessor(CommonProcessors.rrtError())
+ .putProcessor(CommonProcessors.transport())
+ .putProcessor(CommonProcessors.bounces()));
+
+ dataProbe.addMapping(MappingSource.fromUser(BOB), Mapping.forward(BOB.asString()));
+ dataProbe.addMapping(MappingSource.fromUser(BOB), Mapping.forward(CEDRIC.asString()));
+
+ messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+ .authenticate(SENDER.asString(), PASSWORD)
+ .sendMessage(SENDER.asString(), BOB.asString());
+
+ Awaitility.await().until(() -> jamesServer.getProbe(SpoolerProbe.class).processingFinished());
+
+ assertThat(mailRepositoryProbe.getRepositoryMailCount(ERROR_REPOSITORY))
+ .isZero();
+ }
+
+ @Test
+ void bounceWithForwardShouldNotLeadToALoop(@TempDir File temporaryFolder) throws Exception {
+ setUp(temporaryFolder, MailetContainer.builder()
+ .putProcessor(ProcessorConfiguration.root()
+ .addMailet(MailetConfiguration.builder()
+ .matcher(RecipientIs.class)
+ .matcherCondition(CEDRIC.asString())
+ .mailet(ToProcessor.class)
+ .addProperty("processor", STATE_BOUNCES))
+ .addMailet(TO_TRANSPORT))
+ .putProcessor(CommonProcessors.error())
+ .putProcessor(CommonProcessors.rrtError())
+ .putProcessor(CommonProcessors.transport())
+ .putProcessor(ProcessorConfiguration.bounces()
+ .enableJmx(false)
+ .addMailet(MailetConfiguration.builder()
+ .matcher(All.class)
+ .mailet(Bounce.class)
+ .addProperty("passThrough", "false"))
+ .build()));
+
+ dataProbe.addMapping(MappingSource.fromUser(BOB), Mapping.forward(BOB.asString()));
+ dataProbe.addMapping(MappingSource.fromUser(BOB), Mapping.forward(CEDRIC.asString()));
+
+ messageSender.connect(LOCALHOST_IP, jamesServer.getProbe(SmtpGuiceProbe.class).getSmtpPort())
+ .authenticate(SENDER.asString(), PASSWORD)
+ .sendMessage(SENDER.asString(), BOB.asString());
+
+ Awaitility.await().until(() -> jamesServer.getProbe(SpoolerProbe.class).processingFinished());
+
+ assertThat(mailRepositoryProbe.getRepositoryMailCount(ERROR_REPOSITORY))
+ .isZero();
+ }
+}
diff --git a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
index 5e78eae218..e0118f63af 100644
--- a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
+++ b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/redirect/ProcessRedirectNotify.java
@@ -18,11 +18,16 @@
****************************************************************/
package org.apache.james.transport.mailets.redirect;
+import static org.apache.mailet.LoopPrevention.RECORDED_RECIPIENTS_ATTRIBUTE_NAME;
+
+import java.util.Optional;
+
import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage;
import org.apache.james.lifecycle.api.LifecycleUtil;
import org.apache.james.server.core.MailImpl;
+import org.apache.mailet.Attribute;
import org.apache.mailet.Mail;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -108,7 +113,9 @@ public class ProcessRedirectNotify {
private void finalizeMail(MailImpl mail) throws MessagingException {
mail.getMessage().saveChanges();
+ Optional<Attribute> recordedRecipients = mail.getAttribute(RECORDED_RECIPIENTS_ATTRIBUTE_NAME);
mail.removeAllAttributes();
+ recordedRecipients.ifPresent(mail::setAttribute);
}
private boolean keepMessageId() {
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org