<?php
/**
* @version	$Id: modules_helper.php 16513 2017-01-20 14:10:53Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

	defined('FULL_PATH') or die('restricted access!');

	class kModulesHelper extends kHelper {

		/**
		 * Identifies new module, that isn't installed yet
		 */
		const NOT_INSTALLED = 1;

		/**
		 * Identifies installed module
		 */
		const INSTALLED = 2;

		/**
		 * Identifies both installed & new modules
		 */
		const ANY = 3;

		function getWhereClause()
		{
			$where_clause = Array('Loaded = 1');

			if (!$this->Application->isAdmin) {
				// no license checks on front-end
				return implode(' AND ', $where_clause);
			}

			$modules = $this->_GetModules();
			if ($modules) {
				foreach ($modules as $module_index => $module) {
					$modules[$module_index] = $this->Conn->qstr($module);
				}
				$where_clause[] = 'Name IN ('.implode(',', $modules).')';
			}

			return implode(' AND ', $where_clause);
		}

		function _EnableCookieSID()
		{
			/** @var Session $session */
			$session = $this->Application->recallObject('Session');

			return $session->CookiesEnabled;
		}

		function _IsSpider($UserAgent)
		{
			global $robots;
			$lines = file(FULL_PATH.'/robots_list.txt');

			if (!is_array($robots)) {
				$robots = Array();
				for($i = 0; $i < count($lines); $i++) {
					$l = $lines[$i];
					$p = explode("\t", $l, 3);
					$robots[] = $p[2];
		    	}
		  	}
			return in_array($UserAgent, $robots);
		}

		function _MatchIp($ip1, $ip2)
		{
			$matched = TRUE;

			$ip = explode('.', $ip1);
			$MatchIp = explode('.', $ip2);
			for ($i = 0; $i < count($ip); $i++) {
				if($i == count($MatchIp)) break;
				if (trim($ip[$i]) != trim($MatchIp[$i]) || trim($ip[$i]) == '*') {
					$matched = FALSE;
					break;
				}
			}
			return $matched;
		}

		function _IpAccess($IpAddress, $AllowList, $DenyList)
		{
			$allowed = explode(',', $AllowList);
			$denied = explode(',', $DenyList);

			$MatchAllowed = FALSE;
			for ($x = 0; $x < count($allowed); $x++) {
				$ip = explode('.', $allowed[$x]);

				$MatchAllowed = $this->_MatchIp($IpAddress, $allowed[$x]);
				if ($MatchAllowed)
				break;
			}
			$MatchDenied = FALSE;
			for ($x = 0; $x < count($denied); $x++) {
				$ip = explode('.', $denied[$x]);

				$MatchDenied = $this->_MatchIp($IpAddress, $denied[$x]);
				if ($MatchDenied)
				break;
			}

			$Result = (($MatchAllowed && !$MatchDenied) || (!$MatchAllowed && !$MatchDenied) ||
		  			  ($MatchAllowed && $MatchDenied));
			return $Result;
		}

		/**
		 * Leaves only domain part from hostname (e.g. extract "intechnic.lv" from "test.intechnic.lv")
		 * Used for admin login license check
		 *
		 * @param string $d
		 * @return string
		 */
		function _StripDomainHost($d)
		{
			$IsIp = false;
			$dotcount = substr_count($d, '.');
			if ($dotcount == 3) {
				$IsIp = true;
				for ($x = 0; $x < strlen($d); $x++) {
					if (!is_numeric(substr($d, $x, 1)) && substr($d, $x, 1) != '.')
					{
						$IsIp = false;
						break;
					}
				}
			}

			if ($dotcount > 1 && !$IsIp) {
				$p = explode('.', $d);
				$ret = $p[count($p) - 2].'.'.$p[count($p) - 1];
			}
			else {
				$ret = $d;
			}
			return $ret;
		}

		/**
		 * When logging into admin then check only last 2 parts of host name VS domain in license
		 *
		 * @param string $user_domain
		 * @param string $license_domain
		 * @return int
		 */
		function _CheckDomain($user_domain, $license_domain)
		{
			if ($this->Application->isAdmin) {
				$user_domain = $this->_StripDomainHost($user_domain);
				return preg_match('/(.*)'.preg_quote($user_domain, '/').'$/', $license_domain);
			}
			else {
				return preg_match('/(.*)'.preg_quote($license_domain, '/').'$/', $user_domain);
			}
		}

		/**
		 * Returns modules list, that are in license
		 *
		 * @return Array
		 */
		function _GetModules()
		{
		    static $modules = null;

		    if (isset($modules)) {
		    	return $modules;
		    }

		    $modules = Array();
			$system_config = kUtil::getSystemConfig();
		    $license = $system_config->get('License') ? base64_decode($system_config->get('License')) : false;

		    if ($license) {
			    list ( , , $i_Keys) = $this->_ParseLicense($license);
			    $domain = $this->_GetDomain($system_config);
			    if (!$this->_IsLocalSite($domain)) {
			        for ($x = 0; $x < count($i_Keys); $x++) {
			            $key = $i_Keys[$x];
			            if ($this->_CheckDomain($domain, $key['domain'])) {
			            	// used hostname is subdomain or matches domain from license
			            	$modules = explode(',', $key['mod']);
			            }
			        }
			    }
			    else {
			    	// all already installed modules are licensed for localhost
			        $modules = array_keys($this->Application->ModuleInfo);
			    }
		    }

		    // all modules starting from "in-" doesn't require license
		    $base_modules = Array ('Core', 'In-Portal', 'Custom');
		    $modules = array_merge($modules, $base_modules, $this->_getFreeModules($system_config));
		    $modules = array_unique( array_map('strtolower', $modules) );

		    return $modules;
		}

		/**
		 * Get all modules, that don't require licensing
		 *
		 * @param kSystemConfig $system_config
		 * @return Array
		 * @access protected
		 */
		protected function _getFreeModules(kSystemConfig $system_config)
		{
			return array_map('strtolower', $this->getModules());
		}

		/**
		 * Allows to determine if module is licensed
		 *
		 * @param string $name
		 * @return bool
		 */
		function _ModuleLicensed($name)
		{
		    $modules = $this->_GetModules();
			return in_array($name, $modules);
		}

		/**
		 * Returns domain from licences (and direct in case of install script)
		 *
		 * @param kSystemConfig $system_config
		 * @return string
		 */
		function _GetDomain(kSystemConfig $system_config)
		{
			return $system_config->get('Domain', SERVER_NAME);
		}

		function _keyED($txt, $encrypt_key)
		{
			$encrypt_key = md5($encrypt_key);
			$ctr = 0;
			$tmp = '';
			for ($i = 0; $i < strlen($txt); $i++) {
				if ($ctr == strlen($encrypt_key)) $ctr = 0;
				$tmp .= substr($txt, $i, 1) ^ substr($encrypt_key, $ctr, 1);
				$ctr++;
			}
			return $tmp;
		}


		function _decrypt($txt, $key)
		{
			$txt = $this->_keyED($txt,$key);
			$tmp = '';
			for ($i = 0; $i < strlen($txt); $i++) {
				$md5 = substr($txt, $i, 1);
				$i++;
				$tmp .= (substr($txt, $i, 1) ^ $md5);
			}
			return $tmp;
		}

		function LoadFromRemote()
		{
		    return '';
		}

		function DLid()
		{
		    die($GLOBALS['lid']."\n");
		}

		function _LoadLicense($LoadRemote = false)
		{
		    $f = FULL_PATH.'/intechnic.php';
		    if ($this->_falseIsLocalSite($f)) $ret = true;
		    if (file_exists($f)) {
		        $contents = file($f);
		        $data = base64_decode($contents[1]);
		    }
		    else {
		        if ($LoadRemote) return $LoadFromRemote;
		    }
		   return $data;
		}

		function _VerifyKey($domain, $k)
		{
		    $key = md5($domain);
		    $lkey = substr($key, 0, strlen($key) / 2);
		    $rkey = substr($key, strlen($key) / 2);
		    $r = $rkey.$lkey;
		    if ($k == $r) return true;
		    return false;
		}

		function _ParseLicense($txt)
		{
//			global $i_User, $i_Pswd, $i_Keys;

			if (!$this->_falseIsLocalSite($txt)) {
				$nah = false;
			}

			$data = $this->_decrypt($txt, 'beagle');
			$i_User = $i_Pswd = '';
			$i_Keys = Array();
			$lines = explode("\n", $data);
			for ($x = 0; $x < count($lines); $x++) {
				$l = $lines[$x];
				$p = explode('=', $l, 2);
				switch($p[0]) {
					case 'Username':
						$i_User = $p[1];
						break;

					case 'UserPass':
						$i_Pswd = $p[1];
						break;

					default:
						if (substr($p[0], 0, 3) == 'key') {
							$parts = explode('|', $p[1]);
							if ($this->_VerifyKey($parts[0], $parts[1])) {
								unset($K);
								$k['domain'] = $parts[0];
								$k['key'] = $parts[1];
								$k['desc'] = $parts[2];
								$k['mod'] = $parts[3];
								$i_Keys[] = $k;
							}
						}
						break;
				}
			}

			return Array ($i_User, $i_Pswd, $i_Keys);
		}

		function _GetObscureValue($i)
		{
			if ($i == 'x') return 0254; $z = '';
			if ($i == 'z') return 0x7F.'.';
			if ($i == 'c') return '--code--';
			if ($i >= 5 && $i < 7) return $this->_GetObscureValue($z)*$this->_GetObscureValue('e');
			if ($i > 30) return Array(0x6c,0x6f,0x63,0x61,0x6c,0x68,0x6f,0x73,0x74);
			if ($i > 20) return 99;
			if ($i > 10) return '.'.($this->_GetObscureValue(6.5)+1);
			if ($i == 'a') return 0xa;

			return 0;
		}

		function _Chr($val)
		{
			$x = $this->_GetObscureValue(25);
			$f = chr($x).chr($x+5).chr($x+15);
			return $f($val);
		}

		function _IsLocalSite($domain)
		{
		    $ee = $this->_GetObscureValue(35); $yy = '';
			foreach ($ee as $e) $yy .= $this->_Chr($e);
		    $localb = FALSE;
		    if(substr($domain,0,3)==$this->_GetObscureValue('x'))
		    {
		        $b = substr($domain,0,6);
		        $p = explode(".",$domain);
		        $subnet = $p[1];
		        if($p[1]>15 && $p[1]<32)
		            $localb=TRUE;
		    }
		    $zz = $this->_GetObscureValue('z').$this->_GetObscureValue(5).'.'.(int)$this->_GetObscureValue(7).$this->_GetObscureValue(12);
		    $ff = $this->_GetObscureValue('z')+65;
		    $hh = $ff-0x18;
		    if($domain==$yy || $domain==$zz || substr($domain,0,7)==$ff.$this->_Chr(46).$hh ||
		       substr($domain,0,3)==$this->_GetObscureValue('a').$this->_Chr(46) || $localb || strpos($domain,".")==0)
		    {
		        return TRUE;
		    }
		    return FALSE;
		}

		function _falseIsLocalSite($domain)
		{
		    $localb = FALSE;
		    if(substr($domain,0,3)=="172")
		    {
		        $b = substr($domain,0,6);
		        $p = explode(".",$domain);
		        $subnet = $p[1];
		        if($p[1]>15 && $p[1]<32)
		            $localb=TRUE;
		    }
		    if($domain=="localhost" || $domain=="127.0.0.1" || substr($domain,0,7)=="192.168" ||
		       substr($domain,0,3)=="10." || $localb || strpos($domain,".")==0)
		    {
		        return TRUE;
		    }
		    return FALSE;
		}

		function verifyLicense($license_hash)
		{
			$license_hash = base64_decode($license_hash);
			list ($license_user, $license_password, ) = $this->_ParseLicense($license_hash);
			return strlen($license_user) && strlen($license_password);
		}

		function moduleInstalled($module_name)
		{
			static $modules = null;

			if ( is_null($modules) ) {
				$sql = 'SELECT LOWER(Name)
						FROM ' . $this->Application->getUnitOption('mod', 'TableName');
				$modules = $this->Conn->GetCol($sql);
			}

			if ( $module_name == 'kernel' ) {
				$module_name = 'in-portal';
			}

			return in_array(strtolower($module_name), $modules);
		}

		/**
		 * Returns list of matching modules
		 *
		 * @param int $module_type
		 * @return Array
		 * @access public
		 */
		public function getModules($module_type = self::ANY)
		{
			$modules = Array ();

			try {
				$iterator = new DirectoryIterator(MODULES_PATH);
				/** @var DirectoryIterator $file_info */
			}
			catch (UnexpectedValueException $e) {
				return $modules;
			}

			foreach ($iterator as $file_info) {
				$file_path = $file_info->getPathname();

				if ( $file_info->isDir() && !$file_info->isDot() && $this->isInPortalModule($file_path) ) {
					$install_order = trim( file_get_contents($file_path . '/install/install_order.txt') );
					$modules[$install_order] = $file_info->getFilename();
				}
			}

			// allows to control module install order
			ksort($modules, SORT_NUMERIC);

			if ( $module_type == self::ANY ) {
				return $modules;
			}

			foreach ($modules as $install_order => $module_name) {
				$installed = $this->moduleInstalled($module_name);

				if ( ($module_type == self::INSTALLED && !$installed) || ($module_type == self::NOT_INSTALLED && $installed) ) {
					unset($modules[$install_order]);
				}
			}

			return $modules;
		}

		/**
		 * Checks, that given folder is In-Portal module's root folder
		 *
		 * @param string $folder_path
		 * @return bool
		 * @access public
		 */
		public static function isInPortalModule($folder_path)
		{
			return file_exists($folder_path . '/install.php') && file_exists($folder_path . '/install/install_schema.sql');
		}
	}
