diff options
Diffstat (limited to 'apps/dav/lib')
-rw-r--r-- | apps/dav/lib/AppInfo/PluginManager.php | 57 | ||||
-rw-r--r-- | apps/dav/lib/CalDAV/CalendarHome.php | 36 | ||||
-rw-r--r-- | apps/dav/lib/CalDAV/Integration/ExternalCalendar.php | 132 | ||||
-rw-r--r-- | apps/dav/lib/CalDAV/Integration/ICalendarProvider.php | 73 |
4 files changed, 296 insertions, 2 deletions
diff --git a/apps/dav/lib/AppInfo/PluginManager.php b/apps/dav/lib/AppInfo/PluginManager.php index 7063a150b4d..6a44332ddb2 100644 --- a/apps/dav/lib/AppInfo/PluginManager.php +++ b/apps/dav/lib/AppInfo/PluginManager.php @@ -25,6 +25,7 @@ namespace OCA\DAV\AppInfo; use OC\ServerContainer; +use OCA\DAV\CalDAV\Integration\ICalendarProvider; use OCP\App\IAppManager; use OCP\AppFramework\QueryException; @@ -59,6 +60,13 @@ class PluginManager { private $collections = null; /** + * Calendar plugins + * + * @var array + */ + private $calendarPlugins = null; + + /** * Contstruct a PluginManager * * @param ServerContainer $container server container for resolving plugin classes @@ -94,10 +102,23 @@ class PluginManager { } /** + * Returns an array of app-registered calendar plugins + * + * @return array + */ + public function getCalendarPlugins():array { + if (null === $this->calendarPlugins) { + $this->populate(); + } + return $this->calendarPlugins; + } + + /** * Retrieve plugin and collection list and populate attributes */ private function populate() { $this->plugins = []; + $this->calendarPlugins = []; $this->collections = []; foreach ($this->appManager->getInstalledApps() as $app) { // load plugins and collections from info.xml @@ -107,6 +128,7 @@ class PluginManager { } $this->loadSabrePluginsFromInfoXml($this->extractPluginList($info)); $this->loadSabreCollectionsFromInfoXml($this->extractCollectionList($info)); + $this->loadSabreCalendarPluginsFromInfoXml($this->extractCalendarPluginList($info)); } } @@ -140,6 +162,21 @@ class PluginManager { return []; } + private function extractCalendarPluginList(array $array):array { + if (isset($array['sabre']) && is_array($array['sabre'])) { + if (isset($array['sabre']['calendar-plugins']) && is_array($array['sabre']['calendar-plugins'])) { + if (isset($array['sabre']['calendar-plugins']['plugin'])) { + $items = $array['sabre']['calendar-plugins']['plugin']; + if (!is_array($items)) { + $items = [$items]; + } + return $items; + } + } + } + return []; + } + private function loadSabrePluginsFromInfoXml(array $plugins) { foreach ($plugins as $plugin) { try { @@ -168,4 +205,24 @@ class PluginManager { } } + private function loadSabreCalendarPluginsFromInfoXml(array $calendarPlugins):void { + foreach ($calendarPlugins as $calendarPlugin) { + try { + $instantiatedCalendarPlugin = $this->container->query($calendarPlugin); + } catch (QueryException $e) { + if (class_exists($calendarPlugin)) { + $instantiatedCalendarPlugin = new $calendarPlugin(); + } else { + throw new \Exception("Sabre calendar-plugin class '$calendarPlugin' is unknown and could not be loaded"); + } + } + + if (!($instantiatedCalendarPlugin instanceof ICalendarProvider)) { + throw new \Exception("Sabre calendar-plugin class '$calendarPlugin' does not implement ICalendarProvider interface"); + } + + $this->calendarPlugins[] = $instantiatedCalendarPlugin; + } + } + } diff --git a/apps/dav/lib/CalDAV/CalendarHome.php b/apps/dav/lib/CalDAV/CalendarHome.php index b491c2304d2..ce29ed12db6 100644 --- a/apps/dav/lib/CalDAV/CalendarHome.php +++ b/apps/dav/lib/CalDAV/CalendarHome.php @@ -26,6 +26,9 @@ namespace OCA\DAV\CalDAV; +use OCA\DAV\AppInfo\PluginManager; +use OCA\DAV\CalDAV\Integration\ExternalCalendar; +use OCA\DAV\CalDAV\Integration\ICalendarProvider; use Sabre\CalDAV\Backend\BackendInterface; use Sabre\CalDAV\Backend\NotificationSupport; use Sabre\CalDAV\Backend\SchedulingSupport; @@ -44,6 +47,9 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { /** @var \OCP\IConfig */ private $config; + /** @var PluginManager */ + private $pluginManager; + /** @var bool */ private $returnCachedSubscriptions=false; @@ -51,6 +57,10 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { parent::__construct($caldavBackend, $principalInfo); $this->l10n = \OC::$server->getL10N('dav'); $this->config = \OC::$server->getConfig(); + $this->pluginManager = new PluginManager( + \OC::$server, + \OC::$server->getAppManager() + ); } /** @@ -66,7 +76,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { function createExtendedCollection($name, MkCol $mkCol) { $reservedNames = [BirthdayService::BIRTHDAY_CALENDAR_URI]; - if (in_array($name, $reservedNames)) { + if (\in_array($name, $reservedNames, true) || ExternalCalendar::doesViolateReservedName($name)) { throw new MethodNotAllowed('The resource you tried to create has a reserved name'); } @@ -104,6 +114,14 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { } } + foreach ($this->pluginManager->getCalendarPlugins() as $calendarPlugin) { + /** @var ICalendarProvider $calendarPlugin */ + $calendars = $calendarPlugin->fetchAllForCalendarHome($this->principalInfo['uri']); + foreach ($calendars as $calendar) { + $objects[] = $calendar; + } + } + return $objects; } @@ -139,7 +157,21 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { return new Subscription($this->caldavBackend, $subscription); } } + } + + if (ExternalCalendar::isAppGeneratedCalendar($name)) { + [$appId, $calendarUri] = ExternalCalendar::splitAppGeneratedCalendarUri($name); + foreach ($this->pluginManager->getCalendarPlugins() as $calendarPlugin) { + /** @var ICalendarProvider $calendarPlugin */ + if ($calendarPlugin->getAppId() !== $appId) { + continue; + } + + if ($calendarPlugin->hasCalendarInCalendarHome($this->principalInfo['uri'], $calendarUri)) { + return $calendarPlugin->getCalendarInCalendarHome($this->principalInfo['uri'], $calendarUri); + } + } } throw new NotFound('Node with name \'' . $name . '\' could not be found'); @@ -155,7 +187,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome { return $this->caldavBackend->calendarSearch($principalUri, $filters, $limit, $offset); } - + public function enableCachedSubscriptionsForThisRequest() { $this->returnCachedSubscriptions = true; } diff --git a/apps/dav/lib/CalDAV/Integration/ExternalCalendar.php b/apps/dav/lib/CalDAV/Integration/ExternalCalendar.php new file mode 100644 index 00000000000..88d43f0bb55 --- /dev/null +++ b/apps/dav/lib/CalDAV/Integration/ExternalCalendar.php @@ -0,0 +1,132 @@ +<?php +/** + * @copyright 2020, Georg Ehrke <oc.list@georgehrke.com> + * + * @author Georg Ehrke <oc.list@georgehrke.com> + * + * @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/>. + * + */ +namespace OCA\DAV\CalDAV\Integration; + +use Sabre\CalDAV; +use Sabre\DAV; + +/** + * Class ExternalCalendar + * + * @package OCA\DAV\CalDAV\Integration + * @since 19.0.0 + */ +abstract class ExternalCalendar implements CalDAV\ICalendar, DAV\IProperties { + + /** @var string */ + private const PREFIX = 'app-generated'; + + /** + * @var string + * + * Double dash is a valid delimiter, + * because it will always split the calendarURIs correctly: + * - our prefix contains only one dash and won't be split + * - appIds are not allowed to contain dashes as per spec: + * > must contain only lowercase ASCII characters and underscore + * - explode has a limit of three, so even if the app-generated + * calendar uri has double dashes, it won't be split + */ + private const DELIMITER = '--'; + + /** @var string */ + private $appId; + + /** @var string */ + private $calendarUri; + + /** + * ExternalCalendar constructor. + * + * @param string $appId + * @param string $calendarUri + */ + public function __construct(string $appId, string $calendarUri) { + $this->appId = $appId; + $this->calendarUri = $calendarUri; + } + + /** + * @inheritDoc + */ + final public function getName() { + return implode(self::DELIMITER, [ + self::PREFIX, + $this->appId, + $this->calendarUri, + ]); + } + + /** + * @inheritDoc + */ + final public function setName($name) { + throw new DAV\Exception\MethodNotAllowed('Renaming calendars is not yet supported'); + } + + /** + * @inheritDoc + */ + final public function createDirectory($name) { + throw new DAV\Exception\MethodNotAllowed('Creating collections in calendar objects is not allowed'); + + } + + /** + * Checks whether the calendar uri is app-generated + * + * @param string $calendarUri + * @return bool + */ + public static function isAppGeneratedCalendar(string $calendarUri):bool { + return strpos($calendarUri, self::PREFIX) === 0 && substr_count($calendarUri, self::DELIMITER) >= 2; + } + + /** + * Splits an app-generated calendar-uri into appId and calendarUri + * + * @param string $calendarUri + * @return array + */ + public static function splitAppGeneratedCalendarUri(string $calendarUri):array { + $array = array_slice(explode(self::DELIMITER, $calendarUri, 3), 1); + // Check the array has expected amount of elements + // and none of them is an empty string + if (\count($array) !== 2 || \in_array('', $array, true)) { + throw new \InvalidArgumentException('Provided calendar uri was not app-generated'); + } + + return $array; + } + + /** + * Checks whether a calendar-name, the user wants to create, violates + * the reserved name for calendar uris + * + * @param string $calendarUri + * @return bool + */ + public static function doesViolateReservedName(string $calendarUri):bool { + return strpos($calendarUri, self::PREFIX) === 0; + } +} diff --git a/apps/dav/lib/CalDAV/Integration/ICalendarProvider.php b/apps/dav/lib/CalDAV/Integration/ICalendarProvider.php new file mode 100644 index 00000000000..29f5f113ef5 --- /dev/null +++ b/apps/dav/lib/CalDAV/Integration/ICalendarProvider.php @@ -0,0 +1,73 @@ +<?php +/** + * @copyright 2020, Georg Ehrke <oc.list@georgehrke.com> + * + * @author Georg Ehrke <oc.list@georgehrke.com> + * + * @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/>. + * + */ +namespace OCA\DAV\CalDAV\Integration; + +use Sabre\CalDAV\ICalendar; + +/** + * Interface ICalendarProvider + * + * @package OCA\DAV\CalDAV\Integration + * @since 19.0.0 + */ +interface ICalendarProvider { + + /** + * Provides the appId of the plugin + * + * @since 19.0.0 + * @return string AppId + */ + public function getAppId(): string; + + /** + * Fetches all calendars for a given principal uri + * + * @since 19.0.0 + * @param string $principalUri E.g. principals/users/user1 + * @return ExternalCalendar[] Array of all calendars + */ + public function fetchAllForCalendarHome(string $principalUri): array; + + /** + * Checks whether plugin has a calendar for a given principalUri and calendarUri + * + * @since 19.0.0 + * @param string $principalUri E.g. principals/users/user1 + * @param string $calendarUri E.g. personal + * @return bool True if calendar for principalUri and calendarUri exists, false otherwise + */ + public function hasCalendarInCalendarHome(string $principalUri, string $calendarUri): bool; + + /** + * Fetches a calendar for a given principalUri and calendarUri + * Returns null if calendar does not exist + * + * @since 19.0.0 + * @param string $principalUri E.g. principals/users/user1 + * @param string $calendarUri E.g. personal + * @return ExternalCalendar|null Calendar if it exists, null otherwise + */ + public function getCalendarInCalendarHome(string $principalUri, string $calendarUri): ?ExternalCalendar; + +} |