<?php 
/**
* @version		$Id: installer.class.php 2311 2019-12-07 07:56:11Z IOS $
* @package		Elxis
* @subpackage	Extensions manager / Elxis installer
* @copyright	Copyright (c) 2006-2019 Elxis CMS (https://www.elxis.org). All rights reserved.
* @license		Elxis Public License ( https://www.elxis.org/elxis-public-license.html )
* @author		Elxis Team ( https://www.elxis.org )
* @description 	Elxis CMS is free software. Read the license for copyright notices and details
*/

defined('_ELXIS_') or die ('Direct access to this location is not allowed');


class elxisInstaller {

	private $efiles = null;
	private $tmpdir = ''; //absolute path to repository/tmp/
	private $package = ''; //package file name as uploaded in repository/tmp/
	private $ufolder = ''; //initial folder name created after unzip and located in repository/tmp/
	private $uffolder = ''; //path relative to repository/tmp/ where extension files have been unzipped (it might be the same as ufolder or a sub-folder of it).
	private $ifolder = ''; //final installation folder relative to elxis root (i.e. modules/mod_test/)
	private $head = null;
	private $dependencies = array();
	private $is_update = false;
	private $is_synchro = false;
	private $cur_version = 0;
	private $cur_created = '';
	private $cur_author = '';
	private $cur_authorurl = '';
	private $warnings = array();
	private $notify_install = 1;
	private $notify_uninstall = 1;
	private $notify_update = 1;
	private $log_install = 1;
	private $log_uninstall = 1;
	private $log_update = 1;
	private $errormsg = '';
	private $lastid = 0;


	/***************/
	/* CONSTRUCTOR */
	/***************/
	public function __construct() {
		$this->efiles = eFactory::getFiles();
		$this->tmpdir = $this->efiles->elxisPath('tmp/', true);
		$this->loadParams();
	}


	/*************************/
	/* LOAD COMPONENT PARAMS */
	/*************************/
	private function loadParams() {
		$db = eFactory::getDB();

		$component = 'com_extmanager';
		$sql = "SELECT ".$db->quoteId('params')." FROM ".$db->quoteId('#__components')." WHERE ".$db->quoteId('component')." = :xcomp";
		$stmt = $db->prepareLimit($sql, 0, 1);
		$stmt->bindParam(':xcomp', $component, PDO::PARAM_STR);
		$stmt->execute();
		$params_str = $stmt->fetchResult();
		if ($params_str) {
			elxisLoader::loadFile('includes/libraries/elxis/parameters.class.php');
			$params = new elxisParameters($params_str, '', 'component');
			$this->notify_install = (int)$params->get('notify_install', 1);
			$this->notify_uninstall = (int)$params->get('notify_uninstall', 1);
			$this->notify_update = (int)$params->get('notify_update', 1);
			$this->log_install = (int)$params->get('log_install', 1);
			$this->log_uninstall = (int)$params->get('log_uninstall', 1);
			$this->log_update = (int)$params->get('log_update', 1);
			unset($params);
		}
	}


	/************************************************************/
	/* SET PACKAGE FILENAME (UPLOADED IN REPOSITORY TMP FOLDER) */
	/************************************************************/
	public function setPackage($package) {
		$this->package = trim($package);
	}


	/******************************/
	/* CLEAN FILES/FOLDERS IN TMP */
	/******************************/
	private function cleanUP() {
		if (($this->package != '') && is_file($this->tmpdir.$this->package)) {
			$this->efiles->deleteFile('tmp/'.$this->package, true);
		}
		if (($this->ufolder != '') && is_dir($this->tmpdir.$this->ufolder.'/')) {
			$this->efiles->deleteFolder('tmp/'.$this->ufolder.'/', true);
		}
	}


	/***********************/
	/* DELETE PACKAGE FILE */
	/***********************/
	public function deletePackage() {
		if (($this->package != '') && is_file($this->tmpdir.$this->package)) {
			$this->efiles->deleteFile('tmp/'.$this->package, true);
		}
	}


	/******************************************************/
	/* UNZIP AND CHECK PACKAGE, PREPARE TO INSTALL/UPDATE */
	/******************************************************/
	public function prepare($package='', $system=false) {
		$eLang = eFactory::getLang();

		$this->errormsg = '';
		$this->lastid = 0;
		if ($package != '') { $this->setPackage($package); }
		if ($this->package == '') {
			$this->errormsg = $eLang->exist('NO_PACK_SET') ? $eLang->get('NO_PACK_SET') : 'No package set!';
			return false;
		}

		if (!file_exists($this->tmpdir.$this->package)) {
			$this->errormsg = $eLang->exist('PACK_NOTFOUND_REPO') ? $eLang->get('PACK_NOTFOUND_REPO') : 'Package file not found in Elxis repository (folder tmp/)!';
			return false;
		}

		$folder = $this->efiles->getFilename($this->package);
		if (file_exists($this->tmpdir.$folder.'/') && is_dir($this->tmpdir.$folder.'/')) {
			$folder = 'dir'.microtime();
		}

		$ok = $this->efiles->createFolder('tmp/'.$folder.'/', 0755, true);
		if (!$ok) {
			$this->errormsg = $eLang->exist('CNOT_CREATE_TMPFO_REPO') ? $eLang->get('CNOT_CREATE_TMPFO_REPO') : 'Could not create temporary folder in Elxis repository (folder tmp/)!';
			$this->cleanUP();
			return false;
		}

		$this->ufolder = $folder.'/';

		$zip = eFactory::getElxis()->obj('zip');
		$ok = $zip->unzip($this->tmpdir.$this->package, $this->tmpdir.$this->ufolder);
		if (!$ok) {
			$this->errormsg = $zip->getError();
			$this->cleanUP();
			return false;
		}
		unset($zip);

		return $this->continueFromFolder($system);
	}


