<?php 
/**
* @version		$Id: install.class.php 2638 2025-07-15 17:32:07Z IOS $
* @package		Elxis
* @subpackage	Installer
* @copyright	Copyright (c) 2006-2025 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');
defined('ELXIS_INSTALLER') or die ('Direct access to this location is not allowed');


class elxisInstaller {

	private $ilangs = array(); //available installer languages
	private $lang = 'en';
	private $langarr = array();
	private $version = array();
	private $steps = array();
	private $step = 1;
	private $data = array();
	public $url = ''; //site URL without trailing slash
	public $relpath = ''; //site relative path from root folder without slashes at begin or end (empty or part of url)
	private $error = false;
	private $errormsg = '';


	public function __construct() {
		$this->prepareEnv();
		$this->loadLanguage();
		$this->setSteps();
	}


	/**********************/
	/* PREPARE ENVIROMENT */
	/**********************/
	private function prepareEnv() {
		require(ELXIS_PATH.'/includes/version.php');
		$this->version = $elxis_version;

		$reload = false;
		if (ELXIS_SELF != 'index.php') { $reload = true; }
		$protocol = 'http';
		$port = 0;

 		$found = false;
		if (isset($_SERVER['SERVER_PROTOCOL'])) {
			$protocol = strtolower($_SERVER['SERVER_PROTOCOL']);//be careful can have value like "HTTP/1.1" in SSL !
			if (($protocol == 'http') || ($protocol == 'https')) { $found = true; } else { $protocol = 'http'; }
		}
		if (!$found) {
			if (isset($_SERVER['HTTPS'])) {
				if (($_SERVER['HTTPS'] == 'on') || ($_SERVER['HTTPS'] == 1)) { $protocol = 'https'; }
			} else if (isset($_SERVER['REQUEST_SCHEME'])) {
				if (strtolower($_SERVER['REQUEST_SCHEME']) == 'https') { $protocol = 'https'; }
			}
		}

		if (isset($_SERVER['SERVER_PORT'])) {
			if ($protocol == 'http') {
				if ($_SERVER['SERVER_PORT'] != 80) { $port = (int)$_SERVER['SERVER_PORT']; }
			} else {
				if (($_SERVER['SERVER_PORT'] != 443) && ($_SERVER['SERVER_PORT'] != 80)) { $port = (int)$_SERVER['SERVER_PORT']; }
			}
		}

		$host = isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'];
		if (strpos($host, ':') !== false) {
			$hparts = preg_split('@\:@', $host, 2, PREG_SPLIT_NO_EMPTY);
			$host = $hparts[0];
		}
		$urlpath = '';
		if (isset($_SERVER['REQUEST_URI'])) {
			$n = strpos($_SERVER['REQUEST_URI'], '?');
			if ($n !== false) {
				$urlpath = substr($_SERVER['REQUEST_URI'], 0, $n);
			} else {
				$urlpath = $_SERVER['REQUEST_URI'];
			}
		}

		if (strpos($urlpath, '.') !== false) {
			if (strrpos($urlpath, 'index.php') !== false) {
				$reload = false;
			} else {
				$reload = true;
			}
			$slashpos = strrpos($urlpath, '/');
			$urlpath = substr($urlpath, 0, $slashpos);
		}
		$urlpath = trim($urlpath, '/');
		if ($urlpath == '/') { $urlpath = ''; }

		$continue = true;
		while ($continue == true) {
			if ($urlpath == '') { $continue = false; break; }
			if (strrpos(ELXIS_PATH, $urlpath) !== false) {
				$continue = false;
				break;
			} else {
				$slashpos = strrpos($urlpath, '/');
				$urlpath = substr($urlpath, 0, $slashpos);
				$urlpath = trim($urlpath, '/');
			}
		}

		if ($urlpath == '/') { $urlpath = ''; }
		if ($urlpath != '') { $urlpath = '/'.$urlpath; }

		if ($port > 0) {
			$this->url = $protocol.'://'.$host.':'.$port.$urlpath;
		} else {
			$this->url = $protocol.'://'.$host.$urlpath;
		}

		$n = strpos($host.$urlpath, '/');
		if ($n !== false) {//installation in sub-folder
			$this->relpath = trim(substr($host.$urlpath, $n));
			$this->relpath = trim($this->relpath, '/');
			if ($this->relpath == '/') { $this->relpath = ''; }
		}

		if ($reload == true) {
			if (headers_sent()) {
				echo '<script>document.location.href="'.$this->url.'";</script>'."\n";
				echo 'Redirection to the proper installation URL<br>';
				echo '<a href="'.$this->url.'" stle="font-weight:bold;">Click here</a> if you don\'t get redirected automatically.'."\n";
			} else {
				if (ob_get_length() > 0) { ob_end_clean(); }
				@header('content-type:text/html; charset=utf-8');
				@header('Location: '.$this->url);
			}
			exit;
		}
	}


	/*****************/
	/* LOAD LANGUAGE */
	/*****************/
	private function loadLanguage() {
		$ilangs = $this->listFolders(ELXIS_PATH.'/language/');

		include(ELXIS_PATH.'/includes/libraries/elxis/language/langdb.php');
		if ($ilangs) {
			foreach ($ilangs as $ilang) {
				if (isset($langdb[$ilang])) {
					if (file_exists(ELXIS_PATH.'/language/'.$ilang.'/'.$ilang.'.install.php')) {
						$this->ilangs[$ilang] = $langdb[$ilang];
						$this->ilangs[$ilang]['RTLSFX'] = ($langdb[$ilang]['DIR'] == 'rtl') ? '-rtl' : '';
					}
				}
			}
		}

		$lang = '';
		if (isset($_GET['lang']) && (trim($_GET['lang']) != '')) {
			$lng = trim($_GET['lang']);
			if (isset($this->ilangs[$lng])) { $lang = $lng; }
		}
		if (($lang == '') && isset($_POST['lang']) && (trim($_POST['lang']) != '')) {
			$lng = trim($_POST['lang']);
			if (isset($this->ilangs[$lng])) { $lang = $lng; }
		}
		if ($lang == '') {
			$lang = $this->browserLang($ilangs);
		}

		if (!isset($this->ilangs[$lang])) {
			die('Fatal error: Language '.$lang.' was not found!');
		}

		$this->lang = $lang;

		include(ELXIS_PATH.'/language/'.$lang.'/'.$lang.'.php');
		$this->langarr = $_lang;
		unset($_lang);
		if (!isset($locale) || !is_array($locale)) { $locale = array('en_GB.utf8', 'en_GB.UTF-8', 'en_GB', 'en', 'english', 'england'); }
		include(ELXIS_PATH.'/language/'.$lang.'/'.$lang.'.install.php');
		foreach ($_lang as $k => $v) { $this->langarr[$k] = $v; }
		unset($_lang);
		$this->setLocale($locale);
	}


	/****************************/
	/* LIST FOLDERS INSIDE PATH */
	/****************************/
	private function listFolders($path) {
		$handle = @opendir($path);
		if (!$handle) { return array(); }
		$arr = array();
		while ($entry = readdir($handle)) {
			$dir = $path.$entry;
			if (($entry != '.') && ($entry != '..') && is_dir($dir)) { $arr[] = $entry; }
		}
		closedir($handle);
		if ($arr) { asort($arr); }
		return $arr;
	}


	/********************************************************/
	/* DETECT BROWSER LANGUAGE AND CONVERT IT TO ELXIS LANG */
	/********************************************************/
	private function browserLang($allowed_langs=array()) {
		if (!is_array($allowed_langs) || (count($allowed_langs) == 0)) { $allowed_langs = array('en'); }
		if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { return $allowed_langs[0]; }
		$acc_langs_str = trim($_SERVER['HTTP_ACCEPT_LANGUAGE']);
		if ($acc_langs_str == '') { return $allowed_langs[0]; }
		$langs = array();
		$acc_langs = explode(',', trim($acc_langs_str));
		foreach ($acc_langs as $acc_lang) {
			if (preg_match('/(\*|[a-zA-Z0-9]{1,8}(?:-[a-zA-Z0-9]{1,8})*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?/', trim($acc_lang), $match)) {
				$q = (!isset($match[2])) ? '1.0' : (string)floatval($match[2]);
				$m = trim(strtolower($match[1]));
				$n = strpos($m, '-');
				if ($n !== false) { $m = substr($m, 0, $n); }
				if (!in_array($m, $allowed_langs)) { continue; }
				if (!isset($langs[$q])) { $langs[$q] = array(); }
				$langs[$q][] = $m;
			}
		}
		if (!$langs) { return $allowed_langs[0]; }
		krsort($langs);
		$final_lang = $allowed_langs[0];
		foreach ($langs as $k => $lngs) {
			$final_lang = $lngs[0];
			break;
		}
		return $final_lang;
	}


	/*************************/
	/* SET ENVIROMENT LOCALE */
	/*************************/
	private function setLocale($locale) {
		if (strtoupper(substr(php_uname(), 0, 3)) == 'WIN') {
			$loc = array ('en_GB.utf8', 'en_GB.utf-8', 'eng', 'english');
			setlocale(LC_COLLATE, $loc);
			setlocale(LC_CTYPE, $loc);
			setlocale(LC_TIME, $loc);
			return;
		}

		setlocale(LC_COLLATE, $locale);
		setlocale(LC_CTYPE, $locale);
		setlocale(LC_TIME, $locale);	
	}


	/********************/
	/* SET CURRENT STEP */
	/********************/
	private function setSteps() {
		$this->steps = array (
			1 => $this->getLang('ELXIS_INSTALL'),
			2 => $this->getLang('ADMIN_ACCOUNT'),//DATA_IMPORT
			3 => $this->getLang('FINISH')
			//2 => $this->getLang('LICENSE'),
			//3 => $this->getLang('SETTINGS'),
			//4 => $this->getLang('DATA_IMPORT'),
			//5 => $this->getLang('FINISH')
		);

		$step = 1;
		if (isset($_GET['step'])) {
			$step = (int)$_GET['step'];
		} else if (isset($_POST['step'])) {
			$step = (int)$_POST['step'];
		}
		if ($step < 1) { $step = 1; }
		//if ($step > 5) { $step = 1; }
		if ($step > 3) { $step = 1; }
		if (!isset($this->steps[$step])) { $step = 1; }
		$this->step = $step;
	}


	/*****************************/
	/* GET LANGUAGE STRING VALUE */
	/*****************************/
	public function getLang($str) {
		if (isset($this->langarr[$str])) { return $this->langarr[$str]; }
		return $str;
	}


	/***********************/
	/* PRE-PROCESS REQUEST */
	/***********************/
	public function process() {
		if ($this->step == 2) {
			$this->data['queries'] = 0;
			$this->data['import_error'] = '';
			$this->data['cfg'] = $this->catchConfigPost();

			if ($this->data['cfg']['errormsg'] == '') {
				require_once(ELXIS_PATH.'/includes/install/inc/miniloader.php');
				require_once(ELXIS_PATH.'/includes/libraries/elxis/database.class.php');
				$params = array(
					'dbtype' => $this->data['cfg']['cfg_db_type'],
					'host' => $this->data['cfg']['cfg_db_host'],
					'port' => $this->data['cfg']['cfg_db_port'],
					'dbname' => $this->data['cfg']['cfg_db_name'],
					'username' => $this->data['cfg']['cfg_db_user'],
					'password' => $this->data['cfg']['cfg_db_pass'],
					'persistent' => 0,
					'ssl' => $this->data['cfg']['cfg_db_ssl'],
					'sslcafile' => $this->data['cfg']['cfg_db_sslca'],
					'dsn' => $this->data['cfg']['cfg_db_dsn'],
					'scheme' => $this->data['cfg']['cfg_db_scheme'],
					'table_prefix' => $this->data['cfg']['cfg_db_prefix'],
					'debug' => 0
				);

				$db = new elxisDatabase($params, array(), false);
				$okcon = $db->connect($this->data['cfg']['cfg_db_dsn'], $this->data['cfg']['cfg_db_user'], $this->data['cfg']['cfg_db_pass'], array(), true); //on fail returns false
				if (!$okcon) {
					$this->data['queries'] = false;
					$this->data['import_error'] = $db->getErrorMsg();
					if ($this->data['import_error'] == '') { $this->data['import_error'] = 'Could not connect to database!'; }
				} else {
					$sqlfile = ELXIS_PATH.'/includes/install/data/'.$this->data['cfg']['cfg_db_type'].'.sql';
					$this->data['queries'] = $db->import($sqlfile);
					if ($this->data['queries'] === false) { $this->data['import_error'] = $db->getErrorMsg(); }
					$db->disconnect();	
				}
			} else {
				$this->step = 1;
			}
			return;
		}

		if ($this->step == 3) {
			$this->data['cfg'] = $this->catchConfigPost();
			if ($this->data['cfg']['errormsg'] != '') {
				$this->step = 1;
				return;
			}
			$this->data['usr'] = $this->catchUserPost();
			if ($this->data['usr']['errormsg'] != '') {
				$this->step = 2;
				return;
			}

			require_once(ELXIS_PATH.'/includes/install/inc/miniloader.php');
			require_once(ELXIS_PATH.'/includes/libraries/elxis/database.class.php');
			$params = array(
				'dbtype' => $this->data['cfg']['cfg_db_type'],
				'host' => $this->data['cfg']['cfg_db_host'],
				'port' => $this->data['cfg']['cfg_db_port'],
				'dbname' => $this->data['cfg']['cfg_db_name'],
				'username' => $this->data['cfg']['cfg_db_user'],
				'password' => $this->data['cfg']['cfg_db_pass'],
				'persistent' => 0,
				'ssl' => $this->data['cfg']['cfg_db_ssl'],
				'sslcafile' => $this->data['cfg']['cfg_db_sslca'],
				'dsn' => $this->data['cfg']['cfg_db_dsn'],
				'scheme' => $this->data['cfg']['cfg_db_scheme'],
				'table_prefix' => $this->data['cfg']['cfg_db_prefix'],
				'debug' => 0
			);

			if (!class_exists('elxisCryptHelper', false)) {
				include(ELXIS_PATH.'/includes/libraries/elxis/helpers/crypt.helper.php');
			}

			$db = new elxisDatabase($params, array(), false);
			$db->connect($this->data['cfg']['cfg_db_dsn'], $this->data['cfg']['cfg_db_user'], $this->data['cfg']['cfg_db_pass'], array());

			$eparams = array(
				'method' => $this->data['cfg']['cfg_encrypt_method'],
				'key' => $this->data['cfg']['cfg_encrypt_key']
			);
			$encObj = new elxisCryptHelper($eparams);
			$encpword = $encObj->getEncryptedPassword($this->data['usr']['u_pword']);
			unset($encObj, $eparams);

			$now = gmdate('Y-m-d H:i:s');
			$sql = "UPDATE ".$db->quoteId('#__users')." SET ".$db->quoteId('firstname')." = :x1, ".$db->quoteId('lastname')." = :x2, ".$db->quoteId('uname')." = :x3,"
			."\n ".$db->quoteId('pword')." = :x4, ".$db->quoteId('website')." = :x5, ".$db->quoteId('email')." = :x6, ".$db->quoteId('registerdate')." = :x7,"
			."\n ".$db->quoteId('lastvisitdate')." = :x8 WHERE ".$db->quoteId('uid')." = 1";
			$stmt = $db->prepare($sql);

			$stmt->bindParam(':x1', $this->data['usr']['u_firstname'], PDO::PARAM_STR);
			$stmt->bindParam(':x2', $this->data['usr']['u_lastname'], PDO::PARAM_STR);
			$stmt->bindParam(':x3', $this->data['usr']['u_uname'], PDO::PARAM_STR);
			$stmt->bindParam(':x4', $encpword, PDO::PARAM_STR);
			$stmt->bindParam(':x5', $this->data['cfg']['cfg_url'], PDO::PARAM_STR);
			$stmt->bindParam(':x6', $this->data['usr']['u_email'], PDO::PARAM_STR);
			$stmt->bindParam(':x7', $now, PDO::PARAM_STR);
			$stmt->bindParam(':x8', $now, PDO::PARAM_STR);
			$stmt->execute();

			$author_name = $this->data['usr']['u_firstname'].' '.$this->data['usr']['u_lastname'];
			$created_date = gmdate('Y-m-d H:i:s');

			$sql = "UPDATE ".$db->quoteId('#__content')." SET ".$db->quoteId('created')." = :y1, ".$db->quoteId('created_by_name')." = :y2";
			$stmt = $db->prepare($sql);
			$stmt->bindParam(':y1', $created_date, PDO::PARAM_STR);
			$stmt->bindParam(':y2', $author_name, PDO::PARAM_STR);
			$stmt->execute();

			$admin_folder = $this->renameEstia();
			$new_link = $this->data['cfg']['cfg_url'].'/'.$admin_folder.'/';
			$old_link = 'http://localhost/estia/';

			$sql = "UPDATE ".$db->quoteId('#__menu')." SET ".$db->quoteId('link')." = :xnew WHERE ".$db->quoteId('link')." = :xold";
			$stmt = $db->prepare($sql);
			$stmt->bindParam(':xnew', $new_link, PDO::PARAM_STR);
			$stmt->bindParam(':xold', $old_link, PDO::PARAM_STR);
			$stmt->execute();

			$fromname = 'Elxis Team';
			$toname = $this->data['usr']['u_firstname'].' '.$this->data['usr']['u_lastname'];
			$msgtype = 'info';
			$message = 'Welcome to Elxis CMS! For support please visit https://forum.elxis.org';
			$now = gmdate('Y-m-d H:i:s');

			$sql = "INSERT INTO ".$db->quoteId('#__messages')." VALUES (NULL, 0, :xv1, 1, :xv2, :xv3, :xv4, :xv5, 0, 0, 0, 0)";
			$stmt = $db->prepare($sql);
			$stmt->bindParam(':xv1', $fromname, PDO::PARAM_STR);
			$stmt->bindParam(':xv2', $toname, PDO::PARAM_STR);
			$stmt->bindParam(':xv3', $msgtype, PDO::PARAM_STR);
			$stmt->bindParam(':xv4', $message, PDO::PARAM_STR);
			$stmt->bindParam(':xv5', $now, PDO::PARAM_STR);
			$stmt->execute();

			$db->disconnect();

			if ($this->data['cfg']['cfg_repo_path'] == '') {
				$this->renameRepository();
			}
			$this->saveConfig($admin_folder);
			return;
		}
	}


	/***************************************************************/
	/* GET THE VALUE FOR A USER SUBMITTED ITEM FROM THE DATA ARRAY */
	/***************************************************************/
	public function dataValue($first, $second, $default) {
		if ($first != '') {
			if ($second != '') {
				return (isset($this->data[$first][$second])) ? $this->data[$first][$second] : $default;
			} else {
				return (isset($this->data[$first])) ? $this->data[$first] : $default;
			}
		}
		return $default;
	}


	/******************/
	/* MAKE STEP HTML */
	/******************/
	public function makehtml() {
		$fname = 'step'.$this->step.'.php';
		if (file_exists(ELXIS_PATH.'/includes/install/inc/'.$fname)) {
			include(ELXIS_PATH.'/includes/install/inc/'.$fname);
		}
	}


	/**************************/
	/* GET CURRENT STEP TITLE */
	/**************************/
	public function stepTitle() {
		$step = $this->step;
		return $this->steps[$step];
	}


	/********************/
	/* GET STEPS NUMBER */
	/********************/
	public function countSteps() {
		return count($this->steps);
	}


	/********************/
	/* GET CURRENT STEP */
	/********************/
	public function getStep() {
		return $this->step;
	}


	/*****************************************/
	/* CATCH AND VALIDATE CONFIGURATION POST */
	/*****************************************/
	private function catchConfigPost() {
		$cfg = array(
			'errormsg' => '',
			'cfg_sitename' => '',
			'cfg_url' => '',
			'cfg_repo_path' => '',
			'cfg_lang' => '',
			'cfg_encrypt_method' => '',
			'cfg_encrypt_key' => '',
			'cfg_db_type' => '',
			'cfg_db_host' => '',
			'cfg_db_prefix' => '',
			'cfg_db_port' => 0,
			'cfg_db_name' => '',
			'cfg_db_user' => '',
			'cfg_db_pass' => '',
			'cfg_db_ssl' => 0,
			'cfg_db_sslca' => '',
			'cfg_db_dsn' => '',
			'cfg_db_scheme' => '',
			'cfg_ftp' => 0,
			'cfg_ftp_host' => '',
			'cfg_ftp_port' => 21,
			'cfg_ftp_root' => '',
			'cfg_ftp_user' => '',
			'cfg_ftp_pass' => '',
			'cfg_mail_method' => 'mail',//mail smtp sendmail
			'cfg_smtp_host' => '',
			'cfg_smtp_port' => 25,
			'cfg_smtp_secure' => '',//empty, ssl, tls, starttls
			'cfg_smtp_auth' => 0,//attention:checkbox!
			'cfg_mail_auth_method' => '',//empty, CRAM-MD5, LOGIN, NTLM, PLAIN, XOAUTH2
			'cfg_smtp_user' => '',
			'cfg_smtp_pass' => '',
			'cfg_sef' => 0,
			'cfg_sitelangs' => '',
			'cfg_multilinguism' => 1//auto-calculated, not from post
		);

		$cfg['cfg_sitename'] = $this->sanitizeInput('cfg_sitename', true, true, false);
		$cfg['cfg_url'] = trim(filter_input(INPUT_POST, 'cfg_url', FILTER_SANITIZE_URL));
		$cfg['cfg_repo_path'] = $this->sanitizeInput('cfg_repo_path', true, true, true);//this is empty, we will create on save
		$cfg['cfg_repo_path'] = rtrim(str_replace('\\', '/', $cfg['cfg_repo_path']), '/');
		$cfg['cfg_lang'] = trim($this->sanitizeInput('cfg_lang', true, true, true));
		$cfg['cfg_encrypt_method'] = trim($this->sanitizeInput('cfg_encrypt_method', true, true, true));
		if (($cfg['cfg_encrypt_method'] != 'xor') && ($cfg['cfg_encrypt_method'] != 'mcrypt') && ($cfg['cfg_encrypt_method'] != 'openssl')) { $cfg['cfg_encrypt_method'] = ''; }
		$cfg['cfg_encrypt_key'] = trim($this->sanitizeInput('cfg_encrypt_key', true, true, true));
		$cfg['cfg_db_type'] = trim($this->sanitizeInput('cfg_db_type', true, true, true));
		$cfg['cfg_db_host'] = trim($this->sanitizeInput('cfg_db_host', true, true, true));
		$cfg['cfg_db_prefix'] = trim($this->sanitizeInput('cfg_db_prefix', true, true, true));
		$cfg['cfg_db_port'] = (isset($_POST['cfg_db_port'])) ? (int)$_POST['cfg_db_port'] : 0;
		if ($cfg['cfg_db_port'] < 0) { $cfg['cfg_db_port'] = 0; }
		$cfg['cfg_db_name'] = trim($this->sanitizeInput('cfg_db_name', true, true, true));
		$cfg['cfg_db_user'] = trim($this->sanitizeInput('cfg_db_user', true, true, true));
		$cfg['cfg_db_pass'] = trim($this->sanitizeInput('cfg_db_pass', true, true, true));
		$cfg['cfg_db_ssl'] = (isset($_POST['cfg_db_ssl'])) ? (int)$_POST['cfg_db_ssl'] : 0;
		$cfg['cfg_db_sslca'] = trim($this->sanitizeInput('cfg_db_sslca', true, true, true));
		$cfg['cfg_db_dsn'] = trim($this->sanitizeInput('cfg_db_dsn', true, true, true));
		$cfg['cfg_db_scheme'] = trim($this->sanitizeInput('cfg_db_scheme', true, true, true));
		$cfg['cfg_ftp'] = (isset($_POST['cfg_ftp'])) ? (int)$_POST['cfg_ftp'] : 0;
		if ($cfg['cfg_ftp'] <> 1) { $cfg['cfg_ftp'] = 0; }
		$cfg['cfg_ftp_host'] = trim($this->sanitizeInput('cfg_ftp_host', true, true, true));
		$cfg['cfg_ftp_port'] = (isset($_POST['cfg_ftp_port'])) ? (int)$_POST['cfg_ftp_port'] : 0;
		if ($cfg['cfg_ftp_port'] < 1) { $cfg['cfg_ftp_port'] = 21; }
		$cfg['cfg_ftp_root'] = trim($this->sanitizeInput('cfg_ftp_root', true, true, true));
		$cfg['cfg_ftp_root'] = rtrim($cfg['cfg_ftp_root'], '/');
		if ($cfg['cfg_ftp_root'] == '') { $cfg['cfg_ftp_root'] = '/'; }
		$cfg['cfg_ftp_user'] = trim($this->sanitizeInput('cfg_ftp_user', true, true, true));
		$cfg['cfg_ftp_pass'] = trim($this->sanitizeInput('cfg_ftp_pass', true, true, true));
		$cfg['cfg_sef'] = (isset($_POST['cfg_sef'])) ? (int)$_POST['cfg_sef'] : 0;
		if ($cfg['cfg_sef'] < 0) { $cfg['cfg_sef'] = 0; }

		$cfg['cfg_mail_method'] = trim($this->sanitizeInput('cfg_mail_method', true, true, true));
		if ($cfg['cfg_mail_method'] == '') { $cfg['cfg_mail_method'] = 'mail'; }
		if (!in_array($cfg['cfg_mail_method'], array('mail', 'smtp', 'sendmail'))) { $cfg['cfg_mail_method'] = 'mail'; }
		$cfg['cfg_smtp_host'] = trim($this->sanitizeInput('cfg_smtp_host', true, true, false));
		$cfg['cfg_smtp_port'] = (isset($_POST['cfg_smtp_port'])) ? (int)$_POST['cfg_smtp_port'] : 0;
		if ($cfg['cfg_smtp_port'] < 1) { $cfg['cfg_smtp_port'] = 25; }
		$cfg['cfg_smtp_secure'] = trim($this->sanitizeInput('cfg_smtp_secure', true, true, true));
		if ($cfg['cfg_smtp_secure'] != '') {
			if (!in_array($cfg['cfg_smtp_secure'], array('ssl', 'tls', 'starttls'))) { $cfg['cfg_smtp_secure'] = ''; }
		}
		$cfg['cfg_smtp_auth'] = (isset($_POST['cfg_smtp_auth'])) ? (int)$_POST['cfg_smtp_auth'] : 0;
		$cfg['cfg_mail_auth_method'] = trim($this->sanitizeInput('cfg_mail_auth_method', true, true, true));
		if ($cfg['cfg_mail_auth_method'] != '') {
			if (!in_array($cfg['cfg_mail_auth_method'], array('CRAM-MD5', 'LOGIN', 'NTLM', 'PLAIN', 'XOAUTH2'))) { $cfg['cfg_mail_auth_method'] = ''; }
		}
		$cfg['cfg_smtp_user'] = trim($this->sanitizeInput('cfg_smtp_user', true, true, false));
		$cfg['cfg_smtp_pass'] = trim($this->sanitizeInput('cfg_smtp_pass', true, true, false));

		if (trim($cfg['cfg_sitename']) == '') {
			$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('SITENAME'));
			return $cfg;
		}
		if (($cfg['cfg_url'] == '') || !filter_var($cfg['cfg_url'], FILTER_VALIDATE_URL)) {
			$cfg['errormsg'] = $this->getLang('INVALID_URL'); return $cfg;
		}
		if ($cfg['cfg_repo_path'] != '') {//this is empty, we will create on save
			if (!file_exists($cfg['cfg_repo_path'].'/')) { $cfg['errormsg'] = $this->getLang('REPOPATH_NOEX'); return $cfg; }
		}
		if (($cfg['cfg_lang'] == '') || !file_exists(ELXIS_PATH.'/language/'.$cfg['cfg_lang'].'/'.$cfg['cfg_lang'].'.php')) {
			$cfg['errormsg'] = 'Invalid language!'; return $cfg;
		}
		if (($cfg['cfg_encrypt_key'] == '') || (strlen($cfg['cfg_encrypt_key']) != 16)) {
			$cfg['errormsg'] = 'Invalid encryption key!'; return $cfg;
		}
		if ($cfg['cfg_db_type'] == '') {
			$cfg['errormsg'] = 'You must select a database type!'; return $cfg;
		}
		$pdodrivers = PDO::getAvailableDrivers();
		if (!$pdodrivers) { $pdodrivers = array(); }
		if (!in_array($cfg['cfg_db_type'], $pdodrivers)) {
			$cfg['errormsg'] = 'PDO database driver '.$cfg['cfg_db_type'].' is not supported by your system!'; return $cfg;
		}
		if (!file_exists(ELXIS_PATH.'/includes/install/data/'.$cfg['cfg_db_type'].'.sql')) {
			$cfg['errormsg'] = 'PDO database driver '.$cfg['cfg_db_type'].' is not supported by the Elxis installer!'; return $cfg;
		}
		if ($cfg['cfg_db_host'] == '') {
			$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'DB '.$this->getLang('HOST')); return $cfg;
		}
		if ($cfg['cfg_db_prefix'] == '') {
			$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('TABLES_PREFIX')); return $cfg;
		}

		if ($cfg['cfg_db_name'] == '') {
			$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'DB '.$this->getLang('NAME')); return $cfg;
		}
		if ($cfg['cfg_db_scheme'] != '') {
			$cfg['cfg_db_scheme'] = str_replace('\\', '/', $cfg['cfg_db_scheme']);
			if (!is_file($cfg['cfg_db_scheme'])) {
				$cfg['errormsg'] = 'Database schema file does not exist!';
				return $cfg;
			}
		}
		if ((($cfg['cfg_db_type'] == 'sqlite') || ($cfg['cfg_db_type'] == 'sqlite2')) && ($cfg['cfg_db_scheme'] == '')) {
			$cfg['errormsg'] = 'A schema file is required for '.$cfg['cfg_db_type'];
			return $cfg;
		}
		if ($cfg['cfg_db_ssl'] == 1) {
			if (($cfg['cfg_db_sslca'] == '') || (!file_exists(ELXIS_PATH.'/includes/libraries/elxis/database/certificates/'.$cfg['cfg_db_sslca']))) {
				$cfg['errormsg'] = 'SSL Certificate Authority (CA) file was not set or not found!';
				return $cfg;
			}
		}
		if (($cfg['cfg_db_dsn'] == '') && ($cfg['cfg_db_scheme'] == '')) {
			if ($cfg['cfg_db_user'] == '') {
				$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'DB '.$this->getLang('USERNAME')); return $cfg;
			}
			if ($cfg['cfg_db_pass'] == '') {
				$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'DB '.$this->getLang('PASSWORD')); return $cfg;
			}
		}

		if ($cfg['cfg_ftp'] == 1) {
			if ($cfg['cfg_ftp_host'] == '') {
				$cfg['cfg_ftp_host'] = sprintf($this->getLang('FIELDNOEMPTY'), 'FTP '.$this->getLang('HOST')); return $cfg;
			}
			if ($cfg['cfg_ftp_user'] == '') {
				$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'FTP '.$this->getLang('USERNAME')); return $cfg;
			}
			if ($cfg['cfg_ftp_pass'] == '') {
				$cfg['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), 'FTP '.$this->getLang('PASSWORD')); return $cfg;
			}
		}

		if (isset($_POST['cfg_sitelangs'])) {
			$cfg['cfg_sitelangs'] = trim($this->sanitizeInput('cfg_sitelangs', true, true, false));
			if ($cfg['cfg_sitelangs'] == '') {
				$cfg['cfg_multilinguism'] = 1;
			} else if ($cfg['cfg_sitelangs'] == 'none') {
				$cfg['cfg_sitelangs'] = $cfg['cfg_lang'];
				$cfg['cfg_multilinguism'] = 0;
			} else {
				$parts = explode(',', $cfg['cfg_sitelangs']);
				if (!in_array($cfg['cfg_lang'], $parts)) { $parts[] = $cfg['cfg_lang']; }
				$cfg['cfg_sitelangs'] = implode(',', $parts);
				if (count($parts) == 1) { $cfg['cfg_multilinguism'] = 0; }
			}
		}

		return $cfg;
	}


	/********************************/
	/* CATCH AND VALIDATE USER POST */
	/********************************/
	private function catchUserPost() {
		$usr = array(
			'errormsg' => '',
			'u_firstname' => '',
			'u_lastname' => '',
			'u_email' => '',
			'u_uname' => '',
			'u_pword' => ''
		);

		$usr['u_firstname'] = $this->sanitizeInput('u_firstname', true, true, false);
		$usr['u_lastname'] = $this->sanitizeInput('u_lastname', true, true, false);
		$usr['u_email'] = trim($this->sanitizeInput('u_email', true, true, true));
		$usr['u_uname'] = trim($this->sanitizeInput('u_uname', true, true, true));
		$usr['u_pword'] = trim($this->sanitizeInput('u_pword', true, true, true));
		$u_pword2 = trim($this->sanitizeInput('u_pword2', true, true, true));

		if (trim($usr['u_firstname']) == '') {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('FIRSTNAME'));
			return $usr;
		}
		if (trim($usr['u_lastname']) == '') {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('LASTNAME'));
			return $usr;
		}
		if ($usr['u_email'] == '') {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('EMAIL'));
			return $usr;
		}
		if (!filter_var($usr['u_email'], FILTER_VALIDATE_EMAIL)) {
			$usr['errormsg'] = 'Provided email in invalid!'; return $usr;
		}

		if ($usr['u_uname'] == '') {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('USERNAME')); return $usr;
		}
		if ($usr['u_pword'] == '') {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOEMPTY'), $this->getLang('PASSWORD')); return $usr;
		}
		if ($u_pword2 == '') { $usr['errormsg'] = $this->getLang('PASS_NOMATCH'); return $usr; }
		if ($usr['u_pword'] != $u_pword2) { $usr['errormsg'] = $this->getLang('PASS_NOMATCH'); return $usr; }

		$ustr = preg_replace('/[^A-Z\-\_0-9]/i', '', $usr['u_uname']);
		$pstr = preg_replace('/[^a-z0-9\_\-\.\!\@\#\$\&\(\)\{\}\[\]\?\<\>+]/i', '', $usr['u_pword']);

		if ($ustr != $usr['u_uname']) {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOACCCHAR'), $this->getLang('USERNAME')); return $usr;
		}
		if (strlen($usr['u_uname']) < 4) {
			$usr['errormsg'] = 'Username is too short!'; return $usr;
		}
		$lower = strtolower($usr['u_uname']);
		if (in_array($lower, array('admin', 'administrator', 'root', 'elxis', 'manager'))) {
			$usr['errormsg'] = 'You cannot user a common username such as '.$usr['u_uname']; return $usr;
		}

		if (($pstr != $usr['u_pword']) || (trim($_POST['u_pword']) !=  $usr['u_pword'])) {
			$usr['errormsg'] = sprintf($this->getLang('FIELDNOACCCHAR'), $this->getLang('PASSWORD')); return $usr;
		}

		$pwcheck = $this->checkNormalPasswordPolicy($usr['u_pword'], $usr['u_uname']);
		if ($pwcheck['valid'] == false) {
			$usr['errormsg'] = $pwcheck['errormsg'];
			return $usr;
		}

		return $usr;
	}


	/************************/
	/* GET CURRENT LANGUAGE */
	/************************/
	public function currentLang() {
		return $this->lang;
	}


	/*********************/
	/* GET LANGUAGE INFO */
	/*********************/
	public function langInfo($str) {
		$linfo = $this->ilangs[ $this->lang ];
		if (isset($linfo[$str])) { return $linfo[$str]; }
		return '';
	}


	/******************************/
	/* GET INSTALL LANGUAGES KEYS */
	/******************************/
	public function getiLangs() {
		return $this->ilangs ? array_keys($this->ilangs) : array();
	}


	/********************/
	/* GET VERSION INFO */
	/********************/
	public function verInfo($str) {
		if (isset($this->version[$str])) {
			return $this->version[$str];
		}
		return '';
	}


	/***********************************************/
	/* GET A LIST OF ALL AVAILABLE ELXIS LANGUAGES */
	/***********************************************/
	public function elxisLanguages() {
		$ilangs = $this->listFolders(ELXIS_PATH.'/language/');
		$elxislangs = array();
		include(ELXIS_PATH.'/includes/libraries/elxis/language/langdb.php');
		if ($ilangs) {
			foreach ($ilangs as $ilang) {
				if (isset($langdb[$ilang])) { $elxislangs[$ilang] = $langdb[$ilang];  }
			}
		}

		return $elxislangs;
	}


	/*************************************/
	/* BUILD AND SAVE CONFIGURATION FILE */
	/*************************************/
	private function saveConfig($admin_folder) {
		$mparts = explode('@', $this->data['usr']['u_email']);
		$mdomain = strtolower($mparts[1]);
		$sparts = parse_url($this->data['cfg']['cfg_url']);
		$sdomain = strtolower($sparts['host']);
		if ($this->data['cfg']['cfg_smtp_user'] != '') {
			$fromemail = $this->data['cfg']['cfg_smtp_user'];
		} else if ($mdomain == $sdomain) {
			$fromemail = 'elxis@'.$mparts[1];
		} else {
			$fromemail = $this->data['usr']['u_email'];
		}

		$tld = '';
		$n = strrpos($sdomain, '.');
		if ($n !== false) {
			$n = $n + 1;
			$tld = substr($sdomain, $n);
			$tld = strtoupper($tld);
		}
		unset($mparts, $sparts, $mdomain, $sdomain, $n);

		$timezone = '';
		if (($tld != '') && !in_array($tld, array('LOC', 'COM', 'NET', 'ORG', 'TV', 'INFO', 'BIZ', 'EU', 'EDU', 'GOV', 'MIL', 'TRAVEL', 'ASIA'))) {
			$timezone = $this->getTzone($tld);
			if ($timezone == '') {
				$timezone = $this->browserTimezone();
			}
			if ($timezone == '') {
				$region = strtoupper($this->langInfo('REGION'));
				$timezone = $this->getTzone($region);
			}
		} else {
			$timezone = $this->browserTimezone();
			if ($timezone == '') {
				$region = strtoupper($this->langInfo('REGION'));
				$timezone = $this->getTzone($region);
			}
		}

		if ($timezone == '') { $timezone = 'Europe/London'; }
		unset($tld);

		$cvars = array();
		$cvars['URL'] = $this->data['cfg']['cfg_url'];
		$cvars['REPO_PATH'] = $this->data['cfg']['cfg_repo_path'];
		$cvars['LANG'] = $this->data['cfg']['cfg_lang'];
		$cvars['SITELANGS'] = $this->data['cfg']['cfg_sitelangs'];
		$cvars['MULTILINGUISM'] = $this->data['cfg']['cfg_multilinguism'];
		$cvars['SITENAME'] = addslashes($this->data['cfg']['cfg_sitename']);
		$cvars['ENCRYPT_METHOD'] = $this->data['cfg']['cfg_encrypt_method'];
		$cvars['ENCRYPT_KEY'] = $this->data['cfg']['cfg_encrypt_key'];
		$cvars['DB_TYPE'] = $this->data['cfg']['cfg_db_type'];
		$cvars['DB_HOST'] = $this->data['cfg']['cfg_db_host'];
		$cvars['DB_NAME'] = $this->data['cfg']['cfg_db_name'];
		$cvars['DB_PREFIX'] = $this->data['cfg']['cfg_db_prefix'];
		$cvars['DB_USER'] = addslashes($this->data['cfg']['cfg_db_user']);
		$cvars['DB_PASS'] = addslashes($this->data['cfg']['cfg_db_pass']);
		$cvars['DB_SSL'] = $this->data['cfg']['cfg_db_ssl'];
		$cvars['DB_SSLCA'] = $this->data['cfg']['cfg_db_sslca'];
		$cvars['DB_DSN'] = addslashes($this->data['cfg']['cfg_db_dsn']);
		$cvars['DB_SCHEME'] = $this->data['cfg']['cfg_db_scheme'];
		$cvars['DB_PORT'] = $this->data['cfg']['cfg_db_port'];
		$cvars['DB_PERSISTENT'] = 0;
		$cvars['FTP'] = $this->data['cfg']['cfg_ftp'];
		$cvars['FTP_HOST'] = $this->data['cfg']['cfg_ftp_host'];
		$cvars['FTP_PORT'] = $this->data['cfg']['cfg_ftp_port'];
		$cvars['FTP_ROOT'] = $this->data['cfg']['cfg_ftp_root'];
		$cvars['FTP_USER'] = addslashes($this->data['cfg']['cfg_ftp_user']);
		$cvars['FTP_PASS'] = addslashes($this->data['cfg']['cfg_ftp_pass']);
		$cvars['REGISTRATION_EMAIL_DOMAIN'] = '';
		$cvars['REGISTRATION_EXCLUDE_EMAIL_DOMAINS'] = '';
		$cvars['MAIL_METHOD'] = $this->data['cfg']['cfg_mail_method'];
		$cvars['MAIL_SMTP_HOST'] = $this->data['cfg']['cfg_smtp_host'];
		$cvars['MAIL_SMTP_PORT'] = $this->data['cfg']['cfg_smtp_port'];
		$cvars['MAIL_SMTP_SECURE'] = $this->data['cfg']['cfg_smtp_secure'];
		$cvars['MAIL_SMTP_AUTH'] = $this->data['cfg']['cfg_smtp_auth'];
		$cvars['MAIL_AUTH_METHOD'] = $this->data['cfg']['cfg_mail_auth_method'];
		$cvars['MAIL_SMTP_USER'] = $this->data['cfg']['cfg_smtp_user'];
		$cvars['MAIL_SMTP_PASS'] = $this->data['cfg']['cfg_smtp_pass'];
		$cvars['MAIL_NAME'] = addslashes($this->data['usr']['u_firstname'].' '.$this->data['usr']['u_lastname']);
		$cvars['MAIL_EMAIL'] = $this->data['usr']['u_email'];
		$cvars['MAIL_FROM_NAME'] = addslashes($this->data['cfg']['cfg_sitename']);
		$cvars['MAIL_FROM_EMAIL'] = $fromemail;
		$cvars['MAIL_MANAGER_NAME'] = $this->data['usr']['u_firstname'].' '.$this->data['usr']['u_lastname'];
		$cvars['MAIL_MANAGER_EMAIL'] = $this->data['usr']['u_email'];
		$cvars['OFFLINE_MESSAGE'] = '';
		$cvars['METADESC'] = 'This site is powered by Elxis CMS. Feel the power of open source!';
		$cvars['METAKEYS'] = 'elxis cms, open source, free, multilingual, html5, lightweight, responsive';
		$cvars['ONLINE'] = 1;
		$cvars['DEFAULT_ROUTE'] = 'content:/';
		$cvars['TEMPLATE'] = 'five';
		$cvars['ATEMPLATE'] = 'onyx';
		$cvars['STATISTICS'] = 1;
		$cvars['GZIP'] = 0;
		$cvars['CACHE'] = 0;
		$cvars['CACHE_TIME'] = 1800;
		$cvars['REALNAME'] = 1;
		$cvars['REGISTRATION'] = 0;
		$cvars['REGISTRATION_ACTIVATION'] = 1;
		$cvars['PASS_RECOVER'] = 1;
		$cvars['SESSION_LIFETIME'] = 900;
		$cvars['SESSION_MATCHIP'] = 0;
		$cvars['SESSION_MATCHBROWSER'] = 1;
		$cvars['SESSION_MATCHREFERER'] = 0;
		$cvars['SESSION_ENCRYPT'] = 0;
		$cvars['SECURITY_LEVEL'] = 0;
		$cvars['SESSION_HANDLER'] = 'database';
		$cvars['SSL'] = 0;
		$cvars['ERROR_REPORT'] = 0;
		$cvars['ERROR_LOG'] = 2;
		$cvars['ERROR_ALERT'] = 0;
		$cvars['LOG_ROTATE'] = 1;
		$cvars['DEBUG'] = 0;
		$cvars['MINICSS'] = 0;
		$cvars['MINIJS'] = 0;
		$cvars['DEFENDER'] = 'G';
		$cvars['DEFENDER_NOTIFY'] = 1;
		$cvars['DEFENDER_LOG'] = 0;
		$cvars['DEFENDER_IPAFTER'] = 1;
		$cvars['DEFENDER_WHITELIST'] = '';
		$cvars['TIMEZONE'] = $timezone;
		$cvars['SEF'] = ($this->data['cfg']['cfg_sef'] > 0) ? 1 : 0;
		$cvars['CRONJOBS'] = 0;
		$cvars['CRONJOBS_PROB'] = 10;
		$cvars['LANG_DETECT'] = 0;
		$cvars['XFOPTIONS'] = 'SAMEORIGIN';
		$cvars['CSP'] = '';
		$cvars['SEO_MATCH'] = 'normal';
		$cvars['CAPTCHA'] = 'NOROBOT';
		$cvars['JQUERY'] = '3m';
		$cvars['TRANSLATOR'] = '';
		$cvars['TRANSLATOR_PARAMS'] = '';
		$cvars['TWOFA'] = '';
		$cvars['SMS_PARAMS'] = '';
		$cvars['PWPOLICY'] = 1;

		ksort($cvars);

		$this->data['final']['adminfolder'] = $admin_folder;
		$this->data['final']['renhtaccess'] = -1;
		if ($this->data['cfg']['cfg_sef'] > 0) {
			$ok = $this->makeRewriteFile($this->data['cfg']['cfg_sef'], $admin_folder);
			$this->data['final']['renhtaccess'] = $ok ? 1 : 0;
			if (!$ok) {
				$this->data['cfg']['cfg_sef'] = 0;
				$cvars['SEF'] = 0;
			}
		}

		$out = '<?php '._LEND;
		$out .= '/**'._LEND;
		$out .= 'Elxis CMS - Copyright 2006-'.date('Y').' elxis.org. All rights reserved.'._LEND;
		$out .= 'Last saved on '.gmdate('Y-m-d H:i:s').' (UTC) by '.$this->data['usr']['u_uname']._LEND;
		$out .= '******************************************/'._LEND._LEND;
		$out .= 'defined(\'_ELXIS_\') or die (\'Direct access to this location is not allowed\');'._LEND._LEND._LEND;
		$out .= 'class elxisConfig {'._LEND._LEND;
		foreach ($cvars as $key => $val) {
			if (is_int($val)) {
				$out .= "\t".'private $'.$key.' = '.$val.';'._LEND;
			} else {
				$out .= "\t".'private $'.$key.' = \''.$val.'\';'._LEND;
			}
		}
		$out .= _LEND;
		$out .= "\t".'public function __construct() {'._LEND;
		$out .= "\t".'}'._LEND._LEND;
		$out .= "\t".'public function get($var=\'\') {'._LEND;
		$out .= "\t\t".'if (($var != \'\') && isset($this->$var)) { return $this->$var; }'._LEND;
		$out .= "\t\t".'return \'\';'._LEND;
		$out .= "\t".'}'._LEND._LEND;
		$out .= "\t".'public function set($var, $value) {'._LEND;
		$out .= "\t\t".'if (($var == \'\') || (!is_string($var))) { return false; }'._LEND;
		$out .= "\t\t".'if (isset($this->$var)) {'._LEND;
		$out .= "\t\t\t".'if (!in_array($var, array(\'SITENAME\', \'METADESC\', \'METAKEYS\'))) { return false; }'._LEND;
		$out .= "\t\t".'}'._LEND;
		$out .= "\t\t".'$this->$var = $value;'._LEND;
		$out .= "\t\t".'return true;'._LEND;
		$out .= "\t".'}'._LEND._LEND;
		$out .= '}'._LEND._LEND;
		$out .= '?>';

		$this->data['final']['save'] = false;
		$this->data['final']['config'] = $out;

		if ($handle = @fopen(ELXIS_PATH.'/configuration.php', 'w')) {
			$bytes = @fwrite($handle, $out);
			if ($bytes) { $this->data['final']['save'] = true; }
			fclose($handle);
			if (file_exists(ELXIS_PATH.'/configuration_sample.php')) {
				@unlink(ELXIS_PATH.'/configuration_sample.php');
			}
		}

		if ($this->data['final']['save'] === false) {
			if ($this->data['cfg']['cfg_ftp'] == 1) {
				$ftp_root = rtrim($this->data['cfg']['cfg_ftp_root'], '/');
				if ($ftp_root == '') { $ftp_root = '/'; }
				$repo_path = rtrim($this->data['cfg']['cfg_repo_path'], '/');
				if ($repo_path == '') { $repo_path = ELXIS_PATH.'/repository'; }
				$site_root = preg_replace('#('.$ftp_root.')$#', '', ELXIS_PATH);
				$ftp_root_repo = preg_replace('#^('.$site_root.')#', '', $repo_path);

				if ($this->data['cfg']['cfg_ftp_port'] > 0) {
					$conn_id = @ftp_connect($this->data['cfg']['cfg_ftp_host'], $this->data['cfg']['cfg_ftp_port']);
				} else {
					$conn_id = @ftp_connect($this->data['cfg']['cfg_ftp_host']);
				}

				if ($conn_id) {
					$login_result = @ftp_login($conn_id, $this->data['cfg']['cfg_ftp_user'], $this->data['cfg']['cfg_ftp_pass']);
					if ($login_result) {
						$tmpFile = $repo_path.'/tmp/configuration.php';
						if (@file_put_contents($tmpFile, $this->data['final']['config'])) {
							$ftppath = $ftp_root.'/configuration.php';
							$upload = ftp_put($conn_id, $ftppath, $tmpFile, FTP_BINARY);
							if ($upload) {
								$this->data['final']['save'] = true;
								if (file_exists(ELXIS_PATH.'/configuration_sample.php')) {
									@ftp_delete($conn_id, $ftp_root.'/configuration_sample.php');
								}
							}
						}
						@unlink($tmpFile);
					}
					ftp_close($conn_id);
				}
			}
		}
	}


	/**************************/
	/* MAKE A RANDOM USERNAME */
	/**************************/
	public function makeUname() {
		$greek = array('zeus', 'hermes', 'apollo', 'athena', 'poseidon', 'hades', 'cronus', 'erebos', 'chaos', 
		'uranus', 'tartarus', 'iapetos', 'atlas', 'prometheus', 'gaia', 'talos', 'typhon', 'phobos', 'cerberus',
		'medusa', 'proteus', 'triton', 'pandora', 'electra', 'nestor', 'pythagoras', 'socrates', 'archimedes', 'hector', 
		'ajax', 'theseus', 'orpheus', 'cadmus', 'anaxagoras', 'protagoras', 'hypatia', 'homer');
		$italian = array('flora', 'janus', 'juno', 'mars', 'mercury', 'pluto', 'saturn', 'venus', 'vulcan', 
		'vesta', 'minerva', 'fauna', 'diana', 'aurora', 'luna', 'hercules', 'augustus', 'caligula', 'tiberius', 
		'titus', 'magnus', 'severus', 'claudius', 'cicero', 'seneca');
		$german = array('odin', 'loki', 'thor', 'balder', 'njord', 'buri', 'seth', 'freya', 'freyr', 'midgard', 'valhalla', 
		'hesus', 'fornjot', 'druden', 'donar', 'alfadir', 'picullus');
		$inter = array('helix', 'acropolis', 'analysis', 'genesis', 'eureka', 'abyss', 'enigma', 'amazon', 
		'anax', 'asterisk', 'helios', 'nectar', 'utopia', 'paradox', 'alpha', 'delta', 'epsilon', 'sigma', 'omega', 'olympus', 'pilot', 'harmony', 
		'acrobat', 'astronaut', 'captain', 'cosmos', 'discus', 'hypnosis', 'logic', 'micro', 'neutron', 'electron', 'proton', 
		'photon', 'neuron', 'oasis', 'panic', 'phoenix', 'planet', 'python', 'thesis', 'typhoon', 'android', 'energy', 
		'oxygen', 'bios', 'logos');

		$f = rand(0, 9);
		if ($this->langInfo('LANGUAGE') == 'el') {
			shuffle($greek);
			return $greek[$f];
		} else if ($this->langInfo('LANGUAGE') == 'it') {
			shuffle($italian);
			return $italian[$f];
		} else if ($this->langInfo('LANGUAGE') == 'de') {
			shuffle($german);
			return $german[$f];
		} else {
			$arr = array_merge($greek, $inter);
			shuffle($arr);
			return $arr[$f];
		}
	}


	/************************************/
	/* GET PROPER TIMEZONE FOR A REGION */
	/************************************/
	private function getTzone($region) {
		switch ($region) {
			case 'EG': $tzone = 'Africa/Cairo'; break;
			case 'AU': $tzone = 'Australia/Melbourne'; break;
			case 'RS': $tzone = 'Europe/Belgrade'; break;
			case 'DE': $tzone = 'Europe/Berlin'; break;
			case 'PT': $tzone = 'Europe/Lisbon'; break;
			case 'CY': $tzone = 'Europe/Nicosia'; break;
			case 'IT': $tzone = 'Europe/Rome'; break;
			case 'ES': $tzone = 'Europe/Madrid'; break;
			case 'BG': $tzone = 'Europe/Sofia'; break;
			case 'CZ': $tzone = 'Europe/Prague'; break;
			case 'GR': $tzone = 'Europe/Athens'; break;
			case 'CY': $tzone = 'Europe/Nicosia'; break;
			case 'PL': $tzone = 'Europe/Warsaw'; break;
			case 'FR': $tzone = 'Europe/Paris'; break;
			case 'NO': $tzone = 'Europe/Oslo'; break;
			case 'HU': $tzone = 'Europe/Budapest'; break;
			case 'DK': $tzone = 'Europe/Copenhagen'; break;
			case 'UA': $tzone = 'Europe/Kyiv'; break;
			case 'RU': $tzone = 'Europe/Moscow'; break;
			case 'AL': $tzone = 'Europe/Tirane'; break;
			case 'EE': $tzone = 'Europe/Tallinn'; break;
			case 'LV': $tzone = 'Europe/Riga'; break;
			case 'NL': $tzone = 'Europe/Amsterdam'; break;
			case 'FI': $tzone = 'Europe/Helsinki'; break;
			case 'SI': $tzone = 'Europe/Ljubljana'; break;
			case 'SK': $tzone = 'Europe/Bratislava'; break;
			case 'RO': $tzone = 'Europe/Bucharest'; break;
			case 'BE': $tzone = 'Europe/Brussels'; break;
			case 'HR': $tzone = 'Europe/Zagreb'; break;
			case 'LT': $tzone = 'Europe/Vilnius'; break;
			case 'SE': $tzone = 'Europe/Stockholm'; break;
			case 'UK': case 'GB': case 'IS': case 'IE': $tzone = 'Europe/London'; break;
			case 'BA': $tzone = 'Europe/Sarajevo'; break;
			case 'MK': $tzone = 'Europe/Skopje'; break; //macedonia is Greek
			case 'IL': $tzone = 'Asia/Tel_Aviv'; break;
			case 'JP': $tzone = 'Asia/Tokyo'; break;
			case 'TR': $tzone = 'Asia/Istanbul'; break;
			case 'IN': $tzone = 'Asia/Calcutta'; break;
			case 'IR': $tzone = 'Asia/Tehran'; break;
			case 'ZN': $tzone = 'Asia/Shanghai'; break;
			case 'CA': $tzone = 'America/Toronto'; break;
			case 'AR': $tzone = 'America/Buenos_Aires'; break;
			case 'KR': $tzone = 'Asia/Seoul'; break;
			case 'GE': $tzone = 'Asia/Tbilisi'; break;
			case 'MX': $tzone = 'America/Mexico_City'; break;
			case 'US': $tzone = 'America/New_York'; break;
			case 'ZA': $tzone = 'Africa/Lusaka'; break;
			case 'BR': $tzone = 'America/Sao_Paulo'; break;
			default: $tzone = ''; break;
		}
		return $tzone;
	}


	/********************************/
	/* DETECT TIMEZONE FROM BROWSER */
	/********************************/
	private function browserTimezone() {
		if (!isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { return ''; }
		$acc_langs_str = trim($_SERVER['HTTP_ACCEPT_LANGUAGE']);
		if ($acc_langs_str == '') { return ''; }
		$langs = array();
		$acc_langs = explode(',', trim($acc_langs_str));
		foreach ($acc_langs as $acc_lang) {
			if (preg_match('/(\*|[a-zA-Z0-9]{1,8}(?:-[a-zA-Z0-9]{1,8})*)(?:\s*;\s*q\s*=\s*(0(?:\.\d{0,3})|1(?:\.0{0,3})))?/', trim($acc_lang), $match)) {
				$q = (!isset($match[2])) ? '1.0' : (string)floatval($match[2]);
				$m = trim(strtolower($match[1]));
				$n = strpos($m, '-');
				if ($n !== false) { $m = substr($m, 0, $n); }
				if (!isset($langs[$q])) { $langs[$q] = array(); }
				$langs[$q][] = $m;
			}
		}
		if (!$langs) { return ''; }
		krsort($langs);
		$final_lang = '';
		foreach ($langs as $k => $lngs) {
			$final_lang = strtolower($lngs[0]);
			break;
		}

		switch ($final_lang) {
			case 'sv': $tzone = 'Europe/Stockholm'; break;
			case 'lt': $tzone = 'Europe/Vilnius'; break;
			case 'sk': $tzone = 'Europe/Bratislava'; break;
			case 'sl': $tzone = 'Europe/Ljubljana'; break;
			case 'rs': case 'sr': $tzone = 'Europe/Belgrade'; break;
			case 'de': $tzone = 'Europe/Berlin'; break;
			case 'pt': $tzone = 'Europe/Lisbon'; break;
			case 'sq': $tzone = 'Europe/Tirane'; break;
			case 'it': $tzone = 'Europe/Rome'; break;
			case 'es': $tzone = 'Europe/Madrid'; break;
			case 'bg': $tzone = 'Europe/Sofia'; break;
			case 'cs': $tzone = 'Europe/Prague'; break;
			case 'el': $tzone = 'Europe/Athens'; break;
			case 'pl': $tzone = 'Europe/Warsaw'; break;
			case 'fr': $tzone = 'Europe/Paris'; break;
			case 'no': $tzone = 'Europe/Oslo'; break;
			case 'hu': $tzone = 'Europe/Budapest'; break;
			case 'da': $tzone = 'Europe/Copenhagen'; break;
			case 'uk': case 'ua': $tzone = 'Europe/Kyiv'; break;
			case 'ru': $tzone = 'Europe/Moscow'; break;
			case 'et': $tzone = 'Europe/Tallinn'; break;
			case 'lv': $tzone = 'Europe/Riga'; break;
			case 'nl': $tzone = 'Europe/Amsterdam'; break;
			case 'fi': $tzone = 'Europe/Helsinki'; break;
			case 'ro': $tzone = 'Europe/Bucharest'; break;
			case 'hr': $tzone = 'Europe/Zagreb'; break;
			case 'mk': $tzone = 'Europe/Skopje'; break; //macedonia is Greek
			case 'il': $tzone = 'Asia/Tel_Aviv'; break;
			case 'ja': $tzone = 'Asia/Tokyo'; break;
			case 'tr': $tzone = 'Asia/Istanbul'; break;
			case 'hi': $tzone = 'Asia/Calcutta'; break;
			case 'fa': $tzone = 'Asia/Tehran'; break;
			case 'in': $tzone = 'Asia/Jakarta'; break;
			case 'vi': $tzone = 'Asia/Ho_Chi_Minh'; break;
			case 'ar': $tzone = 'Asia/Dubai'; break;
			case 'zt': case 'zh': $tzone = 'Asia/Shanghai'; break;
			case 'ko': $tzone = 'Asia/Seoul'; break;
			case 'en': $tzone = 'Europe/London'; break;
			default: $tzone = ''; break;
		}
		return $tzone;
	}


	/*******************************************/
	/* RENAME ESTIA FOLDER AND RETURN NEW NAME */
	/*******************************************/
	private function renameEstia() {
		$admin_folder = 'estia';
		if (!file_exists(ELXIS_PATH.'/estia/')) { return 'estia'; } 
		if (!is_dir(ELXIS_PATH.'/estia/')) { return 'estia'; }

		$names = array(
			'abydos', 'actium', 'aegina', 'apollonia', 'argos', 'byzantium', 'corinth', 'delos', 'dion', 'eretria', 'ephesus', 'ithaca', 'knossos', 'korinthos', 
			'marathon', 'megara', 'mausolus', 'miletus', 'nemea', 'odessos', 'olbia', 'pella', 'pergamos', 'phaistos', 'salamis', 'selinus', 'smyrna', 'taras', '
			thebes', 'tripolis', 'troy', 'vergina', 'zakros'
		);
		$r = array_rand($names);
		$f = rand(1, 20);
		$newname = $names[$r].''.$f;
		if (file_exists(ELXIS_PATH.'/'.$newname.'/')) { return 'estia'; } //dont rename if there is already such a folder

		$ok = @rename(ELXIS_PATH.'/estia/', ELXIS_PATH.'/'.$newname.'/');
		if ($ok) { return $newname; }
		return 'estia';
	}


	/******************************************************/
	/* RENAME ELXIS REPOSITORY FOLDER TO SOMETHING RANDOM */
	/******************************************************/
	private function renameRepository() {
		if ($this->data['cfg']['cfg_repo_path'] != '') { return; }
		if (!file_exists(ELXIS_PATH.'/repository/')) { return; } 

		$alphanum = array_merge(range(0,9), range('a', 'z'),range('A', 'Z'));
		$length = count($alphanum) - 1;
		$rnd = '';
		for($i=0; $i < 5; $i++) {
			$idx = mt_rand(0, $length);
			$rnd .= $alphanum[$idx];
		}

		$newname = 'elxrepo'.$rnd;
		$ok = @rename(ELXIS_PATH.'/repository/', ELXIS_PATH.'/'.$newname.'/');
		if ($ok) {
			$this->data['cfg']['cfg_repo_path'] = ELXIS_PATH.'/'.$newname.'/';
		}
	}


	/***********************************************/
	/* GENERATE AND SAVE .HTACCESS/WEB.CONFIG FILE */
	/***********************************************/
	private function makeRewriteFile($sef, $admin_folder) {
		$sef = (int)$sef;
		if ($sef < 1) { return; }
		if ($sef == 2) {
			$file_initial = 'web.config.txt';
			$file_final = 'web.config';
		} else {
			$file_initial = 'htaccess.txt';
			$file_final = '.htaccess';
		}

		if (!file_exists(ELXIS_PATH.'/includes/install/data/'.$file_initial)) { return false; }
		$handle = @fopen(ELXIS_PATH.'/includes/install/data/'.$file_initial, 'r');
		if (!$handle) { return false; }
		$data = '';
		while (!feof($handle)) {
			$line = fgets($handle);
			$line = preg_replace( "/\r|\n/", '', $line);
			if ($admin_folder != 'estia') { $line = str_replace('estia', $admin_folder, $line); }
			if ($this->relpath != '') { $line = str_replace('RewriteBase /', 'RewriteBase /'.$this->relpath.'/', $line); }
			$data .= $line."\r\n";
		}
		fclose($handle);

		if (file_exists(ELXIS_PATH.'/'.$file_final)) {
			$ok = @unlink(ELXIS_PATH.'/'.$file_final);
			if (!$ok) { return false; }
		}

		if ($handle = @fopen(ELXIS_PATH.'/'.$file_final, 'w')) {
			@fwrite($handle, $data);
			fclose($handle);
			return true;
		}

		return false;
	}


	/********************************************************************************************/
	/* SANITIZE GET/POST INPUT - EQUIVALENT TO "FILTER_SANITIZE_STRING" (deprecated in PHP 8.1) */
	/********************************************************************************************/
	private function sanitizeInput($name, $is_post=true, $low=false, $high=false) {
		if ($is_post) {
			if (!isset($_POST[$name])) { return ''; }
			$type = INPUT_POST;
		} else {
			if (!isset($_GET[$name])) { return ''; }
			$type = INPUT_GET;
		}
		if ($low && $high) {
			$v = filter_input($type, $name, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW | FILTER_FLAG_STRIP_HIGH);
		} else if ($low) {
			$v = filter_input($type, $name, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_LOW);
		} else if ($high) {
			$v = filter_input($type, $name, FILTER_UNSAFE_RAW, FILTER_FLAG_STRIP_HIGH);
		} else {
			$v = filter_input($type, $name, FILTER_UNSAFE_RAW);
		}
		if ($v == '') { return ''; }

		$str = preg_replace('/\x00|<[^>]*>?/', '', $v);
		return str_replace(["'", '"'], ['&#39;', '&#34;'], $str);
	}


	/*******************************************************/
	/* CHECK IF PASSWORD MEETS "NORMAL" SECURITY STANDARDS */
	/*******************************************************/
	private function checkNormalPasswordPolicy($pword, $uname) {
		$response = array('valid' => false, 'errormsg' => '');

		$pword_san = trim(preg_replace('/[^a-z0-9\_\-\.\!\@\#\$\&\(\)\{\}\[\]\?\<\>+]/i', '', $pword));
		if ($pword !== $pword_san) {
			$response['errormsg'] = sprintf($this->getLang('FIELDNOACCCHAR'), $this->getLang('PASSWORD'));
			return $response;
		}

		if (strlen($pword) < 8) {
			$response['errormsg'] = sprintf($this->getLang('PASS_MUSTLENGTH'), '8');
			return $response;
		}

		preg_match_all("~([a-z+])~", $pword, $matches);
		if (count($matches[0]) < 1) {
			$txt = sprintf($this->getLang('PASS_LOWLCHARS'), '1');
			$response['errormsg'] = sprintf($this->getLang('PASS_MUSTCONTAIN'), $txt);
			return $response;
		}
		preg_match_all("~([A-Z+])~", $pword, $matches);
		if (count($matches[0]) < 1) {
			$txt = sprintf($this->getLang('PASS_UPPLCHARS'), '1');
			$response['errormsg'] = sprintf($this->getLang('PASS_MUSTCONTAIN'), $txt);
			return $response;
		}
		preg_match_all("~([0-9+])~", $pword, $matches);
		if (count($matches[0]) < 1) {
			$txt = sprintf($this->getLang('PASS_DIGITS'), '1');
			$response['errormsg'] = sprintf($this->getLang('PASS_MUSTCONTAIN'), $txt);
			return $response;
		}
		preg_match_all("~([\_\-\.\!\@\#\$\&\(\)\{\}\[\]\?\<\>+])~", $pword, $matches);
		if (count($matches[0]) < 1) {
			$txt = sprintf($this->getLang('PASS_SYMBOLS'), '1');
			$response['errormsg'] = sprintf($this->getLang('PASS_MUSTCONTAIN'), $txt);
			return $response;
		}
		if (stripos($pword, $uname) !== false) {
			$response['errormsg'] = 'Password cannot contain username!';
			return $response;
		}

		$response['valid'] = true;
		return $response;
	}

}

?>