diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index 1bcea5d73533f..adfae6e85ac9f 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -261,8 +261,13 @@ public function handleIMip( } if (!isset($vEvent->ORGANIZER)) { - $this->logger->warning('iMip message event dose not contains an organizer'); - return false; + // quirks mode: for Microsoft Exchange Servers use recipient as organizer if no organizer is set + if (isset($options['recipient']) && $options['recipient'] !== '') { + $vEvent->add('ORGANIZER', 'mailto:' . $options['recipient']); + } else { + $this->logger->warning('iMip message event does not contain an organizer and no recipient was provided'); + return false; + } } if (!isset($vEvent->ATTENDEE)) { @@ -335,7 +340,8 @@ public function handleIMipRequest( return false; } $userId = substr($principalUri, 17); - return $this->handleIMip($userId, $calendarData); + $options = ['recipient' => $recipient]; + return $this->handleIMip($userId, $calendarData, $options); } /** @@ -354,7 +360,8 @@ public function handleIMipReply( return false; } $userId = substr($principalUri, 17); - return $this->handleIMip($userId, $calendarData); + $options = ['recipient' => $recipient]; + return $this->handleIMip($userId, $calendarData, $options); } /** @@ -374,7 +381,8 @@ public function handleIMipCancel( return false; } $userId = substr($principalUri, 17); - return $this->handleIMip($userId, $calendarData); + $options = ['recipient' => $recipient]; + return $this->handleIMip($userId, $calendarData, $options); } public function createEventBuilder(): ICalendarEventBuilder { diff --git a/lib/public/Calendar/IManager.php b/lib/public/Calendar/IManager.php index ae3284256af09..fa108192be083 100644 --- a/lib/public/Calendar/IManager.php +++ b/lib/public/Calendar/IManager.php @@ -143,7 +143,7 @@ public function newQuery(string $principalUri) : ICalendarQuery; /** * Handles a iMip message * - * @param array{absent?: "create"} $options + * @param array{absent?: "create", recipient?: string} $options * * @throws \OCP\DB\Exception * diff --git a/tests/lib/Calendar/ManagerTest.php b/tests/lib/Calendar/ManagerTest.php index 2e7217e06c814..d343745022643 100644 --- a/tests/lib/Calendar/ManagerTest.php +++ b/tests/lib/Calendar/ManagerTest.php @@ -387,6 +387,82 @@ public function testHandleImipWithNoEvent(): void { $this->assertFalse($result); } + public function testHandleImipMissingOrganizerWithRecipient(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + $userCalendar->expects(self::once()) + ->method('isDeleted') + ->willReturn(false); + $userCalendar->expects(self::once()) + ->method('isWritable') + ->willReturn(true); + $userCalendar->expects(self::once()) + ->method('search') + ->willReturn([['uri' => 'principals/user/attendee1/personal']]); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + $this->propertyMapper, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct parameters + $userId = 'attendee1'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->VEVENT->remove('ORGANIZER'); + // construct user calendar returns + $userCalendar->expects(self::once()) + ->method('handleIMipMessage'); + // test method + $result = $manager->handleIMip($userId, $calendar->serialize(), ['recipient' => 'organizer@testing.com']); + } + + public function testHandleImipMissingOrganizerNoRecipient(): void { + // construct mock user calendar + $userCalendar = $this->createMock(ITestCalendar::class); + // construct mock calendar manager and returns + /** @var Manager&MockObject $manager */ + $manager = $this->getMockBuilder(Manager::class) + ->setConstructorArgs([ + $this->coordinator, + $this->container, + $this->logger, + $this->time, + $this->secureRandom, + $this->userManager, + $this->serverFactory, + $this->propertyMapper, + ]) + ->onlyMethods(['getCalendarsForPrincipal']) + ->getMock(); + $manager->expects(self::once()) + ->method('getCalendarsForPrincipal') + ->willReturn([$userCalendar]); + // construct parameters + $userId = 'attendee1'; + $calendar = $this->vCalendar1a; + $calendar->add('METHOD', 'REQUEST'); + $calendar->VEVENT->remove('ORGANIZER'); + // Logger expects warning + $this->logger->expects($this->once()) + ->method('warning') + ->with('iMip message event does not contain an organizer and no recipient was provided'); + + $result = $manager->handleIMip($userId, $calendar->serialize(), []); + } + public function testHandleImipWithNoUid(): void { // construct mock user calendar $userCalendar = $this->createMock(ITestCalendar::class);