<?php

	/*
	  Miscellaneous functions.
	 */

	class debug_log {

		public $log_lines = array();

		public function add($msg)
		{
			$output = '';

			$trace = debug_backtrace();
			foreach ($trace as $value)
			{
				$output .= $value['function'] . "():" . $value['line'] . " ";
			}

			array_push($this->log_lines, array("$msg", $output));
		}

		public function import_from($extern_log)
		{
			foreach ($extern_log->log_lines as $extern_log_line)
			{
				array_push($this->log_lines, $extern_log_line);
			}
		}

	}

// decode 4 byte array into int32 value
	function data_int32($data)
	{
		return (int) (
			(int) ord($data[3]) << 24 |
			(int) ord($data[2]) << 16 |
			(int) ord($data[1]) << 8 |
			(int) ord($data[0])) & 0xffffffff;
	}

// validate host through regular epxression
	function is_valid_host($host)
	{
		global $global_allow_all_ftp;
		if ($global_allow_all_ftp)
			return true;

		if (preg_match('/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/', $host))
			return true;
		else
			return false;
	}

// validate IP
	function is_valid_ip($ip)
	{
		global $global_allow_all_ftp;
		if ($global_allow_all_ftp)
			return true;

		if (preg_match('/^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/', $ip))
			return true;
		else
			return false;
	}

	function validEmail($email)
	{
		$isValid = true;
		$atIndex = strrpos($email, "@");
		if (is_bool($atIndex) && !$atIndex)
		{
			$isValid = false;
		}
		else
		{
			$domain = substr($email, $atIndex + 1);
			$local = substr($email, 0, $atIndex);
			$localLen = strlen($local);
			$domainLen = strlen($domain);
			if ($localLen < 1 || $localLen > 64)
			{
				// local part length exceeded
				$isValid = false;
			}
			else if ($domainLen < 1 || $domainLen > 255)
			{
				// domain part length exceeded
				$isValid = false;
			}
			else if ($local[0] == '.' || $local[$localLen - 1] == '.')
			{
				// local part starts or ends with '.'
				$isValid = false;
			}
			else if (preg_match('/\\.\\./', $local))
			{
				// local part has two consecutive dots
				$isValid = false;
			}
			else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain))
			{
				// character not valid in domain part
				$isValid = false;
			}
			else if (preg_match('/\\.\\./', $domain))
			{
				// domain part has two consecutive dots
				$isValid = false;
			}
			else if
			(!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/', str_replace("\\\\", "", $local))
			)
			{
				// character not valid in local part unless
				// local part is quoted
				if (!preg_match('/^"(\\\\"|[^"])+"$/', str_replace("\\\\", "", $local))
				)
				{
					$isValid = false;
				}
			}
		}
		return $isValid;
	}

// validate email
	function is_valid_email($email)
	{
		global $global_allow_all_ftp;
		if ($global_allow_all_ftp)
			return true;

		return validEmail($email);
	}

// validate IP and apply filter, to remove unneeded IPs
// 10.0.0.0 -> 10.255.255.255
// 172.16.0.0 -> 172.31.255.255
	function is_valid_ip_filter($ip)
	{
		global $global_allow_all_ftp;
		if ($global_allow_all_ftp)
			return true;

		if (!is_valid_ip($ip))
			return false;

		$ip_values = preg_split("/[.]/", $ip);

		for ($i = 0; $i < 4; $i++)
			$ip_values[$i] = intval($ip_values[$i]);

		if ($ip_values[0] == 10)
			return false;

		if ($ip_values[0] == 172 && $ip_values[1] >= 16 && $ip_values[1] <= 31)
			return false;

		return true;
	}

	function parse_lines($value)
	{
		return preg_split("/((\r(?!\n))|((?<!\r)\n)|(\r\n))/", $value);
	}

// parse ini file
	function parse_ini($ini)
	{
		// split lines
		$ini = parse_lines($ini);

		if (count($ini) == 0)
			return array();

		$result = array();
		$sections = array();
		$values = array();
		$globals = array();
		$i = 0;
		foreach ($ini as $line)
		{
			$line = trim($line);

			// Comments, do not parse line
			if ($line == '' || $line{0} == ';')
				continue;

			// Sections
			if ($line{0} == '[')
			{
				$sections[] = substr($line, 1, -1);
				$i++;
				continue;
			}

			// Key-value pair
			if (strpos($line, '=') === false)
			{
				$key = $line;
				$value = '';
			} else
				list($key, $value) = explode('=', $line, 2);
			$key = strtolower(trim($key));
			$value = trim($value);
			if ($i == 0)
			{
				// Array values
				if (substr($line, -1, 2) == '[]')
				{
					$globals[$key][] = $value;
				}
				else
				{
					$globals[$key] = $value;
				}
			}
			else
			{
				// Array values
				if (substr($line, -1, 2) == '[]')
				{
					$values[$i - 1][$key][] = $value;
				}
				else
				{
					$values[$i - 1][$key] = $value;
				}
			}
		}
		foreach ($values as $key => $value)
			$result[$sections[$key]] = $value;
		return $result + $globals;
	}

	function collect_ini_array($ini_array)
	{
		$result = '';
		foreach ($ini_array as $key => $value)
		{
			$result .= $key . '=' . $value . "\r\n";
		}
		return trim($result);
	}

	function nonempty(&$var)
	{
		return (isset($var) && $var);
	}

