<?php /*

 ocPortal
 Copyright (c) ocProducts, 2004-2012

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license		http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright	ocProducts Ltd
 * @package		core
 */

/**
 * Standard code module initialisation function.
 */
function init__global2()
{
	global $BOOTSTRAPPING,$CHECKING_SAFEMODE,$BAD_WORD_CHARS,$FIXED_WORD_CHARS,$FIXED_WORD_CHARS_HTML,$BROWSER_DECACHEING,$CHARSET,$TEMP_CHARSET,$RELATIVE_PATH,$CURRENTLY_HTTPS,$RUNNING_SCRIPT_CACHE,$SERVER_TIMEZONE,$HAS_SET_ERROR_HANDLER,$DYING_BADLY,$XSS_DETECT,$SITE_INFO,$JAVASCRIPTS,$JAVASCRIPT,$CSSS,$IN_MINIKERNEL_VERSION,$EXITING,$FILE_BASE,$MOBILE,$CACHE_TEMPLATES,$BASE_URL_HTTP,$BASE_URL_HTTPS,$WORDS_TO_FILTER,$FIELD_RESTRICTIONS,$VALID_ENCODING,$CONVERTED_ENCODING,$MICRO_BOOTUP,$MICRO_AJAX_BOOTUP,$QUERY_LOG,$_CREATED_FILES,$CURRENT_SHARE_USER,$CACHE_FIND_SCRIPT;

	if (ini_get('output_buffering')=='1') @ob_end_clean();

	if (array_key_exists('HTTP_X_REWRITE_URL',$_SERVER))
	{
		foreach ($_GET as $key=>$val)
		{
			if ($key[0]=='?')
			{
				unset($_GET[$key]);
				$_GET[substr($key,1)]=$val;
			}
		}
		$_SERVER['REQUEST_URI']=$_SERVER['HTTP_X_REWRITE_URL'];
	} elseif ((!array_key_exists('REQUEST_URI',$_SERVER)) && (!array_key_exists('REQUEST_URI',$_ENV)))
	{
		$_SERVER['REQUEST_URI']=$_SERVER['PHP_SELF'];
		$first=true;
		foreach ($_GET as $key=>$val)
		{
			$_SERVER['REQUEST_URI'].=$first?'?':'&';
			$_SERVER['REQUEST_URI'].=urlencode($key).'='.urlencode($val);
			$first=false;
		}
	}
	if ((array_key_exists('SCRIPT_FILENAME',$_SERVER)) && (!array_key_exists('PHP_SELF',$_SERVER))) $_SERVER['PHP_SELF']=$_SERVER['SCRIPT_FILENAME'];
	elseif ((array_key_exists('SCRIPT_NAME',$_SERVER)) && (defined('HIPHOP_PHP'))) $_SERVER['PHP_SELF']=$_SERVER['SCRIPT_NAME'];

	@header('Expires: Mon, 20 Dec 1998 01:00:00 GMT');
	@header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
	//@header('Cache-Control: no-cache, must-revalidate'); // DISABLED AS MAKES IE RELOAD ON 'BACK' AND LOSE FORM CONTENTS
	@header('Pragma: no-cache'); // for proxies, and also IE

	if ((strpos($_SERVER['PHP_SELF'],'upgrader.php')===false) && ((!isset($SITE_INFO['no_extra_closed_file'])) || ($SITE_INFO['no_extra_closed_file']=='0')))
	{
		if (is_file('closed.html'))
		{
			if ((@strpos($_SERVER['SERVER_SOFTWARE'],'IIS')===false)) header('HTTP/1.0 503 Service Temporarily Unavailable');
			header('Location: closed.html');
			exit();
		}
		if (@is_file('../closed.html'))
		{
			if ((@strpos($_SERVER['SERVER_SOFTWARE'],'IIS')===false)) header('HTTP/1.0 503 Service Temporarily Unavailable');
			header('Location: ../closed.html');
			exit();
		}
	}

	// Cover up holes in old PHP versions functionality
	if (!function_exists('str_word_count'))
	{
		/**
		 * Isolate the words in the input string.
		 *
		 * @param  string			String to count words in
		 * @param  integer		The format
		 * @set    0 1 2
		 * @return mixed			Typically a list - the words of the input string
		 */
		function str_word_count($input,$format=0)
		{
			//count words
			$pattern="/[^(\w|\d|\'|\"|\.|\!|\?|;|,|\\|\/|\-\-|:|\&|@)]+/";
			$all_words=trim(preg_replace($pattern,' ',$input));
			$a=array();
			$pos=0;
			while (true)
			{
				$old_pos=$pos;
				$pos=strpos($all_words,' ',$pos);
				if ($pos===false)
				{
					$a[$old_pos]=substr($all_words,$old_pos);
					break;
				}
				$a[$old_pos]=substr($all_words,$old_pos,$pos-$old_pos);
			}
			if ($format==0) return count($a);
			return $a;
		}
	}
	if (!function_exists('html_entity_decode'))
	{
		/**
		 * Decode the HTML entitity encoded input string.
		 *
		 * @param  string			The text to decode
		 * @param  integer		The quote style code
		 * @param  ?string		Character set to decode to (NULL: default)
		 * @return string			The decoded text
		 */
		function html_entity_decode($input,$quote_style,$charset=NULL)
		{
			unset($quote_style);
			unset($charset);
			/*			// NB: &nbsp does not go to <space>. It's not something you use with html escaping, it's for hard-space-formatting. URL's don't contain spaces, but that's due to URL escaping (%20)
			$replace_array=array(
				'&amp;'=>'&',
				'&gt;'=>'>',
				'&lt;'=>'<',
				'&#039;'=>'\'',
				'&quot;'=>'"',
			);

			foreach ($replace_array as $from=>$to)
			{
				$input=str_replace($from,$to,$input);
			}

			return $input;*/

			$trans_tbl=get_html_translation_table(HTML_ENTITIES);
			$trans_tbl=array_flip($trans_tbl);
			return strtr($input,$trans_tbl);
		}
	}
	if (version_compare(phpversion(),'4.3.0')>=0)
	{
		if (!function_exists('unichrm_hex'))
		{
			/**
			 * Convert a unicode character number to a unicode string. Callback for preg_replace.
			 *
			 * @param  array					Regular expression match array.
			 * @return ~string				Converted data (false: could not convert).
			 */
			function unichrm_hex($matches)
			{
				return unichr(hexdec($matches[1]));
			}
		}

		if (!function_exists('unichrm'))
		{
			/**
			 * Convert a unicode character number to a unicode string. Callback for preg_replace.
			 *
			 * @param  array					Regular expression match array.
			 * @return ~string				Converted data (false: could not convert).
			 */
			function unichrm($matches)
			{
				return unichr(intval($matches[1]));
			}
		}

		if (!function_exists('unichr'))
		{
			/**
			 * Convert a unicode character number to a HTML-entity enabled string, using lower ASCII characters where possible.
			 *
			 * @param  integer				Character number.
			 * @return ~string				Converted data (false: could not convert).
			 */
			function unichr($c)
			{
				if ($c<=0x7F)
				{
					return chr($c);
				} else
				{
					return '#&'.strval($c).';';
				}
			}
		}
	}

	$BOOTSTRAPPING=1;
	$CHECKING_SAFEMODE=false;

	$BAD_WORD_CHARS=array(chr(128),chr(130),chr(131),chr(132),chr(133),chr(134),chr(135),chr(136),chr(137),chr(138),chr(139),chr(140),chr(142),chr(145),chr(146),chr(147),chr(148),chr(149),chr(150),chr(151),chr(152),chr(153),chr(154),chr(155),chr(156),chr(158),chr(159));
	$FIXED_WORD_CHARS=array('(EUR-)',',','{f.}','"','...','-|-','=|=','^','{%o}','{~S}','<','CE','{~Z}',"'","'",'"','"','-','-','--','~','(TM)','{~s}','>','ce','{~z}','{.Y.}'); // some of these are Comcode shortcuts. We can't use entities as we can't assume we're converting into Comcode.
	$FIXED_WORD_CHARS_HTML=array('&#8364;','&#8218;','&#402;','&#8222;','&hellip;','&#8224;','&#8225;','&#710;','&#8240;','&#352;','&#8249;','&#338;','&#381;',"&lsquo;","&rsquo;",'&ldquo;','&rdquo;','&bull;','&ndash;','&mdash;','&#732;','&trade;','&#353;','&#8250;','&#339;','&#382;','&#376;');
	$RUNNING_SCRIPT_CACHE=array();
	$BROWSER_DECACHEING=NULL;
	$CHARSET=NULL;
	$TEMP_CHARSET=NULL;
	$CURRENTLY_HTTPS=NULL;
	$CACHE_FIND_SCRIPT=array();

	error_reporting(E_ALL);
	@ini_set('html_errors','1');
	@ini_set('docref_root','http://www.php.net/manual/en/');
	@ini_set('docref_ext','.php');

	$SERVER_TIMEZONE=function_exists('date_default_timezone_get')?@date_default_timezone_get():ini_get('date.timezone');
	@ini_set('date.timezone','UTC');
	if (function_exists('date_default_timezone_set')) date_default_timezone_set('UTC'); // Needed for HPHP

	$HAS_SET_ERROR_HANDLER=false;
	$DYING_BADLY=false; // If ocPortal is bailing out uncontrollably, setting this will make sure the error hander does not try and suppress

	$XSS_DETECT=function_exists('ocp_mark_as_escaped');

	$GLOBALS['DEBUG_MODE']=(((!array_key_exists('debug_mode',$SITE_INFO) || ($SITE_INFO['debug_mode']=='1')) && ((is_dir(get_file_base().'/.svn')) || (is_dir(get_file_base().'/.git')) || (function_exists('ocp_mark_as_escaped')))) && ((!array_key_exists('keep_no_debug_mode',$_GET) || ($_GET['keep_no_debug_mode']=='0'))));
	$GLOBALS['SEMI_DEBUG_MODE']=(((!array_key_exists('debug_mode',$SITE_INFO) || ($SITE_INFO['debug_mode']=='1')) && ((is_dir(get_file_base().'/.svn')) || (is_dir(get_file_base().'/.git')) || (function_exists('ocp_mark_as_escaped')))));
	if (function_exists('set_time_limit')) @set_time_limit(60);
	if ($GLOBALS['DEBUG_MODE'])
	{
		if (function_exists('set_time_limit')) @set_time_limit(10);
		@ini_set('ocproducts.type_strictness','1');
		@ini_set('ocproducts.xss_detect','1');
	}
	if ($GLOBALS['DEBUG_MODE'])
	{
		require_code('developer_tools');
	}

	$JAVASCRIPTS=array('javascript'=>1,'javascript_thumbnails'=>1);
	if (($GLOBALS['CURRENT_SHARE_USER']!==NULL) || (get_domain()=='myocp.com')) $JAVASCRIPTS['javascript_ajax']=1;
	$CSSS=array('no_cache'=>1,'global'=>1);

	// Try and make the PHP environment as we need it
	if (function_exists('set_magic_quotes_runtime')) @set_magic_quotes_runtime(0); // @'d because it's deprecated and PHP 5.3 may give an error

	@ini_set('auto_detect_line_endings','0');
	@ini_set('include_path','');
	@ini_set('default_socket_timeout','60');

	@ini_set('allow_url_fopen','0');
	@ini_set('suhosin.executor.disable_emodifier','1'); // Extra security if suhosin is available
	@ini_set('suhosin.executor.multiheader','1'); // Extra security if suhosin is available
	@ini_set('suhosin.executor.disable_eval','0');
	@ini_set('suhosin.executor.eval.whitelist','');
	@ini_set('suhosin.executor.func.whitelist','');

	// Load most basic config
	$IN_MINIKERNEL_VERSION=0;
	$EXITING=0;
	if ((array_key_exists('use_ocf',$_GET)) && (running_script('upgrader')))
	{
		$SITE_INFO['forum_type']='ocf';
		$SITE_INFO['ocf_table_prefix']=$SITE_INFO['table_prefix'];
	}

	$CACHE_TEMPLATES=true;

	// The URL to our install (no trailing /)
	$BASE_URL_HTTP=NULL;
	$BASE_URL_HTTPS=NULL;

	$WORDS_TO_FILTER=NULL;
	$FIELD_RESTRICTIONS=NULL;

	$VALID_ENCODING=false;
	$CONVERTED_ENCODING=false;

	if (!isset($MICRO_BOOTUP)) $MICRO_BOOTUP=0;
	if (!isset($MICRO_AJAX_BOOTUP)) $MICRO_AJAX_BOOTUP=0;

	require_code_no_override('version');
	if (($MICRO_BOOTUP==0) && ($MICRO_AJAX_BOOTUP==0))
	{
		@header('X-Powered-By: ocPortal '.ocp_version_full().' (PHP '.phpversion().')');

		$QUERY_LOG=false;
		if ((isset($_REQUEST['special_page_type'])) && ($_REQUEST['special_page_type']=='query'))
		{
			$QUERY_LOG=true;
		}
	}

	// Most critical things
	require_code('support'); // A lot of support code is present in this
	srand(make_seed());
	mt_srand(make_seed());
	if (($MICRO_BOOTUP==0) && ($MICRO_AJAX_BOOTUP==0)) // Fast cacheing for bots
	{
		if ((running_script('index')) && (count($_POST)==0))
		{
			$bot_type=get_bot_type();
			if (($bot_type!==NULL) && (isset($SITE_INFO['fast_spider_cache'])) && ($SITE_INFO['fast_spider_cache']!='0'))
			{
				fast_spider_cache(true);
			}
		}
	}
	require_code('caches'); // Recently taken out of 'support' so makes sense to load it here
	require_code('database'); // There's nothing without the database
	if (((!isset($SITE_INFO['known_suexec'])) || ($SITE_INFO['known_suexec']=='0')) && (!is_writable_wrap(get_file_base().'/.htaccess'))) // If we have to run this in software
	{
		require_code('support2');
		if (ip_banned(get_ip_address())) critical_error('BANNED');
	}
	if ((running_script('messages')) && (get_param('action','new')=='new') && (get_param_integer('routine_refresh',0)==0)) // Architecturally unsound chat message precheck (for extra efficiency)
	{
		require_code('chat_poller');
		chat_poller();
	}
	if ($MICRO_BOOTUP==0)
	{
		load_user_stuff();
	}

	// For any kind of niceness we need these. The order is chosen for complex dependency reasons - don't mess with it
	if ($MICRO_AJAX_BOOTUP==0)
	{
		require_code('themes'); // Output needs to know about themes
		require_code('templates'); // So that we can do error templates
		require_code('tempcode'); // Output is done with tempcode
		if ($MICRO_BOOTUP==0)
		{
			require_code('comcode'); // Much output goes through comcode
		}
	}
	require_code('zones'); // Zone is needed because zones are where all ocPortal pages reside
	require_code('config'); // Config is needed for much active stuff
	if ((get_option('collapse_user_zones',true)==='1') && ($RELATIVE_PATH=='site'))
	{
		get_base_url();/*force calculation first*/
		$RELATIVE_PATH='';
	}
	require_code('users'); // Users are important due to permissions
	if (($MICRO_BOOTUP==0) && ($MICRO_AJAX_BOOTUP==0)) // Fast cacheing for Guests
	{
		if ((running_script('index')) && (count($_POST)==0))
		{
			if ((isset($SITE_INFO['any_guest_cached_too'])) && ($SITE_INFO['any_guest_cached_too']=='1') && (is_guest(NULL,true)))
			{
				fast_spider_cache(false);
			}
		}
	}
	$CACHE_TEMPLATES=((get_option('is_on_template_cache')=='1') || (get_param_integer('keep_cache',0)==1) || (get_param_integer('cache',0)==1)) && (get_param_integer('keep_cache',NULL)!==0) && (get_param_integer('cache',NULL)!==0);
	if ($MICRO_AJAX_BOOTUP==0)
	{
		require_code('temporal'); // Date/time functions
		require_code('files'); // Contains fix_permissions, needed for 'lang'
		require_code('lang'); // So that we can do language stuff (e.g. errors)
		convert_data_encodings();
		if ($MICRO_BOOTUP==0)
		{
			require_code('permissions'); // So we can check access
		}
	}

	// At this point we can display errors nicely
	$GLOBALS['SUPRESS_ERROR_DEATH']=false;
	set_error_handler('ocportal_error_handler');
	if (function_exists('error_get_last')) register_shutdown_function('catch_fatal_errors');
	$HAS_SET_ERROR_HANDLER=true;

	if ($MICRO_BOOTUP==0)
	{
		if (method_exists($GLOBALS['FORUM_DRIVER'],'forum_layer_initialise')) $GLOBALS['FORUM_DRIVER']->forum_layer_initialise();
	}

	if ($MICRO_AJAX_BOOTUP==0)
	{
		$JAVASCRIPT=new ocp_tempcode();
	}

	if ($MICRO_BOOTUP==0)
	{
		if (($IN_MINIKERNEL_VERSION!=1) && ($MICRO_AJAX_BOOTUP==0))
		{
			has_cookies(); // Will determine at early point whether we have cookie support
			get_num_users_site(); // Will kill site if there are too many users
		}
	}
	require_code('urls'); // URL building is crucial

	@header('Content-type: text/html; charset='.get_charset());

	if (($MICRO_AJAX_BOOTUP==0) && ($MICRO_BOOTUP==0))
	{
		// Before anything gets outputted
		handle_logins();

		require_code('site'); // This powers the site (top level page generation)

		// Are we installed?
		get_option('site_name');
	}

	// Our logging (change false to true for temporarily changing it so staff get logging)
	if (get_option('log_php_errors')=='1')
	{
		@ini_set('log_errors','1');
		if (addon_installed('errorlog'))
			@ini_set('error_log',get_custom_file_base().'/data_custom/errorlog.php');
	}
	if (($MICRO_BOOTUP==0) && ($MICRO_AJAX_BOOTUP==0) && ((get_option('display_php_errors')=='1') || (running_script('upgrader')) || (has_specific_permission(get_member(),'see_php_errors'))))
	{
		@ini_set('display_errors','1');
	} elseif (!$GLOBALS['DEBUG_MODE']) @ini_set('display_errors','0');

	// G-zip?
	@ini_set('zlib.output_compression',(get_option('gzip_output')=='1')?'On':'Off');

	if ((function_exists('setlocale')) && ($MICRO_AJAX_BOOTUP==0))
	{
		$locales=explode(',',do_lang('locale'));
		setlocale(LC_ALL,$locales[0]);
		@setlocale(LC_ALL,$locales);
		unset($locales);
	}

	if (($MICRO_AJAX_BOOTUP==0) && ($MICRO_BOOTUP==0) && ((!isset($SITE_INFO['no_installer_checks'])) || ($SITE_INFO['no_installer_checks']=='0')))
	{
		if ((is_file(get_file_base().'/install.php')) && (!is_file(get_file_base().'/install_ok')) && (running_script('index')))
			warn_exit(do_lang_tempcode('MUST_DELETE_INSTALLER'));
	}

	if (($MICRO_AJAX_BOOTUP==0) && ($MICRO_BOOTUP==0))
	{
		$changed_base_url=!array_key_exists('base_url',$SITE_INFO) && get_long_value('last_base_url')!==get_base_url(false);
		if ((running_script('index')) && ((is_browser_decacheing()) || ($changed_base_url)))
		{
			require_code('view_modes');
			erase_tempcode_cache();
			erase_cached_templates(!$changed_base_url);
			erase_cached_language();
			persistant_cache_empty();
			if ($changed_base_url)
			{
				require_lang('zones');
				require_code('zones3');
				erase_comcode_page_cache();
				set_long_value('last_base_url',get_base_url(false));
			}
		}

		if (has_zone_access(get_member(),'adminzone'))
		{
			$JAVASCRIPTS['javascript_staff']=1;
			$JAVASCRIPTS['javascript_ajax']=1;
			if (addon_installed('occle')) $JAVASCRIPTS['javascript_button_occle']=1;
		}
		if ((addon_installed('realtime_rain')) && (get_option('bottom_show_realtime_rain_button',true)==='1')) $JAVASCRIPTS['javascript_button_realtime_rain']=1;
	}
	/*ocp_memory_profile('startup');
	$func=get_defined_functions();
	print_r($func['user']);*/

	if (((ocp_srv('HTTPS')!='') && (ocp_srv('HTTPS')!='off')) && (((!defined('HIPHOP_PHP')) || (tacit_https()) || (is_page_https(get_zone_name(),get_page_name()))))) // Fix IE bug
	{
		@header('Cache-Control: private');
		@header('Pragma: private');
	}

	$BOOTSTRAPPING=0;

	if (($GLOBALS['SEMI_DEBUG_MODE']) && ($MICRO_AJAX_BOOTUP==0)) // Lots of code that only runs if you're a programmer. It tries to make sure coding standards are met.
	{
		if ($GLOBALS['SEMI_DEBUG_MODE'])
		{
			/*if ((mt_rand(0,2)==1) && ($GLOBALS['DEBUG_MODE']) && (running_script('index')))	We know this works now, so let's stop messing up our development speed
			{
				require_code('view_modes');
				erase_cached_templates(true); // Stop anything trying to read a template cache item (E.g. CSS, JS) that might not exist!
			}*/

			if ((strpos(ocp_srv('HTTP_REFERER'),ocp_srv('HTTP_HOST'))!==false) && (strpos(ocp_srv('HTTP_REFERER'),'keep_devtest')!==false) && (!running_script('attachment')) && (!running_script('upgrader')) && (strpos(ocp_srv('HTTP_REFERER'),'login')===false) && (is_null(get_param('keep_devtest',NULL))))
			{
				$_GET['keep_devtest']='1';
				fatal_exit('URL not constructed properly: development mode in use but keep_devtest was not specified. This indicates that links have been made without build_url (in PHP) or keep_stub (in Javascript). Whilst not fatal this time, failure to use these functions can cause problems when your site goes live. See the ocPortal codebook for more details.');
			} else $_GET['keep_devtest']='1';
		}

		if ((browser_matches('true_xhtml')) && (get_value('html5')!=='1') && (get_value('html5')!=='_true'/*TODO: deprecate old _true check*/) && (get_param_integer('keep_no_xhtml',0)==0) && (!running_script('upgrader'))) // In theory this is supported (and mostly does work), but a lot of necessary Javascript is not standardised, and the browser's that support XHTML properly do not agree on how to implement this Javascript in a usable way when it is enabled. This leads to a lot of corner-cases. If you're seeing this code enabled, it was possible for us to resolve them, otherwise not yet.
		{
			@header('Content-type: application/xhtml+xml; charset='.get_charset());
		}

		if (isset($_CREATED_FILES)) // Comes from ocProducts custom PHP version
		{
			/**
			 * Run after-tests for debug mode, to make sure coding standards are met.
			 */
			function debug_mode_aftertests()
			{
				global $_CREATED_FILES,$_MODIFIED_FILES;

				// Use the info from ocProduct's custom PHP version to make sure that all files that were created/modified got synched as they should have been.
				foreach ($_CREATED_FILES as $file)
				{
					if ((substr($file,0,strlen(get_file_base()))==get_file_base()) && (substr($file,-4)!='.log') && (basename($file)!='permissioncheckslog.php'))
						@exit(escape_html('File not permission-synched: '.$file));
				}
				foreach ($_MODIFIED_FILES as $file)
				{
					if ((strpos($file,'_cache')===false) && (substr($file,0,strlen(get_file_base()))==get_file_base()) && (substr($file,-4)!='.log') && (basename($file)!='permissioncheckslog.php'))
						@exit(escape_html('File not change-synched: '.$file));
				}

				global $TITLE_CALLED,$SCREEN_TEMPLATE_CALLED,$EXITING;
				if ((is_null($SCREEN_TEMPLATE_CALLED)) && ($EXITING==0) && (strpos(ocp_srv('PHP_SELF'),'index.php')!==false)) @exit(escape_html('No screen template called.'));
				if ((!$TITLE_CALLED) && ((is_null($SCREEN_TEMPLATE_CALLED)) || ($SCREEN_TEMPLATE_CALLED!='')) && ($EXITING==0) && (strpos(ocp_srv('PHP_SELF'),'index.php')!==false)) @exit(escape_html('No title used on screen.'));
			}

			register_shutdown_function('debug_mode_aftertests');
		}

		if ((ocp_srv('SCRIPT_FILENAME')!='') && ($GLOBALS['DEBUG_MODE']) && (strpos(ocp_srv('SCRIPT_FILENAME'),'data_custom')===false))
		{
			if (@strlen(file_get_contents(ocp_srv('SCRIPT_FILENAME'),FILE_TEXT))>4500)
			{
				fatal_exit('Entry scripts (front controllers) should not be shoved full of code.');
			}
		}
	}

	// FirePHP console support, only for administrators
	if (((get_param_integer('keep_firephp',0)==1) || (get_param_integer('keep_queries',0)==1)) && (($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member())) || ($GLOBALS['IS_ACTUALLY_ADMIN'])))
	{
		require_code('firephp');
	}

	$default_memory_limit=get_value('memory_limit');
	if ((is_null($default_memory_limit)) || ($default_memory_limit=='') || ($default_memory_limit=='0') || ($default_memory_limit=='-1'))
		$default_memory_limit='64M';
	@ini_set('memory_limit',$default_memory_limit);
	if ((isset($GLOBALS['FORUM_DRIVER'])) && ($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member())))
	{
		if (get_param_integer('keep_avoid_memory_limit',0)==1)
		{
			disable_php_memory_limit();
		}
		$memory_test=get_param_integer('keep_memory_limit_test',0);
		if (($memory_test!=0) && ($memory_test<=32))
		{
			@ini_set('memory_limit',strval($memory_test).'M');
		}
	}

	if ((get_option('sitewide_im',true)==='1') && (running_script('index')) /* i.e. not running script */ && (get_param('type','misc',true)!='room'))
	{
		require_code('chat');
		enter_chat_lobby();
	}

	// Startup hooks
	if (!running_script('upgrader'))
	{
		$startup_hooks=find_all_hooks('systems','startup');
		foreach (array_keys($startup_hooks) as $hook)
		{
			require_code('hooks/systems/startup/'.filter_naughty_harsh($hook));
			$ob=object_factory('Hook_startup_'.filter_naughty_harsh($hook),true);
			if ($ob===NULL) continue;
			$ob->run($MICRO_BOOTUP,$MICRO_AJAX_BOOTUP,0);
		}
		if (($CURRENT_SHARE_USER!==NULL) && (float_to_raw_string(ocp_version_number())!=get_value('version')))
		{
			require_code('upgrade');
			clear_caches_2();
			version_specific();
			upgrade_modules();
			ocf_upgrade();
		}
	}
}

