Location: PHPKode > scripts > Site Fusion > class/fileHandling.php.inc
<?php
// - - - - - - - - - - - - - BEGIN LICENSE BLOCK - - - - - - - - - - - - -
// Version: MPL 1.1/GPL 2.0/LGPL 2.1
//
// The contents of this file are subject to the Mozilla Public License Version
// 1.1 (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
// http://www.mozilla.org/MPL/
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// The Original Code is sitefusion.sourceforge.net code.
//
// The Initial Developer of the Original Code is
// FrontDoor Media Group.
// Portions created by the Initial Developer are Copyright (C) 2009
// the Initial Developer. All Rights Reserved.
//
// Contributor(s):
//   Nikki Auburger <hide@address.com> (original author)
//   Tom Peeters <hide@address.com>
//
// - - - - - - - - - - - - - - END LICENSE BLOCK - - - - - - - - - - - - -


/**
 * @package API
 * @subpackage FileHandling
*/


/**
 * OS-native filepicker dialog
 * 
 * Opening modes can be one of 'open', 'save', 'getfolder' or 'openmultiple'. This node needs to be
 * added to a window as a child first. Then the open() method can be called, after which the dialog
 * opens. You can set an event handler for the local event 'closed', at which time the property
 * XULFilePicker->returnCode will contain one of the strings 'ok', 'cancel' or 'replace', indicating
 * the action desired by the user. The property XULFilePicker->file will contain the selected file or
 * folder if the dialog was of mode 'save', 'open' or 'getfolder'. In the case of an 'openmultiple'
 * picker the property XULFilePicker->files will contain an array of the selected paths.
*/

class XULFilePicker extends Node
{
	public $remoteConstructor = 'FilePicker';
	public $file = NULL;
	public $files = NULL;
	public $returnCode = 'cancel';
	
	
	/**
	 * Constructor
	 * 
	 * @param string	$title			Title for the file picker dialog
	 * @param string	$mode			Type of file picker ('open' = open single file, 'save' = save a file, 'getfolder' = select a folder, 'openmultiple' = open multiple files
	 * @param string	$defaultString	Default value (file/folder path)
	*/
	
	public function __construct( $title, $mode = 'open', $defaultString = '' ) {
		$this->title = $title;
		$this->defaultString = (string) $defaultString;
		
		if( $mode == 'open' || $mode == 'save' || $mode == 'getfolder' || $mode == 'openmultiple' )
			$this->mode = $mode;
		else
			throw new SFException( 'Invalid mode', ERR_REPORT_APP );
		
		$this->setEventHandler( 'yield', $this, 'yieldCollect' );
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow, $this->title, $this->mode, $this->defaultString ) );
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Opens the file picker
	*/
	
	public function open() {
		$this->callMethod( 'open' );
	}

	
	/**
	 * Adds a file type filter
	 * 
	 * Example: $filePicker->addFilter("Images", "*.jpg; *.gif; *.png; *.");
	 *
	 * @param string	$description	Description of the filter
	 * @param string	$value			Value for the filter, semicolon separated
	*/
	
	public function addFilter($description, $value) {
		$this->callMethod( 'addFilter', array($description, $value) );
	}
	
	
	/**
	 * Event handler for the yield event
	*/
	
	public function yieldCollect( $e, $returnCode, $path ) {
		$args = func_get_args();
		$e = array_shift($args);
		$this->returnCode = array_shift($args);
		
		if( $this->mode == 'openmultiple' ) {
			$this->files = $args;
			$this->file = NULL;
			
			$this->fireLocalEvent( 'closed', array( $this->returnCode, $this->files ) );
		}
		else {
			$this->file = (count($args) ? array_shift($args) : NULL);
			$this->files = NULL;

			$this->fireLocalEvent( 'closed', array( $this->returnCode, $this->file ) );
		}
		
		$this->removeNode();
	}
}


/**
 * Background file uploader
 * 
 * This class creates an object that uploads a file from the client to the server.
 * Construct it with a source (client) path and destination path. Then call the 
 * method startUpload(). This control fires the
 * event 'finished' when the upload was successful, 'failed' when it went wrong or 
 * 'cancelled' when the upload was cancelled through the cancelUpload() method. It
 * also fires the event 'cycle' for every chunk received, allowing you to keep track
 * of progress.
 * 
 * @see XULFilePicker
*/

class FileUploader extends Node
{
	public $filePath;
	public $fileSize;
	public $fileName;
	public $destPath;
	public $clientPath;
	public $uploadedPath = NULL;
	public $remoteConstructor = 'FileUploader';
	
	
	/**
	 * Constructor
	 *
	 * @param string	$clientPath		Source file path on the client side
	 * @param string	$destPath		Destination file path on the server side
	*/
	
