diff options
author | Lukas Reschke <lukas@owncloud.com> | 2015-11-13 11:47:32 +0100 |
---|---|---|
committer | Lukas Reschke <lukas@owncloud.com> | 2015-11-13 23:31:08 +0100 |
commit | a3fc40921b20a3fcc1c7260f4abfcadc5a8c1d13 (patch) | |
tree | b359e30824d121cf2004ae4a048710d68a6ecbee /apps/dav/lib | |
parent | 1e9203cdef7009ef129f0f3e3db6a326d3f4db12 (diff) | |
download | nextcloud-server-a3fc40921b20a3fcc1c7260f4abfcadc5a8c1d13.tar.gz nextcloud-server-a3fc40921b20a3fcc1c7260f4abfcadc5a8c1d13.zip |
Add fake locker plugin for WebDAVFS
WebDAVFS as used by Finder requires a Class 2 compatible WebDAV server. This change introduces a fake locking provider which will simply advertise Locking support when a request originates from WebDAVFS. It will also return successful LOCK and UNLOCK responses.
Diffstat (limited to 'apps/dav/lib')
-rw-r--r-- | apps/dav/lib/connector/sabre/fakelockerplugin.php | 159 | ||||
-rw-r--r-- | apps/dav/lib/connector/sabre/serverfactory.php | 43 | ||||
-rw-r--r-- | apps/dav/lib/server.php | 6 |
3 files changed, 206 insertions, 2 deletions
diff --git a/apps/dav/lib/connector/sabre/fakelockerplugin.php b/apps/dav/lib/connector/sabre/fakelockerplugin.php new file mode 100644 index 00000000000..493d3b0ade4 --- /dev/null +++ b/apps/dav/lib/connector/sabre/fakelockerplugin.php @@ -0,0 +1,159 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OCA\DAV\Connector\Sabre; + +use Sabre\DAV\Locks\LockInfo; +use Sabre\DAV\Property\LockDiscovery; +use Sabre\DAV\Property\SupportedLock; +use Sabre\DAV\ServerPlugin; +use Sabre\HTTP\RequestInterface; +use Sabre\HTTP\ResponseInterface; +use Sabre\DAV\PropFind; +use Sabre\DAV\INode; + +/** + * Class FakeLockerPlugin is a plugin only used when connections come in from + * OS X via Finder. The fake locking plugin does emulate Class 2 WebDAV support + * (locking of files) which allows Finder to access the storage in write mode as + * well. + * + * No real locking is performed, instead the plugin just returns always positive + * responses. + * + * @see https://github.com/owncloud/core/issues/17732 + * @package OCA\DAV\Connector\Sabre + */ +class FakeLockerPlugin extends ServerPlugin { + /** @var \Sabre\DAV\Server */ + private $server; + + /** {@inheritDoc} */ + public function initialize(\Sabre\DAV\Server $server) { + $this->server = $server; + $this->server->on('method:LOCK', [$this, 'fakeLockProvider'], 1); + $this->server->on('method:UNLOCK', [$this, 'fakeUnlockProvider'], 1); + $server->on('propFind', [$this, 'propFind']); + $server->on('validateTokens', [$this, 'validateTokens']); + } + + /** + * Indicate that we support LOCK and UNLOCK + * + * @param string $path + * @return array + */ + public function getHTTPMethods($path) { + return [ + 'LOCK', + 'UNLOCK', + ]; + } + + /** + * Indicate that we support locking + * + * @return array + */ + function getFeatures() { + return [2]; + } + + /** + * Return some dummy response for PROPFIND requests with regard to locking + * + * @param PropFind $propFind + * @param INode $node + * @return void + */ + function propFind(PropFind $propFind, INode $node) { + $propFind->handle('{DAV:}supportedlock', function() { + return new SupportedLock(true); + }); + $propFind->handle('{DAV:}lockdiscovery', function() use ($propFind) { + return new LockDiscovery([]); + }); + } + + /** + * Mark a locking token always as valid + * + * @param RequestInterface $request + * @param array $conditions + */ + public function validateTokens(RequestInterface $request, &$conditions) { + foreach($conditions as &$fileCondition) { + if(isset($fileCondition['tokens'])) { + foreach($fileCondition['tokens'] as &$token) { + if(isset($token['token'])) { + if(substr($token['token'], 0, 16) === 'opaquelocktoken:') { + $token['validToken'] = true; + } + } + } + } + } + } + + /** + * Fakes a successful LOCK + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function fakeLockProvider(RequestInterface $request, + ResponseInterface $response) { + $dom = new \DOMDocument('1.0', 'utf-8'); + $prop = $dom->createElementNS('DAV:', 'd:prop'); + $dom->appendChild($prop); + + $lockDiscovery = $dom->createElementNS('DAV:', 'd:lockdiscovery'); + $prop->appendChild($lockDiscovery); + + $lockInfo = new LockInfo(); + $lockInfo->token = md5($request->getPath()); + $lockInfo->uri = $request->getPath(); + $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; + $lockInfo->timeout = 1800; + + $lockObj = new LockDiscovery([$lockInfo]); + $lockObj->serialize($this->server, $lockDiscovery); + + $response->setBody($dom->saveXML()); + + return false; + } + + /** + * Fakes a successful LOCK + * + * @param RequestInterface $request + * @param ResponseInterface $response + * @return bool + */ + public function fakeUnlockProvider(RequestInterface $request, + ResponseInterface $response) { + $response->setStatus(204); + $response->setHeader('Content-Length', '0'); + return false; + } +} diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php index f67e949e802..a33acc9f00b 100644 --- a/apps/dav/lib/connector/sabre/serverfactory.php +++ b/apps/dav/lib/connector/sabre/serverfactory.php @@ -26,12 +26,41 @@ use OCP\Files\Mount\IMountManager; use OCP\IConfig; use OCP\IDBConnection; use OCP\ILogger; +use OCP\IRequest; use OCP\ITagManager; use OCP\IUserSession; use Sabre\DAV\Auth\Backend\BackendInterface; +use Sabre\DAV\Locks\Plugin; use Symfony\Component\EventDispatcher\EventDispatcherInterface; class ServerFactory { + /** @var IConfig */ + private $config; + /** @var ILogger */ + private $logger; + /** @var IDBConnection */ + private $databaseConnection; + /** @var IUserSession */ + private $userSession; + /** @var IMountManager */ + private $mountManager; + /** @var ITagManager */ + private $tagManager; + /** @var EventDispatcherInterface */ + private $dispatcher; + /** @var IRequest */ + private $request; + + /** + * @param IConfig $config + * @param ILogger $logger + * @param IDBConnection $databaseConnection + * @param IUserSession $userSession + * @param IMountManager $mountManager + * @param ITagManager $tagManager + * @param EventDispatcherInterface $dispatcher + * @param IRequest $request + */ public function __construct( IConfig $config, ILogger $logger, @@ -39,7 +68,8 @@ class ServerFactory { IUserSession $userSession, IMountManager $mountManager, ITagManager $tagManager, - EventDispatcherInterface $dispatcher + EventDispatcherInterface $dispatcher, + IRequest $request ) { $this->config = $config; $this->logger = $logger; @@ -48,6 +78,7 @@ class ServerFactory { $this->mountManager = $mountManager; $this->tagManager = $tagManager; $this->dispatcher = $dispatcher; + $this->request = $request; } /** @@ -57,7 +88,10 @@ class ServerFactory { * @param callable $viewCallBack callback that should return the view for the dav endpoint * @return Server */ - public function createServer($baseUri, $requestUri, BackendInterface $authBackend, callable $viewCallBack) { + public function createServer($baseUri, + $requestUri, + BackendInterface $authBackend, + callable $viewCallBack) { // Fire up server $objectTree = new \OCA\DAV\Connector\Sabre\ObjectTree(); $server = new \OCA\DAV\Connector\Sabre\Server($objectTree); @@ -75,6 +109,11 @@ class ServerFactory { $server->addPlugin(new \OCA\DAV\Connector\Sabre\ExceptionLoggerPlugin('webdav', $this->logger)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\LockPlugin($objectTree)); $server->addPlugin(new \OCA\DAV\Connector\Sabre\ListenerPlugin($this->dispatcher)); + // Finder on OS X requires Class 2 WebDAV support (locking), since we do + // not provide locking we emulate it using a fake locking plugin. + if($this->request->isUserAgent(['/WebDAVFS/'])) { + $server->addPlugin(new \OCA\DAV\Connector\Sabre\FakeLockerPlugin()); + } // wait with registering these until auth is handled and the filesystem is setup $server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) { diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index a92c9980f54..395544761ab 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -37,6 +37,12 @@ class Server { $this->server->addPlugin(new \Sabre\CardDAV\Plugin()); + // Finder on OS X requires Class 2 WebDAV support (locking), since we do + // not provide locking we emulate it using a fake locking plugin. + if($request->isUserAgent(['/WebDAVFS/'])) { + $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FakeLockerPlugin()); + } + // wait with registering these until auth is handled and the filesystem is setup $this->server->on('beforeMethod', function () { // custom properties plugin must be the last one |