/**
 * Find if we can use the fast spider cache.
 *
 * @return boolean			Whether we can
 */
function can_fast_spider_cache()
{
	if (isset($_GET['keep_session'])) return false;
	if (isset($_GET['redirect'])) return false;
	if (isset($_GET['zone'])) return false;
	if (isset($_GET['date'])) return false;
	$url_easy=get_self_url_easy();
	if (strpos($url_easy,'sort=')!==false) return false;
	if (strpos($url_easy,'start=')!==false) return false;
	if (strpos($url_easy,'max=')!==false) return false;
	return true;
}

/**
 * If possible dump the user to 100% static caching.
 *
 * @param  boolean			Whether to cache as a bot
 */
function fast_spider_cache($bot=true)
{
	global $SITE_INFO;

	require_code('urls');

	if (!can_fast_spider_cache()) return;

	$fast_cache_path=get_custom_file_base().'/persistant_cache/'.md5(serialize(get_self_url_easy()));
	if (!$bot) $fast_cache_path.='__non-bot';
	if (!array_key_exists('js_on',$_COOKIE)) $fast_cache_path.='__no-js';
	$fast_cache_path.='.gcd';
	if (is_file($fast_cache_path))
	{
		$expires=60*60*intval($SITE_INFO['fast_spider_cache']);
		$mtime=filemtime($fast_cache_path);
		if ($mtime>time()-$expires)
		{
			if ($bot) // Only bots can do this, as they won't try to login and end up reaching a previously cached page
			{
				header("Pragma: public");
				header("Cache-Control: maxage=".strval($expires));
				header('Expires: '.gmdate('D, d M Y H:i:s',time()+$expires).' GMT');
				header('Last-Modified: '.gmdate('D, d M Y H:i:s',$mtime).' GMT');

				$since=ocp_srv('HTTP_IF_MODIFIED_SINCE');
				if ($since!='')
				{
					if (strtotime($since)<$mtime)
					{
						header('HTTP/1.0 304 Not Modified');
						exit();
					}
				}
			}

			if (function_exists('gzencode'))
			{
				ini_set('zlib.output_compression','Off');
				header('Content-Encoding: gzip');
			}

			exit(file_get_contents($fast_cache_path));
		} else
		{
			@unlink($fast_cache_path);
			sync_file($fast_cache_path);
		}
	}
}