// crc32b function (0xEDB88320 polynomial)
	function crc32b_str($data)
	{
		$CRC32Table = array(
		    (int) 0x00000000, (int) 0x77073096, (int) 0xEE0E612C, (int) 0x990951BA, (int) 0x076DC419, (int) 0x706AF48F, (int) 0xE963A535,
		    (int) 0x9E6495A3, (int) 0x0EDB8832, (int) 0x79DCB8A4, (int) 0xE0D5E91E, (int) 0x97D2D988, (int) 0x09B64C2B, (int) 0x7EB17CBD,
		    (int) 0xE7B82D07, (int) 0x90BF1D91, (int) 0x1DB71064, (int) 0x6AB020F2, (int) 0xF3B97148, (int) 0x84BE41DE, (int) 0x1ADAD47D,
		    (int) 0x6DDDE4EB, (int) 0xF4D4B551, (int) 0x83D385C7, (int) 0x136C9856, (int) 0x646BA8C0, (int) 0xFD62F97A, (int) 0x8A65C9EC,
		    (int) 0x14015C4F, (int) 0x63066CD9, (int) 0xFA0F3D63, (int) 0x8D080DF5, (int) 0x3B6E20C8, (int) 0x4C69105E, (int) 0xD56041E4,
		    (int) 0xA2677172, (int) 0x3C03E4D1, (int) 0x4B04D447, (int) 0xD20D85FD, (int) 0xA50AB56B, (int) 0x35B5A8FA, (int) 0x42B2986C,
		    (int) 0xDBBBC9D6, (int) 0xACBCF940, (int) 0x32D86CE3, (int) 0x45DF5C75, (int) 0xDCD60DCF, (int) 0xABD13D59, (int) 0x26D930AC,
		    (int) 0x51DE003A, (int) 0xC8D75180, (int) 0xBFD06116, (int) 0x21B4F4B5, (int) 0x56B3C423, (int) 0xCFBA9599, (int) 0xB8BDA50F,
		    (int) 0x2802B89E, (int) 0x5F058808, (int) 0xC60CD9B2, (int) 0xB10BE924, (int) 0x2F6F7C87, (int) 0x58684C11, (int) 0xC1611DAB,
		    (int) 0xB6662D3D, (int) 0x76DC4190, (int) 0x01DB7106, (int) 0x98D220BC, (int) 0xEFD5102A, (int) 0x71B18589, (int) 0x06B6B51F,
		    (int) 0x9FBFE4A5, (int) 0xE8B8D433, (int) 0x7807C9A2, (int) 0x0F00F934, (int) 0x9609A88E, (int) 0xE10E9818, (int) 0x7F6A0DBB,
		    (int) 0x086D3D2D, (int) 0x91646C97, (int) 0xE6635C01, (int) 0x6B6B51F4, (int) 0x1C6C6162, (int) 0x856530D8, (int) 0xF262004E,
		    (int) 0x6C0695ED, (int) 0x1B01A57B, (int) 0x8208F4C1, (int) 0xF50FC457, (int) 0x65B0D9C6, (int) 0x12B7E950, (int) 0x8BBEB8EA,
		    (int) 0xFCB9887C, (int) 0x62DD1DDF, (int) 0x15DA2D49, (int) 0x8CD37CF3, (int) 0xFBD44C65, (int) 0x4DB26158, (int) 0x3AB551CE,
		    (int) 0xA3BC0074, (int) 0xD4BB30E2, (int) 0x4ADFA541, (int) 0x3DD895D7, (int) 0xA4D1C46D, (int) 0xD3D6F4FB, (int) 0x4369E96A,
		    (int) 0x346ED9FC, (int) 0xAD678846, (int) 0xDA60B8D0, (int) 0x44042D73, (int) 0x33031DE5, (int) 0xAA0A4C5F, (int) 0xDD0D7CC9,
		    (int) 0x5005713C, (int) 0x270241AA, (int) 0xBE0B1010, (int) 0xC90C2086, (int) 0x5768B525, (int) 0x206F85B3, (int) 0xB966D409,
		    (int) 0xCE61E49F, (int) 0x5EDEF90E, (int) 0x29D9C998, (int) 0xB0D09822, (int) 0xC7D7A8B4, (int) 0x59B33D17, (int) 0x2EB40D81,
		    (int) 0xB7BD5C3B, (int) 0xC0BA6CAD, (int) 0xEDB88320, (int) 0x9ABFB3B6, (int) 0x03B6E20C, (int) 0x74B1D29A, (int) 0xEAD54739,
		    (int) 0x9DD277AF, (int) 0x04DB2615, (int) 0x73DC1683, (int) 0xE3630B12, (int) 0x94643B84, (int) 0x0D6D6A3E, (int) 0x7A6A5AA8,
		    (int) 0xE40ECF0B, (int) 0x9309FF9D, (int) 0x0A00AE27, (int) 0x7D079EB1, (int) 0xF00F9344, (int) 0x8708A3D2, (int) 0x1E01F268,
		    (int) 0x6906C2FE, (int) 0xF762575D, (int) 0x806567CB, (int) 0x196C3671, (int) 0x6E6B06E7, (int) 0xFED41B76, (int) 0x89D32BE0,
		    (int) 0x10DA7A5A, (int) 0x67DD4ACC, (int) 0xF9B9DF6F, (int) 0x8EBEEFF9, (int) 0x17B7BE43, (int) 0x60B08ED5, (int) 0xD6D6A3E8,
		    (int) 0xA1D1937E, (int) 0x38D8C2C4, (int) 0x4FDFF252, (int) 0xD1BB67F1, (int) 0xA6BC5767, (int) 0x3FB506DD, (int) 0x48B2364B,
		    (int) 0xD80D2BDA, (int) 0xAF0A1B4C, (int) 0x36034AF6, (int) 0x41047A60, (int) 0xDF60EFC3, (int) 0xA867DF55, (int) 0x316E8EEF,
		    (int) 0x4669BE79, (int) 0xCB61B38C, (int) 0xBC66831A, (int) 0x256FD2A0, (int) 0x5268E236, (int) 0xCC0C7795, (int) 0xBB0B4703,
		    (int) 0x220216B9, (int) 0x5505262F, (int) 0xC5BA3BBE, (int) 0xB2BD0B28, (int) 0x2BB45A92, (int) 0x5CB36A04, (int) 0xC2D7FFA7,
		    (int) 0xB5D0CF31, (int) 0x2CD99E8B, (int) 0x5BDEAE1D, (int) 0x9B64C2B0, (int) 0xEC63F226, (int) 0x756AA39C, (int) 0x026D930A,
		    (int) 0x9C0906A9, (int) 0xEB0E363F, (int) 0x72076785, (int) 0x05005713, (int) 0x95BF4A82, (int) 0xE2B87A14, (int) 0x7BB12BAE,
		    (int) 0x0CB61B38, (int) 0x92D28E9B, (int) 0xE5D5BE0D, (int) 0x7CDCEFB7, (int) 0x0BDBDF21, (int) 0x86D3D2D4, (int) 0xF1D4E242,
		    (int) 0x68DDB3F8, (int) 0x1FDA836E, (int) 0x81BE16CD, (int) 0xF6B9265B, (int) 0x6FB077E1, (int) 0x18B74777, (int) 0x88085AE6,
		    (int) 0xFF0F6A70, (int) 0x66063BCA, (int) 0x11010B5C, (int) 0x8F659EFF, (int) 0xF862AE69, (int) 0x616BFFD3, (int) 0x166CCF45,
		    (int) 0xA00AE278, (int) 0xD70DD2EE, (int) 0x4E048354, (int) 0x3903B3C2, (int) 0xA7672661, (int) 0xD06016F7, (int) 0x4969474D,
		    (int) 0x3E6E77DB, (int) 0xAED16A4A, (int) 0xD9D65ADC, (int) 0x40DF0B66, (int) 0x37D83BF0, (int) 0xA9BCAE53, (int) 0xDEBB9EC5,
		    (int) 0x47B2CF7F, (int) 0x30B5FFE9, (int) 0xBDBDF21C, (int) 0xCABAC28A, (int) 0x53B39330, (int) 0x24B4A3A6, (int) 0xBAD03605,
		    (int) 0xCDD70693, (int) 0x54DE5729, (int) 0x23D967BF, (int) 0xB3667A2E, (int) 0xC4614AB8, (int) 0x5D681B02, (int) 0x2A6F2B94,
		    (int) 0xB40BBE37, (int) 0xC30C8EA1, (int) 0x5A05DF1B, (int) 0x2D02EF8D);

		$remainder = 0xffffffff;
		$len = strlen($data);
		for ($i = 0; $i < $len; $i++)
		{
			$index = (ord($data[$i]) ^ $remainder) & 0xff;
			$crc = $CRC32Table[$index];
			$remainder = (($remainder >> 8) & 0xffffff) ^ $crc;
		}

		return $remainder;
	}

