diff options
Diffstat (limited to 'apps/dav/lib/CalDAV/Schedule/Plugin.php')
-rw-r--r-- | apps/dav/lib/CalDAV/Schedule/Plugin.php | 142 |
1 files changed, 97 insertions, 45 deletions
diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php index a1297fd2cf1..a001df8b2a8 100644 --- a/apps/dav/lib/CalDAV/Schedule/Plugin.php +++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php @@ -1,31 +1,8 @@ <?php + /** - * @copyright Copyright (c) 2016, Roeland Jago Douma <roeland@famdouma.nl> - * @copyright Copyright (c) 2016, Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Citharel <nextcloud@tcit.fr> - * @author Richard Steinmetz <richard@steinmetz.cloud> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OCA\DAV\CalDAV\Schedule; @@ -33,11 +10,15 @@ use DateTimeZone; use OCA\DAV\CalDAV\CalDavBackend; use OCA\DAV\CalDAV\Calendar; use OCA\DAV\CalDAV\CalendarHome; +use OCA\DAV\CalDAV\CalendarObject; +use OCA\DAV\CalDAV\DefaultCalendarValidator; +use OCA\DAV\CalDAV\TipBroker; use OCP\IConfig; use Psr\Log\LoggerInterface; use Sabre\CalDAV\ICalendar; use Sabre\CalDAV\ICalendarObject; use Sabre\CalDAV\Schedule\ISchedulingObject; +use Sabre\DAV\Exception as DavException; use Sabre\DAV\INode; use Sabre\DAV\IProperties; use Sabre\DAV\PropFind; @@ -61,11 +42,6 @@ use function Sabre\Uri\split; class Plugin extends \Sabre\CalDAV\Schedule\Plugin { - /** - * @var IConfig - */ - private $config; - /** @var ITip\Message[] */ private $schedulingResponses = []; @@ -74,14 +50,15 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin { public const CALENDAR_USER_TYPE = '{' . self::NS_CALDAV . '}calendar-user-type'; public const SCHEDULE_DEFAULT_CALENDAR_URL = '{' . Plugin::NS_CALDAV . '}schedule-default-calendar-URL'; - private LoggerInterface $logger; /** * @param IConfig $config */ - public function __construct(IConfig $config, LoggerInterface $logger) { - $this->config = $config; - $this->logger = $logger; + public function __construct( + private IConfig $config, + private LoggerInterface $logger, + private DefaultCalendarValidator $defaultCalendarValidator, + ) { } /** @@ -105,6 +82,13 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin { } /** + * Returns an instance of the iTip\Broker. + */ + protected function createITipBroker(): TipBroker { + return new TipBroker(); + } + + /** * Allow manual setting of the object change URL * to support public write * @@ -155,6 +139,11 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin { $result = []; } + // iterate through items and html decode values + foreach ($result as $key => $value) { + $result[$key] = urldecode($value); + } + return $result; } @@ -173,7 +162,47 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin { } try { - parent::calendarObjectChange($request, $response, $vCal, $calendarPath, $modified, $isNew); + + // Do not generate iTip and iMip messages if scheduling is disabled for this message + if ($request->getHeader('x-nc-scheduling') === 'false') { + return; + } + + if (!$this->scheduleReply($this->server->httpRequest)) { + return; + } + + /** @var Calendar $calendarNode */ + $calendarNode = $this->server->tree->getNodeForPath($calendarPath); + // extract addresses for owner + $addresses = $this->getAddressesForPrincipal($calendarNode->getOwner()); + // determine if request is from a sharee + if ($calendarNode->isShared()) { + // extract addresses for sharee and add to address collection + $addresses = array_merge( + $addresses, + $this->getAddressesForPrincipal($calendarNode->getPrincipalURI()) + ); + } + // determine if we are updating a calendar event + if (!$isNew) { + // retrieve current calendar event node + /** @var CalendarObject $currentNode */ + $currentNode = $this->server->tree->getNodeForPath($request->getPath()); + // convert calendar event string data to VCalendar object + /** @var \Sabre\VObject\Component\VCalendar $currentObject */ + $currentObject = Reader::read($currentNode->get()); + } else { + $currentObject = null; + } + // process request + $this->processICalendarChange($currentObject, $vCal, $addresses, [], $modified); + + if ($currentObject) { + // Destroy circular references so PHP will GC the object. + $currentObject->destroy(); + } + } catch (SameOrganizerForAllComponentsException $e) { $this->handleSameOrganizerException($e, $vCal, $calendarPath); } @@ -232,7 +261,7 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin { $principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient); $calendarUserType = $this->getCalendarUserTypeForPrincipal($principalUri); if (strcasecmp($calendarUserType, 'ROOM') !== 0 && strcasecmp($calendarUserType, 'RESOURCE') !== 0) { - $this->logger->debug('Calendar user type is room or resource, not processing further'); + $this->logger->debug('Calendar user type is neither room nor resource, not processing further'); return; } @@ -343,8 +372,8 @@ EOF; return null; } - $isResourceOrRoom = str_starts_with($principalUrl, 'principals/calendar-resources') || - str_starts_with($principalUrl, 'principals/calendar-rooms'); + $isResourceOrRoom = str_starts_with($principalUrl, 'principals/calendar-resources') + || str_starts_with($principalUrl, 'principals/calendar-rooms'); if (str_starts_with($principalUrl, 'principals/users')) { [, $userId] = split($principalUrl); @@ -379,11 +408,20 @@ EOF; * - isn't a calendar subscription * - user can write to it (no virtual/3rd-party calendars) * - calendar isn't a share + * - calendar supports VEVENTs */ foreach ($calendarHome->getChildren() as $node) { - if ($node instanceof Calendar && !$node->isSubscription() && $node->canWrite() && !$node->isShared() && !$node->isDeleted()) { - $userCalendars[] = $node; + if (!($node instanceof Calendar)) { + continue; } + + try { + $this->defaultCalendarValidator->validateScheduleDefaultCalendar($node); + } catch (DavException $e) { + continue; + } + + $userCalendars[] = $node; } if (count($userCalendars) > 0) { @@ -392,12 +430,20 @@ EOF; } else { // Otherwise if we have really nothing, create a new calendar if ($currentCalendarDeleted) { - // If the calendar exists but is deleted, we need to purge it first - // This may cause some issues in a non synchronous database setup + // If the calendar exists but is in the trash bin, we try to rename its uri + // so that we can create the new one and still restore the previous one + // otherwise we just purge the calendar by removing it before recreating it $calendar = $this->getCalendar($calendarHome, $uri); if ($calendar instanceof Calendar) { - $calendar->disableTrashbin(); - $calendar->delete(); + $backend = $calendarHome->getCalDAVBackend(); + if ($backend instanceof CalDavBackend) { + // If the CalDAV backend supports moving calendars + $this->moveCalendar($backend, $principalUrl, $uri, $uri . '-back-' . time()); + } else { + // Otherwise just purge the calendar + $calendar->disableTrashbin(); + $calendar->delete(); + } } } $this->createCalendar($calendarHome, $principalUrl, $uri, $displayName); @@ -532,7 +578,9 @@ EOF; $calendarTimeZone = new DateTimeZone('UTC'); $homePath = $result[0][200]['{' . self::NS_CALDAV . '}calendar-home-set']->getHref(); + /** @var Calendar $node */ foreach ($this->server->tree->getNodeForPath($homePath)->getChildren() as $node) { + if (!$node instanceof ICalendar) { continue; } @@ -664,6 +712,10 @@ EOF; ]); } + private function moveCalendar(CalDavBackend $calDavBackend, string $principalUri, string $oldUri, string $newUri): void { + $calDavBackend->moveCalendar($oldUri, $principalUri, $principalUri, $newUri); + } + /** * Try to handle the given exception gracefully or throw it if necessary. * |