<?php
/**
 * @version     backend/classes/actions.php 2015-01-14 14:15:00 UTC zanardi
 * @package     Watchful Client
 * @author      Watchful
 * @authorUrl   https://watchful.li
 * @copyright   Copyright (c) 2012-2015 watchful.li
 * @license     GNU/GPL v3 or later
 */

defined('_JEXEC') or die;
defined('WATCHFULLI_PATH') or die;

require_once dirname(__FILE__) . '/watchfulli.php';
/**
 * watchfulliActions class.
 */
class watchfulliActions
{

    /**
     * 
     */
    public function __construct()
    {
        if (!Watchfulli::checkToken())
        {
            Watchfulli::debug("[ERROR] Invalid token");
            die;
        }
        $this->application = JFactory::getApplication();
    }

    /**
     * 
     */
    public function test()
    {
        die("<~ok~>");
    }

    /**
     * Run a "find updates" for some specific extensions
     *
     * @return void
     */
    public function updateCheck()
    {
        jimport('joomla.updater.updater');
        $updater = JUpdater::getInstance();
        $extensions = json_decode(JRequest::getVar('extensions', '[]'));
        if (!is_array($extensions))
        {
            $this->application->close('COM_JMONITORING_CLIENT_UPDATECHECK_WRONG_JSON');
        }
        foreach ($extensions as $extension)
        {
            $updater->findUpdates(array($extension->id));
        }
        $this->application->close('COM_JMONITORING_CLIENT_UPDATECHECK_OK');
    }

    /**
     * Run a "purge cache" to reset updates data
     *
     * @return bool true on success
     */
    private function purgeCache()
    {
        if (Watchfulli::joomla()->RELEASE == '1.5')
        {
            return 'COM_JMONITORING_CLIENT_PURGECACHE_UNAVAILABLE';
        }
        
        require_once JPATH_ADMINISTRATOR . '/components/com_installer/models/update.php';
        $model = new InstallerModelUpdate;
        if ($model->purge() && $model->enableSites())
        {
            return 'COM_JMONITORING_CLIENT_PURGECACHE_OK';
        }
        else
        {
            return 'COM_JMONITORING_CLIENT_PURGECACHE_ERROR';
        }
    }
    
    /**
     * Wrapper for purgeCache method when called remotely
     */
    public function doPurgeCache()
    {
        $this->application->close($this->purgeCache());
    }
    
    /**
     * Read and return local log (removing all sensitive information like the 
     * secret key)
     *
     * @return string 
     */
    public function getLog()
    {
        if (Watchfulli::joomla()->RELEASE == '1.5')
        {
            $this->log_file = JFactory::getApplication()->getCfg("log_path") . '/error.php';
        }
        else
        {
            $this->log_file = JFactory::getConfig()->get('log_path') . '/watchfulli.log.php';
        }
        if( ! file_exists($this->log_file))
        {
            $this->application->close('COM_JMONITORING_CLIENT_GETLOG_NO_LOG');
        }
    
        $rows = array();
        foreach(explode("\n", file_get_contents($this->log_file)) as $row)
        {
            if (preg_match('/secret/',$row))
            {
                continue;
            }
            $rows[] = $row;
        }

        $this->application->close(implode("\n",$rows));
    }

    /**
     * Get parameters from request
     * 
     * @return stdClass or null
     */
    private function getExtensionParameters()
    {
        $extParams = JRequest::getVar('extParams', null);
        if ( ! $extParams)
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - No extParams!");
            return NULL;
        }
        Watchfulli::debug("WatchfulliActions::doUpdate - Raw extParams: " . print_r($extParams, true));
        
