<?php
/**
* @version	$Id: globals.php 16545 2017-07-02 13:50:43Z 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 kUtil {

//	const KG_TO_POUND = 2.20462262;

	const POUND_TO_KG = 0.45359237;

	/**
	 * Escape text as HTML.
	 *
	 * @see escape
	 */
	const ESCAPE_HTML = 'html';

	/**
	 * Escape text as JavaScript.
	 *
	 * @see escape
	 */
	const ESCAPE_JS = 'js';

	/**
	 * Escape text as Url.
	 */
	const ESCAPE_URL = 'url';

	/**
	 * Don't escape anything.
	 */
	const ESCAPE_RAW = 'raw';

	/**
	 * Current escape strategy.
	 *
	 * @var string
	 * @see escape
	 */
	public static $escapeStrategy = self::ESCAPE_HTML;

	/**
	 * Checks, that given array is associative.
	 *
	 * @param array $array Array.
	 *
	 * @return bool
	 * @access public
	 */
	public static function isAssoc($array)
	{
		return array_keys($array) !== range(0, count($array) - 1);
	}

	/**
	 * Similar to array_merge_recursive but keyed-valued are always overwritten.
	 * Priority goes to the 2nd array.
	 *
	 * @param mixed $array1 Array 1.
	 * @param mixed $array2 Array 2.
	 *
	 * @return array
	 */
	public static function array_merge_recursive($array1, $array2)
	{
		if ( !is_array($array1) || !is_array($array2) ) {
			return $array2;
		}

		foreach ( $array2 as $array2_key => $array2_value ) {
			if ( isset($array1[$array2_key]) ) {
				$array1[$array2_key] = self::array_merge_recursive($array1[$array2_key], $array2_value);
			}
			else {
				$array1[$array2_key] = $array2_value;
			}
		}

		return $array1;
	}

	/**
	 * Prepend a reference to an element to the beginning of an array.
	 * Renumbers numeric keys, so $value is always inserted to $array[0]
	 *
	 * @param $array array
	 * @param $value mixed
	 * @return int
	 * @access public
	 */
	public static function array_unshift_ref(&$array, &$value)
	{
		$return = array_unshift($array,'');
		$array[0] =& $value;
		return $return;
	}

	/**
	 * Rename key in associative array, maintaining keys order
	 *
	 * @param Array $array Associative Array
	 * @param mixed $old Old key name
	 * @param mixed $new New key name
	 * @access public
	 */
	public static function array_rename_key(&$array, $old, $new)
	{
		$new_array = Array ();

		foreach ($array as $key => $val) {
			$new_array[ $key == $old ? $new : $key] = $val;
		}

		$array = $new_array;
	}

	/**
	 * Same as print_r, but outputs result on screen or in debugger report (when in debug mode)
	 *
	 * @param Array $data
	 * @param string $label
	 * @param bool $on_screen
	 * @access public
	 */
	public static function print_r($data, $label = '', $on_screen = false)
	{
		$is_debug = false;

		if ( class_exists('kApplication') && !$on_screen ) {
			$application =& kApplication::Instance();
			$is_debug = $application->isDebugMode();
		}

		if ( $is_debug && isset($application) ) {
			if ( $label ) {
				$application->Debugger->appendHTML('<strong>' . $label . '</strong>');
			}

			$application->Debugger->dumpVars($data);
		}
		else {
			if ( $label ) {
				echo '<strong>' . $label . '</strong><br/>';
			}

			echo '<pre>', print_r($data, true), '</pre>';
		}
	}

	/**
	 * Define constant if it was not already defined before
	 *
	 * @param string $const_name
	 * @param string $const_value
	 * @access public
	 */
	public static function safeDefine($const_name, $const_value)
	{
		if ( !defined($const_name) ) {
			define($const_name, $const_value);
		}
	}

	/**
	 * Instantiate kSystemConfig class once and store locally
	 *
	 * @access public
	 */
	public static function getSystemConfig()
	{
		static $system_config;

		if ( !isset($system_config) ) {
			$system_config = new kSystemConfig();
		}

		return $system_config;
	}

	/**
	 * Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set
	 *
	 * @param string $file
	 * @access public
	 */
	public static function includeOnce($file)
	{
		global $debugger;

		if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) {

			if ( in_array($file, get_included_files()) ) {
				return ;
			}

			global $debugger;

			/*$debugger->IncludeLevel++;
			$before_mem = memory_get_usage();*/

			$debugger->ProfileStart('inc_'.crc32($file), $file);
			include_once($file);
			$debugger->ProfileFinish('inc_'.crc32($file));
			$debugger->profilerAddTotal('includes', 'inc_'.crc32($file));

			/*$used_mem = memory_get_usage() - $before_mem;
			$debugger->IncludeLevel--;
			$debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file);
			$debugger->IncludesData['mem'][] = $used_mem;
			$debugger->IncludesData['time'][] = $used_time;
			$debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/
		}
		else {
			include_once($file);
		}
	}

	/**
	 * Checks if given string is a serialized array
	 *
	 * @param string $string
	 * @return bool
	 * @access public
	 */
	public static function IsSerialized($string)
	{
		if ( is_array($string) ) {
			return false;
		}

		return preg_match('/a:([\d]+):{/', $string);
	}

	/**
	 * Generates password of given length
	 *
	 * @param int $length
	 * @return string
	 * @access public
	 */
	public static function generatePassword($length = 10)
	{
		$pass_length = $length;

		$p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z');
		$p2 = Array ('a','e','i','o','u');
		$p3 = Array ('1','2','3','4','5','6','7','8','9');
		$p4 = Array ('(','&',')',';','%');    // if you need real strong stuff

		// how much elements in the array
		// can be done with a array count but counting once here is faster

		$s1 = 21;// this is the count of $p1
		$s2 = 5; // this is the count of $p2
		$s3 = 9; // this is the count of $p3
		$s4 = 5; // this is the count of $p4

		// possible readable combinations

		$c1 = '121';    // will be like 'bab'
		$c2 = '212';      // will be like 'aba'
		$c3 = '12';      // will be like 'ab'
		$c4 = '3';        // will be just a number '1 to 9'  if you dont like number delete the 3
		//$c5 = '4';        // uncomment to active the strong stuff

		$comb = '4'; // the amount of combinations you made above (and did not comment out)

		for ($p = 0; $p < $pass_length;) {
			mt_srand((double)microtime() * 1000000);
			$strpart = mt_rand(1, $comb);

			// checking if the stringpart is not the same as the previous one
			if ($strpart != $previous) {
				$pass_structure .= ${'c' . $strpart};

				// shortcutting the loop a bit
				$p = $p + mb_strlen(${'c' . $strpart});
			}
			$previous = $strpart;
		}

		// generating the password from the structure defined in $pass_structure
		for ($g = 0; $g < mb_strlen($pass_structure); $g++) {
			mt_srand((double)microtime() * 1000000);
			$sel = mb_substr($pass_structure, $g, 1);
			$pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ];
		}

		return $pass;
	}

	/**
	 * submits $url with $post as POST
	 *
	 * @param string $url
	 * @param mixed $data
	 * @param Array $headers
	 * @param string $request_type
	 * @param Array $curl_options
	 * @return string
	 * @access public
	 * @deprecated
	 */
	public static function curl_post($url, $data, $headers = NULL, $request_type = 'POST', $curl_options = NULL)
	{
		$application =& kApplication::Instance();

		/** @var kCurlHelper $curl_helper */
		$curl_helper = $application->recallObject('CurlHelper');

		if ($request_type == 'POST') {
			$curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST);
		}

		$curl_helper->SetRequestData($data);

		if (!is_null($headers)) {
			// not an associative array, so don't use kCurlHelper::SetHeaders method
			$curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) );
		}

		if (is_array($curl_options)) {
			$curl_helper->setOptions($curl_options);
		}

		$curl_helper->followLocation = false;
		$ret = $curl_helper->Send($url);

		$GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode;
		$GLOBALS['curl_error'] = $curl_helper->lastErrorMsg;

		return $ret;
	}

	/**
	 * Checks if constant is defined and has positive value
	 *
	 * @param string $const_name
	 * @return bool
	 * @access public
	 */
	public static function constOn($const_name)
	{
		return defined($const_name) && constant($const_name);
	}

	/**
	 * Converts KG to Pounds
	 *
	 * @param float $kg
	 * @param bool $pounds_only
	 * @return float
	 * @access public
	 */
	public static function Kg2Pounds($kg, $pounds_only = false)
	{
		$major = floor( round($kg / self::POUND_TO_KG, 3) );
		$minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2));

		if ($pounds_only) {
			$major += round($minor * 0.0625, 2);
			$minor = 0;
		}
		return array($major, $minor);
	}

	/**
	 * Converts Pounds to KG
	 *
	 * @param float $pounds
	 * @param float $ounces
	 * @return float
	 * @access public
	 */
	public static function Pounds2Kg($pounds, $ounces = 0.00)
	{
		return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5);
	}

	/**
	 * Formats file/memory size in nice way
	 *
	 * @param int $bytes
	 * @return string
	 * @access public
	 */
	public static function formatSize($bytes)
	{
		if ($bytes >= 1099511627776) {
			$return = round($bytes / 1024 / 1024 / 1024 / 1024, 2);
			$suffix = "TB";
		} elseif ($bytes >= 1073741824) {
			$return = round($bytes / 1024 / 1024 / 1024, 2);
			$suffix = "GB";
		} elseif ($bytes >= 1048576) {
			$return = round($bytes / 1024 / 1024, 2);
			$suffix = "MB";
		} elseif ($bytes >= 1024) {
			$return = round($bytes / 1024, 2);
			$suffix = "KB";
		} else {
			$return = $bytes;
			$suffix = "Byte";
		}

		$return .= ' '.$suffix;

		return $return;
	}

	/**
	 * Enter description here...
	 *
	 * @param resource $filePointer the file resource to write to
	 * @param Array $data the data to write out
	 * @param string $delimiter the field separator
	 * @param string $enclosure symbol to enclose field data to
	 * @param string $recordSeparator symbols to separate records with
	 * @access public
	 */
	public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
	{
		fwrite($filePointer, self::getcsvline($data, $delimiter, $enclosure, $recordSeparator));
	}

	/**
	 * Enter description here...
	 *
	 * @param Array $data the data to write out
	 * @param string $delimiter the field separator
	 * @param string $enclosure symbol to enclose field data to
	 * @param string $recordSeparator symbols to separate records with
	 * @return string
	 * @access public
	 */
	public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n")
	{
		ob_start();
		$fp = fopen('php://output', 'w');
		fputcsv($fp, $data, $delimiter, $enclosure);
		fclose($fp);
		$ret = ob_get_clean();

		if ( $recordSeparator != "\n" ) {
			return substr($ret, 0, -1) . $recordSeparator;
		}

		return $ret;
	}

	/**
	 * Allows to replace #section# within any string with current section
	 *
	 * @param string $string
	 * @return string
	 * @access public
	 */
	public static function replaceModuleSection($string)
	{
		$application =& kApplication::Instance();
		$module_section = $application->RecallVar('section');

		if ($module_section) {
			// substitute section instead of #section# parameter in title preset name
			$module_section = explode(':', $module_section);
			$section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]);
			$string = str_replace('#section#', mb_strtolower($section), $string);
		}

		return $string;
	}

	/**
	 * Checks, that user IP address is within allowed range
	 *
	 * @param string $ip_list semi-column (by default) separated ip address list
	 * @param string $separator ip address separator (default ";")
	 *
	 * @return bool
	 * @access public
	 */
	public static function ipMatch($ip_list, $separator = ';')
	{
		if ( php_sapi_name() == 'cli' ) {
			return false;
		}

		$ip_match = false;
		$ip_addresses = $ip_list ? explode($separator, $ip_list) : Array ();

		$application =& kApplication::Instance();
		$client_ip = $application->getClientIp();

		foreach ($ip_addresses as $ip_address) {
			if ( self::netMatch($ip_address, $client_ip) ) {
				$ip_match = true;
				break;
			}
		}

		return $ip_match;
	}

	/**
	 * Checks, that given ip belongs to given subnet
	 *
	 * @param string $network
	 * @param string $ip
	 * @return bool
	 * @access public
	 */
	public static function netMatch($network, $ip)
	{
		$network = trim($network);
		$ip = trim($ip);

		if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) {
			$network = gethostbyname($network);
		}

		if ($network == $ip) {
			// comparing two ip addresses directly
			return true;
		}

		$d = strpos($network, '-');
		if ($d !== false) {
			// ip address range specified
			$from = ip2long(trim(substr($network, 0, $d)));
			$to = ip2long(trim(substr($network, $d + 1)));

			$ip = ip2long($ip);
			return ($ip >= $from && $ip <= $to);
		}
		elseif (strpos($network, '/') !== false) {
			// single subnet specified
			$ip_arr = explode('/', $network);

			if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) {
				$ip_arr[0] .= '.0';    // Alternate form 194.1.4/24
			}

			$network_long = ip2long($ip_arr[0]);
			$x = ip2long($ip_arr[1]);

			$mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1]));
			$ip_long = ip2long($ip);

			return ($ip_long & $mask) == ($network_long & $mask);
		}

		return false;
	}

	/**
	 * Returns mime type corresponding to given file
	 * @param string $file
	 * @return string
	 * @access public
	 */
	public static function mimeContentType($file)
	{
		$ret = self::vendorMimeContentType($file);

		if ( $ret ) {
			// vendor-specific mime types override any automatic detection
			return $ret;
		}

		if ( function_exists('finfo_open') && function_exists('finfo_file') ) {
			$mime_magic_resource = finfo_open(FILEINFO_MIME_TYPE);

			if ( $mime_magic_resource ) {
				$ret = finfo_file($mime_magic_resource, $file);
				finfo_close($mime_magic_resource);
			}
		}
		elseif ( function_exists('mime_content_type') ) {
			$ret = mime_content_type($file);
		}

	    return $ret ? $ret : self::mimeContentTypeByExtension($file);
 	}

	/**
	 * Determines vendor-specific mime type from a given file
	 *
	 * @param string $file
	 * @return bool
	 * @access public
	 * @static
	 */
	public static function vendorMimeContentType($file)
	{
		$file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION));

		$mapping = Array (
			'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
			'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
			'docm' => 'application/vnd.ms-word.document.macroEnabled.12',
			'dotm' => 'application/vnd.ms-word.template.macroEnabled.12',
			'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
			'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
			'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12',
			'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12',
			'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12',
			'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12',
			'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
			'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template',
			'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
			'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12',
			'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12',
			'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12',
			'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12'
		);

		return isset($mapping[$file_extension]) ? $mapping[$file_extension] : false;
	}

	/**
	 * Detects mime type of the file purely based on it's extension
	 *
	 * @param string $file
	 * @return string
	 * @access public
	 */
	public static function mimeContentTypeByExtension($file)
	{
		$file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION));

		$mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf)
					(pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf)
					(tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript)
					(ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip)
					(mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap)
					(gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff)
					(wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml)
					(mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822)
					(sxw:application/vnd.sun.xml.writer)(sxc:application/vnd.sun.xml.calc)(sxi:application/vnd.sun.xml.impress)
					(sxd:application/vnd.sun.xml.draw)(sxm:application/vnd.sun.xml.math)
					(odt:application/vnd.oasis.opendocument.text)(oth:application/vnd.oasis.opendocument.text-web)
					(odm:application/vnd.oasis.opendocument.text-master)(odg:application/vnd.oasis.opendocument.graphics)
					(odp:application/vnd.oasis.opendocument.presentation)(ods:application/vnd.oasis.opendocument.spreadsheet)
					(odc:application/vnd.oasis.opendocument.chart)(odf:application/vnd.oasis.opendocument.formula)
					(odi:application/vnd.oasis.opendocument.image)';

		if ( preg_match('/[\(,]' . $file_extension . '[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs) ) {
			return $regs[1];
		}

		return 'application/octet-stream';
	}

	/**
	 * Strips ".tmp" suffix (added by flash uploader) from a filename
	 *
	 * @param string $file
	 * @return string
	 * @access public
	 * @static
	 */
	public static function removeTempExtension($file)
	{
		return preg_replace('/(_[\d]+)?\.tmp$/', '', $file);
	}

	/**
	 * Return param value and removes it from params array
	 *
	 * @param string $name
	 * @param Array $params
	 * @param bool $default
	 * @return string
	 */
	public static function popParam($name, &$params, $default = false)
	{
		if ( isset($params[$name]) ) {
			$value = $params[$name];
			unset($params[$name]);

			return $value;
		}

		return $default;
	}

	/**
	 * Generate subpath from hashed value
	 *
	 * @param string $name
	 * @param int $levels
	 * @return string
	 */
	public static function getHashPathForLevel($name, $levels = 2)
	{
		if ( $levels == 0 ) {
			return '';
		}
		else {
			$path = '';
			$hash = md5($name);

			for ($i = 0; $i < $levels; $i++) {
				$path .= substr($hash, $i, 1) . '/';
			}

			return $path;
		}
	}

	/**
	 * Calculates the crc32 polynomial of a string (always positive number)
	 *
	 * @param string $str
	 * @return int
	 */
	public static function crc32($str)
	{
		return sprintf('%u', crc32($str));
	}

	/**
	 * Returns instance of DateTime class with date set based on timestamp
	 *
	 * @static
	 * @param int $timestamp
	 * @return DateTime
	 * @access public
	 */
	public static function dateFromTimestamp($timestamp)
	{
		if ( version_compare(PHP_VERSION, '5.3.0', '<') ) {
			$date = new DateTime('@' . $timestamp);
			$date->setTimezone(new DateTimeZone(date_default_timezone_get()));
		}
		else {
			$date = new DateTime();
			$date->setTimestamp($timestamp);
		}

		return $date;
	}

	/**
	 * Returns timestamp from given DateTime class instance
	 *
	 * @static
	 * @param DateTime $date_time
	 * @return int|string
	 * @access public
	 */
	public static function timestampFromDate(DateTime $date_time)
	{
		if ( version_compare(PHP_VERSION, '5.3.0', '<') ) {
			return $date_time->format('U');
		}

		return $date_time->getTimestamp();
	}

	/**
	 * Generates random numeric id
	 *
	 * @static
	 * @return string
	 * @access public
	 */
	public static function generateId()
	{
		list($usec, $sec) = explode(' ', microtime());

		$id_part_1 = substr($usec, 4, 4);
		$id_part_2 = mt_rand(1, 9);
		$id_part_3 = substr($sec, 6, 4);
		$digit_one = substr($id_part_1, 0, 1);

		if ( $digit_one == 0 ) {
			$digit_one = mt_rand(1, 9);
			$id_part_1 = preg_replace('/^0/', '', $id_part_1);
			$id_part_1 = $digit_one . $id_part_1;
		}

		return $id_part_1 . $id_part_2 . $id_part_3;
	}

	/**
	 * Changes script resource limits. Omitted argument results in limit removal.
	 *
	 * @static
	 * @param string|int $memory_limit
	 * @param int $time_limit
	 * @return void
	 * @access public
	 */
	public static function setResourceLimit($memory_limit = null, $time_limit = null)
	{
		set_time_limit(isset($time_limit) ? $time_limit : 0);
		ini_set('memory_limit', isset($memory_limit) ? $memory_limit : -1);
	}

	/**
	 * Escapes a string.
	 *
	 * @param string $text     Text to escape.
	 * @param string $strategy Escape strategy.
	 *
	 * @return string
	 * @throws InvalidArgumentException When unknown escape strategy is given.
	 */
	public static function escape($text, $strategy = null)
	{
		if ( !isset($strategy) ) {
			$strategy = self::$escapeStrategy;
		}

		if ( strpos($strategy, '+') !== false ) {
			$previous_strategy = '';
			$strategies = explode('+', $strategy);

			foreach ($strategies as $current_strategy) {
				// apply default strategy
				if ( $current_strategy == '' ) {
					$current_strategy = self::$escapeStrategy;
				}

				// don't double-escape
				if ( $current_strategy != $previous_strategy ) {
					$text = self::escape($text, $current_strategy);
					$previous_strategy = $current_strategy;
				}
			}

			return $text;
		}

		if ( $strategy == self::ESCAPE_HTML ) {
			return htmlspecialchars($text, ENT_QUOTES, CHARSET);
		}

		if ( $strategy == self::ESCAPE_JS ) {
			// TODO: consider using "addcslashes", because "addslashes" isn't really for JavaScript escaping (according to docs)
			$text = addslashes($text);
			$text = str_replace(array("\r", "\n"), array('\r', '\n'), $text);
			$text = str_replace('</script>', "</'+'script>", $text);

			return $text;
		}

		if ( $strategy == self::ESCAPE_URL ) {
			return rawurlencode($text);
		}

		if ( $strategy == self::ESCAPE_RAW ) {
			return $text;
		}

		throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy));
	}

	/**
	 * Unescapes a string.
	 *
	 * @param string $text     Text to unescape.
	 * @param string $strategy Escape strategy.
	 *
	 * @return string
	 * @throws InvalidArgumentException When unknown escape strategy is given.
	 */
	public static function unescape($text, $strategy = null)
	{
		if ( !isset($strategy) ) {
			$strategy = self::$escapeStrategy;
		}

		if ( strpos($strategy, '+') !== false ) {
			$previous_strategy = '';
			$strategies = explode('+', $strategy);

			foreach ($strategies as $current_strategy) {
				// apply default strategy
				if ( $current_strategy == '' ) {
					$current_strategy = self::$escapeStrategy;
				}

				// don't double-unescape
				if ( $current_strategy != $previous_strategy ) {
					$text = self::unescape($text, $current_strategy);
					$previous_strategy = $current_strategy;
				}
			}

			return $text;
		}

		if ( $strategy == self::ESCAPE_HTML ) {
			return htmlspecialchars_decode($text, ENT_QUOTES);
		}

		if ( $strategy == self::ESCAPE_JS ) {
			// TODO: consider using "stripcslashes", because "stripslashes" isn't really for JavaScript unescaping (according to docs)
			$text = str_replace("</'+'script>", '</script>', $text);
			$text = str_replace(array('\r', '\n'), array("\r", "\n"), $text);
			$text = stripslashes($text);

			return $text;
		}

		if ( $strategy == self::ESCAPE_URL ) {
			return rawurldecode($text);
		}

		if ( $strategy == self::ESCAPE_RAW ) {
			return $text;
		}

		throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy));
	}

	/**
	 * Mark a method as deprecated and inform when it has been used.
	 *
	 * The current behavior is to trigger a user deprecation notice in Debug Mode.
	 * This method is to be used in every method that is deprecated.
	 *
	 * @param string      $method      The method that was called.
	 * @param string      $version     The version that deprecated the method.
	 * @param string|null $replacement The method that should have been called.
	 *
	 * @return void
	 */
	public static function deprecatedMethod($method, $version, $replacement = null)
	{
		$application =& kApplication::Instance();

		if ( !$application->isDebugMode() ) {
			return;
		}

		$msg = '%1$s is <strong>deprecated</strong> since version %2$s';

		if ( !is_null($replacement) ) {
			@trigger_error(sprintf($msg . '! Use %3$s instead.', $method, $version, $replacement), E_USER_DEPRECATED);
		}
		else {
			@trigger_error(sprintf($msg . ' with no alternative available.', $method, $version), E_USER_DEPRECATED);
		}
	}

	/**
	 * Mark a method argument as deprecated and inform when it has been used.
	 *
	 * This method is to be used whenever a deprecated method argument is used.
	 * Before this method is called, the argument must be checked for whether it was
	 * used by comparing it to its default value or evaluating whether it is empty.
	 * For example:
	 *
	 *     if ( !$deprecated ) {
	 *         kUtil::deprecatedArgument(__METHOD__, '5.2.2');
	 *     }
	 *
	 * The current behavior is to trigger a user deprecation notice in Debug Mode.
	 *
	 * @param string      $method  The method that was called.
	 * @param string      $version The version that deprecated the argument used.
	 * @param string|null $message A message regarding the change.
	 *
	 * @return void
	 */
	public static function deprecatedArgument($method, $version, $message = null)
	{
		$application =& kApplication::Instance();

		if ( !$application->isDebugMode() ) {
			return;
		}

		$msg = '%1$s was called with an argument that is <strong>deprecated</strong> since version %2$s';

		if ( !is_null($message) ) {
			@trigger_error(sprintf($msg . '! %3$s', $method, $version, $message), E_USER_DEPRECATED);
		}
		else {
			@trigger_error(sprintf($msg . ' with no alternative available.', $method, $version), E_USER_DEPRECATED);
		}
	}

}

