<?php
/**
 * @version 0.1.0
 * @link http://www.xapp-studio.com
 * @author XApp-Studio.com support@xapp-studio.com
 * @license : GPL v2. http://www.gnu.org/licenses/gpl-2.0.html
 * @package XApp\xide\Directory
 */

xapp_import('xapp.VFS.Interface.Access');
xapp_import('xapp.xide.Directory.Service');
xapp_import('xapp.Path.Utils');


/***
 * Class XIDE_Directory_Service extends the standard directory service
 * @link : ls : http://192.168.1.37:81/x4mm/Code/trunk/xide-php/xapp/xcf/index.php?debug=true&view=smdCall&service=XCF_Directory_Service.ls&path=.&callback=asd
 */
class XCOM_Directory_Service extends XIDE_Directory_Service
{
    protected $_currentItems=array();



	public function compress($mount,$selection, $type='zip')
	{
		$error=array();
		xapp_import('xapp.Path.Utils');
		xapp_import('xapp.Utils.SystemTextEncoding');
		$vfs = $this->getFileSystem($mount);
		return $vfs->compress($mount,$selection,$type,$error);


	}
    /**
     * @param $path
     * @param $newFileName
     * @return array
     */
    public function rename($mount,$path,$newFileName){

        $error=array();
        xapp_import('xapp.Path.Utils');
        xapp_import('xapp.Utils.SystemTextEncoding');
        $file = XApp_Path_Utils::decodeSecureMagic($path);
        $filename_new = XApp_Path_Utils::decodeSecureMagic($newFileName);
        $vfs = $this->getFileSystem();
        /***
         * Prevent scriptables
         * @TODO, use backend managed filters
         */
        if(!@is_dir($vfs->toRealPath($mount . DIRECTORY_SEPARATOR . $filename_new))){
            $ext = pathinfo( strtolower($filename_new), PATHINFO_EXTENSION);
            $allowable = explode(',', xapp_get_option(self::UPLOAD_EXTENSIONS,$this));
            if ($ext == '' || $ext == false || (!in_array($ext, $allowable)))
            {
                $errors[]=XAPP_TEXT_FORMATTED('UPLOAD_EXTENSIONS_NOT_ALLOWED',array($filename_new,$ext));
            }
        }


        $vfs->rename($mount,$file,$filename_new,null,$error);
        return self::toRPCError( count($error) > 0 ? 1 : 0, $error);
    }

    /**
     * @return array
     * @throws Xapp_XFile_Exception
     */
    public function put(){

        xapp_import('xapp.Path.Utils');
        xapp_import('xapp.Utils.SystemTextEncoding');

        $vars = array_merge($_GET, $_POST);

        $dstIn ='/';
        $mount ='/';

        if(array_key_exists('dstDir', $vars)){
            $dstIn =XApp_Path_Utils::decodeSecureMagic($vars['dstDir']);
        }
        if(array_key_exists('mount', $vars)){
            $mount=XApp_Path_Utils::decodeSecureMagic($vars['mount']);
        }
        if($dstIn==='.'){
            $dstIn='/';
        }
        $vfs = $this->getFileSystem();
        $destination = $vfs->toRealPath(XApp_Path_Utils::normalizePath($mount . DIRECTORY_SEPARATOR . $dstIn));
        $errors=array();

        //writable check
        if(!is_writable($destination))
        {
            throw new Xapp_XFile_Exception(XAPP_TEXT_FORMATTED('DIRECTORY_NOT_WRITEABLE',array($destination),55100));
        }


	    //parse files
        $fileVars = $_FILES;
        foreach ($fileVars as $boxName => $boxData)
        {
            if(substr($boxName, 0, 9) != "userfile_"){
	            continue;
            }
            $err = self::parseFileDataErrors($boxData);
            if($err != null)
            {
                $errorCode = $err[0];
                $errorMessage = $err[1];
            }

            //basic sanitize
            $userfile_name = $boxData["name"];
            $userfile_name=XApp_Path_Utils::sanitizeEx(XApp_SystemTextEncoding::fromPostedFileName($userfile_name), XApp_Path_Utils::SANITIZE_HTML_STRICT);
            $userfile_name = substr($userfile_name, 0, 128);


            //rename if needed!
            $autorename = xapp_get_option(self::AUTO_RENAME);
            if($autorename){
                $userfile_name = self::autoRenameForDest($destination, $userfile_name);
            }

            /***
             * file extension check
             */
            $ext = pathinfo( strtolower($userfile_name), PATHINFO_EXTENSION);
            $allowable = explode(',', xapp_get_option(self::UPLOAD_EXTENSIONS,$this));
            if ($ext == '' || $ext == false || (!in_array($ext, $allowable)))
            {
                $errors[]=XAPP_TEXT_FORMATTED('UPLOAD_EXTENSIONS_NOT_ALLOWED',array($userfile_name,$ext));
                xapp_clog('file not allowed');
	            continue;
            }

            try {
                if(file_exists($destination."/".$userfile_name)){

                }else{

                }

            }catch (Exception $e){
                $errorMessage = $e->getMessage();
                $errors[]=XAPP_TEXT_FORMATTED('UPLOAD_UNKOWN_ERROR',array($userfile_name,$errorMessage));
                break;
            }

            if(isSet($boxData["input_upload"])){
                try{
                    $input = fopen("php://input", "r");
                    $output = fopen("$destination/".$userfile_name, "w");
                    $sizeRead = 0;
                    while($sizeRead < intval($boxData["size"])){
                        $chunk = fread($input, 4096);
                        $sizeRead += strlen($chunk);
                        fwrite($output, $chunk, strlen($chunk));
                    }
                    fclose($input);
                    fclose($output);
                }catch (Exception $e){
                    $errorMessage = $e->getMessage();
                    $errors[]=XAPP_TEXT_FORMATTED('UPLOAD_UNKOWN_ERROR',array($userfile_name,$errorMessage));
                    break;
                }
            }else{
                $result = @move_uploaded_file($boxData["tmp_name"], "$destination/".$userfile_name);
                if(!$result){
                    $realPath = $destination.DS. $userfile_name;
                    $result = move_uploaded_file($boxData["tmp_name"], $realPath);
                }
                if (!$result)
                {
                    $errors[]=XAPP_TEXT_FORMATTED('UPLOAD_UNKOWN_ERROR',array($userfile_name));
                    break;
                }
            }
        }
	    return $errors;
    }