	/***************************************/
	/* PREPARE INSTALL/UPDATE FROM UFOLDER */
	/***************************************/
	public function prepareFromFolder($folder='', $system=false) {
		$folder = trim(trim($folder, '/'));
		if ($folder == '') {
			$this->errormsg = 'Temporary folder (ufolder) can not be empty!';
			$this->cleanUP();
			return false;
		}

		if (!file_exists($this->tmpdir.$folder.'/') || !is_dir($this->tmpdir.$folder.'/')) {
			$this->errormsg = 'Temporary folder (ufolder) was not found!';
			$this->cleanUP();
			return false;
		}

		$this->ufolder = $folder.'/';
		if ($this->package == '') {
			$this->package = 'it_does_not_matter.zip';
		}

		return $this->continueFromFolder($system);
	}


	/****************************************/
	/* CONTINUE INSTALL/UPDATE FROM UFOLDER */
	/****************************************/
	private function continueFromFolder($system=false) {
		$eLang = eFactory::getLang();

		$this->uffolder = $this->ufolder;
		$files = $this->efiles->listFiles('tmp/'.$this->ufolder, '.', false, false, true);
		if (!$files) {
			$folders = $this->efiles->listFolders('tmp/'.$this->ufolder, false, false, true);
			if (!$folders || !is_array($folders) || (count($folders) <> 1)) {
				$this->errormsg = $eLang->exist('INV_ELXIS_PACK') ? $eLang->get('INV_ELXIS_PACK') : 'Invalid Elxis package!';
				$this->cleanUP();
				return false;
			}
			$folder = $this->ufolder.$folders[0].'/';
			$files = $this->efiles->listFiles('tmp/'.$folder, '.', false, false, true);
			if (!$files) {
				$this->errormsg = $eLang->exist('INV_ELXIS_PACK') ? $eLang->get('INV_ELXIS_PACK') : 'Invalid Elxis package!';
				$this->cleanUP();
				return false;
			}
			$this->uffolder = $this->ufolder.$folders[0].'/';
			unset($folders, $folder);
		}

		$xmlfiles = array();
		foreach ($files as $file) {
			if (preg_match('/(\.xml)$/', $file)) { $xmlfiles[] = $file; }
		}

		if (count($xmlfiles) == 0) {
			$this->errormsg = $eLang->exist('XML_NFOUND_INV_PACK') ? $eLang->get('XML_NFOUND_INV_PACK') : 'Installation XML file was not found. Invalid Elxis package!';
			$this->cleanUP();
			return false;
		}

		$xmlfile = '';
		if (count($xmlfiles) == 1) {
			$xmlfile = $xmlfiles[0];
		} else { //more than 1 XML files
			foreach ($xmlfiles as $xfile) {
				foreach ($files as $file) {
					if (preg_match('/(\.php)$/', $file)) {
						$fname = $this->efiles->getFilename($file);
						if ($fname.'.xml' == $xfile) {
							$xmlfile = $xfile;
							break(2);
						}
					}
				}
			}

			if ($xmlfile == '') {
				foreach ($xmlfiles as $xfile) {
					if ($xfile == 'templateDetails.xml') {//compatibility for Elxis 2009.x templates
						$xmlfile = $xfile;
						break;
					}
				}
			}

			if ($xmlfile == '') { //nothing found, get the first one without dots in filename
				foreach ($xmlfiles as $xfile) {
					$fname = $this->efiles->getFilename($xfile);
					if (strpos($fname, '.') === false) {
						$xmlfile = $xfile;
						break;
					}
				}
			}

			//nothing found, get the first one...
			if ($xmlfile == '') { $xmlfile = $xmlfiles[0]; }
		}

		elxisLoader::loadFile('components/com_extmanager/includes/extension.xml.php');
		$exml = new extensionXML();
		$ok = $exml->parse($this->tmpdir.$this->uffolder.$xmlfile, true);
		if (!$ok) {
			$msg = $exml->getErrorMsg();
			if ($msg == '') { $msg = 'Could not parse extension XML file!'; }
			$this->errormsg = $msg;
			$this->cleanUP();
			return false;
		}
		$exml->checkDependencies();
		$this->head = $exml->getHead();
		$this->dependencies = $exml->getDependencies();
		unset($exml);

		$aclcheck = ($system == true) ? false : true;

		return $this->check($aclcheck);
	}


