diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index 13e310d8221..08d597cbbab 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -11,6 +11,7 @@ namespace OC\Calendar; use DateTimeInterface; use OC\AppFramework\Bootstrap\Coordinator; use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin; +use OCA\DAV\Db\PropertyMapper; use OCA\DAV\ServerFactory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Calendar\Exceptions\CalendarException; @@ -58,6 +59,7 @@ class Manager implements IManager { private ISecureRandom $random, private IUserManager $userManager, private ServerFactory $serverFactory, + private PropertyMapper $propertyMapper, ) { } @@ -227,6 +229,7 @@ class Manager implements IManager { public function handleIMip( string $userId, string $message, + array $options = [], ): bool { $userUri = 'principals/users/' . $userId; @@ -287,6 +290,30 @@ class Manager implements IManager { } } + if ($options['absent'] === 'create') { + // retrieve the primary calendar for the user + $calendar = $this->getPrimaryCalendar($userId); + if ($calendar !== null && ( + !$calendar instanceof IHandleImipMessage || !$calendar instanceof ICalendarIsWritable || $calendar->isDeleted() || !$calendar->isWritable() + )) { + $calendar = null; + } + // if no primary calendar is set, use the first writable calendar + if ($calendar === null) { + foreach ($userCalendars as $userCalendar) { + if ($userCalendar instanceof IHandleImipMessage && $userCalendar instanceof ICalendarIsWritable && !$userCalendar->isDeleted() && $userCalendar->isWritable()) { + $calendar = $userCalendar; + break; + } + } + } + if ($calendar === null) { + $this->logger->warning('iMip message could not be processed because no writable calendar was found'); + return false; + } + $calendar->handleIMipMessage($userId, $vObject->serialize()); + } + $this->logger->warning('iMip message could not be processed because no corresponding event was found in any calendar'); return false; @@ -437,4 +464,32 @@ class Manager implements IManager { return $result; } + + public function getPrimaryCalendar(string $userId): ?ICalendar { + // determine if the principal has a default calendar configured + $properties = $this->propertyMapper->findPropertyByPathAndName( + $userId, + 'principals/users/' . $userId, + '{urn:ietf:params:xml:ns:caldav}schedule-default-calendar-URL' + ); + if ($properties === []) { + return null; + } + // extract the calendar URI from the property value + $propertyValue = $properties[0]->getPropertyvalue() ?? null; + if (str_starts_with($propertyValue, 'calendars/' . $userId)) { + $calendarUri = rtrim(str_replace('calendars/' . $userId . '/', '', $propertyValue), '/'); + } + if (empty($calendarUri)) { + return null; + } + // retrieve the calendar by URI + $calendars = $this->getCalendarsForPrincipal('principals/users/' . $userId, [$calendarUri]); + if ($calendars === []) { + return null; + } + + return $calendars[0]; + } + } diff --git a/lib/public/Calendar/IManager.php b/lib/public/Calendar/IManager.php index ea0a759a239..ae3284256af 100644 --- a/lib/public/Calendar/IManager.php +++ b/lib/public/Calendar/IManager.php @@ -143,11 +143,13 @@ interface IManager { /** * Handles a iMip message * - * @since 32.0.0 + * @param array{absent?: "create"} $options * * @throws \OCP\DB\Exception + * + * @since 32.0.0 */ - public function handleIMip(string $userId, string $message): bool; + public function handleIMip(string $userId, string $message, array $options = []): bool; /** * Handle a iMip REQUEST message diff --git a/tests/lib/Calendar/ManagerTest.php b/tests/lib/Calendar/ManagerTest.php index bd0bdbde9b2..2e7217e06c8 100644 --- a/tests/lib/Calendar/ManagerTest.php +++ b/tests/lib/Calendar/ManagerTest.php @@ -13,6 +13,7 @@ use OC\Calendar\AvailabilityResult; use OC\Calendar\Manager; use OCA\DAV\CalDAV\Auth\CustomPrincipalPlugin; use OCA\DAV\Connector\Sabre\Server; +use OCA\DAV\Db\PropertyMapper; use OCA\DAV\ServerFactory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Calendar\ICalendar; @@ -61,6 +62,7 @@ class ManagerTest extends TestCase { private IUserManager&MockObject $userManager; private ServerFactory&MockObject $serverFactory; + private PropertyMapper&MockObject $propertyMapper; private VCalendar $vCalendar1a; private VCalendar $vCalendar2a; @@ -76,6 +78,7 @@ class ManagerTest extends TestCase { $this->secureRandom = $this->createMock(ISecureRandom::class); $this->userManager = $this->createMock(IUserManager::class); $this->serverFactory = $this->createMock(ServerFactory::class); + $this->propertyMapper = $this->createMock(PropertyMapper::class); $this->manager = new Manager( $this->coordinator, @@ -85,6 +88,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ); // construct calendar with a 1 hour event and same start/end time zones @@ -328,6 +332,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['getCalendarsForPrincipal']) ->getMock(); @@ -361,6 +366,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['getCalendarsForPrincipal']) ->getMock(); @@ -395,6 +401,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['getCalendarsForPrincipal']) ->getMock(); @@ -438,6 +445,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['getCalendarsForPrincipal']) ->getMock(); @@ -480,6 +488,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['getCalendarsForPrincipal']) ->getMock(); @@ -527,6 +536,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['handleIMip']) ->getMock(); @@ -569,6 +579,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['handleIMip']) ->getMock(); @@ -613,6 +624,7 @@ class ManagerTest extends TestCase { $this->secureRandom, $this->userManager, $this->serverFactory, + $this->propertyMapper, ]) ->onlyMethods(['handleIMip']) ->getMock();