<?php
// (c) Copyright 2002-2012 by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id: File.php 42542 2012-08-06 18:48:41Z lphuberdeau $

class TikiWebdav_Backends_File extends ezcWebdavSimpleBackend implements ezcWebdavLockBackend
{
	private $requestMimeType;

	protected $options;
	protected $root;
	protected $lockLevel = 0;

	protected $handledLiveProperties = array(
			'getcontentlength',
			'getlastmodified',
			'creationdate',
			'displayname',
			'getetag',
			'getcontenttype',
			'resourcetype',
			//'supportedlock',
			//'lockdiscovery',
			);

	protected $resourceStorage = null;
	protected $propertyStorage = null;

	public function getRoot()
	{
		return $this->root;
	}

	public function __construct()
	{
		global $prefs;

		// avoid not having a deadlock when trying to acquire WebDav lock
		print_debug("Lock Directory: ".$prefs['fgal_use_dir']."\n", FILE_APPEND);
		if (!empty($prefs['fgal_use_dir']) && file_exists($prefs['fgal_use_dir'])) {
			$this->root = realpath($prefs['fgal_use_dir']);
		} else {
			$this->root = realpath('temp/');
		}
		$this->options = new ezcWebdavFileBackendOptions(
						array(
							'lockFileName' => $this->root.'/.webdav_lock',
							'waitForLock' => 200000,
							'propertyStoragePath' => $this->root,
							'noLock' => false
						)
		);
		$this->propertyStorage = new ezcWebdavBasicPropertyStorage();
	}

	public function lock($waitTime, $timeout)
	{
		// Check and raise lockLevel counter
		print_debug("LOCK Level: ".$this->lockLevel." \n");
		if ($this->lockLevel > 0) {
			// Lock already acquired
			++$this->lockLevel;
			return;
		}

		$lockStart = microtime(true);

		// Timeout is in microseconds...
		$timeout /= 1000000;
		$lockFileName = $this->options->lockFileName;

		// fopen in mode 'x' will only open the file, if it does not exist yet.
		// Even this is is expected it will throw a warning, if the file
		// exists, which we need to silence using the @
		if (!empty($this->options->lockFileName)) {
			if (file_exists($lockFileName)) {
				while (file_exists($lockFileName)) {
					// This is untestable.
					if (microtime(true) - $lockStart > $timeout) {
						// Release timed out lock
						unlink($lockFileName);
						$lockStart = microtime(true);
					} else {
						usleep($waitTime);
					}
				}
			}
			@file_put_contents($lockFileName, microtime(), FILE_APPEND);
		}

		// Add first lock
		++$this->lockLevel;
		print_debug("LOCK END \n");
	}

	public function unlock()
	{
		print_debug("unLOCK Level: ".$this->lockLevel." \n");
		if (--$this->lockLevel === 0) {
			// Remove the lock file
			$lockFileName = $this->options->lockFileName;
			if (!empty($this->options->lockFileName)) {
				unlink($lockFileName);
			}
			print_debug("Remove LOCK: ".$this->options->lockFileName." \n");
		}
	}

	public function __get($name)
	{
		switch ($name) {
			case 'options':
				return $this->$name;

			default:
				throw new ezcBasePropertyNotFoundException($name);
		}
	}

	public function __set($name, $value)
	{
		switch ($name) {
			case 'options':
				if (! $value instanceof ezcWebdavMemoryBackendOptions) {///FIXME
					throw new ezcBaseValueException($name, $value, 'ezcWebdavMemoryBackendOptions'); ///FIXME
				}

				$this->$name = $value;
    			break;

			default:
				throw new ezcBasePropertyNotFoundException($name);
		}
	}

	protected function acquireLock($readOnly = false)
	{
		if (empty($this->options->lockFileName)) {
			return true;
		}

		try {
			$this->lock($this->options->waitForLock, 2000000);
		}
		catch (ezcWebdavLockTimeoutException $e) {
			print_debug("LOCK: failed\n");
			return false;
		}
		print_debug("LOCK: Acquired\n");
		return true;
	}

	protected function freeLock()
	{
		print_debug("FreeLOCK: ".$this->options->lockFileName."\n");
		if (empty($this->options->lockFileName)) {
			return true;
		}

		$this->unlock();
	}