	/********************/
	/* MAKE SOME CHECKS */
	/********************/
	private function check($aclcheck=true) {
		$elxis = eFactory::getElxis();
		$eLang = eFactory::getLang();

		if (($this->head->type == '') || !in_array($this->head->type, array('component', 'module', 'template', 'engine', 'auth', 'plugin'))) {
			$this->errormsg = $eLang->exist('EXT_TYPE_XML_INVALID') ? $eLang->get('EXT_TYPE_XML_INVALID') : 'The Elxis extension type in XML file is not set ot it is invalid!';
			$this->cleanUP();
			return false;
		}

		switch ($this->head->type) {
			case 'component':
				$acl_element = 'components';
				$this->ifolder = 'components/'.$this->head->name.'/';
				if (!preg_match('/^(com\_)/', $this->head->name)) {
					$this->errormsg = 'A valid Elxis component name should start with com_!';
					$this->cleanUP();
					return false;
				}
			break;
			case 'module':
				$acl_element = 'modules';
				$this->ifolder = 'modules/'.$this->head->name.'/';
				if (!preg_match('/^(mod\_)/', $this->head->name)) {
					$this->errormsg = 'A valid Elxis module name should start with mod_!';
					$this->cleanUP();
					return false;
				}
			break;
			case 'template':
				$acl_element = 'templates';
				if ($this->head->section == 'backend') {
					$this->ifolder = 'templates/admin/'.$this->head->name.'/';
				} else {
					$this->ifolder = 'templates/'.$this->head->name.'/';
				}
			break;
			case 'engine':
				$acl_element = 'engines';
				$this->ifolder = 'components/com_search/engines/'.$this->head->name.'/';
			break;
			case 'auth':
				$acl_element = 'auth';
				$this->ifolder = 'components/com_user/auth/'.$this->head->name.'/';
			break;
			case 'plugin':
				$acl_element = 'plugins';
				$this->ifolder = 'components/com_content/plugins/'.$this->head->name.'/';
			break;
			default: break;
		}

		if ($aclcheck) {
			if ($elxis->acl()->check('component', 'com_extmanager', 'manage') < 1) {
				$this->errormsg = 'You are not allowed to manage extensions!';
				$this->cleanUP();
				return false;
			}

			if ($elxis->acl()->check('com_extmanager', $acl_element, 'install') < 1) {
				if ($eLang->exist('NALLOW_INSTALL_EXT')) {
					$this->errormsg = sprintf($eLang->get('NALLOW_INSTALL_EXT'), $acl_element);
				} else {
					$this->errormsg = 'You are not allowed to install '.$acl_element;
				}
				$this->cleanUP();
				return false;
			}
			if (($elxis->getConfig('SECURITY_LEVEL') > 0) && ($elxis->user()->gid <> 1)) {
				$this->errormsg = $eLang->exist('SECLEV_OADMIN_INSTALL') ? $eLang->get('SECLEV_OADMIN_INSTALL') : 'Under the current security level only administrators can install extensions!';
				$this->cleanUP();
				return false;
			}
		}

		$ipath = ELXIS_PATH.'/'.$this->ifolder;
		if (file_exists($ipath) && is_dir($ipath)) {
			$this->is_update = true;

			$exttype = $this->head->type;
			if (($this->head->type == 'template') && ($this->head->section == 'backend')) { $exttype= 'atemplate'; }

			elxisLoader::loadFile('components/com_extmanager/includes/extension.xml.php');
			$exml = new extensionXML();
			$info = $exml->quickXML($exttype, $this->head->name);

			if ($info['installed'] == false) {
				$this->errormsg = 'A folder found that should have the current version of the '.$this->head->type.' '.$this->head->name.' but the extension XML file was not found! If you previously uninstalled this extension but its folder remains delete that folder and re-try install.';
				$this->cleanUP();
				return false;
			}

			$this->cur_version = $info['version'];
			$this->cur_created = $info['created'];
			$this->cur_author = $info['author'];
			$this->cur_authorurl = $info['authorurl'];
			unset($exml, $info);

			if ($this->cur_version == 0) {
				$this->warnings[] = 'An unknown version of '.$dpc->type.' '.$dpc->extension.' is already installed!';
			}

			if ($this->cur_version == $this->head->version) {
				if ($eLang->exist('EXT_ALREADY_INSTALL')) {
					$this->errormsg = sprintf($eLang->get('EXT_ALREADY_INSTALL'), $this->head->title.' v'.$this->head->version);
				} else {
					$this->errormsg = 'Extension '.$this->head->title.' v'.$this->head->version.' is already installed!';
				}
				$this->cleanUP();
				return false;
			}

			if ($this->cur_version > $this->head->version) {
				if ($eLang->exist('NEWER_ALREADY_INSTALL')) {
					$this->errormsg = sprintf($eLang->get('NEWER_ALREADY_INSTALL'), $this->cur_version, $this->head->title);
				} else {
					$this->errormsg = 'A newer version ('.$this->cur_version.') of '.$this->head->title.' is already installed!';
				}
				$this->cleanUP();
				return false;
			}
		}

		if ($this->dependencies) {
			foreach ($this->dependencies as $dpc) {
				if (!$dpc->installed) {
					if ($eLang->exist('EXT_REQ_EXT_NOTINSTALL')) {
						$arg2 = $dpc->type.' '.$dpc->extension.' ('.implode(', ',$dpc->versions).')';
						$this->warnings[] = sprintf($eLang->get('EXT_REQ_EXT_NOTINSTALL'), $this->head->title, $arg2);
					} else {
						$this->warnings[] = $this->head->title.' requires '.$dpc->type.' '.$dpc->extension.' ('.implode(', ',$dpc->versions).') which is not installed!';
					}
				} elseif (!$dpc->icompatible) {
					if ($eLang->exist('EXT_REQ_EXT_NOTCOMPAT')) {
						$arg2 = $dpc->type.' '.$dpc->extension.' ('.implode(', ',$dpc->versions).')';
						$this->warnings[] = sprintf($eLang->get('EXT_REQ_EXT_NOTCOMPAT'), $this->head->title, $arg2, $dpc->iversion);
					} else {
						$this->warnings[] = $this->head->title.' requires '.$dpc->type.' '.$dpc->extension.' ('.implode(', ',$dpc->versions).') but the installed version ('.$dpc->iversion.') is not compatible!';
					}
				}
			}
		}

		return true;
	}


