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]