	protected function createCollection($path)
	{
		global $user ;
		global $filegallib; require_once('lib/filegals/filegallib.php');

		if (empty($path))
			return false;

		if (substr($path, -1, 1) === '/')
			$path = substr($path, 0, -1);

		if (($objectId = $filegallib->get_objectid_from_virtual_path(dirname($path))) === false || $objectId['type'] != 'filegal')
			return false;

		// Get parent filegal info as a base
		$filegalInfo = $filegallib->get_file_gallery_info($objectId['id']);

		$filegalInfo['galleryId'] = -1;
		$filegalInfo['parentId'] = $objectId['id'];
		$filegalInfo['name'] = basename($path);
		$filegalInfo['description'] = '';
		$filegalInfo['user'] = $user;

		return (bool) $filegallib->replace_file_gallery($filegalInfo);
	}

	protected function createResource($path, $content = null)
	{
		return true;
	}

	protected function _createResource($path, $content = null)
	{
		global $user, $prefs;
		global $filegallib; require_once('lib/filegals/filegallib.php');

		print_debug("createResource: $path\n");
		if (empty($path)
				|| substr($path, -1, 1) == '/'
				|| ($objectId = $filegallib->get_objectid_from_virtual_path(dirname($path))) === false
				|| $objectId['type'] != 'filegal'
			) {
			print_debug("createResource: failed\n");	
			return false;
		}

		$name = basename($path);
		if (empty($content)) $content = '';
		$filesize = @strlen($content);

		if ($prefs['fgal_use_db'] === 'n') {
			$fhash = md5($name);
			do {
				$fhash = md5(uniqid($fhash));
			} while (file_exists($this->root . '/' . $fhash));

			if (@file_put_contents($this->root . '/' . $fhash, $content) === false) {
				print_debug("createResource: ". $this->root . '/' . $fhash ." failed\n");	
				return false;
			}
			$mime = TikiLib::lib('mime')->from_path($name, $this->root . '/' . $fhash);
			$content = '';
		} else {
			$fhash = '';
			$mime = TikiLib::lib('mime')->from_content($name, $content);
		}

		$fileId = $filegallib->insert_file(
						$objectId['id'],
						$name,
						'',
						$name,
						$content,
						$filesize,
						$mime,
						$user,
						$fhash,
						'',
						$user
		);
		print_debug("createResource: end fileID=$fileId\n");	
		return $fileId != 0;
	}

	protected function setResourceContents($path, $content)
	{
		global $user, $prefs;
		global $filegallib; require_once('lib/filegals/filegallib.php');

		if (empty($path) || substr($path, -1, 1) == '/') {
			print_debug("\nsetResourceContents failed empty path or directory\n");
			return false;
		}
		if (($objectId = $filegallib->get_objectid_from_virtual_path($path)) === false) {
			print_debug("\nCreateResouce new $path\n");
			return $this->_createResource($path, $content);
		}

		if ($objectId['type'] != 'file') {
			print_debug("\nsetResourceContents failed : destination is not a file\n");
			return false;
		}

		$name = basename($path);
		if (empty($content))
			$content = '';

		if ($prefs['fgal_use_db'] === 'n') {
			$fhash = md5($name);
			do {
				$fhash = md5(uniqid($fhash));
			} while (file_exists($this->root . '/' . $fhash));

			$mime = TikiLib::lib('mime')->from_path($name, $this->root . '/' . $fhash);
		} else {
			$fhash = '';
			$mime = TikiLib::lib('mime')->from_content($name, $content);
		}

		print_debug("setResourceContents : $path/$fhash \n");

		$fileInfo = $filegallib->get_file_info($objectId['id'], false, false);
		$filegalInfo = $filegallib->get_file_gallery_info($fileInfo['galleryId']);

		if ($prefs['fgal_use_db'] === 'n' && @file_put_contents($this->root . '/' . $fhash, $content) === false) {
			return false;
		}

		$fileId = $filegallib->replace_file(
						$objectId['id'],
						$fileInfo['name'],
						$fileInfo['description'],
						$fileInfo['filename'],
						$content,
						@strlen($content),
						$mime,
						$user,
						$fhash,
						'',
						$filegalInfo,
						true
		);
		print_debug("setResourceContents: fileId = $fileId end \n");	
		return $fileId;
	}