	/****************************/
	/* INSTALL/UPDATE EXTENSION */
	/****************************/
	public function install() {
		//just in case
		if ($this->errormsg != '') { return false; }
		if ($this->package == '') { return false; }
		if ($this->ufolder == '') { return false; }
		if ($this->uffolder == '') { return false; }
		if ($this->ifolder == '') { return false; }
		if ($this->head->type == '') { return false; }
		if ($this->head->name == '') { return false; }

		if (!$this->efiles->createFolder($this->ifolder)) {
			$this->errormsg = $this->efiles->getError();
			$this->cleanUP();
			return false;
		}

		$files = $this->efiles->listFiles('tmp/'.$this->uffolder, '.', false, false, true);
		if ($files) {
			foreach ($files as $file) {
				if ($this->is_update) {
					if (file_exists(ELXIS_PATH.'/'.$this->ifolder.$file)) {
						if (strpos($file, 'config') !== false) {
							continue; //dont overwrite "config" files on update!
						}
					}
				}

				if (!$this->efiles->move('tmp/'.$this->uffolder.$file, $this->ifolder.$file, true, false)) {
					$this->errormsg = $this->efiles->getError();
					$this->cleanUP();
					$this->efiles->deleteFolder($this->ifolder);
					return false;
				}
			}
		}
		unset($files);

		$folders = $this->efiles->listFolders('tmp/'.$this->uffolder, false, false, true);
		if ($folders) {
			foreach ($folders as $folder) {
				if (!$this->efiles->moveFolder('tmp/'.$this->uffolder.$folder.'/', $this->ifolder.$folder.'/', true, false)) {
					$this->errormsg = $this->efiles->getError();
					$this->cleanUP();
					$this->efiles->deleteFolder($this->ifolder);
					return false;
				}
			}
		}
		unset($folders);

		$this->cleanUP();

		$func = ($this->is_update) ? 'update_'.$this->head->type : 'install_'.$this->head->type;
		if (method_exists($this, $func)) {
			$ok = $this->$func();
			if (!$ok) { return false; }
		}

		//custom install actions
		if (file_exists(ELXIS_PATH.'/'.$this->ifolder.'install.php')) {
			elxisLoader::loadFile($this->ifolder.'install.php');
			$class = $this->head->name.'_installer';
			if (class_exists($class, false)) {
				$obj = new $class();
				if ($this->is_update) {
					if (method_exists($obj, 'update')) {
						$obj->update($this->cur_version);
					}
				} else {
					if (method_exists($obj, 'install')) {
						$obj->install();
					}
				}
				unset($obj);
			}
		}

		if ($this->is_update) {
			if ($this->log_update == 1) {
				$this->logger($this->head->name, 'update', $this->head->version);
			}
			if ($this->notify_update == 1) {
				$this->notify('update', $this->head->type, $this->head->name, $this->head->version);
			}
		} else {
			if ($this->log_install == 1) {
				$this->logger($this->head->name, 'install', $this->head->version);
			}
			if ($this->notify_install == 1) {
				$this->notify('install', $this->head->type, $this->head->name, $this->head->version);
			}
		}

		return true;
	}


	/********************/
	/* UPDATE EXTENSION */
	/********************/
	public function update() {
		$ok = $this->install();
		return $ok;
	}


