aboutsummaryrefslogtreecommitdiffstats
path: root/vendor/sabre/dav/lib/DAVACL
diff options
context:
space:
mode:
authorredmatrix <git@macgirvin.com>2016-05-10 17:26:44 -0700
committerredmatrix <git@macgirvin.com>2016-05-10 17:26:44 -0700
commit0b02a6d123b2014705998c94ddf3d460948d3eac (patch)
tree78ff2cab9944a4f5ab3f80ec93cbe1120de90bb2 /vendor/sabre/dav/lib/DAVACL
parent40b5b6e9d2da7ab65c8b4d38cdceac83a4d78deb (diff)
downloadvolse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.tar.gz
volse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.tar.bz2
volse-hubzilla-0b02a6d123b2014705998c94ddf3d460948d3eac.zip
initial sabre upgrade (needs lots of work - to wit: authentication, redo the browser interface, and rework event export/import)
Diffstat (limited to 'vendor/sabre/dav/lib/DAVACL')
-rw-r--r--vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php181
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php35
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php82
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php35
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php35
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php35
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/Collection.php153
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/File.php123
-rw-r--r--vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php188
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IACL.php75
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IPrincipal.php77
-rw-r--r--vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php62
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Plugin.php1323
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Principal.php288
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php53
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php141
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php30
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php431
-rw-r--r--vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php151
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php277
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php45
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php159
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php196
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php159
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php103
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php127
-rw-r--r--vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php58
27 files changed, 4622 insertions, 0 deletions
diff --git a/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php
new file mode 100644
index 000000000..460f78981
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/AbstractPrincipalCollection.php
@@ -0,0 +1,181 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+use Sabre\HTTP\URLUtil;
+
+/**
+ * Principals Collection
+ *
+ * This is a helper class that easily allows you to create a collection that
+ * has a childnode for every principal.
+ *
+ * To use this class, simply implement the getChildForPrincipal method.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractPrincipalCollection extends DAV\Collection implements IPrincipalCollection {
+
+ /**
+ * Principal backend
+ *
+ * @var PrincipalBackend\BackendInterface
+ */
+ protected $principalBackend;
+
+ /**
+ * The path to the principals we're listing from.
+ *
+ * @var string
+ */
+ protected $principalPrefix;
+
+ /**
+ * If this value is set to true, it effectively disables listing of users
+ * it still allows user to find other users if they have an exact url.
+ *
+ * @var bool
+ */
+ public $disableListing = false;
+
+ /**
+ * Creates the object
+ *
+ * This object must be passed the principal backend. This object will
+ * filter all principals from a specified prefix ($principalPrefix). The
+ * default is 'principals', if your principals are stored in a different
+ * collection, override $principalPrefix
+ *
+ *
+ * @param PrincipalBackend\BackendInterface $principalBackend
+ * @param string $principalPrefix
+ */
+ function __construct(PrincipalBackend\BackendInterface $principalBackend, $principalPrefix = 'principals') {
+
+ $this->principalPrefix = $principalPrefix;
+ $this->principalBackend = $principalBackend;
+
+ }
+
+ /**
+ * This method returns a node for a principal.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principalInfo
+ * @return IPrincipal
+ */
+ abstract function getChildForPrincipal(array $principalInfo);
+
+ /**
+ * Returns the name of this collection.
+ *
+ * @return string
+ */
+ function getName() {
+
+ list(, $name) = URLUtil::splitPath($this->principalPrefix);
+ return $name;
+
+ }
+
+ /**
+ * Return the list of users
+ *
+ * @return array
+ */
+ function getChildren() {
+
+ if ($this->disableListing)
+ throw new DAV\Exception\MethodNotAllowed('Listing members of this collection is disabled');
+
+ $children = [];
+ foreach ($this->principalBackend->getPrincipalsByPrefix($this->principalPrefix) as $principalInfo) {
+
+ $children[] = $this->getChildForPrincipal($principalInfo);
+
+
+ }
+ return $children;
+
+ }
+
+ /**
+ * Returns a child object, by its name.
+ *
+ * @param string $name
+ * @throws DAV\Exception\NotFound
+ * @return IPrincipal
+ */
+ function getChild($name) {
+
+ $principalInfo = $this->principalBackend->getPrincipalByPath($this->principalPrefix . '/' . $name);
+ if (!$principalInfo) throw new DAV\Exception\NotFound('Principal with name ' . $name . ' not found');
+ return $this->getChildForPrincipal($principalInfo);
+
+ }
+
+ /**
+ * 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. You should at least allow searching on
+ * http://sabredav.org/ns}email-address.
+ *
+ * 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 a list of 'child names', which may be
+ * used to call $this->getChild in the future.
+ *
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals(array $searchProperties, $test = 'allof') {
+
+ $result = $this->principalBackend->searchPrincipals($this->principalPrefix, $searchProperties, $test);
+ $r = [];
+
+ foreach ($result as $row) {
+ list(, $r[]) = URLUtil::splitPath($row);
+ }
+
+ return $r;
+
+ }
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @return string
+ */
+ function findByUri($uri) {
+
+ return $this->principalBackend->findByUri($uri, $this->principalPrefix);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php b/vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php
new file mode 100644
index 000000000..22450b4a6
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/AceConflict.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Sabre\DAVACL\Exception;
+
+use Sabre\DAV;
+
+/**
+ * This exception is thrown when a client attempts to set conflicting
+ * permissions.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AceConflict extends DAV\Exception\Conflict {
+
+ /**
+ * Adds in extra information in the xml response.
+ *
+ * This method adds the {DAV:}no-ace-conflict element as defined in rfc3744
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $doc = $errorNode->ownerDocument;
+
+ $np = $doc->createElementNS('DAV:', 'd:no-ace-conflict');
+ $errorNode->appendChild($np);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php b/vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php
new file mode 100644
index 000000000..5624fd22f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NeedPrivileges.php
@@ -0,0 +1,82 @@
+<?php
+
+namespace Sabre\DAVACL\Exception;
+
+use Sabre\DAV;
+
+/**
+ * NeedPrivileges
+ *
+ * The 403-need privileges is thrown when a user didn't have the appropriate
+ * permissions to perform an operation
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class NeedPrivileges extends DAV\Exception\Forbidden {
+
+ /**
+ * The relevant uri
+ *
+ * @var string
+ */
+ protected $uri;
+
+ /**
+ * The privileges the user didn't have.
+ *
+ * @var array
+ */
+ protected $privileges;
+
+ /**
+ * Constructor
+ *
+ * @param string $uri
+ * @param array $privileges
+ */
+ function __construct($uri, array $privileges) {
+
+ $this->uri = $uri;
+ $this->privileges = $privileges;
+
+ parent::__construct('User did not have the required privileges (' . implode(',', $privileges) . ') for path "' . $uri . '"');
+
+ }
+
+ /**
+ * Adds in extra information in the xml response.
+ *
+ * This method adds the {DAV:}need-privileges element as defined in rfc3744
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $doc = $errorNode->ownerDocument;
+
+ $np = $doc->createElementNS('DAV:', 'd:need-privileges');
+ $errorNode->appendChild($np);
+
+ foreach ($this->privileges as $privilege) {
+
+ $resource = $doc->createElementNS('DAV:', 'd:resource');
+ $np->appendChild($resource);
+
+ $resource->appendChild($doc->createElementNS('DAV:', 'd:href', $server->getBaseUri() . $this->uri));
+
+ $priv = $doc->createElementNS('DAV:', 'd:privilege');
+ $resource->appendChild($priv);
+
+ preg_match('/^{([^}]*)}(.*)$/', $privilege, $privilegeParts);
+ $priv->appendChild($doc->createElementNS($privilegeParts[1], 'd:' . $privilegeParts[2]));
+
+
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php b/vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php
new file mode 100644
index 000000000..a2363b174
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NoAbstract.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Sabre\DAVACL\Exception;
+
+use Sabre\DAV;
+
+/**
+ * This exception is thrown when a user tries to set a privilege that's marked
+ * as abstract.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class NoAbstract extends DAV\Exception\PreconditionFailed {
+
+ /**
+ * Adds in extra information in the xml response.
+ *
+ * This method adds the {DAV:}no-abstract element as defined in rfc3744
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $doc = $errorNode->ownerDocument;
+
+ $np = $doc->createElementNS('DAV:', 'd:no-abstract');
+ $errorNode->appendChild($np);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php b/vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php
new file mode 100644
index 000000000..4349bf101
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NotRecognizedPrincipal.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Sabre\DAVACL\Exception;
+
+use Sabre\DAV;
+
+/**
+ * If a client tried to set a privilege assigned to a non-existant principal,
+ * this exception will be thrown.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class NotRecognizedPrincipal extends DAV\Exception\PreconditionFailed {
+
+ /**
+ * Adds in extra information in the xml response.
+ *
+ * This method adds the {DAV:}recognized-principal element as defined in rfc3744
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $doc = $errorNode->ownerDocument;
+
+ $np = $doc->createElementNS('DAV:', 'd:recognized-principal');
+ $errorNode->appendChild($np);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php b/vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php
new file mode 100644
index 000000000..73b81190d
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Exception/NotSupportedPrivilege.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace Sabre\DAVACL\Exception;
+
+use Sabre\DAV;
+
+/**
+ * If a client tried to set a privilege that doesn't exist, this exception will
+ * be thrown.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class NotSupportedPrivilege extends DAV\Exception\PreconditionFailed {
+
+ /**
+ * Adds in extra information in the xml response.
+ *
+ * This method adds the {DAV:}not-supported-privilege element as defined in rfc3744
+ *
+ * @param DAV\Server $server
+ * @param \DOMElement $errorNode
+ * @return void
+ */
+ function serialize(DAV\Server $server, \DOMElement $errorNode) {
+
+ $doc = $errorNode->ownerDocument;
+
+ $np = $doc->createElementNS('DAV:', 'd:not-supported-privilege');
+ $errorNode->appendChild($np);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/Collection.php b/vendor/sabre/dav/lib/DAVACL/FS/Collection.php
new file mode 100644
index 000000000..5fab4768c
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/Collection.php
@@ -0,0 +1,153 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\FSExt\Directory as BaseCollection;
+use Sabre\DAVACL\IACL;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\Exception\NotFound;
+
+/**
+ * This is an ACL-enabled collection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Collection extends BaseCollection implements IACL {
+
+ /**
+ * A list of ACL rules.
+ *
+ * @var array
+ */
+ protected $acl;
+
+ /**
+ * Owner uri, or null for no owner.
+ *
+ * @var string|null
+ */
+ protected $owner;
+
+ /**
+ * Constructor
+ *
+ * @param string $path on-disk path.
+ * @param array $acl ACL rules.
+ * @param string|null $owner principal owner string.
+ */
+ function __construct($path, array $acl, $owner = null) {
+
+ parent::__construct($path);
+ $this->acl = $acl;
+ $this->owner = $owner;
+
+ }
+
+ /**
+ * Returns a specific child node, referenced by its name
+ *
+ * This method must throw Sabre\DAV\Exception\NotFound if the node does not
+ * exist.
+ *
+ * @param string $name
+ * @throws DAV\Exception\NotFound
+ * @return DAV\INode
+ */
+ function getChild($name) {
+
+ $path = $this->path . '/' . $name;
+
+ if (!file_exists($path)) throw new NotFound('File could not be located');
+ if ($name == '.' || $name == '..') throw new Forbidden('Permission denied to . and ..');
+
+ if (is_dir($path)) {
+
+ return new self($path, $this->acl, $this->owner);
+
+ } else {
+
+ return new File($path, $this->acl, $this->owner);
+
+ }
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->owner;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return $this->acl;
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/File.php b/vendor/sabre/dav/lib/DAVACL/FS/File.php
new file mode 100644
index 000000000..0d549528b
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/File.php
@@ -0,0 +1,123 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\FSExt\File as BaseFile;
+use Sabre\DAVACL\IACL;
+use Sabre\DAV\Exception\Forbidden;
+
+/**
+ * This is an ACL-enabled file node.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class File extends BaseFile implements IACL {
+
+ /**
+ * A list of ACL rules.
+ *
+ * @var array
+ */
+ protected $acl;
+
+ /**
+ * Owner uri, or null for no owner.
+ *
+ * @var string|null
+ */
+ protected $owner;
+
+ /**
+ * Constructor
+ *
+ * @param string $path on-disk path.
+ * @param array $acl ACL rules.
+ * @param string|null $owner principal owner string.
+ */
+ function __construct($path, array $acl, $owner = null) {
+
+ parent::__construct($path);
+ $this->acl = $acl;
+ $this->owner = $owner;
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->owner;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return $this->acl;
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php b/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php
new file mode 100644
index 000000000..c27616770
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/FS/HomeCollection.php
@@ -0,0 +1,188 @@
+<?php
+
+namespace Sabre\DAVACL\FS;
+
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAVACL\AbstractPrincipalCollection;
+use Sabre\DAVACL\IACL;
+use Sabre\DAVACL\PrincipalBackend\BackendInterface;
+use Sabre\Uri;
+
+/**
+ * This collection contains a collection for every principal.
+ * It is similar to /home on many unix systems.
+ *
+ * The per-user collections can only be accessed by the user who owns the
+ * collection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class HomeCollection extends AbstractPrincipalCollection implements IACL {
+
+ /**
+ * Name of this collection.
+ *
+ * @var string
+ */
+ public $collectionName = 'home';
+
+ /**
+ * Path to where the users' files are actually stored.
+ *
+ * @var string
+ */
+ protected $storagePath;
+
+ /**
+ * Creates the home collection.
+ *
+ * @param BackendInterface $principalBackend
+ * @param string $storagePath Where the actual files are stored.
+ * @param string $principalPrefix list of principals to iterate.
+ */
+ function __construct(BackendInterface $principalBackend, $storagePath, $principalPrefix = 'principals') {
+
+ parent::__construct($principalBackend, $principalPrefix);
+ $this->storagePath = $storagePath;
+
+ }
+
+ /**
+ * Returns the name of the node.
+ *
+ * This is used to generate the url.
+ *
+ * @return string
+ */
+ function getName() {
+
+ return $this->collectionName;
+
+ }
+
+ /**
+ * Returns a principals' collection of files.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principalInfo
+ * @return void
+ */
+ function getChildForPrincipal(array $principalInfo) {
+
+ $owner = $principalInfo['uri'];
+ $acl = [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => $owner,
+ 'protected' => true,
+ ],
+ [
+ 'privilege' => '{DAV:}write',
+ 'principal' => $owner,
+ 'protected' => true,
+ ],
+ ];
+
+ list(, $principalBaseName) = Uri\split($owner);
+
+ $path = $this->storagePath . '/' . $principalBaseName;
+
+ if (!is_dir($path)) {
+ mkdir($path, 0777, true);
+ }
+ return new Collection(
+ $path,
+ $acl,
+ $owner
+ );
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}read',
+ 'protected' => true,
+ ]
+ ];
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Setting ACL is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/IACL.php b/vendor/sabre/dav/lib/DAVACL/IACL.php
new file mode 100644
index 000000000..81908d08f
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/IACL.php
@@ -0,0 +1,75 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+
+/**
+ * ACL-enabled node
+ *
+ * If you want to add WebDAV ACL to a node, you must implement this class
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IACL extends DAV\INode {
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner();
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup();
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL();
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl);
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet();
+
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/IPrincipal.php b/vendor/sabre/dav/lib/DAVACL/IPrincipal.php
new file mode 100644
index 000000000..13728471e
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/IPrincipal.php
@@ -0,0 +1,77 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+
+/**
+ * IPrincipal interface
+ *
+ * Implement this interface to define your own principals
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IPrincipal extends DAV\INode {
+
+ /**
+ * Returns a list of alternative urls for a principal
+ *
+ * This can for example be an email address, or ldap url.
+ *
+ * @return array
+ */
+ function getAlternateUriSet();
+
+ /**
+ * Returns the full principal url
+ *
+ * @return string
+ */
+ function getPrincipalUrl();
+
+ /**
+ * Returns the list of group members
+ *
+ * If this principal is a group, this function should return
+ * all member principal uri's for the group.
+ *
+ * @return array
+ */
+ function getGroupMemberSet();
+
+ /**
+ * Returns the list of groups this principal is member of
+ *
+ * If this principal is a member of a (list of) groups, this function
+ * should return a list of principal uri's for it's members.
+ *
+ * @return array
+ */
+ function getGroupMembership();
+
+ /**
+ * Sets a list of group members
+ *
+ * If this principal is a group, this method sets all the group members.
+ * The list of members is always overwritten, never appended to.
+ *
+ * This method should throw an exception if the members could not be set.
+ *
+ * @param array $principals
+ * @return void
+ */
+ function setGroupMemberSet(array $principals);
+
+ /**
+ * Returns the displayname
+ *
+ * This should be a human readable name for the principal.
+ * If none is available, return the nodename.
+ *
+ * @return string
+ */
+ function getDisplayName();
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php
new file mode 100644
index 000000000..3ab8382fa
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/IPrincipalCollection.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+
+/**
+ * Principal Collection interface.
+ *
+ * Implement this interface to ensure that your principal collection can be
+ * searched using the principal-property-search REPORT.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface IPrincipalCollection extends DAV\ICollection {
+
+ /**
+ * 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. You should at least allow searching on
+ * http://sabredav.org/ns}email-address.
+ *
+ * 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 a list of 'child names', which may be
+ * used to call $this->getChild in the future.
+ *
+ * @param array $searchProperties
+ * @param string $test
+ * @return array
+ */
+ function searchPrincipals(array $searchProperties, $test = 'allof');
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @return string
+ */
+ function findByUri($uri);
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Plugin.php b/vendor/sabre/dav/lib/DAVACL/Plugin.php
new file mode 100644
index 000000000..601dffecc
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Plugin.php
@@ -0,0 +1,1323 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+use Sabre\DAV\INode;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\HTTP\RequestInterface;
+use Sabre\HTTP\ResponseInterface;
+use Sabre\Uri;
+
+/**
+ * SabreDAV ACL Plugin
+ *
+ * This plugin provides functionality to enforce ACL permissions.
+ * ACL is defined in RFC3744.
+ *
+ * In addition it also provides support for the {DAV:}current-user-principal
+ * property, defined in RFC5397 and the {DAV:}expand-property report, as
+ * defined in RFC3253.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Plugin extends DAV\ServerPlugin {
+
+ /**
+ * Recursion constants
+ *
+ * This only checks the base node
+ */
+ const R_PARENT = 1;
+
+ /**
+ * Recursion constants
+ *
+ * This checks every node in the tree
+ */
+ const R_RECURSIVE = 2;
+
+ /**
+ * Recursion constants
+ *
+ * This checks every parentnode in the tree, but not leaf-nodes.
+ */
+ const R_RECURSIVEPARENTS = 3;
+
+ /**
+ * Reference to server object.
+ *
+ * @var Sabre\DAV\Server
+ */
+ protected $server;
+
+ /**
+ * List of urls containing principal collections.
+ * Modify this if your principals are located elsewhere.
+ *
+ * @var array
+ */
+ public $principalCollectionSet = [
+ 'principals',
+ ];
+
+ /**
+ * By default ACL is only enforced for nodes that have ACL support (the
+ * ones that implement IACL). For any other node, access is
+ * always granted.
+ *
+ * To override this behaviour you can turn this setting off. This is useful
+ * if you plan to fully support ACL in the entire tree.
+ *
+ * @var bool
+ */
+ public $allowAccessToNodesWithoutACL = true;
+
+ /**
+ * By default nodes that are inaccessible by the user, can still be seen
+ * in directory listings (PROPFIND on parent with Depth: 1)
+ *
+ * In certain cases it's desirable to hide inaccessible nodes. Setting this
+ * to true will cause these nodes to be hidden from directory listings.
+ *
+ * @var bool
+ */
+ public $hideNodesFromListings = false;
+
+ /**
+ * This list of properties are the properties a client can search on using
+ * the {DAV:}principal-property-search report.
+ *
+ * The keys are the property names, values are descriptions.
+ *
+ * @var array
+ */
+ public $principalSearchPropertySet = [
+ '{DAV:}displayname' => 'Display name',
+ '{http://sabredav.org/ns}email-address' => 'Email address',
+ ];
+
+ /**
+ * Any principal uri's added here, will automatically be added to the list
+ * of ACL's. They will effectively receive {DAV:}all privileges, as a
+ * protected privilege.
+ *
+ * @var array
+ */
+ public $adminPrincipals = [];
+
+ /**
+ * Returns a list of features added by this plugin.
+ *
+ * This list is used in the response of a HTTP OPTIONS request.
+ *
+ * @return array
+ */
+ function getFeatures() {
+
+ return ['access-control', 'calendarserver-principal-property-search'];
+
+ }
+
+ /**
+ * Returns a list of available methods for a given url
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getMethods($uri) {
+
+ return ['ACL'];
+
+ }
+
+ /**
+ * 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 'acl';
+
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ * Note that you still need to subscribe to the 'report' event to actually
+ * implement them
+ *
+ * @param string $uri
+ * @return array
+ */
+ function getSupportedReportSet($uri) {
+
+ return [
+ '{DAV:}expand-property',
+ '{DAV:}principal-property-search',
+ '{DAV:}principal-search-property-set',
+ ];
+
+ }
+
+
+ /**
+ * Checks if the current user has the specified privilege(s).
+ *
+ * You can specify a single privilege, or a list of privileges.
+ * This method will throw an exception if the privilege is not available
+ * and return true otherwise.
+ *
+ * @param string $uri
+ * @param array|string $privileges
+ * @param int $recursion
+ * @param bool $throwExceptions if set to false, this method won't throw exceptions.
+ * @throws Sabre\DAVACL\Exception\NeedPrivileges
+ * @return bool
+ */
+ function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
+
+ if (!is_array($privileges)) $privileges = [$privileges];
+
+ $acl = $this->getCurrentUserPrivilegeSet($uri);
+
+ if (is_null($acl)) {
+ if ($this->allowAccessToNodesWithoutACL) {
+ return true;
+ } else {
+ if ($throwExceptions)
+ throw new Exception\NeedPrivileges($uri, $privileges);
+ else
+ return false;
+
+ }
+ }
+
+ $failed = [];
+ foreach ($privileges as $priv) {
+
+ if (!in_array($priv, $acl)) {
+ $failed[] = $priv;
+ }
+
+ }
+
+ if ($failed) {
+ if ($throwExceptions)
+ throw new Exception\NeedPrivileges($uri, $failed);
+ else
+ return false;
+ }
+ return true;
+
+ }
+
+ /**
+ * Returns the standard users' principal.
+ *
+ * This is one authorative principal url for the current user.
+ * This method will return null if the user wasn't logged in.
+ *
+ * @return string|null
+ */
+ function getCurrentUserPrincipal() {
+
+ $authPlugin = $this->server->getPlugin('auth');
+ if (is_null($authPlugin)) return null;
+ /** @var $authPlugin Sabre\DAV\Auth\Plugin */
+
+ return $authPlugin->getCurrentPrincipal();
+
+ }
+
+
+ /**
+ * Returns a list of principals that's associated to the current
+ * user, either directly or through group membership.
+ *
+ * @return array
+ */
+ function getCurrentUserPrincipals() {
+
+ $currentUser = $this->getCurrentUserPrincipal();
+
+ if (is_null($currentUser)) return [];
+
+ return array_merge(
+ [$currentUser],
+ $this->getPrincipalMembership($currentUser)
+ );
+
+ }
+
+ /**
+ * This array holds a cache for all the principals that are associated with
+ * a single principal.
+ *
+ * @var array
+ */
+ protected $principalMembershipCache = [];
+
+
+ /**
+ * Returns all the principal groups the specified principal is a member of.
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getPrincipalMembership($mainPrincipal) {
+
+ // First check our cache
+ if (isset($this->principalMembershipCache[$mainPrincipal])) {
+ return $this->principalMembershipCache[$mainPrincipal];
+ }
+
+ $check = [$mainPrincipal];
+ $principals = [];
+
+ while (count($check)) {
+
+ $principal = array_shift($check);
+
+ $node = $this->server->tree->getNodeForPath($principal);
+ if ($node instanceof IPrincipal) {
+ foreach ($node->getGroupMembership() as $groupMember) {
+
+ if (!in_array($groupMember, $principals)) {
+
+ $check[] = $groupMember;
+ $principals[] = $groupMember;
+
+ }
+
+ }
+
+ }
+
+ }
+
+ // Store the result in the cache
+ $this->principalMembershipCache[$mainPrincipal] = $principals;
+
+ return $principals;
+
+ }
+
+ /**
+ * Returns the supported privilege structure for this ACL plugin.
+ *
+ * See RFC3744 for more details. Currently we default on a simple,
+ * standard structure.
+ *
+ * You can either get the list of privileges by a uri (path) or by
+ * specifying a Node.
+ *
+ * @param string|INode $node
+ * @return array
+ */
+ function getSupportedPrivilegeSet($node) {
+
+ if (is_string($node)) {
+ $node = $this->server->tree->getNodeForPath($node);
+ }
+
+ if ($node instanceof IACL) {
+ $result = $node->getSupportedPrivilegeSet();
+
+ if ($result)
+ return $result;
+ }
+
+ return self::getDefaultSupportedPrivilegeSet();
+
+ }
+
+ /**
+ * Returns a fairly standard set of privileges, which may be useful for
+ * other systems to use as a basis.
+ *
+ * @return array
+ */
+ static function getDefaultSupportedPrivilegeSet() {
+
+ return [
+ 'privilege' => '{DAV:}all',
+ 'abstract' => true,
+ 'aggregates' => [
+ [
+ 'privilege' => '{DAV:}read',
+ 'aggregates' => [
+ [
+ 'privilege' => '{DAV:}read-acl',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}read-current-user-privilege-set',
+ 'abstract' => false,
+ ],
+ ],
+ ], // {DAV:}read
+ [
+ 'privilege' => '{DAV:}write',
+ 'aggregates' => [
+ [
+ 'privilege' => '{DAV:}write-acl',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}write-properties',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}write-content',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}bind',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}unbind',
+ 'abstract' => false,
+ ],
+ [
+ 'privilege' => '{DAV:}unlock',
+ 'abstract' => false,
+ ],
+ ],
+ ], // {DAV:}write
+ ],
+ ]; // {DAV:}all
+
+ }
+
+ /**
+ * Returns the supported privilege set as a flat list
+ *
+ * This is much easier to parse.
+ *
+ * The returned list will be index by privilege name.
+ * The value is a struct containing the following properties:
+ * - aggregates
+ * - abstract
+ * - concrete
+ *
+ * @param string|INode $node
+ * @return array
+ */
+ final function getFlatPrivilegeSet($node) {
+
+ $privs = $this->getSupportedPrivilegeSet($node);
+
+ $fpsTraverse = null;
+ $fpsTraverse = function($priv, $concrete, &$flat) use (&$fpsTraverse) {
+
+ $myPriv = [
+ 'privilege' => $priv['privilege'],
+ 'abstract' => isset($priv['abstract']) && $priv['abstract'],
+ 'aggregates' => [],
+ 'concrete' => isset($priv['abstract']) && $priv['abstract'] ? $concrete : $priv['privilege'],
+ ];
+
+ if (isset($priv['aggregates'])) {
+
+ foreach ($priv['aggregates'] as $subPriv) {
+
+ $myPriv['aggregates'][] = $subPriv['privilege'];
+
+ }
+
+ }
+
+ $flat[$priv['privilege']] = $myPriv;
+
+ if (isset($priv['aggregates'])) {
+
+ foreach ($priv['aggregates'] as $subPriv) {
+
+ $fpsTraverse($subPriv, $myPriv['concrete'], $flat);
+
+ }
+
+ }
+
+ };
+
+ $flat = [];
+ $fpsTraverse($privs, null, $flat);
+
+ return $flat;
+
+ }
+
+ /**
+ * Returns the full ACL list.
+ *
+ * Either a uri or a INode may be passed.
+ *
+ * null will be returned if the node doesn't support ACLs.
+ *
+ * @param string|DAV\INode $node
+ * @return array
+ */
+ function getACL($node) {
+
+ if (is_string($node)) {
+ $node = $this->server->tree->getNodeForPath($node);
+ }
+ if (!$node instanceof IACL) {
+ return null;
+ }
+ $acl = $node->getACL();
+ foreach ($this->adminPrincipals as $adminPrincipal) {
+ $acl[] = [
+ 'principal' => $adminPrincipal,
+ 'privilege' => '{DAV:}all',
+ 'protected' => true,
+ ];
+ }
+ return $acl;
+
+ }
+
+ /**
+ * Returns a list of privileges the current user has
+ * on a particular node.
+ *
+ * Either a uri or a DAV\INode may be passed.
+ *
+ * null will be returned if the node doesn't support ACLs.
+ *
+ * @param string|DAV\INode $node
+ * @return array
+ */
+ function getCurrentUserPrivilegeSet($node) {
+
+ if (is_string($node)) {
+ $node = $this->server->tree->getNodeForPath($node);
+ }
+
+ $acl = $this->getACL($node);
+
+ if (is_null($acl)) return null;
+
+ $principals = $this->getCurrentUserPrincipals();
+
+ $collected = [];
+
+ foreach ($acl as $ace) {
+
+ $principal = $ace['principal'];
+
+ switch ($principal) {
+
+ case '{DAV:}owner' :
+ $owner = $node->getOwner();
+ if ($owner && in_array($owner, $principals)) {
+ $collected[] = $ace;
+ }
+ break;
+
+
+ // 'all' matches for every user
+ case '{DAV:}all' :
+
+ // 'authenticated' matched for every user that's logged in.
+ // Since it's not possible to use ACL while not being logged
+ // in, this is also always true.
+ case '{DAV:}authenticated' :
+ $collected[] = $ace;
+ break;
+
+ // 'unauthenticated' can never occur either, so we simply
+ // ignore these.
+ case '{DAV:}unauthenticated' :
+ break;
+
+ default :
+ if (in_array($ace['principal'], $principals)) {
+ $collected[] = $ace;
+ }
+ break;
+
+ }
+
+
+ }
+
+ // Now we deduct all aggregated privileges.
+ $flat = $this->getFlatPrivilegeSet($node);
+
+ $collected2 = [];
+ while (count($collected)) {
+
+ $current = array_pop($collected);
+ $collected2[] = $current['privilege'];
+
+ foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) {
+ $collected2[] = $subPriv;
+ $collected[] = $flat[$subPriv];
+ }
+
+ }
+
+ return array_values(array_unique($collected2));
+
+ }
+
+
+ /**
+ * Returns a principal based on its uri.
+ *
+ * Returns null if the principal could not be found.
+ *
+ * @param string $uri
+ * @return null|string
+ */
+ function getPrincipalByUri($uri) {
+
+ $result = null;
+ $collections = $this->principalCollectionSet;
+ foreach ($collections as $collection) {
+
+ $principalCollection = $this->server->tree->getNodeForPath($collection);
+ if (!$principalCollection instanceof IPrincipalCollection) {
+ // Not a principal collection, we're simply going to ignore
+ // this.
+ continue;
+ }
+
+ $result = $principalCollection->findByUri($uri);
+ if ($result) {
+ return $result;
+ }
+
+ }
+
+ }
+
+ /**
+ * Principal property search
+ *
+ * This method can search for principals matching certain values in
+ * properties.
+ *
+ * This method will return a list of properties for the matched properties.
+ *
+ * @param array $searchProperties The properties to search on. This is a
+ * key-value list. The keys are property
+ * names, and the values the strings to
+ * match them on.
+ * @param array $requestedProperties This is the list of properties to
+ * return for every match.
+ * @param string $collectionUri The principal collection to search on.
+ * If this is ommitted, the standard
+ * principal collection-set will be used.
+ * @param string $test "allof" to use AND to search the
+ * properties. 'anyof' for OR.
+ * @return array This method returns an array structure similar to
+ * Sabre\DAV\Server::getPropertiesForPath. Returned
+ * properties are index by a HTTP status code.
+ */
+ function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') {
+
+ if (!is_null($collectionUri)) {
+ $uris = [$collectionUri];
+ } else {
+ $uris = $this->principalCollectionSet;
+ }
+
+ $lookupResults = [];
+ foreach ($uris as $uri) {
+
+ $principalCollection = $this->server->tree->getNodeForPath($uri);
+ if (!$principalCollection instanceof IPrincipalCollection) {
+ // Not a principal collection, we're simply going to ignore
+ // this.
+ continue;
+ }
+
+ $results = $principalCollection->searchPrincipals($searchProperties, $test);
+ foreach ($results as $result) {
+ $lookupResults[] = rtrim($uri, '/') . '/' . $result;
+ }
+
+ }
+
+ $matches = [];
+
+ foreach ($lookupResults as $lookupResult) {
+
+ list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0);
+
+ }
+
+ return $matches;
+
+ }
+
+ /**
+ * Sets up the plugin
+ *
+ * This method is automatically called by the server class.
+ *
+ * @param DAV\Server $server
+ * @return void
+ */
+ function initialize(DAV\Server $server) {
+
+ $this->server = $server;
+ $server->on('propFind', [$this, 'propFind'], 20);
+ $server->on('beforeMethod', [$this, 'beforeMethod'], 20);
+ $server->on('beforeBind', [$this, 'beforeBind'], 20);
+ $server->on('beforeUnbind', [$this, 'beforeUnbind'], 20);
+ $server->on('propPatch', [$this, 'propPatch']);
+ $server->on('beforeUnlock', [$this, 'beforeUnlock'], 20);
+ $server->on('report', [$this, 'report']);
+ $server->on('method:ACL', [$this, 'httpAcl']);
+ $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']);
+
+ array_push($server->protectedProperties,
+ '{DAV:}alternate-URI-set',
+ '{DAV:}principal-URL',
+ '{DAV:}group-membership',
+ '{DAV:}principal-collection-set',
+ '{DAV:}current-user-principal',
+ '{DAV:}supported-privilege-set',
+ '{DAV:}current-user-privilege-set',
+ '{DAV:}acl',
+ '{DAV:}acl-restrictions',
+ '{DAV:}inherited-acl-set',
+ '{DAV:}owner',
+ '{DAV:}group'
+ );
+
+ // Automatically mapping nodes implementing IPrincipal to the
+ // {DAV:}principal resourcetype.
+ $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal';
+
+ // Mapping the group-member-set property to the HrefList property
+ // class.
+ $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href';
+ $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl';
+ $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport';
+ $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport';
+ $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport';
+
+ }
+
+ /* {{{ Event handlers */
+
+ /**
+ * Triggered before any method is handled
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return void
+ */
+ function beforeMethod(RequestInterface $request, ResponseInterface $response) {
+
+ $method = $request->getMethod();
+ $path = $request->getPath();
+
+ $exists = $this->server->tree->nodeExists($path);
+
+ // If the node doesn't exists, none of these checks apply
+ if (!$exists) return;
+
+ switch ($method) {
+
+ case 'GET' :
+ case 'HEAD' :
+ case 'OPTIONS' :
+ // For these 3 we only need to know if the node is readable.
+ $this->checkPrivileges($path, '{DAV:}read');
+ break;
+
+ case 'PUT' :
+ case 'LOCK' :
+ case 'UNLOCK' :
+ // This method requires the write-content priv if the node
+ // already exists, and bind on the parent if the node is being
+ // created.
+ // The bind privilege is handled in the beforeBind event.
+ $this->checkPrivileges($path, '{DAV:}write-content');
+ break;
+
+
+ case 'PROPPATCH' :
+ $this->checkPrivileges($path, '{DAV:}write-properties');
+ break;
+
+ case 'ACL' :
+ $this->checkPrivileges($path, '{DAV:}write-acl');
+ break;
+
+ case 'COPY' :
+ case 'MOVE' :
+ // Copy requires read privileges on the entire source tree.
+ // If the target exists write-content normally needs to be
+ // checked, however, we're deleting the node beforehand and
+ // creating a new one after, so this is handled by the
+ // beforeUnbind event.
+ //
+ // The creation of the new node is handled by the beforeBind
+ // event.
+ //
+ // If MOVE is used beforeUnbind will also be used to check if
+ // the sourcenode can be deleted.
+ $this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE);
+
+ break;
+
+ }
+
+ }
+
+ /**
+ * Triggered before a new node is created.
+ *
+ * This allows us to check permissions for any operation that creates a
+ * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE.
+ *
+ * @param string $uri
+ * @return void
+ */
+ function beforeBind($uri) {
+
+ list($parentUri) = Uri\split($uri);
+ $this->checkPrivileges($parentUri, '{DAV:}bind');
+
+ }
+
+ /**
+ * Triggered before a node is deleted
+ *
+ * This allows us to check permissions for any operation that will delete
+ * an existing node.
+ *
+ * @param string $uri
+ * @return void
+ */
+ function beforeUnbind($uri) {
+
+ list($parentUri) = Uri\split($uri);
+ $this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS);
+
+ }
+
+ /**
+ * Triggered before a node is unlocked.
+ *
+ * @param string $uri
+ * @param DAV\Locks\LockInfo $lock
+ * @TODO: not yet implemented
+ * @return void
+ */
+ function beforeUnlock($uri, DAV\Locks\LockInfo $lock) {
+
+
+ }
+
+ /**
+ * Triggered before properties are looked up in specific nodes.
+ *
+ * @param DAV\PropFind $propFind
+ * @param DAV\INode $node
+ * @param array $requestedProperties
+ * @param array $returnedProperties
+ * @TODO really should be broken into multiple methods, or even a class.
+ * @return bool
+ */
+ function propFind(DAV\PropFind $propFind, DAV\INode $node) {
+
+ $path = $propFind->getPath();
+
+ // Checking the read permission
+ if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) {
+ // User is not allowed to read properties
+
+ // Returning false causes the property-fetching system to pretend
+ // that the node does not exist, and will cause it to be hidden
+ // from listings such as PROPFIND or the browser plugin.
+ if ($this->hideNodesFromListings) {
+ return false;
+ }
+
+ // Otherwise we simply mark every property as 403.
+ foreach ($propFind->getRequestedProperties() as $requestedProperty) {
+ $propFind->set($requestedProperty, null, 403);
+ }
+
+ return;
+
+ }
+
+ /* Adding principal properties */
+ if ($node instanceof IPrincipal) {
+
+ $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getAlternateUriSet());
+ });
+ $propFind->handle('{DAV:}principal-URL', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getPrincipalUrl() . '/');
+ });
+ $propFind->handle('{DAV:}group-member-set', function() use ($node) {
+ $members = $node->getGroupMemberSet();
+ foreach ($members as $k => $member) {
+ $members[$k] = rtrim($member, '/') . '/';
+ }
+ return new DAV\Xml\Property\Href($members);
+ });
+ $propFind->handle('{DAV:}group-membership', function() use ($node) {
+ $members = $node->getGroupMembership();
+ foreach ($members as $k => $member) {
+ $members[$k] = rtrim($member, '/') . '/';
+ }
+ return new DAV\Xml\Property\Href($members);
+ });
+ $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']);
+
+ }
+
+ $propFind->handle('{DAV:}principal-collection-set', function() {
+
+ $val = $this->principalCollectionSet;
+ // Ensuring all collections end with a slash
+ foreach ($val as $k => $v) $val[$k] = $v . '/';
+ return new DAV\Xml\Property\Href($val);
+
+ });
+ $propFind->handle('{DAV:}current-user-principal', function() {
+ if ($url = $this->getCurrentUserPrincipal()) {
+ return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/');
+ } else {
+ return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED);
+ }
+ });
+ $propFind->handle('{DAV:}supported-privilege-set', function() use ($node) {
+ return new Xml\Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node));
+ });
+ $propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) {
+ if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
+ $propFind->set('{DAV:}current-user-privilege-set', null, 403);
+ } else {
+ $val = $this->getCurrentUserPrivilegeSet($node);
+ if (!is_null($val)) {
+ return new Xml\Property\CurrentUserPrivilegeSet($val);
+ }
+ }
+ });
+ $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) {
+ /* The ACL property contains all the permissions */
+ if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) {
+ $propFind->set('{DAV:}acl', null, 403);
+ } else {
+ $acl = $this->getACL($node);
+ if (!is_null($acl)) {
+ return new Xml\Property\Acl($this->getACL($node));
+ }
+ }
+ });
+ $propFind->handle('{DAV:}acl-restrictions', function() {
+ return new Xml\Property\AclRestrictions();
+ });
+
+ /* Adding ACL properties */
+ if ($node instanceof IACL) {
+ $propFind->handle('{DAV:}owner', function() use ($node) {
+ return new DAV\Xml\Property\Href($node->getOwner() . '/');
+ });
+ }
+
+ }
+
+ /**
+ * This method intercepts PROPPATCH methods and make sure the
+ * group-member-set is updated correctly.
+ *
+ * @param string $path
+ * @param DAV\PropPatch $propPatch
+ * @return void
+ */
+ function propPatch($path, DAV\PropPatch $propPatch) {
+
+ $propPatch->handle('{DAV:}group-member-set', function($value) use ($path) {
+ if (is_null($value)) {
+ $memberSet = [];
+ } elseif ($value instanceof DAV\Xml\Property\Href) {
+ $memberSet = array_map(
+ [$this->server, 'calculateUri'],
+ $value->getHrefs()
+ );
+ } else {
+ throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null');
+ }
+ $node = $this->server->tree->getNodeForPath($path);
+ if (!($node instanceof IPrincipal)) {
+ // Fail
+ return false;
+ }
+
+ $node->setGroupMemberSet($memberSet);
+ // We must also clear our cache, just in case
+
+ $this->principalMembershipCache = [];
+
+ return true;
+ });
+
+ }
+
+ /**
+ * This method handles HTTP REPORT requests
+ *
+ * @param string $reportName
+ * @param mixed $report
+ * @param mixed $path
+ * @return bool
+ */
+ function report($reportName, $report, $path) {
+
+ switch ($reportName) {
+
+ case '{DAV:}principal-property-search' :
+ $this->server->transactionType = 'report-principal-property-search';
+ $this->principalPropertySearchReport($report);
+ return false;
+ case '{DAV:}principal-search-property-set' :
+ $this->server->transactionType = 'report-principal-search-property-set';
+ $this->principalSearchPropertySetReport($report);
+ return false;
+ case '{DAV:}expand-property' :
+ $this->server->transactionType = 'report-expand-property';
+ $this->expandPropertyReport($report);
+ return false;
+
+ }
+
+ }
+
+ /**
+ * This method is responsible for handling the 'ACL' event.
+ *
+ * @param RequestInterface $request
+ * @param ResponseInterface $response
+ * @return bool
+ */
+ function httpAcl(RequestInterface $request, ResponseInterface $response) {
+
+ $path = $request->getPath();
+ $body = $request->getBodyAsString();
+
+ if (!$body) {
+ throw new DAV\Exception\BadRequest('XML body expected in ACL request');
+ }
+
+ $acl = $this->server->xml->expect('{DAV:}acl', $body);
+ $newAcl = $acl->getPrivileges();
+
+ // Normalizing urls
+ foreach ($newAcl as $k => $newAce) {
+ $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
+ }
+ $node = $this->server->tree->getNodeForPath($path);
+
+ if (!$node instanceof IACL) {
+ throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
+ }
+
+ $oldAcl = $this->getACL($node);
+
+ $supportedPrivileges = $this->getFlatPrivilegeSet($node);
+
+ /* Checking if protected principals from the existing principal set are
+ not overwritten. */
+ foreach ($oldAcl as $oldAce) {
+
+ if (!isset($oldAce['protected']) || !$oldAce['protected']) continue;
+
+ $found = false;
+ foreach ($newAcl as $newAce) {
+ if (
+ $newAce['privilege'] === $oldAce['privilege'] &&
+ $newAce['principal'] === $oldAce['principal'] &&
+ $newAce['protected']
+ )
+ $found = true;
+ }
+
+ if (!$found)
+ throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
+
+ }
+
+ foreach ($newAcl as $newAce) {
+
+ // Do we recognize the privilege
+ if (!isset($supportedPrivileges[$newAce['privilege']])) {
+ throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
+ }
+
+ if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
+ throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege');
+ }
+
+ // Looking up the principal
+ try {
+ $principal = $this->server->tree->getNodeForPath($newAce['principal']);
+ } catch (DAV\Exception\NotFound $e) {
+ throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
+ }
+ if (!($principal instanceof IPrincipal)) {
+ throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
+ }
+
+ }
+ $node->setACL($newAcl);
+
+ $response->setStatus(200);
+
+ // Breaking the event chain, because we handled this method.
+ return false;
+
+ }
+
+ /* }}} */
+
+ /* Reports {{{ */
+
+ /**
+ * The expand-property report is defined in RFC3253 section 3-8.
+ *
+ * This report is very similar to a standard PROPFIND. The difference is
+ * that it has the additional ability to look at properties containing a
+ * {DAV:}href element, follow that property and grab additional elements
+ * there.
+ *
+ * Other rfc's, such as ACL rely on this report, so it made sense to put
+ * it in this plugin.
+ *
+ * @param Xml\Request\ExpandPropertyReport $report
+ * @return void
+ */
+ protected function expandPropertyReport($report) {
+
+ $depth = $this->server->getHTTPDepth(0);
+ $requestUri = $this->server->getRequestUri();
+
+ $result = $this->expandProperties($requestUri, $report->properties, $depth);
+
+ $xml = $this->server->xml->write(
+ '{DAV:}multistatus',
+ new DAV\Xml\Response\MultiStatus($result),
+ $this->server->getBaseUri()
+ );
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setBody($xml);
+
+ }
+
+ /**
+ * This method expands all the properties and returns
+ * a list with property values
+ *
+ * @param array $path
+ * @param array $requestedProperties the list of required properties
+ * @param int $depth
+ * @return array
+ */
+ protected function expandProperties($path, array $requestedProperties, $depth) {
+
+ $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth);
+
+ $result = [];
+
+ foreach ($foundProperties as $node) {
+
+ foreach ($requestedProperties as $propertyName => $childRequestedProperties) {
+
+ // We're only traversing if sub-properties were requested
+ if (count($childRequestedProperties) === 0) continue;
+
+ // We only have to do the expansion if the property was found
+ // and it contains an href element.
+ if (!array_key_exists($propertyName, $node[200])) continue;
+
+ if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) {
+ continue;
+ }
+
+ $childHrefs = $node[200][$propertyName]->getHrefs();
+ $childProps = [];
+
+ foreach ($childHrefs as $href) {
+ // Gathering the result of the children
+ $childProps[] = [
+ 'name' => '{DAV:}response',
+ 'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0]
+ ];
+ }
+
+ // Replacing the property with its expannded form.
+ $node[200][$propertyName] = $childProps;
+
+ }
+ $result[] = new DAV\Xml\Element\Response($node['href'], $node);
+
+ }
+
+ return $result;
+
+ }
+
+ /**
+ * principalSearchPropertySetReport
+ *
+ * This method responsible for handing the
+ * {DAV:}principal-search-property-set report. This report returns a list
+ * of properties the client may search on, using the
+ * {DAV:}principal-property-search report.
+ *
+ * @param Xml\Request\PrincipalSearchPropertySetReport $report
+ * @return void
+ */
+ protected function principalSearchPropertySetReport($report) {
+
+ $httpDepth = $this->server->getHTTPDepth(0);
+ if ($httpDepth !== 0) {
+ throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
+ }
+
+ $writer = $this->server->xml->getWriter();
+ $writer->openMemory();
+ $writer->startDocument();
+
+ $writer->startElement('{DAV:}principal-search-property-set');
+
+ foreach ($this->principalSearchPropertySet as $propertyName => $description) {
+
+ $writer->startElement('{DAV:}principal-search-property');
+ $writer->startElement('{DAV:}prop');
+
+ $writer->writeElement($propertyName);
+
+ $writer->endElement(); // prop
+
+ if ($description) {
+ $writer->write([[
+ 'name' => '{DAV:}description',
+ 'value' => $description,
+ 'attributes' => ['xml:lang' => 'en']
+ ]]);
+ }
+
+ $writer->endElement(); // principal-search-property
+
+
+ }
+
+ $writer->endElement(); // principal-search-property-set
+
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setStatus(200);
+ $this->server->httpResponse->setBody($writer->outputMemory());
+
+ }
+
+ /**
+ * principalPropertySearchReport
+ *
+ * This method is responsible for handing the
+ * {DAV:}principal-property-search report. This report can be used for
+ * clients to search for groups of principals, based on the value of one
+ * or more properties.
+ *
+ * @param Xml\Request\PrincipalPropertySearchReport $report
+ * @return void
+ */
+ protected function principalPropertySearchReport($report) {
+
+ $uri = null;
+ if (!$report->applyToPrincipalCollectionSet) {
+ $uri = $this->server->httpRequest->getPath();
+ }
+ if ($this->server->getHttpDepth('0') !== 0) {
+ throw new BadRequest('Depth must be 0');
+ }
+ $result = $this->principalSearch(
+ $report->searchProperties,
+ $report->properties,
+ $uri,
+ $report->test
+ );
+
+ $prefer = $this->server->getHTTPPrefer();
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
+ $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
+
+ }
+
+ /* }}} */
+
+ /**
+ * This method is used to generate HTML output for the
+ * DAV\Browser\Plugin. This allows us to generate an interface users
+ * can use to create new calendars.
+ *
+ * @param DAV\INode $node
+ * @param string $output
+ * @return bool
+ */
+ function htmlActionsPanel(DAV\INode $node, &$output) {
+
+ if (!$node instanceof PrincipalCollection)
+ return;
+
+ $output .= '<tr><td colspan="2"><form method="post" action="">
+ <h3>Create new principal</h3>
+ <input type="hidden" name="sabreAction" value="mkcol" />
+ <input type="hidden" name="resourceType" value="{DAV:}principal" />
+ <label>Name (uri):</label> <input type="text" name="name" /><br />
+ <label>Display name:</label> <input type="text" name="{DAV:}displayname" /><br />
+ <label>Email address:</label> <input type="text" name="{http://sabredav*DOT*org/ns}email-address" /><br />
+ <input type="submit" value="create" />
+ </form>
+ </td></tr>';
+
+ return false;
+
+ }
+
+ /**
+ * Returns a bunch of meta-data about the plugin.
+ *
+ * Providing this information is optional, and is mainly displayed by the
+ * Browser plugin.
+ *
+ * The description key in the returned array may contain html and will not
+ * be sanitized.
+ *
+ * @return array
+ */
+ function getPluginInfo() {
+
+ return [
+ 'name' => $this->getPluginName(),
+ 'description' => 'Adds support for WebDAV ACL (rfc3744)',
+ 'link' => 'http://sabre.io/dav/acl/',
+ ];
+
+ }
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Principal.php b/vendor/sabre/dav/lib/DAVACL/Principal.php
new file mode 100644
index 000000000..16375d3fc
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Principal.php
@@ -0,0 +1,288 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV;
+use Sabre\HTTP\URLUtil;
+
+/**
+ * Principal class
+ *
+ * This class is a representation of a simple principal
+ *
+ * Many WebDAV specs require a user to show up in the directory
+ * structure.
+ *
+ * This principal also has basic ACL settings, only allowing the principal
+ * access it's own principal.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Principal extends DAV\Node implements IPrincipal, DAV\IProperties, IACL {
+
+ /**
+ * Struct with principal information.
+ *
+ * @var array
+ */
+ protected $principalProperties;
+
+ /**
+ * Principal backend
+ *
+ * @var PrincipalBackend\BackendInterface
+ */
+ protected $principalBackend;
+
+ /**
+ * Creates the principal object
+ *
+ * @param IPrincipalBackend $principalBackend
+ * @param array $principalProperties
+ */
+ function __construct(PrincipalBackend\BackendInterface $principalBackend, array $principalProperties = []) {
+
+ if (!isset($principalProperties['uri'])) {
+ throw new DAV\Exception('The principal properties must at least contain the \'uri\' key');
+ }
+ $this->principalBackend = $principalBackend;
+ $this->principalProperties = $principalProperties;
+
+ }
+
+ /**
+ * Returns the full principal url
+ *
+ * @return string
+ */
+ function getPrincipalUrl() {
+
+ return $this->principalProperties['uri'];
+
+ }
+
+ /**
+ * Returns a list of alternative urls for a principal
+ *
+ * This can for example be an email address, or ldap url.
+ *
+ * @return array
+ */
+ function getAlternateUriSet() {
+
+ $uris = [];
+ if (isset($this->principalProperties['{DAV:}alternate-URI-set'])) {
+
+ $uris = $this->principalProperties['{DAV:}alternate-URI-set'];
+
+ }
+
+ if (isset($this->principalProperties['{http://sabredav.org/ns}email-address'])) {
+ $uris[] = 'mailto:' . $this->principalProperties['{http://sabredav.org/ns}email-address'];
+ }
+
+ return array_unique($uris);
+
+ }
+
+ /**
+ * Returns the list of group members
+ *
+ * If this principal is a group, this function should return
+ * all member principal uri's for the group.
+ *
+ * @return array
+ */
+ function getGroupMemberSet() {
+
+ return $this->principalBackend->getGroupMemberSet($this->principalProperties['uri']);
+
+ }
+
+ /**
+ * Returns the list of groups this principal is member of
+ *
+ * If this principal is a member of a (list of) groups, this function
+ * should return a list of principal uri's for it's members.
+ *
+ * @return array
+ */
+ function getGroupMembership() {
+
+ return $this->principalBackend->getGroupMemberShip($this->principalProperties['uri']);
+
+ }
+
+ /**
+ * Sets a list of group members
+ *
+ * If this principal is a group, this method sets all the group members.
+ * The list of members is always overwritten, never appended to.
+ *
+ * This method should throw an exception if the members could not be set.
+ *
+ * @param array $groupMembers
+ * @return void
+ */
+ function setGroupMemberSet(array $groupMembers) {
+
+ $this->principalBackend->setGroupMemberSet($this->principalProperties['uri'], $groupMembers);
+
+ }
+
+ /**
+ * Returns this principals name.
+ *
+ * @return string
+ */
+ function getName() {
+
+ $uri = $this->principalProperties['uri'];
+ list(, $name) = URLUtil::splitPath($uri);
+ return $name;
+
+ }
+
+ /**
+ * Returns the name of the user
+ *
+ * @return string
+ */
+ function getDisplayName() {
+
+ if (isset($this->principalProperties['{DAV:}displayname'])) {
+ return $this->principalProperties['{DAV:}displayname'];
+ } else {
+ return $this->getName();
+ }
+
+ }
+
+ /**
+ * Returns a list of properties
+ *
+ * @param array $requestedProperties
+ * @return array
+ */
+ function getProperties($requestedProperties) {
+
+ $newProperties = [];
+ foreach ($requestedProperties as $propName) {
+
+ if (isset($this->principalProperties[$propName])) {
+ $newProperties[$propName] = $this->principalProperties[$propName];
+ }
+
+ }
+
+ return $newProperties;
+
+ }
+
+ /**
+ * Updates properties on this node.
+ *
+ * This method received a PropPatch object, which contains all the
+ * information about the update.
+ *
+ * To update specific properties, call the 'handle' method on this object.
+ * Read the PropPatch documentation for more information.
+ *
+ * @param DAV\PropPatch $propPatch
+ * @return void
+ */
+ function propPatch(DAV\PropPatch $propPatch) {
+
+ return $this->principalBackend->updatePrincipal(
+ $this->principalProperties['uri'],
+ $propPatch
+ );
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+
+ return $this->principalProperties['uri'];
+
+
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+
+ return null;
+
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+
+ return [
+ [
+ 'privilege' => '{DAV:}read',
+ 'principal' => '{DAV:}authenticated',
+ 'protected' => true,
+ ],
+ ];
+
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new DAV\Exception\MethodNotAllowed('Updating ACLs is not allowed here');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php
new file mode 100644
index 000000000..9bf9ba445
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/AbstractBackend.php
@@ -0,0 +1,53 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+/**
+ * Abstract Principal Backend
+ *
+ * Currently this class has no function. It's here for consistency and so we
+ * have a non-bc-breaking way to add a default generic implementation to
+ * functions we may add in the future.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+abstract class AbstractBackend implements BackendInterface {
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+
+ // Note that the default implementation here is a bit slow and could
+ // likely be optimized.
+ if (substr($uri, 0, 7) !== 'mailto:') {
+ return;
+ }
+ $result = $this->searchPrincipals(
+ $principalPrefix,
+ ['{http://sabredav.org/ns}email-address' => substr($uri, 7)]
+ );
+
+ if ($result) {
+ return $result[0];
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php
new file mode 100644
index 000000000..2cb83071a
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/BackendInterface.php
@@ -0,0 +1,141 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+/**
+ * Implement this interface to create your own principal backends.
+ *
+ * Creating backends for principals is entirely optional. You can also
+ * implement Sabre\DAVACL\IPrincipal directly. This interface is used solely by
+ * Sabre\DAVACL\AbstractPrincipalCollection.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface BackendInterface {
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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 documenation 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');
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix);
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMemberSet($principal);
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMembership($principal);
+
+ /**
+ * 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);
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php
new file mode 100644
index 000000000..e354a697d
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/CreatePrincipalSupport.php
@@ -0,0 +1,30 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+use Sabre\DAV\MkCol;
+
+/**
+ * Implement this interface to add support for creating new principals to your
+ * principal backend.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+interface CreatePrincipalSupport extends BackendInterface {
+
+ /**
+ * Creates a new principal.
+ *
+ * This method receives a full path for the new principal. The mkCol object
+ * contains any additional webdav properties specified during the creation
+ * of the principal.
+ *
+ * @param string $path
+ * @param MkCol $mkCol
+ * @return void
+ */
+ function createPrincipal($path, MkCol $mkCol);
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php
new file mode 100644
index 000000000..01b7a175c
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalBackend/PDO.php
@@ -0,0 +1,431 @@
+<?php
+
+namespace Sabre\DAVACL\PrincipalBackend;
+
+use Sabre\DAV;
+use Sabre\DAV\MkCol;
+use Sabre\HTTP\URLUtil;
+
+/**
+ * PDO principal backend
+ *
+ *
+ * This backend assumes all principals are in a single collection. The default collection
+ * is 'principals/', but this can be overriden.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PDO extends AbstractBackend implements CreatePrincipalSupport {
+
+ /**
+ * PDO table name for 'principals'
+ *
+ * @var string
+ */
+ public $tableName = 'principals';
+
+ /**
+ * PDO table name for 'group members'
+ *
+ * @var string
+ */
+ public $groupMembersTableName = 'groupmembers';
+
+ /**
+ * pdo
+ *
+ * @var PDO
+ */
+ protected $pdo;
+
+ /**
+ * A list of additional fields to support
+ *
+ * @var array
+ */
+ protected $fieldMap = [
+
+ /**
+ * This property can be used to display the users' real name.
+ */
+ '{DAV:}displayname' => [
+ 'dbField' => 'displayname',
+ ],
+
+ /**
+ * This is the users' primary email-address.
+ */
+ '{http://sabredav.org/ns}email-address' => [
+ 'dbField' => 'email',
+ ],
+ ];
+
+ /**
+ * Sets up the backend.
+ *
+ * @param PDO $pdo
+ */
+ function __construct(\PDO $pdo) {
+
+ $this->pdo = $pdo;
+
+ }
+
+ /**
+ * 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 actualy injected in a number of other properties. If
+ * you have an email address, use this property.
+ *
+ * @param string $prefixPath
+ * @return array
+ */
+ function getPrincipalsByPrefix($prefixPath) {
+
+ $fields = [
+ 'uri',
+ ];
+
+ foreach ($this->fieldMap as $key => $value) {
+ $fields[] = $value['dbField'];
+ }
+ $result = $this->pdo->query('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName);
+
+ $principals = [];
+
+ while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
+
+ // Checking if the principal is in the prefix
+ list($rowPrefix) = URLUtil::splitPath($row['uri']);
+ if ($rowPrefix !== $prefixPath) continue;
+
+ $principal = [
+ 'uri' => $row['uri'],
+ ];
+ foreach ($this->fieldMap as $key => $value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ $principals[] = $principal;
+
+ }
+
+ 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) {
+
+ $fields = [
+ 'id',
+ 'uri',
+ ];
+
+ foreach ($this->fieldMap as $key => $value) {
+ $fields[] = $value['dbField'];
+ }
+ $stmt = $this->pdo->prepare('SELECT ' . implode(',', $fields) . ' FROM ' . $this->tableName . ' WHERE uri = ?');
+ $stmt->execute([$path]);
+
+ $row = $stmt->fetch(\PDO::FETCH_ASSOC);
+ if (!$row) return;
+
+ $principal = [
+ 'id' => $row['id'],
+ 'uri' => $row['uri'],
+ ];
+ foreach ($this->fieldMap as $key => $value) {
+ if ($row[$value['dbField']]) {
+ $principal[$key] = $row[$value['dbField']];
+ }
+ }
+ return $principal;
+
+ }
+
+ /**
+ * 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 documenation for more info and examples.
+ *
+ * @param string $path
+ * @param DAV\PropPatch $propPatch
+ */
+ function updatePrincipal($path, DAV\PropPatch $propPatch) {
+
+ $propPatch->handle(array_keys($this->fieldMap), function($properties) use ($path) {
+
+ $query = "UPDATE " . $this->tableName . " SET ";
+ $first = true;
+
+ $values = [];
+
+ foreach ($properties as $key => $value) {
+
+ $dbField = $this->fieldMap[$key]['dbField'];
+
+ if (!$first) {
+ $query .= ', ';
+ }
+ $first = false;
+ $query .= $dbField . ' = :' . $dbField;
+ $values[$dbField] = $value;
+
+ }
+
+ $query .= " WHERE uri = :uri";
+ $values['uri'] = $path;
+
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ return true;
+
+ });
+
+ }
+
+ /**
+ * 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') {
+ if (count($searchProperties) == 0) return []; //No criteria
+
+ $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE ';
+ $values = [];
+ foreach ($searchProperties as $property => $value) {
+ switch ($property) {
+ case '{DAV:}displayname' :
+ $column = "displayname";
+ break;
+ case '{http://sabredav.org/ns}email-address' :
+ $column = "email";
+ break;
+ default :
+ // Unsupported property
+ return [];
+ }
+ if (count($values) > 0) $query .= (strcmp($test, "anyof") == 0 ? " OR " : " AND ");
+ $query .= 'lower(' . $column . ') LIKE lower(?)';
+ $values[] = '%' . $value . '%';
+
+ }
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute($values);
+
+ $principals = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+
+ // Checking if the principal is in the prefix
+ list($rowPrefix) = URLUtil::splitPath($row['uri']);
+ if ($rowPrefix !== $prefixPath) continue;
+
+ $principals[] = $row['uri'];
+
+ }
+
+ return $principals;
+
+ }
+
+ /**
+ * Finds a principal by its URI.
+ *
+ * This method may receive any type of uri, but mailto: addresses will be
+ * the most common.
+ *
+ * Implementation of this API is optional. It is currently used by the
+ * CalDAV system to find principals based on their email addresses. If this
+ * API is not implemented, some features may not work correctly.
+ *
+ * This method must return a relative principal path, or null, if the
+ * principal was not found or you refuse to find it.
+ *
+ * @param string $uri
+ * @param string $principalPrefix
+ * @return string
+ */
+ function findByUri($uri, $principalPrefix) {
+ $value = null;
+ $scheme = null;
+ list($scheme, $value) = explode(":", $uri, 2);
+ if ($value == null) return null;
+
+ $uri = null;
+ switch ($scheme){
+ case "mailto":
+ $query = 'SELECT uri FROM ' . $this->tableName . ' WHERE lower(email)=lower(?)';
+ $stmt = $this->pdo->prepare($query);
+ $stmt->execute([ $value ]);
+
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ // Checking if the principal is in the prefix
+ list($rowPrefix) = URLUtil::splitPath($row['uri']);
+ if ($rowPrefix !== $principalPrefix) continue;
+
+ $uri = $row['uri'];
+ break; //Stop on first match
+ }
+ break;
+ default:
+ //unsupported uri scheme
+ return null;
+ }
+ return $uri;
+ }
+
+ /**
+ * Returns the list of members for a group-principal
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMemberSet($principal) {
+
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) throw new DAV\Exception('Principal not found');
+
+ $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.member_id = principals.id WHERE groupmembers.principal_id = ?');
+ $stmt->execute([$principal['id']]);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $result[] = $row['uri'];
+ }
+ return $result;
+
+ }
+
+ /**
+ * Returns the list of groups a principal is a member of
+ *
+ * @param string $principal
+ * @return array
+ */
+ function getGroupMembership($principal) {
+
+ $principal = $this->getPrincipalByPath($principal);
+ if (!$principal) throw new DAV\Exception('Principal not found');
+
+ $stmt = $this->pdo->prepare('SELECT principals.uri as uri FROM ' . $this->groupMembersTableName . ' AS groupmembers LEFT JOIN ' . $this->tableName . ' AS principals ON groupmembers.principal_id = principals.id WHERE groupmembers.member_id = ?');
+ $stmt->execute([$principal['id']]);
+
+ $result = [];
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ $result[] = $row['uri'];
+ }
+ return $result;
+
+ }
+
+ /**
+ * 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) {
+
+ // Grabbing the list of principal id's.
+ $stmt = $this->pdo->prepare('SELECT id, uri FROM ' . $this->tableName . ' WHERE uri IN (? ' . str_repeat(', ? ', count($members)) . ');');
+ $stmt->execute(array_merge([$principal], $members));
+
+ $memberIds = [];
+ $principalId = null;
+
+ while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) {
+ if ($row['uri'] == $principal) {
+ $principalId = $row['id'];
+ } else {
+ $memberIds[] = $row['id'];
+ }
+ }
+ if (!$principalId) throw new DAV\Exception('Principal not found');
+
+ // Wiping out old members
+ $stmt = $this->pdo->prepare('DELETE FROM ' . $this->groupMembersTableName . ' WHERE principal_id = ?;');
+ $stmt->execute([$principalId]);
+
+ foreach ($memberIds as $memberId) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->groupMembersTableName . ' (principal_id, member_id) VALUES (?, ?);');
+ $stmt->execute([$principalId, $memberId]);
+
+ }
+
+ }
+
+ /**
+ * Creates a new principal.
+ *
+ * This method receives a full path for the new principal. The mkCol object
+ * contains any additional webdav properties specified during the creation
+ * of the principal.
+ *
+ * @param string $path
+ * @param MkCol $mkCol
+ * @return void
+ */
+ function createPrincipal($path, MkCol $mkCol) {
+
+ $stmt = $this->pdo->prepare('INSERT INTO ' . $this->tableName . ' (uri) VALUES (?)');
+ $stmt->execute([$path]);
+ $this->updatePrincipal($path, $mkCol);
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php b/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php
new file mode 100644
index 000000000..54911e7b5
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/PrincipalCollection.php
@@ -0,0 +1,151 @@
+<?php
+
+namespace Sabre\DAVACL;
+
+use Sabre\DAV\Exception\InvalidResourceType;
+use Sabre\DAV\Exception\Forbidden;
+use Sabre\DAV\IExtendedCollection;
+use Sabre\DAV\MkCol;
+
+/**
+ * Principals Collection
+ *
+ * This collection represents a list of users.
+ * The users are instances of Sabre\DAVACL\Principal
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalCollection extends AbstractPrincipalCollection implements IExtendedCollection, IACL {
+
+ /**
+ * This method returns a node for a principal.
+ *
+ * The passed array contains principal information, and is guaranteed to
+ * at least contain a uri item. Other properties may or may not be
+ * supplied by the authentication backend.
+ *
+ * @param array $principal
+ * @return \Sabre\DAV\INode
+ */
+ function getChildForPrincipal(array $principal) {
+
+ return new Principal($this->principalBackend, $principal);
+
+ }
+
+ /**
+ * Creates a new collection.
+ *
+ * This method will receive a MkCol object with all the information about
+ * the new collection that's being created.
+ *
+ * The MkCol object contains information about the resourceType of the new
+ * collection. If you don't support the specified resourceType, you should
+ * throw Exception\InvalidResourceType.
+ *
+ * The object also contains a list of WebDAV properties for the new
+ * collection.
+ *
+ * You should call the handle() method on this object to specify exactly
+ * which properties you are storing. This allows the system to figure out
+ * exactly which properties you didn't store, which in turn allows other
+ * plugins (such as the propertystorage plugin) to handle storing the
+ * property for you.
+ *
+ * @param string $name
+ * @param MkCol $mkCol
+ * @throws Exception\InvalidResourceType
+ * @return void
+ */
+ function createExtendedCollection($name, MkCol $mkCol) {
+
+ if (!$mkCol->hasResourceType('{DAV:}principal')) {
+ throw new InvalidResourceType('Only resources of type {DAV:}principal may be created here');
+ }
+
+ $this->principalBackend->createPrincipal(
+ $this->principalPrefix . '/' . $name,
+ $mkCol
+ );
+
+ }
+
+ /**
+ * Returns the owner principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getOwner() {
+ return null;
+ }
+
+ /**
+ * Returns a group principal
+ *
+ * This must be a url to a principal, or null if there's no owner
+ *
+ * @return string|null
+ */
+ function getGroup() {
+ return null;
+ }
+
+ /**
+ * Returns a list of ACE's for this node.
+ *
+ * Each ACE has the following properties:
+ * * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
+ * currently the only supported privileges
+ * * 'principal', a url to the principal who owns the node
+ * * 'protected' (optional), indicating that this ACE is not allowed to
+ * be updated.
+ *
+ * @return array
+ */
+ function getACL() {
+ return [
+ [
+ 'principal' => '{DAV:}authenticated',
+ 'privilege' => '{DAV:}read',
+ 'protected' => true,
+ ],
+ ];
+ }
+
+ /**
+ * Updates the ACL
+ *
+ * This method will receive a list of new ACE's as an array argument.
+ *
+ * @param array $acl
+ * @return void
+ */
+ function setACL(array $acl) {
+
+ throw new Forbidden('Updating ACLs is not allowed on this node');
+
+ }
+
+ /**
+ * Returns the list of supported privileges for this node.
+ *
+ * The returned data structure is a list of nested privileges.
+ * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
+ * standard structure.
+ *
+ * If null is returned from this method, the default privilege set is used,
+ * which is fine for most common usecases.
+ *
+ * @return array|null
+ */
+ function getSupportedPrivilegeSet() {
+
+ return null;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php
new file mode 100644
index 000000000..9f5e40df1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Acl.php
@@ -0,0 +1,277 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * This class represents the {DAV:}acl property.
+ *
+ * The {DAV:}acl property is a full list of access control entries for a
+ * resource.
+ *
+ * {DAV:}acl is used as a WebDAV property, but it is also used within the body
+ * of the ACL request.
+ *
+ * See:
+ * http://tools.ietf.org/html/rfc3744#section-5.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Acl implements Element, HtmlOutput {
+
+ /**
+ * List of privileges
+ *
+ * @var array
+ */
+ protected $privileges;
+
+ /**
+ * Whether or not the server base url is required to be prefixed when
+ * serializing the property.
+ *
+ * @var bool
+ */
+ protected $prefixBaseUrl;
+
+ /**
+ * Constructor
+ *
+ * This object requires a structure similar to the return value from
+ * Sabre\DAVACL\Plugin::getACL().
+ *
+ * Each privilege is a an array with at least a 'privilege' property, and a
+ * 'principal' property. A privilege may have a 'protected' property as
+ * well.
+ *
+ * The prefixBaseUrl should be set to false, if the supplied principal urls
+ * are already full urls. If this is kept to true, the servers base url
+ * will automatically be prefixed.
+ *
+ * @param array $privileges
+ * @param bool $prefixBaseUrl
+ */
+ function __construct(array $privileges, $prefixBaseUrl = true) {
+
+ $this->privileges = $privileges;
+ $this->prefixBaseUrl = $prefixBaseUrl;
+
+ }
+
+ /**
+ * Returns the list of privileges for this property
+ *
+ * @return array
+ */
+ function getPrivileges() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * 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) {
+
+ foreach ($this->privileges as $ace) {
+
+ $this->serializeAce($writer, $ace);
+
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ ob_start();
+ echo "<table>";
+ echo "<tr><th>Principal</th><th>Privilege</th><th></th></tr>";
+ foreach ($this->privileges as $privilege) {
+
+ echo '<tr>';
+ // if it starts with a {, it's a special principal
+ if ($privilege['principal'][0] === '{') {
+ echo '<td>', $html->xmlName($privilege['principal']), '</td>';
+ } else {
+ echo '<td>', $html->link($privilege['principal']), '</td>';
+ }
+ echo '<td>', $html->xmlName($privilege['privilege']), '</td>';
+ echo '<td>';
+ if (!empty($privilege['protected'])) echo '(protected)';
+ echo '</td>';
+ echo '</tr>';
+
+ }
+ echo "</table>";
+ return ob_get_clean();
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elementMap = [
+ '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue',
+ '{DAV:}privilege' => 'Sabre\Xml\Element\Elements',
+ '{DAV:}principal' => 'Sabre\DAVACL\Xml\Property\Principal',
+ ];
+
+ $privileges = [];
+
+ foreach ((array)$reader->parseInnerTree($elementMap) as $element) {
+
+ if ($element['name'] !== '{DAV:}ace') {
+ continue;
+ }
+ $ace = $element['value'];
+
+ if (empty($ace['{DAV:}principal'])) {
+ throw new DAV\Exception\BadRequest('Each {DAV:}ace element must have one {DAV:}principal element');
+ }
+ $principal = $ace['{DAV:}principal'];
+
+ switch ($principal->getType()) {
+ case Principal::HREF :
+ $principal = $principal->getHref();
+ break;
+ case Principal::AUTHENTICATED :
+ $principal = '{DAV:}authenticated';
+ break;
+ case Principal::UNAUTHENTICATED :
+ $principal = '{DAV:}unauthenticated';
+ break;
+ case Principal::ALL :
+ $principal = '{DAV:}all';
+ break;
+
+ }
+
+ $protected = array_key_exists('{DAV:}protected', $ace);
+
+ if (!isset($ace['{DAV:}grant'])) {
+ throw new DAV\Exception\NotImplemented('Every {DAV:}ace element must have a {DAV:}grant element. {DAV:}deny is not yet supported');
+ }
+ foreach ($ace['{DAV:}grant'] as $elem) {
+ if ($elem['name'] !== '{DAV:}privilege') {
+ continue;
+ }
+
+ foreach ($elem['value'] as $priv) {
+ $privileges[] = [
+ 'principal' => $principal,
+ 'protected' => $protected,
+ 'privilege' => $priv,
+ ];
+ }
+
+ }
+
+ }
+
+ return new self($privileges);
+
+ }
+
+ /**
+ * Serializes a single access control entry.
+ *
+ * @param Writer $writer
+ * @param array $ace
+ * @return void
+ */
+ private function serializeAce(Writer $writer, array $ace) {
+
+ $writer->startElement('{DAV:}ace');
+
+ switch ($ace['principal']) {
+ case '{DAV:}authenticated' :
+ $principal = new Principal(Principal::AUTHENTICATED);
+ break;
+ case '{DAV:}unauthenticated' :
+ $principal = new Principal(Principal::UNAUTHENTICATED);
+ break;
+ case '{DAV:}all' :
+ $principal = new Principal(Principal::ALL);
+ break;
+ default:
+ $principal = new Principal(Principal::HREF, $ace['principal']);
+ break;
+ }
+
+ $writer->writeElement('{DAV:}principal', $principal);
+ $writer->startElement('{DAV:}grant');
+ $writer->startElement('{DAV:}privilege');
+
+ $writer->writeElement($ace['privilege']);
+
+ $writer->endElement(); // privilege
+ $writer->endElement(); // grant
+
+ if (!empty($ace['protected'])) {
+ $writer->writeElement('{DAV:}protected');
+ }
+
+ $writer->endElement(); // ace
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php
new file mode 100644
index 000000000..f669cc5e1
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/AclRestrictions.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+
+/**
+ * AclRestrictions property
+ *
+ * This property represents {DAV:}acl-restrictions, as defined in RFC3744.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class AclRestrictions implements XmlSerializable {
+
+ /**
+ * 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) {
+
+ $writer->writeElement('{DAV:}grant-only');
+ $writer->writeElement('{DAV:}no-invert');
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php
new file mode 100644
index 000000000..0a95eb2b7
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\Element;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * CurrentUserPrivilegeSet
+ *
+ * This class represents the current-user-privilege-set property. When
+ * requested, it contain all the privileges a user has on a specific node.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class CurrentUserPrivilegeSet implements Element, HtmlOutput {
+
+ /**
+ * List of privileges
+ *
+ * @var array
+ */
+ private $privileges;
+
+ /**
+ * Creates the object
+ *
+ * Pass the privileges in clark-notation
+ *
+ * @param array $privileges
+ */
+ function __construct(array $privileges) {
+
+ $this->privileges = $privileges;
+
+ }
+
+ /**
+ * 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) {
+
+ foreach ($this->privileges as $privName) {
+
+ $writer->startElement('{DAV:}privilege');
+ $writer->writeElement($privName);
+ $writer->endElement();
+
+ }
+
+
+ }
+
+ /**
+ * Returns true or false, whether the specified principal appears in the
+ * list.
+ *
+ * @param string $privilegeName
+ * @return bool
+ */
+ function has($privilegeName) {
+
+ return in_array($privilegeName, $this->privileges);
+
+ }
+
+ /**
+ * Returns the list of privileges.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $result = [];
+
+ $tree = $reader->parseInnerTree(['{DAV:}privilege' => 'Sabre\\Xml\\Element\\Elements']);
+ foreach ($tree as $element) {
+ if ($element['name'] !== '{DAV:}privilege') {
+ continue;
+ }
+ $result[] = $element['value'][0];
+ }
+ return new self($result);
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ return implode(
+ ', ',
+ array_map([$html, 'xmlName'], $this->getValue())
+ );
+
+ }
+
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php
new file mode 100644
index 000000000..d32249d8b
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/Principal.php
@@ -0,0 +1,196 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\Xml\Reader;
+use Sabre\Xml\Writer;
+
+/**
+ * Principal property
+ *
+ * The principal property represents a principal from RFC3744 (ACL).
+ * The property can be used to specify a principal or pseudo principals.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class Principal extends DAV\Xml\Property\Href {
+
+ /**
+ * To specify a not-logged-in user, use the UNAUTHENTICATED principal
+ */
+ const UNAUTHENTICATED = 1;
+
+ /**
+ * To specify any principal that is logged in, use AUTHENTICATED
+ */
+ const AUTHENTICATED = 2;
+
+ /**
+ * Specific principals can be specified with the HREF
+ */
+ const HREF = 3;
+
+ /**
+ * Everybody, basically
+ */
+ const ALL = 4;
+
+ /**
+ * Principal-type
+ *
+ * Must be one of the UNAUTHENTICATED, AUTHENTICATED or HREF constants.
+ *
+ * @var int
+ */
+ protected $type;
+
+ /**
+ * Creates the property.
+ *
+ * The 'type' argument must be one of the type constants defined in this class.
+ *
+ * 'href' is only required for the HREF type.
+ *
+ * @param int $type
+ * @param string|null $href
+ */
+ function __construct($type, $href = null) {
+
+ $this->type = $type;
+ if ($type === self::HREF && is_null($href)) {
+ throw new DAV\Exception('The href argument must be specified for the HREF principal type.');
+ }
+ if ($href) {
+ $href = rtrim($href, '/') . '/';
+ parent::__construct($href);
+ }
+
+ }
+
+ /**
+ * Returns the principal type
+ *
+ * @return int
+ */
+ function getType() {
+
+ return $this->type;
+
+ }
+
+
+ /**
+ * 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) {
+
+ switch ($this->type) {
+
+ case self::UNAUTHENTICATED :
+ $writer->writeElement('{DAV:}unauthenticated');
+ break;
+ case self::AUTHENTICATED :
+ $writer->writeElement('{DAV:}authenticated');
+ break;
+ case self::HREF :
+ parent::xmlSerialize($writer);
+ break;
+ case self::ALL :
+ $writer->writeElement('{DAV:}all');
+ break;
+ }
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ switch ($this->type) {
+
+ case self::UNAUTHENTICATED :
+ return '<em>unauthenticated</em>';
+ case self::AUTHENTICATED :
+ return '<em>authenticated</em>';
+ case self::HREF :
+ return parent::toHtml($html);
+ case self::ALL :
+ return '<em>all</em>';
+ }
+
+ }
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called staticly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * Important note 2: You are responsible for advancing the reader to the
+ * next element. Not doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $tree = $reader->parseInnerTree()[0];
+
+ switch ($tree['name']) {
+ case '{DAV:}unauthenticated' :
+ return new self(self::UNAUTHENTICATED);
+ case '{DAV:}authenticated' :
+ return new self(self::AUTHENTICATED);
+ case '{DAV:}href':
+ return new self(self::HREF, $tree['value']);
+ case '{DAV:}all':
+ return new self(self::ALL);
+ default :
+ throw new BadRequest('Unknown or unsupported principal type: ' . $tree['name']);
+ }
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php b/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php
new file mode 100644
index 000000000..572bed4dd
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Property/SupportedPrivilegeSet.php
@@ -0,0 +1,159 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Property;
+
+use Sabre\DAV\Browser\HtmlOutput;
+use Sabre\DAV\Browser\HtmlOutputHelper;
+use Sabre\Xml\XmlSerializable;
+use Sabre\Xml\Writer;
+
+/**
+ * SupportedPrivilegeSet property
+ *
+ * This property encodes the {DAV:}supported-privilege-set property, as defined
+ * in rfc3744. Please consult the rfc for details about it's structure.
+ *
+ * This class expects a structure like the one given from
+ * Sabre\DAVACL\Plugin::getSupportedPrivilegeSet as the argument in its
+ * constructor.
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class SupportedPrivilegeSet implements XmlSerializable, HtmlOutput {
+
+ /**
+ * privileges
+ *
+ * @var array
+ */
+ protected $privileges;
+
+ /**
+ * Constructor
+ *
+ * @param array $privileges
+ */
+ function __construct(array $privileges) {
+
+ $this->privileges = $privileges;
+
+ }
+
+ /**
+ * Returns the privilege value.
+ *
+ * @return array
+ */
+ function getValue() {
+
+ return $this->privileges;
+
+ }
+
+ /**
+ * 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) {
+
+ $this->serializePriv($writer, $this->privileges);
+
+ }
+
+ /**
+ * Generate html representation for this value.
+ *
+ * The html output is 100% trusted, and no effort is being made to sanitize
+ * it. It's up to the implementor to sanitize user provided values.
+ *
+ * The output must be in UTF-8.
+ *
+ * The baseUri parameter is a url to the root of the application, and can
+ * be used to construct local links.
+ *
+ * @param HtmlOutputHelper $html
+ * @return string
+ */
+ function toHtml(HtmlOutputHelper $html) {
+
+ $traverse = function($priv) use (&$traverse, $html) {
+ echo "<li>";
+ echo $html->xmlName($priv['privilege']);
+ if (isset($priv['abstract']) && $priv['abstract']) {
+ echo " <i>(abstract)</i>";
+ }
+ if (isset($priv['description'])) {
+ echo " " . $html->h($priv['description']);
+ }
+ if (isset($priv['aggregates'])) {
+ echo "\n<ul>\n";
+ foreach ($priv['aggregates'] as $subPriv) {
+ $traverse($subPriv);
+ }
+ echo "</ul>";
+ }
+ echo "</li>\n";
+ };
+
+ ob_start();
+ echo "<ul class=\"tree\">";
+ $traverse($this->getValue());
+ echo "</ul>\n";
+
+ return ob_get_clean();
+
+ }
+
+
+
+ /**
+ * Serializes a property
+ *
+ * This is a recursive function.
+ *
+ * @param Writer $writer
+ * @param array $privilege
+ * @return void
+ */
+ private function serializePriv(Writer $writer, $privilege) {
+
+ $writer->startElement('{DAV:}supported-privilege');
+
+ $writer->startElement('{DAV:}privilege');
+ $writer->writeElement($privilege['privilege']);
+ $writer->endElement(); // privilege
+
+ if (!empty($privilege['abstract'])) {
+ $writer->writeElement('{DAV:}abstract');
+ }
+ if (!empty($privilege['description'])) {
+ $writer->writeElement('{DAV:}description', $privilege['description']);
+ }
+ if (isset($privilege['aggregates'])) {
+ foreach ($privilege['aggregates'] as $subPrivilege) {
+ $this->serializePriv($writer, $subPrivilege);
+ }
+ }
+
+ $writer->endElement(); // supported-privilege
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php
new file mode 100644
index 000000000..3f535e301
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/ExpandPropertyReport.php
@@ -0,0 +1,103 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+
+/**
+ * ExpandProperty request parser.
+ *
+ * This class parses the {DAV:}expand-property REPORT, as defined in:
+ *
+ * http://tools.ietf.org/html/rfc3253#section-3.8
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class ExpandPropertyReport implements XmlDeserializable {
+
+ /**
+ * An array with requested properties.
+ *
+ * The requested properties will be used as keys in this array. The value
+ * is normally null.
+ *
+ * If the value is an array though, it means the property must be expanded.
+ * Within the array, the sub-properties, which themselves may be null or
+ * arrays.
+ *
+ * @var array
+ */
+ public $properties;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $elems = $reader->parseInnerTree();
+
+ $obj = new self();
+ $obj->properties = self::traverse($elems);
+
+ return $obj;
+
+ }
+
+ /**
+ * This method is used by deserializeXml, to recursively parse the
+ * {DAV:}property elements.
+ *
+ * @param array $elems
+ * @return void
+ */
+ private static function traverse($elems) {
+
+ $result = [];
+
+ foreach ($elems as $elem) {
+
+ if ($elem['name'] !== '{DAV:}property') {
+ continue;
+ }
+
+ $namespace = isset($elem['attributes']['namespace']) ?
+ $elem['attributes']['namespace'] :
+ 'DAV:';
+
+ $propName = '{' . $namespace . '}' . $elem['attributes']['name'];
+
+ $value = null;
+ if (is_array($elem['value'])) {
+ $value = self::traverse($elem['value']);
+ }
+
+ $result[$propName] = $value;
+
+ }
+
+ return $result;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php
new file mode 100644
index 000000000..1e7aa4481
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php
@@ -0,0 +1,127 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+
+/**
+ * PrincipalSearchPropertySetReport request parser.
+ *
+ * This class parses the {DAV:}principal-property-search REPORT, as defined
+ * in:
+ *
+ * https://tools.ietf.org/html/rfc3744#section-9.4
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalPropertySearchReport implements XmlDeserializable {
+
+ /**
+ * The requested properties.
+ *
+ * @var array|null
+ */
+ public $properties;
+
+ /**
+ * searchProperties
+ *
+ * @var array
+ */
+ public $searchProperties = [];
+
+ /**
+ * By default the property search will be conducted on the url of the http
+ * request. If this is set to true, it will be applied to the principal
+ * collection set instead.
+ *
+ * @var bool
+ */
+ public $applyToPrincipalCollectionSet = false;
+
+ /**
+ * Search for principals matching ANY of the properties (OR) or a ALL of
+ * the properties (AND).
+ *
+ * This property is either "anyof" or "allof".
+ *
+ * @var string
+ */
+ public $test;
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ $self = new self();
+
+ $foundSearchProp = false;
+ $self->test = 'allof';
+ if ($reader->getAttribute('test') === 'anyof') {
+ $self->test = 'anyof';
+ }
+
+ $elemMap = [
+ '{DAV:}property-search' => 'Sabre\\Xml\\Element\\KeyValue',
+ '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue',
+ ];
+
+ foreach ($reader->parseInnerTree($elemMap) as $elem) {
+
+ switch ($elem['name']) {
+
+ case '{DAV:}prop' :
+ $self->properties = array_keys($elem['value']);
+ break;
+ case '{DAV:}property-search' :
+ $foundSearchProp = true;
+ // This property has two sub-elements:
+ // {DAV:}prop - The property to be searched on. This may
+ // also be more than one
+ // {DAV:}match - The value to match with
+ if (!isset($elem['value']['{DAV:}prop']) || !isset($elem['value']['{DAV:}match'])) {
+ throw new BadRequest('The {DAV:}property-search element must contain one {DAV:}match and one {DAV:}prop element');
+ }
+ foreach ($elem['value']['{DAV:}prop'] as $propName => $discard) {
+ $self->searchProperties[$propName] = $elem['value']['{DAV:}match'];
+ }
+ break;
+ case '{DAV:}apply-to-principal-collection-set' :
+ $self->applyToPrincipalCollectionSet = true;
+ break;
+
+ }
+
+ }
+ if (!$foundSearchProp) {
+ throw new BadRequest('The {DAV:}principal-property-search report must contain at least 1 {DAV:}property-search element');
+ }
+
+ return $self;
+
+ }
+
+}
diff --git a/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php
new file mode 100644
index 000000000..ade157b19
--- /dev/null
+++ b/vendor/sabre/dav/lib/DAVACL/Xml/Request/PrincipalSearchPropertySetReport.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace Sabre\DAVACL\Xml\Request;
+
+use Sabre\Xml\Reader;
+use Sabre\Xml\XmlDeserializable;
+use Sabre\DAV\Exception\BadRequest;
+
+/**
+ * PrincipalSearchPropertySetReport request parser.
+ *
+ * This class parses the {DAV:}principal-search-property-set REPORT, as defined
+ * in:
+ *
+ * https://tools.ietf.org/html/rfc3744#section-9.5
+ *
+ * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
+ * @author Evert Pot (http://evertpot.com/)
+ * @license http://sabre.io/license/ Modified BSD License
+ */
+class PrincipalSearchPropertySetReport implements XmlDeserializable {
+
+ /**
+ * The deserialize method is called during xml parsing.
+ *
+ * This method is called statictly, this is because in theory this method
+ * may be used as a type of constructor, or factory method.
+ *
+ * Often you want to return an instance of the current class, but you are
+ * free to return other data as well.
+ *
+ * You are responsible for advancing the reader to the next element. Not
+ * doing anything will result in a never-ending loop.
+ *
+ * If you just want to skip parsing for this element altogether, you can
+ * just call $reader->next();
+ *
+ * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
+ * the next element.
+ *
+ * @param Reader $reader
+ * @return mixed
+ */
+ static function xmlDeserialize(Reader $reader) {
+
+ if (!$reader->isEmptyElement) {
+ throw new BadRequest('The {DAV:}principal-search-property-set element must be empty');
+ }
+
+ // The element is actually empty, so there's not much to do.
+ $reader->next();
+
+ $self = new self();
+ return $self;
+
+ }
+
+}