aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/DAV
diff options
context:
space:
mode:
authorJoas Schilling <nickvergessen@gmx.de>2016-05-12 09:42:40 +0200
committerThomas Müller <DeepDiver1975@users.noreply.github.com>2016-05-12 09:42:40 +0200
commitdd9ee10bc0699e5dcc17c3ef2181dcda01d9a69b (patch)
tree2d464fcc20ea330481e56487287aeae462ce5917 /apps/dav/lib/DAV
parent4a3311f430ec6e45c62b2ebde2cae71e943f3c81 (diff)
downloadnextcloud-server-dd9ee10bc0699e5dcc17c3ef2181dcda01d9a69b.tar.gz
nextcloud-server-dd9ee10bc0699e5dcc17c3ef2181dcda01d9a69b.zip
Move dav app to PSR-4 (#24527)
* Move Application to correct namespace and PSR-4 it * Move dav app to PSR-4
Diffstat (limited to 'apps/dav/lib/DAV')
-rw-r--r--apps/dav/lib/DAV/GroupPrincipalBackend.php200
-rw-r--r--apps/dav/lib/DAV/Sharing/Backend.php206
-rw-r--r--apps/dav/lib/DAV/Sharing/IShareable.php74
-rw-r--r--apps/dav/lib/DAV/Sharing/Plugin.php200
-rw-r--r--apps/dav/lib/DAV/Sharing/Xml/Invite.php168
-rw-r--r--apps/dav/lib/DAV/Sharing/Xml/ShareRequest.php84
-rw-r--r--apps/dav/lib/DAV/SystemPrincipalBackend.php179
7 files changed, 1111 insertions, 0 deletions
diff --git a/apps/dav/lib/DAV/GroupPrincipalBackend.php b/apps/dav/lib/DAV/GroupPrincipalBackend.php
new file mode 100644
index 00000000000..e0568639784
--- /dev/null
+++ b/apps/dav/lib/DAV/GroupPrincipalBackend.php
@@ -0,0 +1,200 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\DAV;
+
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use Sabre\DAV\Exception;
+use \Sabre\DAV\PropPatch;
+use Sabre\DAVACL\PrincipalBackend\BackendInterface;
+
+class GroupPrincipalBackend implements BackendInterface {
+
+ const PRINCIPAL_PREFIX = 'principals/groups';
+
+ /** @var IGroupManager */
+ private $groupManager;
+
+ /**
+ * @param IGroupManager $IGroupManager
+ */
+ public function __construct(IGroupManager $IGroupManager) {
+ $this->groupManager = $IGroupManager;
+ }
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ *
+ * @param string $prefixPath
+ * @return string[]
+ */
+ public function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === self::PRINCIPAL_PREFIX) {
+ foreach($this->groupManager->search('') as $user) {
+ $principals[] = $this->groupToPrincipal($user);
+ }
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ public function getPrincipalByPath($path) {
+ $elements = explode('/', $path);
+ if ($elements[0] !== 'principals') {
+ return null;
+ }
+ if ($elements[1] !== 'groups') {
+ return null;
+ }
+ $name = $elements[2];
+ $group = $this->groupManager->get($name);
+
+ if (!is_null($group)) {
+ return $this->groupToPrincipal($group);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return string[]
+ * @throws Exception
+ */
+ public function getGroupMemberSet($principal) {
+ $elements = explode('/', $principal);
+ if ($elements[0] !== 'principals') {
+ return [];
+ }
+ if ($elements[1] !== 'groups') {
+ return [];
+ }
+ $name = $elements[2];
+ $group = $this->groupManager->get($name);
+
+ if (is_null($group)) {
+ return [];
+ }
+
+ return array_map(function($user) {
+ return $this->userToPrincipal($user);
+ }, $group->getUsers());
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ * @throws Exception
+ */
+ public function getGroupMembership($principal) {
+ return [];
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param string[] $members
+ * @throws Exception
+ */
+ public function setGroupMemberSet($principal, array $members) {
+ throw new Exception('Setting members of the group is not supported yet');
+ }
+
+ /**
+ * @param string $path
+ * @param PropPatch $propPatch
+ * @return int
+ */
+ function updatePrincipal($path, PropPatch $propPatch) {
+ return 0;
+ }
+
+ /**
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ return [];
+ }
+
+ /**
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+ return '';
+ }
+
+ /**
+ * @param IGroup $group
+ * @return array
+ */
+ protected function groupToPrincipal($group) {
+ $groupId = $group->getGID();
+ $principal = [
+ 'uri' => "principals/groups/$groupId",
+ '{DAV:}displayname' => $groupId,
+ ];
+
+ return $principal;
+ }
+
+ /**
+ * @param IUser $user
+ * @return array
+ */
+ protected function userToPrincipal($user) {
+ $principal = [
+ 'uri' => 'principals/users/' . $user->getUID(),
+ '{DAV:}displayname' => $user->getDisplayName(),
+ ];
+
+ return $principal;
+ }
+}
diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php
new file mode 100644
index 00000000000..225b773713d
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/Backend.php
@@ -0,0 +1,206 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\DAV\Sharing;
+
+use OCA\DAV\Connector\Sabre\Principal;
+use OCP\IDBConnection;
+
+class Backend {
+
+ /** @var IDBConnection */
+ private $db;
+ /** @var Principal */
+ private $principalBackend;
+ /** @var string */
+ private $resourceType;
+
+ const ACCESS_OWNER = 1;
+ const ACCESS_READ_WRITE = 2;
+ const ACCESS_READ = 3;
+
+ /**
+ * @param IDBConnection $db
+ * @param Principal $principalBackend
+ * @param string $resourceType
+ */
+ public function __construct(IDBConnection $db, Principal $principalBackend, $resourceType) {
+ $this->db = $db;
+ $this->principalBackend = $principalBackend;
+ $this->resourceType = $resourceType;
+ }
+
+ /**
+ * @param IShareable $shareable
+ * @param string[] $add
+ * @param string[] $remove
+ */
+ public function updateShares($shareable, $add, $remove) {
+ foreach($add as $element) {
+ $this->shareWith($shareable, $element);
+ }
+ foreach($remove as $element) {
+ $this->unshare($shareable, $element);
+ }
+ }
+
+ /**
+ * @param IShareable $shareable
+ * @param string $element
+ */
+ private function shareWith($shareable, $element) {
+ $user = $element['href'];
+ $parts = explode(':', $user, 2);
+ if ($parts[0] !== 'principal') {
+ return;
+ }
+
+ // don't share with owner
+ if ($shareable->getOwner() === $parts[1]) {
+ return;
+ }
+
+ // remove the share if it already exists
+ $this->unshare($shareable, $element['href']);
+ $access = self::ACCESS_READ;
+ if (isset($element['readOnly'])) {
+ $access = $element['readOnly'] ? self::ACCESS_READ : self::ACCESS_READ_WRITE;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->insert('dav_shares')
+ ->values([
+ 'principaluri' => $query->createNamedParameter($parts[1]),
+ 'type' => $query->createNamedParameter($this->resourceType),
+ 'access' => $query->createNamedParameter($access),
+ 'resourceid' => $query->createNamedParameter($shareable->getResourceId())
+ ]);
+ $query->execute();
+ }
+
+ /**
+ * @param $resourceId
+ */
+ public function deleteAllShares($resourceId) {
+ $query = $this->db->getQueryBuilder();
+ $query->delete('dav_shares')
+ ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
+ ->execute();
+ }
+
+ /**
+ * @param IShareable $shareable
+ * @param string $element
+ */
+ private function unshare($shareable, $element) {
+ $parts = explode(':', $element, 2);
+ if ($parts[0] !== 'principal') {
+ return;
+ }
+
+ // don't share with owner
+ if ($shareable->getOwner() === $parts[1]) {
+ return;
+ }
+
+ $query = $this->db->getQueryBuilder();
+ $query->delete('dav_shares')
+ ->where($query->expr()->eq('resourceid', $query->createNamedParameter($shareable->getResourceId())))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
+ ->andWhere($query->expr()->eq('principaluri', $query->createNamedParameter($parts[1])))
+ ;
+ $query->execute();
+ }
+
+ /**
+ * Returns the list of people whom this resource is shared with.
+ *
+ * Every element in this array should have the following properties:
+ * * href - Often a mailto: address
+ * * commonName - Optional, for example a first + last name
+ * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
+ * * readOnly - boolean
+ * * summary - Optional, a description for the share
+ *
+ * @param int $resourceId
+ * @return array
+ */
+ public function getShares($resourceId) {
+ $query = $this->db->getQueryBuilder();
+ $result = $query->select(['principaluri', 'access'])
+ ->from('dav_shares')
+ ->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
+ ->execute();
+
+ $shares = [];
+ while($row = $result->fetch()) {
+ $p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
+ $shares[]= [
+ 'href' => "principal:${row['principaluri']}",
+ 'commonName' => isset($p['{DAV:}displayname']) ? $p['{DAV:}displayname'] : '',
+ 'status' => 1,
+ 'readOnly' => ($row['access'] == self::ACCESS_READ),
+ '{http://owncloud.org/ns}principal' => $row['principaluri'],
+ '{http://owncloud.org/ns}group-share' => is_null($p)
+ ];
+ }
+
+ return $shares;
+ }
+
+ /**
+ * For shared resources the sharee is set in the ACL of the resource
+ *
+ * @param int $resourceId
+ * @param array $acl
+ * @return array
+ */
+ public function applyShareAcl($resourceId, $acl) {
+
+ $shares = $this->getShares($resourceId);
+ foreach ($shares as $share) {
+ $acl[] = [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
+ 'protected' => true,
+ ];
+ if (!$share['readOnly']) {
+ $acl[] = [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
+ 'protected' => true,
+ ];
+ } else if ($this->resourceType === 'calendar') {
+ // Allow changing the properties of read only calendars,
+ // so users can change the visibility.
+ $acl[] = [
+ 'privilege' => '{DAV:}write-properties',
+ 'principal' => $share['{' . \OCA\DAV\DAV\Sharing\Plugin::NS_OWNCLOUD . '}principal'],
+ 'protected' => true,
+ ];
+ }
+ }
+ return $acl;
+ }
+}
diff --git a/apps/dav/lib/DAV/Sharing/IShareable.php b/apps/dav/lib/DAV/Sharing/IShareable.php
new file mode 100644
index 00000000000..f6b6bfa8862
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/IShareable.php
@@ -0,0 +1,74 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\DAV\Sharing;
+use Sabre\DAV\INode;
+
+/**
+ * This interface represents a dav resource that can be shared with other users.
+ *
+ */
+interface IShareable extends INode {
+
+ /**
+ * Updates the list of shares.
+ *
+ * The first array is a list of people that are to be added to the
+ * resource.
+ *
+ * Every element in the add array has the following properties:
+ * * href - A url. Usually a mailto: address
+ * * commonName - Usually a first and last name, or false
+ * * summary - A description of the share, can also be false
+ * * readOnly - A boolean value
+ *
+ * Every element in the remove array is just the address string.
+ *
+ * @param array $add
+ * @param array $remove
+ * @return void
+ */
+ function updateShares(array $add, array $remove);
+
+ /**
+ * Returns the list of people whom this resource is shared with.
+ *
+ * Every element in this array should have the following properties:
+ * * href - Often a mailto: address
+ * * commonName - Optional, for example a first + last name
+ * * status - See the Sabre\CalDAV\SharingPlugin::STATUS_ constants.
+ * * readOnly - boolean
+ * * summary - Optional, a description for the share
+ *
+ * @return array
+ */
+ function getShares();
+
+ /**
+ * @return int
+ */
+ public function getResourceId();
+
+ /**
+ * @return string
+ */
+ public function getOwner();
+
+} \ No newline at end of file
diff --git a/apps/dav/lib/DAV/Sharing/Plugin.php b/apps/dav/lib/DAV/Sharing/Plugin.php
new file mode 100644
index 00000000000..3173d1397ba
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/Plugin.php
@@ -0,0 +1,200 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\DAV\Sharing;
+
+use OCA\DAV\Connector\Sabre\Auth;
+use OCA\DAV\DAV\Sharing\Xml\Invite;
+use OCP\IRequest;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\INode;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\Server;
+use Sabre\DAV\ServerPlugin;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+
+class Plugin extends ServerPlugin {
+
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ /** @var Auth */
+ private $auth;
+
+ /** @var IRequest */
+ private $request;
+
+ /**
+ * Plugin constructor.
+ *
+ * @param Auth $authBackEnd
+ * @param IRequest $request
+ */
+ public function __construct(Auth $authBackEnd, IRequest $request) {
+ $this->auth = $authBackEnd;
+ $this->request = $request;
+ }
+
+ /**
+ * Reference to SabreDAV server object.
+ *
+ * @var \Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * This method should return a list of server-features.
+ *
+ * This is for example 'versioning' and is added to the DAV: header
+ * in an OPTIONS response.
+ *
+ * @return string[]
+ */
+ function getFeatures() {
+ return ['oc-resource-sharing'];
+ }
+
+ /**
+ * Returns a plugin name.
+ *
+ * Using this name other plugins will be able to access other plugins
+ * using Sabre\DAV\Server::getPlugin
+ *
+ * @return string
+ */
+ function getPluginName() {
+ return 'oc-resource-sharing';
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param Server $server
+ * @return void
+ */
+ function initialize(Server $server) {
+ $this->server = $server;
+ $this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}share'] = 'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest';
+ $this->server->xml->elementMap['{' . Plugin::NS_OWNCLOUD . '}invite'] = 'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite';
+
+ $this->server->on('method:POST', [$this, 'httpPost']);
+ $this->server->on('propFind', [$this, 'propFind']);
+ }
+
+ /**
+ * We intercept this to handle POST requests on a dav resource.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return null|false
+ */
+ function httpPost(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+
+ // Only handling xml
+ $contentType = $request->getHeader('Content-Type');
+ if (strpos($contentType, 'application/xml') === false && strpos($contentType, 'text/xml') === false)
+ return;
+
+ // Making sure the node exists
+ try {
+ $node = $this->server->tree->getNodeForPath($path);
+ } catch (NotFound $e) {
+ return;
+ }
+
+ $requestBody = $request->getBodyAsString();
+
+ // If this request handler could not deal with this POST request, it
+ // will return 'null' and other plugins get a chance to handle the
+ // request.
+ //
+ // However, we already requested the full body. This is a problem,
+ // because a body can only be read once. This is why we preemptively
+ // re-populated the request body with the existing data.
+ $request->setBody($requestBody);
+
+ $message = $this->server->xml->parse($requestBody, $request->getUrl(), $documentType);
+
+ switch ($documentType) {
+
+ // Dealing with the 'share' document, which modified invitees on a
+ // calendar.
+ case '{' . self::NS_OWNCLOUD . '}share' :
+
+ // We can only deal with IShareableCalendar objects
+ if (!$node instanceof IShareable) {
+ return;
+ }
+
+ $this->server->transactionType = 'post-oc-resource-share';
+
+ // Getting ACL info
+ $acl = $this->server->getPlugin('acl');
+
+ // If there's no ACL support, we allow everything
+ if ($acl) {
+ /** @var \Sabre\DAVACL\Plugin $acl */
+ $acl->checkPrivileges($path, '{DAV:}write');
+ }
+
+ $node->updateShares($message->set, $message->remove);
+
+ $response->setStatus(200);
+ // Adding this because sending a response body may cause issues,
+ // and I wanted some type of indicator the response was handled.
+ $response->setHeader('X-Sabre-Status', 'everything-went-well');
+
+ // Breaking the event chain
+ return false;
+ }
+ }
+
+ /**
+ * This event is triggered when properties are requested for a certain
+ * node.
+ *
+ * This allows us to inject any properties early.
+ *
+ * @param PropFind $propFind
+ * @param INode $node
+ * @return void
+ */
+ function propFind(PropFind $propFind, INode $node) {
+ if ($node instanceof IShareable) {
+
+ $propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function() use ($node) {
+ return new Invite(
+ $node->getShares()
+ );
+ });
+
+ }
+ }
+
+}
diff --git a/apps/dav/lib/DAV/Sharing/Xml/Invite.php b/apps/dav/lib/DAV/Sharing/Xml/Invite.php
new file mode 100644
index 00000000000..ae40ff06701
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/Xml/Invite.php
@@ -0,0 +1,168 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\DAV\Sharing\Xml;
+
+use OCA\DAV\DAV\Sharing\Plugin;
+use Sabre\Xml\Writer;
+use Sabre\Xml\XmlSerializable;
+
+/**
+ * Invite property
+ *
+ * This property encodes the 'invite' property, as defined by
+ * the 'caldav-sharing-02' spec, in the http://calendarserver.org/ns/
+ * namespace.
+ *
+ * @see https://trac.calendarserver.org/browser/CalendarServer/trunk/doc/Extensions/caldav-sharing-02.txt
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Invite implements XmlSerializable {
+
+ /**
+ * The list of users a calendar has been shared to.
+ *
+ * @var array
+ */
+ protected $users;
+
+ /**
+ * The organizer contains information about the person who shared the
+ * object.
+ *
+ * @var array
+ */
+ protected $organizer;
+
+ /**
+ * Creates the property.
+ *
+ * Users is an array. Each element of the array has the following
+ * properties:
+ *
+ * * href - Often a mailto: address
+ * * commonName - Optional, for example a first and lastname for a user.
+ * * status - One of the SharingPlugin::STATUS_* constants.
+ * * readOnly - true or false
+ * * summary - Optional, description of the share
+ *
+ * The organizer key is optional to specify. It's only useful when a
+ * 'sharee' requests the sharing information.
+ *
+ * The organizer may have the following properties:
+ * * href - Often a mailto: address.
+ * * commonName - Optional human-readable name.
+ * * firstName - Optional first name.
+ * * lastName - Optional last name.
+ *
+ * If you wonder why these two structures are so different, I guess a
+ * valid answer is that the current spec is still a draft.
+ *
+ * @param array $users
+ */
+ function __construct(array $users, array $organizer = null) {
+
+ $this->users = $users;
+ $this->organizer = $organizer;
+
+ }
+
+ /**
+ * Returns the list of users, as it was passed to the constructor.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->users;
+
+ }
+
+ /**
+ * The xmlSerialize metod is called during xml writing.
+ *
+ * Use the $writer argument to write its own xml serialization.
+ *
+ * An important note: do _not_ create a parent element. Any element
+ * implementing XmlSerializble should only ever write what's considered
+ * its 'inner xml'.
+ *
+ * The parent of the current element is responsible for writing a
+ * containing element.
+ *
+ * This allows serializers to be re-used for different element names.
+ *
+ * If you are opening new elements, you must also close them again.
+ *
+ * @param Writer $writer
+ * @return void
+ */
+ function xmlSerialize(Writer $writer) {
+
+ $cs = '{' . Plugin::NS_OWNCLOUD . '}';
+
+ if (!is_null($this->organizer)) {
+
+ $writer->startElement($cs . 'organizer');
+ $writer->writeElement('{DAV:}href', $this->organizer['href']);
+
+ if (isset($this->organizer['commonName']) && $this->organizer['commonName']) {
+ $writer->writeElement($cs . 'common-name', $this->organizer['commonName']);
+ }
+ if (isset($this->organizer['firstName']) && $this->organizer['firstName']) {
+ $writer->writeElement($cs . 'first-name', $this->organizer['firstName']);
+ }
+ if (isset($this->organizer['lastName']) && $this->organizer['lastName']) {
+ $writer->writeElement($cs . 'last-name', $this->organizer['lastName']);
+ }
+ $writer->endElement(); // organizer
+
+ }
+
+ foreach ($this->users as $user) {
+
+ $writer->startElement($cs . 'user');
+ $writer->writeElement('{DAV:}href', $user['href']);
+ if (isset($user['commonName']) && $user['commonName']) {
+ $writer->writeElement($cs . 'common-name', $user['commonName']);
+ }
+ $writer->writeElement($cs . 'invite-accepted');
+
+ $writer->startElement($cs . 'access');
+ if ($user['readOnly']) {
+ $writer->writeElement($cs . 'read');
+ } else {
+ $writer->writeElement($cs . 'read-write');
+ }
+ $writer->endElement(); // access
+
+ if (isset($user['summary']) && $user['summary']) {
+ $writer->writeElement($cs . 'summary', $user['summary']);
+ }
+
+ $writer->endElement(); //user
+
+ }
+
+ }
+}
diff --git a/apps/dav/lib/DAV/Sharing/Xml/ShareRequest.php b/apps/dav/lib/DAV/Sharing/Xml/ShareRequest.php
new file mode 100644
index 00000000000..776fb446b6c
--- /dev/null
+++ b/apps/dav/lib/DAV/Sharing/Xml/ShareRequest.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+namespace OCA\DAV\DAV\Sharing\Xml;
+
+use OCA\DAV\DAV\Sharing\Plugin;
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+class ShareRequest implements XmlDeserializable {
+
+ public $set = [];
+
+ public $remove = [];
+
+ /**
+ * Constructor
+ *
+ * @param array $set
+ * @param array $remove
+ */
+ function __construct(array $set, array $remove) {
+
+ $this->set = $set;
+ $this->remove = $remove;
+
+ }
+
+ static function xmlDeserialize(Reader $reader) {
+
+ $elements = $reader->parseInnerTree([
+ '{' . Plugin::NS_OWNCLOUD. '}set' => 'Sabre\\Xml\\Element\\KeyValue',
+ '{' . Plugin::NS_OWNCLOUD . '}remove' => 'Sabre\\Xml\\Element\\KeyValue',
+ ]);
+
+ $set = [];
+ $remove = [];
+
+ foreach ($elements as $elem) {
+ switch ($elem['name']) {
+
+ case '{' . Plugin::NS_OWNCLOUD . '}set' :
+ $sharee = $elem['value'];
+
+ $sumElem = '{' . Plugin::NS_OWNCLOUD . '}summary';
+ $commonName = '{' . Plugin::NS_OWNCLOUD . '}common-name';
+
+ $set[] = [
+ 'href' => $sharee['{DAV:}href'],
+ 'commonName' => isset($sharee[$commonName]) ? $sharee[$commonName] : null,
+ 'summary' => isset($sharee[$sumElem]) ? $sharee[$sumElem] : null,
+ 'readOnly' => !array_key_exists('{' . Plugin::NS_OWNCLOUD . '}read-write', $sharee),
+ ];
+ break;
+
+ case '{' . Plugin::NS_OWNCLOUD . '}remove' :
+ $remove[] = $elem['value']['{DAV:}href'];
+ break;
+
+ }
+ }
+
+ return new self($set, $remove);
+
+ }
+
+}
diff --git a/apps/dav/lib/DAV/SystemPrincipalBackend.php b/apps/dav/lib/DAV/SystemPrincipalBackend.php
new file mode 100644
index 00000000000..8001ec4e6c6
--- /dev/null
+++ b/apps/dav/lib/DAV/SystemPrincipalBackend.php
@@ -0,0 +1,179 @@
+<?php
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * 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, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\DAV;
+
+use Sabre\DAVACL\PrincipalBackend\AbstractBackend;
+use Sabre\HTTP\URLUtil;
+
+class SystemPrincipalBackend extends AbstractBackend {
+
+ /**
+ * Returns a list of principals based on a prefix.
+ *
+ * This prefix will often contain something like 'principals'. You are only
+ * expected to return principals that are in this base path.
+ *
+ * You are expected to return at least a 'uri' for every user, you can
+ * return any additional properties if you wish so. Common properties are:
+ * {DAV:}displayname
+ * {http://sabredav.org/ns}email-address - This is a custom SabreDAV
+ * field that's actually injected in a number of other properties. If
+ * you have an email address, use this property.
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ function getPrincipalsByPrefix($prefixPath) {
+ $principals = [];
+
+ if ($prefixPath === 'principals/system') {
+ $principals[] = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ }
+
+ return $principals;
+ }
+
+ /**
+ * Returns a specific principal, specified by it's path.
+ * The returned structure should be the exact same as from
+ * getPrincipalsByPrefix.
+ *
+ * @param string $path
+ * @return array
+ */
+ function getPrincipalByPath($path) {
+
+ if ($path === 'principals/system/system') {
+ $principal = [
+ 'uri' => 'principals/system/system',
+ '{DAV:}displayname' => 'system',
+ ];
+ return $principal;
+ }
+
+ return null;
+ }
+
+ /**
+ * Updates one ore more webdav properties on a principal.
+ *
+ * The list of mutations is stored in a Sabre\DAV\PropPatch object.
+ * To do the actual updates, you must tell this object which properties
+ * you're going to process with the handle() method.
+ *
+ * Calling the handle method is like telling the PropPatch object "I
+ * promise I can handle updating this property".
+ *
+ * Read the PropPatch documentation for more info and examples.
+ *
+ * @param string $path
+ * @param \Sabre\DAV\PropPatch $propPatch
+ * @return void
+ */
+ function updatePrincipal($path, \Sabre\DAV\PropPatch $propPatch) {
+ }
+
+ /**
+ * This method is used to search for principals matching a set of
+ * properties.
+ *
+ * This search is specifically used by RFC3744's principal-property-search
+ * REPORT.
+ *
+ * The actual search should be a unicode-non-case-sensitive search. The
+ * keys in searchProperties are the WebDAV property names, while the values
+ * are the property values to search on.
+ *
+ * By default, if multiple properties are submitted to this method, the
+ * various properties should be combined with 'AND'. If $test is set to
+ * 'anyof', it should be combined using 'OR'.
+ *
+ * This method should simply return an array with full principal uri's.
+ *
+ * If somebody attempted to search on a property the backend does not
+ * support, you should simply return 0 results.
+ *
+ * You can also just return 0 results if you choose to not support
+ * searching at all, but keep in mind that this may stop certain features
+ * from working.
+ *
+ * @param string $prefixPath
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals($prefixPath, array $searchProperties, $test = 'allof') {
+ return [];
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMemberSet($principal) {
+ // TODO: for now the group principal has only one member, the user itself
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [$principal['uri']];
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMembership($principal) {
+ list($prefix, $name) = URLUtil::splitPath($principal);
+
+ if ($prefix === 'principals/system') {
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) {
+ throw new \Sabre\DAV\Exception('Principal not found');
+ }
+
+ return [];
+ }
+ return [];
+ }
+
+ /**
+ * Updates the list of group members for a group principal.
+ *
+ * The principals should be passed as a list of uri's.
+ *
+ * @param string $principal
+ * @param array $members
+ * @return void
+ */
+ function setGroupMemberSet($principal, array $members) {
+ throw new \Sabre\DAV\Exception('Setting members of the group is not supported yet');
+ }
+}