// decode hex-encoded string
	function hextostr($x)
	{
		// validate hex string
		if (!preg_match("/^([0-9A-Fa-f]{2})+$/", $x))
			return false;
		$s = '';
		foreach (explode("\n", trim(chunk_split($x, 2))) as $h)
			$s .= chr(hexdec($h));
		return($s);
	}

// decode hex-encoded string
	function int3tostr($x)
	{
		// validate hex string
		if (!preg_match("/^([0-9]{3})+$/", $x))
			return false;
		$s = '';
		foreach (explode("\n", trim(chunk_split($x, 3))) as $h)
			$s .= chr(intval($h));
		return($s);
	}

// fast integer divide
	function int_divide($x, $y)
	{
		if ($x == 0)
			return 0;
		if ($y == 0)
			return FALSE;
		return ($x - ($x % $y)) / $y;
	}

// shift left 
	function gmp_shiftl($x, $n)
	{
		return(gmp_mul($x, gmp_pow(2, $n)));
	}

// shift right 
	function gmp_shiftr($x, $n)
	{
		return(gmp_div($x, gmp_pow(2, $n)));
	}

	function myrand($max, &$seed)
	{
		$seed = hexdec(gmp_strval(gmp_and(gmp_add(gmp_mul("$seed", "0x8088405"), "1"), "0xffffffff"), 16));
		$v = hexdec(gmp_strval(gmp_and(gmp_shiftr(gmp_mul("0x" . dechex($seed), "$max"), "32"), "0xffffffff"), 16));
		return $v;
	}

	function _BF_SHR32($x, $bits)
	{
		if ($bits == 0)
			return $x;
		if ($bits == 32)
			return 0;
		$y = ($x & 0x7FFFFFFF) >> $bits;
		if (0x80000000 & $x)
		{
			$y |= (1 << (31 - $bits));
		}
		return $y;
	}

	function _BF_SHL32($x, $bits)
	{
		if ($bits == 0)
			return $x;
		if ($bits == 32)
			return 0;
		$mask = (1 << (32 - $bits)) - 1;
		return (($x & $mask) << $bits) & 0xFFFFFFFF;
	}

	function _BF_GETBYTE($x, $y)
	{
		return _BF_SHR32($x, 8 * $y) & 0xFF;
	}

	function _BF_OR32($x, $y)
	{
		return ($x | $y) & 0xFFFFFFFF;
	}

	function _BF_ADD32($x, $y)
	{

		$x = $x & 0xFFFFFFFF;
		$y = $y & 0xFFFFFFFF;

		$total = 0;
		$carry = 0;
		for ($i = 0; $i < 4; $i++)
		{
			$byte_x = _BF_GETBYTE($x, $i);
			$byte_y = _BF_GETBYTE($y, $i);
			$sum = $byte_x + $byte_y;

			$result = $sum & 0xFF;
			$carryforward = _BF_SHR32($sum, 8);

			$sum = $result + $carry;
			$result = $sum & 0xFF;
			$carry = $carryforward + _BF_SHR32($sum, 8);

			$total = _BF_OR32(_BF_SHL32($result, $i * 8), $total);
		}

		return $total;
	}

	function _BF_SUB32($x, $y)
	{
		return _BF_ADD32($x, -$y);
	}

	function _BF_XOR32($x, $y)
	{
		$x = $x & 0xFFFFFFFF;
		$y = $y & 0xFFFFFFFF;
		return $x ^ $y;
	}

	function strip_quotes($s)
	{
		if (!$s)
			return "";
		if ($s[0] == '"')
		{
			return substr($s, 1, -1);
		} else
			return $s;
	}

	function is_num($s)
	{
		return (preg_match("/^([0-9])+$/", $s));
	}

	function append_dir($host, &$dir)
	{
		$host = trim($host);

		// empty dir or zero-length host
		if (!nonempty($dir) || !strlen($host))
			return $host;

		$dir = trim($dir);

		$s = strip_quotes($dir);
		if ($s && $s[0] != '/' && $s[0] != '\\')
			$s = "/" . $s;

		return $host . $s;
	}

	function append_port($host, &$port)
	{
		$host = trim($host);

		// empty port or zero-length host
		if (!nonempty($port) || !strlen($host))
			return $host;

		$port = trim(strval($port));
		$s = strip_quotes($port);
		if (strlen($s) && is_num($s))
			$s = ":" . $s;
		else
			$s = "";

		return $host . $s;
	}

	function append_port_dir($host, &$port, &$dir)
	{
		return append_dir(append_port($host, $port), $dir);
	}

	function assign(&$value)
	{
		if (nonempty($value))
			return $value;
		else
			return '';
	}

	function assign_trim(&$value)
	{
		return trim(assign($value));
	}

