<?php
// (c) Copyright 2002-2010 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 32809 2011-02-15 22:16:42Z pkdille $

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, $tikilib;
		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, $tikilib, $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 = '';

		include_once('lib/mime/mimelib.php');
		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 = tiki_get_mime($name, 'application/octet-stream', $this->root . '/' . $fhash);
		} else {
			$fhash = '';
			$mime = ($objectId['type'] != 'file') ? 'inode/directory' : tiki_get_mime_from_content($content);
		}

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

	protected function setResourceContents( $path, $content )
	{
		global $user, $tikilib, $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;
		}

		include_once('lib/mime/mimelib.php');
		$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 = tiki_get_mime($name,  'application/octet-stream', $this->root . '/' . $fhash);
		} else {
			$fhash = '';
			$mime = ($objectId['type'] != 'file') ? 'inode/directory' : tiki_get_mime_from_content($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 $tikilib;
		global $filegallib; require_once('lib/filegals/filegallib.php');

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

		if ( $objectId !== false && $objectId['type'] == 'file' )
		{
			$fileInfo = $tikilib->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");
		if ( @file_exists($storagePath = $this->options->propertyStoragePath.'/properties-'.md5($path)) ) {
			$xml = ezcWebdavServer::getInstance()->xmlTool->createDom( @file_get_contents($storagePath) );
		} else {
			$xml = ezcWebdavServer::getInstance()->xmlTool->createDom();
		}
		$handler = new ezcWebdavPropertyHandler(
				new ezcWebdavXmlTool()
				);
		try {
			$handler->extractProperties($xml->getElementsByTagNameNS('DAV:','*'),$this->propertyStorage);
		}
		catch ( Exception $e ) {
		}

		print_debug("getPropertyStorage method end\n");
		return $this->propertyStorage;
	}

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

		// 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 );

		// Store and store properties
		foreach ($this->handledLiveProperties as $propName) {
			$storage->detach($propName);
		}
		$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 $tikilib;
		global $filegallib; include_once('lib/filegals/filegallib.php');

		print_debug("GetProperty($path, $propertyName, $namespace)");
		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 {
				$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(
						$tikiInfo['name']
						);
				print_debug("-> " . $tikiInfo['name'] ."\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");
				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 )
	{
		print_debug("getContentLength $path". $this->getProperty( $path, 'getcontentlength' )->contentlength ."\n");
		return $this->getProperty( $path, 'getcontentlength' )->contentlength;
	}

	protected function getETag( $path )
	{
		if ( $etag = $this->getProperty( $path, 'getetag' ) ) {
			return $this->getProperty( $path, 'getetag' )->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 ) );
		}

		return $storage;
	}

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

		$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'] = $tikilib->get_file( $infos[$k]['id'] );
						$infos[$k]['parentId'] = $infos[$k]['infos']['galleryId'];
						$infos[$k]['name'] = $infos[$k]['infos']['filename'];
						break;
				}
			}
			// If dest doesn't exist, it usually means that the file / filegal has to be renamed
			elseif ( $k == 'dest' )
			{
				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;
				}
			}
			// If source doesn't exist, we stop here
			else
			{
				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');
		return $filegallib->get_objectid_from_virtual_path($path) !== false;
	}

	protected function isCollection( $path )
	{
		global $filegallib; include_once('lib/filegals/filegallib.php');
		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 ) {
			$files = $tikilib->get_files(0, -1, 'name_desc', '', (int)$galleryId, true, true, false, true, false, false, false, false, 'admin', true, false);

			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['name'] );
				}
			}
		}

		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 $tikilib, $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;
		}
		// Verify If-[None-]Match headers on the on $dests parent dir, if it
		// does not exist
		elseif ( ( $res = $this->checkIfMatchHeaders( $request, $destDir ) ) !== null )
		{
			$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'] = $tikilib->get_file( $infos[$k]['id'] );
						$infos[$k]['parentId'] = $infos[$k]['infos']['galleryId'];
						$infos[$k]['name'] = $infos[$k]['infos']['filename'];
						break;
				}
			}
			// If dest doesn't exist, it usually means that the file / filegal has to be renamed
			elseif ( $k == 'dest' )
			{
				///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;
				}
			}
			// If source doesn't exist, we stop here
			else
			{
				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']
							);
				}
				// Move is not needed if the rename occured, since filegal renaming function handle the move already
				elseif ( $doMove )
				{
					$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;
	}
}
