<?php

/**
 * @license		GNU General Public License version 2 or later; see LICENSE.txt
 */
// No direct access
defined('_JEXEC') or die;

jimport('joomla.application.component.modelitem');
jimport('joomla.updater.update');

class SPStgSrvModelInstaller extends JModelItem {

    public function &getItem($data) {
        $params = &JComponentHelper::getParams('com_spstgsrv');
        $hash = $params->get('hash');
        $password = $params->get('password');

        //Initialize
        $date = JFactory::getDate();
        $item['created'] = $date->toSql();
        $item['result'] = '1';

        foreach ($data as $key => $value) {
            if (($item[$key] = $this->decrypt($value, $password, $hash)) === false) {
                $item['result'] = '0';
                $item['message'] = JText::_('COM_SPSTGSRV_FAILED_DECRYPTION');
            }
        }

        return $item;
    }

    function install($data) {
        if (!($package = $this->getPackage($data['package_path'] . $data['package_name']))) {
            $message = JText::_('COM_SPSTGSRV_INSTALL_INVALID_URL');
            return $message;
        }

        // Was the package unpacked?
        if (!$package) {
            $message = JText::_('COM_SPSTGSRV_UNABLE_TO_FIND_INSTALL_PACKAGE');
            return $message;
        }

        // Get an installer instance
        $installer = JInstaller::getInstance();

        // Install the package
        if (!$installer->install($package['dir'])) {
            // There was an error installing the package
            $message = JText::sprintf('COM_INSTALLER_INSTALL_ERROR', JText::_('COM_INSTALLER_TYPE_TYPE_' . strtoupper($package['type'])));
            return $message;
        }

        // Cleanup the install files
        if (!is_file($package['packagefile'])) {
            $config = JFactory::getConfig();
            $package['packagefile'] = $config->get('tmp_path') . '/' . $package['packagefile'];
        }

        JInstallerHelper::cleanupInstall($package['packagefile'], $package['extractdir']);

        return 'success';
    }

    function update($data) {

        $uid = $data['extension_id'];
        $update = new JUpdate();
        $instance = JTable::getInstance('update');
        $instance = JTableUpdate::getInstance('update');
        $instance->load(Array('extension_id' => $uid));

        if (!$update->loadFromXML($instance->detailsurl)) {
            $message = JText::_('COM_SPSTGSRV_FAILED_TO_READ_XML');
            return $message;
        }

        // install sets state and enqueues messages
        $result = $this->installUpdate($update);

        if ($result) {
            $this->purge();
        }

        if ($result) {
            return 'success';
        } else {
            return JText::_('COM_SPSTGSRV_FAILED_TO_UPDATE');
        }
    }

    /**
     * Handles the actual update installation.
     *
     * @param	JUpdate	An update definition
     * @return	boolean	Result of install
     */
    private function installUpdate($update) {
        $app = JFactory::getApplication();

        if (isset($update->get('downloadurl')->_data)) {
            $url = $update->downloadurl->_data;
        } else {
            //JError::raiseWarning('', JText::_('COM_INSTALLER_INVALID_EXTENSION_UPDATE'));
            return false;
        }

        $p_file = JInstallerHelper::downloadPackage($url);

        // Was the package downloaded?
        if (!$p_file) {
            //JError::raiseWarning('', JText::sprintf('COM_INSTALLER_PACKAGE_DOWNLOAD_FAILED', $url));
            return false;
        }

        $config = JFactory::getConfig();
        $tmp_dest = $config->get('tmp_path');

        // Unpack the downloaded package file
        $package = JInstallerHelper::unpack($tmp_dest . '/' . $p_file);

        // Get an installer instance
        $installer = JInstaller::getInstance();
        $update->set('type', $package['type']);

        // Install the package
        if (!$installer->update($package['dir'])) {
            // There was an error updating the package
            //$msg = JText::sprintf('COM_INSTALLER_MSG_UPDATE_ERROR', JText::_('COM_INSTALLER_TYPE_TYPE_'.strtoupper($package['type'])));
            $result = false;
        } else {
            // Package updated successfully
            //$msg = JText::sprintf('COM_INSTALLER_MSG_UPDATE_SUCCESS', JText::_('COM_INSTALLER_TYPE_TYPE_'.strtoupper($package['type'])));
            $result = true;
        }

        // Quick change
        $this->type = $package['type'];

        // Cleanup the install files
        if (!is_file($package['packagefile'])) {
            $config = JFactory::getConfig();
            $package['packagefile'] = $config->get('tmp_path') . '/' . $package['packagefile'];
        }

        JInstallerHelper::cleanupInstall($package['packagefile'], $package['extractdir']);

        return $result;
    }