// restore obfuscated crc32 value
	function obf_crc32($v)
	{
		return (int) (~((($v & 0x000000ff) << 24) |
			(($v & 0x0000ff00) << 8) |
			((($v & 0x00ff0000) >> 8) & 0xff00) | // it's a sar operation, not shr
			((($v & 0xff000000) >> 24) & 0xff))) & 0xffffffff;  // it's a sar operation, not shr
	}

// aplib unpack code, ported from C exsample
	class APDEPACKDATA {

		public $source;
		public $src_offset = 0;
		public $destination;
		public $dst_offset = 0;
		public $tag;
		public $bitcount;

	}

	function aP_getbit(&$ud)
	{
		if (!$ud->bitcount--)
		{
			$ud->tag = ord($ud->source[$ud->src_offset++]);
			$ud->bitcount = 7;
		}

		$bit = ($ud->tag >> 7) & 0x01;
		$ud->tag = $ud->tag << 1;

		return $bit;
	}

	function aP_getgamma(&$ud)
	{
		$result = 1;
		do
		{
			$result = ($result << 1) + aP_getbit($ud);
		} while (aP_getbit($ud));
		return $result;
	}

	function aP_depack($source, &$dest)
	{
		$ud = new APDEPACKDATA();

		$R0 = 0;
		$LWM = 0;
		$done = 0;

		$ud->source = $source;
		$ud->destination = &$dest;
		$ud->bitcount = 0;

		$ud->destination .= $ud->source[$ud->src_offset++];
		$ud->dst_offset++;

		while (!$done)
		{
			if (aP_getbit($ud))
			{
				if (aP_getbit($ud))
				{
					if (aP_getbit($ud))
					{
						$offs = 0;
						for ($i = 4; $i; $i--)
							$offs = ($offs << 1) + aP_getbit($ud);

						if ($offs)
						{
							$ud->destination .= $ud->destination[$ud->dst_offset - $offs];
							$ud->dst_offset++;
						}
						else
						{
							$ud->destination[$ud->dst_offset++] = chr(0);
						}
						$LWM = 0;
					}
					else
					{
						$offs = ord($ud->source[$ud->src_offset++]);

						$len = 2 + ($offs & 0x0001);

						$offs = $offs >> 1;

						if ($offs)
						{
							while ($len--)
							{
								$ud->destination .= $ud->destination[$ud->dst_offset - $offs];
								$ud->dst_offset++;
							}
						} else
							$done = 1;

						$R0 = $offs;
						$LWM = 1;
					}
				} else
				{
					$offs = aP_getgamma($ud);

					if (($LWM == 0) && ($offs == 2))
					{
						$offs = $R0;

						$len = aP_getgamma($ud);

						while ($len--)
						{
							$ud->destination .= $ud->destination[$ud->dst_offset - $offs];
							$ud->dst_offset++;
						}
					}
					else
					{
						if ($LWM == 0)
							$offs -= 3; else
							$offs -= 2;

						$offs = $offs << 8;
						$offs += ord($ud->source[$ud->src_offset++]);

						$len = aP_getgamma($ud);

						if ($offs >= 32000)
							$len++;
						if ($offs >= 1280)
							$len++;
						if ($offs < 128)
							$len += 2;

						while ($len--)
						{
							$ud->destination .= $ud->destination[$ud->dst_offset - $offs];
							$ud->dst_offset++;
						}
						$R0 = $offs;
					}
					$LWM = 1;
				}
			}
			else
			{
				$ud->destination .= $ud->source[$ud->src_offset++];
				$ud->dst_offset++;
				$LWM = 0;
			}
		}

		return $ud->dst_offset;
	}

