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

commit 52548c55f566f7a2c7fc0cf27737a1c3a13ad97f
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Aug 14 11:31:39 2026 +0700

    JAMES-4218 APPEND should trigger flag unsolicited notifications
---
 .../james/imap/scripts/UserFlagsSupport.test       | 20 +++++-
 .../imap/processor/base/SelectedMailboxImpl.java   | 18 ++++++
 .../processor/base/SelectedMailboxImplTest.java    | 71 ++++++++++++++++++++++
 3 files changed, 108 insertions(+), 1 deletion(-)

diff --git 
a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/UserFlagsSupport.test
 
b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/UserFlagsSupport.test
index 5324893264..32c68ebf1a 100644
--- 
a/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/UserFlagsSupport.test
+++ 
b/mpt/impl/imap-mailbox/core/src/main/resources/org/apache/james/imap/scripts/UserFlagsSupport.test
@@ -59,4 +59,22 @@ S: A7 OK STORE completed\.
 
 C: A8 FETCH 1:1 (FLAGS)
 S: \* 1 FETCH \(FLAGS \(\\Recent\)\)
-S: A8 OK FETCH completed.
\ No newline at end of file
+S: A8 OK FETCH completed.
+
+C: A9 APPEND testmailbox (appendflag) {185+}
+C: From: Timothy Tayler <[email protected]>
+C: To: Samual Smith <[email protected]>
+C: Date: Thu, 14 Feb 2008 12:00:00 +0000 (GMT)
+C: Subject: A Simple Email
+C:
+C: This is a very simple email.
+C:
+S: \* 2 EXISTS
+S: \* 2 RECENT
+S: \* FLAGS \(.*appendflag.*\)
+S: \* OK \[PERMANENTFLAGS \(.*appendflag.*\\\*\)\] Limited
+S: A9 OK (\[.+\] )?APPEND completed\.
+
+C: A10 FETCH 2:2 (FLAGS)
+S: \* 2 FETCH \(FLAGS \(\\Recent appendflag\)\)
+S: A10 OK FETCH completed.
\ No newline at end of file
diff --git 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
index 1698be2dba..e922d576ca 100644
--- 
a/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
+++ 
b/protocols/imap/src/main/java/org/apache/james/imap/processor/base/SelectedMailboxImpl.java
@@ -59,6 +59,7 @@ import 
org.apache.james.mailbox.events.MailboxIdRegistrationKey;
 import org.apache.james.mailbox.exception.MailboxException;
 import org.apache.james.mailbox.model.MailboxId;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.MessageMetaData;
 import org.apache.james.mailbox.model.SearchQuery;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.reactivestreams.Publisher;
@@ -476,6 +477,9 @@ public class SelectedMailboxImpl implements 
SelectedMailbox, EventListener.React
         sizeChanged.set(true);
         uidMsnConverter.addAll(added.getUids());
         recentUids.addAll(added.getUids());
+        long stamp = applicableFlagsLock.writeLock();
+        applicableFlags = updateApplicableFlags(applicableFlags, added);
+        applicableFlagsLock.unlockWrite(stamp);
         return VOID;
     }
 
@@ -485,6 +489,12 @@ public class SelectedMailboxImpl implements 
SelectedMailbox, EventListener.React
         return applicableFlags.updateWithNewFlags(updatedFlags);
     }
 
+    @VisibleForTesting
+    static ApplicableFlags updateApplicableFlags(ApplicableFlags 
applicableFlags, Added added) {
+        Flags addedFlags = mergeAllNewFlags(added);
+        return applicableFlags.updateWithNewFlags(addedFlags);
+    }
+
     private static Flags mergeAllNewFlags(FlagsUpdated flagsUpdated) {
         List<UpdatedFlags> flags = flagsUpdated.getUpdatedFlags();
         FlagsBuilder builder = FlagsBuilder.builder();
@@ -492,6 +502,14 @@ public class SelectedMailboxImpl implements 
SelectedMailbox, EventListener.React
         return builder.build();
     }
 