	/***********************/
	/* UNINSTALL EXTENSION */
	/***********************/
	public function uninstall($type, $extension, $id, $version='') {
		$this->errormsg = '';
		$this->lastid = 0;
		$id = (int)$id;

		if ($id < 1) {
			$this->errormsg = 'You must provide the Elxis Installer the exact extension id!';
			return false;
		}

		$fn = $type;
		switch($type) {
			case 'template':
				$idir = 'templates/'.$extension.'/';
			break;
			case 'atemplate':
				$idir = 'templates/admin/'.$extension.'/';
				$fn = 'template';
			break;
			case 'module':
				$idir = 'modules/'.$extension.'/';
			break;
			case 'component':
				$idir = 'components/'.$extension.'/';
			break;
			case 'engine':
				$idir = 'components/com_search/engines/'.$extension.'/';
			break;
			case 'auth':
				$idir = 'components/com_user/auth/'.$extension.'/';
			break;
			case 'plugin':
				$idir = 'components/com_content/plugins/'.$extension.'/';
			break;
			default:
				$this->errormsg = 'Not supported Extension type ('.$type.')';
				return false;
			break;
		}

		$func = 'uninstall_'.$fn;
		if (method_exists($this, $func)) {
			$ok = $this->$func($extension, $id);
			if (!$ok) { return false; }
		}

		if (file_exists(ELXIS_PATH.'/'.$idir)) {
			if (file_exists(ELXIS_PATH.'/'.$idir.'install.php')) {
				include_once(ELXIS_PATH.'/'.$idir.'install.php');
				$class = $extension.'_installer';
				if (class_exists($class, false)) {
					$obj = new $class();
					if (method_exists($obj, 'uninstall')) {
						$obj->uninstall();
					}
					unset($obj);
				}
			}

			if (!$this->efiles->deleteFolder($idir)) {
				$this->errormsg = $this->efiles->getError();
				return false;
			}
		}

		$langs = $this->efiles->listFolders('language/');
		if ($langs) {
			foreach ($langs as $lng) {
				if ($type == 'engine') {
					$lf = ELXIS_PATH.'/language/'.$lng.'/'.$lng.'.engine_'.$extension.'.php';
				} else if ($type == 'auth') {
					$lf = ELXIS_PATH.'/language/'.$lng.'/'.$lng.'.auth_'.$extension.'.php';
				} else if ($type == 'plugin') {
					$lf = ELXIS_PATH.'/language/'.$lng.'/'.$lng.'.plugin_'.$extension.'.php';
				} else {
					$lf = ELXIS_PATH.'/language/'.$lng.'/'.$lng.'.'.$extension.'.php';
				}

				if (file_exists($lf)) { $this->efiles->deleteFile($lf); }
			}
		}

		if ($this->log_uninstall == 1) {
			$this->logger($extension, 'uninstall', $version);
		}

		if ($this->notify_uninstall == 1) {
			$this->notify('uninstall', $type, $extension, $version);
		}

		return true;
	}


	/*************************/
	/* SYNCHRONIZE EXTENSION */
	/*************************/
	public function synchronize($type, $extension) {
		$db = eFactory::getDB();
		$eLang = eFactory::getLang();

		$this->is_synchro = true;
		if ($this->errormsg != '') { return false; }
		if (!defined('ELXIS_MULTISITE') || (ELXIS_MULTISITE == 1)) {
			$this->errormsg = $eLang->get('SYNC_EXT_SUBSITES');
			return false;
		}

		switch ($type) {
			case 'component':
				$ifolder = 'components';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__components')." WHERE ".$db->quoteId('component')." = :xts";
			break;
			case 'module':
				$ifolder = 'modules';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__modules')." WHERE ".$db->quoteId('module')." = :xts";
			break;
			case 'template':
				$ifolder = 'templates';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__templates')." WHERE ".$db->quoteId('template')." = :xts";
			break;
			case 'engine':
				$ifolder = 'components/com_search/engines';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__engines')." WHERE ".$db->quoteId('engine')." = :xts";
			break;
			case 'auth':
				$ifolder = 'components/com_user/auth';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__authentication')." WHERE ".$db->quoteId('auth')." = :xts";
			break;
			case 'plugin':
				$ifolder = 'components/com_content/plugins';
				$sql = "SELECT COUNT(id) FROM ".$db->quoteId('#__plugins')." WHERE ".$db->quoteId('plugin')." = :xts";
			break;
			default:
				$this->errormsg = 'Invalid extension type!';
				return false;
			break;
		}

		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xts', $extension, PDO::PARAM_STR);
		$stmt->execute();
		$n = (int)$stmt->fetchResult();
		if ($n > 0) {
			$this->errormsg = sprintf($eLang->get('EXT_ALREADY_INSTALL'), $extension);
			return false;
		}

		elxisLoader::loadFile('components/com_extmanager/includes/extension.xml.php');
		$exml = new extensionXML();
		$info = $exml->quickXML($type, $extension);
		unset($exml);	

		if (($info['installed'] == false) || ($info['title'] == '')) {
			$this->errormsg = $eLang->get('XML_NFOUND_INV_PACK');
			return false;
		}

		$this->head = new stdClass;
		$this->head->type = $type;
		$this->head->name = $extension;
		$this->head->title = $info['title'];
		$this->head->version = $info['version'];
		$this->head->section = ($info['section'] != '') ? $info['section'] : 'frontend';
		$this->head->created = $info['created'];
		$this->head->author = $info['author'];
		$this->head->authorurl = $info['authorurl'];

		$func = 'install_'.$type;
		if (method_exists($this, $func)) {
			$ok = $this->$func();
			if (!$ok) { return false; }
		}

		//custom install actions
		if (file_exists(ELXIS_PATH.'/'.$ifolder.'/'.$extension.'/install.php')) {
			elxisLoader::loadFile($ifolder.'/'.$extension.'/install.php');
			$class = $extension.'_installer';
			if (class_exists($class, false)) {
				$obj = new $class();
				if (method_exists($obj, 'install')) {
					$obj->install();
				}
				unset($obj);
			}
		}

		return true;
	}


	/***********************************/
	/* MODULE SPECIFIC INSTALL ACTIONS */
	/***********************************/
	private function install_module() {
		$row = new modulesDbTable();
		$row->title = $this->head->title;
		$row->ordering = 99;
		$row->position = 'left';
		$row->published = 0;
		$row->module = $this->head->name;
		$row->showtitle = 2;
		$row->iscore = 0;
		$row->section = $this->head->section;
		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}

		$wheres = array(
			array('position', '=', $row->position),
			array('section', '=', $row->section)
			
		);
		$row->reorder($wheres, true);
		$modid = (int)$row->id;
		$section = $row->section;
		$this->lastid = $modid;
		unset($row);