/**
 * Disable the PHP memory limit. Do not use this carelessly, use it if a screen is a bit fat or in an importer, don't use it assuming memory is infinite.
 */
function disable_php_memory_limit()
{
	$shl=@ini_get('suhosin.memory_limit');
	if (($shl===false) || ($shl=='') || ($shl=='0'))
	{
		@ini_set('memory_limit','64M');
		@ini_set('memory_limit','-1');
	} else
	{
		@ini_set('memory_limit',$shl);
	}
}

/**
 * Get the character set to use. We try and be clever to allow AJAX scripts to avoid loading up language
 *
 * @return string			The character set
 */
function get_charset()
{
	global $CHARSET;
	if (isset($CHARSET)) return $CHARSET;

	global $SITE_INFO;
	if (isset($SITE_INFO['charset'])) // An optimisation, if you want to put it in here
	{
		$CHARSET=$SITE_INFO['charset'];
		return $CHARSET;
	}

	global $LANG_LOADED_LANG;
	if ((function_exists('do_lang')) && (function_exists('user_lang')) && (isset($LANG_LOADED_LANG[user_lang()]['critical_error'])) && (!in_safe_mode()))
	{
		$attempt=do_lang('charset',NULL,NULL,NULL,NULL,false);
		if ($attempt!==NULL)
		{
			$CHARSET=$attempt;
			return $attempt;
		}
	}

	global $TEMP_CHARSET;
	if (isset($TEMP_CHARSET)) return $TEMP_CHARSET;

	global $SITE_INFO;
	$lang=array_key_exists('default_lang',$SITE_INFO)?$SITE_INFO['default_lang']:'EN';
	$path=get_file_base().'/lang_custom/'.$lang.'/global.ini';
	if (!is_file($path)) $path=get_file_base().'/lang/'.$lang.'/global.ini';
	if (!is_file($path))
	{
		$path=get_file_base().'/lang_custom/'.$lang.'/global.po';
		if (is_file($path))
		{
			$TEMP_CHARSET='utf-8';
			return $TEMP_CHARSET;
		}
	}
	if (!is_file($path))
	{
		$path=get_file_base().'/lang_custom/'.$lang.'/global-'.strtolower($lang).'.po';
		if (is_file($path))
		{
			$TEMP_CHARSET='utf-8';
			return $TEMP_CHARSET;
		}
	}
	if (!is_file($path)) $path=get_file_base().'/lang/EN/global.ini';
	$file=fopen($path,'rt');
	$contents=str_replace(chr(13),chr(10),fread($file,3000));
	fclose($file);
	$matches=array();
	if (preg_match('#\[strings\].*charset=([\w\-]+)\n#s',$contents,$matches)!=0)
	{
		$TEMP_CHARSET=$matches[1];
		return $TEMP_CHARSET;
	}
	$TEMP_CHARSET='iso-8859-1';
	return $TEMP_CHARSET;
}

/**
 * Load stuff that allows user code to work.
 */
function load_user_stuff()
{
	if ((!array_key_exists('FORUM_DRIVER',$GLOBALS)) || ($GLOBALS['FORUM_DRIVER']===NULL)) // Second clause is for Quercus, as it pre-NULLs referenced variables
	{
		global $SITE_INFO;
		require_code('forum_stub');

		if (!array_key_exists('forum_type',$SITE_INFO)) $SITE_INFO['forum_type']='ocf';
		require_code('forum/'.$SITE_INFO['forum_type']);	 // So we can at least get user details
		$GLOBALS['FORUM_DRIVER']=object_factory('forum_driver_'.filter_naughty_harsh($SITE_INFO['forum_type']));
		if (($SITE_INFO['forum_type']=='ocf') && (get_db_forums()==get_db_site()) && ($GLOBALS['FORUM_DRIVER']->get_drivered_table_prefix()==get_table_prefix()) && (!$GLOBALS['DEBUG_MODE'])) // NB: In debug mode needs separating so we can properly test our boundaries
		{
			$GLOBALS['FORUM_DRIVER']->connection=$GLOBALS['SITE_DB'];
		}
		elseif ($SITE_INFO['forum_type']!='none')
		{
			$GLOBALS['FORUM_DRIVER']->connection=new database_driver(get_db_forums(),get_db_forums_host(),get_db_forums_user(),get_db_forums_password(),$GLOBALS['FORUM_DRIVER']->get_drivered_table_prefix());
		}
		$GLOBALS['FORUM_DRIVER']->MEMBER_ROWS_CACHED=array();
		$GLOBALS['FORUM_DB']=&$GLOBALS['FORUM_DRIVER']->connection;
	}
}

/**
 * ocPortal error catcher for fatal versions. This is hooked in only on PHP5.2 as error_get_last() only works on these versions.
 */