	public function __construct( $clientPath = NULL, $destPath = NULL ) {
		if( $clientPath ) $this->clientPath = $clientPath;
		if( $destPath ) $this->destPath = $destPath;
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		
		$this->setEvent( 'cycle', MSG_SEND, $this, 'handleFileChunk' );
		$this->setEvent( 'cancelled', MSG_SEND, $this, 'handleError' );
		$this->setEvent( 'failed', MSG_SEND, $this, 'handleError' );
		$this->setEventType( 'finished', MSG_SEND );
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the source and destination paths
	 *
	 * @param string	$clientPath		Source file path on the client side
	 * @param string	$destPath		Destination file path on the server side
	*/
	
	public function setPaths( $clientPath, $destPath ) {
		$this->clientPath = $clientPath;
		$this->destPath = $destPath;
	}
	
	
	/**
	 * Start the upload
	 * 
	 * Starts the uploader which fires the 'started' event beforehand
	*/
	
	public function startUpload() {
		if( (!isset($this->destPath)) || (!isset($this->clientPath)) )
			throw new SFException( 'No source or destination file path given. Use setPaths()', ERR_REPORT_APP );
		
		$this->callMethod( 'startUpload', array( $this->clientPath ) );
	}
	
	
	/**
	 * Cancel a running upload
	 * 
	 * Cancels the uploader which fires the 'cancelled' event
	*/
	
	public function cancelUpload() {
		$this->callMethod( 'cancelUpload' );
	}
	
	
	/**
	 * Event handler for the 'failed' and 'cancelled' events
	*/
	
	public function handleError( $e, $remotePath ) {
		$path = $this->destPath;
		
		if( file_exists($path) )
			@unlink( $path );
	}
	
	
	/**
	 * Event handler for the 'cycle' event
	*/
	
	public function handleFileChunk( $e, $filesize, $progress, $cycle, $data ) {
		$path = $this->destPath;
		
		if( file_exists($path) && $cycle == 1 )
			@unlink( $path );
		
		$file = fopen( $path, 'a' );
		fwrite( $file, base64_decode($data) );
		fclose( $file );
		
		$this->uploadedPath = $path;
		
		$this->filePath = dirname($path);
		$this->fileName = basename($path);
		clearstatcache();
		$this->fileSize = filesize( $path );
	}
}



/**
 * Background file-to-memory uploader
 * 
 * This class creates an object that uploads a file from the client to the server memory.
 * Construct it with a source (client) path. Then call the method startUpload(). This control fires the
 * event 'finished' when the upload was successful, 'failed' when it went wrong or 
 * 'cancelled' when the upload was cancelled through the cancelUpload() method. It
 * also fires the event 'cycle' for every chunk received, allowing you to keep track
 * of progress. The resulting file contents are placed in the FileToStringUploader::$data property.
 * 
 * @see XULFilePicker
*/

class FileToStringUploader extends Node
{
	public $data = NULL;
	public $bufferSize = NULL;
	public $fileSize = NULL;
	public $clientPath;
	public $remoteConstructor = 'FileUploader';
	
	
	/**
	 * Constructor
	 *
	 * @param string	$clientPath		Source file path on the client side
	*/
	
	public function __construct( $clientPath = NULL ) {
		if( $clientPath ) $this->clientPath = $clientPath;
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		
		$this->setEvent( 'cycle', MSG_SEND, $this, 'handleFileChunk' );
		$this->setEvent( 'cancelled', MSG_SEND, $this, 'handleError' );
		$this->setEvent( 'failed', MSG_SEND, $this, 'handleError' );
		$this->setEventType( 'finished', MSG_SEND );
	}

	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the source path
	 *
	 * @param string	$clientPath		Source file path on the client side
	*/
	
	public function setSourcePath( $clientPath ) {
		$this->clientPath = $clientPath;
	}
	
	
	/**
	 * Start the upload
	 * 
	 * Starts the uploader which fires the 'started' event beforehand
	*/
	
	public function startUpload() {
		if( (!isset($this->clientPath)) )
			throw new SFException( 'No source path given. Use setSourcePath()', ERR_REPORT_APP );
		
		$this->callMethod( 'startUpload', array( $this->clientPath ) );
	}
	
	
	/**
	 * Cancel a running upload
	 * 
	 * Cancels the uploader which fires the 'cancelled' event
	*/
	
	public function cancelUpload() {
		$this->callMethod( 'cancelUpload' );
	}
	
	
	/**
	 * Event handler for the 'failed' and 'cancelled' events
	*/
	
	public function handleError( $e, $remotePath ) {
		$this->data = NULL;
		$this->bufferSize = NULL;
		$this->fileSize = NULL;
	}
	
	
	/**
	 * Event handler for the 'cycle' event
	*/
	
	public function handleFileChunk( $e, $filesize, $progress, $cycle, $data ) {
		if( $this->data === NULL )
			$this->data = '';
		
		$this->data .= base64_decode($data);
		$this->bufferSize = strlen( $this->data );
		$this->fileSize = $filesize;
	}
}



/**
 * Background file-to-stream uploader
 * 
 * This class creates an object that uploads a file from the client to a stream on the server.
 * Construct it with a source (client) path and a stream resource. Then call the method startUpload(). This control fires the
 * event 'finished' when the upload was successful, 'failed' when it went wrong or 
 * 'cancelled' when the upload was cancelled through the cancelUpload() method. It
 * also fires the event 'cycle' for every chunk received, allowing you to keep track
 * of progress.
 *
 * @see XULFilePicker
*/

class FileToStreamUploader extends Node
{
	public $stream = NULL;
	public $fileSize = NULL;
	public $clientPath;
	public $remoteConstructor = 'FileUploader';
	
	
	/**
	 * Constructor
	 *
	 * @param string	$clientPath		Source file path on the client side
	 * @param resource	$stream			Stream to write data to
	*/
	
