1<?php 2/** 3 * @copyright Copyright (c) 2016, ownCloud, Inc. 4 * 5 * @author Christoph Wurst <christoph@winzerhof-wurst.at> 6 * @author Joas Schilling <coding@schilljs.com> 7 * @author Lukas Reschke <lukas@statuscode.ch> 8 * @author Roeland Jago Douma <roeland@famdouma.nl> 9 * @author Thomas Müller <thomas.mueller@tmit.eu> 10 * 11 * @license AGPL-3.0 12 * 13 * This code is free software: you can redistribute it and/or modify 14 * it under the terms of the GNU Affero General Public License, version 3, 15 * as published by the Free Software Foundation. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU Affero General Public License for more details. 21 * 22 * You should have received a copy of the GNU Affero General Public License, version 3, 23 * along with this program. If not, see <http://www.gnu.org/licenses/> 24 * 25 */ 26 27namespace dokuwiki\plugin\webdav\core\Plugin; 28 29use Sabre\DAV\INode; 30use Sabre\DAV\Locks\LockInfo; 31use Sabre\DAV\PropFind; 32use Sabre\DAV\ServerPlugin; 33use Sabre\DAV\Xml\Property\LockDiscovery; 34use Sabre\DAV\Xml\Property\SupportedLock; 35use Sabre\HTTP\RequestInterface; 36use Sabre\HTTP\ResponseInterface; 37 38/** 39 * Class FakeLockerPlugin is a plugin only used when connections come in from 40 * OS X via Finder. The fake locking plugin does emulate Class 2 WebDAV support 41 * (locking of files) which allows Finder to access the storage in write mode as 42 * well. 43 * 44 * No real locking is performed, instead the plugin just returns always positive 45 * responses. 46 * 47 * @see https://github.com/owncloud/core/issues/17732 48 */ 49class FakeLocker extends ServerPlugin 50{ 51 /** @var \Sabre\DAV\Server */ 52 private $server; 53 54 /** {@inheritDoc} */ 55 public function initialize(\Sabre\DAV\Server $server) 56 { 57 $this->server = $server; 58 $this->server->on('method:LOCK', [$this, 'fakeLockProvider'], 1); 59 $this->server->on('method:UNLOCK', [$this, 'fakeUnlockProvider'], 1); 60 $server->on('propFind', [$this, 'propFind']); 61 $server->on('validateTokens', [$this, 'validateTokens']); 62 } 63 64 /** 65 * Indicate that we support LOCK and UNLOCK 66 * 67 * @param string $path 68 * @return string[] 69 */ 70 public function getHTTPMethods($path) 71 { 72 return [ 73 'LOCK', 74 'UNLOCK', 75 ]; 76 } 77 78 /** 79 * Indicate that we support locking 80 * 81 * @return integer[] 82 */ 83 public function getFeatures() 84 { 85 return [2]; 86 } 87 88 /** 89 * Return some dummy response for PROPFIND requests with regard to locking 90 * 91 * @param PropFind $propFind 92 * @param INode $node 93 * @return void 94 */ 95 public function propFind(PropFind $propFind, INode $node) 96 { 97 $propFind->handle('{DAV:}supportedlock', function () { 98 return new SupportedLock(true); 99 }); 100 $propFind->handle('{DAV:}lockdiscovery', function () use ($propFind) { 101 return new LockDiscovery([]); 102 }); 103 } 104 105 /** 106 * Mark a locking token always as valid 107 * 108 * @param RequestInterface $request 109 * @param array $conditions 110 */ 111 public function validateTokens(RequestInterface $request, &$conditions) 112 { 113 foreach ($conditions as &$fileCondition) { 114 if (isset($fileCondition['tokens'])) { 115 foreach ($fileCondition['tokens'] as &$token) { 116 if (isset($token['token'])) { 117 if (substr($token['token'], 0, 16) === 'opaquelocktoken:') { 118 $token['validToken'] = true; 119 } 120 } 121 } 122 } 123 } 124 } 125 126 /** 127 * Fakes a successful LOCK 128 * 129 * @param RequestInterface $request 130 * @param ResponseInterface $response 131 * @return bool 132 */ 133 public function fakeLockProvider(RequestInterface $request, ResponseInterface $response) 134 { 135 $lockInfo = new LockInfo(); 136 $lockInfo->token = md5($request->getPath()); 137 $lockInfo->uri = $request->getPath(); 138 $lockInfo->depth = \Sabre\DAV\Server::DEPTH_INFINITY; 139 $lockInfo->timeout = 1800; 140 141 $body = $this->server->xml->write('{DAV:}prop', [ 142 '{DAV:}lockdiscovery' => new LockDiscovery([$lockInfo]), 143 ]); 144 145 $response->setStatus(200); 146 $response->setBody($body); 147 148 return false; 149 } 150 151 /** 152 * Fakes a successful LOCK 153 * 154 * @param RequestInterface $request 155 * @param ResponseInterface $response 156 * @return bool 157 */ 158 public function fakeUnlockProvider(RequestInterface $request, ResponseInterface $response) 159 { 160 $response->setStatus(204); 161 $response->setHeader('Content-Length', '0'); 162 return false; 163 } 164} 165