<?php

/**
 * Goweb Sitemanager 5 Framework
 *
 * @version    5.1.7
 * @category   Goweb
 * @package    Goweb_Ftp
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */

goweb::loadClass('Goweb_Ftp_Abstract');

/**
 *
 * @version    5.1.7
 * @category   Goweb
 * @package    Goweb_Ftp
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2010 (c) Goweb (http://www.goweb.pt)
 */
class Goweb_Ftp_Sockets extends Goweb_Ftp_Abstract {
    /**
     * mensagens dos comandos
     *
     * @var array
     */
    protected $_message = array();
    
    /**
     * Efectuar a ligação ao servidor ftp
     *
     * @param String $host
     * @param int $port
     * @param int $timeout
     * @param boolean $ssl
     */
    protected function _connect($host, $port = 21, $timeout = 90, $ssl = false){
		if(!($sock = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) {
            $this->_error = self::ERROR_INVALID_CONNECTION;
		    throw new Goweb_Exception('socket create failed ' . socket_strerror(socket_last_error($sock)));
		}
		if(!$this->_setTimeout($sock, $timeout)) return false;
		if (!($res = @socket_connect($sock, $host, $port))) {
            $this->_error = self::ERROR_INVALID_CONNECTION;
			@socket_close($sock);
		    throw new Goweb_Exception('socket connect failed' . socket_strerror(socket_last_error($sock)));
		}
		$this->_connection = $sock;
    }
    
	
    function _setTimeout($sock = null, $timeout = 90) {
        if (is_null($sock)) $sock = $this->_connection;
        if (!$sock){
            $this->_error = self::ERROR_INVALID_CONNECTION;
            throw new Goweb_Exception('Could get current socket');
        }
		if(!@socket_set_option($sock, SOL_SOCKET, SO_RCVTIMEO, array("sec"=>$timeout, "usec"=>0))) {
            $this->_error = self::ERROR_INVALID_CONNECTION;
		    @socket_close($sock);
		    throw new Goweb_Exception('socket set receive timeout ' . socket_strerror(socket_last_error($sock)));
		}
		return true;
	}
    
    /**
     * Efectuar o login no servidor ftp
     *
     * @param String $user
     * @param String $password
     */
    protected function _login($user = 'anonymous', $password = 'anonymous@goweb.pt'){
        if (!$this->_exec("USER ".$user) || !$this->_exec("PASS ".$password)) {
            $this->_error = self::ERROR_INVALID_LOGIN;
            $this->_close();
            throw new Goweb_Exception('Invalid user name or password');
        }
    }

    /**
     * Change directory
     *
     * @param string $path
     */
    protected function _chdir($path = false){
        if ($path) {
            if (!$this->_cd($path)) {
                $this->_error = self::ERROR_INVALID_PATH;
                $this->_close();
                throw new Goweb_Exception('Invalid path');
            }
        }
    }

    /**
     * Usar modo passivo
     *
     * @param boolean $flag
     */
    protected function _passive($flag = true){
        if ($flag){
            if (!$this->_exec("PASV")){
                $this->_error = self::ERROR_INVALID_MODE;
                $this->_close();
                throw new Goweb_Exception('Invalid mode');
            }
        }
    }
    
    /**
     * Fechar ligação
     *
     * @return boolean
     */
    protected function _close(){
        return @socket_close($this->_connection);
    }
    
    /**
     * Get current working directory
     *
     * @return string
     */
    protected function _pwd(){
        return $this->_exec("PWD");
    }

    /**
     * Change current working directory
     *
     * @param string $dir
     * @return boolean
     */
    protected function _cd($dir){
        return $this->_exec("CWD ".$dir);
    }
    
    /**
     * Ler um ficheiro para uma string, ficheiro ou stream
     *
     * @param string $filename
     * @param string|resource|null $dest nome do ficheiro de destino, stream, ou se for null vai retornar o conteudo do ficheiro
     * @return string
     */
    protected function _read($filename, $dest=null){
        if (is_string($dest)) {
            $result = ftp_get($this->_connection, $dest, $filename, $this->_config['file_mode']);
        } else {
            if (is_resource($dest)) {
                $stream = $dest;
            } elseif (is_null($dest)) {
                $stream = tmpfile();
            } else {
                $this->_error = self::ERROR_INVALID_DESTINATION;
                return false;
            }

            $result = ftp_fget($this->_connection, $stream, $filename, $this->_config['file_mode']);

            if (is_null($dest)) {
                fseek($stream, 0);
                $result = '';
                for ($result = ''; $s = fread($stream, 4096); $result .= $s);
                fclose($stream);
            }
        }
        return $result;
    }
    
    /**
     * Gravar um ficheiro de uma string, outro ficheiro ou stream
     *
     * @param string $filename
     * @param string|resource $src filename, dados ou stream de origem
     * @return int|boolean
     */
    protected function _write($filename, $src, $mode=null){
        if (is_string($src) && is_readable($src)) {
            return @ftp_put($this->_connection, $filename, $src, $this->_config['file_mode']);
        } else {
            if (is_string($src)) {
                $stream = tmpfile();
                fputs($stream, $src);
                fseek($stream, 0);
            } elseif (is_resource($src)) {
                $stream = $src;
            } else {
                $this->_error = self::ERROR_INVALID_SOURCE;
                return false;
            }

            $result = ftp_fput($this->_connection, $filename, $stream, $this->_config['file_mode']);
            if (is_string($src)) {
                fclose($stream);
            }
            return $result;
        }
    }
    
    /**
     * Apagar um ficheiro
     *
     * @param string $filename
     * @return boolean
     */
    protected function _rm($filename){
        return $this->_exec("RMD ".$filename);
    }

        
    /**
     * Renomear ou mover um directório ou ficheiro
     *
     * @param string $src
     * @param string $dest
     * @return boolean
     */
    protected function _mv($src, $dest){
        return $this->_exec("RNFR ".$src) && $this->_exec("RNTO ".$dest);
    }
    
    /**
     * Listar a directório corrente
     *
     * @return array
     */
    protected function _ls(){
        $this->_exec('NLST');
        $list = $this->_message;
        return $list;
    }

    /**
     * Criar directório
     *
     * @param string $path
     * @return boolean
     */
    protected function _mkdir($dir){
        return $this->_exec("MKD " . $dir);
    }
    
    /**
     * Configurar permissões de ficheiro ou directório
     *
     * @param int $mode
     * @param string $dir
     * @return boolean
     */
    protected function _chmod($mode=0777, $dir){
        return !$this->_site("CHMOD ".decoct($mode)." ". $dir);
    }
    
    protected function _exec($cmd) {
		$bool = @socket_write($this->_connection, $cmd.CRLF);
		if(!socket_last_error($this->_connection)){
		    $this->_readmsg();
		}
		return $bool;
	}

	protected function _site($command) {
		if(!$this->_exec("SITE ".$command)) return FALSE;
		return TRUE;
	}

    /**
     * Verificar se um ficheiro existe,
     * e retorna o nome do mesmo(útil para wildcards, em que retorna o primeiro encontrado)
     *
     * @param String $filesearch
     * @return bool|String
     */
    protected function _fileExists($filesearch){

        if(empty($filesearch)) {
            $this->print_err('Por favor introduza o caminho para o ficheiro');
            return false;
        }
        goweb::dump($this->_ls()); exit;
        $filelist = ftp_nlist($this->_connection, $filesearch);   

        if ( $filelist === false )
        {
            return false;
        }
        
        /**
         * Verificar se o ficheiro existe, mesmo para algumas respostas inválidas de alguns servidores FTP
         */
        if ( !count($filelist) || ftp_size($this->_connection, $filelist[0]) == -1 )
        {
            return false;
        }
        return $filelist[0];
    }
	
    protected function _readmsg(){
		if(!$this->_connection) {
            throw new Goweb_Exception('No connection');
		}
		$result=true;
		$this->_code=0;
		$go=true;
		$message = '';
		do {
			$tmp=@socket_read($this->_connection, 4096, PHP_BINARY_READ);
			if($tmp===false) {
				$go=false;
			} else {
				$message .= $tmp;
				$go = !preg_match("/^([0-9]{3})(-.+\\1)? [^".CRLF."]+".CRLF."$/Us", $message, $regs);
			}
		} while($go);
		$this->_message[] = $message;
		if (isset($regs[1])){
    		$this->_code=(int)$regs[1];
		}
		return $result;
	}
	
	
}
