summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Steinmetz <richard@steinmetz.cloud>2024-01-02 13:48:09 +0100
committerGitHub <noreply@github.com>2024-01-02 13:48:09 +0100
commitfc0822d37db94fa8cc21cadca118a47f425f5f5f (patch)
tree360f1ba2c599b194dc9039eb53be86618931f562
parent138a4714b39a9d037522a477337f07a351cf2930 (diff)
parentfe5b5a7fa3dcff0e3488bfba7a902870521c06be (diff)
downloadnextcloud-server-fc0822d37db94fa8cc21cadca118a47f425f5f5f.tar.gz
nextcloud-server-fc0822d37db94fa8cc21cadca118a47f425f5f5f.zip
Merge pull request #42375 from nextcloud/backport/42339/stable27
[stable27] fix(dav): allow multiple organizers if possible
-rw-r--r--apps/dav/lib/CalDAV/Schedule/Plugin.php68
1 files changed, 67 insertions, 1 deletions
diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php
index 0751638b697..8b535880aa4 100644
--- a/apps/dav/lib/CalDAV/Schedule/Plugin.php
+++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php
@@ -36,11 +36,14 @@ use OCA\DAV\CalDAV\CalendarHome;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\ICalendar;
+use Sabre\CalDAV\ICalendarObject;
+use Sabre\CalDAV\Schedule\ISchedulingObject;
use Sabre\DAV\INode;
use Sabre\DAV\IProperties;
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\Xml\Property\LocalHref;
+use Sabre\DAVACL\IACL;
use Sabre\DAVACL\IPrincipal;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
@@ -50,6 +53,7 @@ use Sabre\VObject\Component\VEvent;
use Sabre\VObject\DateTimeParser;
use Sabre\VObject\FreeBusyGenerator;
use Sabre\VObject\ITip;
+use Sabre\VObject\ITip\SameOrganizerForAllComponentsException;
use Sabre\VObject\Parameter;
use Sabre\VObject\Property;
use Sabre\VObject\Reader;
@@ -161,7 +165,29 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
$this->pathOfCalendarObjectChange = $request->getPath();
}
- parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew);
+ try {
+ parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew);
+ } catch (SameOrganizerForAllComponentsException $e) {
+ $this->handleSameOrganizerException($e, $vCal, $calendarPath);
+ }
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function beforeUnbind($path): void {
+ try {
+ parent::beforeUnbind($path);
+ } catch (SameOrganizerForAllComponentsException $e) {
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!$node instanceof ICalendarObject || $node instanceof ISchedulingObject) {
+ throw $e;
+ }
+
+ /** @var VCalendar $vCal */
+ $vCal = Reader::read($node->get());
+ $this->handleSameOrganizerException($e, $vCal, $path);
+ }
}
/**
@@ -630,4 +656,44 @@ EOF;
'{DAV:}displayname' => $displayName,
]);
}
+
+ /**
+ * Try to handle the given exception gracefully or throw it if necessary.
+ *
+ * @throws SameOrganizerForAllComponentsException If the exception should not be ignored
+ */
+ private function handleSameOrganizerException(
+ SameOrganizerForAllComponentsException $e,
+ VCalendar $vCal,
+ string $calendarPath,
+ ): void {
+ // This is very hacky! However, we want to allow saving events with multiple
+ // organizers. Those events are not RFC compliant, but sometimes imported from major
+ // external calendar services (e.g. Google). If the current user is not an organizer of
+ // the event we ignore the exception as no scheduling messages will be sent anyway.
+
+ // It would be cleaner to patch Sabre to validate organizers *after* checking if
+ // scheduling messages are necessary. Currently, organizers are validated first and
+ // afterwards the broker checks if messages should be scheduled. So the code will throw
+ // even if the organizers are not relevant. This is to ensure compliance with RFCs but
+ // a bit too strict for real world usage.
+
+ if (!isset($vCal->VEVENT)) {
+ throw $e;
+ }
+
+ $calendarNode = $this->server->tree->getNodeForPath($calendarPath);
+ if (!($calendarNode instanceof IACL)) {
+ // Should always be an instance of IACL but just to be sure
+ throw $e;
+ }
+
+ $addresses = $this->getAddressesForPrincipal($calendarNode->getOwner());
+ foreach ($vCal->VEVENT as $vevent) {
+ if (in_array($vevent->ORGANIZER->getNormalizedValue(), $addresses, true)) {
+ // User is an organizer => throw the exception
+ throw $e;
+ }
+ }
+ }
}