	protected function getResourceContents($path)
	{
		global $prefs;
		global $filegallib; require_once('lib/filegals/filegallib.php');

		$result = false;
		$objectId = $filegallib->get_objectid_from_virtual_path($path);

		if ($objectId !== false && $objectId['type'] == 'file') {
			if ($prefs['feature_file_galleries_save_draft'] == 'y') {
				$fileInfo = $filegallib->get_file_draft($objectId['id']);
			} else {
				$fileInfo = $filegallib->get_file($objectId['id']);
			}

			if (empty($fileInfo['path'])) {
				return $fileInfo['data'];
			} else {
				$result = $this->root . '/' . $fileInfo['path'];

				if (! file_exists($result))
					return false;
			}
			return file_get_contents($result);
		}
	}

	///TODO
	protected function getPropertyStorage($path)
	{
		//print_debug("getPropertyStorage method \n");
		$storagePath = $this->options->propertyStoragePath . '/properties-' . md5($path);
		// If no properties has been stored yet, just return an empty property
		// storage.
		if (!is_file($storagePath)) {
			return new ezcWebdavBasicPropertyStorage();
		}

		// Create handler structure to read properties
		$handler = new ezcWebdavPropertyHandler(
						$xml = new ezcWebdavXmlTool()
		);
		$storage = new ezcWebdavBasicPropertyStorage();

		// Read document
		try {
			$doc = $xml->createDom(file_get_contents($storagePath));
		}
		catch (ezcWebdavInvalidXmlException $e) {
			throw new ezcWebdavFileBackendBrokenStorageException(
							"Could not open XML as DOMDocument: '{$storage}'."
			);
		}

		// Get property node from document
		$properties = $doc->getElementsByTagname('properties')->item(0)->childNodes;

		// Extract and return properties
		$handler->extractProperties(
						$properties,
						$storage
		);


		print_debug("getPropertyStorage method end " . print_r($properties, true) ."\n");
		return $storage;
	}

	protected function storeProperties($path, ezcWebdavBasicPropertyStorage $storage)
	{
		$storagePath = $this->options->propertyStoragePath.'/properties-'.md5($path);
		print_debug("storeProperties method $storagePath\n");

		// Create handler structure to read properties
		$handler = new ezcWebdavPropertyHandler(
						$xml = new ezcWebdavXmlTool()
		);

		// Create new dom document with property storage for one namespace
		$doc = new DOMDocument('1.0');

		$properties = $doc->createElement('properties');
		$doc->appendChild($properties);

		$handler->serializeProperties(
						$storage,
						$properties
		);

		print_debug("storeProperties method end\n");

		return $doc->save($storagePath);
	}

	///TODO
	public function setProperty($path, ezcWebdavProperty $property)
	{
		print_debug("setProperty method PATH=$path PROPERTY:".$property->name."\n");
		// Check if property is a self handled live property and return an
		// error in this case.
		if (($property->namespace === 'DAV:') && in_array($property->name, $this->handledLiveProperties, true) &&
				($property->name !== 'getcontenttype') &&
				($property->name !== 'lockdiscovery')) {
			return false;
		}

		// Get namespace property storage
		$storage = $this->getPropertyStorage($path);

		// Attach property to store
		$storage->attach($property);

		// Store document back
		$this->storeProperties($path, $storage);

		return true;
	}

	public function removeProperty($path, ezcWebdavProperty $property)
	{
		print_debug("removeProperty method\n");
		// Live properties may not be removed.
		if ($property instanceof ezcWebdavLiveProperty) {
			return false;
		}

		// Get namespace property storage
		$storage = $this->getPropertyStorage($path);

		// Attach property to store
		$storage->detach($property->name, $property->namespace);

		// Store document back
		$this->storeProperties($path, $storage);

		return true;
	}

	public function resetProperties($path, ezcWebdavPropertyStorage $storage)
	{
		print_debug("resetProperties method\n");
		$this->storeProperties($path, $storage);
		return true;
	}

