<?php

require_once 'classes/ServicePlugin.php';
/**
* @package Plugins
*/
class PluginBackup extends ServicePlugin
{
    function getVariables()
    {
        $variables = array(
            /*T*/'Plugin Name'/*/T*/   => array(
                'type'          => 'hidden',
                'description'   => /*T*/''/*/T*/,
                'value'         => /*T*/'CE Database Backup'/*/T*/,
            ),
            /*T*/'Enabled'/*/T*/       => array(
                'type'          => 'yesno',
                'description'   => /*T*/'Generates a file with an SQL dump of your ClientExec database on a periodic basis, and delivers it to any of the locations set below.'/*/T*/,
                'value'         => '0',
            ),
            /*T*/'Compress file with gzip'/*/T*/ => array(
                'type'          => 'yesno',
                'description'   => /*T*/'Only possible if you have the zlib extension in your PHP installation.'/*/T*/,
                'value'         => '0',
                'enableIf'      => 'return extension_loaded(\'zlib\');',
            ),
            /*T*/'Encryption password'/*/T*/ => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter a password if you wish to encrypt the file, or else leave empty. Only possible if you have the mcrypt extension in your PHP installation'/*/T*/,
                'value'         => '',
                'enableIf'      => 'return extension_loaded(\'mcrypt\');',
            ),
            /*T*/'Deliver to remote FTP or SFTP account'/*/T*/ => array(
                'type'          => 'text',
                'description'   => /*T*/'To send the file to a remote FTP or SFTP account enter the host and your credentials in the format <b>ftp://username:password@host.com/subdirectory</b> for FTP<br>or<br><b>sftp://username:password@host.com/subdirectory</b> for SFTP<br>SFTP is only possible if you have the ssh2 extension in your PHP installation'/*/T*/,
                'value'         => '',
            ),
            /*T*/'Deliver to local directory'/*/T*/ => array(
                'type'          => 'text',
                'description'   => /*T*/'To save the file in a local directory accessible and writable by the web server, enter it\'s full path here.'/*/T*/,
                'value'         => '',
            ),
            /*T*/'Deliver to E-mail address'/*/T*/ => array(
                'type'          => 'smallTextarea',
                'description'   => /*T*/'To send the file as an E-mail attachment, enter the address here.'/*/T*/,
                'value'         => '',
            ),
            /*T*/'Run schedule - Minute'/*/T*/  => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter number, range, list or steps'/*/T*/,
                'value'         => '30',
                'helpid'        => '8',
            ),
            /*T*/'Run schedule - Hour'/*/T*/  => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter number, range, list or steps'/*/T*/,
                'value'         => '01',
            ),
            /*T*/'Run schedule - Day'/*/T*/  => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter number, range, list or steps'/*/T*/,
                'value'         => '*',
            ),
            /*T*/'Run schedule - Month'/*/T*/  => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter number, range, list or steps'/*/T*/,
                'value'         => '*',
            ),
            /*T*/'Run schedule - Day of the week'/*/T*/  => array(
                'type'          => 'text',
                'description'   => /*T*/'Enter number in range 0-6 (0 is Sunday) or a 3 letter shortcut (e.g. sun)'/*/T*/,
                'value'         => '*',
            ),
        );
    
        return $variables;
    }
    
    function execute()
    {
        // this can take a while
        @set_time_limit(0);

        $messages = array();
        $configuration = NE_Registry::getConfiguration();
        $fileName = "backup_" . $configuration['application']['dbSchema'] . "_" . date('Y_m_d_H_i') . ".sql";
    
        // use references to save memory
        $sql = '';
        $this->_getSQL($sql);
        if ($this->settings->get('plugin_backup_Compress file with gzip')) {
            $fileName .= '.gz';
            if ($message = $this->_gzip($sql, $fileName)) {
                $messages[] = $message;
            }
        }
    
        if ($this->settings->get('plugin_backup_Encryption password')) {
            $fileName .= '.enc';
            if ($message = $this->_encrypt($sql, $this->settings->get('plugin_backup_Encryption password'))) {
                $messages[] = $message;
            }
        }
    
        if ($this->settings->get('plugin_backup_Deliver to remote FTP or SFTP account')) {
            $messages[] = $this->_sendFTP($sql, $fileName, $this->settings->get('plugin_backup_Deliver to remote FTP or SFTP account'));
        }
    
        if ($this->settings->get('plugin_backup_Deliver to local directory')) {
            $messages[] = $this->_sendLocal($sql, $fileName, $this->settings->get('plugin_backup_Deliver to local directory'));
        }
    
        if ($this->settings->get('plugin_backup_Deliver to E-mail address')) {
            $destinataries = explode("\r\n", $this->settings->get('plugin_backup_Deliver to E-mail address'));
            foreach ($destinataries as $destinatary) {
                $messages[] = $this->_sendEmail($sql, $fileName, $destinatary);
            }
        }
    
        return $messages;
    }
    
    function output()
    {
        $output = "<a href=\"#\" onclick=\"window.open('index.php?fuse=admin&view=DecryptSQLDump', 'decryptFile', 'width=400,height=200')\" style=\"font-weight:bold\">"
                 .$this->user->lang('Click here to decrypt files')
                 ."</a>";
    
        return $output;
    }
    
    function dashboard()
    {
    }
    
    // this is a non-standard plugin function. Don't use it anywhere else.
    function _popup(&$tpl)
    {
        $tpl->define(array('backup_decrypt' => 'views/admin/backup_decrypt.tpl'));
        $tpl->parse('output', 'backup_decrypt');
    
        return $tpl->fetch('output');   
    }
    
    // this is a non-standard plugin function. Don't use it anywhere else.
    function _decrypt()
    {
        $sql= NE_Lib::decryptData(   file_get_contents($_FILES['file']['tmp_name']),
                                        $this->settings->get('plugin_backup_Encryption password'));
        if (is_a($sql, 'NE_Error')) {
            die($sql->getMessage());
        }
        $this->_downloadFile($sql);
    }
    
    
    // PRIVATE FUNCTIONS
    function _getSQL(&$sql)
    {
        $sql .= file_get_contents(dirname(__FILE__).'/../../../newedge/setup/sql/schema.sql');

        $modulesDir = realpath(dirname(__FILE__) . '/../../../modules');
    	$dir = dir($modulesDir);
    	while (false !== $entry = $dir->read()) {
            if (is_dir($modulesDir.'/'.$entry) && $entry != '.' && $entry != '..' && $entry != 'CVS') {
                if(file_exists($modulesDir.'/'.$entry.'/setup/sql/schema.sql')){
                    $sql .= file_get_contents($modulesDir.'/'.$entry.'/setup/sql/schema.sql');
                } else {
                     continue;
                }
            }
    	}
        $result1 = $this->db->query('SHOW TABLES');
        while ($row1 = $result1->fetch()) {
            $sql .= "--\n-- Dumping data for table `{$row1[0]}`\n--\n\n";
            $result2 = $this->db->query("SELECT * FROM {$row1[0]}");
            while ($row2 = $result2->fetch(MYSQL_NUM)) {
                if($row1[0] == 'domains'){
                    $row2[4] = '0x'.bin2hex($row2[4]);
                }
                for ($i = 0; $i < count($row2); $i++) {
                    $row2[$i] = $this->db->escape_string($row2[$i]);
                }
                $values = implode("', '", $row2);
                if($row1[0] == 'domains'){
                    $values = str_replace("'{$row2[4]}'", $row2[4], $values);
                }
                $values = "'$values'";
                $values = str_replace("'NULL'", "NULL", $values);
                $sql .= "INSERT INTO `{$row1[0]}` VALUES($values);\n";
            }
            $sql .= "\n\n";
        }
    }
    
    function _gzip(&$sql, $fileName)
    {
        // maximum compression. Make it smaller if you're having performance problems
        $compressionLevel = 9;
    
        if (!extension_loaded('zlib')) {
            return new NE_Error($this->user->lang('Error: attempted to compress SQL dump file without having the zlib extension enabled'));
        }
    
        if (!$sql = gzencode($sql, $compressionLevel)) {
            return new NE_Error($this->user->lang('Error: Couldn\'t gzip SQL dump file'));
        }
    }
    
    function _encrypt(&$sql, $password)
    {
        if (!extension_loaded('mcrypt')) {
            return new NE_Error($this->user->lang('Error: attempted to encrypt SQL dump file without having the mcrypt extension enabled'));
        }

        $sql = NE_Lib::encryptData($sql, $password);
    }
    
    // we don't use a reference for $sql here because the ftp class might change the data
    function _sendFTP($sql, $fileName, $host)
    {
        require_once 'plugins/services/backup/ftp_class.php';
    
        list($login, $password, $domain, $directory) = $this->_getFTPVars($host);
    
        if (strpos($host, 'ftp://') === 0) {
            $ftp = new ftp();
            if (!$ftp->setServer($domain)) {
                $ftp->quit();
                return new NE_Error($this->user->lang('Error: error setting FTP server'));
            }
            if (!$ftp->connect()) {
                return new NE_Error($this->user->lang('Error: can\'t connect to FTP server'));
            }
            if (!$ftp->login($login, $password)) {
                $ftp->quit();
                return new NE_Error($this->user->lang('Error: authentication with FTP server failed'));
            }
            $ftp->setType(FTP_AUTOASCII);
            $ftp->passive(false);
    
            if ($directory) {
                $ftp->chdir($directory);
            }
    
            if (false !== $ftp->put($fileName, $sql)) {
                return $this->user->lang('SQL dump file successfuly uploaded to FTP server: '.$fileName);
            } else {
                $ftp->quit();
                return new NE_Error($this->user->lang('Error: ftp file uploading failed'));
            }
        } else if (strpos($host, 'sftp://') === 0) {
        	if(!function_exists('ssh2_connect')) {
        	    return new NE_Error($this->user->lang('ssh2 extension not found in your PHP installation'));
        	}
            $connection = @ssh2_connect($domain, 22);
            if($connection == false) {
                return new NE_Error($this->user->lang('Conection to %s failed using SFTP. No SSH server listening?', $domain));
            }
            $result = @ssh2_auth_password($connection, $login, $password);
    
            if($result == false) {
                return new NE_Error($this->user->lang('Authentication Failure - Username/Password not accepted'));
            }
            $sftp = @ssh2_sftp($connection);
            $dir = "ssh2.sftp://$sftp/".$directory;
    
            $stream = @fopen("$dir/$fileName", 'w');
            if (!$stream) {
                return new NE_Error($this->user->lang('Unable to create file on remote server'));
            }
            if(@fwrite($stream, $sql) === FALSE) {
                return new NE_Error($this->user->lang('Cannot write to file (%s)', $fileName));
            }
            @fclose($stream);
            return $this->user->lang('SQL dump file successfuly uploaded to SFTP server: '.$fileName);
        }    
    }
    
    function _sendLocal(&$sql, $fileName, $dir)
    {
        $dir = $this->_stripDirSlash($dir);
    
        if (!@is_dir($dir)) {
            return new NE_Error($this->user->lang('Error: local directory to deploy SQL dump file is not accessible'));
        }
    
        if (!@is_writable($dir)) {
            return new NE_Error($this->user->lang('Error: local directory to deploy SQL dump file is not writable'));
        }
    
        if (!$fp = @fopen("$dir/$fileName", 'wb')) {
            return new NE_Error($this->user->lang('Error: couldn\'t create file'));
        }
        if (!fwrite($fp, $sql)) {
            return new NE_Error($this->user->lang('Error: couldn\'t write to new SQL dump file'));
        }
        fclose($fp);
    
        return $this->user->lang('SQL dump file successfully written:')."$dir/$fileName";
    }
    
    function _sendEmail(&$sql, $fileName, $email)
    {
        require_once 'newedge/classes/NE_MailGateway.php';
        $mailGateway = new NE_MailGateway();
        $mailSend = $mailGateway->mailMessageEmail("Attached $fileName",
            $this->settings->get('Support E-mail'),
            'ClientExec Backup Service',
            $email,
            '',
            $this->user->lang('%s attached',$fileName),
            3,
            false,
            $sql,
            $fileName);
        if (is_a($mailSend, 'NE_Error'))
        {
            return new NE_Error($this->user->lang('Error: couldn\'t send Email with SQL dump file'));
        }
    
        return $this->user->lang('SQL dump file successfuly sent by E-mail: '.$fileName);
    }
    
    function _downloadFile(&$sql)
    {
        header('content-disposition: attachment; filename='.$_FILES['file']['name']);
        header('content-type: application/x-download');
        header('content-length: '.strlen($sql));
        echo $sql;
        exit;
    }
    
    function _stripDirSlash($dir)
    {
        if ($dir{strlen($dir)-1} == '/') {
            $dir = substr($dir, 0, strlen($dir)-1);
        }
    
        return $dir;
    }
    
    function _getFTPVars($host)
    {
        if (strpos($host, 'ftp://') === 0) {
            $host = substr($host, 6);
        } else if (strpos($host, 'sftp://') === 0) {
            $host = substr($host, 7);
        }
    
        $host = $this->_stripDirSlash($host);
        $loginpassword = substr($host, 0, strrpos($host, '@'));
        list($login, $password) = explode(':', $loginpassword);
    
        $domain = substr($host, strrpos($host, '@') + 1);
        if (($position = strpos($domain, '/')) === false) {
            $directory = false;
        } else {
            $directory = substr($domain, $position);
            $domain = substr($domain, 0, $position);
        }
    
        return array($login, $password, $domain, $directory);
    }
}
?>
