<?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 OC\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 OC\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;
	}
}