    /***
     * @param $selection
     * @return array
     */
    public function delete($selection,$secure=false){

        $finalSelection = array();
        if($selection)
        {
            if(is_string($selection)){
                array_push($finalSelection,$selection);
            }elseif(is_array($selection)){
                foreach ($selection as $selectedFile)
                {
                    $finalSelection[]=$selectedFile;
                }
            }
        }

        $success=array();
        $error=array();
        $inclusion = $inclusion=array('*','.*');
        $vfs = $this->getFileSystem();
        $vfs->delete($finalSelection,array(),$inclusion,array(),$error,$success);
        return self::toRPCError( count($error) > 0 ? 1 : 0, $error);
    }

    /**
     * @param $selection
     * @param $dst
     * @param bool $move
     * @return array
     */
    public function move($selection,$dst,$inclusion=Array(),$exclusion=Array(),$mode=1504){

        $success=array();
        $error=array();
        if($dst==='.'){
            $dst='/';
        }

        if(!count($inclusion)){
            $inclusion=array('*','.*');
        }
        $vfs = $this->getFileSystem();
        $vfs->move($selection,$dst,null,$inclusion,$exclusion,$error,$success,$mode);
        return self::toRPCError( count($error) > 0 ? 1 : 0, $error);
    }
    /**
     * @param $selection
     * @param $dst
     * @param bool $move
     * @return array
     */
    public function copy($selection,$dst,$inclusion=Array(),$exclusion=Array(),$mode=1504){

        $success=array();
        $error=array();
        if($dst==='.'){
            $dst='/';
        }

        if(!count($inclusion)){
            $inclusion=array('*','.*');
        }
        $vfs = $this->getFileSystem();
        $vfs->copy($selection,$dst,null,$inclusion,$exclusion,$error,$success,$mode);
        return self::toRPCError( count($error) > 0 ? 1 : 0, $error);
    }
    /**
     * Directory listing
     * @link http://localhost/xide/code/php/xide-php/server/xide/service.php?service=XApp_Directory_Service.ls&callback=asdF&path=/ws/&recursive=true
     * @param string $path
     * @param bool $recursive
     * @param null $options
     * @return array
     */
    public function ls($basePath='.',$path='./',$options=null){

        xapp_import('xapp.Xapp.Hook');
        $recursive=false;
        /**
         * variable normalization
         */
        $basePath = XApp_Path_Utils::normalizePath($basePath,false,false);
        if($basePath==='' || $basePath==='.'){
            $basePath='root';
        }
        $path = XApp_Path_Utils::normalizePath($path,false,false);
        $scanPath =  $basePath . DIRECTORY_SEPARATOR .$path;
        $scanPath = XApp_Path_Utils::normalizePath($scanPath,false,false);

	    $vfs = $this->getFileSystem($basePath);

        //defaults
        if(!$options){

            $options= Array(
                XApp_File_Utils::OPTION_DIR_LIST_FIELDS=>
                    XAPP_XFILE_SHOW_SIZE|
                    XAPP_XFILE_SHOW_PERMISSIONS|
                    XAPP_XFILE_SHOW_ISREADONLY|
                    XAPP_XFILE_SHOW_ISDIR|
                    XAPP_XFILE_SHOW_OWNER|
                    XAPP_XFILE_SHOW_TIME|
                    XAPP_XFILE_SHOW_MIME
            );
        }
        //hook into meta data creation for custom completion
        Xapp_Hook::connect(
            XApp_VFS_Base::EVENT_ON_NODE_META_CREATED,
            $this,"_onItem",
            '',
            array(//user data, we need a parent and the base path
                'parentPath'=>$path,
                'mount'=>$basePath,
	            'remote'=>$vfs->remote,
                'options'=>$options
            )
        );

        //hook into node add for custom rejection
        Xapp_Hook::connect(
            XApp_VFS_Base::EVENT_ON_NODE_ADD,
            $this,"_addNode",'',
            array(
                'mount'=>$basePath,
                'owner'=>$this,
                'options'=>$options,
	            'remote'=>$vfs->remote
            )
        );



        $vfs->ls(XApp_Path_Utils::normalizePath($scanPath,false),$recursive,$options);

        //prepare root object
        $rootObject = new stdClass();
        $fullPath = $vfs->toRealPath($basePath);


        if(!file_exists($fullPath)){

            xapp_import('xapp.File.Utils');
            $res = XApp_File_Utils::mkDir($fullPath,0755,true);
            if($res==false){
                return $this->toRPCError(1,'Sorry, couldnt create directory');
            }
        }



        $vfs->add_ls_file_information($vfs->toRealPath($basePath),$rootObject,
            XAPP_XFILE_SHOW_SIZE|
            XAPP_XFILE_SHOW_PERMISSIONS|
            XAPP_XFILE_SHOW_ISREADONLY|
            XAPP_XFILE_SHOW_TIME|
            XAPP_XFILE_SHOW_MIME
        );


        //corrections
        $rootName = '' .$path;
        $rootPath = '' .$path;

        if($rootName==='' || $rootName==='/'){
            $rootName='.';
        }
        if($rootPath==='' || $rootPath==='/'){
            $rootPath='.';
        }else{
            $rootPath='./'.$path;
        }

        $rootPath=str_replace('//','/',$rootPath);
	    if($this->isWindows()){//whatever
            $rootPath=str_replace('././','./',$rootName);
	    }
        $rootName=basename($rootName);
        $rootName=str_replace('/','',$rootName);

        //prepare Dojo store root structure
        $result = array(
            'status'=>200,
            'total'=>1,
            'items'=>array()
        );
        $result['items'][]=array(
            'children'=>$this->_currentItems,//created in the hook
            '_EX'=>true,
            'size'=>0,
            'name'=>$rootName,
            'path'=>$rootPath,
            'mount'=>$basePath,
            'directory'=>true
        );
        return $result;
    }