// trim password value at zero char
	function ztrim($data)
	{
		for ($i = 0; $i < strlen($data); $i++)
			if ($data[$i] == chr(0))
			{
				$data = substr($data, 0, $i);
				break;
			}
		return $data;
	}

	function ztrim_unicode($data)
	{
		for ($i = 0; $i < strlen($data) - 1; $i += 2)
			if ($data[$i] == chr(0) && $data[$i + 1] == chr(0))
			{
				$data = substr($data, 0, $i);
				break;
			}
		return $data;
	}

	class XMLParser {

		var $rawXML;
		var $valueArray = array();
		var $keyArray = array();
		var $parsed = array();
		var $index = 0;
		var $attribKey = 'attributes';
		var $valueKey = 'value';
		var $cdataKey = 'cdata';
		var $isError = false;
		var $error = '';

		function XMLParser($xml = NULL)
		{
			$this->rawXML = $xml;
		}

		function parse($xml = NULL)
		{
			if (!is_null($xml))
			{
				$this->rawXML = $xml;
			}

			$this->isError = false;

			if (!$this->parse_init())
			{
				return false;
			}

			$this->index = 0;
			$this->parsed = $this->parse_recurse();
			$this->status = 'parsing complete';

			return $this->parsed;
		}

		function parse_recurse()
		{
			$found = array();
			$tagCount = array();

			while (isset($this->valueArray[$this->index]))
			{
				$tag = $this->valueArray[$this->index];
				$this->index++;

				if ($tag['type'] == 'close')
				{
					return $found;
				}

				if ($tag['type'] == 'cdata')
				{
					$tag['tag'] = $this->cdataKey;
					$tag['type'] = 'complete';
				}

				$tagName = $tag['tag'];

				if (isset($tagCount[$tagName]))
				{
					if ($tagCount[$tagName] == 1)
					{
						$found[$tagName] = array($found[$tagName]);
					}

					$tagRef = & $found[$tagName][$tagCount[$tagName]];
					$tagCount[$tagName]++;
				}
				else
				{
					$tagCount[$tagName] = 1;
					$tagRef = & $found[$tagName];
				}

				switch ($tag['type'])
				{
					case 'open':
						$tagRef = $this->parse_recurse();

						if (isset($tag['attributes']))
						{
							$tagRef[$this->attribKey] = $tag['attributes'];
						}

						if (isset($tag['value']))
						{
							if (isset($tagRef[$this->cdataKey]))
							{
								$tagRef[$this->cdataKey] = (array) $tagRef[$this->cdataKey];
								array_unshift($tagRef[$this->cdataKey], $tag['value']);
							}
							else
							{
								$tagRef[$this->cdataKey] = $tag['value'];
							}
						}
						break;

					case 'complete':
						if (isset($tag['attributes']))
						{
							$tagRef[$this->attribKey] = $tag['attributes'];
							$tagRef = & $tagRef[$this->valueKey];
						}

						if (isset($tag['value']))
						{
							$tagRef = $tag['value'];
						}
						break;
				}
			}

			return $found;
		}

		function parse_init()
		{
			$this->parser = xml_parser_create();

			$parser = $this->parser;
			xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
			xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
			xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "utf-8");

			if (!$res = (bool) xml_parse_into_struct($parser, $this->rawXML, $this->valueArray, $this->keyArray))
			{
				$this->isError = true;
				$this->error = 'error: ' . xml_error_string(xml_get_error_code($parser)) . ' at line ' . xml_get_current_line_number($parser);
			}
			xml_parser_free($parser);

			return $res;
		}

	}

	function Utf8ToWin($fcontents)
	{
		$out = $c1 = '';
		if (!is_string($fcontents))
			return '';
		$byte2 = false;
		for ($c = 0; $c < strlen($fcontents); $c++)
		{
			$i = ord($fcontents[$c]);
			if ($i <= 127)
			{
				$out .= $fcontents[$c];
			}
			if ($byte2)
			{
				$new_c2 = ($c1 & 3) * 64 + ($i & 63);
				$new_c1 = ($c1 >> 2) & 5;
				$new_i = $new_c1 * 256 + $new_c2;
				if ($new_i == 1025)
				{
					$out_i = 168;
				}
				else
				{
					if ($new_i == 1105)
					{
						$out_i = 184;
					}
					else
					{
						$out_i = $new_i - 848;
					}
				}
				$out .= chr($out_i);
				$byte2 = false;
			}
			if (($i >> 5) == 6)
			{
				$c1 = $i;
				$byte2 = true;
			}
		}
		return $out;
	}

	function unicode_char_to_utf8($c)
	{
		$output = "";

		if ($c < 0x80)
		{
			return chr($c);
		}
		else if ($c < 0x800)
		{
			return chr(0xc0 | ($c >> 6)) . chr(0x80 | ($c & 0x3f));
		}
		else if ($c < 0x10000)
		{
			return chr(0xe0 | ($c >> 12)) . chr(0x80 | (($c >> 6) & 0x3f)) . chr(0x80 | ($c & 0x3f));
		}
		else if ($c < 0x200000)
		{
			return chr(0xf0 | ($c >> 18)) . chr(0x80 | (($c >> 12) & 0x3f)) . chr(0x80 | (($c >> 6) & 0x3f)) . chr(0x80 | ($c & 0x3f));
		}

		return false;
	}

	function unicode_to_utf8($string)
	{
		$log = new debug_log();
		$stream = new stream($string, $log);
		$r = '';
		while ($stream->state && $stream->pos < $stream->datalen)
		{
			$r .= unicode_char_to_utf8($stream->read_word());
		}
		if (!$stream->state)
			return false;
		return $r;
	}

// maske sure input string is unicode
	function unicode_to_ansi($string)
	{
		if (!strlen($string))
			return '';

		// check for unicode length validness
		if (strlen($string) % 2 != 0)
		{
			return '';
		}
		else
			return Utf8ToWin(unicode_to_utf8($string));

		// alternative
		//return mb_convert_encoding($string, "cp1251", "UTF-16LE");
	}

