<?php
/**
* Community Builder (TM)
* @version $Id: $
* @package CommunityBuilder
* @copyright (C) 2004-2021 www.joomlapolis.com / Lightning MultiCom SA - and its licensors, all rights reserved
* @license http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU/GPL version 2
*/

use CBLib\Application\Application;
use CBLib\Registry\GetterInterface;
use CB\Database\Table\PluginTable;

if ( ! ( defined( '_VALID_CB' ) || defined( '_JEXEC' ) || defined( '_VALID_MOS' ) ) ) { die( 'Direct Access to this location is not allowed.' ); }

class cbpackageinstallerAdmin
{

	/**
	 * @param object $row
	 * @param string $option
	 * @param string $task
	 * @param int    $uid
	 * @param string $action
	 * @param string $element
	 * @param int    $mode
	 * @param object $pluginParams
	 */
	public function editPluginView( $row, $option, $task, $uid, $action, $element, $mode, $pluginParams )
	{
		$install	=	new packageinstallerInstall( 'cb' );

		$install->install();
	}
}

class packageinstallerInstall
{
	/** @var null|PluginTable  */
	private $plugin		=	null;

	/**
	 * @param string $location
	 */
	public function __construct( $location = 'joomla' )
	{
		if ( $location === 'cb' ) {
			static $plugin	=	null;

			if ( $plugin === null ) {
				$plugin		=	new PluginTable();

				$plugin->load( array( 'element' => 'cbpackageinstaller' ) );
			}

			$this->plugin	=	$plugin;
		}

		// Override error handlers for this install process so we can catch them and output a proper JSON response with the error message
		set_exception_handler( array( __CLASS__, 'handleException' ) );
	}

	public static function handleException( $error )
	{
		$message		=	$error->getMessage();

		if ( JDEBUG ) {
			$message	.=	"\n" . $error->getTraceAsString();
		}

		header( 'HTTP/1.1 500 Internal Server Error' );

		/** @noinspection PhpStatementHasEmptyBodyInspection */
		/** @noinspection LoopWhichDoesNotLoopInspection */
		/** @noinspection MissingOrEmptyGroupStatementInspection */
		while ( @ob_end_clean() ) {}

		echo $message;

		exit();
	}

	public function install()
	{
		$this->prepare();
	}

	private function prepare()
	{
		global $_PLUGINS;

		try {
			if ( $this->plugin ) {
				$input				=	Application::Input();
				$view				=	$input->get( 'action', null, GetterInterface::COMMAND );
				$folder				=	$input->get( 'fld', null, GetterInterface::STRING );
				$package			=	$input->get( 'pkg', null, GetterInterface::STRING );
			} else {
				$jInput				=	JFactory::getApplication()->input;
				$view				=	$jInput->get( 'view', null, 'cmd' );

				if ( ! $view ) {
					$view			=	$jInput->get( 'task', null, 'cmd' );
				}

				$folder				=	$jInput->get( 'fld', null, 'string' );
				$package			=	$jInput->get( 'pkg', null, 'string' );

				jimport( 'joomla.application.component.model' );
				jimport( 'joomla.installer.installer' );
				jimport( 'joomla.installer.helper' );
			}

			if ( $view === 'uninstallself' ) {
				$this->installResult( $this->uninstallInstaller() );
			}

			if ( $view !== 'installpkg' ) {
				$this->installResult( array( 'status' => 'error', 'message' => null ) );
			}

			if ( ( ! $folder ) || ( ! $package ) ) {
				$this->installResult( array( 'status' => 'error', 'message' => null ) );
			}

			if ( $this->plugin ) {
				$packageDir			=	$_PLUGINS->getPluginPath( $this->plugin ) . '/extensions/' . $folder . '/' . $package;
			} else {
				$packageDir			=	JPATH_ADMINISTRATOR . '/components/com_packageinstaller/extensions/' . $folder . '/' . $package;
			}

			if ( ! file_exists( $packageDir ) ) {
				$this->installResult( array( 'status' => 'error', 'message' => null ) );
			}

			switch ( $folder ) {
				case 'custom':
					$results	=	$this->installCustom( $package, $packageDir );
					break;
				case 'scripts':
					$results	=	$this->installScript( $package, $packageDir );
					break;
				case 'queries':
					$results	=	$this->installQuery( $package, $packageDir );
					break;
				case 'overrides':
					$results	=	$this->installOverride( $package, $packageDir );
					break;
				case 'cb_plugins':
					$results	=	$this->installCBPlugin( $package, $packageDir );
					break;
				default:
					$results	=	$this->installJoomlaExtension( $package, $packageDir );
					break;
			}
		} catch ( Exception $e ) {
			$message			=	$e->getMessage();

			if ( JDEBUG ) {
				$message		.=	"\n" . $e->getTraceAsString();
			}

			$results			=	array( 'status' => 'error', 'message' => $message );
		}

		$this->installResult( $results );
	}