	public function __construct( $clientPath = NULL, $stream = NULL ) {
		if( $clientPath ) $this->clientPath = $clientPath;
		if( $stream ) $this->setStream( $stream );
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		
		$this->setEvent( 'cycle', MSG_SEND, $this, 'handleFileChunk' );
		$this->setEvent( 'cancelled', MSG_SEND, $this, 'handleError' );
		$this->setEvent( 'failed', MSG_SEND, $this, 'handleError' );
		$this->setEventType( 'finished', MSG_SEND );
	}

	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the source path
	 *
	 * @param string	$clientPath		Source file path on the client side
	*/
	
	public function setSourcePath( $clientPath ) {
		$this->clientPath = $clientPath;
	}
	
	
	/**
	 * Set the stream to write to
	 *
	 * @param resource	$stream		Stream to write data to
	*/
	
	public function setStream( $stream ) {
		if( ! is_resource($stream) )
			throw new SFException( "setStream(): not a stream resource", ERR_REPORT_APP );
		
		$this->stream = $stream;
	}
	
	
	/**
	 * Start the upload
	 * 
	 * Starts the uploader which fires the 'started' event beforehand
	*/
	
	public function startUpload() {
		if( (!isset($this->clientPath)) || (!is_resource($this->stream)) )
			throw new SFException( 'No source path given or no stream set. Use setSourcePath() and setStream()', ERR_REPORT_APP );
		
		$this->callMethod( 'startUpload', array( $this->clientPath ) );
	}
	
	
	/**
	 * Cancel a running upload
	 * 
	 * Cancels the uploader which fires the 'cancelled' event
	*/
	
	public function cancelUpload() {
		$this->callMethod( 'cancelUpload' );
	}
	
	
	/**
	 * Event handler for the 'failed' and 'cancelled' events
	*/
	
	public function handleError( $e, $remotePath ) {
		$this->fileSize = NULL;
	}
	
	
	/**
	 * Event handler for the 'cycle' event
	*/
	
	public function handleFileChunk( $e, $filesize, $progress, $cycle, $data ) {
		fwrite( $this->stream, base64_decode($data) );
		$this->fileSize = $filesize;
	}
}


/**
 * Background file downloader
 * 
 * This class constructs a node that downloads a file from the server to the client in 
 * the background. Construct it with the local (server) path as the first argument and
 * the remote (client) path as the second argument, and call startDownload(). This control
 * fires a 'started' event when the download starts, a 'cycle' event periodically with progress
 * information, a 'finished' event when the download is finished and a 'cancelled' event when
 * the download is cancelled.
 * 
 * @see XULFilePicker
*/

class FileDownloader extends Node
{
	public $filePath;
	public $destPath;
	public $remoteConstructor = 'FileDownloader';
	public $removeAfterTransfer = FALSE;
	
	private $transferStarted = FALSE;
	private $transferSize = NULL;
	private $transferFile = NULL;
	
	
	/**
	 * Constructor
	 *
	 * @param string	$filePath		Source file path on the server side
	 * @param string	$destPath		Destination file path on the client side
	*/
	