+    private static Flags mergeAllNewFlags(Added added) {
+        FlagsBuilder builder = FlagsBuilder.builder();
+        added.getAdded().values().stream()
+            .map(MessageMetaData::getFlags)
+            .forEach(builder::add);
+        return builder.build();
+    }
+
     @Override
     public NullableMessageSequenceNumber msn(MessageUid uid) {
         return uidMsnConverter.getMsn(uid);
diff --git 
a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
index 0e1dea9161..991177915a 100644
--- 
a/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
+++ 
b/protocols/imap/src/test/java/org/apache/james/imap/processor/base/SelectedMailboxImplTest.java
@@ -40,6 +40,7 @@ import java.util.concurrent.Executors;
 import java.util.concurrent.ThreadFactory;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.function.Consumer;
+import java.util.stream.IntStream;
 
 import jakarta.mail.Flags;
 
@@ -56,6 +57,7 @@ import org.apache.james.mailbox.MailboxSessionUtil;
 import org.apache.james.mailbox.MessageManager;
 import org.apache.james.mailbox.MessageUid;
 import org.apache.james.mailbox.ModSeq;
+import org.apache.james.mailbox.events.MailboxEvents.Added;
 import org.apache.james.mailbox.events.MailboxEvents.FlagsUpdated;
 import org.apache.james.mailbox.events.MailboxIdRegistrationKey;
 import org.apache.james.mailbox.model.Mailbox;
@@ -313,6 +315,75 @@ class SelectedMailboxImplTest {
                 
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).add("Foo").build());
             });
         }
+
+        @Test
+        void updateApplicableFlagsShouldNotUpdateWhenAddedWithoutUserFlag() {
+            ApplicableFlags applicableFlags = 
ApplicableFlags.from(flagsBuilder().add(SEEN).build());
+            Added added = added(flagsBuilder().add(ANSWERED).build());
+            ApplicableFlags actual = 
SelectedMailboxImpl.updateApplicableFlags(applicableFlags, added);
+            assertThat(actual).satisfies(ap -> {
+                assertThat(ap.updated()).isFalse();
+                
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).add(ANSWERED).build());
+            });
+        }
+
+        @Test
+        void updateApplicableFlagsShouldNotIncludeRecentWhenAdded() {
+            ApplicableFlags applicableFlags = 
ApplicableFlags.from(flagsBuilder().add(SEEN).build());
+            Added added = added(flagsBuilder().add(RECENT).build());
+            ApplicableFlags actual = 
SelectedMailboxImpl.updateApplicableFlags(applicableFlags, added);
+            assertThat(actual).satisfies(ap -> {
+                assertThat(ap.updated()).isFalse();
+                
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).build());
+            });
+        }
+
+        @Test
+        void updateApplicableFlagsShouldUpdateWhenAddedWithNewUserFlag() {
+            ApplicableFlags applicableFlags = 
ApplicableFlags.from(flagsBuilder().add(SEEN).build());
+            Added added = added(flagsBuilder().add("Foo").build());
+            ApplicableFlags actual = 
SelectedMailboxImpl.updateApplicableFlags(applicableFlags, added);
+            assertThat(actual).satisfies(ap -> {
+                assertThat(ap.updated()).isTrue();
+                
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).add("Foo").build());
+            });
+        }
+
+        @Test
+        void 
updateApplicableFlagsShouldNotUpdateWhenAddedWithAlreadyKnownUserFlag() {
+            ApplicableFlags applicableFlags = 
ApplicableFlags.from(flagsBuilder().add(SEEN).add("Foo").build());
+            Added added = added(flagsBuilder().add("Foo").build());
+            ApplicableFlags actual = 
SelectedMailboxImpl.updateApplicableFlags(applicableFlags, added);
+            assertThat(actual).satisfies(ap -> {
+                assertThat(ap.updated()).isFalse();
+                
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).add("Foo").build());
+            });
+        }
+
+        @Test
+        void updateApplicableFlagsShouldMergeUserFlagsOfAllAddedMessages() {
+            ApplicableFlags applicableFlags = 
ApplicableFlags.from(flagsBuilder().add(SEEN).build());
+            Added added = added(flagsBuilder().add("Foo").build(), 
flagsBuilder().add("Bar").build());
+            ApplicableFlags actual = 
SelectedMailboxImpl.updateApplicableFlags(applicableFlags, added);
+            assertThat(actual).satisfies(ap -> {
+                assertThat(ap.updated()).isTrue();
+                
assertThat(ap.flags()).isEqualTo(flagsBuilder().add(SEEN).add("Foo").add("Bar").build());
+            });
+        }
+    }
+
+    private Added added(Flags... flags) {
+        return EventFactory.added()
+            .randomEventId()
+            .mailboxSession(MailboxSessionUtil.create(BOB))
+            .mailbox(mailbox)
+            .addMetaData(IntStream.range(0, flags.length)
+                .mapToObj(i -> new MessageMetaData(MessageUid.of(i + 1), 
MOD_SEQ, flags[i], SIZE, new Date(),
+                    Optional.empty(), new DefaultMessageId(), 
ThreadId.fromBaseMessageId(new DefaultMessageId())))
+                .collect(ImmutableList.toImmutableList()))
+            .isDelivery(!IS_DELIVERY)
+            .isAppended(IS_APPENDED)
+            .build();
     }
 
     private static FlagsBuilder flagsBuilder() {


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

Reply via email to