	/**
	 * @param array $data
	 */
	private function installResult( $data )
	{
		if ( $data['status'] === 'failed' ) {
			header( 'HTTP/1.1 500 Internal Server Error' );
		} else {
			header( 'HTTP/1.1 200 OK' );
		}

		/** @noinspection PhpStatementHasEmptyBodyInspection */
		/** @noinspection LoopWhichDoesNotLoopInspection */
		/** @noinspection MissingOrEmptyGroupStatementInspection */
		while ( @ob_end_clean() ) {}

		if ( $data['message'] ) {
			echo $data['message'];
		}

		exit();
	}

	/**
	 * @return array
	 */
	private function uninstallInstaller()
	{
		if ( $this->plugin ) {
			ob_start();
			$plgInstaller	=	new cbInstallerPlugin();
			$uninstalled	=	$plgInstaller->uninstall( $this->plugin->id, 'com_comprofiler' );
			ob_end_clean();

			if ( $uninstalled ) {
				return array( 'status' => 'success', 'message' => null );
			}

			return array( 'status' => 'error', 'message' => $plgInstaller->getError() );
		}

		$component			=	JComponentHelper::getComponent( 'com_packageinstaller' );
		$installer			=	JInstaller::getInstance();

		if ( $installer->uninstall( 'component', $component->id ) ) {
			return array( 'status' => 'success', 'message' => null );
		}

		return array( 'status' => 'error', 'message' => JFactory::getApplication()->getMessageQueue() );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installCustom( $package, $packageDir )
	{
		$packageDest	=	JPATH_SITE . '/images/' . $package;

		if ( @copy( $packageDir, $packageDest ) ) {
			return array( 'status' => 'success', 'message' => null );
		}

		return array( 'status' => 'error', 'message' => null );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installScript( $package, $packageDir )
	{
		ob_clean();
		include_once( $packageDir );
		$return		=	ob_get_clean();

		if ( $return ) {
			if ( preg_match( '/(^TRUE$|^FALSE$|^TRUE:|^FALSE:)(.*)/i', $return, $scriptResults ) ) {
				if ( ( $scriptResults[1] === 'TRUE' ) || ( $scriptResults[1] === 'TRUE:' ) ) {
					return array( 'status' => 'success', 'message' => $scriptResults[2] );
				}

				if ( ( $scriptResults[1] === 'FALSE' ) || ( $scriptResults[1] === 'FALSE:' ) ) {
					return array( 'status' => 'error', 'message' => $scriptResults[2] );
				}
			}

			if ( (bool) $return === true ) {
				return array( 'status' => 'success', 'message' => null );
			}

			if ( (bool) $return === false ) {
				return array( 'status' => 'error', 'message' => null );
			}
		}

		return array( 'status' => 'success', 'message' => null );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installQuery( $package, $packageDir )
	{
		$buffer						=	file_get_contents( $packageDir );

		if ( ! $buffer ) {
			return array( 'status' => 'error', 'message' => null );
		}

		$database					=	JFactory::getDbo();
		$queries					=	JDatabaseDriver::splitSql( $buffer );

		if ( ! $queries ) {
			return array( 'status' => 'error', 'message' => null );
		}

		$errors						=	array();

		if ( $queries ) {
			foreach ( $queries as $query ) {
				$query				=	trim( $query );

				if ( ( $query !== '' ) && ( $query[0] !== '#' ) ) {
					$database->setQuery( $query );

					try {
						$database->execute();
					} catch ( Exception $e ) {
						$errors[]	=	array( 'status' => 'error', 'message' => $e->getMessage() );
					}
				}
			}
		}

		if ( ! $errors ) {
			return array( 'status' => 'success', 'message' => null );
		}

		return array( 'status' => 'error', 'message' => $errors );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installOverride( $package, $packageDir )
	{
		$zip				=	new ZipArchive();
		$zipOpen			=	$zip->open( $packageDir );

		if ( $zipOpen === true ) {
			$zip->extractTo( JPATH_ROOT . '/' );
			$zip->close();

			return array( 'status' => 'success', 'message' => null );
		}

		$zipError			=	null;

		switch ( $zipOpen ) {
			case 4:
				$zipError	=	'Seek error. Error Code: ZIPARCHIVE::ER_SEEK';
				break;
			case 5:
				$zipError	=	'Read error. Error Code: ZIPARCHIVE::ER_READ';
				break;
			case 9:
				$zipError	=	'No such file. Error Code: ZIPARCHIVE::ER_NOENT';
				break;
			case 10:
				$zipError	=	'File already exists. Error Code: ZIPARCHIVE::ER_EXISTS';
				break;
			case 11:
				$zipError	=	'Can not open file. Error Code: ZIPARCHIVE::ER_OPEN';
				break;
			case 14:
				$zipError	=	'Malloc failure. Error Code: ZIPARCHIVE::ER_MEMORY';
				break;
			case 18:
				$zipError	=	'Invalid argument. Error Code: ZIPARCHIVE::ER_INVAL';
				break;
			case 19:
				$zipError	=	'Not a zip archive. Error Code: ZIPARCHIVE::ER_NOZIP';
				break;
			case 21:
				$zipError	=	'Zip archive inconsistent. Error Code: ZIPARCHIVE::ER_INCONS';
				break;
		}

		return array( 'status' => 'error', 'message' => $zipError );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installCBPlugin( $package, $packageDir )
	{
		if ( ! $this->plugin ) {
			if ( ( ! file_exists( JPATH_SITE . '/libraries/CBLib/CBLib/Core/CBLib.php' ) ) || ( ! file_exists( JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php' ) ) ) {
				return array( 'status' => 'error', 'message' => 'CB not installed' );
			}

			include_once( JPATH_ADMINISTRATOR . '/components/com_comprofiler/plugin.foundation.php' );

			cbimport( 'cb.html' );
		}

		cbimport( 'cb.installer' );

		$installer			=	new cbInstallerPlugin();
		$installed			=	false;

		if ( $installer->upload( $packageDir ) ) {
			if ( $installer->preInstallCheck( null, $installer->elementType ) && $installer->install() ) {
				$installed	=	true;
			}

			$installer->cleanupInstall( null, $installer->unpackDir() );
		}

		$msg				=	$installer->getError();

		if ( $installed ) {
			return array( 'status' => 'success', 'message' => $msg );
		}

		return array( 'status' => 'error', 'message' => $msg );
	}

	/**
	 * @param string $package
	 * @param string $packageDir
	 * @return array
	 */
	private function installJoomlaExtension( $package, $packageDir )
	{
		$extension			=	JInstallerHelper::unpack( $packageDir );
		$installed			=	false;
		$msg				=	null;

		if ( $extension ) {
			$installer		=	JInstaller::getInstance();

			$installer->setPath( 'source', $extension['dir'] );

			if ( $installer->setupInstall() && $installer->install( $extension['dir'] ) ) {
				$installed	=	true;
			}

			$msg			=	$installer->get( 'message' ) . $installer->get( 'extension_message' );

			JInstallerHelper::cleanupInstall( null, $extension['dir'] );
		}

		if ( $installed ) {
			return array( 'status' => 'success', 'message' => $msg );
		}

		return array( 'status' => 'error', 'message' => JFactory::getApplication()->getMessageQueue() );
	}
}