/**
 * Returns array value if key exists
 * Accepts infinite number of parameters
 *
 * @param Array $array searchable array
 * @param int $key array key
 * @return string
 */
function getArrayValue(&$array, $key)
{
	$ret = isset($array[$key]) ? $array[$key] : false;

	if ( $ret && func_num_args() > 2 ) {
		for ($i = 2; $i < func_num_args(); $i++) {
			$cur_key = func_get_arg($i);
			$ret = getArrayValue($ret, $cur_key);

			if ( $ret === false ) {
				break;
			}
		}
	}

	return $ret;
}

if ( !function_exists('parse_ini_string') ) {
	/**
	 * Equivalent for "parse_ini_string" function available since PHP 5.3.0
	 *
	 * @param string $ini
	 * @param bool $process_sections
	 * @param int $scanner_mode
	 * @return Array
	 */
	function parse_ini_string($ini, $process_sections = false, $scanner_mode = NULL)
	{
		# Generate a temporary file.
		$tempname = tempnam('/tmp', 'ini');
		$fp = fopen($tempname, 'w');
		fwrite($fp, $ini);
		$ini = parse_ini_file($tempname, !empty($process_sections));
		fclose($fp);
		@unlink($tempname);

		return $ini;
	}
}

if ( !function_exists('memory_get_usage') ) {
	// PHP 4.x and compiled without --enable-memory-limit option
	function memory_get_usage() { return -1; }
}

