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

chibenwa 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 861aab6332 [ENHANCEMENT] Attribute for forwarded mail (#3036)
861aab6332 is described below

commit 861aab6332a60c3e7934a39ba8a1ea85600d8133
Author: Benoit TELLIER <[email protected]>
AuthorDate: Mon May 11 08:58:18 2026 +0200

    [ENHANCEMENT] Attribute for forwarded mail (#3036)
    
    Co-authored-by: Rene Cordier <[email protected]>
---
 .../servers/partials/RecipientRewriteTable.adoc    |  6 ++++-
 .../transport/mailets/RecipientRewriteTable.java   |  3 +++
 .../mailets/RecipientRewriteTableProcessor.java    |  7 ++++++
 .../RecipientRewriteTableProcessorTest.java        | 27 ++++++++++++++++++++++
 4 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/docs/modules/servers/partials/RecipientRewriteTable.adoc 
b/docs/modules/servers/partials/RecipientRewriteTable.adoc
index ec94fac455..429882f827 100644
--- a/docs/modules/servers/partials/RecipientRewriteTable.adoc
+++ b/docs/modules/servers/partials/RecipientRewriteTable.adoc
@@ -19,4 +19,8 @@ from external senders to external addresses as the DKIM and 
SPF records will not
 domain.
 
 The *forwardAutoSubmittedEmails* option (default to false) can be used to 
prevent forwarding bounces as such a scenario
-can lead to an infinite loop if the forward recipient bounces the email.
\ No newline at end of file
+can lead to an infinite loop if the forward recipient bounces the email.
+
+When *rewriteSenderUponForward* is enabled, forwarded copies carry the 
`org.apache.james.forwarded` mail attribute set to `true`.
+Downstream mailets can match on this attribute to apply specific processing to 
forwarded traffic (routing, rate-limiting, header rewriting…)
+without relying on fragile heuristics such as inspecting `Return-Path` or 
envelope topology.
\ No newline at end of file
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java
index a0a1f3a7d5..eede0636c4 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTable.java
@@ -57,6 +57,9 @@ import com.google.common.collect.ImmutableList;
  *
  * The <b>forwardAutoSubmittedEmails</b> option (default to false) can be used 
to prevent forwarding bounces as such a scenario
  * can lead to an infinite loop if the forward recipient bounces the email.
+ *
+ * When <b>rewriteSenderUponForward</b> is enabled, forwarded copies have the 
{@code org.apache.james.forwarded} mail attribute set to {@code true},
+ * allowing downstream mailets to condition their behaviour on forwarded 
traffic without heuristics.
  */
 public class RecipientRewriteTable extends GenericMailet {
     public static final String ERROR_PROCESSOR = "errorProcessor";
diff --git 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessor.java
 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessor.java
index bea226ff77..1d04029c5d 100644
--- 
a/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessor.java
+++ 
b/server/mailet/mailets/src/main/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessor.java
@@ -49,6 +49,9 @@ import org.apache.james.rrt.lib.Mappings;
 import org.apache.james.server.core.MailImpl;
 import org.apache.james.util.AuditTrail;
 import org.apache.james.util.MemoizedSupplier;
+import org.apache.mailet.Attribute;
+import org.apache.mailet.AttributeName;
+import org.apache.mailet.AttributeValue;
 import org.apache.mailet.DsnParameters;
 import org.apache.mailet.DsnParameters.RecipientDsnParameters;
 import org.apache.mailet.LoopPrevention;
@@ -69,6 +72,9 @@ import com.google.common.collect.Sets;
 
 public class RecipientRewriteTableProcessor {
     private static final Logger LOGGER = 
LoggerFactory.getLogger(RecipientRewriteTableProcessor.class);
+
+    public static final AttributeName FORWARDED_ATTRIBUTE_NAME = 
AttributeName.of("org.apache.james.forwarded");
+    private static final Attribute FORWARDED_ATTRIBUTE = new 
Attribute(FORWARDED_ATTRIBUTE_NAME, AttributeValue.of(true));
     private static final boolean REWRITE_SENDER_UPON_FORWARD = true;
     private static final boolean FORWARD_AUTOMATED_EMAILS = true;
 
@@ -237,6 +243,7 @@ public class RecipientRewriteTableProcessor {
                 try {
                     copy.setSender(originalRecipient);
                     copy.setRecipients(newRecipients);
+                    copy.setAttribute(FORWARDED_ATTRIBUTE);
                     recordedRecipients.merge(originalRecipient).recordOn(copy);
 
                     context.sendMail(copy);
diff --git 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
index c3ddac0d7b..a673b5209f 100644
--- 
a/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
+++ 
b/server/mailet/mailets/src/test/java/org/apache/james/transport/mailets/RecipientRewriteTableProcessorTest.java
@@ -44,6 +44,7 @@ import org.apache.mailet.Attribute;
 import org.apache.mailet.AttributeName;
 import org.apache.mailet.AttributeValue;
 import org.apache.mailet.Mail;
+import org.apache.mailet.ProcessingState;
 import org.apache.mailet.base.MailAddressFixture;
 import org.apache.mailet.base.test.FakeMail;
 import org.apache.mailet.base.test.FakeMailContext;
@@ -348,6 +349,32 @@ class RecipientRewriteTableProcessorTest {
         assertThat(mail.getRecipients()).isEmpty();
     }
 
+    @Test
+    void processForwardsShouldSetForwardedAttributeOnForwardedCopy() throws 
Exception {
+        RecipientRewriteTableProcessor processorWithRewrite = new 
RecipientRewriteTableProcessor(
+            virtualTableStore, domainList, mailetContext, new 
ProcessingState(Mail.ERROR), true, false);
+
+        virtualTableStore.addMapping(
+            MappingSource.fromMailAddress(MailAddressFixture.OTHER_AT_LOCAL),
+            Mapping.forward(MailAddressFixture.ANY_AT_JAMES.asString()));
+
+        mail = FakeMail.builder()
+            .name("mail")
+            .sender(MailAddressFixture.ANY_AT_JAMES)
+            .mimeMessage(message)
+            .recipients(MailAddressFixture.OTHER_AT_LOCAL)
+            .build();
+
+        processorWithRewrite.processForwards(mail);
+
+        assertThat(mailetContext.getSentMails())
+            .hasSize(1)
+            .allSatisfy(sentMail -> assertThat(sentMail.getAttributes())
+                .containsEntry(
+                    RecipientRewriteTableProcessor.FORWARDED_ATTRIBUTE_NAME,
+                    new 
Attribute(RecipientRewriteTableProcessor.FORWARDED_ATTRIBUTE_NAME, 
AttributeValue.of(true))));
+    }
+
     private void defineDefaultDomain(String jamesLocal) throws 
ConfigurationException {
         
domainList.configure(DomainListConfiguration.builder().defaultDomain(Domain.of(jamesLocal)));
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to