<?php

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

goweb::loadClass('Goweb_Exception');


if (!defined("FTP_BINARY")) {
    define("FTP_BINARY", 1);
}

if (!defined("FTP_ASCII")) {
    define("FTP_ASCII", 0);
}

if(!defined('CRLF')){
    define('CRLF',"\r\n");
}

/**
 *
 * @version    5.0.0
 * @category   Goweb
 * @package    Goweb_Ftp
 * @author     I&D <dev@goweb.pt>, Pedro Pinto <ppinto@goweb.pt>
 * @copyright  2009 (c) Goweb (http://www.goweb.pt)
 */
class Goweb_Ftp_Abstract {
    const ERROR_EMPTY_HOST = 1;
    const ERROR_INVALID_CONNECTION = 2;
    const ERROR_INVALID_LOGIN = 3;
    const ERROR_INVALID_PATH = 4;
    const ERROR_INVALID_MODE = 5;
    const ERROR_INVALID_DESTINATION = 6;
    const ERROR_INVALID_SOURCE = 7;

    /**
     * Ligação ftp
     *
     * @var resource
     */
    protected $_connection = false;

    /**
     * Configuração da ligação
     *
     * @var array
     */
    protected $_config;

    /**
     * Código do erro
     *
     * @var int
     */
    protected $_error;
    
    /**
     * Nome de ficheiro temporário
     *
     * @var String
     */
    protected $_tmpFilename;
    
    /**
     * Timeout da ligação
     *
     * @var array
     */
    protected $_timeout;

    /**
     * Passive mode
     */
    protected $_passiveMode = false;

    /**
     * Singleton instance
     *
     * @return Goweb_Ftp_Abstract
     */
    public static function getInstance($host, $username, $password, $port){
        if (extension_loaded('ftp')) {
            goweb::loadClass('Goweb_Ftp_Native');
            $ftp = new Goweb_Ftp_Native($host, $username, $password, $port);
        } else {
            throw new Goweb_Exception('Sem extensão ftp');
            /**
             * @todo re-implementar com sockets
             */ 
            goweb::loadClass('Goweb_Ftp_Sockets');
            $ftp = new Goweb_Ftp_Sockets($host, $username, $password, $port);
        }
        return $ftp;
    }

    /**
     * Constructor
     *
     * @param String $host
     * @param String $username
     * @param String $password
     * @param String $port
     */
    public function __construct($host, $username, $password, $port) {
        $this->_config['host'] = $host;
        $this->_config['username'] = $username;
        $this->_config['password'] = $password;
        $this->_config['port'] = $port;
        $this->_connect($host, $port);
        $this->_login($username, $password);
    }

    /**
     * Abrir uma ligação
     *
     * Argumentos possíveis:
     * - host        obrigatório
     * - port        default 21
     * - timeout     default 90
     * - user        default anonymous
     * - password    default empty
     * - ssl         default false
     * - passive     default false
     * - path        default empty
     * - file_mode   default FTP_BINARY
     *
     * @param array $args
     * @return boolean
     */
    public function open(array $args = array())
    {
        if (empty($args['host'])) {
            $this->_error = self::ERROR_EMPTY_HOST;
            throw new Goweb_Exception('Empty host specified');
        }

        if (empty($args['port'])) {
            $args['port'] = 21;
        }

        if (empty($args['user'])) {
            $args['user'] = 'anonymous';
            $args['password'] = 'anonymous@goweb.pt';
        }

        if (empty($args['password'])) {
            $args['password'] = '';
        }

        if (empty($args['timeout'])) {
            $args['timeout'] = 90;
        }

        if (empty($args['file_mode'])) {
            $args['file_mode'] = FTP_BINARY;
        }
        
        if (empty($args['ssl'])) {
            $args['ssl'] = false;
        }
        
        $this->_config = $args;
        $this->_timeout = $this->_config['timeout']; 
        
        $this->_connect($this->_config['host'], $this->_config['port'], $this->_config['timeout'], $this->_config['ssl']);
        $this->_login($this->_config['user'], $this->_config['password']);
        if (!empty($this->_config['passive'])) {
            $this->_passive();
        }
        
        if (!$this->_connection) {
            $this->_error = self::ERROR_INVALID_CONNECTION;
            throw new Goweb_Exception('Could not establish FTP connection, invalid host or port');
        }

        if (!empty($this->_config['path'])) {
            $this->_chdir($this->_config['path']);
        }


        return true;
    }
    
