|
36 | 36 | use OCP\IConfig; |
37 | 37 | use Psr\Log\LoggerInterface; |
38 | 38 | use Sabre\CalDAV\ICalendar; |
| 39 | +use Sabre\CalDAV\ICalendarObject; |
| 40 | +use Sabre\CalDAV\Schedule\ISchedulingObject; |
39 | 41 | use Sabre\DAV\INode; |
40 | 42 | use Sabre\DAV\IProperties; |
41 | 43 | use Sabre\DAV\PropFind; |
42 | 44 | use Sabre\DAV\Server; |
43 | 45 | use Sabre\DAV\Xml\Property\LocalHref; |
| 46 | +use Sabre\DAVACL\IACL; |
44 | 47 | use Sabre\DAVACL\IPrincipal; |
45 | 48 | use Sabre\HTTP\RequestInterface; |
46 | 49 | use Sabre\HTTP\ResponseInterface; |
|
50 | 53 | use Sabre\VObject\DateTimeParser; |
51 | 54 | use Sabre\VObject\FreeBusyGenerator; |
52 | 55 | use Sabre\VObject\ITip; |
| 56 | +use Sabre\VObject\ITip\SameOrganizerForAllComponentsException; |
53 | 57 | use Sabre\VObject\Parameter; |
54 | 58 | use Sabre\VObject\Property; |
55 | 59 | use Sabre\VObject\Reader; |
@@ -161,7 +165,29 @@ public function calendarObjectChange(RequestInterface $request, ResponseInterfac |
161 | 165 | $this->pathOfCalendarObjectChange = $request->getPath(); |
162 | 166 | } |
163 | 167 |
|
164 | | - parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew); |
| 168 | + try { |
| 169 | + parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew); |
| 170 | + } catch (SameOrganizerForAllComponentsException $e) { |
| 171 | + $this->handleSameOrganizerException($e, $vCal, $calendarPath); |
| 172 | + } |
| 173 | + } |
| 174 | + |
| 175 | + /** |
| 176 | + * @inheritDoc |
| 177 | + */ |
| 178 | + public function beforeUnbind($path): void { |
| 179 | + try { |
| 180 | + parent::beforeUnbind($path); |
| 181 | + } catch (SameOrganizerForAllComponentsException $e) { |
| 182 | + $node = $this->server->tree->getNodeForPath($path); |
| 183 | + if (!$node instanceof ICalendarObject || $node instanceof ISchedulingObject) { |
| 184 | + throw $e; |
| 185 | + } |
| 186 | + |
| 187 | + /** @var VCalendar $vCal */ |
| 188 | + $vCal = Reader::read($node->get()); |
| 189 | + $this->handleSameOrganizerException($e, $vCal, $path); |
| 190 | + } |
165 | 191 | } |
166 | 192 |
|
167 | 193 | /** |
@@ -630,4 +656,44 @@ private function createCalendar(CalendarHome $calendarHome, string $principalUri |
630 | 656 | '{DAV:}displayname' => $displayName, |
631 | 657 | ]); |
632 | 658 | } |
| 659 | + |
| 660 | + /** |
| 661 | + * Try to handle the given exception gracefully or throw it if necessary. |
| 662 | + * |
| 663 | + * @throws SameOrganizerForAllComponentsException If the exception should not be ignored |
| 664 | + */ |
| 665 | + private function handleSameOrganizerException( |
| 666 | + SameOrganizerForAllComponentsException $e, |
| 667 | + VCalendar $vCal, |
| 668 | + string $calendarPath, |
| 669 | + ): void { |
| 670 | + // This is very hacky! However, we want to allow saving events with multiple |
| 671 | + // organizers. Those events are not RFC compliant, but sometimes imported from major |
| 672 | + // external calendar services (e.g. Google). If the current user is not an organizer of |
| 673 | + // the event we ignore the exception as no scheduling messages will be sent anyway. |
| 674 | + |
| 675 | + // It would be cleaner to patch Sabre to validate organizers *after* checking if |
| 676 | + // scheduling messages are necessary. Currently, organizers are validated first and |
| 677 | + // afterwards the broker checks if messages should be scheduled. So the code will throw |
| 678 | + // even if the organizers are not relevant. This is to ensure compliance with RFCs but |
| 679 | + // a bit too strict for real world usage. |
| 680 | + |
| 681 | + if (!isset($vCal->VEVENT)) { |
| 682 | + throw $e; |
| 683 | + } |
| 684 | + |
| 685 | + $calendarNode = $this->server->tree->getNodeForPath($calendarPath); |
| 686 | + if (!($calendarNode instanceof IACL)) { |
| 687 | + // Should always be an instance of IACL but just to be sure |
| 688 | + throw $e; |
| 689 | + } |
| 690 | + |
| 691 | + $addresses = $this->getAddressesForPrincipal($calendarNode->getOwner()); |
| 692 | + foreach ($vCal->VEVENT as $vevent) { |
| 693 | + if (in_array($vevent->ORGANIZER->getNormalizedValue(), $addresses, true)) { |
| 694 | + // User is an organizer => throw the exception |
| 695 | + throw $e; |
| 696 | + } |
| 697 | + } |
| 698 | + } |
633 | 699 | } |
0 commit comments