diff --git a/lib/Listener/MailboxesSynchronizedSpecialMailboxesUpdater.php b/lib/Listener/MailboxesSynchronizedSpecialMailboxesUpdater.php index ef7a67ab38..7fc0bcf959 100644 --- a/lib/Listener/MailboxesSynchronizedSpecialMailboxesUpdater.php +++ b/lib/Listener/MailboxesSynchronizedSpecialMailboxesUpdater.php @@ -46,10 +46,13 @@ public function handle(Event $event): void { $mailboxes = $this->indexMailboxes( $this->mailboxMapper->findAll($account) ); + // Never auto-pick a shared mailbox; a shared "Sent" or "Drafts" folder + // belongs to someone else's account, not this one. + $personalMailboxes = array_filter($mailboxes, static fn (Mailbox $mb): bool => !$mb->isShared()); if ($mailAccount->getDraftsMailboxId() === null || !array_key_exists($mailAccount->getDraftsMailboxId(), $mailboxes)) { try { - $draftsMailbox = $this->findSpecial($mailboxes, 'drafts'); + $draftsMailbox = $this->findSpecial($personalMailboxes, 'drafts'); $mailAccount->setDraftsMailboxId($draftsMailbox->getId()); } catch (DoesNotExistException $e) { $this->logger->info("Account {$account->getId()} does not have a drafts mailbox"); @@ -59,7 +62,7 @@ public function handle(Event $event): void { } if ($mailAccount->getSentMailboxId() === null || !array_key_exists($mailAccount->getSentMailboxId(), $mailboxes)) { try { - $sentMailbox = $this->findSpecial($mailboxes, 'sent'); + $sentMailbox = $this->findSpecial($personalMailboxes, 'sent'); $mailAccount->setSentMailboxId($sentMailbox->getId()); } catch (DoesNotExistException $e) { $this->logger->info("Account {$account->getId()} does not have a sent mailbox"); @@ -69,7 +72,7 @@ public function handle(Event $event): void { } if ($mailAccount->getTrashMailboxId() === null || !array_key_exists($mailAccount->getTrashMailboxId(), $mailboxes)) { try { - $trashMailbox = $this->findSpecial($mailboxes, 'trash'); + $trashMailbox = $this->findSpecial($personalMailboxes, 'trash'); $mailAccount->setTrashMailboxId($trashMailbox->getId()); } catch (DoesNotExistException $e) { $this->logger->info("Account {$account->getId()} does not have a trash mailbox"); @@ -79,7 +82,7 @@ public function handle(Event $event): void { } if ($mailAccount->getArchiveMailboxId() === null || !array_key_exists($mailAccount->getArchiveMailboxId(), $mailboxes)) { try { - $archiveMailbox = $this->findSpecial($mailboxes, 'archive'); + $archiveMailbox = $this->findSpecial($personalMailboxes, 'archive'); $mailAccount->setArchiveMailboxId($archiveMailbox->getId()); } catch (DoesNotExistException $e) { $this->logger->info("Account {$account->getId()} does not have an archive mailbox"); @@ -89,7 +92,7 @@ public function handle(Event $event): void { } if ($mailAccount->getJunkMailboxId() === null || !array_key_exists($mailAccount->getJunkMailboxId(), $mailboxes)) { try { - $junkMailbox = $this->findSpecial($mailboxes, 'junk'); + $junkMailbox = $this->findSpecial($personalMailboxes, 'junk'); $mailAccount->setJunkMailboxId($junkMailbox->getId()); } catch (DoesNotExistException) { $this->logger->info("Account {$account->getId()} does not have an junk mailbox"); @@ -98,7 +101,7 @@ public function handle(Event $event): void { } if ($mailAccount->getSnoozeMailboxId() === null || !array_key_exists($mailAccount->getSnoozeMailboxId(), $mailboxes)) { try { - $snoozeMailbox = $this->findSpecial($mailboxes, 'snooze'); + $snoozeMailbox = $this->findSpecial($personalMailboxes, 'snooze'); $mailAccount->setSnoozeMailboxId($snoozeMailbox->getId()); } catch (DoesNotExistException $e) { $this->logger->info("Account {$account->getId()} does not have an snooze mailbox"); diff --git a/tests/Unit/Listener/MailboxesSynchronizedSpecialMailboxesUpdaterTest.php b/tests/Unit/Listener/MailboxesSynchronizedSpecialMailboxesUpdaterTest.php new file mode 100644 index 0000000000..36f079a247 --- /dev/null +++ b/tests/Unit/Listener/MailboxesSynchronizedSpecialMailboxesUpdaterTest.php @@ -0,0 +1,107 @@ +mailAccountMapper = $this->createMock(MailAccountMapper::class); + $this->mailboxMapper = $this->createMock(MailboxMapper::class); + + $this->listener = new MailboxesSynchronizedSpecialMailboxesUpdater( + $this->mailAccountMapper, + $this->mailboxMapper, + $this->createMock(LoggerInterface::class), + ); + } + + public function testIgnoresSharedMailboxWithMatchingName(): void { + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + $sharedSent = new Mailbox(); + $sharedSent->setId(100); + $sharedSent->setName('Sent'); + $sharedSent->setSpecialUse('[]'); + $sharedSent->setShared(true); + $personalSent = new Mailbox(); + $personalSent->setId(200); + $personalSent->setName('Sent'); + $personalSent->setSpecialUse('[]'); + $personalSent->setShared(false); + $this->mailboxMapper->method('findAll') + ->willReturn([$sharedSent, $personalSent]); + $this->mailAccountMapper->expects($this->once()) + ->method('update') + ->with($this->callback(function (MailAccount $updated) { + return $updated->getSentMailboxId() === 200; + })); + + $this->listener->handle(new MailboxesSynchronizedEvent($account)); + } + + public function testIgnoresSharedMailboxWithMatchingSpecialUse(): void { + $mailAccount = new MailAccount(); + $account = new Account($mailAccount); + $sharedDrafts = new Mailbox(); + $sharedDrafts->setId(100); + $sharedDrafts->setName('Shared/Drafts'); + $sharedDrafts->setSpecialUse('["drafts"]'); + $sharedDrafts->setShared(true); + $personalDrafts = new Mailbox(); + $personalDrafts->setId(200); + $personalDrafts->setName('Drafts'); + $personalDrafts->setSpecialUse('["drafts"]'); + $personalDrafts->setShared(false); + $this->mailboxMapper->method('findAll') + ->willReturn([$sharedDrafts, $personalDrafts]); + $this->mailAccountMapper->expects($this->once()) + ->method('update') + ->with($this->callback(function (MailAccount $updated) { + return $updated->getDraftsMailboxId() === 200; + })); + + $this->listener->handle(new MailboxesSynchronizedEvent($account)); + } + + public function testKeepsExistingAssignmentEvenIfShared(): void { + $mailAccount = new MailAccount(); + $mailAccount->setSentMailboxId(100); + $account = new Account($mailAccount); + $sharedSent = new Mailbox(); + $sharedSent->setId(100); + $sharedSent->setName('Sent'); + $sharedSent->setSpecialUse('[]'); + $sharedSent->setShared(true); + $this->mailboxMapper->method('findAll') + ->willReturn([$sharedSent]); + $this->mailAccountMapper->expects($this->once()) + ->method('update') + ->with($this->callback(function (MailAccount $updated) { + return $updated->getSentMailboxId() === 100; + })); + + $this->listener->handle(new MailboxesSynchronizedEvent($account)); + } +}