	public function __construct( $filePath = NULL, $destPath = NULL ) {
		if( $filePath ) $this->filePath = $filePath;
		if( $destPath ) $this->destPath = $destPath;
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		$this->setEventType( 'finished', MSG_SEND );
		$this->setEventType( 'cancelled', MSG_SEND );
		$this->setEventType( 'failed', MSG_SEND );
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the source and destination paths
	 *
	 * @param string	$filePath		Source file path on the server side
	 * @param string	$destPath		Destination file path on the client side
	*/
	
	public function setPaths( $filePath, $destPath ) {
		$this->filePath = $filePath;
		$this->destPath = $destPath;
	}
	
	
	/**
	 * Cancel a running download
	 * 
	 * Cancels the downloader which fires the 'cancelled' event
	*/
	
	public function cancelDownload() {
		$this->callMethod( 'cancelDownload' );
	}
	
	
	/**
	 * Start the download
	 * 
	 * Starts the downloader which fires the 'started' event beforehand
	*/
	
	public function startDownload() {
		if( (!isset($this->destPath)) || (!isset($this->filePath)) )
			throw new SFException( 'No file path or name given. Use setPaths()', ERR_REPORT_APP );
		
		$this->callMethod( 'startDownload', array( $this->destPath ) );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to start a transfer. It returns an array
	 * of two elements, first is a string with the MIME content-type of the data, and
	 * second is a integer number containing the size in bytes of the transfer.
	 * 
	 * @return array	Transfer descriptives
	*/
	
	public function transferStart() {
		$this->transferStarted = TRUE;
		$this->transferSize = filesize($this->filePath);
		$this->transferFile = fopen( $this->filePath, 'r' );
		
		return array( 'application/octet-stream', $this->transferSize );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to get (a piece of) data of a transfer.
	 * It should output this data to the output buffer, which will be intercepted
	 * by the daemon.
	*/
	
	public function transferGetData() {
		if( !$this->transferStarted )
			throw new SFException( "File transfer not started!" );
		
		echo fread( $this->transferFile, 8192 );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon when all data has been transferred.
	 * It is used to close open filehandles and clean up.
	*/
	
	public function transferEnd() {
		fclose( $this->transferFile );
		$this->transferStarted = FALSE;
		$this->transferSize = NULL;
		$this->transferFile = NULL;
		
		if( $this->removeAfterTransfer )
			unlink( $this->filePath );
	}
}


/**
 * Background file-from-memory downloader
 * 
 * This class constructs a node that downloads from server memory to a file on the client in 
 * the background. Construct it with the remote (client) destination path as the first argument and
 * the data string as the second, then call startDownload(). This control
 * fires a 'started' event when the download starts, a 'cycle' event periodically with progress
 * information, a 'finished' event when the download is finished and a 'cancelled' event when
 * the download is cancelled.
 * 
 * @see XULFilePicker
*/

class FileFromStringDownloader extends Node
{
	public $destPath;
	public $data = NULL;
	public $dataPos = NULL;
	public $remoteConstructor = 'FileDownloader';
	
	private $transferStarted = FALSE;
	private $transferSize = NULL;
	
	
	/**
	 * Constructor
	 *
	 * @param string	$destPath		Destination file path on the client side
	 * @param string	$data			Data to store in the file
	*/
	
	public function __construct( $destPath = NULL, $data = NULL ) {
		if( $destPath !== NULL ) $this->destPath = $destPath;
		if( $data !== NULL ) $this->data = (string) $data;
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		$this->setEventType( 'finished', MSG_SEND );
		$this->setEventType( 'cancelled', MSG_SEND );
		$this->setEventType( 'failed', MSG_SEND );
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the destination path
	 *
	 * @param string	$destPath		Destination file path on the client side
	*/
	
	public function setDestinationPath( $destPath ) {
		$this->destPath = $destPath;
	}
	
	
	/**
	 * Set the data to store in the file
	 *
	 * @param string	$data		Data to store in the file
	*/
	
	public function setData( $data ) {
		$this->data = (string)$data;
	}
	
	
	/**
	 * Cancel a running download
	 * 
	 * Cancels the downloader which fires the 'cancelled' event
	*/
	
	public function cancelDownload() {
		$this->callMethod( 'cancelDownload' );
	}
	
	
	/**
	 * Start the download
	 * 
	 * Starts the downloader which fires the 'started' event beforehand
	*/
	
	public function startDownload() {
		if( (!isset($this->destPath)) || $this->data === NULL )
			throw new SFException( 'No destination path or data. Use setDestinationPath() and setData()', ERR_REPORT_APP );
		
		$this->callMethod( 'startDownload', array( $this->destPath ) );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to start a transfer. It returns an array
	 * of two elements, first is a string with the MIME content-type of the data, and
	 * second is a integer number containing the size in bytes of the transfer.
	 * 
	 * @return array	Transfer descriptives
	*/
	
	public function transferStart() {
		$this->transferStarted = TRUE;
		$this->transferSize = strlen($this->data);
		$this->dataPos = 0;
		
		return array( 'application/octet-stream', $this->transferSize );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to get (a piece of) data of a transfer.
	 * It should output this data to the output buffer, which will be intercepted
	 * by the daemon.
	*/
	
	public function transferGetData() {
		if( !$this->transferStarted )
			throw new SFException( "File transfer not started!" );
		
		echo substr( $this->data, $this->dataPos, ($len = min(8192,$this->transferSize-$this->dataPos)) );
		$this->dataPos += $len;
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon when all data has been transferred.
	 * It is used to close open filehandles and clean up.
	*/
	
	public function transferEnd() {
		$this->transferStarted = FALSE;
		$this->transferSize = NULL;
		$this->data = NULL;
		$this->dataPos = NULL;
	}
}



/**
 * Background file-from-stream downloader
 * 
 * This class constructs a node that downloads from a stream resource on the server to a file on the client in 
 * the background. Construct it with the remote (client) destination path as the first argument,
 * the data stream as the second and the amount of bytes to read as the third, then call startDownload(). This control
 * fires a 'started' event when the download starts, a 'cycle' event periodically with progress
 * information, a 'finished' event when the download is finished and a 'cancelled' event when
 * the download is cancelled.
 * 
 * @see XULFilePicker
*/

class FileFromStreamDownloader extends Node
{
	public $destPath;
	public $stream = NULL;
	public $length = NULL;
	public $dataPos = NULL;
	public $remoteConstructor = 'FileDownloader';
	
	private $transferStarted = FALSE;
	private $transferSize = NULL;
	
	
	/**
	 * Constructor
	 *
	 * @param string	$destPath		Destination file path on the client side
	 * @param resource	$stream			Stream to read from
	 * @param int		$length			Amount of bytes to read from the stream and write to the file
	*/
	
	public function __construct( $destPath = NULL, $stream = NULL, $length = NULL ) {
		if( $destPath !== NULL ) $this->destPath = $destPath;
		if( $stream !== NULL ) $this->setStream( $stream, (int) $length );
		
		parent::__construct();
	}
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		$this->setEventType( 'finished', MSG_SEND );
		$this->setEventType( 'cancelled', MSG_SEND );
		$this->setEventType( 'failed', MSG_SEND );
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Set the destination path
	 *
	 * @param string	$destPath		Destination file path on the client side
	*/
	
	public function setDestinationPath( $destPath ) {
		$this->destPath = $destPath;
	}
	
	
	/**
	 * Set the stream to read from
	 *
	 * @param resource	$stream		Stream to read from
 	 * @param int		$length		Amount of bytes to read from the stream and write to the file
	*/
	
	public function setStream( $stream, $length ) {
		if( ! is_resource($stream) )
			throw new SFException( "setStream(): not a stream resource", ERR_REPORT_APP );
		
		$this->stream = $stream;
		$this->length = (int) $length;
	}
	
	
	/**
	 * Cancel a running download
	 * 
	 * Cancels the downloader which fires the 'cancelled' event
	*/
	
	public function cancelDownload() {
		$this->callMethod( 'cancelDownload' );
	}
	
	
	/**
	 * Start the download
	 * 
	 * Starts the downloader which fires the 'started' event beforehand
	*/
	
	public function startDownload() {
		if( (!isset($this->destPath)) || $this->data === NULL )
			throw new SFException( 'No destination path or data. Use setDestinationPath() and setData()', ERR_REPORT_APP );
		
		$this->callMethod( 'startDownload', array( $this->destPath ) );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to start a transfer. It returns an array
	 * of two elements, first is a string with the MIME content-type of the data, and
	 * second is a integer number containing the size in bytes of the transfer.
	 * 
	 * @return array	Transfer descriptives
	*/
	
	public function transferStart() {
		$this->transferStarted = TRUE;
		$this->transferSize = $this->length;
		$this->dataPos = 0;
		
		return array( 'application/octet-stream', $this->transferSize );
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon to get (a piece of) data of a transfer.
	 * It should output this data to the output buffer, which will be intercepted
	 * by the daemon.
	*/
	
	public function transferGetData() {
		if( !$this->transferStarted )
			throw new SFException( "File transfer not started!" );
		
		echo fread( $this->stream, ($len = min(8192,$this->transferSize-$this->dataPos)) );
		$this->dataPos += $len;
	}
	
	
	/**
	 * [INTERNAL FUNCTION]
	 * This function is called by the daemon when all data has been transferred.
	 * It is used to close open filehandles and clean up.
	*/
	
	public function transferEnd() {
		$this->transferStarted = FALSE;
		$this->transferSize = NULL;
		$this->stream = NULL;
		$this->dataPos = NULL;
	}
}



/**
 * Client filesystem service
 * 
 * This class is used to access the client filesystem, to read and write
 * to directories and files, to run executables and to monitor files.
 * All operations are asynchronous, meaning that the call will not block
 * the script. A handler object and method can be given to handle the
 * result of the operation.
*/

class ClientFileService extends Node
{
	public $remoteConstructor = 'FileService';
	private $handlers = array();
	private $fileMonitors = array();
	
	public function attach() {
		$this->createRemoteObject( array( $this->hostWindow ) );
		$this->setEvent( 'result', MSG_SEND, $this, 'onResult' );
	}
	
	public function detach() {
		$this->callMethod( 'cancelAllMonitors' );
		$this->unRegister();
	}
	
	
	/**
	 * Gets the contents of a directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - ClientDirectory $dirFile: if successful, a file object referring to the remote directory with the directory contents in the ClientFile::$entries array
	 *
	 * @param string		$path				The path on the client side to read
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function getDirectory( $path, $handlerObj, $handlerMethod ) {
		$this->handlers[] = array( $handlerObj, $handlerMethod );
		$this->callMethod( 'getDirectory', array( $path ) );
	}
	
	
	/**
	 * Gets the contents of a special directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService: this object
	 * - bool $status: indicates whether the operation was successful
	 * - ClientDirectory $dirFile: if successful, a file object referring to the remote directory with the directory contents in the ClientFile::$entries array
	 *
	 * @link https://developer.mozilla.org/en/Code_snippets/File_I%2F%2FO#Getting_special_files
	 * @param string		$id					ID of the special folder, see link
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function getSpecialDirectory( $id, $handlerObj, $handlerMethod ) {
		$this->handlers[] = array( $handlerObj, $handlerMethod );
		$this->callMethod( 'getSpecialDirectory', array( $id ) );
	}
	
	
	/**
	 * Creates the directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - ClientDirectory $dirFile: if successful, a file object referring to the new remote directory
	 *
	 * @param string		$path				The path on the client side to create
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function createDirectory( $path, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->handlers[] = ($handlerObj&&$handlerMethod ? array( $handlerObj, $handlerMethod ) : NULL);
		$this->callMethod( 'createDirectory', array( $path ) );
	}
	
	
	/**
	 * Copies a file from server to client
	 * 
	 * Returns the FileDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileDownloader.
	 *
	 * @param string		$localPath			The path on the server side
	 * @param string		$clientPath			The path on the client side
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function writeFile( $localPath, $clientPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fdn = new FileDownloader( $localPath, $clientPath ) );
		if( $handlerObj && $handlerMethod ) {
			$fdn->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fdn->setEventHandler( 'finished', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'failed', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'cancelled', $this, 'onDownloaderFinished' );
		$fdn->startDownload();
		
		return $fdn;
	}
	
	
	/**
	 * Stores a string in a file on the client side
	 * 
	 * Returns the FileFromStringDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileFromStringDownloader.
	 *
	 * @param string		$clientPath			The path on the client side
	 * @param string		$data				Data to write to the file
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileFromStringDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function writeFileFromString( $clientPath, $data, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fdn = new FileFromStringDownloader( $clientPath, $data ) );
		if( $handlerObj && $handlerMethod ) {
			$fdn->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fdn->setEventHandler( 'finished', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'failed', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'cancelled', $this, 'onDownloaderFinished' );
		$fdn->startDownload();
		
		return $fdn;
	}
	
	
	/**
	 * Stores data read from a stream in a file on the client side
	 * 
	 * Returns the FileFromStreamDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileFromStringDownloader.
	 *
	 * @param string		$clientPath			The path on the client side
	 * @param resource		$stream				Stream to read from
	 * @param int			$length				Amount of bytes to read from the stream and write to the file
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileFromStringDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function writeFileFromStream( $clientPath, $stream, $length, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fdn = new FileFromStreamDownloader( $clientPath, $stream, $length ) );
		if( $handlerObj && $handlerMethod ) {
			$fdn->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fdn->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fdn->setEventHandler( 'finished', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'failed', $this, 'onDownloaderFinished' );
		$fdn->setEventHandler( 'cancelled', $this, 'onDownloaderFinished' );
		$fdn->startDownload();
		
		return $fdn;
	}
	
	
	/**
	 * [INTERNAL FUNCTION] Event handler
	*/
	
	public function onDownloaderFinished( $event ) {
		if($event->sourceObject->isChild)
			$event->sourceObject->removeNode();
	}
	
	
	/**
	 * Copies a file from client to server
	 * 
	 * Returns the FileUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileUploader.
	 *
	 * @param string		$clientPath			The path on the client side
	 * @param string		$localPath			The path on the server side
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function readFile( $clientPath, $localPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fup = new FileUploader( $clientPath, $localPath ) );
		if( $handlerObj && $handlerMethod ) {
			$fup->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fup->setEventHandler( 'finished', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'failed', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'cancelled', $this, 'onUploaderFinished' );
		$fup->startUpload();
		
		return $fup;
	}
	
	
	/**
	 * Reads a file on the client side to a string
	 * 
	 * Returns the FileToStringUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileToStringUploader. The file contents are put in the FileToStringUploader::$data property
	 *
	 * @param string		$clientPath			The path on the client side
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileToStringUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function readFileToString( $clientPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fup = new FileToStringUploader( $clientPath ) );
		if( $handlerObj && $handlerMethod ) {
			$fup->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fup->setEventHandler( 'finished', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'failed', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'cancelled', $this, 'onUploaderFinished' );
		$fup->startUpload();
		
		return $fup;
	}
	
	
	/**
	 * Reads a file on the client side and writes it to a stream resource on the server
	 * 
	 * Returns the FileToStreamUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileToStreamUploader.
	 *
	 * @param string		$clientPath			The path on the client side
	 * @param resource		$stream				Stream to write to
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileToStreamUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function readFileToStream( $clientPath, $stream, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->hostWindow->addChild( $fup = new FileToStreamUploader( $clientPath, $stream ) );
		if( $handlerObj && $handlerMethod ) {
			$fup->setEventHandler( 'finished', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'failed', $handlerObj, $handlerMethod );
			$fup->setEventHandler( 'cancelled', $handlerObj, $handlerMethod );
		}
		$fup->setEventHandler( 'finished', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'failed', $this, 'onUploaderFinished' );
		$fup->setEventHandler( 'cancelled', $this, 'onUploaderFinished' );
		$fup->startUpload();
		
		return $fup;
	}
	
	
	/**
	 * [INTERNAL FUNCTION] Event handler
	*/
	
	public function onUploaderFinished( $event ) {
		if( $event->sourceObject->isChild )
			$event->sourceObject->removeNode();
	}
	
	
	/**
	 * Removes the directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param string		$clientPath			The path on the client side to remove
	 * @param bool			$recursive			If TRUE, all underlying contents are removed as well. If FALSE, the directory needs to be empty
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function removeDirectory( $clientPath, $recursive, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->handlers[] = ($handlerObj&&$handlerMethod ? array( $handlerObj, $handlerMethod ) : NULL);
		$this->callMethod( 'removeDirectory', array( $clientPath, (bool)$recursive ) );
	}
	
	
	/**
	 * Removes the file
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param string		$clientPath			The path on the client side to remove
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function removeFile( $clientPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->handlers[] = ($handlerObj&&$handlerMethod ? array( $handlerObj, $handlerMethod ) : NULL);
		$this->callMethod( 'removeFile', array( $clientPath ) );
	}
	
	
	/**
	 * Starts monitoring the file
	 * 
	 * The handler receives these parameters when the file is created, removed or changed:
	 * - ClientFileService $fileService	- this object
	 * - string $path: the remote path
	 * - bool $exists: whether the file exists
	 * - int $modificationTime: the modification time of the file
	 * - int $size: file size
	 *
	 * @param string		$clientPath			The path on the client side to monitor
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function monitorFile( $clientPath, $handlerObj, $handlerMethod ) {
		if( isset($this->fileMonitors[$clientPath]) )
			throw new SFException( "Path $clientPath is already being monitored!", ERR_REPORT_APP );
		
		$this->fileMonitors[$clientPath] = array( $handlerObj, $handlerMethod );
		$this->callMethod( 'monitorFile', array( $clientPath ) );
	}
	
	
	/**
	 * Executes the file
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param string		$clientPath			The path on the client side to execute
	 * @param array 		$args				Arguments for the executable
	 * @param bool			$async				If TRUE, the file is executed in the background. If FALSE, the application blocks until the executable ends
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function executeFile( $clientPath, $args = array(), $async = TRUE, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->handlers[] = ($handlerObj&&$handlerMethod ? array( $handlerObj, $handlerMethod ) : NULL);
		$this->callMethod( 'executeFile', array( $clientPath, (array) $args, $async ) );
	}
	
	
	/**
	 * [INTERNAL FUNCTION] Event handler
	*/
	
	public function onResult() {
		$args = func_get_args();
		$event = array_shift( $args );
		$op = array_shift( $args );
		
		switch ( $op ) {
			case 'list':
				$status = array_shift( $args );
				$path = array_shift( $args );
				$files = array();
				if( $status ) {
					$dirFile = $this->fileFromResult( $path, $args[0] );
					$dirFile->entries = array();
					foreach ( $args[1] as $file ) {
						$cf = $this->fileFromResult( $path.$this->getDirSeparator().$file[0], $file );
						$dirFile->entries[$file[0]] = $cf;
					}
				}
				else $dirFile = NULL;
				
				call_user_func_array( array_shift($this->handlers), array($this,$status,$dirFile) );
			break;
			
			case 'createDirectory':
				$status = array_shift( $args );
				$path = array_shift( $args );
				if( $status ) {
					$file = array_shift( $args );
					$dirFile = $this->fileFromResult( $path, $file );
					$dirFile->entries = array();
				}
				else $dirFile = NULL;
				
				if( ($handler = array_shift($this->handlers)) !== NULL )
					call_user_func_array( $handler, array($this,$status,$dirFile) );
			break;
			
			case 'removeDirectory':
			case 'removeFile':
				$status = array_shift( $args );
				$path = array_shift( $args );
				
				if( ($handler = array_shift($this->handlers)) !== NULL )
					call_user_func_array( $handler, array($this,$status,$path) );
			break;
			
			case 'fileChanged':
				list($path,$exists,$modificationTime,$size) = $args;
				call_user_func_array( $this->fileMonitors[$path], array($this,$path,$exists,$modificationTime,$size) );
			break;
			
			case 'executeFile':
				list($status) = $args;
				if( ($handler = array_shift($this->handlers)) !== NULL )
					call_user_func_array( $handler, array($this,$status) );
			break;
		}
	}
	
	public function fileFromResult( $path, $file ) {
		$cf = ($file[1] ? new ClientDirectory() : new ClientFile());
		$cf->fileService = $this;
		$cf->path = $path;
		$cf->name = $file[0];
		$cf->isDirectory = $file[1];
		$cf->isReadable = $file[2];
		$cf->isWritable = $file[3];
		$cf->isExecutable = $file[4];
		$cf->isHidden = $file[5];
		$cf->size = $file[6];
		$cf->exists = TRUE;
		return $cf;
	}
	
	public function getDirSeparator() {
		if( stripos(ApplicationProcess::$PlatformInfo['platform'],'win') !== FALSE )
			return "\\";
		else
			return "/";
	}
}


/**
 * Client filesystem class structure representing files (ClientFile) and directories (ClientDirectory) on the client side
 * 
 * These classes form the object representation of the filesystem of the client and can be created from the
 * methods ClientFileService::getDirectory(), ClientFileService::getSpecialDirectory() and ClientFileService::createDirectory().
*/

class ClientBaseFile
{
	public $fileService;
	public $path;
	public $name;
	public $isDirectory;
	public $isReadable;
	public $isWritable;
	public $isExecutable;
	public $isHidden;
	public $exists = FALSE;
}

class ClientFile extends ClientBaseFile
{	
	public $isDirectory = FALSE;
	public $size;
	
	
	/**
	 * Copies a file from server to client
	 * 
	 * Returns the FileDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileDownloader.
	 *
	 * @param string		$localPath			The path on the server side
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function write( $localPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->writeFile( $localPath, $this->path, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Copies a file from client to server
	 * 
	 * Returns the FileUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileUploader.
	 *
	 * @param string		$localPath			The path on the server side
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function read( $localPath, $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->readFile( $this->path, $localPath, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Stores a string in a file on the client side
	 * 
	 * Returns the FileFromStringDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileFromStringDownloader.
	 *
	 * @param string		$data				Data to write to the file
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileFromStringDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function writeFromString( $data, $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->writeFileFromString( $this->path, $data, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Reads a file on the client side to a string
	 * 
	 * Returns the FileToStringUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileToStringUploader. The file contents are put in the FileToStringUploader::$data property
	 *
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileToStringUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function readToString( $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->readFileToString( $this->path, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Read from a stream and write to a file on the client side
	 * 
	 * Returns the FileFromStreamDownloader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileFromStreamDownloader.
	 *
	 * @param resource		$stream				Stream to read from
	 * @param int			$length				Amount of bytes to read from the stream and write to the file
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileFromStreamDownloader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function writeFromStream( $stream, $length, $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->writeFileFromStream( $this->path, $stream, $length, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Reads a file on the client side to a stream
	 * 
	 * Returns the FileToStreamUploader object to allow progress checking. The handler is attached to the 'finished', 'failed' and 'cancelled' events
	 * of the FileToStreamUploader.
	 *
	 * @param resource		$stream				Stream to write to
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	 * @return FileToStringUploader $transfer			The transfer object keeping track of transfer progress
	*/
	
	public function readToStream( $stream, $handlerObj = NULL, $handlerMethod = NULL ) {
		return $this->fileService->readFileToStream( $this->path, $stream, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Removes the file
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function remove( $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->fileService->removeFile( $this->path, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Starts monitoring the file
	 * 
	 * The handler receives these parameters when the file is created, removed or changed:
	 * - ClientFileService $fileService	- this object
	 * - string $path: the remote path
	 * - bool $exists: whether the file exists
	 * - int $modificationTime: the modification time of the file
	 * - int $size: file size
	 *
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function monitor( $handlerObj, $handlerMethod ) {
		$this->fileService->monitorFile( $this->path, $handlerObj, $handlerMethod );
	}

	
	/**
	 * Executes the file
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param array 		$args				Arguments for the executable
	 * @param bool			$async				If TRUE, the file is executed in the background. If FALSE, the application blocks until the executable ends
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function execute( $args = array(), $async = TRUE, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->fileService->executeFile( $this->path, $args, $async, $handlerObj, $handlerMethod );
	}
}

class ClientDirectory extends ClientBaseFile
{
	public $isDirectory = TRUE;
	public $entries = array();
	
	
	/**
	 * Returns a new ClientDirectory representing a new directory in the current one
	 * 
	 * @param string		$dirName		The directory name to append to the current path
	 * @return ClientDirectory	The (new or existing) directory
	*/
	
	public function appendDirectory( $dirName ) {
		if( isset($this->entries[$dirName]) ) {
			if( $this->entries[$dirName]->isDirectory )
				return $this->entries[$dirName];
			else
				throw new SFException( "$dirName is a file", ERR_REPORT_APP );
		}
		
		$file = new ClientDirectory;
		$file->fileService = $this->fileService;
		$file->path = $this->path.$this->fileService->getDirSeparator().$dirName;
		$file->name = $dirName;
		$file->exists = FALSE;
		return $file;
	}
	
	
	/**
	 * Returns a new ClientFile representing a new file in the current directory
	 * 
	 * @param string		$fileName		The filename to append to the current path
	 * @return ClientFile	The (new or existing) file
	*/
	
	public function appendFile( $fileName ) {
		if( isset($this->entries[$fileName]) ) {
			if( !$this->entries[$fileName]->isDirectory )
				return $this->entries[$fileName];
			else
				throw new SFException( "$fileName is a directory", ERR_REPORT_APP );
		}
		
		$file = new ClientFile;
		$file->fileService = $this->fileService;
		$file->path = $this->path.$this->fileService->getDirSeparator().$fileName;
		$file->name = $fileName;
		$file->exists = FALSE;
		return $file;
	}
	
	
	/**
	 * Creates the directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - ClientDirectory $dirFile: if successful, a file object referring to the new remote directory
	 *
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function create( $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->fileService->createDirectory( $this->path, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Gets the contents of a directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - ClientDirectory $dirFile: if successful, a file object referring to the remote directory with the directory contents in the ClientFile::$entries array
	 *
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function get( $handlerObj, $handlerMethod ) {
		$this->fileService->getDirectory( $this->path, $handlerObj, $handlerMethod );
	}
	
	
	/**
	 * Removes the directory
	 * 
	 * The handler receives these parameters:
	 * - ClientFileService $fileService	- this object
	 * - bool $status: indicates whether the operation was successful
	 * - string $path: the remote path
	 *
	 * @param bool			$recursive			If TRUE, all underlying contents are removed as well. If FALSE, the directory needs to be empty
	 * @param object|string	$handlerObj			The object or classname to call the handler on when the operation finishes
	 * @param string		$handlerMethod		The method to call on the handler object
	*/
	
	public function remove( $recursive = FALSE, $handlerObj = NULL, $handlerMethod = NULL ) {
		$this->fileService->removeDirectory( $this->path, $recursive, $handlerObj, $handlerMethod );
	}
}


/**
 * Executes AppleScript code on MacOSX clients
 */

class AppleScriptService extends Node
{
	public $remoteConstructor = 'AppleScriptService';
	
	public function attach() {
		$this->createRemoteObject();
	}
	
	public function detach() {
		$this->unRegister();
	}
	
	
	/**
	 * Executes AppleScript code on the client
	 * 
	 * @param string	$code		Code to execute
	 */
	
	public function execute( $code ) {
		$lines = explode( "\n", $code );
		for( $n = 0; $n < count($lines); $n++ ) {
			$lines[$n] = trim($lines[$n]);
		}
		$this->callMethod( 'execute', array($lines) );
	}
}



Return current item: Site Fusion