<?php
/**
* @version	$Id: http_query.php 14360 2011-06-01 07:50:46Z 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 kHTTPQuery extends Params {

	/**
	 * $_POST vars
	 *
	 * @var Array
	 * @access private
	 */
	var $Post;

	/**
	 * $_GET vars
	 *
	 * @var Array
	 * @access private
	 */
	var $Get;

	/**
	 * $_COOKIE vars
	 *
	 * @var Array
	 * @access private
	 */
	var $Cookie;

	/**
	 * $_SERVER vars
	 *
	 * @var Array
	 * @access private
	 */
	var $Server;

	/**
	 * $_ENV vars
	 *
	 * @var Array
	 * @access private
	 */
	var $Env;

	/**
	 * Order in what write
	 * all vars together in
	 * the same array
	 *
	 * @var string
	 */
	var $Order;

	/**
	 * Uploaded files info
	 *
	 * @var Array
	 * @access private
	 */
	var $Files;

	var $specialsToRemove = Array();

	/**
	* Description
	*
	* @var kDBConnection
	* @access public
	*/
	var $Conn;

	/**
	 * SessionID is given via "sid" variable in query string
	 *
	 * @var bool
	 */
	var $_sidInQueryString = false;

	/**
	 * Loads info from $_POST, $_GET and
	 * related arrays into common place
	 *
	 * @param string $order
	 * @return HTTPQuery
	 * @access public
	 */
	function kHTTPQuery($order = 'CGPF')
	{
		parent::Params();

		$this->Conn =& $this->Application->GetADODBConnection();
		$this->Order = $order;

		if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
			// when AJAX request is made from jQuery, then create ajax variable,
			// so any logic based in it (like redirects) will not break down
			$_GET['ajax'] = 'yes';
		}
	}

	function Init($prefix, $special)
	{
		$this->AddAllVars();

		$this->specialsToRemove = $this->Get('remove_specials');
		if ($this->specialsToRemove) {
			foreach ($this->specialsToRemove as $prefix_special => $flag) {
				if ($flag && strpos($prefix_special, '.') === false) {
					unset($this->specialsToRemove[$prefix_special]);
					trigger_error('Incorrect usage of "<strong>remove_specials['.$prefix_special.']</strong>" field (no special found)', E_USER_NOTICE);
				}
			}
			$this->_Params = $this->removeSpecials($this->_Params);
		}
		ini_set('magic_quotes_gpc', 0);
	}

	function removeSpecials($array)
	{
		$ret = Array();
		$removed = false;
		foreach ($this->specialsToRemove as $prefix_special => $flag) {
			if ($flag) {
				$removed = true;
				list ($prefix,$special) = explode('.', $prefix_special, 2);
				foreach ($array as $key => $val) {
					$new_key = preg_match("/^".$prefix."[._]{1}".$special."(.*)/", $key, $regs) ? $prefix.$regs[1] : $key;
					$ret[$new_key] = is_array($val) ? $this->removeSpecials($val) : $val;
				}
			}
		}
		return $removed ? $ret : $array;
	}

	/**
	 * All all requested vars to
	 * common storage place
	 *
	 * @access private
	 */
	function AddAllVars()
	{
		for ($i=0; $i < strlen($this->Order); $i++)
		{
			$current = $this->Order[$i];
			switch ($current) {
				case 'G':
					$this->Get = $this->AddVars($_GET);
					if (array_key_exists('sid', $_GET)) {
						$this->_sidInQueryString = true;
					}

					$vars = $this->processQueryString( $this->Get(ENV_VAR_NAME) );

					if (array_key_exists('sid', $vars)) {
						// used by Session::GetPassedSIDValue
						$this->Get['sid'] = $vars['sid'];
					}

					$this->AddParams($vars);
					break;

				case 'P':
					$this->Post = $this->AddVars($_POST);
					$this->convertPostEvents();
					$this->_processPostEnvVariables();
					break;

				case 'C':
					$this->Cookie = $this->AddVars($_COOKIE);
					break;

				case 'E';
					$this->Env = $this->AddVars($_ENV);
					break;

				case 'S';
					$this->Server = $this->AddVars($_SERVER);
					break;

				case 'F';
					$this->convertFiles();
					$this->Files = $this->MergeVars($_FILES, false); //do not strip slashes!
					break;
			}
		}

//		$this->AfterInit();
	}

	/**
	 * Allow POST variables, that names were transformed by PHP ("." replaced with "_") to
	 * override variables, that were virtually created through enviroment variable parsing
	 *
	 */
	function _processPostEnvVariables()
	{
		$passed = $this->Get('passed');
		if (!$passed) {
			return ;
		}

		$passed = explode(',', $passed);
		foreach ($passed as $prefix_special) {
			if (strpos($prefix_special, '.') === false) {
				continue;
			}

			list ($prefix, $special) = explode('.', $prefix_special);
			$query_map = $this->Application->EventManager->getQueryMap($prefix);
			$post_prefix_special = $prefix . '_' . $special;

			foreach ($query_map as $index => $var_name) {
				if (array_key_exists($post_prefix_special . '_' . $var_name, $this->Post)) {
					$this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]);
				}
			}
		}
	}

	function AfterInit()
	{
		$rewrite_url = $this->Get('_mod_rw_url_');

		if ($this->Application->RewriteURLs() || $rewrite_url) {
			// maybe call onafterconfigread here

			if (defined('DEBUG_MODE') && $this->Application->isDebugMode()) {
				$this->Application->Debugger->profileStart('url_parsing', 'Parsing <b>MOD_REWRITE</b> url');
				$this->processRewriteURL();
				$description = 'Parsing <b>MOD_REWRITE</b> url (template: <b>' . $this->Get('t') . '</b>)';
				$this->Application->Debugger->profileFinish('url_parsing', $description);
			}
			else {
				$this->processRewriteURL();
			}

			if ( !$rewrite_url && $this->rewriteRedirectRequired() ) {
				// rewrite url is missing (e.g. not a script from tools folder)
				$url_params = $this->getRedirectParams();

				// no idea about how to check, that given template require category to be passed with it, so pass anyway
				$url_params['pass_category'] = 1;
				$url_params['response_code'] = 301; // Moved Permanently

				$this->Application->Redirect('', $url_params);
			}
		}
		else {
			$this->Application->VerifyThemeId();
			$this->Application->VerifyLanguageId();
		}
	}

	/**
	 * Checks, that non-rewrite url was visited and it's automatic rewrite is required
	 *
	 * @return bool
	 */
	function rewriteRedirectRequired()
	{
		$redirect_conditions = Array (
			!$this->Application->Session->IsHTTPSRedirect(), // not https <-> http redirect
			!$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains)
			!defined('GW_NOTIFY'), // not in payment gateway notification script
			preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited
			$this->Get('t') != 'index', // not on index page
		);

		$perform_redirect = true;

		foreach ($redirect_conditions as $redirect_condition) {
			$perform_redirect = $perform_redirect && $redirect_condition;

			if (!$perform_redirect) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks, that referer is out site
	 *
	 * @return bool
	 */
	function refererIsOurSite()
	{
		if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) {
			// no referer -> don't care what happens
			return false;
		}

		$site_helper =& $this->Application->recallObject('SiteHelper');
		/* @var $site_helper SiteHelper */

		$found = false;
		$http_referer = $_SERVER['HTTP_REFERER'];
		preg_match('/^(.*?):\/\/(.*?)(\/|$)/', $http_referer, $regs); // 1 - protocol, 2 - domain

		if ($regs[1] == 'https') {
			$found = $site_helper->getDomainByName('SSLUrl', $http_referer) > 0;

			if (!$found) {
				// check if referer starts with our ssl url
				$ssl_url = $this->Application->ConfigValue('SSL_URL');
				$found = $ssl_url && preg_match('/^' . preg_quote($ssl_url, '/') . '/', $http_referer);
			}
		}
		else {
			$found = $site_helper->getDomainByName('DomainName', $regs[2]) > 0;

			if (!$found) {
				$found = $regs[2] == DOMAIN;
			}
		}

		return $found;
	}

	function convertFiles()
	{
		if (!$_FILES)
		{
			return false;
		}

		$file_keys = Array('error','name','size','tmp_name','type');

		$tmp = Array();
		foreach($_FILES as $file_name => $file_info)
		{
			if( is_array($file_info['error']) )
			{
				$tmp[$file_name] = $this->getArrayLevel( $file_info['error'], $file_name );
			}
			else
			{
				$normal_files[$file_name] = $file_info;
			}
		}

		if(!$tmp) return false;

		$files = $_FILES;
		$_FILES = Array();

		foreach($tmp as $prefix => $prefix_files)
		{
			$anchor =& $_FILES;
			foreach($prefix_files['keys'] as $key)
			{
				$anchor =& $anchor[$key];
			}
			foreach($prefix_files['value'] as $field_name)
			{
				unset($inner_anchor);
				unset($copy);
				$work_copy = $prefix_files['keys'];
				foreach($file_keys as $file_key)
				{
					$inner_anchor =& $files[$prefix][$file_key];
					if (isset($copy))
					{
						$work_copy = $copy;
					}
					else
					{
						$copy = $work_copy;
					}
					array_shift($work_copy);
					foreach($work_copy as $prefix_file_key)
					{
						$inner_anchor =& $inner_anchor[$prefix_file_key];
					}
					$anchor[$field_name][$file_key] = $inner_anchor[$field_name];
				}
			}
		}

		// keys: img_temp, 0, values: LocalPath, ThumbPath
	}

	function getArrayLevel(&$level, $prefix='')
	{
		$ret['keys'] = $prefix ? Array($prefix) : Array();
		$ret['value'] = Array();

		foreach($level as $level_key => $level_value)
		{
			if( is_array($level_value) )
			{
				$ret['keys'][] = $level_key;
				$tmp = $this->getArrayLevel($level_value);

				$ret['keys'] = array_merge($ret['keys'], $tmp['keys']);
				$ret['value'] = array_merge($ret['value'], $tmp['value']);
			}
			else
			{
				$ret['value'][] = $level_key;
			}
		}

		return $ret;
	}

	/**
	 * Owerwrites GET events with POST events in case if they are set and not empty
	 *
	 */
	function convertPostEvents()
	{
		$events = $this->Get('events');
		if (is_array($events)) {
			foreach ($events as $prefix_special => $event_name) {
				if ($event_name) {
					$this->Set($prefix_special.'_event', $event_name);
				}
			}
		}
	}

	function finalizeParsing($passed = Array(), $module_params = Array() )
	{
		if ($passed) {
			$event_manger =& $this->Application->recallObject('EventManager');
			foreach ($passed as $passed_prefix) {
				$event_manger->setQueryMap($passed_prefix);
			}
			$this->Set('passed', implode(',', array_keys($event_manger->queryMaps)));
		}

		// get joined version (env var + mod rewrite parsed)
		$passed = $this->Application->GetVar('passed');

		if (!array_key_exists('editing_mode', $module_params)) {
			$module_params['editing_mode'] = '';
		}

		$module_params['__URLENCODE__'] = 1;

		$env = $this->Application->BuildEnv( $this->Get('t'), $module_params, $passed, false, false);
		$this->Set(ENV_VAR_NAME, $env);
		$_REQUEST['env'] = $_GET['env'] = $env; // for capability with old in-portal code
	}

	function processRewriteURL()
	{
		$mod_rw_helper =& $this->Application->recallObject('ModRewriteHelper');
		$mod_rw_helper->processRewriteURL();
	}

	function getDefaultTemplate($t)
	{
		$t = $this->getTemplateName( trim($t, '/') );
		if (!$t) $t = 'index';
		return trim($t, '/');
	}

	function extractSIDAndTemplate(&$parts)
	{
		$vars = Array ();
		$sub_parts = array_shift($parts);

		list ($sid, $t) = explode('-', $sub_parts, 2);

		if ($sid) {
			// Save Session ID
			$this->Set('sid', $sid);
			$vars['sid'] = $sid;
		}

		// Save Template Name
		$vars['t'] = $this->getDefaultTemplate($t);

		return $vars;
	}

	/**
	 * Process QueryString only, create
	 * events, ids, based on config
	 * set template name and sid in
	 * desired application variables.
	 *
	 * @param string $env_var enviroment string value
	 *
	 * @access public
	 */
	function processQueryString($env_var, $pass_name = 'passed')
	{
		// env=SID:TEMPLATE:m-1-1-1-1:l0-0-0:n-0-0-0:bb-0-0-1-1-1-0
		$vars = Array ();
		if ($env_var) {
			$more_vars = strpos($env_var, '&');
			if ($more_vars !== false) {
				parse_str(substr($env_var, $more_vars + 1), $vars);
				$env_var = substr($env_var, 0, $more_vars);
			}

			// replace escaped ":" symbol not to explode by it
			$env_var = str_replace('\:','_&+$$+&_', $env_var);	// replace escaped "=" with spec-chars :)
			$parts = explode(':', $env_var);

			if (!$this->Application->RewriteURLs() || ($this->Application->RewriteURLs() && $this->Get('rewrite') != 'on')) {
				$vars = array_merge_recursive2($vars, $this->extractSIDAndTemplate($parts));
			}

			if ($parts) {
				$passed = Array ();
				foreach ($parts as $mixed_part) {
					list ($passed[], $processed_vars) = $this->_parseEnvPart($mixed_part);
					$vars = array_merge_recursive2($vars, $processed_vars);
				}

				$vars[$pass_name] = implode(',', array_unique($passed));
			}
		}
		else {
			$t = $this->getTemplateName('index');
			$vars['t'] = $t;
		}

		return $vars;
	}

	/**
	 * Converts enviroment part into variable array (based on query map for given prefix)
	 *
	 * @param string $mixed_part
	 * @return Array
	 */
	function _parseEnvPart($mixed_part)
	{
		// In-portal old style env conversion - adds '-' between prefix and first var
		$mixed_part = str_replace('_&+$$+&_', ':', $mixed_part);
		$mixed_part = preg_replace("/^([a-zA-Z]+)([0-9]+)-(.*)/", "$1-$2-$3", $mixed_part);

		// replace escaped "-" symbol not to explode by it
		$escaped_part = str_replace('\-', '_&+$$+&_', $mixed_part);
		$escaped_part = explode('-', $escaped_part);

		$mixed_part = Array();
		foreach ($escaped_part as $escaped_val) {
			$mixed_part[] = str_replace('_&+$$+&_', '-', $escaped_val);
		}

		$prefix_special = array_shift($mixed_part); // l.pick, l
		$query_map = $this->Application->EventManager->setQueryMap($prefix_special);

		$vars = Array ();

		// if config is not defined for prefix in QueryString, then don't process it
		if ($query_map) {
			foreach ($query_map as $index => $var_name) {
				// l_id, l_page, l_bla-bla-bla
				$val = $mixed_part[$index - 1];
				if ($val == '') $val = false;
				$vars[$prefix_special.'_'.$var_name] = $val;
			}
		}

		return Array ($prefix_special, $vars);
	}

	/**
	 * Removes tpl part from template name + resolved template ID to name
	 *
	 * @param string $t
	 * @return string
	 * @access private
	 */
	function getTemplateName($t)
	{
		if (array_key_exists('t', $this->Get) && $this->Get['t']) {
			// template name is passed directly in url (GET method)
			$t = $this->Get['t'];
		}

		// if t was set through env, even in mod_rewrite mode!
		if ($this->Get('env') && $this->Application->RewriteURLs() && $this->Get('t')) {
			$t = $this->Get('t');
		}

		return preg_replace('/\.tpl$/', '', $t);
	}

	/**
	 * Saves variables from array specified
	 * into common variable storage place
	 *
	 * @param Array $array
	 * @return Array
	 * @access private
	 */
	function AddVars($array)
	{
		$array = $this->StripSlashes($array);
		foreach($array as $key => $value)
		{
			$this->Set($key,$value);
		}
		return $array;
	}

	function MergeVars($array, $strip_slashes=true)
	{
		if ($strip_slashes) $array = $this->StripSlashes($array);
		foreach($array as $key => $value)
		{
			$this->_Params = array_merge_recursive2($this->_Params, Array($key=>$value));
		}
		return $array;
	}

	function StripSlashes($array)
	{
		static $magic_quotes = null;

		if (!isset($magic_quotes)) {
			$magic_quotes = get_magic_quotes_gpc();
		}

		foreach ($array as $key => $value) {
			if (is_array($value)) {
				$array[$key] = $this->StripSlashes($value);
			}
			else {
				if ($magic_quotes) {
					$value = stripslashes($value);
				}

				if (!$this->Application->isAdmin) {
					$value = htmlspecialchars($value);
				}

				$array[$key] = $value;
			}
		}

		return $array;
	}

	/**
	 * Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls
	 *
	 * @param bool $access_error Method is called during no_permission, require login, session expiration link preparation
	 * @return Array
	 */
	function getRedirectParams($access_error = false)
	{
		$vars = $this->Get;
		$unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action');

		if (!$this->_sidInQueryString) {
			$unset_vars[] = 'sid';
		}

		// remove system variables
		foreach ($unset_vars as $var_name) {
			if (array_key_exists($var_name, $vars)) {
				unset($vars[$var_name]);
			}
		}

		if ($access_error) {
			// place 1 of 2 (also in UsersEventHandler::OnSessionExpire)
			$vars = $this->_removePassThroughVariables($vars);
		}

		// transform arrays
		return $this->_transformArrays($vars);
	}

	/**
	 * Removes all pass_though variables from redirect params
	 *
	 * @param Array $url_params
	 * @return Array
	 */
	function _removePassThroughVariables($url_params)
	{
		$pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : '';
		if (!$pass_through) {
			return $url_params;
		}

		$pass_through = explode(',', $pass_through . ',pass_through');
		foreach ($pass_through as $pass_through_var) {
			unset($url_params[$pass_through_var]);
		}

		$url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again

		return $url_params;
	}

	function _transformArrays($array, $level_prefix = '')
	{
		$ret = Array ();
		foreach ($array as $var_name => $var_value) {
			$new_var_name = $level_prefix ? $level_prefix . '[' . $var_name . ']' : $var_name;

			if (is_array($var_value)) {
				$ret = array_merge($ret, $this->_transformArrays($var_value, $new_var_name));
			}
			else {
				$ret[$new_var_name] = $var_value;
			}
		}

		return $ret;
	}

	function writeRequestLog($filename)
	{
		$log_file = (defined('RESTRICTED') ? RESTRICTED : FULL_PATH) . '/' . $filename;

		if ( is_writable( dirname($log_file) ) ) {
			$fp = fopen($log_file, 'a');

			if ($fp) {
				$session =& $this->Application->recallObject('Session');
				$user_id = $session->GetField('PortalUserId');
				$admin_mark = $this->Application->isAdmin ? 'ADMIN' : 'FRONT';

				$data = '['.date('D M d H:i:s Y').'] '.$admin_mark.'; ip: '.$_SERVER['REMOTE_ADDR'].'; user_id: '.$user_id.'; sid: '.$this->Application->GetSID().'; request: '."\n";
				if ($this->Get) {
					$data .= "_GET:\n".print_r($this->Get, true);
				}

				if ($this->Post) {
					$data .= "_POST:\n".print_r($this->Post, true);
				}

				if ($this->Cookie) {
					$data .= "_COOKIE:\n".print_r($this->Cookie, true);
				}
				$data .= str_repeat('=', 100)."\n";

				fwrite($fp, $data);
				fclose($fp);
			}
			else {
				trigger_error('Requrest Log directory not writable', E_USER_WARNING);
			}
		}
		else {
			trigger_error('Requrest Log directory not writable', E_USER_WARNING);
		}
	}

}