    /**
     * Fechar ligação
     *
     * @return boolean
     */
    public function close()
    {
        return $this->_close();
    }
    
    /**
     * Criar directório
     *
     * @param string $dir
     * @param int $mode
     * @param boolean $recursive
     * @return boolean
     */
    public function mkdir($dir, $mode=0777, $recursive=true){
        return $this->_mkdirGeneric($dir, $mode, $recursive);
    }
    
    /**
     * Get current working directory
     *
     * @return string
     */
    public function pwd(){
        return $this->_pwd();
    }
    
    /**
     * Change current working directory
     *
     * @param string $dir
     * @return boolean
     */
    public function cd($dir){
        return $this->_cd($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
     */
    public function read($filename, $dest=null){
        return $this->_read($filename, $dest);
    }
    
    /**
     * 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
     */
    public function write($filename, $src, $mode=null){
        return $this->_write($filename, $src, $mode);
    }

    /**
     * Apagar um ficheiro
     *
     * @param string $filename
     * @return boolean
     */
    public function rm($filename){
        return $this->_rm($filename);
    }
    
    /**
     * Renomear ou mover um directório ou ficheiro
     *
     * @param string $src
     * @param string $dest
     * @return boolean
     */
    public function mv($src, $dest){
        return $this->_mv($src, $dest);
    }

    /**
     * Listar a directório corrente
     *
     * @return array
     */
    public function ls(){
        return $this->_ls();
    }
    
    /**
     * Configurar permissões de ficheiro ou directório
     *
     * @param int $mode
     * @param string $dir
     * @return boolean
     */
    public function chmod($mode=0777, $dir){
        return $this->_chmod($mode, $dir);
    }

    /**
     * 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
     */
    public function fileExists($filesearch){
        return $this->_fileExists($filesearch);
    }
        
    /**
     * Obter conteudo de um ficheiro remoto sem efectuar a transferï¿œncia
     *
     * @param String $filename caminho para o ficheiro a efectuar a transferï¿œncia
     * @return bool|String
     */
    public function getFileContents($filename){
        return $this->_getFileContents($filename);
    }
    /**
     * Criar directório e configurar permissões
     *
     * @param string $dir
     * @param int $mode
     * @param boolean $recursive
     * @return boolean
     */
    protected function _mkdirGeneric($dir, $mode=0777, $recursive=false){
        if ($recursive){
          return $this->_mkdir_recursive($mode, $dir); 
        }
        $mkdir = $this->_mkdir($dir);
        $chmod = $this->_chmod($mode, $dir);
        return $mkdir && $chmod;
    }
    
    /**
     * Criar directório e configurar permissões recursivamente
     *
     * @param int $mode
     * @param string $path
     * @return boolean
     */
    protected function _mkdir_recursive($mode, $path){
        $dir=split("/", $path);
        $path="";
        $ret = true;

        for ($i=0;$i<count($dir);$i++)
        {
            $path.="/".$dir[$i];
            if(!$this->_chdir($path))
            {
                $this->_chdir("/");
                if(!$this->_mkdir($path)){
                    $ret=false;
                    break;
                } else {
                    $this->_chmod($mode, $path);
                }
            }
        }
        return $ret;
    }

    /**
     * Criar ficheiro temporário
     *
     * @param bool $new
     * @return String
     */
    protected function _tmpFilename($new=false){
        if ($new || !$this->_tmpFilename) {
            $this->_tmpFilename = tempnam(md5(uniqid(rand(), TRUE)), '');
        }
        return $this->_tmpFilename;
    }
}