// Returns true if $string is valid UTF-8 and false otherwise.
	function is_utf8($string)
	{
		// From http://w3.org/International/questions/qa-forms-utf-8.html
		return preg_match('%^(?:
          [\x09\x0A\x0D\x20-\x7E]            # ASCII
        | [\xC2-\xDF][\x80-\xBF]             # non-overlong 2-byte
        |  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
        |  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
        |  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
        | [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
        |  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
    )*$%xs', $string);
	}

// Unicode BOM is U+FEFF, but after encoded, it will look like this.
	define('UTF32_BIG_ENDIAN_BOM', chr(0x00) . chr(0x00) . chr(0xFE) . chr(0xFF));
	define('UTF32_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE) . chr(0x00) . chr(0x00));
	define('UTF16_BIG_ENDIAN_BOM', chr(0xFE) . chr(0xFF));
	define('UTF16_LITTLE_ENDIAN_BOM', chr(0xFF) . chr(0xFE));
	define('UTF8_BOM', chr(0xEF) . chr(0xBB) . chr(0xBF));

	function detect_utf_encoding($text)
	{
		$first2 = substr($text, 0, 2);
		$first3 = substr($text, 0, 3);
		$first4 = substr($text, 0, 4);

		if ($first3 == UTF8_BOM)
			return 'UTF-8';
		elseif ($first4 == UTF32_BIG_ENDIAN_BOM)
			return 'UTF-32BE';
		elseif ($first4 == UTF32_LITTLE_ENDIAN_BOM)
			return 'UTF-32LE';
		elseif ($first2 == UTF16_BIG_ENDIAN_BOM)
			return 'UTF-16BE';
		elseif ($first2 == UTF16_LITTLE_ENDIAN_BOM)
			return 'UTF-16LE';

		return "auto";
	}

// Convert unknown-encoded text into CP1251
// correct UTF encoding is extracted from the data header, see detect_utf_encoding() function
	function UnkTextToAnsi($string)
	{
		$encoding = detect_utf_encoding($string);

		// remove utf header character
		switch ($encoding)
		{
			case "UTF-8": $string = substr($string, 3);
				break;
			case "UTF-32BE": $string = substr($string, 4);
				break;
			case "UTF-32LE": $string = substr($string, 4);
				break;
			case "UTF-16BE": $string = substr($string, 2);
				break;
			case "UTF-16LE": $string = substr($string, 2);
				break;
			default:
				// cannot detect text encoding, utf header character missing
				return $string;
		}

		return mb_convert_encoding($string, "cp1251", $encoding);
	}

	function CutXMLEncoding($string)
	{
		do
		{
			$old_string = $string;
			$encoding = detect_utf_encoding($string);

			// remove utf header character
			switch ($encoding)
			{
				case "UTF-8": $string = substr($string, 3);
					break;
				case "UTF-32BE": $string = substr($string, 4);
					break;
				case "UTF-32LE": $string = substr($string, 4);
					break;
				case "UTF-16BE": $string = substr($string, 2);
					break;
				case "UTF-16LE": $string = substr($string, 2);
					break;
			}
		} while ($old_string != $string);

		return $old_string;
	}

	function xor_block($b1, $b2)
	{
		for ($i = 0; $i < min(strlen($b1), strlen($b2)); $i++)
			$b1[$i] = chr(ord($b1[$i]) ^ ord($b2[$i]));
		return $b1;
	}

// Implementation of (CTS) Ciphertext stealing mode (encryption) for mcrypt library
// 64-bit blocks only
// Padding is not implemented here, as it wasn't required for encryption process
	function encrypt_cts($module, $data, &$iv)
	{
		$outdata = "";
		for ($i = 0; $i < int_divide(strlen($data), 8); $i++)
		{
			$p = substr($data, $i * 8, 8);
			$p = xor_block($p, $iv);
			$p = mcrypt_generic($module, $p);

			$outdata .= $p;

			$iv = xor_block($iv, $p);
		}

		return $outdata;
	}

// Implementation of (CTS) Ciphertext stealing mode (decryption) for mcrypt library
// 64-bit blocks only
	function decrypt_cts($module, $data, &$iv)
	{
		$outdata = "";

		$s = substr($data, 0, 8);
		$d = $s;
		$f = $iv;

		$b = "";

		for ($i = 0; $i < int_divide(strlen($data), 8); $i++)
		{
			$b = $d;

			$b = xor_block($b, $f);
			$d = mdecrypt_generic($module, $d);
			$d = xor_block($d, $f);

			$outdata .= $d;

			// exchange b <-> f
			$s = $b;
			$b = $f;
			$f = $s;

			$d = substr($data, ($i + 1) * 8, 8);
		}

		$iv = $f;

		if (strlen($data) % 8 != 0)
		{
			$fbuffer = mcrypt_generic($module, $iv);
			$d = xor_block(substr($data, -(strlen($data) % 8)), $fbuffer);
			$outdata .= $d;
			$iv = xor_block($iv, $fbuffer);
		}

		$outdata = substr($outdata, 0, strlen($data));
		return $outdata;
	}

	function decrypt_cfbblock($module, $data, &$iv)
	{
		$outdata = "";

		$p1 = substr($data, 0, 8);
		$p2 = '';

		for ($i = 0; $i < int_divide(strlen($data), 8); $i++)
		{
			$temp = $p1;
			$iv = mcrypt_generic($module, $iv);

			$p2 = $p1;
			$p2 = xor_block($p2, $iv);

			$iv = $temp;

			$outdata .= $p2;

			$p1 = substr($data, ($i + 1) * 8, 8);
		}

		if (strlen($data) % 8 != 0)
		{
			$iv = mcrypt_generic($module, $iv);
			$outdata .= xor_block(substr($data, -(strlen($data) % 8)), $iv);
		}

		$outdata = substr($outdata, 0, strlen($data));
		return $outdata;
	}

	/**
	 * Encrypt given plain text using the key with RC4 algorithm.
	 * All parameters and return value are in binary format.
	 *
	 * @param string key - secret key for encryption
	 * @param string pt - plain text to be encrypted
	 * @return string
	 */
	function rc4Encrypt($key, $pt)
	{
		if (!strlen($key))
			return $pt;
		$s = array();
		for ($i = 0; $i < 256; $i++)
			$s[$i] = $i;
		$j = 0;
		$x;
		for ($i = 0; $i < 256; $i++)
		{
			$j = ($j + $s[$i] + ord($key[$i % strlen($key)])) % 256;
			$x = $s[$i];
			$s[$i] = $s[$j];
			$s[$j] = $x;
		}
		$i = 0;
		$j = 0;
		$ct = '';
		$y;
		for ($y = 0; $y < strlen($pt); $y++)
		{
			$i = ($i + 1) % 256;
			$j = ($j + $s[$i]) % 256;
			$x = $s[$i];
			$s[$i] = $s[$j];
			$s[$j] = $x;
			$ct .= $pt[$y] ^ chr($s[($s[$i] + $s[$j]) % 256]);
		}
		return $ct;
	}

	/**
	 * Decrypt given cipher text using the key with RC4 algorithm.
	 * All parameters and return value are in binary format.
	 *
	 * @param string key - secret key for decryption
	 * @param string ct - cipher text to be decrypted
	 * @return string
	 */
	function rc4Decrypt($key, $ct)
	{
		return rc4Encrypt($key, $ct);
	}

	function fwrite_stream($fp, $string)
	{
		for ($written = 0; $written < strlen($string); $written += $fwrite)
		{
			$fwrite = fwrite($fp, substr($string, $written));
			if ($fwrite === false)
				return $written;
		}
		return $written;
	}

	function print_ok_err($msg, $state, $print_result, $print_fails_only)
	{
		if ($state)
		{
			if ($print_result && !$print_fails_only)
				print($msg . " - <font color=\"#25b725\"><b>OK!</b></font><br />");
		}
		else
		{
			if ($print_result)
				print($msg . " - <font color=\"#b72525\"><b>FAIL!</b></font><br />");
		}
		return $state;
	}

	if (!defined('PHP_VERSION_ID'))
	{
		$version = explode('.', PHP_VERSION);
		define('PHP_VERSION_ID', ($version[0] * 10000 + $version[1] * 100 + $version[2]));
	}

	if (PHP_VERSION_ID < 50207)
	{
		define('PHP_MAJOR_VERSION', $version[0]);
		define('PHP_MINOR_VERSION', $version[1]);
		define('PHP_RELEASE_VERSION', $version[2]);
	}

	function install_check($print_result, $print_fails_only = false)
	{
		$result = true;

		$result = print_ok_err('php version 5.2+ installed', PHP_VERSION_ID >= 50200, $print_result, $print_fails_only) && $result;
		//$result = print_ok_err('32-bit php installed', PHP_INT_SIZE == 4, $print_result, $print_fails_only) && $result;

		$sqlite3_available = false;
		if (class_exists('SQLite3'))
		{
			$sqlite3_available = true;
		}
		elseif (class_exists('PDO'))
		{
			$drivers = PDO::getAvailableDrivers();
			$sqlite3_available = array_search('sqlite', $drivers) !== false;
		}

		print_ok_err('SQLite3 (class or PDO driver) exists (non-critical)', $sqlite3_available, $print_result, $print_fails_only);

		// check if we can write data into temp dir
		// required to process sqlite3 databases
		$temp_check = false;
		global $global_temporary_directory, $global_directory_slash;
		if ($global_temporary_directory && $global_directory_slash && is_writable($global_temporary_directory))
		{
			$temp_filename = tempnam($global_temporary_directory, 'sqlite');
			$fp = fopen($temp_filename, "w");
			if ($fp)
			{
				if (fwrite($fp, "111") == 3)
					$temp_check = true;
				fclose($fp);
				if (file_exists($temp_filename))
					unlink($temp_filename);
			}
		}

		$result = print_ok_err('can write data into temporary directory', $temp_check, $print_result, $print_fails_only) && $result;

		// check for required extensions
		if (PHP_VERSION_ID >= 50300)
		{
			$required_extensions = array("zlib", "libxml", "mysql", "hash", "mcrypt", "gmp", "iconv", "mbstring", "gd", "pcre", "curl", "json", "zip");
		}
		else
		{
			$required_extensions = array("zlib", "libxml", "mysql", "mhash", "mcrypt", "gmp", "iconv", "mbstring", "gd", "pcre", "curl", "json", "zip");
		}
		$loaded_extensions = get_loaded_extensions();
		$missing_extensions = array_diff($required_extensions, $loaded_extensions);

		foreach ($required_extensions as $key => $value)
			print_ok_err('"' . $value . '" extension installed', true, $print_result, $print_fails_only);
		foreach ($missing_extensions as $key => $value)
			$result = print_ok_err('"' . $value . '" extension installed', false, $print_result, $print_fails_only) && $result;

		return $result;
	}

	if (function_exists('geoip_country_code_by_name'))
	{
		require_once("includes/geoip/geoip2.inc");
	}
	else
	{
		require_once("includes/geoip/geoip.inc");
	}

	function geo_ip_country_code($ip)
	{
		$ip = trim($ip);
		$result = '';

		if (strlen($ip))
		{
			if (function_exists('geoip_country_code_by_name'))
			{
				$result = @geoip_country_code_by_name($ip);
			}
			else
			{
				if (function_exists('geoip_open') && file_exists("includes/geoip/GeoIP.dat"))
				{
					$gi = geoip_open("includes/geoip/GeoIP.dat", GEOIP_STANDARD);

					if ($gi)
					{
						$result = geoip_country_code_by_addr($gi, $ip);
						geoip_close($gi);
					}
				}
			}
		}
		return $result;
	}

	function geo_ip_country_name($ip)
	{
		$ip = trim($ip);
		$result = '';

		if (strlen($ip))
		{
			if (function_exists('geoip_country_code_by_name'))
			{
				$result = @geoip_country_name_by_name($ip);
			}
			else
			{
				if (function_exists('geoip_open') && file_exists("includes/geoip/GeoIP.dat"))
				{
					$gi = geoip_open("includes/geoip/GeoIP.dat", GEOIP_STANDARD);

					if ($gi)
					{
						$result = geoip_country_name_by_addr($gi, $ip);
						geoip_close($gi);
					}
				}
			}
		}
		return $result;
	}

	function curl_ping($url)
	{
		clearstatcache();

		$url = trim($url);

		if (substr($url, 0, 4) != "http")
			$url = "http://" . $url;

		$userAgent = 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; .NET CLR 1.1.4322)';

		$ch = curl_init($url);

		if (!$ch)
			return false;

		curl_setopt($ch, CURLOPT_HEADER, true);
		curl_setopt($ch, CURLOPT_NOBODY, true);
		curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
		curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
		curl_setopt($ch, CURLOPT_TIMEOUT, 15);
		curl_setopt($ch, CURLOPT_FAILONERROR, true);
		curl_setopt($ch, CURLOPT_FRESH_CONNECT, true);
		curl_setopt($ch, CURLOPT_DNS_USE_GLOBAL_CACHE, false);

		curl_exec($ch);

		// Check if any error occurred
		if (!curl_errno($ch))
		{
			$return = true;
		}
		else
		{
			$return = false;
		}
		curl_close($ch);

		return $return;
	}

	function sql_safe($s)
	{
		if (get_magic_quotes_gpc())
			$s = stripslashes($s);

		return mysql_real_escape_string($s);
	}

	function mixed_sha1($value)
	{
		return sha1('random_salt_value_start' . $value . 'random_salt_value_end');
	}

	function formatBytes($b, $p = null)
	{
		if ($b == 0)
			return '0 bytes';
		$units = array("Bytes", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB");
		$c = 0;
		if (!$p && $p !== 0)
		{
			foreach ($units as $k => $u)
			{
				if (($b / pow(1024, $k)) >= 1)
				{
					$r["bytes"] = $b / pow(1024, $k);
					$r["units"] = $u;
					$c++;
				}
			}
			return number_format($r["bytes"], 2) . " " . $r["units"];
		}
		else
		{
			return number_format($b / pow(1024, $p)) . " " . $units[$p];
		}
	}

	function mysql_now_date()
	{
		return date('Y-m-d H:i:s', time());
	}

	function table_if_nil_write_nbsp($value)
	{
		if (strlen($value) != 0)
			return $value;
		else
			return '&nbsp;';
	}

	function my_flush()
	{
		@ob_flush();
		@flush();
		@ob_end_flush();
		@ob_start();
	}

	function smart_implode($glue, $pieces)
	{
		$result = '';

		$pieces = array_filter($pieces);

		if (count($pieces) == 1)
			return array_pop($pieces);

		$first_elem = 1;
		foreach ($pieces as $piece)
		{
			if (!$first_elem)
				$result .= $glue;
			$result .= $piece;
			$first_elem = 0;
		}

		return $result;
	}

	function win2uni($winstr)
	{
		$uniline = '';
		$isoline = convert_cyr_string($winstr, "w", "i");

		for ($i = 0; $i < strlen($isoline); $i++)
		{
			$thischar = substr($isoline, $i, 1);
			$charcode = ord($thischar);
			$uniline .= ($charcode > 175) ? "&#" . (1040 + ($charcode - 176)) . ";" : $thischar;
		}
		return $uniline;
	}

	function show_smarty_error($smarty, $code = '', $back_link = null)
	{
		$smarty->assign('err_code', $code);
		if ($back_link !== null)
			$smarty->assign('back_link', $back_link);
		$smarty->display('error.tpl');
		$smarty->clearAssign('err_code');
		$smarty->clearAssign('back_link');
	}

	function show_smarty_success($smarty, $code = '')
	{
		$smarty->assign('success_code', $code);
		$smarty->display('success.tpl');
		$smarty->clearAssign('success_code');
	}

	function show_smarty_confirm($smarty, $code)
	{
		$smarty->assign('confirm_code', $code);
		$smarty->display('confirm.tpl');
		$smarty->clearAssign('confirm_code');
	}

	function str_insert($insertstring, $intostring, $offset)
	{
		$part1 = substr($intostring, 0, $offset);
		$part2 = substr($intostring, $offset);

		$part1 = $part1 . $insertstring;
		$whole = $part1 . $part2;
		return $whole;
	}

	function write_file_data($file_name, $bin_data)
	{
		$fp = fopen($file_name, "wb");
		fwrite($fp, $bin_data);
		fclose($fp);
	}

	function json_fmt_nice($json)
	{
		$json = str_replace(array("\n", "\r"), "", $json);
		$json = preg_replace('/([{,])(\s*)([^"]+?)\s*:/', '$1"$3":', $json);
		return $json;
	}

	function str_begins($string, $start_substr)
	{
		return (strtolower(substr($string, 0, strlen($start_substr))) === $start_substr);
	}

	function str_ends($string, $start_substr)
	{
		return (strtolower(substr($string, -strlen($start_substr))) === $start_substr);
	}

	function trim_ftp_dir($line)
	{
		$pos = strpos($line, '://');
		if ($pos !== false)
			$pos += 3;
		$pos = strpos($line, ':', $pos);
		if ($pos !== false)
		{
			$pos = strpos($line, '@', $pos);
			if ($pos !== false)
			{
				$pos = strpos($line, '/', $pos);
				if ($pos !== false)
				{
					$line = substr($line, 0, $pos);
				}
			}
		}
		return $line;
	}

	function ftp_force_ssh($ftp, $is_ssh = true)
	{
		if (!$is_ssh)
			return $ftp;

		$ftp = trim($ftp);

		if (!strlen($ftp))
			return '';

		if ($is_ssh)
		{
			$p = strpos($ftp, '://');
			if ($p === false)
			{
				return "sftp://" . $ftp;
			}
			else
			{
				if (str_begins($ftp, 'ftp://'))
					$ftp = 'sftp://' . substr($ftp, strlen('ftp://'));
				return $ftp;
			}
		}

		return $ftp;
	}

	function get_client_ip()
	{
		if (strlen(assign($_REQUEST['pass_ip'])))
			$ip = $_REQUEST['pass_ip']; // used in proxy script for IP pass through
		else
			$ip = $_SERVER['REMOTE_ADDR'];
		return $ip;
	}

	function remove_zero_char($value)
	{
		return str_replace(chr(0), '', $value);
	}

	