    /***
     * VFS callback when a node will be added, return false if its not in
     * our file match pattern
     * @param $evt
     */
    public function _addNode($evt){

        if( $this->_vfs!==null &&               //need valid VFS
            is_array($evt)     &&               //must be an array
            array_key_exists('item',$evt) &&    //need item
            array_key_exists('userData',$evt)&& //need user data
            array_key_exists('options',$evt['userData'] //need options
            )
        )
        {
            $userData = $evt['userData'];
            $options =  $userData['options'];
            //no filters? return
            if(
                !isset($options[self::INCLUDES_FILE_EXTENSIONS]) &&
                !isset($options[self::EXCLUDED_FILE_EXTENSION])){
                return true;
            }

            $item =$evt['item'];

            $isDirectory = $item->{XAPP_NODE_FIELD_IS_DIRECTORY};

            //check files
            if($isDirectory===false){

                $allowedFileExtensions = isset($options[self::INCLUDES_FILE_EXTENSIONS]) ? $options[self::INCLUDES_FILE_EXTENSIONS] : array();
                $forbiddenFileExtensions = isset($options[self::EXCLUDED_FILE_EXTENSION]) ? $options[self::EXCLUDED_FILE_EXTENSION] : array();

                if(is_string($allowedFileExtensions)){
                    if(strpos($allowedFileExtensions,',')!==false){
                        $allowedFileExtensions = explode(',',$allowedFileExtensions);
                    }else{
                        $allowedFileExtensions = array($allowedFileExtensions);
                    }
                }

                if(is_string($forbiddenFileExtensions)){
                    if(strpos($forbiddenFileExtensions,',')!==false){
                        $forbiddenFileExtensions = explode(',',$forbiddenFileExtensions);
                    }else{
                        $forbiddenFileExtensions = array($forbiddenFileExtensions);
                    }
                }

                $isAllowed = XApp_Directory_Utils::isAllowed(
                    $item->{XAPP_NODE_FIELD_NAME},
                    $allowedFileExtensions,
                    $forbiddenFileExtensions);
                return $isAllowed===true;
            }
        }
        return true;
    }

