* * @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 * */ 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; } }