function catch_fatal_errors()
{
	if (!function_exists('error_get_last')) return;

	$error=error_get_last();
	if (!is_null($error))
	{
		if (!array_key_exists('message',$error)) return; // Needed for HipHop PHP
		if (substr($error['message'],0,26)=='Maximum execution time of ')
		{
			if (function_exists('i_force_refresh'))
			{
				i_force_refresh();
			}
		}
		//$tmp=$GLOBALS;unset($tmp['GLOBALS']);@var_dump($tmp);@exit();
		//@var_dump(get_defined_functions()); exit(); // Useful for debugging memory problems, finding unneeded stuff that is loaded
		switch($error['type'])
		{
			case E_ERROR:
			case E_CORE_ERROR:
			case E_COMPILE_ERROR:
			case E_USER_ERROR:
				$GLOBALS['SUPRESS_ERROR_DEATH']=false; // We can't recover as we've lost our execution track. Force a nice death rather than trying to display a recoverable error.
				$GLOBALS['DYING_BADLY']=true; // Does not actually work unfortunately. @'d calls never get here at all.
				ocportal_error_handler($error['type'],$error['message'],$error['file'],$error['line']);
		}
	}
}

/**
 * ocPortal error handler (hooked into PHP error system).
 *
 * @param  integer		The error code-number
 * @param  PATH			The error message
 * @param  string			The file the error occurred in
 * @param  integer		The line the error occurred on
 * @return boolean		Always false
 */
function ocportal_error_handler($errno,$errstr,$errfile,$errline)
{
	if ((error_reporting()==0) && (!$GLOBALS['DYING_BADLY'])) return false; // This actually tells if @ was used oddly enough. You wouldn't figure from the PHP docs.

	if ((error_reporting() & $errno) || ($GLOBALS['DYING_BADLY']))
	{
		// Strip down path for security
		if (substr(str_replace(DIRECTORY_SEPARATOR,'/',$errfile),0,strlen(get_file_base().'/'))==str_replace(DIRECTORY_SEPARATOR,'/',get_file_base().'/'))
			$errfile=substr($errfile,strlen(get_file_base().'/'));

		// Work out the error type
		if (!defined('E_RECOVERABLE_ERROR')) define('E_RECOVERABLE_ERROR',4096);
		switch ($errno)
		{
			case E_RECOVERABLE_ERROR: // constant not defined in all php versions but we defined it
			case E_USER_ERROR:
			case E_PARSE:
			case E_CORE_ERROR:
			case E_COMPILE_ERROR:
			case E_ERROR:
				$type='error';
				break;
			case -123: // Hacked in for the memtrack extension, which was buggy
			case E_CORE_WARNING:
			case E_COMPILE_WARNING:
			case E_USER_WARNING:
			case E_WARNING:
				$type='warning';
				break;
			//case E_STRICT: (constant not defined in all php versions)
			//case E_DEPRECATED: (constant not defined in all php versions)
			//case E_USER_DEPRECATED: (constant not defined in all php versions)
			case E_USER_NOTICE:
			case E_NOTICE:
				$type='notice';
				break;
			default: // We don't know the error type so it's probably best to continue (could be a problem with something getting deprecated)
				return false;
		}

		$GLOBALS['DYING_BADLY']=false; // So error suppress works again
		if (strpos($errstr,'Allowed memory')!==false)
		{
			global $_REQUIRED_CODE;
			if (!array_key_exists('failure',$_REQUIRED_CODE))
			{
				critical_error('EMERGENCY',$errstr.' ['.$errfile.' at '.strval($errline).']');
			}
		}
		require_code('failure');
		_ocportal_error_handler($type,$errno,$errstr,$errfile,$errline);
	}

	return false;
}

/*function ocp_memory_profile($id)
{
	if (!function_exists('memory_get_usage')) return;
	echo memory_get_usage().'  ('.$id.')<br />';
}
function ocp_memory_profile_2($id,&$struct)
{
	if (!function_exists('memory_get_usage')) return;
	if (!function_exists('var_export')) return;
	@ob_end_clean();
	echo memory_get_usage().'  ('.$id.')'.(is_null($struct)?'':(' ['.strlen(var_export($struct,true)).']')).'<br />';
}*/

/**
 * Find whether the browser session is set to be doing a hard cache-empty refresh.
 *
 * @return boolean		Whether the browser session is set to be doing a hard cache-empty refresh
 */
function is_browser_decacheing()
{
	global $BROWSER_DECACHEING;
	if ($BROWSER_DECACHEING!==NULL) return $BROWSER_DECACHEING;

	if (is_null(get_value('ran_once')))
	{
		set_value('ran_once','1');
		return true;
	}
	$header_method=(array_key_exists('HTTP_CACHE_CONTROL',$_SERVER)) && ($_SERVER['HTTP_CACHE_CONTROL']=='no-cache') && (ocp_srv('REQUEST_METHOD')!='POST') && ((!function_exists('browser_matches')) || (!browser_matches('opera')));
	$BROWSER_DECACHEING=(($header_method) && ((array_key_exists('FORUM_DRIVER',$GLOBALS)) && (has_actual_page_access(get_member(),'admin_cleanup')) || ($GLOBALS['IS_ACTUALLY_ADMIN'])));
	return $BROWSER_DECACHEING;
}

/**
 * Find whether a certain script is being run to get here.
 *
 * @param  string				Script filename (canonically we want NO .php file type suffix)
 * @return boolean			Whether the script is running
 */
function running_script($is_this_running)
{
	// First check cache
	global $RUNNING_SCRIPT_CACHE;
	if (isset($RUNNING_SCRIPT_CACHE[$is_this_running.'.php'])) return $RUNNING_SCRIPT_CACHE[$is_this_running.'.php'];
	if (isset($RUNNING_SCRIPT_CACHE[$is_this_running])) return $RUNNING_SCRIPT_CACHE[$is_this_running];

	// Make $is_this_running fully-qualified, to stop common filename prefixes matching with a stem compare
	if (substr($is_this_running,-4)!='.php') $is_this_running.='.php';

	// Strip down current URL so we can do a simple stem compare
	$stripped_current_url=preg_replace('#^.*/#','',function_exists('ocp_srv')?ocp_srv('PHP_SELF'):$_SERVER['PHP_SELF']);

	// Do the stem compare
	$answer=(substr($stripped_current_url,0,strlen($is_this_running))==$is_this_running);

	// Cache and return result
	$RUNNING_SCRIPT_CACHE[$is_this_running]=$answer;
	return $answer;
}

/**
 * This is a intended to output an informational exit at the same time as terminating execution
 *
 * @param  mixed			The error message (string or tempcode)
 */
function inform_exit($text)
{
	require_code('failure'); // It's in failure.php although this isn't REALLY failure. Still it's an exceptional event so we can't justify loading the code as global.
	_generic_exit($text,'INFORM_SCREEN');
}

/**
 * This is a less-revealing alternative to fatal_exit, that is used for user-errors/common-corruption-scenarios
 *
 * @param  mixed			The error message (string or tempcode)
 */
function warn_exit($text)
{
	require_code('failure');
	suggest_fatalistic();
	_generic_exit($text,'WARN_SCREEN');
	if (running_script('cron_bridge'))
		relay_error_notification(is_object($text)?$text->evaluate():escape_html($text),false,'error_occurred_cron');
}

/**
 * Give the user an option to see a stack trace by adding in a link, but only if they have permission
 */
function suggest_fatalistic()
{
	if ((may_see_stack_dumps()) && (get_param_integer('keep_fatalistic',0)==0) && (running_script('index')))
	{
		if (count($_POST)==0)
		{
			$stack_trace_url=build_url(array('page'=>'_SELF','keep_fatalistic'=>1),'_SELF',NULL,true);
			$st=do_lang_tempcode('WARN_TO_STACK_TRACE',escape_html($stack_trace_url->evaluate()));
		} elseif (count($_FILES)==0)
		{
			$stack_trace_url=build_url(array('page'=>'_SELF','keep_fatalistic'=>1),'_SELF',NULL,true);
			$p=build_keep_post_fields();
			$st=do_lang_tempcode('WARN_TO_STACK_TRACE_2',escape_html($stack_trace_url->evaluate()),$p->evaluate());
		} else
		{
			$stack_trace_url=build_url(array('page'=>'','keep_fatalistic'=>1),'');
			$st=do_lang_tempcode('WARN_TO_STACK_TRACE_3',escape_html($stack_trace_url->evaluate()));
		}
		require_code('site');
		attach_message($st,'inform');
	}
}

/**
 * Do a fatal exit, echo the header (if possible) and an error message, followed by a debugging back-trace.
 * It also adds an entry to the error log, for reference.
 *
 * @param  mixed				The error message (string or tempcode)
 */
function fatal_exit($text)
{
	require_code('failure');
	_fatal_exit($text);
}

/**
 * Log a hackattack, then displays an error message. It also attempts to send an e-mail to the staff alerting them of the hackattack.
 *
 * @param  ID_TEXT		The reason for the hack attack. This has to be a language string codename
 * @param  SHORT_TEXT	A parameter for the hack attack language string (this should be based on a unique ID, preferably)
 * @param  SHORT_TEXT	A more illustrative parameter, which may be anything (e.g. a title)
 */
function log_hack_attack_and_exit($reason,$reason_param_a='',$reason_param_b='')
{
	require_code('failure');
	_log_hack_attack_and_exit($reason,$reason_param_a,$reason_param_b);
}

/**
 * Use the url_title_cache table (a bit of a hack but saved changed the DB structure) to see if a check-op was performed has been performed within the last 30 days.
 *
 * @param  ID_TEXT		Special check code (often a URL but does not need to be).
 * @return boolean		Whether the check has happened recently.
*/
function handle_has_checked_recently($id_code)
{
	$last_check_test=$GLOBALS['SITE_DB']->query_value_null_ok('url_title_cache','t_title',array('t_url'=>'!'.$id_code));
	if ((is_null($last_check_test)) || (substr($last_check_test,0,1)!='!') || (intval(substr($last_check_test,1))+60*60*24*30<time())) // only re-checks every 30 days
	{
		// Show when it was last tested
		$GLOBALS['SITE_DB']->query_delete('url_title_cache',array('t_url'=>'!'.$id_code),'',1); // To make sure it can insert below
		$GLOBALS['SITE_DB']->query_insert('url_title_cache',array('t_title'=>'!'.strval(time()),'t_url'=>'!'.$id_code),false,true); // To stop weird race-like conditions

		return false;
	}

	return true;
}

/**
 * A custom random number seed generator. It returns a random number seed.
 *
 * @return integer		A random seed
 */
function make_seed()
{
	list($usec,$sec)=explode(' ', microtime(false));
	$u=produce_salt();
	return intval(floatval($sec)*floatval($usec))+ord(substr($u,0,1))+(ord(substr($u,1,2))<<8);
}

/**
 * Get the full string version of ocPortal that you are running.
 *
 * @return string			The string saying the full ocPortal version number
 */
function ocp_version_full()
{
	$minor=ocp_version_minor();
	return strval(ocp_version()).(($minor=='')?'':(is_numeric($minor[0])?'.':'-').$minor);
}

/**
 * Get the domain the website is installed on (preferably, without any www). The domain is used for e-mail defaults amongst other things.
 *
 * @return string			The domain of the website
 */
function get_domain()
{
	global $SITE_INFO;
	$ret=array_key_exists('domain',$SITE_INFO)?$SITE_INFO['domain']:'';
	if ($ret=='')
	{
		if ((!array_key_exists('base_url',$SITE_INFO)) || ($SITE_INFO['base_url']=='')) return array_key_exists('HTTP_HOST',$_SERVER)?preg_replace('#^www\.#','',$_SERVER['HTTP_HOST']):'localhost'; // Can't be ocp_srv due to bootstrap order
		$matches=array();
		preg_match('#://([^/\#]+)#',$SITE_INFO['base_url'],$matches);
		$ret=preg_replace('#^www\.#','',$matches[1]);
	}
	return $ret;
}