if ( !function_exists('imagecreatefrombmp') ) {
	// just in case if GD will add this function in future
	function imagecreatefrombmp($filename)
	{
		//Ouverture du fichier en mode binaire
		if (! $f1 = fopen($filename,"rb")) return FALSE;

		//1 : Chargement des ent�tes FICHIER
		$FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14));
		if ($FILE['file_type'] != 19778) return FALSE;

		//2 : Chargement des ent�tes BMP
		$BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'.
		'/Vcompression/Vsize_bitmap/Vhoriz_resolution'.
		'/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40));
		$BMP['colors'] = pow(2,$BMP['bits_per_pixel']);
		if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset'];
		$BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8;
		$BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']);
		$BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4);
		$BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4);
		$BMP['decal'] = 4-(4*$BMP['decal']);
		if ($BMP['decal'] == 4) $BMP['decal'] = 0;

		//3 : Chargement des couleurs de la palette
		$PALETTE = array();
		if ($BMP['colors'] < 16777216)
		{
			$PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4));
		}

		//4 : Cr�ation de l'image
		$IMG = fread($f1,$BMP['size_bitmap']);
		$VIDE = chr(0);

		$res = imagecreatetruecolor($BMP['width'],$BMP['height']);
		$P = 0;
		$Y = $BMP['height']-1;
		while ($Y >= 0)
		{
			$X=0;
			while ($X < $BMP['width'])
			{
				if ($BMP['bits_per_pixel'] == 24)
				$COLOR = unpack("V",substr($IMG,$P,3).$VIDE);
				elseif ($BMP['bits_per_pixel'] == 16)
				{
					$COLOR = unpack("n",substr($IMG,$P,2));
					$COLOR[1] = $PALETTE[$COLOR[1]+1];
				}
				elseif ($BMP['bits_per_pixel'] == 8)
				{
					$COLOR = unpack("n",$VIDE.substr($IMG,$P,1));
					$COLOR[1] = $PALETTE[$COLOR[1]+1];
				}
				elseif ($BMP['bits_per_pixel'] == 4)
				{
					$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
					if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F);
					$COLOR[1] = $PALETTE[$COLOR[1]+1];
				}
				elseif ($BMP['bits_per_pixel'] == 1)
				{
					$COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1));
					if     (($P*8)%8 == 0) $COLOR[1] =  $COLOR[1]        >>7;
					elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6;
					elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5;
					elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4;
					elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3;
					elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2;
					elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1;
					elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1);
					$COLOR[1] = $PALETTE[$COLOR[1]+1];
				}
				else
				return FALSE;
				imagesetpixel($res,$X,$Y,$COLOR[1]);
				$X++;
				$P += $BMP['bytes_per_pixel'];
			}
			$Y--;
			$P+=$BMP['decal'];
		}

		//Fermeture du fichier
		fclose($f1);

		return $res;
	}
}