        $extParams = json_decode(stripslashes($extParams));        
        if ( ! is_object($extParams))
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - Unable to get a valid object from extParams. Possibly the JSON is broken.");
            return NULL;
        }
        Watchfulli::debug("WatchfulliActions::doUpdate - Decoded extParams: " . print_r($extParams, true));

        return $extParams;
    }
    
     /**
     * Run an extension update
     * 
     * @return void (closes the app with a success or error message)
     */
    public function doUpdate()
    {
        $app = JFactory::getApplication();
        if ($app->getCfg('offline'))
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - Site is offline, exiting");
            $app->close("COM_JMONITORING_CLIENT_SITE_IS_OFFLINE");
        }

        $extParams = $this->getExtensionParameters();
        $this->type = empty($extParams->type) ? null : $extParams->type;
        $this->package_name = empty($extParams->package_name) ? null : $extParams->package_name;
        $this->update_url = empty($extParams->update_url) ? null : $extParams->update_url;

        if ('1.5' == Watchfulli::joomla()->RELEASE)
        {
            $id = JRequest::getInt('extId', 0);
        }
        else
        {
            $id = $app->input->get('extId', 0);
        }

        //JCE update
        if ($this->type == 5)
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - JCE plugin, calling doInstallJCEPlugins instead");
            $app->close($this->doInstallJCEPlugins($id));
        }

        if ( ! $id)
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - no update id, calling doInstall instead");
            $app->close($this->doInstall()); // No update ID, try normal install
        }

        jimport('joomla.updater.update');
        jimport('joomla.database.table');

        $updaterow = JTable::getInstance('update');
        $updaterow->load($id);

        if ( ! $updaterow->update_id)
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - update record not found with the given id: " . $updaterow->update_id);
            $app->close("COM_JMONITORING_CANT_FIND_UPDATE_RECORD");
        }

        $update = new JUpdate;

        if ( ! $update->loadFromXML($updaterow->detailsurl))
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - unable to get load a valid XML from the given URL: " . $updaterow->detailsurl);
            $app->close("COM_JMONITORING_CANT_GET_UPDATE");
        }

        if (empty($this->update_url))
        {
            if (isset($update->get('downloadurl')->_data))
            {
                $this->update_url = $update->downloadurl->_data;
            }
            else
            {
                Watchfulli::debug("WatchfulliActions::doUpdate - unable to get the download URL for the update");
                $app->close("COM_JMONITORING_CANT_GET_UPDATE_URL");
            }
        }

        $file = JInstallerHelper::downloadPackage($this->update_url, $this->package_name);

        // Was the package downloaded?
        if ( ! $file)
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - unable to download the update");
            $app->close("COM_JMONITORING_CANT_DOWNLOAD_UPDATE");
        }

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

        // Rename the file with custom name
        if ($this->package_name && ($file != $this->package_name))
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - renaming the download package from $file to " . $this->package_name);
            JFile::move($tmp_path . '/' . $file, $tmp_path . '/' . $this->package_name);
            $file = $this->package_name;
        }

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

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

        $this->originalApp = $this->switchToWatchfulApp();

        // Install the package
        if (($installer->update($package['dir']) === false) && !$this->checkInstall($id))
        {
            Watchfulli::debug("WatchfulliActions::doUpdate - unable to execute the update");
            $app->close("COM_JMONITORING_CANT_INSTALL_UPDATE"); // There was an error updating the package
        }

        // replace application
        JFactory::$application = $this->originalApp;

        // 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']);
        $ver = $updaterow->version;
        $updaterow->delete($id);
        ob_clean();
        Watchfulli::debug("WatchfulliActions::doUpdate - update completed ok");
        $app->close("ok_" . $ver);
    }

    /**
     * Custom installer for JCEPlugins
     * 
     * @param int $id update id
     * @return string
     */
    private function doInstallJCEPlugins($id)
    {
        jimport('joomla.filesystem.folder');
        $jceBase = JPATH_ADMINISTRATOR . '/components/com_jce/includes/base.php';
        if (!file_exists($jceBase))
        {
            return 'COM_JMONITORING_JCE_NOT_INSTALLED';
        }
        
        require_once($jceBase);
        require_once(JPATH_ADMINISTRATOR . '/components/com_jce/models/updates.php');

        $WFModelUpdates = new WFModelUpdates();

        JRequest::setVar('id', $id);
        $result = json_decode($WFModelUpdates->download());

        if (!$result->file)
        {
            return "COM_JMONITORING_CANT_DOWNLOAD_UPDATE";
        }

        // set vars
        JRequest::setVar('file', $result->file);
        JRequest::setVar('hash', $result->hash, 'post');
        JRequest::setVar('installer', $result->installer);
        JRequest::setVar('type', $result->type);

        $install = json_decode($WFModelUpdates->install());

        if ($intall->error)
        {
            return "COM_JMONITORING_CANT_INSTALL_UPDATE";
        }

        $plugin_name_parts = explode("_", $result->file);

        if(count($plugin_name_parts)==3)
        {
            $finalDir = $plugin_name_parts[1];
        }
        else
        {
            $finalDir = $plugin_name_parts[1].'_'.$plugin_name_parts[2];;
        }


        $source = JPATH_ROOT . "/components/com_watchfulli/editor/tiny_mce/plugins/" . $finalDir;
        $destination = JPATH_ROOT . "/components/com_jce/editor/tiny_mce/plugins/" . $finalDir;

        if (!JFolder::delete($destination))
            return 'JCE - can delete ' . $destination;

        if (!JFolder::move($source, $destination))
            return 'JCE - can move from ' . $source . ' to ' . $destination;

        $path = JPATH_ROOT . "/components/com_watchfulli/editor/";
        if (!JFolder::delete($path))
            return 'JCE - can delete ' . $path;

        return "ok_" . $result->file;
    }

    /**
     * Manually check if the update has been completed successfully
     * This is required because some of installer scripts do not return a clear
     * true / false message
     * 
     * @param int $id update id
     * @return bool true if update is ok
     */
    public function checkInstall($id)
    {
        jimport('joomla.database.table');

        $updaterow = JTable::getInstance('update');
        if (!$updaterow->load($id))
        {
            // If the Id is no longer in the updater table, we can guess that the install went fine
            return true;
        }

        $extension = JTable::getInstance('extension');
        $extension->load($updaterow->extension_id);

        $current_version = json_decode($extension->manifest_cache)->version;
        $current_version = str_replace(array('FREE', 'PRO'), '', $current_version);
        $updated_version = str_replace(array('FREE', 'PRO'), '', $updaterow->version);

        return version_compare($current_version, $updated_version, '>=');
    }

    /**
     * Some installers always want to be in admin application. Some other send
     * a redirect after install. To fix all these exceptions, we use our own
     * application while installing.
     * 
     * @return JApplicationAdministrator the original application object
     */
    public function switchToWatchfulApp()
    {
        $originalApp = JFactory::getApplication();

        // Reset db driver override made by Falang extension
        JFactory::$database = null;        
        
        if (Watchfulli::joomla()->getShortVersion() == '3.2.0')
        {
            return $originalApp;
        }

        if (!class_exists('JAdministratorHelper'))
        {
            require_once JPATH_ADMINISTRATOR . '/includes/helper.php';
        }
        if (!class_exists('JAdministrator'))
        {
            require_once JPATH_ADMINISTRATOR . '/includes/application.php';
        }
        if (!($originalApp instanceof JAdministrator))
        {
            JFactory::$application = new WatchfulliApplication;
        }
        return $originalApp;
    }

    /**
     * With this function we just install a package with the passed URL
     */
    public function doInstall()
    {
        Watchfulli::debug("WatchfulliActions::doInstall - starting");
        if (empty($this->update_url))
        {
            return("COM_JMONITORING_CANT_GET_UPDATE_URL");
        }

        if ('1.5' == Watchfulli::joomla()->RELEASE)
        {
            jimport('joomla.installer.helper');
            jimport('joomla.installer.installer');
        }

        $file = JInstallerHelper::downloadPackage($this->update_url, $this->package_name);
        if ( ! $file)
        {
            return("COM_JMONITORING_CANT_DOWNLOAD_UPDATE");
        }

        try
        {
            $package = $this->unpackFile($file);
        } 
        catch (Exception $ex) 
        {
            return $ex->getMessage();
        }

        $installer = JInstaller::getInstance();
        if ('1.5' != Watchfulli::joomla()->RELEASE)
        {
            $this->originalApp = $this->switchToWatchfulApp();
        }
        if ($installer->install($package['dir']) === false && !JFactory::getApplication()->installStatus)
        {
            return("COM_JMONITORING_CANT_INSTALL_UPDATE"); // There was an error updating the package
        }
        if ('1.5' != Watchfulli::joomla()->RELEASE)
        {
            JFactory::$application = $this->originalApp;
        }

        JInstallerHelper::cleanupInstall($package['packagefile'], $package['extractdir']);
        
        $this->purgeCache();

        if ('1.5' == Watchfulli::joomla()->RELEASE)
        {
            return("ok_" . $installer->_manifest->document->version[0]->_data);
        }
        return("ok_" . $installer->manifest->version);
    }

    /**
     * Unpack a given file
     * 
     * @param string $file   the name of the file to unpack
     * @return object   a package object
     */
    public function unpackFile($file)
    {
        if ( ! $file)
        {
            throw new Exception('COM_JMONITORING_CANT_UNPACK_UPDATE_EMPTY_FILE');
        }
        
        $tmp_path = JFactory::getConfig()->get('tmp_path', JPATH_SITE . '/tmp');
        if ( ! file_exists($tmp_path))
        {
            throw new Exception('COM_JMONITORING_CANT_UNPACK_UPDATE_WRONG_TMP_PATH');
        }

        // Rename the file with custom name if present
        if ($this->package_name && ($file != $this->package_name))
        {
            JFile::move($tmp_path . '/' . $file, $tmp_path . '/' . $this->package_name);
            $file = $this->package_name;
        }
        
        if ( ! file_exists($tmp_path . '/' . $file))
        {
            throw new Exception('COM_JMONITORING_CANT_UNPACK_UPDATE_MISSING_FILE');
        }
        
        $package = JInstallerHelper::unpack($tmp_path . '/' . $file);
        if (empty($package))
        {
            throw new Exception('COM_JMONITORING_CANT_UNPACK_UPDATE');
        }
        
        return $package;
    }

}
if (Watchfulli::joomla()->getShortVersion() != '3.2.0')
{
    if (!class_exists('JAdministrator'))
    {
        require_once JPATH_ADMINISTRATOR . '/includes/application.php';
    }
    /**
     * WatchfulliApplication class.
     * 
     * @see JAdministrator
     */
    class WatchfulliApplication extends JAdministrator
    {
        public $_name = 'Administrator';
        public $installStatus;

        /**
         * Some extensions force an application redirect after install and
         * that breaks our remote updates. So we override the redirect
         * method by setting a successful install status and skipping the
         * redirect itself.
         * This is far from perfect, because the redirect could also be raised by 
         * an error status, but it's the best we came up with to deal with the 
         * mentioned non-standard behaviour.
         * 
         * @param string $url
         * @return boolean
         */
        public function redirect($url, $msg = '', $msgType = 'message', $moved = false)
        {
            $this->installStatus = true;
            return true;
        }

    }
}