	public function getProperty($path, $propertyName, $namespace = 'DAV:')
	{
		global $prefs;
		global $filegallib; include_once('lib/filegals/filegallib.php');

		print_debug("GetProperty($path, $propertyName, $namespace)\n");
		if ($path == '/Wiki Pages/') {
			print_debug("GetPropertyFake($path, $propertyName, $namespace)\n");
			$storage = $this->getPropertyStorage('/File Galleries/');
			$properties = $storage->getAllProperties('/File Galleries/');
			if ($propertyName === 'resourcetype') {
				return new ezcWebdavResourceTypeProperty(ezcWebdavResourceTypeProperty::TYPE_COLLECTION);
			}
			return $properties[$namespace][$propertyName];
		}
		if (($objectId = $filegallib->get_objectid_from_virtual_path($path)) === false) {
			return false;
		}

		$isCollection = ($objectId['type'] == 'filegal');

		if (isset($this->resourceStorage[$objectId['id']])) {
			// Use cached values
			$tikiInfo = $this->resourceStorage[$objectId['id']];
		} else {
			if ($isCollection) {
				$tikiInfo = $filegallib->get_file_gallery_info($objectId['id']);
			} else {
				if ($prefs['feature_file_galleries_save_draft'] == 'y') {
					$tikiInfo = $filegallib->get_file_info($objectId['id'], true, true, true);
				} else {
					$tikiInfo = $filegallib->get_file_info($objectId['id']);
				}
			}

			$this->resourceStorage[$objectId['id']] = $tikiInfo;
		}

		$storage = $this->getPropertyStorage($path);

		$properties = $storage->getAllProperties();
		// Handle dead propreties
		if ($namespace !== 'DAV:') {
			return $properties[$namespace][$propertyName];
		}

		// Handle live properties
		switch ($propertyName) {
			case 'getcontentlength':
				$property = new ezcWebdavGetContentLengthProperty(
								$isCollection ?
								ezcWebdavGetContentLengthProperty::COLLECTION :
								$tikiInfo['filesize']
				);
				return $property;

			case 'getlastmodified':
				$property = new ezcWebdavGetLastModifiedProperty(new ezcWebdavDateTime('@' . (int)$tikiInfo['lastModif']));
				print_debug("-> " . $tikiInfo['lastModif'] ."\n");
				return $property;

			case 'creationdate':
				$property = new ezcWebdavCreationDateProperty(new ezcWebdavDateTime('@' . (int)$tikiInfo['created']));
				print_debug("-> " . $tikiInfo['created'] ."\n");
				return $property;

			case 'displayname':
				$property = new ezcWebdavDisplayNameProperty(
								$isCollection ? $tikiInfo['name'] : $tikiInfo['filename']
				);
				print_debug("-> " . ($isCollection ? $tikiInfo['name'] : $tikiInfo['filename']) ."\n");
				return $property;

			case 'getcontenttype':
				$property = new ezcWebdavGetContentTypeProperty(
								$isCollection ?
								'httpd/unix-directory' :
								(empty($tikiInfo['filetype']) ? 'application/octet-stream' : $tikiInfo['filetype'])
				);
				print_debug("-> " . ($isCollection ? 'httpd/unix-directory' : (empty($tikiInfo['filetype']) ? 'application/octet-stream' : $tikiInfo['filetype'])) ."\n" . print_r($property, true) . "\n");
				return $property;

			case 'getetag':
				$md5 = ($isCollection || empty($tikiInfo['hash'])) ? md5($path) : $tikiInfo['hash'];
				$property = new ezcWebdavGetEtagProperty('"' . $md5 . '-' . crc32($md5) . '"');
				print_debug("-> " . '"' . $md5 . '-' . crc32($md5) . '"' ."\n");
				return $property;

			case 'resourcetype':
				$property = new ezcWebdavResourceTypeProperty(
								$isCollection ?
								ezcWebdavResourceTypeProperty::TYPE_COLLECTION :
								ezcWebdavResourceTypeProperty::TYPE_RESOURCE
				);
				print_debug("-> " . ($isCollection ? 'TYPE_COLLECTION' : 'TYPE_RESOURCE') ."\n");
				return $property;

			case 'supportedlock':
				if (!isset($properties[$namespace][$propertyName])) {
					$property = new ezcWebdavLockDiscoveryProperty();
				} else {
					$property = $properties[$namespace][$propertyName];
				}
				print_debug("-> " . print_r($property, true) . "\n");
				return $property;

			case 'lockdiscovery':
				if (!isset($properties[$namespace][$propertyName])) {
					$property = new ezcWebdavLockDiscoveryProperty();
				} else {
					$property = $properties[$namespace][$propertyName];
				}
				print_debug("-> " . print_r($property, true) . "\n");
				return $property;

			default:
				// Handle all other live properties like dead properties
				$properties = $storage->getAllProperties();
				return $properties[$namespace][$propertyName];
		}
	}

