diff options
Diffstat (limited to 'vendor/sabre/dav/lib/DAV/PropertyStorage')
3 files changed, 477 insertions, 0 deletions
diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php new file mode 100644 index 000000000..31ecafdb2 --- /dev/null +++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/BackendInterface.php @@ -0,0 +1,80 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; + +/** + * Propertystorage backend interface. + * + * Propertystorage backends must implement this interface to be used by the + * propertystorage plugin. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +interface BackendInterface { + + /** + * Fetches properties for a path. + * + * This method received a PropFind object, which contains all the + * information about the properties that need to be fetched. + * + * Ususually you would just want to call 'get404Properties' on this object, + * as this will give you the _exact_ list of properties that need to be + * fetched, and haven't yet. + * + * However, you can also support the 'allprops' property here. In that + * case, you should check for $propFind->isAllProps(). + * + * @param string $path + * @param PropFind $propFind + * @return void + */ + function propFind($path, PropFind $propFind); + + /** + * Updates properties for a path + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * Usually you would want to call 'handleRemaining' on this object, to get; + * a list of all properties that need to be stored. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch); + + /** + * This method is called after a node is deleted. + * + * This allows a backend to clean up all associated properties. + * + * The delete method will get called once for the deletion of an entire + * tree. + * + * @param string $path + * @return void + */ + function delete($path); + + /** + * This method is called after a successful MOVE + * + * This should be used to migrate all properties from one path to another. + * Note that entire collections may be moved, so ensure that all properties + * for children are also moved along. + * + * @param string $source + * @param string $destination + * @return void + */ + function move($source, $destination); + +} diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php new file mode 100644 index 000000000..910e4979d --- /dev/null +++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Backend/PDO.php @@ -0,0 +1,217 @@ +<?php + +namespace Sabre\DAV\PropertyStorage\Backend; + +use Sabre\DAV\PropFind; +use Sabre\DAV\PropPatch; +use Sabre\DAV\Xml\Property\Complex; + +/** + * PropertyStorage PDO backend. + * + * This backend class uses a PDO-enabled database to store webdav properties. + * Both sqlite and mysql have been tested. + * + * The database structure can be found in the examples/sql/ directory. + * + * @copyright Copyright (C) fruux GmbH (https://fruux.com/) + * @author Evert Pot (http://evertpot.com/) + * @license http://sabre.io/license/ Modified BSD License + */ +class PDO implements BackendInterface { + + /** + * Value is stored as string. + */ + const VT_STRING = 1; + + /** + * Value is stored as XML fragment. + */ + const VT_XML = 2; + + /** + * Value is stored as a property object. + */ + const VT_OBJECT = 3; + + /** + * PDO + * + * @var \PDO + */ + protected $pdo; + + /** + * PDO table name we'll be using + * + * @var string + */ + public $tableName = 'propertystorage'; + + /** + * Creates the PDO property storage engine + * + * @param \PDO $pdo + */ + function __construct(\PDO $pdo) { + + $this->pdo = $pdo; + + } + + /** + * Fetches properties for a path. + * + * This method received a PropFind object, which contains all the + * information about the properties that need to be fetched. + * + * Ususually you would just want to call 'get404Properties' on this object, + * as this will give you the _exact_ list of properties that need to be + * fetched, and haven't yet. + * + * However, you can also support the 'allprops' property here. In that + * case, you should check for $propFind->isAllProps(). + * + * @param string $path + * @param PropFind $propFind + * @return void + */ + function propFind($path, PropFind $propFind) { + + if (!$propFind->isAllProps() && count($propFind->get404Properties()) === 0) { + return; + } + + $query = 'SELECT name, value, valuetype FROM ' . $this->tableName . ' WHERE path = ?'; + $stmt = $this->pdo->prepare($query); + $stmt->execute([$path]); + + while ($row = $stmt->fetch(\PDO::FETCH_ASSOC)) { + switch ($row['valuetype']) { + case null : + case self::VT_STRING : + $propFind->set($row['name'], $row['value']); + break; + case self::VT_XML : + $propFind->set($row['name'], new Complex($row['value'])); + break; + case self::VT_OBJECT : + $propFind->set($row['name'], unserialize($row['value'])); + break; + } + } + + } + + /** + * Updates properties for a path + * + * This method received a PropPatch object, which contains all the + * information about the update. + * + * Usually you would want to call 'handleRemaining' on this object, to get; + * a list of all properties that need to be stored. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + $propPatch->handleRemaining(function($properties) use ($path) { + + $updateStmt = $this->pdo->prepare("REPLACE INTO " . $this->tableName . " (path, name, valuetype, value) VALUES (?, ?, ?, ?)"); + $deleteStmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? AND name = ?"); + + foreach ($properties as $name => $value) { + + if (!is_null($value)) { + if (is_scalar($value)) { + $valueType = self::VT_STRING; + } elseif ($value instanceof Complex) { + $valueType = self::VT_XML; + $value = $value->getXml(); + } else { + $valueType = self::VT_OBJECT; + $value = serialize($value); + } + $updateStmt->execute([$path, $name, $valueType, $value]); + } else { + $deleteStmt->execute([$path, $name]); + } + + } + + return true; + + }); + + } + + /** + * This method is called after a node is deleted. + * + * This allows a backend to clean up all associated properties. + * + * The delete method will get called once for the deletion of an entire + * tree. + * + * @param string $path + * @return void + */ + function delete($path) { + + $stmt = $this->pdo->prepare("DELETE FROM " . $this->tableName . " WHERE path = ? OR path LIKE ? ESCAPE '='"); + $childPath = strtr( + $path, + [ + '=' => '==', + '%' => '=%', + '_' => '=_' + ] + ) . '/%'; + + $stmt->execute([$path, $childPath]); + + } + + /** + * This method is called after a successful MOVE + * + * This should be used to migrate all properties from one path to another. + * Note that entire collections may be moved, so ensure that all properties + * for children are also moved along. + * + * @param string $source + * @param string $destination + * @return void + */ + function move($source, $destination) { + + // I don't know a way to write this all in a single sql query that's + // also compatible across db engines, so we're letting PHP do all the + // updates. Much slower, but it should still be pretty fast in most + // cases. + $select = $this->pdo->prepare('SELECT id, path FROM ' . $this->tableName . ' WHERE path = ? OR path LIKE ?'); + $select->execute([$source, $source . '/%']); + + $update = $this->pdo->prepare('UPDATE ' . $this->tableName . ' SET path = ? WHERE id = ?'); + while ($row = $select->fetch(\PDO::FETCH_ASSOC)) { + + // Sanity check. SQL may select too many records, such as records + // with different cases. + if ($row['path'] !== $source && strpos($row['path'], $source . '/') !== 0) continue; + + $trailingPart = substr($row['path'], strlen($source) + 1); + $newPath = $destination; + if ($trailingPart) { + $newPath .= '/' . $trailingPart; + } + $update->execute([$newPath, $row['id']]); + + } + + } + +} diff --git a/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php b/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php new file mode 100644 index 000000000..0c28b7882 --- /dev/null +++ b/vendor/sabre/dav/lib/DAV/PropertyStorage/Plugin.php @@ -0,0 +1,180 @@ +<?php + +namespace Sabre\DAV\PropertyStorage; + +use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; +use Sabre\DAV\PropPatch; +use Sabre\DAV\PropFind; +use Sabre\DAV\INode; + +/** + * PropertyStorage Plugin. + * + * Adding this plugin to your server allows clients to store any arbitrary + * WebDAV property. + * + * See: + * http://sabre.io/dav/property-storage/ + * + * for more information. + * + * @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 ServerPlugin { + + /** + * If you only want this plugin to store properties for a limited set of + * paths, you can use a pathFilter to do this. + * + * The pathFilter should be a callable. The callable retrieves a path as + * its argument, and should return true or false wether it allows + * properties to be stored. + * + * @var callable + */ + public $pathFilter; + + /** + * Creates the plugin + * + * @param Backend\BackendInterface $backend + */ + function __construct(Backend\BackendInterface $backend) { + + $this->backend = $backend; + + } + + /** + * This initializes the plugin. + * + * This function is called by Sabre\DAV\Server, after + * addPlugin is called. + * + * This method should set up the required event subscriptions. + * + * @param Server $server + * @return void + */ + function initialize(Server $server) { + + $server->on('propFind', [$this, 'propFind'], 130); + $server->on('propPatch', [$this, 'propPatch'], 300); + $server->on('afterMove', [$this, 'afterMove']); + $server->on('afterUnbind', [$this, 'afterUnbind']); + + } + + /** + * Called during PROPFIND operations. + * + * If there's any requested properties that don't have a value yet, this + * plugin will look in the property storage backend to find them. + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + + $path = $propFind->getPath(); + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->propFind($propFind->getPath(), $propFind); + + } + + /** + * Called during PROPPATCH operations + * + * If there's any updated properties that haven't been stored, the + * propertystorage backend can handle it. + * + * @param string $path + * @param PropPatch $propPatch + * @return void + */ + function propPatch($path, PropPatch $propPatch) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->propPatch($path, $propPatch); + + } + + /** + * Called after a node is deleted. + * + * This allows the backend to clean up any properties still in the + * database. + * + * @param string $path + * @return void + */ + function afterUnbind($path) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($path)) return; + $this->backend->delete($path); + + } + + /** + * Called after a node is moved. + * + * This allows the backend to move all the associated properties. + * + * @param string $source + * @param string $destination + * @return void + */ + function afterMove($source, $destination) { + + $pathFilter = $this->pathFilter; + if ($pathFilter && !$pathFilter($source)) return; + // If the destination is filtered, afterUnbind will handle cleaning up + // the properties. + if ($pathFilter && !$pathFilter($destination)) return; + + $this->backend->move($source, $destination); + + } + + /** + * 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 'property-storage'; + + } + + /** + * 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' => 'This plugin allows any arbitrary WebDAV property to be set on any resource.', + 'link' => 'http://sabre.io/dav/property-storage/', + ]; + + } +} |