    /***
     * Xapp_Rpc_Interface_Callable Impl. Before the actual call is being invoked.
     */
    public function onBeforeCall($function=null, $class=null, $params=null){
        parent::init();
    }


	protected static  function isWindows(){
		$os = PHP_OS;
		switch($os)
		{
			case "WINNT": {
				return true;
			}
		}

		return false;
	}
    /***
     * Maqetta & XIDE specific extension, we mixin 2 new attributes per node and we mark common directories
     * as read-only
     * @param $evt
     */
    public function _onItem($evt){

        if(is_array($evt) && isset($evt['item'])){

            $item = $evt['item'];
            $userData = $evt['userData'];
            if(!$userData){
                return null;
            }

            $options = isset($userData['options']) ? $userData['options'] : array();



            if(isset($userData['parentPath'])){
                $parentPath = $userData['parentPath'];
                $item->path='./'.$parentPath . '/'. $item->name;
                $item->path=str_replace('//','/',$item->path);
                $item->mount=$userData['mount'];
	            $item->remote=$userData['remote'];
            }else{
                $item->path='./'.$item->name;
            }

            if($item->isDir===true){
                $item->children=array();
                $item->_EX=false;
                $item->size=0;
                $item->directory=$item->isDir;
            }else{

                if(property_exists($item,'size')){
	                if($item->size===''){
	                    $item->size='0 Kb';
	                }else{

	                }

                }else{
                    $item->size='0 Kb';
                }
            }
            $item->path=str_replace('//','/',$item->path);


	        if($this->isWindows()){//whatever
                $item->path=str_replace('././','./',$item->path);
	        }
            $this->_currentItems[]=$item;
            return $item;
        }
        return null;
    }


    /**
     * Parse the $fileVars[] PHP errors
     * @static
     * @param $boxData
     * @return array|null
     */
    static function parseFileDataErrors($boxData)
    {
        $userfile_error = $boxData["error"];
        $userfile_tmp_name = $boxData["tmp_name"];
        $userfile_size = $boxData["size"];
        if ($userfile_error != UPLOAD_ERR_OK) {
	        $errorsArray = array();
            $errorsArray[UPLOAD_ERR_FORM_SIZE] = $errorsArray[UPLOAD_ERR_INI_SIZE] = array(409, "File is too big! Max is" . ini_get("upload_max_filesize"));
            $errorsArray[UPLOAD_ERR_NO_FILE] = array(410, "No file found on server!");
            $errorsArray[UPLOAD_ERR_PARTIAL] = array(410, "File is partial");
            $errorsArray[UPLOAD_ERR_INI_SIZE] = array(410, "No file found on server!");
            if ($userfile_error == UPLOAD_ERR_NO_FILE) {
                // OPERA HACK, do not display "no file found error"
                if (!ereg('Opera', $_SERVER['HTTP_USER_AGENT'])) {
                    return $errorsArray[$userfile_error];
                }
            }
            else
            {
                return $errorsArray[$userfile_error];
            }
        }
        if ($userfile_tmp_name == "none" || $userfile_size == 0) {
            return array(410, 'no file name');
        }
        return null;
    }
    public static function autoRenameForDest($destination, $fileName){
        if(!is_file($destination."/".$fileName)) return $fileName;
        $i = 1;
        $ext = "";
        $name = "";
        $split = explode(".", $fileName);
        if(count($split) > 1){
            $ext = ".".$split[count($split)-1];
            array_pop($split);
            $name = join("\.", $split);
        }else{
            $name = $fileName;
        }
        while (is_file($destination."/".$name."-$i".$ext)) {
            $i++; // increment i until finding a non existing file.
        }
        return $name."-$i".$ext;
    }
}