/**
 * Get the type of forums installed.
 *
 * @return string			The type of forum installed
 */
function get_forum_type()
{
	global $SITE_INFO;
	if (!isset($SITE_INFO['forum_type'])) $SITE_INFO['forum_type']='ocf';
	return $SITE_INFO['forum_type'];
}

/**
 * Get the installed forum base URL.
 *
 * @param  boolean		Whether to get the base directory of the forum. Unless running OCF, this makes no difference - if possibly running OCF, you need to think about this parameter: are you trying to reach the MSN-central-site or just a link to the forums?
 * @return URLPATH		The installed forum base URL
 */
function get_forum_base_url($forum_base=false)
{
	global $SITE_INFO;

	if (!array_key_exists('board_prefix',$SITE_INFO)) $SITE_INFO['board_prefix']=get_base_url();
	$forum_type=get_forum_type();
	if ($forum_type=='none') return '';
	$needs_forum_strip=(substr($SITE_INFO['board_prefix'],-6)=='/forum') && (substr(get_base_url(),-6)!='/forum');
	if (($forum_type=='ocf') && (!$forum_base) && ($needs_forum_strip)) return substr($SITE_INFO['board_prefix'],0,strlen($SITE_INFO['board_prefix'])-6);
	if (($forum_type=='ocf') && ($forum_base) && ($needs_forum_strip)) return $SITE_INFO['board_prefix'].'/forum';
	return $SITE_INFO['board_prefix'];
}

/**
 * Get the ocPortal cookie path.
 *
 * @return ?string		The ocPortal cookie path (NULL: no special path, global)
 */
function get_cookie_path()
{
	global $SITE_INFO;
	$ret=array_key_exists('cookie_path',$SITE_INFO)?$SITE_INFO['cookie_path']:'/';
	return ($ret=='')?NULL:$ret;
}

/**
 * Get the ocPortal cookie domain.
 *
 * @return ?string		The ocPortal cookie domain (NULL: current domain)
 */
function get_cookie_domain()
{
	global $SITE_INFO;
	$ret=array_key_exists('cookie_domain',$SITE_INFO)?$SITE_INFO['cookie_domain']:NULL;
	return ($ret=='')?NULL:$ret;
}

/**
 * Get the number of days to store our cookies.
 *
 * @return integer		The number of days to store our cookies
 */
function get_cookie_days()
{
	global $SITE_INFO;
	return array_key_exists('cookie_domain',$SITE_INFO)?intval($SITE_INFO['cookie_days']):120;
}

/**
 * Get the site name.
 *
 * @return string			The name of the site
 */
function get_site_name()
{
	return get_option('site_name');
}

/**
 * Find whether we are running in safe mode.
 *
 * @return boolean		Whether we are in safe mode
 */
function in_safe_mode()
{
	global $CHECKING_SAFEMODE;
	if ($CHECKING_SAFEMODE) return false; // Stops infinite loops (e.g. Check safe mode > Check access > Check usergroups > Check implicit usergroup hooks > Check whether to look at custom implicit usergroup hooks [i.e. if not in safe mode])
	$CHECKING_SAFEMODE=true;
	$ret=((get_param_integer('keep_safe_mode',0)==1) && ((isset($GLOBALS['IS_ACTUALLY_ADMIN']) && ($GLOBALS['IS_ACTUALLY_ADMIN'])) || (!array_key_exists('FORUM_DRIVER',$GLOBALS)) || ($GLOBALS['FORUM_DRIVER']===NULL) || (!function_exists('get_member')) || ($GLOBALS['FORUM_DRIVER']->is_super_admin(get_member()))));
	$CHECKING_SAFEMODE=false;
	return $ret;
}

/**
 * Find the URL to a certain external handler script (ocPortal allows these to be moved around between zones, to suit site .htaccess requirements).
 *
 * @param  string			The codename of the needed script
 * @param  boolean		Whether to append keep variables
 * @param  integer		Code representing what base URL type to use (0=guess, 1=http, 2=https)
 * @set 0 1 2
 * @return URLPATH		The URL to the script
 */
function find_script($name,$append_keep=false,$base_url_code=0)
{
	$append='';
	if ($append_keep)
	{
		$keep=symbol_tempcode('KEEP',array('1'));
		$append.=$keep->evaluate();
	}

	global $CACHE_FIND_SCRIPT;
	if ($CACHE_FIND_SCRIPT===array())
	{
		if (function_exists('persistant_cache_get')) $CACHE_FIND_SCRIPT=persistant_cache_get('SCRIPT_PLACES');
		if ($CACHE_FIND_SCRIPT===NULL) $CACHE_FIND_SCRIPT=array();
	}
	if (isset($CACHE_FIND_SCRIPT[$name][$append_keep][$base_url_code])) return $CACHE_FIND_SCRIPT[$name][$append_keep][$base_url_code].$append;

	$zones=array(get_zone_name());
	if (!in_safe_mode())
	{
		$zones[]='data_custom';
	}
	$zones[]='data';
	$zones=array_merge($zones,find_all_zones());
	foreach ($zones as $zone)
	{
		if ($zone!='site') // If not found, we assume in here
		{
			if (is_file(get_file_base().'/'.$zone.'/'.$name.'.php'))
			{
				$ret=get_base_url().'/'.$zone.(($zone!='')?'/':'').$name.'.php';
				$CACHE_FIND_SCRIPT[$name][$append_keep][$base_url_code]=$ret;
				if (function_exists('persistant_cache_set')) persistant_cache_set('SCRIPT_PLACES',$CACHE_FIND_SCRIPT,true);
				return $ret.$append;
			}
		}
	}
	$ret=get_base_url(($base_url_code==0)?NULL:($base_url_code==2)).'/site/'.$name.'.php';
	$CACHE_FIND_SCRIPT[$name][$append_keep][$base_url_code]=$ret;
	if (function_exists('persistant_cache_set')) persistant_cache_set('SCRIPT_PLACES',$CACHE_FIND_SCRIPT,true);
	return $ret.$append;
}

/**
 * Get the base url (the minimum fully qualified URL to our installation).
 *
 * @param  ?boolean		Whether to get the HTTPS base URL (NULL: do so only if the current page uses the HTTPS base URL)
 * @param  ?ID_TEXT		The zone the link is for (NULL: root zone)
 * @return URLPATH		The base-url
 */
function get_base_url($https=NULL,$zone_for=NULL)
{
	if ($https===NULL) // If we don't know, we go by what the current page is
	{
		global $CURRENTLY_HTTPS;
		$https=$CURRENTLY_HTTPS;
		if ($https===NULL)
		{
			if ((get_option('enable_https',true)=='0') || (!running_script('index')))
			{
				$https=false;
			} else
			{
				$https=function_exists('is_page_https') && function_exists('get_zone_name') && ((tacit_https()) || is_page_https(get_zone_name(),get_page_name()));
			}
			$CURRENTLY_HTTPS=$https;
		}
	}

	global $BASE_URL_HTTP,$BASE_URL_HTTPS,$VIRTUALISED_ZONES;

	if ($VIRTUALISED_ZONES===NULL)
	{
		require_code('zones');
		get_zone_name();
	}

	if (($BASE_URL_HTTP!==NULL) && (!$https) && ((!$VIRTUALISED_ZONES) || ($zone_for===NULL))) return $BASE_URL_HTTP.(($zone_for=='')?'':('/'.$zone_for));
	if (($BASE_URL_HTTPS!==NULL) && ($https) && ((!$VIRTUALISED_ZONES) || ($zone_for===NULL))) return $BASE_URL_HTTPS.(($zone_for=='')?'':('/'.$zone_for));

	global $SITE_INFO;
	if ((!isset($SITE_INFO)) || (!array_key_exists('base_url',$SITE_INFO)) || ($SITE_INFO['base_url']=='')) // Try and autodetect the base URL if it's not configured
	{
		$domain=ocp_srv('HTTP_HOST');
		$colon_pos=strpos($domain,':');
		if ($colon_pos!==false) $domain=substr($domain,0,$colon_pos);
		$port=ocp_srv('SERVER_PORT');
		if (($port=='') || ($port=='80') || ($port=='443')) $port=''; else $port=':'.$port;
			$SITE_INFO['base_url']='http://'.$domain.$port.str_replace('%2F','/',rawurlencode(preg_replace('#/'.str_replace('#','\#',preg_quote($GLOBALS['RELATIVE_PATH'])).'$#','',str_replace('\\','/',dirname(ocp_srv('PHP_SELF'))))));
	}

	$base_url=$SITE_INFO['base_url'];
	global $CURRENT_SHARE_USER;
	if ($CURRENT_SHARE_USER!==NULL)
	{
		$base_url=preg_replace('#^http://([\w]+\.)?'.str_replace('#','\#',preg_quote($SITE_INFO['custom_share_domain'])).'#','http://'.ocp_srv('HTTP_HOST'),$base_url);
	}
	$found_mapping=false;
	if ($VIRTUALISED_ZONES)
	{
		$zone_doing=($zone_for===NULL)?'':str_replace('/','',$zone_for);

		if (array_key_exists('ZONE_MAPPING_'.$zone_doing,$SITE_INFO))
		{
			$domain=$SITE_INFO['ZONE_MAPPING_'.$zone_doing][0];
			$path=$SITE_INFO['ZONE_MAPPING_'.$zone_doing][1];
			$base_url='http://'.$domain;
			if ($path!='') $base_url.='/'.$path;
			$found_mapping=true;
		}
	}

	if ($https)
	{
		$base_url='https://'.preg_replace('#^\w*://#','',$base_url);
		if ((!$VIRTUALISED_ZONES) || ($zone_for===NULL)) $BASE_URL_HTTPS=$base_url;
	} elseif ((!$VIRTUALISED_ZONES) || ($zone_for===NULL)) $BASE_URL_HTTP=$base_url;

	if (!$found_mapping)
	{
		$base_url.=(($zone_for=='')?'':('/'.$zone_for));
	}

	return $base_url;
}

/**
 * Get the base url (the minimum fully qualified URL to our personal data installation). For a shared install only, this is different to the base-url.
 *
 * @param  ?boolean		Whether to get the HTTPS base URL (NULL: do so only if the current page uses the HTTPS base URL)
 * @return URLPATH		The base-url
 */
function get_custom_base_url($https=NULL)
{
	global $SITE_INFO;
	if (!isset($SITE_INFO['custom_base_url_stub'])) return get_base_url($https);

	// Note that HTTPS is not supported for shared installs
	$u=current_share_user();
	if ($u===NULL) return get_base_url($https);
	return $SITE_INFO['custom_base_url_stub'].'/'.$u;
}

/**
 * Function to get a base URL for an OCF relative-URL. The situation is complex as it needs to take into account OCF multi-site-network's, locally defined theme images, and shared-installs (myocp style).
 *
 * @param  URLPATH		Short base URL we need to probe
 * @return URLPATH		The appropriate base-url
 */
function get_complex_base_url($at)
{
	return ((get_forum_base_url()!=get_base_url())?get_forum_base_url():((substr($at,0,22)=='themes/default/images/')?get_base_url():get_custom_base_url()));
}

/**
 * Get a value (either POST [u]or[/u] GET), or the default if neither can be found.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined (NULL: allow missing parameter) (false: give error on missing parameter)
 * @return ?string		The parameter value (NULL: missing)
 */