	private function getContentLength($path)
	{
		$contentlength = $this->getProperty($path, 'getcontentlength');
		print_debug("getContentLength $path". $getcontentlength->contentlength . "\n");
		return $getcontentlength->contentlength;
	}

	protected function getETag($path)
	{
		if ($etag = $this->getProperty($path, 'getetag')) {
			return $etag->etag;
		} else {
			return md5($path);
		}
	}

	public function getAllProperties($path)
	{
		$storage = $this->getPropertyStorage($path);

		// Add all live properties to stored properties
		foreach ($this->handledLiveProperties as $property) {
			$storage->attach($this->getProperty($path, $property));
			$this->storeProperties($path, $storage);
		}

		return $storage;
	}

	protected function performCopy($fromPath, $toPath, $depth = ezcWebdavRequest::DEPTH_INFINITY)
	{
		global $prefs, $filegallib, $user;

		$infos = array('source' => array(), 'dest' => array());
		$infos['dest']['name'] = basename($toPath);
		$source = $fromPath;
		$dest = $toPath;

		foreach (array('source', 'dest') as $k) {
			// Get source and dest infos
			if (($infos[$k] = $filegallib->get_objectid_from_virtual_path($$k)) !== false) {
				switch ($infos[$k]['type']) {
					case 'filegal':
						$infos[$k]['infos'] = $filegallib->get_file_gallery_info($infos[$k]['id']);
						$infos[$k]['parentId'] = $infos[$k]['infos']['parentId'];
						$infos[$k]['name'] = $infos[$k]['infos']['name'];
    					break;

					case 'file':
						$infos[$k]['infos'] = $filegallib->get_file($infos[$k]['id']);
						$infos[$k]['parentId'] = $infos[$k]['infos']['galleryId'];
						$infos[$k]['name'] = $infos[$k]['infos']['filename'];
    					break;
				}
			} elseif ($k == 'dest') {
			// If dest doesn't exist, it usually means that the file / filegal has to be renamed
				if (($objectId = $filegallib->get_objectid_from_virtual_path(dirname($$k))) !== false
						&& $objectId['type'] == 'filegal'
					) {
					$infos[$k] = array(
							'id' => $infos['source']['id'],
							'type' => $infos['source']['type'],
							'infos' => $infos['source']['infos'],
							'parentId' => $objectId['id'],
							'name' => basename($$k)
							);

					switch ($infos[$k]['type']) {
						case 'filegal':
							$infos[$k]['infos']['name'] = $infos[$k]['name'];
							$infos[$k]['infos']['parentId'] = $infos[$k]['parentId'];
    						break;

						case 'file':
							$infos[$k]['infos']['name'] = $infos[$k]['name'];
							$infos[$k]['infos']['filename'] = $infos[$k]['name'];
							$infos[$k]['infos']['galleryId'] = $infos[$k]['parentId'];
    						break;
					}

					$doRename = true;
				}
			} else {
			// If source doesn't exist, we stop here
				return false;
			}
		}

		$doMove = $infos['source']['parentId'] != $infos['dest']['parentId'];

		switch ($infos['source']['type']) {
			case 'filegal':
				// Duplicate
				$newId = $filegallib->duplicate_file_gallery($infos['source']['id'], $infos['dest']['name']);

				if (((bool) $newId) !== true) {
					return false;
				}

				if ($doMove) {
					// Move in an other gallery
					return (bool) $filegallib->move_file_gallery(
									$newId,
									$infos['dest']['parentId']
					);
				}

				return true;

			case 'file':
				if ($prefs['fgal_use_db'] === 'n') {
					$newPath = md5($infos['dest']['name']);
					do {
						$newPath = md5(uniqid($newPath));
					} while (file_exists($this->root . '/' . $newPath));

					if (@copy($this->root . '/' . $infos['source']['infos']['path'], $this->root . '/' . $newPath) === false) {
						return false;
					}
				} else {
					$newPath = '';
				}

				$newId = $filegallib->insert_file(
								$infos['source']['parentId'],
								$infos['dest']['name'],
								$infos['source']['infos']['description'],
								$infos['dest']['name'],
								$infos['source']['infos']['data'],
								$infos['source']['infos']['filesize'],
								$infos['source']['infos']['filetype'],
								$user,
								$newPath,
								'',
								$infos['source']['infos']['author'],
								$infos['source']['infos']['created'],
								$infos['source']['infos']['lockedby']
				);

				if (((bool) $newId) !== true) {
					return false;
				}

				if ($doMove) {
					return (bool) $filegallib->set_file_gallery(
									$newId,
									$infos['dest']['parentId']
					);
				}

				return true;
		}

		return false;
	}