		$arow = new aclDbTable();
		$arow->category = 'module';
		$arow->element = $this->head->name;
		$arow->identity = $modid;
		$arow->action = 'view';
		$arow->minlevel = 0;
		$arow->gid = 0;
		$arow->uid = 0;
		$arow->aclvalue = 1;
		$arow->insert();
		unset($arow);

		$arow = new aclDbTable();
		$arow->category = 'module';
		$arow->element = $this->head->name;
		$arow->identity = $modid;
		$arow->action = 'manage';
		$arow->minlevel = 70;
		$arow->gid = 0;
		$arow->uid = 0;
		$arow->aclvalue = 1;
		$arow->insert();
		unset($arow);

		if ($section == 'frontend') {
			$db = eFactory::getDB();
			$menuid = 0;
			$sql = "INSERT INTO ".$db->quoteId('#__modules_menu')." (".$db->quoteId('mmid').", ".$db->quoteId('moduleid').", ".$db->quoteId('menuid').")"
			."\n VALUES (NULL, :xmod, :xmen)";
			$stmt = $db->prepare($sql);
			$stmt->bindParam(':xmod', $modid, PDO::PARAM_INT);
			$stmt->bindParam(':xmen', $menuid, PDO::PARAM_INT);
			$stmt->execute();
		}