function either_param($name,$default=false)
{
	$a=__param(array_merge($_POST,$_GET),$name,$default,false,NULL);
	if ($a===NULL) return NULL;

	if ($a!==$default) // Check input field security
	{
		require_code('input_filter');
		check_input_field($name,$a);
	}
	return function_exists('ocp_url_decode_post_process')?ocp_url_decode_post_process($a):$a;
}

/**
 * Get the value of the specified POST key, if it is found, or the default otherwise.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined (NULL: allow missing parameter) (false: give error on missing parameter)
 * @param  boolean		Whether we are cleaning for HTML rather than Comcode/plain-text
 * @param  boolean		Whether to convert WYSIWYG contents to Comcode automatically
 * @return ?string		The parameter value (NULL: missing)
 */
function post_param($name,$default=false,$html=false,$conv_from_wysiwyg=true)
{
	$a=__param($_POST,$name,$default,false,true);

	if ($a===NULL) return NULL;
	if ((trim($a)=='') && ($default!='') && (array_key_exists('require__'.$name,$_POST)) && ($_POST['require__'.$name]!='0'))
	{
		require_code('failure');
		improperly_filled_in_post($name);
	}

	if (($a!='') && (addon_installed('wordfilter')))
	{
		if ($name!='password')
		{
			require_code('word_filter');
			$a=check_word_filter($a,$name);
		}
	}
	if ($a!==NULL) $a=unixify_line_format($a,NULL,$html);

	if ((isset($_POST[$name.'__is_wysiwyg'])) && ($_POST[$name.'__is_wysiwyg']=='1') && ($conv_from_wysiwyg))
	{
		if (trim($a)=='')
		{
			$a='';
		} else
		{
			require_code('comcode_from_html');
			$a=trim(semihtml_to_comcode($a));
		}
	} else
	{
		if ((substr($a,0,10)=='[semihtml]') && (substr(trim($a),-11)=='[/semihtml]'))
		{
			$_a=trim($a);
			$_a=substr($_a,10,strlen($_a)-11-10);
			if (strpos($_a,'[semihtml')===false)
			{
				require_code('comcode_from_html');
				$a=trim(semihtml_to_comcode($_a));
			}
		}
	}

	return function_exists('ocp_url_decode_post_process')?ocp_url_decode_post_process($a):$a;
}

/**
 * Get the value of the specified GET key, if it is found, or the default otherwise.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined (NULL: allow missing parameter) (false: give error on missing parameter)
 * @param  boolean		Whether to skip the security check
 * @return ?string		The parameter value (NULL: missing)
 */
function get_param($name,$default=false,$no_security=false)
{
	$a=__param($_GET,$name,$default);
	if (($a=='') && (isset($_GET['require__'.$name])) && ($default!==$a) && ($_GET['require__'.$name]!='0'))
	{
		// We didn't give some required input
		$GLOBALS['HTTP_STATUS_CODE']='400';
		if (!headers_sent())
		{
			if ((!browser_matches('ie')) && (strpos(ocp_srv('SERVER_SOFTWARE'),'IIS')===false)) header('HTTP/1.0 400 Bad Request');
		}
		warn_exit(do_lang_tempcode('IMPROPERLY_FILLED_IN'));
	}
	if ($a===$default) return $a;

	if (strpos($a,':')!==false)
		$a=function_exists('ocp_url_decode_post_process')?ocp_url_decode_post_process($a):$a;

	// Security check
	$is_url=($name=='from') || ($name=='preview_url') || ($name=='redirect') || ($name=='redirect_passon') || ($name=='url');
	if (($name!='s_message') && (!$is_url) && (!$no_security))
	{
		if (((isset($a[100])) && (strpos(substr($a,10),'::slash::slash:')===false) && (strpos(substr($a,10),'://')===false) && (strpos(substr($a,10),'::slash::slash:')===false)) || (preg_match('#\n|\000|<|(".*[=<>])|\.\./|^\s*((((j\s*a\s*v\s*a\s*)|(v\s*b\s*))?s\s*c\s*r\s*i\s*p\s*t)|(d\s*a\s*t\s*a\s*))\s*:#mi',$a)!=0))
		{
			log_hack_attack_and_exit('DODGY_GET_HACK',$name,$a);
		}
	} else
	{
		if ($is_url)
		{
			if (preg_match('#\n|\000|<|(".*[=<>])|^\s*((((j\s*a\s*v\s*a\s*)|(v\s*b\s*))?s\s*c\s*r\s*i\s*p\s*t)|(d\s*a\s*t\s*a\s*))\s*:#mi',$a)!=0)
			{
				log_hack_attack_and_exit('DODGY_GET_HACK',$name,$a);
			}

			$bu=get_base_url();
			if ((looks_like_url($a)) && (substr($a,0,strlen($bu))!=$bu) && (substr($a,0,strlen(get_forum_base_url()))!=get_forum_base_url())) // Don't allow external redirections
			{
				$a=get_base_url();
			}
		}
	}

	if ($a===NULL) return NULL;
	return $a;
}

/**
 * Helper function to load up a GET/POST parameter.
 *
 * @param  array			The array we're extracting parameters from
 * @param  string			The name of the parameter
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined (NULL: allow missing parameter) (false: give error on missing parameter)
 * @param  boolean		Whether the parameter has to be an integer
 * @param  ?boolean		Whether the parameter is a POST parameter (NULL: undetermined)
 * @return string			The value of the parameter
 */
function __param($array,$name,$default,$integer=false,$posted=false)
{
	if ((!isset($array[$name])) || (($integer) && ($array[$name]=='')))
	{
		if ($default!==false) return $default;

		require_code('failure');
		improperly_filled_in($name,$posted,$array);
	}

	$val=$array[$name];
	if (is_array($val)) $val=implode(',',$val);
	if (get_magic_quotes_gpc()) $val=stripslashes($val);

	if (($posted) && ($GLOBALS['BOOTSTRAPPING']==0) && ($GLOBALS['MICRO_AJAX_BOOTUP']==0)) // Check against fields.xml
	{
		require_code('input_filter');
		return check_posted_field($name,$val);
	}

	return $val;
}

/**
 * Do a wildcard match by converting to a regular expression.
 *
 * @param  string			The haystack
 * @param  string			The needle (a wildcard expression)
 * @param  boolean		Whether full-coverance is required
 * @return boolean		Whether we have a match
 */
function simulated_wildcard_match($context,$word,$full_cover=false)
{
	$rexp=str_replace('%','.*',str_replace('_','.',str_replace('\\?','.',str_replace('\\*','.*',preg_quote($word)))));
	if ($full_cover) $rexp='^'.$rexp.'$';

	return preg_match('#'.str_replace('#','\#',$rexp).'#i',$context)!=0;
}

/**
 * This function is the integeric partner of either_param, as it returns the value as an integer.
 * You should always use integer specified versions when inputting integers, for the added security that type validation allows. If the value is of the wrong type, it indicates a hack attempt and will be logged.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined or the empty string (NULL: allow missing parameter) (false: give error on missing parameter)
 * @return ?integer		The parameter value (NULL: not set, and NULL given as default)
 */
function either_param_integer($name,$default=false)
{
	$ret=__param(array_merge($_POST,$_GET),$name,($default===false)?$default:(($default===NULL)?'':strval($default)),true,NULL); // $_REQUEST contains cookies too, so can't use
	if (($default===NULL) && ($ret==='')) return NULL;
	$ret=trim($ret);
	if (!is_numeric($ret))
	{
		require_code('failure');
		$ret=_param_invalid($name,$ret,true);
	}
	$reti=intval($ret);
	if (($reti>2147483647) || ($reti<-2147483648))
	{
		require_code('failure');
		_param_invalid($name,NULL,true);
	}
	return $reti;
}

/**
 * This function is the integeric partner of post_param, as it returns the value as an integer.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined or the empty string (NULL: allow missing parameter) (false: give error on missing parameter)
 * @return ?integer		The parameter value (NULL: not set, and NULL given as default)
 */
function post_param_integer($name,$default=false)
{
	$ret=__param($_POST,$name,($default===false)?$default:(($default===NULL)?'':strval($default)),true,true);
	if (($default===NULL) && ($ret==='')) return NULL;
	$ret=trim($ret);
	if (!is_numeric($ret))
	{
		require_code('failure');
		$ret=_param_invalid($name,$ret,true);
	}
	if ($ret=='0') return 0;
	if ($ret=='1') return 1;
	$reti=intval($ret);
	$retf=floatval($reti);
	if (($retf>2147483647.0) || ($retf<-2147483648.0))
	{
		if ($name!='security_image')
		{
			require_code('failure');
			_param_invalid($name,NULL,true);
		}
	}
	return $reti;
}

/**
 * This function is the integeric partner of get_param, as it returns the value as an integer.
 *
 * @param  ID_TEXT		The name of the parameter to get
 * @param  ?mixed			The default value to give the parameter if the parameter value is not defined or the empty string (NULL: allow missing parameter) (false: give error on missing parameter)
 * @param  boolean		If a string is given, use the default parameter rather than giving an error (only use this if you are suffering from a parameter conflict situation between different parts of ocPortal)
 * @return ?integer		The parameter value (NULL: not set, and NULL given as default)
 */
function get_param_integer($name,$default=false,$not_string_ok=false)
{
	$m_default=($default===false)?false:(isset($default)?(($default==0)?'0':strval($default)):'');
	$ret=__param($_GET,$name,$m_default,true); // do not set $ret to mixed(), breaks bootstrapping
	if ((!isset($default)) && ($ret==='')) return NULL;
	$ret=trim($ret);
	if (!is_numeric($ret))
	{
		if (substr($ret,-1)=='/') $ret=substr($ret,0,strlen($ret)-1);
		if (!is_numeric($ret)) // Bizarre situation (bug in IIS?)
		{
			$matches=array();
			if (preg_match('#^(\d+)\#[\w]*$#',$ret,$matches)!=0)
			{
				$ret=$matches[1];
			} else
			{
				if ($not_string_ok) return $default;
				require_code('failure');
				$ret=_param_invalid($name,$ret,false);
			}
		}
	}
	if ($ret=='0') return 0;
	if ($ret=='1') return 1;
	$reti=intval($ret);
	$retf=floatval($reti);
	if (($retf>2147483647.0) || ($retf<-2147483648.0))
	{
		require_code('failure');
		_param_invalid($name,NULL,false);
	}
	return $reti;
}

/**
 * Make sure that lines are seperated by chr(10), with no chr(13)'s there at all. For Mac data, this will be a flip scenario. For Linux data this will be a null operation. For windows data this will be change from chr(13).chr(10) to just chr(10). For a realistic scenario, data could have originated on all kinds of platforms, with some editors converting, some situations being inter-platform, and general confusion. Don't make blind assumptions - use this function to clean data, then write clean code that only considers chr(10)'s.
 *
 * @param  string			The data to clean
 * @param  ?ID_TEXT		The character set it should be in. We don't do any real conversions using this, only make sure that common problems with fed ISO-8859-1 data are resolved (NULL: output character set)
 * @param  boolean		Whether we are cleaning for HTML rather than Comcode/plain-text
 * @param  boolean		Whether the file is loaded from disk (less conversion needed)
 * @return string			The cleaned data
 */
function unixify_line_format($in,$desired_charset=NULL,$html=false,$from_disk=false)
{
	if ($in=='') return $in;

	if ($desired_charset===NULL) $desired_charset=get_charset();

	$in=str_replace(array(chr(13).chr(10),'&#8298;',chr(13)),array(chr(10),'',chr(10)),$in); // &#8298; is very odd- seems to come from open office copy & paste
	if (!$from_disk)
	{
		if ($desired_charset=='ISO-8859-1') // Fix up Windows ANSI characters
		{
			$in=str_replace($GLOBALS['BAD_WORD_CHARS'],$html?$GLOBALS['FIXED_WORD_CHARS_HTML']:$GLOBALS['FIXED_WORD_CHARS'],$in);
		}
	}
	return $in;
}