	protected function performDelete($path)
	{
		global $filegallib; include_once('lib/filegals/filegallib.php');

		if (($objectId = $filegallib->get_objectid_from_virtual_path($path)) === false)
			return false;

		switch ($objectId['type']) {
			case 'file':
					return (bool) $filegallib->remove_file($filegallib->get_file($objectId['id']));

			case 'filegal':
					return (bool) $filegallib->remove_file_gallery($objectId['id']);
		}

		return false;
	}

	protected function nodeExists($path)
	{
		global $filegallib; include_once('lib/filegals/filegallib.php');
		if ($path == '/Wiki Pages/') {
			return true;
		}
		return $filegallib->get_objectid_from_virtual_path($path) !== false;
	}

	protected function isCollection($path)
	{
		global $filegallib; include_once('lib/filegals/filegallib.php');
		if ($path == '/Wiki Pages/') {
			return true;
		}
		return ($objectId = $filegallib->get_objectid_from_virtual_path($path)) !== false && $objectId['type'] == 'filegal';
	}

	protected function getCollectionMembers($path)
	{
		global $tikilib;
		global $filegallib; include_once('lib/filegals/filegallib.php');

		$contents = array();
		$errors = array();


		$galleryId = ($objectId = $filegallib->get_objectid_from_virtual_path($path)) !== false ? $objectId['id'] : false;

		print_debug("-> getCollectionMembers\ngalleryId:$galleryId\n");
		if ($galleryId !== false) {
			if ($gal_info = $filegallib->get_file_gallery((int)$galleryId)) {
				$tikilib->get_perm_object($galleryId, 'file gallery', $gal_info);
			}

			$files = $filegallib->get_files(
							0,
							-1,
							'name_desc',
							'',
							(int)$galleryId,
							true,
							true,
							false,
							true,
							false,
							false,
							false,
							false,
							'',
							true,
							false,
							($gal_info['show_backlinks']!='n'),
							'',
							''
			);


			foreach ($files['data'] as $fileInfo) {
				if ($fileInfo['isgal'] == '1') {
					// Add collection without any children
					$contents[] = new ezcWebdavCollection($path . $fileInfo['name'] . '/');
				} else {
					// Add files without content
					//$contents[] = new ezcWebdavResource($path . $fileInfo['name'] . ($fileInfo['nbArchives'] > 0 ? "?".$fileInfo['nbArchives'] : ''));
					$contents[] = new ezcWebdavResource($path . $fileInfo['filename']);
				}
			}
		}
		if ($galleryId == -1) {
			$contents[] = new ezcWebdavCollection('/Wiki Pages/');
		}

		print_debug("getCollectionMembers ".print_r($contents, true). "\n");
		return $contents;
	}

	public function get(ezcWebdavGetRequest $request)
	{
		print_debug("-- HTTP method: GET --\n");
		$return = parent::get($request);

		return $return;
	}

	public function head(ezcWebdavHeadRequest $request)
	{
		print_debug("-- HTTP method: HEAD --\n");
		$return = parent::head($request);

		return $return;
	}

	public function propFind(ezcWebdavPropFindRequest $request)
	{
		print_debug("-- HTTP method: PROPFIND --\n");
		$return = parent::propFind($request);

		return $return;
	}

	public function propPatch(ezcWebdavPropPatchRequest $request)
	{
		print_debug("-- HTTP method: PROPPATCH --\n");
		$this->acquireLock();
		$return = parent::propPatch($request);
		$this->freeLock();

		return $return;
	}