		return true;
	}


	/**************************************/
	/* COMPONENT SPECIFIC INSTALL ACTIONS */
	/**************************************/
	private function install_component() {
		$row = new componentsDbTable();
		$row->name = $this->head->title;
		$row->component = $this->head->name;
		$row->route = null;
		$row->iscore = 0;
		$row->params = null;
		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}

		$comid = (int)$row->id;
		$this->lastid = $comid;
		unset($row);
		
		$arow = new aclDbTable();
		$arow->category = 'component';
		$arow->element = $this->head->name;
		$arow->identity = 0;
		$arow->action = 'view';
		$arow->minlevel = 0;
		$arow->gid = 0;
		$arow->uid = 0;
		$arow->aclvalue = 1;
		$arow->insert();
		unset($arow);

		$arow = new aclDbTable();
		$arow->category = 'component';
		$arow->element = $this->head->name;
		$arow->identity = 0;
		$arow->action = 'manage';
		$arow->minlevel = 70;
		$arow->gid = 0;
		$arow->uid = 0;
		$arow->aclvalue = 1;
		$arow->insert();
		unset($arow);

		return true;
	}


	/*************************************/
	/* TEMPLATE SPECIFIC INSTALL ACTIONS */
	/*************************************/
	private function install_template() {
		$row = new templatesDbTable();
		$row->title = $this->head->title;
		$row->template = $this->head->name;
		$row->section = $this->head->section;
		$row->iscore = 0;
		$row->params = null;
		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}
		
		$this->lastid = $row->id;

		return true;
	}


	/******************************************/
	/* SEARCH ENGINE SPECIFIC INSTALL ACTIONS */
	/******************************************/
	private function install_engine() {
		$row = new enginesDbTable();
		$row->title = $this->head->title;
		$row->ordering = 99;
		$row->published = 0;
		$row->engine = $this->head->name;
		$row->alevel = 0;
		$row->defengine = 0;
		$row->iscore = 0;

		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}

		$row->reorder(array(), true);
		$this->lastid = (int)$row->id;
		return true;
	}


	/**************************************************/
	/* AUTHENTICATION METHOD SPECIFIC INSTALL ACTIONS */
	/**************************************************/
	private function install_auth() {
		$row = new authenticationDbTable();
		$row->title = $this->head->title;
		$row->ordering = 99;
		$row->published = 0;
		$row->auth = $this->head->name;
		$row->iscore = 0;

		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}

		$row->reorder(array(), true);
		$this->lastid = (int)$row->id;
		return true;
	}


	/*******************************************/
	/* CONTENT PLUGIN SPECIFIC INSTALL ACTIONS */
	/*******************************************/
	private function install_plugin() {
		$row = new pluginsDbTable();
		$row->title = $this->head->title;
		$row->plugin = $this->head->name;
		$row->alevel = 0;
		$row->ordering = 99;
		$row->published = 0;
		$row->iscore = 0;
		if (!$row->insert()) {
			$this->errormsg = $row->getErrorMsg();
			if ($this->is_synchro == false) {
				$this->efiles->deleteFolder($this->ifolder);
			}
			return false;
		}

		$row->reorder(array(), true);
		$this->lastid = (int)$row->id;
		return true;
	}


	/**********************************/
	/* COMPONENT SPECIFIC UPDATE ACTIONS */
	/**********************************/
	private function update_component() {
		return true;
	}


	/**********************************/
	/* MODULE SPECIFIC UPDATE ACTIONS */
	/**********************************/
	private function update_module() {
		return true;
	}


	/**********************************/
	/* TEMPLATE SPECIFIC UPDATE ACTIONS */
	/**********************************/
	private function update_template() {
		return true;
	}


	/**********************************/
	/* ENGINE SPECIFIC UPDATE ACTIONS */
	/**********************************/
	private function update_engine() {
		return true;
	}


	/*************************************************/
	/* AUTHENTICATION METHOD SPECIFIC UPDATE ACTIONS */
	/*************************************************/
	private function update_auth() {
		return true;
	}


	/**********************************/
	/* PLUGIN SPECIFIC UPDATE ACTIONS */
	/**********************************/
	private function update_plugin() {
		return true;
	}


	/****************************************/
	/* COMPONENT SPECIFIC UNINSTALL ACTIONS */
	/****************************************/
	private function uninstall_component($extension, $id=0) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__components')." WHERE ".$db->quoteId('component')." = :xcomp");
		$stmt->bindParam(':xcomp', $extension, PDO::PARAM_STR);
		$stmt->execute();

		$ctg = 'component';
		$sql = "DELETE FROM ".$db->quoteId('#__acl')." WHERE ".$db->quoteId('category')." = :xcat AND ".$db->quoteId('element')." = :xelem";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xcat', $ctg, PDO::PARAM_STR);
		$stmt->bindParam(':xelem', $extension, PDO::PARAM_STR);
		$stmt->execute();

		$sql = "DELETE FROM ".$db->quoteId('#__acl')." WHERE ".$db->quoteId('category')." = :xelem";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xelem', $extension, PDO::PARAM_STR);
		$stmt->execute();

		$sql = "DELETE FROM ".$db->quoteId('#__translations')." WHERE ".$db->quoteId('category')." = :xelem";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xelem', $extension, PDO::PARAM_STR);
		$stmt->execute();

		return true;
	}


	/*************************************/
	/* MODULE SPECIFIC UNINSTALL ACTIONS */
	/*************************************/
	private function uninstall_module($extension, $id) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__modules')." WHERE ".$db->quoteId('id')." = :xid");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->execute();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__modules_menu')." WHERE ".$db->quoteId('moduleid')." = :xid");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->execute();

		$ctg = 'module';
		$sql = "DELETE FROM ".$db->quoteId('#__acl')
		."\n WHERE ".$db->quoteId('category')." = :xcat AND ".$db->quoteId('element')." = :xelem AND ".$db->quoteId('identity')." = :xident";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xcat', $ctg, PDO::PARAM_STR);
		$stmt->bindParam(':xelem', $extension, PDO::PARAM_STR);
		$stmt->bindParam(':xident', $id, PDO::PARAM_INT);
		$stmt->execute();

		$sql = "DELETE FROM ".$db->quoteId('#__translations')
		."\n WHERE ".$db->quoteId('category')." = :xcat AND ".$db->quoteId('elid')." = :xelid";
		$stmt = $db->prepare($sql);
		$stmt->bindParam(':xcat', $ctg, PDO::PARAM_STR);
		$stmt->bindParam(':xelid', $id, PDO::PARAM_INT);
		$stmt->execute();

		return true;
	}


	/***************************************/
	/* TEMPLATE SPECIFIC UNINSTALL ACTIONS */
	/***************************************/
	private function uninstall_template($extension, $id) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__templates')." WHERE ".$db->quoteId('id')." = :xid AND ".$db->quoteId('template')." = :xtpl");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->bindParam(':xtpl', $extension, PDO::PARAM_STR);
		$stmt->execute();

		return true;
	}


	/********************************************/
	/* SEARCH ENGINE SPECIFIC UNINSTALL ACTIONS */
	/********************************************/
	private function uninstall_engine($extension, $id) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__engines')." WHERE ".$db->quoteId('id')." = :xid");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->execute();

		return true;
	}


	/****************************************************/
	/* AUTHENTICATION METHOD SPECIFIC UNINSTALL ACTIONS */
	/****************************************************/
	private function uninstall_auth($extension, $id) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__authentication')." WHERE ".$db->quoteId('id')." = :xid");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->execute();

		return true;
	}


	/*********************************************/
	/* CONTENT PLUGIN SPECIFIC UNINSTALL ACTIONS */
	/*********************************************/
	private function uninstall_plugin($extension, $id) {
		$db = eFactory::getDB();

		$stmt = $db->prepare("DELETE FROM ".$db->quoteId('#__plugins')." WHERE ".$db->quoteId('id')." = :xid");
		$stmt->bindParam(':xid', $id, PDO::PARAM_INT);
		$stmt->execute();

		return true;
	}


	/*******************************************/
	/* SEND NOTIFICATION EMAIL TO TECH MANAGER */
	/*******************************************/
	private function notify($action, $type, $extension, $version) {
		$elxis = eFactory::getElxis();

		if (($version == '') || !is_numeric($version)) { $version = 0; }
		if ($elxis->getConfig('MAIL_MANAGER_NAME') == '') { return; }
		if ($elxis->getConfig('MAIL_MANAGER_EMAIL') == '') { return; }

    	$parsed = parse_url($elxis->getConfig('URL')); 
 		$host = preg_replace('#^(www\.)#i', '', $parsed['host']);
		$vtxt = '';
		$vtxt2 = '';
		if ($version > 0) { $vtxt = ' v'.$version; $vtxt2 = ' to v'.$version; }

		if ($action == 'install') {
			switch ($type) {
				case 'module':
					$subject = 'Module '.$extension.$vtxt.' installed on '.$host;
				break;
				case 'component':
					$subject = 'Component '.$extension.$vtxt.' installed on '.$host;
				break;
				case 'template':
				case 'atemplate':
					$subject = 'Template '.$extension.$vtxt.' installed on '.$host;
				break;
				case 'engine':
					$subject = 'Search engine '.$extension.$vtxt.' installed on '.$host;
				break;
				case 'auth':
					$subject = 'Authentication method '.$extension.$vtxt.' installed on '.$host;
				break;
				case 'plugin':
					$subject = 'Content plugin '.$extension.$vtxt.' installed on '.$host;
				break;
				default:
					$subject = 'Extension '.$extension.$vtxt.' installed on '.$host;
				break;
			}
		} elseif ($action == 'update') {
			switch ($type) {
				case 'module':
					$subject = 'Module '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				case 'component':
					$subject = 'Component '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				case 'template':
				case 'atemplate':
					$subject = 'Template '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				case 'engine':
					$subject = 'Search engine '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				case 'auth':
					$subject = 'Authentication method '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				case 'plugin':
					$subject = 'Content plugin '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
				default:
					$subject = 'Extension '.$extension.' updated'.$vtxt2.' on '.$host;
				break;
			}
		} elseif ($action == 'uninstall') {
			switch ($type) {
				case 'module':
					$subject = 'Module '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				case 'component':
					$subject = 'Component '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				case 'template':
				case 'atemplate':
					$subject = 'Template '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				case 'engine':
					$subject = 'Search engine '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				case 'auth':
					$subject = 'Authentication method '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				case 'plugin':
					$subject = 'Content plugin '.$extension.$vtxt.' uninstalled from '.$host;
				break;
				default:
					$subject = 'Extension '.$extension.$vtxt.' uninstalled from '.$host;
				break;
			}
		} else {
			$subject = 'Extension '.$extension.$vtxt.' '.$action.' on '.$host;
		}

    	$to = $elxis->getConfig('MAIL_MANAGER_EMAIL').','.$elxis->getConfig('MAIL_MANAGER_NAME');

		$body = 'This is a notification message from the Elxis extensions manager on '.$elxis->getConfig('URL')."\r\n\r\n";
		$body .= "Elxis installer report\r\n";
		$body .= "----------------------------------------------\r\n";
		$body .= "Action    : \t".$action."\r\n";
		$body .= "Type      : \t".$type."\r\n";
		$body .= "Extension : \t".$extension."\r\n";
		if ($version > 0) { $body .= "Version   : \t".$version."\r\n"; }
		$body .= "User      : \t".$elxis->user()->uname.' (UID '.$elxis->user()->uid.')'."\r\n";
		$body .= "Date      : \t".gmdate('Y-m-d H:i:s')." (UTC)\r\n";
		$body .= "Site URL  : \t".$elxis->getConfig('URL')."\r\n\r\n\r\n";
		$body .= "You have selected to receive Elxis installer notifications on\r\n";
		if ($this->notify_install == 1) { $body .= "\t- New extension install\r\n"; }
		if ($this->notify_uninstall == 1) { $body .= "\t- Extension uninstall\r\n"; }
		if ($this->notify_update == 1) { $body .= "\t- Extension update\r\n"; }
		$body .= "\r\n".'If you dont want to receive such notifications you can ';
		$body .= 'turn off notifications in Extensions Manager component parameters.'."\r\n\r\n\r\n";
		$body .= "----------------------------------------------\r\n";
		$body .= 'Please do not reply to this message as it was generated automatically by the Elxis Installer ';
		$body .= 'and it was sent for informational purposes.'."\r\n";
		$body .= 'Powered by Elxis v'.$elxis->getVersion()." ( https://www.elxis.org )\r\n";

		$elxis->sendmail($subject, $body, '', null, 'plain', $to, null, null, null, 1);
	}


	/*****************************************************/
	/* WRITE INSTALL/UNINSTALL/UPDATE MESSSAGE INTO LOGS */
	/*****************************************************/
	private function logger($extension, $action, $version='') {
		$elxis = eFactory::getElxis();

		if (($version == '') || !is_numeric($version)) { $version = 0; }
		$txt = '['.gmdate('Y-m-d H:i:s').' UTC]'." ".ucfirst($action).' '.$extension.' ';
		if ($version > 0) { $txt .= 'v'.$version.' '; }
		$txt .= 'by '.$elxis->user()->uname.' (UID '.$elxis->user()->uid.')'._LEND._LEND;
		$ok = eFactory::getFiles()->writeFile('logs/installer.log', $txt, true);
		return $ok;
	}


	/************ PUBLIC GETTERS ****************/


	public function getError() {
		return $this->errormsg;
	}

	public function getWarnings() {
		return $this->warnings;
	}

	public function getHead() {
		return $this->head;
	}

	public function getDependencies() {
		return $this->dependencies;
	}

	public function isUpdate() {
		return $this->is_update;
	}

	public function getPackage() {
		return $this->package;
	}

	public function getUfolder() {
		return $this->ufolder;
	}

	public function getUffolder() {
		return $this->uffolder;
	}

	public function getIfolder() {
		return $this->ifolder;
	}

	public function getCurrent() {
		$arr = array(
			'version' => $this->cur_version,
			'created' => $this->cur_created,
			'author' => $this->cur_author,
			'authorurl' => $this->cur_authorurl
		);
		return $arr;
	}

	public function getLastID() {
		return $this->lastid;
	}

}


interface iExtension {
    public function install();
    public function uninstall($oldversion='');
    public function update();
}

?>