* @author Joas Schilling * @author Lukas Reschke * @author Roeland Jago Douma * @author Thomas Müller * * @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 dokuwiki\plugin\webdav\core\Plugin; use Sabre\DAV\INode; use Sabre\DAV\Locks\LockInfo; use Sabre\DAV\PropFind; use Sabre\DAV\ServerPlugin; use Sabre\DAV\Xml\Property\LockDiscovery; use Sabre\DAV\Xml\Property\SupportedLock; use Sabre\HTTP\RequestInterface; use Sabre\HTTP\ResponseInterface; /** * 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 */ class FakeLocker 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 string[] */ public function getHTTPMethods($path) { return [ 'LOCK', 'UNLOCK', ]; } /** * Indicate that we support locking * * @return integer[] */ public function getFeatures() { return [2]; } /** * Return some dummy response for PROPFIND requests with regard to locking * * @param PropFind $propFind * @param INode $node * @return void */ public 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) { $lockInfo = new LockInfo(); $lockInfo->token = md5($request->getPath()); $lockInfo->uri = $request->getPath(); $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; $lockInfo->timeout = 1800; $body = $this->server->xml->write('{DAV:}prop', [ '{DAV:}lockdiscovery' => new LockDiscovery([$lockInfo]), ]); $response->setStatus(200); $response->setBody($body); 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; } }