/**
 * Force a Javascript file to be cached (ordinarily we can rely on this to be automated by require_javascript/javascript_tempcode).
 *
 * @param  string			The javascript file required
 * @param  ?ID_TEXT		The name of the theme (NULL: current theme)
 * @param  ?boolean		Whether to minify (NULL: read from environment)
 * @return string			The path to the javascript file in the cache (blank: no file)
 */
function javascript_enforce($j,$theme=NULL,$minify=NULL)
{
	if (get_param_integer('keep_textonly',0)==1) return '';

	if ($minify===NULL)
		$minify=(get_param_integer('keep_no_minify',0)==0);

	global $SITE_INFO;

	// Make sure the Javascript exists
	if ($theme===NULL)
		$theme=filter_naughty($GLOBALS['FORUM_DRIVER']->get_theme());
	$dir=get_custom_file_base().'/themes/'.$theme.'/templates_cached/'.filter_naughty(user_lang());
	if ((!isset($SITE_INFO['no_disk_sanity_checks'])) || ($SITE_INFO['no_disk_sanity_checks']=='0'))
	{
		if (!is_dir($dir))
		{
			if (@mkdir($dir,0777)===false)
			{
				warn_exit(do_lang_tempcode('WRITE_ERROR_DIRECTORY_REPAIR',escape_html($dir)));
			}
			fix_permissions($dir,0777);
			sync_file($dir);
		}
	}
	$js_cache_path=$dir.'/'.filter_naughty_harsh($j);
	if (!$minify) $js_cache_path.='_non_minified';
	if ((get_option('enable_https',true)=='1') && function_exists('is_page_https') && function_exists('get_zone_name') && ((tacit_https()) || is_page_https(get_zone_name(),get_page_name())))
		$js_cache_path.='_ssl';
	if (is_mobile()) $js_cache_path.='_mobile';
	$js_cache_path.='.js';

	global $CACHE_TEMPLATES;
	$support_smart_decaching=(!isset($SITE_INFO['disable_smart_decaching'])) || ($SITE_INFO['disable_smart_decaching']=='0');
	$is_cached=($CACHE_TEMPLATES || !running_script('index')/*must cache for non-index to stop getting blanked out in depended sub-script output generation and hence causing concurrency issues*/) && (@filesize($js_cache_path)!=0) && (!is_browser_decacheing()) && (!in_safe_mode());

	if (($support_smart_decaching) || (!$is_cached))
	{
		$found=find_template_place(strtoupper($j),'',$theme,'.tpl','templates');
		if ($found===NULL) return '';
		$theme=$found[0];
		$fullpath=get_custom_file_base().'/themes/'.$theme.$found[1].strtoupper($j).'.tpl';
		if (!is_file($fullpath))
			$fullpath=get_file_base().'/themes/'.$theme.$found[1].strtoupper($j).'.tpl';
		$globals_custom=str_replace('default/templates/JAVASCRIPT.tpl',filter_naughty($GLOBALS['FORUM_DRIVER']->get_theme()).'/templates_custom/JAVASCRIPT_CUSTOM_GLOBALS.tpl',$fullpath);
	}

	if ((($support_smart_decaching) && ((@(filemtime($js_cache_path)<filemtime($fullpath)) && (@filemtime($fullpath)<time())) || (@filemtime(get_file_base().'/info.php')>@filemtime($js_cache_path)) || ((is_file($globals_custom)) && (@filemtime($globals_custom)>@filemtime($js_cache_path))))) || (!$is_cached))
	{
		require_code('css_and_js');
		js_compile($j,$js_cache_path,$minify);
	}

	if (filesize($js_cache_path)==0) return '';

	return $js_cache_path;
}

/**
 * Get tempcode to tie in (to the HTML, in <head>) all the Javascript files that have been required.
 *
 * @param  ?string		Position to get Javascript for (NULL: all positions)
 * @set NULL header footer
 * @return tempcode		The tempcode to tie in the Javascript files
 */
function javascript_tempcode($position=NULL)
{
	global $JAVASCRIPTS,$JAVASCRIPT;
	$js=new ocp_tempcode();

	$minify=(get_param_integer('keep_no_minify',0)==0);
	$https=((get_option('enable_https',true)=='1') && function_exists('is_page_https') && function_exists('get_zone_name') && ((tacit_https()) || is_page_https(get_zone_name(),get_page_name())));
	$mobile=is_mobile();

	// Special merge operation for staff. In truth it's to get a better score on Google Page Speed ;)
	$to_merge=array('javascript_staff','javascript_button_occle','javascript_fractional_edit');
	$good_to_merge=true;
	foreach ($to_merge as $j)
		if (!array_key_exists($j,$JAVASCRIPTS)) $good_to_merge=false;
	if ($good_to_merge)
	{
		$j='javascript_staff___merged';
		if (!$minify) $j.='_non_minified';
		if ($https) $j.='_ssl';
		if ($mobile) $j.='_mobile';

		$theme=filter_naughty($GLOBALS['FORUM_DRIVER']->get_theme());
		$dir=get_custom_file_base().'/themes/'.$theme.'/templates_cached/'.filter_naughty(user_lang());
		$write_path=$dir.'/'.filter_naughty_harsh($j);
		$write_path.='.js';

		/*$rebuild=false;	Performance hit
		foreach ($to_merge as $j2)
		{
			$merge_from=javascript_enforce($j2);
			if (filemtime($merge_from)==time()) $rebuild=true; // Hmm, just recalculated
		}*/
		if (!is_file($write_path)/* || $rebuild*/) // Merging algorithm
		{
			$data='';
			foreach ($to_merge as $j2)
			{
				$merge_from=javascript_enforce($j2);
				if (is_file($merge_from))
				{
					$data.=unixify_line_format(file_get_contents($merge_from,FILE_TEXT));
				} else // race condition
				{
					$good_to_merge=false;
					break;
				}
			}

			if ($good_to_merge)
			{
				$myfile=@fopen($write_path,'wb') OR intelligent_write_error($write_path); // Intentionally wb to stop line ending conversions on Windows
				fwrite($myfile,$data);
				fclose($myfile);
				fix_permissions($write_path,0777);
				sync_file($write_path);
			}
		}

		if ($good_to_merge)
		{
			if ($position!='header')
				$js->attach(do_template('JAVASCRIPT_NEED',array('CODE'=>$j)));
		}
	}

	// Our main loop
	$bottom_ones=array('javascript_staff'=>1,'javascript_button_occle'=>1,'javascript_fractional_edit'=>1,'javascript_thumbnails'=>1,'javascript_button_realtime_rain'=>1);
	foreach (array_keys($JAVASCRIPTS) as $j)
	{
		if (($good_to_merge) && (in_array($j,$to_merge))) continue;

		if ($position!==NULL)
		{
			$bottom=(isset($bottom_ones[$j])); // TODO: progmattic way of saying this at point of calls
			if (($position=='header') && ($bottom)) continue;
			if (($position=='footer') && (!$bottom)) continue;
		}

		$temp=javascript_enforce($j);
		if ($temp!='')
		{
			if (!$minify) $j.='_non_minified';
			if ($https) $j.='_ssl';
			if ($mobile) $j.='_mobile';

			$js->attach(do_template('JAVASCRIPT_NEED',array('_GUID'=>'b5886d9dfc4d528b7e1b0cd6f0eb1670','CODE'=>$j)));
		}
	}
	$js->attach($JAVASCRIPT);
	return $js;
}

/**
 * Make sure that the given javascript file is loaded up.
 *
 * @param  ID_TEXT		The javascript file required
 */
function require_javascript($javascript)
{
	//if ((!array_key_exists('DONE_HEADER',$GLOBALS)) || (!$GLOBALS['DONE_HEADER']))
	//{
		if ($javascript=='javascript_forums_embed') // Has to be first
		{
			$GLOBALS['JAVASCRIPTS']=array_merge(array('javascript_forums_embed'=>1),$GLOBALS['JAVASCRIPTS']);
		} else
		{
			$GLOBALS['JAVASCRIPTS'][$javascript]=1;
		}
	/*} else	 -- Won't work, as we can't sync with the output properly
	{
		$GLOBALS['JAVASCRIPTS'][$javascript]=1;
		$file=javascript_enforce($javascript);
		$temp=do_template('JAVASCRIPT_NEED_INLINE',array('_GUID'=>'a6c907e26c5a8dd8c65f1d36a1a674a9','CODE'=>file_get_contents($file,FILE_TEXT)));
		$temp->evaluate_echo();
	}*/
}

/**
 * Force a CSS file to be cached.
 *
 * @param  string			The CSS file required
 * @param  ?ID_TEXT		The name of the theme (NULL: current theme)
 * @param  ?boolean		Whether to minify (NULL: read from environment)
 * @return string			The path to the CSS file in the cache (blank: no file)
 */
function css_enforce($c,$theme=NULL,$minify=NULL)
{
	$text_only=(get_param_integer('keep_textonly',0)==1);
	if ($text_only) $c.='_textonly';

	if ($minify===NULL)
		$minify=(get_param_integer('keep_no_minify',0)==0);

	global $SITE_INFO;

	// Make sure the CSS file exists
	if ($theme===NULL)
		$theme=@method_exists($GLOBALS['FORUM_DRIVER'],'get_theme')?$GLOBALS['FORUM_DRIVER']->get_theme():'default';
	$dir=get_custom_file_base().'/themes/'.$theme.'/templates_cached/'.filter_naughty(user_lang());
	if ((!isset($SITE_INFO['no_disk_sanity_checks'])) || ($SITE_INFO['no_disk_sanity_checks']=='0'))
	{
		if (!is_dir($dir))
		{
			if (@mkdir($dir,0777)===false)
			{
				warn_exit(do_lang_tempcode('WRITE_ERROR_DIRECTORY_REPAIR',escape_html($dir)));
			}
			fix_permissions($dir,0777);
			sync_file($dir);
		}
	}
	$css_cache_path=$dir.'/'.filter_naughty_harsh($c);
	if (!$minify) $css_cache_path.='_non_minified';
	if ((get_option('enable_https',true)=='1') && function_exists('is_page_https') && function_exists('get_zone_name') && ((tacit_https()) || is_page_https(get_zone_name(),get_page_name())))
		$css_cache_path.='_ssl';
	if (is_mobile()) $css_cache_path.='_mobile';
	$css_cache_path.='.css';

	global $CACHE_TEMPLATES;
	$support_smart_decaching=(!isset($SITE_INFO['disable_smart_decaching'])) || ($SITE_INFO['disable_smart_decaching']=='0');
	$is_cached=($CACHE_TEMPLATES || !running_script('index')/*must cache for non-index to stop getting blanked out in depended sub-script output generation and hence causing concurrency issues*/) && (@filesize($css_cache_path)!=0) && (!is_browser_decacheing()) && (!in_safe_mode());

	if (($support_smart_decaching) || (!$is_cached) || ($text_only))
	{
		$found=find_template_place($c,'',$theme,'.css','css');
		if ($found===NULL) return '';
		$theme=$found[0];
		$fullpath=get_custom_file_base().'/themes/'.$theme.$found[1].$c.'.css';
		if (!is_file($fullpath))
			$fullpath=get_file_base().'/themes/'.$theme.$found[1].$c.'.css';
		if (($text_only) && (!is_file($fullpath))) return '';
	}

	if (((!$is_cached) || (($support_smart_decaching) && (@(filemtime($css_cache_path)<filemtime($fullpath)) && (@filemtime($fullpath)<time())))))
	{
		require_code('css_and_js');
		css_compile($theme,$c,$fullpath,$css_cache_path,$minify);
	}

	if (filesize($css_cache_path)==0) return '';

	return $css_cache_path;
}