	public function put(ezcWebdavPutRequest $request)
	{
		print_debug("-- HTTP method: PUT --\n");
		$this->acquireLock();
		$return = parent::put($request);
		$this->freeLock();

		return $return;
	}

	public function delete(ezcWebdavDeleteRequest $request)
	{
		print_debug("-- HTTP method: DELETE --".print_r($request, true)."\n");
		$this->acquireLock();
		$return = parent::delete($request);
		$this->freeLock();

		return $return;
	}

	public function copy(ezcWebdavCopyRequest $request)
	{
		print_debug("-- HTTP method: COPY --\n");
		$this->acquireLock();
		$return = parent::copy($request);
		$this->freeLock();

		return $return;
	}

	public function move(ezcWebdavMoveRequest $request)
	{
		global $prefs;
		global $filegallib; include_once('lib/filegals/filegallib.php');

		print_debug("-- HTTP method: MOVE --\n");

		$this->acquireLock();

		// Indicates wheather a destiantion resource has been replaced or not.
		// The success response code depends on this.
		$replaced = false;

		// Extract paths from request
		$source = $request->requestUri;
		$dest = $request->getHeader('Destination');

		// Check authorization
		// Need to do this before checking of node existence is checked, to
		// avoid leaking information

		if (!ezcWebdavServer::getInstance()->isAuthorized($dest, $request->getHeader('Authorization'), ezcWebdavAuthorizer::ACCESS_WRITE)) {
			$this->freeLock();
			return $this->createUnauthorizedResponse(
							$dest,
							$request->getHeader('Authorization')
			);
		}

		// Check if resource is available
		if (!$this->nodeExists($source)) {
			$this->freeLock();
			return new ezcWebdavErrorResponse(
							ezcWebdavResponse::STATUS_404,
							$source
			);
		}

		// If source and destination are equal, the request should always fail.
		if ($source === $dest) {
			$this->freeLock();
			return new ezcWebdavErrorResponse(
							ezcWebdavResponse::STATUS_403,
							$source
			);
		}

		// Check if destination resource exists and throw error, when
		// overwrite header is F
		if (($request->getHeader('Overwrite') === 'F') &&
				$this->nodeExists($dest)) {
			$this->freeLock();
			return new ezcWebdavErrorResponse(
							ezcWebdavResponse::STATUS_412,
							$dest
			);
		}

		// Check if the destination parent directory already exists, otherwise
		// bail out.
		if (!$this->nodeExists($destDir = dirname($dest))) {
			$this->freeLock();
			return new ezcWebdavErrorResponse(
							ezcWebdavResponse::STATUS_409,
							$dest
			);
		}

		// Verify If-[None-]Match headers on the $dest if it exists
		if ($this->nodeExists($dest) &&
				($res = $this->checkIfMatchHeaders($request, $dest)) !== null
			) {
			$this->freeLock();
			return $res;
		} elseif (($res = $this->checkIfMatchHeaders($request, $destDir)) !== null) {
		// Verify If-[None-]Match headers on the on $dests parent dir, if it
		// does not exist
			$this->freeLock();
			return $res;
		}

		// The destination resource should be deleted if it exists and the
		// overwrite headers is T
		if (($request->getHeader('Overwrite') === 'T') &&
				$this->nodeExists($dest)) {
			// Check sub-sequent authorization on destination
			$authState = $this->recursiveAuthCheck(
							$request,
							$dest,
							ezcWebdavAuthorizer::ACCESS_WRITE,
							true
			);
			if (count($authState['errors']) !== 0) {
				// Permission denied on deleting destination
				$this->freeLock();
				return $authState['errors'][0];
			}

			$replaced = true;

			if (count($delteErrors = $this->performDelete($dest)) > 0) {
				$this->freeLock();
				return new ezcWebdavMultistatusResponse($delteErrors);
			}
		}

		// All checks are passed, we can actually move now.

		$infos = array();
		$doRename = false;
		$doMove = false;

		foreach (array('source', 'dest') as $k) {
			// Get source and dest infos
			if (($infos[$k] = $filegallib->get_objectid_from_virtual_path($$k)) !== false) {
				switch ($infos[$k]['type']) {
					case 'filegal':
						$infos[$k]['infos'] = $filegallib->get_file_gallery_info($infos[$k]['id']);
						$infos[$k]['parentId'] = $infos[$k]['infos']['parentId'];
						$infos[$k]['name'] = $infos[$k]['infos']['name'];
    					break;

					case 'file':
						///TODO: Throw an error if dest is a file, but source is a filegal

						$infos[$k]['infos'] = $filegallib->get_file($infos[$k]['id']);
						$infos[$k]['parentId'] = $infos[$k]['infos']['galleryId'];
						$infos[$k]['name'] = $infos[$k]['infos']['filename'];
    					break;
				}
			} elseif ($k == 'dest') {
			// If dest doesn't exist, it usually means that the file / filegal has to be renamed
				///TODO: Throw an error if dest is a new filegal, but source is a file

				if (($objectId = $filegallib->get_objectid_from_virtual_path(dirname($$k))) !== false
						&& $objectId['type'] == 'filegal'
					) {
					$infos[$k] = array(
							'id' => $infos['source']['id'],
							'type' => $infos['source']['type'],
							'infos' => $infos['source']['infos'],
							'parentId' => $objectId['id'],
							'name' => basename($$k)
							);

					switch ($infos[$k]['type']) {
						case 'filegal':
							$infos[$k]['infos']['name'] = $infos[$k]['name'];
							$infos[$k]['infos']['parentId'] = $infos[$k]['parentId'];
    						break;

						case 'file':
							$infos[$k]['infos']['name'] = $infos[$k]['name'];
							$infos[$k]['infos']['filename'] = $infos[$k]['name'];
							$infos[$k]['infos']['galleryId'] = $infos[$k]['parentId'];
    						break;
					}

					$doRename = true;
				}
			} else {
			// If source doesn't exist, we stop here
				break;
			}
		}

		$doMove = $infos['source']['parentId'] != $infos['dest']['parentId'];
		$noErrors = true;

		switch ($infos['source']['type']) {
			case 'filegal':

				if ($doRename) {
					$noErrors = (bool) $filegallib->replace_file_gallery(
									$infos['dest']['infos']
					);
				} elseif ($doMove) {
				// Move is not needed if the rename occurred, since filegal renaming function handle the move already
					$noErrors = (bool) $filegallib->move_file_gallery(
									$infos['source']['id'],
									$infos['dest']['parentId']
					);
				}

    			break;

			case 'file':

				if ($doRename) {
					if ($prefs['fgal_use_db'] === 'n') {
						$newPath = md5($infos['dest']['name']);
						do {
							$newPath = md5(uniqid($newPath));
						} while (file_exists($this->root . '/' . $newPath));

						if ((@rename($this->root . '/' . $infos['source']['infos']['path'], $this->root . '/' . $newPath) === false)
								|| (@file_put_contents($this->root . '/' . $infos['source']['infos']['path'], '') === false)
							) {
							$this->freeLock();
							return false;
						}
					} else {
						$newPath = '';
					}

					$noErrors = (bool) $filegallib->replace_file(
									$infos['source']['id'],
									$infos['dest']['name'],
									$infos['source']['infos']['description'],
									$infos['dest']['name'],
									$infos['source']['infos']['data'],
									$infos['source']['infos']['filesize'],
									$infos['source']['infos']['filetype'],
									$user,
									$newPath,
									'',
									$filegallib->get_file_gallery_info($infos['source']['parentId']),
									false,
									$infos['source']['infos']['author'],
									$infos['source']['infos']['created'],
									$infos['source']['infos']['lockedby']
					);
				}

				if ($doMove && $noErrors) {
					$noErrors = (bool) $filegallib->set_file_gallery(
									$infos['source']['id'],
									$infos['dest']['parentId']
					);
				}

    			break;
		}
		$this->freeLock();

		// Send proper response on success
		if ($noErrors) {
			$return = new ezcWebdavMoveResponse(
							$replaced
			);
		} else {
			$return = new ezcWebdavErrorResponse(
							ezcWebdavResponse::STATUS_500
			);
		}


		print_debug("-- HTTP method: MOVE end --\n");
		return $return;
	}

	public function makeCollection(ezcWebdavMakeCollectionRequest $request)
	{
		print_debug("-- HTTP method: MAKECOL --\n");
		$this->acquireLock();
		$return = parent::makeCollection($request);
		$this->freeLock();

		return $return;
	}
}
