aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib
diff options
context:
space:
mode:
authorLukas Reschke <lukas@owncloud.com>2015-11-13 11:47:32 +0100
committerLukas Reschke <lukas@owncloud.com>2015-11-13 23:31:08 +0100
commita3fc40921b20a3fcc1c7260f4abfcadc5a8c1d13 (patch)
treeb359e30824d121cf2004ae4a048710d68a6ecbee /apps/dav/lib
parent1e9203cdef7009ef129f0f3e3db6a326d3f4db12 (diff)
downloadnextcloud-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.php159
-rw-r--r--apps/dav/lib/connector/sabre/serverfactory.php43
-rw-r--r--apps/dav/lib/server.php6
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