/**
 * Get tempcode to tie in (to the HTML, in <head>) all the CSS files that have been required.
 *
 * @param  boolean		Force inline CSS
 * @param  boolean		Only do global CSS
 * @param  ?string		HTML context for which we filter (minimise) any CSS we spit out as inline (NULL: none)
 * @param  ?ID_TEXT		The name of the theme (NULL: current theme)
 * @return tempcode		The tempcode to tie in the CSS files
 */
function css_tempcode($inline=false,$only_global=false,$context=NULL,$theme=NULL)
{
	global $CSSS;

	$seed='';
	if (has_specific_permission(get_member(),'view_profiling_modes'))
	{
		$seed=get_param('keep_theme_seed','');
	}

	$text_only=(get_param_integer('keep_textonly',0)==1);
	$minify=(get_param_integer('keep_no_minify',0)==0);
	$https=((get_option('enable_https',true)=='1') && function_exists('is_page_https') && function_exists('get_zone_name') && ((tacit_https()) || is_page_https(get_zone_name(),get_page_name())));
	$mobile=is_mobile();

	$css=new ocp_tempcode();
	$css_need_inline=new ocp_tempcode();
	$css_to_do=$only_global?array('global','no_cache'):array_keys($CSSS);

	foreach ($css_to_do as $c)
	{
		if ($seed!='')
		{
			$keep=symbol_tempcode('KEEP');
			$css->attach(do_template('CSS_NEED_FULL',array('URL'=>find_script('themewizard').'?type=css&show='.$c.'.css'.$keep->evaluate()),user_lang(),false,NULL,'.tpl','templates',$theme));
		}
		elseif (($c=='no_cache') || ($inline))
		{
			if (!$text_only)
			{
				$_css=do_template($c,NULL,user_lang(),false,NULL,'.css','css',$theme);
				$__css=$_css->evaluate();
				if ($context!==NULL)
				{
					$__css=filter_css($__css,$context);
				} else
				{
					$__css=str_replace('} ','}'.chr(10),preg_replace('#\s+#',' ',$__css));
				}

				if (trim($__css)!='')
					$css_need_inline->attach(do_template('CSS_NEED_INLINE',array('_GUID'=>'f5b225e080c633ffa033ec5af5aec866','CSS'=>$__css),user_lang(),false,NULL,'.tpl','templates',$theme));
			}
		} else
		{
			$temp=css_enforce($c,$theme);
			if (!$minify) $c.='_non_minified';
			if ($https) $c.='_ssl';
			if ($mobile) $c.='_mobile';
			if ($temp!='')
				$css->attach(do_template('CSS_NEED',array('_GUID'=>'ed35fac857214000f69a1551cd483096','CODE'=>$c),user_lang(),false,NULL,'.tpl','templates',$theme));
		}
	}
	$css_need_inline->attach($css);
	return $css_need_inline;
}

/**
 * Make sure that the given CSS file is loaded up.
 *
 * @param  ID_TEXT		The CSS file required
 */
function require_css($css)
{
//	if ((!array_key_exists('DONE_HEADER',$GLOBALS)) || (!$GLOBALS['DONE_HEADER']))
	{
		$GLOBALS['CSSS'][$css]=1;
	}/* else	 -- Won't work, as we can't sync with the output properly
	{
		$GLOBALS['CSSS'][$css]=1;
		$file=css_enforce($css);
		$temp=do_template('CSS_NEED_INLINE',array('_GUID'=>'b6c907e26c5a8dd8c65f1d36a1a674a9','CSS'=>file_get_contents($file,FILE_TEXT)));
		$temp->evaluate_echo();
	}*/
}

/**
 * Provides a hook for file synchronisation between mirrored servers. Called after any file creation, deletion or edit.
 *
 * @param  PATH				File/directory name to sync on (full path)
 */
function sync_file($filename)
{
	global $FILE_BASE,$_MODIFIED_FILES,$_CREATED_FILES;
	static $has_sync_script=NULL;
	if (is_null($has_sync_script)) $has_sync_script=is_file($FILE_BASE.'/data_custom/sync_script.php');
	if ((!$has_sync_script) && (!isset($_MODIFIED_FILES))) return;
	if (substr($filename,0,strlen($FILE_BASE)+1)==$FILE_BASE.'/')
	{
		$filename=substr($filename,strlen($FILE_BASE)+1);
	}
	if ($has_sync_script)
	{
		require_once($FILE_BASE.'/data_custom/sync_script.php');
		if (function_exists('master__sync_file')) master__sync_file($filename);
	}
	if (isset($_MODIFIED_FILES))
		foreach ($_MODIFIED_FILES as $i=>$x)
			if (($x==$FILE_BASE.'/'.$filename) || ($x==$filename)) unset($_MODIFIED_FILES[$i]);
	if (isset($_CREATED_FILES))
		foreach ($_CREATED_FILES as $i=>$x)
			if (($x==$FILE_BASE.'/'.$filename) || ($x==$filename)) unset($_CREATED_FILES[$i]);
}

/**
 * Provides a hook for file-move synchronisation between mirrored servers. Called after any rename or move action.
 *
 * @param  PATH				File/directory name to move from (may be full or relative path)
 * @param  PATH				File/directory name to move to (may be full or relative path)
 */
function sync_file_move($old,$new)
{
	require_code('files2');
	_sync_file_move($old,$new);
}

/**
 * Performs lots of magic to make sure data encodings are converted correctly. Input, and output too (as often stores internally in UTF or performs automatic dynamic conversions from internal to external charsets).
 * Roll on PHP6 that has a true internal UTF string model. For now, anyone who uses UTF will get some (albeit minor) imperfections from PHP's manipulations of the strings.
 *
 * @param  boolean				Whether we know we are working in UTF-8. This is the case for AJAX calls.
 */
function convert_data_encodings($known_utf8=false)
{
	global $VALID_ENCODING,$CONVERTED_ENCODING;
	$VALID_ENCODING=true;

	if ($CONVERTED_ENCODING) return; // Already done it
	if ((array_key_exists('KNOWN_UTF8',$GLOBALS)) && ($GLOBALS['KNOWN_UTF8'])) $known_utf8=true;

	$charset=get_charset();

	$done_something=false;

	// Conversion of parameters that might be in the wrong character encoding (e.g. Javascript uses UTF to make requests regardless of document encoding, so the stuff needs converting)
	//  If we don't have any PHP extensions (mbstring etc) that can perform the detection/conversion, our code will take this into account and use utf8_decode at points where it knows that it's being communicated with by Javascript.
	if (@strlen(ini_get('unicode.runtime_encoding'))>0)
	{
		@ini_set('default_charset',$charset);
		@ini_set('unicode.runtime_encoding',$charset);
		@ini_set('unicode.output_encoding',$charset);
		@ini_set('unicode.semantics','1');

		$done_something=true;
	}
	elseif (($known_utf8) && ((version_compare(phpversion(),'4.3.0')>=0) || (strtolower($charset)=='iso-8859-1')) && /*test method works...*/(will_be_unicode_neutered(serialize($_GET).serialize($_POST))) && (in_array(strtolower($charset),array('iso-8859-1','iso-8859-15','koi8-r','big5','gb2312','big5-hkscs','shift_jis','euc-jp')))) // Preferred as it will sub entities where there's no equivalent character
	{
		require_code('character_sets');

		do_environment_utf8_conversion($charset);

		$done_something=true;
	}
	elseif ((function_exists('iconv_set_encoding')) && (get_value('disable_iconv')!=='1'))
	{
		$encoding=$known_utf8?'UTF-8':$charset;
		if (@iconv_set_encoding('input_encoding',$encoding))
		{
			iconv_set_encoding('output_encoding',$charset);
			iconv_set_encoding('internal_encoding',$charset);
		} else
		{
			$VALID_ENCODING=false;
		}

		$done_something=true;
	}
	elseif ((function_exists('mb_convert_encoding')) && (get_value('disable_mbstring')!=='1'))
	{
		if (function_exists('mb_list_encodings'))
		{
			$VALID_ENCODING=in_array(strtolower($charset),array_map('strtolower',mb_list_encodings()));
		} else $VALID_ENCODING=true;

		if ($VALID_ENCODING)
		{
			$encoding=$known_utf8?'UTF-8':'';
			if ((function_exists('mb_http_input')) && ($encoding==''))
			{
				if (count($_POST)!=0)
				{
					$encoding=mb_http_input('P');
					if ((!is_string($encoding)) || ($encoding=='pass')) $encoding='';
				}
			}
			if ((function_exists('mb_http_input')) && ($encoding==''))
			{
				$encoding=mb_http_input('G');
				if ((!is_string($encoding)) || ($encoding=='pass')) $encoding='';
				if ((function_exists('mb_detect_encoding')) && ($encoding=='') && (ocp_srv('REQUEST_URI')!=''))
				{
					$encoding=mb_detect_encoding(urldecode(ocp_srv('REQUEST_URI')),$charset.',UTF-8,ISO-8859-1');
					if ((!is_string($encoding)) || ($encoding=='pass')) $encoding='';
				}
			}
			if ($encoding!='')
			{
				foreach ($_GET as $key=>$val)
				{
					if (is_string($val))
					{
						$_GET[$key]=mb_convert_encoding($val,$charset,$encoding);
					} elseif (is_array($val))
					{
						foreach ($val as $i=>$v)
						{
							$_GET[$key][$i]=mb_convert_encoding($v,$charset,$encoding);
						}
					}
				}
				foreach ($_POST as $key=>$val)
				{
					if (is_string($val))
					{
						$_POST[$key]=mb_convert_encoding($val,$charset,$encoding);
					} elseif (is_array($val))
					{
						foreach ($val as $i=>$v)
						{
							$_POST[$key][$i]=mb_convert_encoding($v,$charset,$encoding);
						}
					}
				}
			}
			if (function_exists('mb_http_output')) mb_http_output($charset);
		}

		$done_something=true;
	}
	elseif (($known_utf8) && (strtolower($charset)!='utf-8') && (strtolower($charset)!='utf8')) // This is super-easy, but it's imperfect as it assumes ISO-8859-1 -- hence our worst option
	{
		require_code('character_sets');

		do_simple_environment_utf8_conversion();

		$done_something=true;
	}

	if ($done_something) $CONVERTED_ENCODING=true;
}

/**
 * Guard for entity_utf8_decode. Checks that the data can be stripped so there is no unicode left. Either the htmlentities function must convert mechanically to entity-characters or all higher ascii character codes (which are actually unicode control codes in a unicode interpretation) that are used happen to be linked to named entities.
 *
 * @param  string					Data to check.
 * @return boolean				Whether we are good to execute entity_utf8_decode.
 */
function will_be_unicode_neutered($data)
{
	$data=@htmlentities($data,ENT_COMPAT,'UTF-8');
	if ($data=='') return false; // Some servers fail at the first step
	for ($i=0;$i<strlen($data);$i++)
	{
		if (ord($data[$i])>0x7F) return false;
	}
	return true;
}