    protected function getPackage($url) {
        // Download the package at the URL given
        $p_file = JInstallerHelper::downloadPackage($url);

        // Was the package downloaded?
        if (!$p_file) {
            return false;
        }

        $config = JFactory::getConfig();
        $tmp_dest = $config->get('tmp_path');

        // Unpack the downloaded package file
        $package = JInstallerHelper::unpack($tmp_dest . '/' . $p_file);

        return $package;
    }

    function encrypt($decrypted, $password, $salt) {
        $salt = 'xxx';
        // Build a 256-bit $key which is a SHA256 hash of $salt and $password.
        $key = hash('SHA256', $salt . $password, true);
        // Build $iv and $iv_base64.  We use a block size of 128 bits (AES compliant) and CBC mode.  (Note: ECB mode is inadequate as IV is not used.)
        srand();
        $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
        if (strlen($iv_base64 = rtrim(base64_encode($iv), '=')) != 22)
            return false;
        // Encrypt $decrypted and an MD5 of $decrypted using $key.  MD5 is fine to use here because it's just to verify successful decryption.
        $encrypted = base64_encode(mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $decrypted . md5($decrypted), MCRYPT_MODE_CBC, $iv));
        // We're done!
        return $iv_base64 . $encrypted;
    }

    function decrypt($encrypted, $password, $salt) {
        $salt = 'xxx';
        // Build a 256-bit $key which is a SHA256 hash of $salt and $password.
        $key = hash('SHA256', $salt . $password, true);
        // Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
        $iv = base64_decode(substr($encrypted, 0, 22) . '==');
        // Remove $iv from $encrypted.
        $encrypted = substr($encrypted, 22);
        // Decrypt the data.  rtrim won't corrupt the data because the last 32 characters are the md5 hash; thus any \0 character has to be padding.
        if (!($decrypted = @rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, base64_decode($encrypted), MCRYPT_MODE_CBC, $iv), "\0\4")))
            return false;
        // Retrieve $hash which is the last 32 characters of $decrypted.
        $hash = substr($decrypted, -32);
        // Remove the last 32 characters from $decrypted.
        $decrypted = substr($decrypted, 0, -32);
        // Integrity check.  If this fails, either the data is corrupted, or the password/salt was incorrect.
        if (md5($decrypted) != $hash)
            return false;
        // Yay!
        return $decrypted;
    }

    function authorizeIP($ip) {
        //disable IP authorization
        /*
        $params = &JComponentHelper::getParams('com_spstgsrv');
        $mngip = $params->get('mngip');
        if ($mngip == "localhost")
            $mngip = "127.0.0.1";

        if ($mngip == $ip) {
            return true;
        } else {
            return false;
        }
         * 
         */
        return true;
    }

    function getIP() {
        if (!empty($_SERVER["HTTP_CLIENT_IP"])) {
            //check for ip from share internet
            $ip = $_SERVER["HTTP_CLIENT_IP"];
        } elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])) {
            // Check for the Proxy User
            $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
        } else {
            $ip = $_SERVER["REMOTE_ADDR"];
        }

        if ($ip == "::1")
            $ip = "127.0.0.1";

        return $ip;
    }

    function save($item) {
        $table = $this->getTable('Log', 'SPStgSrvTable');
        $table->bind($item);
        $table->store();
    }

    public function getPackages($filter = null) {
        $search = $filter['search'];
        $enabled = $filter['enabled'];
        $type = $filter['type'];
        $client = $filter['client'];
        $group = $filter['group'];
        $hideprotected = $filter['hideprotected'];
        $limit = (int) $filter['sp_limit'];
        $start = (int) $filter['start'];

        $db = JFactory::getDBO();
        $query = $db->getQuery(true);
        $query->select('*');
        $query->from('#__extensions');
        $query->where('state=0');
        if ($hideprotected) {
            $query->where('protected!=1');
        }
        if ($enabled != '') {
            $query->where('enabled=' . intval($enabled));
        }
        if ($type) {
            $query->where('type=' . $this->_db->Quote($type));
        }
        if ($client != '') {
            $query->where('client_id=' . intval($client));
        }
        if ($group != '' && in_array($type, array('plugin', 'library', ''))) {

            $query->where('folder=' . $this->_db->Quote($group == '*' ? '' : $group));
        }

        // Filter by search in title.
        if (!empty($search)) {
            if (stripos($search, 'id:') === 0) {
                $query->where('extension_id = ' . (int) substr($search, 3));
            } else {
                $search = $db->Quote('%' . $db->escape($search, true) . '%');
                $query->where('(name LIKE ' . $search . ' OR element LIKE ' . $search . ')');
            }
        }

        $db->setQuery($query, $start, $limit);
        if (!$db->query()) {
            $items['result'] = '0';
            $items['message'] = $db->getErrorMsg();
            return $items;
        }

        $items = $db->loadAssocList();
        if (empty($items)) {
            $items['message'] = JText::_('COM_SPSTGSRV_READ_EMPTY');
            $items['result'] = '0';
            return $items;
        }

        //Get name and updates
        $lang = & JFactory::getLanguage();
        foreach ($items as $key => $item) {
            if (!$lang->load($item['element'], JPATH_ADMINISTRATOR)) {
                if (!$lang->load($item['element'], JPATH_SITE)) {
                    $lang->load('plg_' . $item['folder'] . '_' . $item['element'], JPATH_ADMINISTRATOR);
                }
            }
            $items[$key]['name'] = JText::_($item['name']);
            $manifest_cache = json_decode($items[$key]['manifest_cache']);
            $manifest_cache->name = JText::_($manifest_cache->name);
            $manifest_cache->description = JText::_($manifest_cache->description);
            $items[$key]['manifest_cache'] = json_encode($manifest_cache);
            $items[$key]['updates'] = $this->getUpdates($item['extension_id']);
        }

        $items['message'] = 'success';
        $items['result'] = '1';
        return $items;
    }
    
    public function count($filter = null) {
        $search = $filter['search'];
        $enabled = $filter['enabled'];
        $type = $filter['type'];
        $client = $filter['client'];
        $group = $filter['group'];
        $hideprotected = $filter['hideprotected'];

        $db = JFactory::getDBO();
        $query = $db->getQuery(true);
        $query->select('COUNT(*)');
        $query->from('#__extensions');
        $query->where('state=0');
        if ($hideprotected) {
            $query->where('protected!=1');
        }
        if ($enabled != '') {
            $query->where('enabled=' . intval($enabled));
        }
        if ($type) {
            $query->where('type=' . $this->_db->Quote($type));
        }
        if ($client != '') {
            $query->where('client_id=' . intval($client));
        }
        if ($group != '' && in_array($type, array('plugin', 'library', ''))) {

            $query->where('folder=' . $this->_db->Quote($group == '*' ? '' : $group));
        }

        // Filter by search in title.
        if (!empty($search)) {
            if (stripos($search, 'id:') === 0) {
                $query->where('extension_id = ' . (int) substr($search, 3));
            } else {
                $search = $db->Quote('%' . $db->escape($search, true) . '%');
                $query->where('(name LIKE ' . $search . ' OR element LIKE ' . $search . ')');
            }
        }

        $db->setQuery($query);
        if (!$db->query()) {
            $items['result'] = '0';
            $items['message'] = $db->getErrorMsg();
            return $items;
        }
        
        $count = $db->loadResult();
        
        if (empty($count)) {
            $items['message'] = JText::_('COM_SPSTGSRV_READ_EMPTY');
            $items['result'] = '0';
            return $items;
        }
        
        $items['count'] = $count;
        $items['message'] = 'success';
        $items['result'] = '1';
        return $items;
    }

    function publish($data) {
        // Get a database connector
        $db = JFactory::getDBO();

        // Get a table object for the extension type
        $table = JTable::getInstance('Extension');
        JTable::addIncludePath(JPATH_ADMINISTRATOR . '/components/com_templates/tables');
        // Enable the extension in the table and store it in the database
        $table->load($data['package_id']);
        if ($table->type == 'template') {
            $style = JTable::getInstance('Style', 'TemplatesTable');
            if ($style->load(array('template' => $table->element, 'client_id' => $table->client_id, 'home' => 1))) {
                $message = JText::_('COM_SPSTGSRV_ERROR_DISABLE_DEFAULT_TEMPLATE_NOT_PERMITTED');
                return $message;
            }
        }

        //Return if different element
        if ($table->element != $data['package_element']) {
            return JText::_('COM_SPSTGSRV_ELEMENT_DIFFERENT');
        }

        $table->enabled = $data['enabled'];
        if (!$table->store()) {
            $message = $table->getError();
            $result = $message;
        }

        return 'success';
    }

    function remove($data) {
        // Initialise variables.
        $failed = array();

        // Get a database connector
        $db = JFactory::getDBO();

        // Get an installer object for the extension type
        $installer = JInstaller::getInstance();
        $row = JTable::getInstance('extension');

        // Uninstall the chosen extensions
        $id = trim($data['package_id']);
        $row->load($id);

        //Return if different element
        if ($row->element != $data['package_element']) {
            return JText::_('COM_SPSTGSRV_ELEMENT_DIFFERENT');
        }

        if ($row->type) {
            $result = $installer->uninstall($row->type, $id);

            // Build an array of extensions that failed to uninstall
            if ($result === false) {
                $failed[] = $id;
            }
        } else {
            $failed[] = $id;
        }

        if (count($failed)) {

            // There was an error in uninstalling the package
            $message = JText::_('COM_SPSTGMNG_UNINSTALL_ERROR');
            return $message;
        }

        return 'success';
    }

    function sync($data) {
        $id = $data['package_id'];

        // Get an installer object for the extension type
        $installer = JInstaller::getInstance();
        $row = JTable::getInstance('extension');
        $result = 0;

        // Refresh manifest cache
        $result|= $installer->refreshManifestCache($id);

        $row->load($id);
        $filter['search'] = $row->get('element');
        $filter['enabled'] = '';
        $filter['type'] = '';
        $filter['client'] = '';
        $filter['group'] = '';
        $filter['hideprotected'] = '';
        return $this->getPackages($filter);
    }

    function find($data) {

        $result = Array();
        //Purge cache
        if (!$this->purge()) {
            $result['result'] = 0;
            $result['message'] = JText::_('COM_SPSTGSRV_FAILED_TO_PURGE_UPDATES');
            return $result;
        }
        if (!$this->enableSites())
            return JText::_('COM_SPSTGSRV_FAILED_TO_ENABLE_UPDATES');

        //Find        
        $cache_timeout = 21600;
        $updater = JUpdater::getInstance();
        //if(!$updater->findUpdates(0, $cache_timeout)) return JText::_('COM_SPSTGSRV_FAILED_TO_FIND_UPDATES');
        try {
            $updater->findUpdates(0, $cache_timeout);
        } catch (Exception $exc) {
            $result['result'] = 0;
            $result['message'] = JText::_('COM_SPSTGSRV_FAILED_TO_FIND_UPDATES');
            return $result;
        }

        try {
            $updates = $this->getUpdates();
        } catch (Exception $exc) {
            $result['result'] = 0;
            $result['message'] = JText::_('COM_SPSTGSRV_FAILED_TO_FIND_UPDATES');
            return $result;
        }

        $updates['result'] = 1;
        return $updates;
    }

    public function purge() {
        $db = JFactory::getDBO();
        // Note: TRUNCATE is a DDL operation
        // This may or may not mean depending on your database
        $db->setQuery('TRUNCATE TABLE #__updates');
        if ($db->execute()) {
            // Reset the last update check timestamp
            $query = $db->getQuery(true);
            $query->update($db->qn('#__update_sites'));
            $query->set($db->qn('last_check_timestamp') . ' = ' . $db->q(0));
            $db->setQuery($query);
            $db->execute();

            return true;
        } else {
            return false;
        }
    }

    public function enableSites() {
        $db = JFactory::getDBO();
        $db->setQuery('UPDATE #__update_sites SET enabled = 1 WHERE enabled = 0');
        if ($db->execute()) {
            return true;
        } else {
            return false;
        }
    }

    protected function getUpdates($pk = null) {
        $db = $this->getDbo();
        $query = $db->getQuery(true);
        $query->select('*')->from('#__updates');
        if (is_null($pk)) {
            $query->where('extension_id != 0');
        } else {
            $query->where('extension_id = ' . $pk);
        }

        $db->setQuery($query);
        if (!$db->query())
            return false;
        return $db->loadObjectList();
    }
    
    public function backup() {

        $this->backup_db();
        $this->backup_files();

        $items['message'] = 'success';
        $items['result'] = '1';
        return $items;
    }
    
    public function backup_files() {
                
        set_time_limit(0);

        $config = JFactory::getConfig();
        $backup_dir = JPATH_COMPONENT_ADMINISTRATOR . '/backups/';

        //backup files
        $backup_file = $backup_dir.'site.tgz';
        $command = 'tar --create \
                    --file='.$backup_file.' \
                    --gzip \
                    --exclude-tag=sp_exclude_tag \
                    ./
            ';

        exec($command);
        if (filesize($backup_file) < 1000) {
            $items['message'] = 'Files backup failed.';
            $items['result'] = '0';
            return $items;
        }

        set_time_limit(30);

        $items['message'] = 'success';
        $items['result'] = '1';
        return $items;
    }
    
    public function backup_db() {
                
        set_time_limit(0);

        $config = JFactory::getConfig();
        $backup_dir = JPATH_COMPONENT_ADMINISTRATOR . '/backups/';

        //backup db
        exec("mysqldump -u " . $config->get('user') . " -p" . $config->get('password') . " " . $config->get('db') . " | gzip > " . $backup_dir . $config->get('db') . '.sql.gz');
        if (filesize($backup_dir . $config->get('db') . '.sql.gz') < 1000) {
            $items['message'] = 'Database backup failed.';
            $items['result'] = '0';
            return $items;
        }

        set_time_limit(30);

        $items['message'] = 'success';
        $items['result'] = '1';
        return $items;
    }

}
