<?php
/**
 * <pre>
 * Invision Power Services
 * IP.Board v3.4.6
 * ipsRegistry:: Registry file controlls handling of objects needed throughout IPB
 * Last Updated: $Date: 2013-10-16 12:57:41 -0400 (Wed, 16 Oct 2013) $
 * </pre>
 *
 * @author 		$Author: AndyMillne $
 * @copyright	(c) 2001 - 2009 Invision Power Services, Inc.
 * @license		http://www.invisionpower.com/company/standards.php#license
 * @package		IP.Board
 * @link		http://www.invisionpower.com
 * @since		Tue. 17th August 2004
 * @version		$Rev: 12380 $
 */
 
/**
* Base registry class
*/
class ipsRegistry
{
	/**
	 * Holds instance of registry (singleton implementation)
	 *
	 * @var		object
	 */
	private static $instance;

	/**
	 * Registry initialized yet?
	 *
	 * @var		boolean
	 */
	private static $initiated = FALSE;

	/**
	 * SEO templates
	 *
	 * @var		array
	 */
	protected static $_seoTemplates = array();

	/**
	 * Incoming URI - used in SEO / fURL stuffs
	 *
	 * @var		string
	 */
	protected static $_uri = '';
	
	/**
	 * Flag to note incorrect FURL (no furl template match)
	 *
	 * @var		string
	 */
	protected static $_noFurlMatch = false;

	/**#@+
	 * Holds data for app / coreVariables
	 *
	 * @var		array
	 */
	protected static $_coreVariables			= array();
	protected static $_masterCoreVariables	= array();
	/**#@-*/

	/**
	 * Handles for other singletons
	 *
	 * @var		array
	 */
	protected static $handles					= array();

	/**
	 * Handles for other classes
	 *
	 * @var		array
	 */
	protected static $classes					= array();

	/**
	 * Word array
	 *
	 * @var		array
	 */
	static public $words					= array();

	/**
	 * URLs
	 *
	 * @var		array
	 */
	static public $urls						= array();

	/**
	 * Our processed URL
	 *
	 * @var		string
	 */
	static public $processed_url			= '';

	/**
	 * Server load
	 *
	 * @var		string
	 */
	static public $server_load;

	/**
	 * Do not print HTTP headers
	 *
	 * @var		bool
	 */
	static public $no_print_header			= false;

	/**#@+
	 * Query string information
	 *
	 * @var		string
	 */
	static public $query_string_safe;
	static public $query_string_real;
	static public $query_string_formatted;
	/**#@-*/

	/**#@+
	 * Version information
	 *
	 * @var		string
	 */
	static public $version         = null;
	static public $acpversion      = null;
	static public $vn_full         = '';
	static public $vn_build_date   = '';
	static public $vn_build_reason = '';
	/**#@-*/

	/**#@+
	 * Version information
	 *
	 * @var		mixed	Strings and arrays
	 */
	static public $applications        = array();
	static public $modules             = array();
	static public $modules_by_section  = array();
	static public $current_application = '';
	static public $current_module      = '';
	static public $current_section     = '';
	/**#@-*/

	/**
	 * Application class vars
	 *
	 * @var		array
	 */
	static public $app_class = array();

	/**
	 * Settings
	 *
	 * @var		array
	 */
	static public $settings	= array();

	/**
	 * Input parameters
	 *
	 * @var		array
	 */
	static public $request	= array();

	/**
	 * Template striping
	 *
	 * @var		array
	 */
	public $templateStriping = array();

	/**
	 * Initialize singleton
	 *
	 * @return	object
	 */
	static public function instance()
	{
		if ( ! self::$instance )
		{
			self::$instance = new self();
		}
		
		return self::$instance;
	}
	
	/**
	 * Enable app search
	 */
	static public $appSearch = FALSE;

	/**
	 * Ok, we need a guaranteed way to perform some clean up
	 * before __destruct() is called on other classes. For example
	 * using the DB connection isn't possible if class_db::__destruct() runs
	 * before ipsRegistry::__destruct. PHP 5 - 5.2.5 ran the __destruct functions
	 * in the order the classes are created, so we could bank on ipsRegistry::__destruct
	 * being called first as it's created first. Happy days
	 * But as of 5.2.5 the order is __reversed__ so that ipsRegistry would be called last
	 * making it all but useless.
	 * So, we use a register_shutdown_function instead which is always called before any
	 * __destruct() destructors.  Happy days again. We hope
	 *
	 * @return	@e void
	 */
	static public function __myDestruct()
	{
		foreach( self::$handles as $name => $obj )
		{
			if ( method_exists( $obj, '__myDestruct' ) )
			{
				$obj->__myDestruct();
			}
		}

		//-----------------------------------------
		// Process any pending emails to go out...
		//-----------------------------------------

		self::processMailQueue();
	}

	/**
	 * Process the mail queue
	 *
	 * @return	@e void
	 */
	static public function processMailQueue()
	{
		//-----------------------------------------
		// SET UP
		//-----------------------------------------
		
		$doReset								= 0;
		$cache									= self::$handles['caches']->getCache('systemvars');
		self::$settings['mail_queue_per_blob']	= isset(self::$settings['mail_queue_per_blob']) ? self::$settings['mail_queue_per_blob'] : 10;

		if ( ! isset( $cache['mail_queue'] ) OR $cache['mail_queue'] < 0 )
		{
			$mailQueue		     = self::DB()->buildAndFetch( array( 'select' => 'COUNT(*) as total',
																	 'from'   => 'mail_queue' ) );
			$mailQueueCount	     = intval($mailQueue['total']);
			$cache['mail_queue'] = $mailQueueCount;
			$doReset		     = 1;
		}
		else
		{
			$mailQueueCount = intval($cache['mail_queue']);
		}
		
		$sent_ids = array();

		if ( $mailQueueCount > 0 )
		{
			/* Test lock */
			$test = self::DB()->buildAndFetch( array( 'select' => 'cs_value, cs_updated',
													  'from'   => 'cache_store',
													  'where'  => 'cs_key=\'mail_processing\'' ) );
			
			if ( ! empty( $test['cs_value'] ) )
			{
				/* Check time */
				if ( empty( $test['cs_updated'] ) OR ( time() - 30 > $test['cs_updated'] ) )
				{
					/* 30 seconds, so unlock incase its stuck */
					self::DB()->replace( 'cache_store', array( 'cs_key' => 'mail_processing', 'cs_value' => 0, 'cs_updated' => time() ), array( 'cs_key' ) );
				}
				
				return false;
			}
			
			/* LOCK- Race condition when mail queue table is locked so it cannot delete mail so dupe emails are sent */
			self::DB()->replace( 'cache_store', array( 'cs_key' => 'mail_processing', 'cs_value' => 1, 'cs_updated' => time() ), array( 'cs_key' ) );
			if ( !self::DB()->getAffectedRows() )
			{
				return;
			}
			
			//-----------------------------------------
			// Get the mail stuck in the queue
			//-----------------------------------------

			self::DB()->build( array( 'select' => '*',
									  'from'   => 'mail_queue',
									  'order'  => 'mail_id ASC',
									  'limit'  => array( 0, self::$settings['mail_queue_per_blob'] ) ) );
			self::DB()->execute();

			while ( $r = self::DB()->fetch() )
			{
				$data[]     = $r;
				$sent_ids[] = $r['mail_id'];
			}

			if ( count($sent_ids) )
			{
				//-----------------------------------------
				// Delete sent mails and update count
				//-----------------------------------------

				$mailQueueCount = $mailQueueCount - count($sent_ids);

				self::DB()->delete( 'mail_queue', 'mail_id IN (' . implode( ",", $sent_ids ) . ')' );

				foreach( $data as $mail )
				{
					if ( $mail['mail_to'] and $mail['mail_subject'] and $mail['mail_content'] )
					{
						/* Clear out previous data */
						IPSText::getTextClass('email')->clearContent();
						
						/* Turn off HTML by default */
						IPSText::getTextClass('email')->setHtmlEmail( false );
						
						/* Specifically a HTML email */
						if ( $mail['mail_html_on'] )
						{
							IPSText::getTextClass('email')->setHtmlEmail( true );
							
							if ( $mail['mail_html_content'] )
							{
								IPSText::getTextClass('email')->setHtmlTemplate( $mail['mail_html_content'] );
							}
							else
							{
								IPSText::getTextClass('email')->setPlainTextTemplate( $mail['mail_content'] );
								IPSText::getTextClass('email')->buildHtmlContent( array() );
							}
						}
						
						/* We want to parse the plain text emails */
						IPSText::getTextClass('email')->setPlainTextTemplate( $mail['mail_content'] );
						IPSText::getTextClass('email')->buildPlainTextContent(false);

						IPSText::getTextClass('email')->to		= $mail['mail_to'];
						IPSText::getTextClass('email')->cc		= empty($mail['mail_cc']) ? array() : explode( ',', $mail['mail_cc'] );
						IPSText::getTextClass('email')->from	= $mail['mail_from'] ? $mail['mail_from'] : self::$settings['email_out'];
						IPSText::getTextClass('email')->subject	= $mail['mail_subject'];

						IPSText::getTextClass('email')->sendMail();

						IPSDebug::addLogMessage('Email Sent: ' . $mail['mail_to'], 'bulkemail' );
					}
				}
			}
			else
			{
				//-----------------------------------------
				// No mail after all?
				//-----------------------------------------

				$mailQueueCount = 0;
				$doReset        = 1;
			}

			//-----------------------------------------
			// Set new mail_queue count
			//-----------------------------------------

			$cache['mail_queue']	= $mailQueueCount;
		}
		
		//-----------------------------------------
		// Update cache with remaning email count
		//-----------------------------------------
		
		if ( $mailQueueCount > 0 OR $doReset )
		{
			self::$handles['caches']->setCache( 'systemvars', $cache, array( 'array' => 1, 'donow' => 1 ) );
			
			/* UNLOCK */
			self::DB()->replace( 'cache_store', array( 'cs_key' => 'mail_processing', 'cs_value' => 0, 'cs_updated' => time() ), array( 'cs_key' ) );
		}
	}

	/**
	 * Initiate the registry
	 *
	 * @return	mixed	false or void
	 */
	static public function init()
	{
		$INFO 			   = array();
		$_ipsPowerSettings = array();
		
		if ( self::$initiated === TRUE )
		{
			return FALSE;
		}
		
		self::$initiated = TRUE;
	
		/* Load static classes */
		require( IPS_ROOT_PATH . "sources/base/core.php" );/*noLibHook*/
		require( IPS_ROOT_PATH . "sources/base/ipsMember.php" );/*noLibHook*/
		
		/* Debugging notices? */
		if ( defined( 'IPS_ERROR_CAPTURE' ) AND IPS_ERROR_CAPTURE !== FALSE )
		{
			@error_reporting( E_ALL | E_NOTICE );
			@set_error_handler("IPSDebug::errorHandler");
		}
		
		/* Load core variables */
		self::_loadCoreVariables();

		/* Load config file */
		if ( is_file( DOC_IPS_ROOT_PATH . 'conf_global.php' ) )
		{
			require( DOC_IPS_ROOT_PATH . 'conf_global.php' );/*noLibHook*/

			if ( is_array( $INFO ) )
			{
				foreach( $INFO as $key => $val )
				{
					ipsRegistry::$settings[ $key ]	= str_replace( '&#092;', '\\', $val );
				}
			}
		}

		/* Load secret sauce */
		if ( is_array( $_ipsPowerSettings) )
		{
			ipsRegistry::$settings = array_merge( $_ipsPowerSettings, ipsRegistry::$settings );
		}
				
		/* Make sure we're installed */
		if ( empty( $INFO['sql_database'] ) )
		{
			/* Quick PHP version check */
			if ( ! version_compare( MIN_PHP_VERS, PHP_VERSION, '<=' ) )
			{
				print "You must be using PHP " . MIN_PHP_VERS . " or better. You are currently using: " . PHP_VERSION;
				exit();
			}

			$host = $_SERVER['HTTP_HOST'] ? $_SERVER['HTTP_HOST'] : @getenv('HTTP_HOST');
			$self = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : @getenv('PHP_SELF');

			if( IPS_AREA == 'admin' )
			{
				@header("Location: http://".$host.rtrim(dirname($self), '/\\')."/install/index.php" );
			}
			else
			{
				if( !defined('CP_DIRECTORY') )
				{
					define( 'CP_DIRECTORY', 'admin' );
				}

				@header("Location: http://".$host.rtrim(dirname($self), '/\\')."/".CP_DIRECTORY."/install/index.php" );
			}
		}

		/* Switch off dev mode you idjit */
		if ( ! defined( 'IN_DEV' ) )
		{
			define( 'IN_DEV', 0 );
		}

		/* Shell defined? */
		if ( ! defined( 'IPS_IS_SHELL' ) )
		{
			define( 'IPS_IS_SHELL', FALSE );
		}

		/* If this wasn't defined in the gateway file... */
		if ( ! defined( 'ALLOW_FURLS' ) )
		{
			define( 'ALLOW_FURLS', ( ipsRegistry::$settings['use_friendly_urls'] ) ? TRUE : FALSE );
		}
		
		if ( ! defined( 'IPS_IS_MOBILE_APP' ) )
		{
			define( 'IPS_IS_MOBILE_APP', false );
		}
		
		/**
		 * File and folder permissions
		 */
	 	if( !defined('IPS_FILE_PERMISSION') )
	 	{
			define( 'IPS_FILE_PERMISSION', 0777 );
		}

	 	if( !defined('IPS_FOLDER_PERMISSION') )
	 	{
			define( 'IPS_FOLDER_PERMISSION', 0777 );
		}
		
		/* Set it again incase a gateway turned it off */
		ipsRegistry::$settings['use_friendly_urls'] = ALLOW_FURLS;
		
		/* Start timer */
		IPSDebug::startTimer();

		/* Cookies... */
		IPSCookie::$sensitive_cookies = array( 'session_id', 'admin_session_id', 'member_id', 'pass_hash' );

		/* INIT DB */
		self::$handles['db'] = ips_DBRegistry::instance();

		/* Set DB */
		self::$handles['db']->setDB( ipsRegistry::$settings['sql_driver'] );

		/* Input set up... */
		if ( is_array( $_POST ) and count( $_POST ) )
		{
			foreach( $_POST as $key => $value )
			{
				# Skip post arrays
				if ( ! is_array( $value ) )
				{
					$_POST[ $key ] = IPSText::stripslashes( $value );
				}
			}
		}
		
    	//-----------------------------------------
    	// Clean globals, first.
    	//-----------------------------------------

		IPSLib::cleanGlobals( $_GET );
		IPSLib::cleanGlobals( $_POST );
		IPSLib::cleanGlobals( $_COOKIE );
		IPSLib::cleanGlobals( $_REQUEST );

		# GET first
		$input = IPSLib::parseIncomingRecursively( $_GET, array() );

		# Then overwrite with POST
		self::$request = IPSLib::parseIncomingRecursively( $_POST, $input );
		
		# Fix some notices
		if( !isset(self::$request['module']) )
		{
			self::$request['module']	= '';
		}
		
		if( !isset(self::$request['section']) )
		{
			self::$request['section']	= '';
		}

		# Assign request method
		self::$request['request_method'] = strtolower( my_getenv('REQUEST_METHOD') );
		
		/* Define some constants */
		define( 'IPS_IS_TASK', ( isset( self::$request['module'] ) AND self::$request['module'] == 'task' AND self::$request['app'] == 'core' ) ? TRUE : FALSE );
		define( 'IPS_IS_AJAX', ( isset( self::$request['module'] ) AND self::$request['module'] == 'ajax' ) ? TRUE : FALSE );
		
		/* First pass of app set up. Needs to be BEFORE caches and member are set up */
		self::_fUrlInit();

		self::_manageIncomingURLs();

		/* _manageIncomingURLs MUST be called first!!! */
		self::_setUpAppData();
		
		/* Load app / coreVariables.. must be called after app Data */
		self::_loadAppCoreVariables( IPS_APP_COMPONENT );

		/* Must be called after _manageIncomingURLs */
		self::$handles['db']->getDB()->setDebugMode( ( IPS_SQL_DEBUG_MODE ) ? ( isset($_GET['debug']) ? intval($_GET['debug']) : 0 ) : 0 );

		/* Get caches */
		self::$handles['caches']   = ips_CacheRegistry::instance();

		/* Make sure all is well before we proceed */
		try
		{
			self::instance()->setUpSettings();
		}
		catch( Exception $e )
		{
			/* If they're using defaults show the file */
			if( file_exists( DOC_IPS_ROOT_PATH . 'cache/skin_cache/settingsEmpty.html' ) )
			{
				print file_get_contents( DOC_IPS_ROOT_PATH . 'cache/skin_cache/settingsEmpty.html' );
			}
			else /* otherwise the cache directory setting has possibly been customised which cannot be retrieved above, show a generic error */
			{
				print "Your settings could not be read by IP.Board possibly due to importing the database incorrectly.  This is a fatal error and IP.Board cannot function while this issue persists. Please contact IPS technical support for assistance.";
			}
			exit;
		}

		/* Reset database log file paths to cache path */
		self::$handles['db']->resetLogPaths();
		
		/* Just in case they copy a space in the license... */
		ipsRegistry::$settings['ipb_reg_number'] = trim(ipsRegistry::$settings['ipb_reg_number']);
		
		/* Bah, now let's go over any input cleaning routines that have settings *sighs* */
		self::$request = IPSLib::postParseIncomingRecursively( self::$request );
		
		/* Set up dummy member class to prevent errors if cache rebuild required */
		self::$handles['member']   = ips_MemberRegistryDummy::instance();
		
		/* Build module and application caches */
		self::instance()->checkCaches();
		
		/* Set up app specific redirects. Must be called before member/sessions setup */
		self::_parseAppResets();
		
		/* Re-assign member */
		unset( self::$handles['member'] );
		self::$handles['member']   = ips_MemberRegistry::instance();
		
		/* Load other classes */
		$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_localization.php', 'class_localization' );
		self::instance()->setClass( 'class_localization', new $classToLoad( self::instance() ) );
		
		$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_public_permissions.php', 'classPublicPermissions' );
		self::instance()->setClass( 'permissions'       , new $classToLoad( self::instance() ) );

		/* Must be called before output initiated */
		self::getAppClass( IPS_APP_COMPONENT );
		
		if ( IPS_AREA == 'admin' )
		{
			require_once( IPS_ROOT_PATH . 'sources/classes/output/publicOutput.php' );/*noLibHook*/
			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/output/adminOutput.php', 'adminOutput' );
			self::instance()->setClass( 'output'           , new $classToLoad( self::instance() ) );
			
			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/classes/class_admin_functions.php", 'adminFunctions' );
			self::instance()->setClass( 'adminFunctions'   , new $classToLoad( self::instance() ) );
			
			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_permissions.php', 'class_permissions' );
			self::instance()->setClass( 'class_permissions', new $classToLoad( self::instance() ) );
			
			/* Do stuff that needs both adminFunctions and output initiated */
			self::instance()->getClass('adminFunctions')->postOutputInit();
		}
		else
		{
			$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH  . 'sources/classes/output/publicOutput.php', 'output' );
			self::instance()->setClass( 'output', new $classToLoad( self::instance(), TRUE ) );
			
			register_shutdown_function( array( 'ipsRegistry', '__myDestruct' ) );
		}
		
		/* Post member processing */
		self::$handles['member']->postOutput();
		
		/* Add SEO templates to the output system */
		self::instance()->getClass('output')->seoTemplates = self::$_seoTemplates;

		//-----------------------------------------
		// Sort out report center early, so counts
		// and cache is right
		//-----------------------------------------

		$memberData	=& self::$handles['member']->fetchMemberData();
		$memberData['showReportCenter']	= false;

		$member_group_ids	= array( $memberData['member_group_id'] );
		$member_group_ids	= array_diff( array_merge( $member_group_ids, explode( ',', $memberData['mgroup_others'] ) ), array('') );
		$report_center		= array_diff( explode( ',', ipsRegistry::$settings['report_mod_group_access'] ), array('') );

		foreach( $report_center as $groupId )
		{
			if( in_array( $groupId, $member_group_ids ) )
			{
				$memberData['showReportCenter']	= true;
				break;
			}
		}

		if( $memberData['showReportCenter'] )
		{
			$memberData['access_report_center']	= true;

			$memberCache	= $memberData['_cache'];
			$reportsCache	= self::$handles['caches']->getCache('report_cache');

			if( ! $memberCache['report_last_updated'] || $memberCache['report_last_updated'] < $reportsCache['last_updated'] )
			{
				$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir('core') . '/sources/classes/reportLibrary.php', 'reportLibrary' );
				$reports = new $classToLoad( ipsRegistry::instance() );
				$totalReports = $reports->rebuildMemberCacheArray();

				$memberCache['report_num']	= $totalReports;
				$memberData['_cache']	= $memberCache;
			}
		}
		
		/* More set up */
		self::_finalizeAppData();
		
		/* Finish fURL stuffs */
		self::_fUrlComplete();
		
		self::instance()->getClass('class_localization')->loadLanguageFile( array( 'public_global' ), 'core' );

		if ( IPS_AREA == 'admin' )
		{
			$validationStatus  = self::member()->sessionClass()->getStatus();
			$validationMessage = self::member()->sessionClass()->getMessage();
			
			if ( ( ipsRegistry::$request['module'] != 'login' ) AND ( ! $validationStatus ) )
			{
				//-----------------------------------------
				// Force log in
				//-----------------------------------------

				if ( ipsRegistry::$request['module'] == 'ajax' )
				{
					@header( "Content-type: application/json;charset=" . IPS_DOC_CHAR_SET );
					print json_encode( array(
							'error' => self::instance()->getClass('class_localization')->words['acp_sessiontimeout'],
							'__session__expired__log__out__' => 1
					  	   ) );
					exit();
				}
				/* @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/https-for-logins-r41336 */
				elseif ( ipsRegistry::$settings['logins_over_https'] && ( empty($_SERVER['HTTPS']) OR $_SERVER['HTTPS'] != 'on' ) )
				{
					/* Bug 38301 */
					ipsRegistry::getClass('output')->silentRedirect( str_replace( 'http://', 'https://', ipsRegistry::$settings['this_url'] ) );
					return;
				}
				else
				{
					ipsRegistry::$request['module'] = 'login';
					ipsRegistry::$request['core']   = 'login';
					
					$classToLoad = IPSLib::loadActionOverloader( IPSLib::getAppDir( 'core' ) . "/modules_admin/login/manualResolver.php", 'admin_core_login_manualResolver' );
					$runme       = new $classToLoad( self::instance() );
					$runme->doExecute( self::instance() );

					exit();
				}
			}
		}
		else if ( IPS_AREA == 'public' )
		{
			/* Set up member */
			self::$handles['member']->finalizePublicMember();
			
			/* Proper no cache key <update:1> */
			ipsRegistry::$settings['noCacheKey'] = md5('$Rev: 12380 $');

			/* Are we banned: Via IP Address? */
			if ( IPSMember::isBanned( 'ipAddress', self::$handles['member']->ip_address ) === TRUE )
			{
				self::instance()->getClass('output')->showError( 'you_are_banned', 2000, true, null, 403 );
			}
			
			/* Are we banned: By DB */
			if ( self::$handles['member']->getProperty('member_banned') == 1 or self::$handles['member']->getProperty( 'temp_ban' ) )
			{
				/* Don't show this message if we're viewing the warn log */
				if ( ipsRegistry::$request['module'] != 'ajax' or ipsRegistry::$request['section'] != 'warnings' )
				{
					self::getClass( 'class_localization' )->loadLanguageFile( 'public_error', 'core' );
					
					$message = '';
					if ( self::$handles['member']->getProperty('member_banned') )
					{
						$message = self::getClass( 'class_localization' )->words['no_view_board_b'];
					}
					else
					{
						$ban_arr = IPSMember::processBanEntry( self::$handles['member']->getProperty( 'temp_ban' ) );
		
						/* No longer banned */
						if( time() >= $ban_arr['date_end'] )
						{
							self::DB()->update( 'members', array( 'temp_ban' => '' ), 'member_id=' . self::$handles['member']->getProperty( 'member_id' ) );
						}
						/* Still banned */
						else
						{
							$message = sprintf( self::getClass( 'class_localization' )->words['account_susp'], self::getClass( 'class_localization' )->getDate( $ban_arr['date_end'], 'LONG', 1 ) );
						}
					}
									
					/* Get anything? */
					if ( $message )
					{
						$warn = ipsRegistry::DB()->buildAndFetch( array( 'select' => '*', 'from' => 'members_warn_logs', 'where' => 'wl_member=' . self::$handles['member']->getProperty('member_id') . ' AND wl_suspend<>0 AND wl_suspend<>-2', 'order' => 'wl_date DESC', 'limit' => 1 ) );
						
						if ( $warn['wl_id'] and ipsRegistry::$settings['warn_show_own'] )
						{
							$moredetails = "<a href='javascript:void(0);' onclick='warningPopup( this, {$warn['wl_id']} );'>". self::getClass('class_localization')->words['warnings_moreinfo'] ."</a>";
						}
					
						self::instance()->getClass('output')->showError( "{$message} {$moredetails}", 1001, true, null, 403 );
					}
				}
			}

			
			/* Check server load */
			if ( ipsRegistry::$settings['load_limit'] > 0 )
			{
				$server_load	= IPSDebug::getServerLoad();

				if ( $server_load )
				{
					$loadinfo = explode( "-", $server_load );

					if ( count($loadinfo) )
					{
						self::$server_load = $loadinfo[0];

						if ( self::$server_load > ipsRegistry::$settings['load_limit'] )
						{
							self::instance()->getClass('output')->showError( 'server_too_busy', 2001 );
						}
					}
				}
			}
			
			/* Specific Ajax Check */
			if ( IPS_IS_AJAX and ipsRegistry::$request['section'] != 'warnings' )
			{
				if ( self::$handles['member']->getProperty('g_view_board') != 1 || ( ipsRegistry::$settings['board_offline'] && ! self::$handles['member']->getProperty('g_access_offline') ) )
				{
					@header( "Content-type: application/json;charset=" . IPS_DOC_CHAR_SET );
					print json_encode( array( 'error' => 'no_permission', '__board_offline__' => 1 ) );
					exit();
				}
			}
			
			/* Other public check */
			if ( IPB_THIS_SCRIPT == 'public' and
				IPS_ENFORCE_ACCESS				   === FALSE AND (
				ipsRegistry::$request['section']   != 'login'  and
				ipsRegistry::$request['section']   != 'lostpass'  and
				IPS_IS_AJAX === FALSE and
				ipsRegistry::$request['section']   != 'rss' and
				ipsRegistry::$request['section']   != 'attach' and
				ipsRegistry::$request['module']    != 'task'   and
				ipsRegistry::$request['section']   != 'captcha' ) )
			{
				//-----------------------------------------
				// Permission to see the board?
				//-----------------------------------------
				
				if ( self::$handles['member']->getProperty('g_view_board') != 1 )
				{
					self::getClass('output')->showError( 'no_view_board', 1000, null, null, 403 );
				}
				
				//--------------------------------
				//  Is the board offline?
				//--------------------------------

				if ( ipsRegistry::$settings['board_offline'] == 1 AND ! IPS_IS_SHELL )
				{
					if ( self::$handles['member']->getProperty('g_access_offline') != 1 )
					{
						ipsRegistry::$settings['no_reg'] = 1;
						self::getClass('output')->showBoardOffline();
					}
				}

				//-----------------------------------------
				// Do we have a display name?
				//-----------------------------------------
				
				if( !( ipsRegistry::$request['section'] == 'register' AND ( ipsRegistry::$request['do'] == 'complete_login' OR ipsRegistry::$request['do'] == 'complete_login_do' ) ) )
				{
					if ( ! self::$handles['member']->getProperty('members_display_name') /*AND self::$handles['member']->getProperty('members_created_remote') - bug report 36622 */ )
					{
						$pmember = self::DB()->buildAndFetch( array( 'select' => '*', 'from' => 'members_partial', 'where' => "partial_member_id=" . self::$handles['member']->getProperty('member_id') ) );
	
						if ( !$pmember['partial_member_id'] )
						{
							$pmember = array(
								'partial_member_id' => self::$handles['member']->getProperty('member_id'),
								'partial_date'		=> time(),
								'partial_email_ok'	=> ( self::$handles['member']->getProperty('email') == self::$handles['member']->getProperty('name') . '@' . self::$handles['member']->getProperty('joined') ) ? 0 : 1
								);
							self::DB()->insert( 'members_partial', $pmember );
							$pmember['partial_id'] = self::DB()->getInsertId();
						}
						
						self::instance()->getClass('output')->silentRedirect( ipsRegistry::$settings['base_url'] . 'app=core&module=global&section=register&do=complete_login&mid='.self::$handles['member']->getProperty('member_id').'&key='.$pmember['partial_date'] );
					}
				}

				//--------------------------------
				//  Is log in enforced?
				//--------------------------------

				if ( ! ( defined( 'IPS_IS_SHELL' ) && IPS_IS_SHELL === TRUE ) && ( ( ! IPS_IS_MOBILE_APP ) && ( self::$handles['member']->getProperty('member_group_id') == ipsRegistry::$settings['guest_group'] ) and (ipsRegistry::$settings['force_login'] == 1) && !in_array( ipsRegistry::$request['section'], array( 'register', 'privacy', 'unsubscribe' ) ) ) )
				{
					if( ipsRegistry::$settings['logins_over_https'] AND ( !$_SERVER['HTTPS'] OR $_SERVER['HTTPS'] != 'on' ) )
					{
						//-----------------------------------------
						// Set referrer
						//-----------------------------------------
						
						if ( !my_getenv('HTTP_REFERER') OR stripos( my_getenv('HTTP_REFERER'), ipsRegistry::$settings['board_url'] ) === false )
						{
							$http_referrer = ( strtolower($_SERVER['HTTPS']) == 'on' ? "https://" : "http://" ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
						}
						else
						{
							$http_referrer = my_getenv('HTTP_REFERER');
						}
			
						self::instance()->getClass('output')->silentRedirect( str_replace( 'http://', 'https://', ipsRegistry::$settings['base_url'] ) . 'app=core&module=global&section=login&referer=' . urlencode($http_referrer) );
					}
					
					ipsRegistry::$request['app']		= 'core';
					ipsRegistry::$request['module'] 	= 'login';
					ipsRegistry::$request['core']   	= 'login';
					ipsRegistry::$request['referer']	= ipsRegistry::$request['referer'] ? ipsRegistry::$request['referer'] : ( strtolower($_SERVER['HTTPS']) == 'on' ? "https://" : "http://" ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];

					if( is_file( DOC_IPS_ROOT_PATH . '/' . PUBLIC_DIRECTORY . '/style_css/' . ipsRegistry::getClass('output')->skin['_csscacheid'] . '/ipb_login_register.css' ) )
					{
						ipsRegistry::getClass('output')->addToDocumentHead( 'importcss', ipsRegistry::$settings['css_base_url'] . 'style_css/' . ipsRegistry::getClass('output')->skin['_csscacheid'] . '/ipb_login_register.css' );
					}
					
					$classToLoad = IPSLib::loadActionOverloader( IPSLib::getAppDir( 'core' ) . "/modules_public/global/login.php", 'public_core_global_login' );
					$runme       = new $classToLoad( self::instance() );
					$runme->doExecute( self::instance() );
					exit;
				}
			}
			
			/* Have we entered an incorrect FURL that has no match? */
			if ( ipsRegistry::$settings['use_friendly_urls'] AND self::$_noFurlMatch === true )
			{
				self::getClass('output')->showError( 'incorrect_furl', 404, null, null, 404 );
			}
			else if( isset(ipsRegistry::$request['act']) AND ipsRegistry::$request['act'] == 'rssout' )
			{
				self::getClass('output')->showError( 'incorrect_furl', 404, null, null, 404 );
			}
			
			/* Track search engine visits */
			if ( !IPS_IS_TASK and $_SERVER['HTTP_REFERER'] )
			{
				seoTracker::track( $_SERVER['HTTP_REFERER'], self::$settings['query_string_real'], self::$handles['member']->getProperty('member_id') );
			}
		}
		
		IPSDebug::setMemoryDebugFlag( "Registry initialized" );
	}

	/**
	 * Loads application's core variables (if required)
	 *
	 * @return	@e void
	 */
	static public function _loadCoreVariables()
	{
		if ( ! ( isset( self::$_masterCoreVariables['__CLASS__'] ) AND is_object( self::$_masterCoreVariables['__CLASS__'] ) ) )
		{
			require_once( IPS_ROOT_PATH . 'extensions/coreVariables.php' );/*noLibHook*/

			/* Add handle */
			self::$_masterCoreVariables['__CLASS__'] = new coreVariables();
		}
	}

	/**
	 * Fetches apps core variable data
	 *
	 * @param	string		App dir
	 * @return	string		Core variable
	 */
	static public function _fetchCoreVariables( $type )
	{
		if ( ! isset( self::$_masterCoreVariables[ $type ] ) OR ! is_array( self::$_masterCoreVariables[ $type ] ) )
		{
			self::_loadCoreVariables();

			switch( $type )
			{
				case 'cache':
				case 'cacheload':
					$return = self::$_masterCoreVariables['__CLASS__']->fetchCaches();
					self::$_masterCoreVariables['cache']     = is_array( $return['caches'] )    ? $return['caches'] : array();
					self::$_masterCoreVariables['cacheload'] = is_array( $return['cacheload'] ) ? $return['cacheload'] : array();
				break;
				case 'redirect':
					$return = self::$_masterCoreVariables['__CLASS__']->fetchRedirects();
					self::$_masterCoreVariables['redirect'] = is_array( $return ) ? $return : array();
				break;
				case 'templates':
					$return = self::$_masterCoreVariables['__CLASS__']->fetchTemplates();
					self::$_masterCoreVariables['templates'] = is_array( $return ) ? $return : array();
				break;
				case 'bitwise':
					$return = self::$_masterCoreVariables['__CLASS__']->fetchBitwise();
					self::$_masterCoreVariables['bitwise'] = is_array( $return ) ? $return : array();
				break;
			}
		}

		return self::$_masterCoreVariables[ $type ];
	}

	/**
	 * Loads application's core variables (if required)
	 *
	 * @param	string		App key (dir.. that's directory, not duuhr)
	 * @return	@e void
	 */
	static public function _loadAppCoreVariables( $appDir )
	{
		$CACHE = $_LOAD = $_RESET = $_BITWISE = array();
		
		if ( ! isset( self::$_coreVariables[ $appDir ] ) )
		{
			$file = IPSLib::getAppDir( $appDir ) . '/extensions/coreVariables.php';
			
			if( is_file( $file ) )
			{
				require( $file );/*noLibHook*/

				/* Add caches */
				self::$_coreVariables[ $appDir ]['cache']     = ( isset($CACHE) AND is_array( $CACHE ) ) ? $CACHE : array();
				self::$_coreVariables[ $appDir ]['cacheload'] = ( isset($_LOAD) AND is_array( $_LOAD ) ) ? $_LOAD : array();

				/* Add redirect */
				if ( IPS_AREA == 'admin' )
				{
					self::$_coreVariables[ $appDir ]['redirect'] = ( isset($_RESETACP) AND is_array( $_RESETACP ) ) ? $_RESETACP : array();
				}
				else
				{
					self::$_coreVariables[ $appDir ]['redirect'] = ( isset($_RESET) AND is_array( $_RESET ) ) ? $_RESET : array();
				}

				/* Add bitwise */
				self::$_coreVariables[ $appDir ]['bitwise']  = ( isset($_BITWISE) AND is_array( $_BITWISE ) ) ? $_BITWISE : array();
			}
		}
	}

	/**
	 * Fetches apps core variable data
	 *
	 * @param	string		App dir
	 * @param	string		Type of variable to return
	 * @return	string		Core variable
	 */
	static public function _fetchAppCoreVariables( $appDir, $type )
	{
		if ( !isset(self::$_coreVariables[ $appDir ][ $type ]) OR !is_array( self::$_coreVariables[ $appDir ][ $type ] ) )
		{
			self::_loadAppCoreVariables( $appDir );
		}

		return self::$_coreVariables[ $appDir ][ $type ];
	}

	/**
	 * Grabs apps bitwise array
	 *
	 * @param	string	$appDir
	 * @return	string	Core variables
	 */
	static public function fetchBitWiseOptions( $appDir )
	{
		if ( $appDir == 'global' )
		{
			return self::_fetchCoreVariables( 'bitwise' );
		}
		else
		{
			return self::_fetchAppCoreVariables( $appDir, 'bitwise' );
		}
	}

	/**
	 * Function for storing class handles to
	 * prevent having to re-initialize them constantly
	 *
	 * @param	string		Key
	 * @param	object		Object to store
	 */
	static public function setClass( $key='', $value='' )
	{
		self::checkForInit();

		if ( ! $key OR ! $value )
		{
			throw new Exception( "Missing a key or value" );
		}
		else if ( ! is_object( $value ) )
		{
			throw new Exception( "$value is not an object" );
		}

		self::$classes[ $key ] = $value;
	}

	/**
	 * Function for retrieving class handles
	 * AUTOLOADED classes are as follows:
	 * KERNEL -> class_captcha
	 * KERNEL -> classTemplateEngine (templateEngine)
	 * CORE   -> localization class
	 *
	 * @param	string		Key
	 * @param	object		Retrieved object
	 */
	static public function getClass( $key )
	{
		/* Do some magic here to retreive common classes without
		   having to initialize them first */
		if ( ! isset( self::$classes[ $key ] ) )
		{
			switch( $key )
			{
				default:
					throw new Exception( "$key is not an object" );
				break;
				case 'class_captcha':
					$classToLoad = IPSLib::loadLibrary( IPS_KERNEL_PATH . 'classCaptcha.php', 'classCaptcha' );
					self::$classes['class_captcha'] = new $classToLoad( self::instance() );
				break;
				case 'templateEngine':
					$classToLoad = IPSLib::loadLibrary( IPS_KERNEL_PATH . 'classTemplateEngine.php', 'classTemplate' );
					self::$classes['templateEngine'] = new $classToLoad( IPS_ROOT_PATH . 'sources/template_plugins' );
				break;
				case 'cacheSimple':
					$classToLoad = IPSLib::loadLibrary(IPS_ROOT_PATH . 'sources/classes/cache/simple.php', 'classes_cache_simple' );
					self::$classes['cacheSimple'] = new $classToLoad();
				break;
				case 'class_localization':
					$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/class_localization.php', 'class_localization' );
					self::$classes['class_localization'] = new $classToLoad( self::instance() );
				break;
				case 'memberStatus':
					$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/member/status.php', 'memberStatus' );
					self::$classes['memberStatus'] = new $classToLoad( self::instance() );
				break;
				case 'IPSAdCode':
					/* Ads */
					if ( IPSLib::appIsInstalled( 'nexus' ) )
					{
						$systemvars = self::cache()->getCache('systemvars');

						if ( isset($systemvars['nexus_ads']) AND $systemvars['nexus_ads'] ) // Avoids loading the nexus_ads cache on every page load unless needed
						{
							require_once( IPSLib::getAppDir( 'nexus' ).'/sources/ads.php' );/*noLibHook*/
							$classToLoad = 'IPSAdCodeNexus';
						}
						else
						{
							self::$settings['ad_code_global_enabled'] = FALSE;
						}
					}
					
					if ( !isset( $classToLoad ) )
					{
						$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/classes/ads.php", 'IPSAdCode' );
					}
										
					/* INIT ad class */
					self::$classes['IPSAdCode'] = new $classToLoad( self::instance() );
				break;
				/* Yes I know this is forums app and not global, but it's still called so much this is a good idea */
				case 'class_forums':
					$classToLoad = IPSLib::loadLibrary( IPSLib::getAppDir( 'forums' ) . "/sources/classes/forums/class_forums.php", 'class_forums', 'forums' );
					self::$classes['class_forums'] = new $classToLoad( self::instance() );
					self::$classes['class_forums']->strip_invisible	= true;
					self::$classes['class_forums']->forumsInit();
				break;
				
				case 'classItemMarking':
					$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/itemmarking/classItemMarking.php', 'classItemMarking' );
					self::$classes['classItemMarking'] = new $classToLoad( self::instance() );
				break;
				
			}

			return self::$classes[ $key ];
		}
		else
		{
			return self::$classes[ $key ];
		}
	}

	/**
	 * Return a list of classes
	 * IN_DEV ONLY
	 *
	 * @return	mixed 		Array of items or false if IN_DEV is off
	 */
	public static function getLoadedClassesAsArray()
	{
		if ( ! IN_DEV )
		{
			return FALSE;
		}
		else
		{
			return array_keys( self::$classes );
		}
	}

	/**
	 * Shortcut function for accessing the registry
	 *
	 * @param	string	Key
	 * @return	object	Stored object
	 */
	public function __get( $key )
	{
		self::checkForInit();

		$_class = self::getClass( $key );

		if ( is_object( $_class ) )
		{
			return $_class;
		}
	}

	/**
	 * See if a class is loaded
	 *
	 * @param	string	Key
	 * @return	bool	Loaded or not
	 */
	static public function isClassLoaded( $key )
	{
		return ( isset( self::$classes[ $key ] ) ) ? TRUE : FALSE;
	}

	/**
	 * Get DB object
	 *
	 * @param	string	Key
	 * @return	object
	 */
	static public function DB( $key='' )
	{
		self::checkForInit();
		return self::$handles['db']->getDB( $key );
	}

	/**
	 * Get DB functions object
	 *
	 * @return	object
	 */
	static public function dbFunctions()
	{
		self::checkForInit();
		return self::$handles['db'];
	}

	/**
	 * Get Cache object
	 *
	 * @return	object
	 */
	static public function cache()
	{
		self::checkForInit();
		return self::$handles['caches'];
	}

	/**
	 * Get settings array
	 *
	 * @return	array
	 */
	static public function settings()
	{
		self::checkForInit();
		return ipsRegistry::$settings;
	}

	/**
	 * Fetch all the settings as a reference
	 *
	 * @return	array
	 */
	static public function &fetchSettings()
	{
		return ipsRegistry::$settings;
	}

	/**
	 * Fetch all request items as reference
	 *
	 * @return	array
	 */
	static public function &fetchRequest()
	{
		return ipsRegistry::$request;
	}

	/**
	 * Get Request array
	 *
	 * @return	array
	 */
	static public function request()
	{
		self::checkForInit();
		return ipsRegistry::$request;
	}

	/**
	 * Get Member object
	 *
	 * @return	object
	 */
	static public function member()
	{
		self::checkForInit();

		if( isset( self::$handles['member'] ) )
		{
			return self::$handles['member'];
		}
	}

	/**
	 * Get current application
	 *
	 * @return	string
	 */
	static public function getCurrentApplication()
	{
		return self::$current_application;
	}

	/**
	 * Get all applications
	 *
	 * @return	array
	 */
	public function getApplications()
	{
		return self::$applications;
	}

	/**
	 * Check to see if we've initialized
	 *
	 * @return	@e void
	 */
	static protected function checkForInit()
	{
		if ( self::$initiated !== TRUE )
		{
			throw new Exception('ipsRegistry has not been initiated. Do so by calling ipsRegistry::init()' );
		}
	}

	/**
	 * Grab the app class file
	 *
	 * @param	string		$app	Application to check for app class
	 * @return	mixed		Null if invalid or no app passed, otherwise app class object
	 */
	public static function getAppClass( $app='' )
	{
		if( !$app OR !is_string($app) )
		{
			return null;
		}

		# Load app class
		if ( ! isset( self::$app_class[ $app ] ) )
		{
			self::$app_class[ $app ] = null;

			$_file = IPSLib::getAppDir( $app ) . '/app_class_' . $app . '.php';
			$_name = 'app_class_' . $app;

			if ( is_file( $_file ) )
			{
				$classToLoad = IPSLib::loadLibrary( $_file, $_name, $app );

				self::$app_class[ $app ] = new $classToLoad( self::instance() );
			}
		}
		
		return self::$app_class[ $app ];
	}
	
	/**
	 * Fix the $_SERVER['REQUEST_URI'] variable
	 *
	 * @return	@e void
	 */
	protected static function _fixRequestUri()
	{
		/* IIS rewrite */
		if( !empty($_SERVER['HTTP_X_ORIGINAL_URL']) )		// IIS 7 with Microsoft Rewrite module
		{
			$_SERVER['REQUEST_URI']	= $_SERVER['HTTP_X_ORIGINAL_URL'];
		}
		else if( !empty($_SERVER['HTTP_X_REWRITE_URL']) )	// IIS with ISAPI_Rewrite
		{
			$_SERVER['REQUEST_URI']	= $_SERVER['HTTP_X_REWRITE_URL'];
		}
		
		/* Got it? */
		if ( ! isset( $_SERVER['REQUEST_URI'] ) )
		{
			$_SERVER['REQUEST_URI'] = $_SERVER['PHP_SELF'];
			
			if ( isset( $_SERVER['QUERY_STRING'] ) )
			{
				/* Ok, can't use bot detection as that is set up later, so this should do it
				   Basically facebook bot encodes all / to %2F after query string, so LIKE button
				   returns board index and when clicking link from FB brings up board index as URL is ?%2Ftopic%2F4314-test-digest%2F */
				if ( substr( $_SERVER['QUERY_STRING'], 0, 3 ) == '%2F' )
				{
					$_SERVER['QUERY_STRING'] = str_replace( '%2F', '/', $_SERVER['QUERY_STRING'] );
				}
				
				$_SERVER['REQUEST_URI']	.= '?' . $_SERVER['QUERY_STRING'];
			}
		}
		else
		{
			if ( isset( $_SERVER['QUERY_STRING'] ) )
			{
				/* Facebook thing */
				if ( substr( $_SERVER['QUERY_STRING'], 0, 3 ) == '%2F' )
				{
					$_SERVER['REQUEST_URI'] = str_replace( '%2F', '/', $_SERVER['REQUEST_URI'] );
				}
			}
		}
	}

	/**
	 * INIT furls
	 * Performs set up and figures out any incoming links
	 *
	 * @return	@e void
	 */
	protected static function _fUrlInit()
	{
		/**
		 * Fix request uri
		 */
		self::_fixRequestUri();
		
		if ( ipsRegistry::$settings['use_friendly_urls'] )
		{
			/* Grab and store accessing URL */
			self::$_uri = preg_replace( "/s=(&|$)/", '', str_replace( '/?', '/' . IPS_PUBLIC_SCRIPT . '?', $_SERVER['REQUEST_URI'] ) );
			
			$_urlBits = array();
			
			/* Grab FURL data... */
			if ( ! IN_DEV AND is_file( FURL_CACHE_PATH ) )
			{
				$templates = array();
				include( FURL_CACHE_PATH );/*noLibHook*/
				self::$_seoTemplates = $templates;
			}
			else
			{
				/* Attempt to write it */
				self::$_seoTemplates = IPSLib::buildFurlTemplates();
				
				try
				{
					IPSLib::cacheFurlTemplates();
				}
				catch( Exception $e )
				{
				}
			}

			if ( is_array( self::$_seoTemplates ) AND count( self::$_seoTemplates ) )
			{
				$uri = $_SERVER['REQUEST_URI']  ? $_SERVER['REQUEST_URI']  : @getenv('REQUEST_URI');
				
				/* Bug 21295 - remove known URL from test URI */
				$_t        = !empty(ipsRegistry::$settings['board_url']) ? @parse_url( ipsRegistry::$settings['board_url'] ) : @parse_url( ipsRegistry::$settings['base_url'] );
				$_toTest   = ( $_t['path'] AND $_t['path'] != '/' ) ? preg_replace( "#^{$_t['path']}#", '', $uri ) : str_replace( $_t['scheme'] . '://' . $_t['host'], '', $uri );
				$_404Check = $_toTest; //We want to retain any /index.php for this test later in this block of code
				$_toTest   = str_ireplace( array( '//' . IPS_PUBLIC_SCRIPT . '?', '/' . IPS_PUBLIC_SCRIPT . '?', '/' . IPS_PUBLIC_SCRIPT ), '', $_toTest );
				$_gotMatch = false;
				
				foreach( self::$_seoTemplates as $key => $data )
				{
					if ( empty( $data['in']['regex'] ) )
					{
						continue;
					}
					
					/* Clean up regex */
					$data['in']['regex'] = str_replace( '#{__varBlock__}', preg_quote( self::$_seoTemplates['__data__']['varBlock'], '#' ), $data['in']['regex'] );
					
					if ( preg_match( $data['in']['regex'], $_toTest, $matches ) )
					{
						$_gotMatch = true;
						
						/* Handling pages as a special thing? */
						if ( $data['isPagesMode'] )
						{
							if ( strstr( $_toTest, self::$_seoTemplates['__data__']['varPage'] ) )
							{
								preg_match( '#(' . preg_quote( self::$_seoTemplates['__data__']['varPage'], '#' ) . '(\d+?))(?:$|' . preg_quote( self::$_seoTemplates['__data__']['varBlock'], '#' ) . ')#', $_toTest, $pageMatches );
								
								if ( $pageMatches[1] )
								{
									$k = 'page';
									
									$_GET[ $k ]     = intval( $pageMatches[2] );
									$_POST[ $k ]    = intval( $pageMatches[2] );
									$_REQUEST[ $k ] = intval( $pageMatches[2] );
									$_urlBits[ $k ] = intval( $pageMatches[2] );
	
									ipsRegistry::$request[ $k ]	= intval( $pageMatches[2] );
									
									$_toTest = str_replace( $pageMatches[1], '', $_toTest );
								}
							}
							else
							{
								/* Redirect all < 3.4 links to the new sexy awesome format if need be */
								if ( self::$_seoTemplates['__data__']['varBlock'] != '/page__' && $uri && strstr( $uri, '/page__' ) )
								{
									preg_match( '#(.*)(page__.*)$#', $uri, $matches );
										
									$url      = $matches[1];
									$query    = $matches[2];
									$newQuery = '?';
										
									$data  = explode( '__', substr( $query, 6 ) );
										
									for( $i = 0, $j = count($data); $i < $j; $i++ )
									{
										$newQuery .= ( $i % 2 == 0 ? '&' : '=' ) . $data[$i];
									}
								
									/* Class output not created here */
									header( "HTTP/1.1 301 Moved Permanently" );
									header( "Location: " . $_t['scheme'] . '://' . $_t['host'] . $url . $newQuery );
									exit();
								}
							}
						}
						
						if ( is_array( $data['in']['matches'] ) )
						{
							foreach( $data['in']['matches'] as $_replace )
							{
								$k = IPSText::parseCleanKey( $_replace[0] );

								if ( strpos( $_replace[1], '$' ) !== false )
								{
									$v = IPSText::parseCleanValue( $matches[ intval( str_replace( '$', '', $_replace[1] ) ) ] );
								}
								else
								{
									$v = IPSText::parseCleanValue( $_replace[1] );
								}

								$_GET[ $k ]     = $v;
								$_POST[ $k ]    = $v;
								$_REQUEST[ $k ] = $v;
								$_urlBits[ $k ] = $v;

								ipsRegistry::$request[ $k ]	= $v;
							}
						}
						
						if ( strpos( $_toTest, self::$_seoTemplates['__data__']['varBlock'] ) !== false )
						{
							/* Changed how the input variables are parsed based on feedback in bug report 24907
							   @link http://community.invisionpower.com/tracker/issue-24907-member-list-pagination-not-work-with-checkbox
							   Input variables now preserve array depth properly as a result */
							$_parse	= substr( $_toTest, strpos( $_toTest, self::$_seoTemplates['__data__']['varBlock'] ) + strlen( self::$_seoTemplates['__data__']['varBlock'] ) );
							$_data	= explode( self::$_seoTemplates['__data__']['varSep'], $_parse );
							$_query	= '';

							foreach( $_data as $line )
							{
								list( $k, $v ) = explode( self::$_seoTemplates['__data__']['varJoin'], $line );
								
								$_query	.= $k . '=' . $v . '&';
							}

							$_data = array();
							parse_str($_query, $_data);
							$_data = IPSLib::parseIncomingRecursively($_data);
							
							foreach( $_data as $k => $v )
							{
								$_GET[ $k ]     = $v;
								$_POST[ $k ]    = $v;
								$_REQUEST[ $k ] = $v;
								$_urlBits[ $k ] = $v;

								ipsRegistry::$request[ $k ]	= $v;
							}
						}
						
						break;
					}
				}
				
				/* Check against the original request for 404 error */
				$_404checkPass	= false;
				
				if( !strstr( $_404Check, '&' ) AND !strstr( $_404Check, '=' ) AND ( strstr( $_404Check, IPS_PUBLIC_SCRIPT . '?/' ) OR !strstr( $_404Check, '.php' ) ) )
				{
					$_404checkPass	= true;
				}
				
				if( strstr( $_404Check, '/' . IPS_PUBLIC_SCRIPT ) )
				{
					if( preg_match( "#(.+?)/" . preg_quote( IPS_PUBLIC_SCRIPT ) . "#", $_404Check, $matches ) AND ! @is_file( DOC_IPS_ROOT_PATH . preg_replace( '/(.+?)\?.+/', '$1', $_404Check ) ) )
					{
						$_404checkPass	= true;
					}
				}
				
				
				/* Got a match? */
				if (
					! defined('CCS_GATEWAY_CALLED')
					AND ! defined('IPS_ENFORCE_ACCESS')
					AND ! defined('LOFIVERSION_CALLED')
					AND IPS_IS_MOBILE_APP === false
					AND IPS_DEFAULT_PUBLIC_APP == 'forums'
					AND $_gotMatch === false
					AND $_toTest
					AND $_toTest != '/'
					AND $_toTest != '/?'
					AND $_404checkPass
				  )
				{
					self::$_noFurlMatch = true;
				}
				
				//-----------------------------------------
				// If using query string furl, extract any
				// secondary query string.
				// Ex: http://localhost/index.php?/path/file.html?key=value
				// Will pull the key=value properly
				//-----------------------------------------
				
				$_qmCount = substr_count( $_toTest, '?' );
				
				/* We don't want to check for secondary query strings in the ACP */
				if ( ! IN_ACP && $_qmCount > 1 )
				{
					$_secondQueryString	= substr( $_toTest, strrpos( $_toTest, '?' ) + 1 );
					$_secondParams		= explode( '&', $_secondQueryString );
					
					if( count($_secondParams) )
					{
						foreach( $_secondParams as $_param )
						{
							list( $k, $v )	= explode( '=', $_param );
							
							$k	= IPSText::parseCleanKey( $k );
							$v	= IPSText::parseCleanValue( $v );

							$_GET[ $k ]     = $v;
							$_REQUEST[ $k ] = $v;
							$_urlBits[ $k ] = $v;

							ipsRegistry::$request[ $k ]	= $v;
						}
					}
				}

				/* Process URL bits for extra ? in them */
				/* We don't want to check for secondary query strings in the ACP */
				if ( ! IN_ACP && is_array( $_GET ) AND count( $_GET ) )
				{
					foreach( $_GET as $k => $v )
					{
						/* Nexus sends &url=.... as a parameter, which can have a ? in it, but we don't want to strip that out of the parameter or consider those values part of the input */
						if( $k == 'url' )
						{
							continue;
						}

						if ( ! is_array( $v ) AND strstr( $v, '?') )
						{
							list( $rvalue, $more ) = explode( '?', $v );

							if ( $rvalue AND $more )
							{
								//$k	= IPSText::parseCleanKey( $_k );
								//$v	= IPSText::parseCleanValue( $_v );

								/* Reset key with correct value */
								$_v = IPSText::parseCleanValue( $rvalue );
								
								$_GET[ $k ]     = $_v;
								$_REQUEST[ $k ] = $_v;
								$_urlBits[ $k ] = $_v;

								ipsRegistry::$request[ $k ]	= $_v;
								
								/* Now add in the other value */
								if ( strstr( $more, '=' ) )
								{
									list( $_k, $_v ) = explode( '=', $more );
									
									if ( $_k and $_v )
									{
										$_GET[ $_k ]     = $_v;
										$_REQUEST[ $_k ] = $_v;
										$_urlBits[ $_k ] = $_v;
			
										ipsRegistry::$request[ $_k ]	= $_v;
									}
								}
							}
						}
					}
				}
			}
			
			/* Reformat basic URL */
			if ( is_array( $_urlBits ) )
			{
				ipsRegistry::$settings['query_string_real'] = trim( http_build_query( $_urlBits ), '&' );
			}
		}
	}

	/**
	 * Complete furls
	 * Redirects if settings permit
	 *
	 * @return	@e void
	 */
	protected static function _fUrlComplete()
	{
		/* INIT */
		$_template = FALSE;
		
		if ( IPS_IS_TASK === TRUE || IPS_IS_AJAX === TRUE )
		{
			return;
		}
		
		/* www.hostname.com vs hostname.com - need to ensure we are at the correct URL for AJAX purposes */
		if ( ! defined('CCS_GATEWAY_CALLED') && ipsRegistry::$request['request_method'] == 'get' )
		{
			$_requestedHost		= $_SERVER['HTTP_HOST'];
			$_configuredHost	= parse_url( ipsRegistry::$settings['board_url'], PHP_URL_HOST );
			$uri				= $_SERVER['REQUEST_URI']  ? $_SERVER['REQUEST_URI']  : @getenv('REQUEST_URI');
			$_t					= !empty(ipsRegistry::$settings['board_url']) ? @parse_url( ipsRegistry::$settings['board_url'] ) : @parse_url( ipsRegistry::$settings['base_url'] );
			$_toTest			= ( $_t['path'] AND $_t['path'] != '/' ) ? preg_replace( "#^{$_t['path']}#", '', $uri ) : str_replace( $_t['scheme'] . '://' . $_t['host'], '', $uri );
			
			if( strpos( $_configuredHost, 'www.' ) === 0 AND strpos( $_requestedHost, 'www.' ) !== 0 )
			{
				ipsRegistry::getClass('output')->silentRedirect( ipsRegistry::$settings['board_url'] . $_toTest, '', true );
			}
			else if( strpos( $_configuredHost, 'www.' ) !== 0 AND strpos( $_requestedHost, 'www.' ) === 0 )
			{
				ipsRegistry::getClass('output')->silentRedirect( ipsRegistry::$settings['board_url'] . $_toTest, '', true );
			}
		}
		
		if ( ipsRegistry::$settings['use_friendly_urls'] AND ipsRegistry::$settings['seo_r_on'] AND is_array( self::$_seoTemplates ) AND self::$_uri )
		{
			/* SEO Tweak - if default app is forums then don't bother with act=idx nonsense */
			if ( IPS_DEFAULT_APP == 'forums' AND ipsRegistry::$request['request_method'] == 'get' AND ! ipsRegistry::$settings['actidx_override'] )
			{
				if ( ! stristr( self::$_uri, 'interface/' ) AND preg_match( '#(/index$|act=idx(\?)?)$#i', self::$_uri ) )
				{
					ipsRegistry::getClass('output')->silentRedirect( ipsRegistry::$settings['_original_base_url'], '', true, 'act=idx' );
				}
			}
									
			/* Quick check */
			if ( strpos( self::$_uri, '=' ) !== false )
			{
				/* Got a template? */
				foreach( self::$_seoTemplates as $key => $data )
				{
					//-----------------------------------------
					// This was checking against key, which was "idmshowfile" for IP.Downloads
					// but that isn't in the URL.  Changed 3.3.2010, see report 20900.
					//-----------------------------------------
					
					if ( !empty($data['out'][0]) AND preg_match( $data['out'][0], self::$_uri ) )
					{
						$_template = $key;
						break;
					}
				}
				
				/* Goddit? */
				if ( $_template !== FALSE && ! IN_ACP )
				{
					if ( self::$_seoTemplates[ $_template ]['app'] AND self::$_seoTemplates[ $_template ]['allowRedirect'] )
					{
						/* Load information file */
						if( is_file( IPSLib::getAppDir( self::$_seoTemplates[ $_template ]['app'] ) . '/extensions/furlRedirect.php' ) )
						{
							$_class = IPSLib::loadLibrary( IPSLib::getAppDir( self::$_seoTemplates[ $_template ]['app'] ) . '/extensions/furlRedirect.php', 'furlRedirect_' . self::$_seoTemplates[ $_template ]['app'], self::$_seoTemplates[ $_template ]['app'] );
							$_furl  = new $_class( ipsRegistry::instance() );

							$_furl->setKeyByUri( self::$_uri );
							$_seoTitle = $_furl->fetchSeoTitle();

							if ( $_seoTitle && empty( ipsRegistry::$request['debug'] ) )
							{
								/* redirect... */
								// Per recommendations, removing user option and forcing 301 redirect
								//$_send301 = ( ipsRegistry::$settings['seo_r301'] ) ? TRUE : FALSE;

								ipsRegistry::getClass('output')->silentRedirect( self::$_uri, $_seoTitle, true, $_template );
							}
						}
					}
				}
			}
		}
	}

	/**
	 * Rebuild URL data from incoming sources.
	 * Called after _fUrlInit
	 *
	 * @return	@e void
	 */
	protected static function _manageIncomingURLs()
	{
		//-----------------------------------------
		// Build master load requests
		//-----------------------------------------

		$_RESET = self::_fetchCoreVariables( 'redirect' );

		if ( is_array( $_RESET ) )
		{
			foreach( $_RESET as $k => $v )
			{
				$_GET[ $k ]  = $v;
				$_POST[ $k ] = $v;
				$_REQUEST[ $k ] = $v;
				ipsRegistry::$request[ $k ]	= $v;
			}
		}
	}

	/**
	 * Set up request redirect stuff
	 *
	 * @return	@e void
	 * @author	MattMecham
	 */
	protected static function _setUpAppData()
	{
		# Finalize the app component constants
		$_REQUEST['app'] = str_replace( '&module', '', $_REQUEST['app'] );
		$_appcomponent = preg_replace( '/[^a-zA-Z0-9\-\_]/', "" , ( isset( $_REQUEST['app'] ) && trim( (string) $_REQUEST['app'] ) ? $_REQUEST['app'] : IPS_DEFAULT_APP ) );
		
		/**
		 * @link	http://community.invisionpower.com/tracker/issue-21825-urls/
		 */
		if( is_array($_appcomponent) )
		{
			$_appcomponent	= array_shift($_appcomponent);
		}

		define( 'IPS_APP_COMPONENT', ( $_appcomponent ) ? $_appcomponent : IPS_DEFAULT_APP );
	}

	/**
	 * Parse any current app redirects
	 *
	 * @return void
	 */
	protected static function _parseAppResets()
	{
		//-----------------------------------------
		// Build app data and APP specific load requests
		//-----------------------------------------

		$_RESET = self::_fetchAppCoreVariables( IPS_APP_COMPONENT, 'redirect' );
		
		if ( is_array( $_RESET ) AND count( $_RESET ) )
		{
			foreach( $_RESET as $k => $v )
			{
				$_GET[ $k ]  = $v;
				
				self::$request[ $k ]	= $v;
			}
		}
	}

	/**
	 * Set up application data, etc.
	 *
	 * @return	@e void
	 * @author	MattMecham
	 */
	protected static function _finalizeAppData()
	{
		//-----------------------------------------
		// Run the app class post output func
		//-----------------------------------------

		$app_class	= self::getAppClass( IPS_APP_COMPONENT );

		if ( $app_class AND method_exists( $app_class, 'afterOutputInit' ) )
		{
			$app_class->afterOutputInit( self::instance() );
		}
		
		//-----------------------------------------
		// Version numbers
		//-----------------------------------------

		if ( strpos( self::$acpversion , '.' ) !== false )
		{
			list( $n, $b, $r ) = explode( ".", self::$acpversion );
		}
		else
		{
			$n = $b = $r = '';
		}

		self::$vn_full         = self::$acpversion;
		self::$acpversion      = $n;
		self::$vn_build_date   = $b;
		self::$vn_build_reason = $r;

		# Figure out default modules, etc
		$_module = IPSText::alphanumericalClean( ipsRegistry::$request['module'] );
		$_first  = '';

		//-----------------------------------------
		// Set up some defaults
		//-----------------------------------------

		ipsRegistry::$current_application  = IPS_APP_COMPONENT;

		if ( IPS_AREA == 'admin' )
		{
			//-----------------------------------------
			// Application: Do we have permission?
			//-----------------------------------------
			
			if( ipsRegistry::$request['module'] != 'login' )
			{
				ipsRegistry::getClass('class_permissions')->return = 0;
				ipsRegistry::getClass('class_permissions')->checkForAppAccess( IPS_APP_COMPONENT );
				ipsRegistry::getClass('class_permissions')->return = 1;
			}

			//-----------------------------------------
			// Got a module
			//-----------------------------------------

			if ( ipsRegistry::$request['module'] == 'ajax' )
			{
				$_module = 'ajax';
			}
			else
			{
				$fakeApps	= ipsRegistry::getClass('output')->fetchFakeApps();

				foreach( ipsRegistry::$modules as $app => $items )
				{
					if ( is_array( $items ) )
					{
						foreach( $items as $data )
						{
							if ( $data['sys_module_admin'] AND ( $data['sys_module_application'] == ipsRegistry::$current_application ) )
							{
								if ( ! $_first )
								{
									# Got permission for this one?
									ipsRegistry::getClass('class_permissions')->return = 1;

									if ( ipsRegistry::getClass('class_permissions')->checkForModuleAccess( $data['sys_module_application'], $data['sys_module_key'] ) === TRUE )
									{
										if ( is_dir( IPSLib::getAppDir( $data['sys_module_application'] ) . "/modules_admin/{$data['sys_module_key']}" ) === TRUE )
										{
											$isFakeApp	= false;

											foreach( $fakeApps as $tab => $apps )
											{
												foreach( $apps as $thisApp )
												{
													if( $thisApp['app'] == $app AND $thisApp['module'] == $data['sys_module_key'] )
													{
														$isFakeApp	= true;
													}
												}
											}

											if( !$isFakeApp )
											{
												$_first = $data['sys_module_key'];
											}
										}
									}

									ipsRegistry::getClass('class_permissions')->return = 0;
								}

								if ( ipsRegistry::$request['module'] == $data['sys_module_key'] )
								{
									$_module = $data['sys_module_key'];
									break;
								}
							}
						}
					}
				}
			}
		}
		else
		{
			//-----------------------------------------
			// Got a module?
			//-----------------------------------------

			if ( $_module == 'ajax' )
			{
				$_module = 'ajax';
			}
			else
			{
				foreach( ipsRegistry::$modules as $app => $items )
				{
					if ( is_array( $items ) )
					{
						foreach( $items as $data )
						{
							if ( ! $data['sys_module_admin'] AND ( $data['sys_module_application'] == ipsRegistry::$current_application ) )
							{
								if ( ! $_first )
								{
									$_first = $data['sys_module_key'];
								}

								if ( $_module == $data['sys_module_key'] )
								{
									$_module = $data['sys_module_key'];
									break;
								}
							}
						}
					}
				}
			}
		}

		//-----------------------------------------
		// Finish off...
		//-----------------------------------------

		ipsRegistry::$current_module  = ( $_module ) ? $_module : $_first;
		ipsRegistry::$current_section = ( ipsRegistry::$request['section'] ) ? ipsRegistry::$request['section'] : '';
		
		/* Clean */
		ipsRegistry::$current_module = IPSText::alphanumericalClean( ipsRegistry::$current_module );
		ipsRegistry::$current_section = IPSText::alphanumericalClean( ipsRegistry::$current_section );
		
		IPSDebug::addMessage( "Setting current module to: " . ipsRegistry::$current_module . " and current section to: " . ipsRegistry::$current_section );

		if ( IPS_AREA == 'admin' )
		{
			//-----------------------------------------
			// Module: Do we have permission?
			//-----------------------------------------

			ipsRegistry::getClass('class_permissions')->return = 0;
			ipsRegistry::getClass('class_permissions')->checkForModuleAccess( ipsRegistry::$current_application, ipsRegistry::$current_module );
			ipsRegistry::getClass('class_permissions')->return = 1;
		}
	}

	/**
	 * Set up settings
	 *
	 * @return	@e void
	 * @author	MattMecham
	 */
	protected function setUpSettings()
	{
		$settings_cache = self::$handles['caches']->getCache('settings');

		if ( ! is_array( $settings_cache ) OR ! count( $settings_cache ) )
		{
			throw new Exception( "Could not initiate the registry, the settings cache is empty or missing" );
		}

		foreach( $settings_cache as $k => $v )
		{
			ipsRegistry::$settings[$k] = str_replace( '&#092;', '\\', $v );
		}

		//-----------------------------------------
		// Back up base URL
		//-----------------------------------------

		ipsRegistry::$settings['base_url']           = !empty(ipsRegistry::$settings['board_url']) ? ipsRegistry::$settings['board_url'] : ipsRegistry::$settings['base_url'];
		ipsRegistry::$settings['board_url']          = !empty(ipsRegistry::$settings['board_url']) ? ipsRegistry::$settings['board_url'] : ipsRegistry::$settings['base_url'];
		ipsRegistry::$settings['_original_base_url'] = ipsRegistry::$settings['base_url'];
		
		//-----------------------------------------
		// Fetch correct current URL
		//-----------------------------------------
		
		ipsRegistry::$settings['this_url']		 	= ( strtolower($_SERVER['HTTPS']) == 'on' ? "https://" : "http://" ) . my_getenv('HTTP_HOST') . str_replace( array( '<', '>', '(', ')' ), '-', my_getenv('REQUEST_URI') );
		
		//-----------------------------------------
		// Make a safe query string (we build the query string in furlinit)
		//-----------------------------------------

		ipsRegistry::$settings['query_string_safe'] = str_replace( '&amp;amp;', '&amp;', IPSText::parseCleanValue( urldecode( !empty(ipsRegistry::$settings['query_string_real']) ? ipsRegistry::$settings['query_string_real'] : my_getenv('QUERY_STRING') ) ) );
		ipsRegistry::$settings['query_string_real'] = str_replace( '&amp;'    , '&'    , ipsRegistry::$settings['query_string_safe'] );

		//-----------------------------------------
		// Format it..
		//-----------------------------------------

		ipsRegistry::$settings['query_string_formatted'] = str_replace( ipsRegistry::$settings['board_url'] . '/index.'.ipsRegistry::$settings['php_ext'].'?', '', ipsRegistry::$settings['query_string_safe'] );
		ipsRegistry::$settings['query_string_formatted'] = preg_replace( "#s=([a-z0-9]){32}#", '', ipsRegistry::$settings['query_string_formatted'] );
		ipsRegistry::$settings['query_string_formatted'] = preg_replace( '#settingNewSkin=\d+?($|&)#', '', ipsRegistry::$settings['query_string_formatted'] );
		ipsRegistry::$settings['query_string_formatted'] = preg_replace( '#langid=\d+?($|&)#', '', ipsRegistry::$settings['query_string_formatted'] );

		//-----------------------------------------
		// Default settings
		//-----------------------------------------

		ipsRegistry::$settings['_admin_link'] = ipsRegistry::$settings['base_url'] . '/' . CP_DIRECTORY . '/index.php';
		if ( ipsRegistry::$settings['logins_over_https'] )
		{
			ipsRegistry::$settings['_admin_link'] = str_replace( 'http://', 'https://', ipsRegistry::$settings['_admin_link'] );
		}
		
		ipsRegistry::$settings['max_user_name_length'] = ipsRegistry::$settings['max_user_name_length'] ? ipsRegistry::$settings['max_user_name_length'] : 26;

		# Upload
		ipsRegistry::$settings['upload_dir'] = ipsRegistry::$settings['upload_dir'] ? ipsRegistry::$settings['upload_dir'] : DOC_IPS_ROOT_PATH . 'uploads';
		ipsRegistry::$settings['upload_url'] = ipsRegistry::$settings['upload_url'] ? ipsRegistry::$settings['upload_url'] : ipsRegistry::$settings['base_url'] . '/uploads';

		# Char set
		ipsRegistry::$settings['gb_char_set'] = ipsRegistry::$settings['gb_char_set'] ? ipsRegistry::$settings['gb_char_set'] : 'UTF-8';

		if( ! defined( 'IPS_DOC_CHAR_SET' ) )
		{
			define( 'IPS_DOC_CHAR_SET', strtoupper( ipsRegistry::$settings['gb_char_set'] ) );
			
			if( function_exists('mb_internal_encoding') AND function_exists('mb_list_encodings') )
			{
				$valid_encodings	= array();
				$valid_encodings	= mb_list_encodings();
	
				if ( count($valid_encodings) )
				{
					if ( in_array( strtoupper(IPS_DOC_CHAR_SET), $valid_encodings ) )
					{
						mb_internal_encoding( strtoupper(IPS_DOC_CHAR_SET) );
					}
				}
			}
		}
		
		/* Save us a lot of bother later */
		define( 'IPS_IS_UTF8', ( IPS_DOC_CHAR_SET == 'UTF-8' ) ? true : false );
		
		# Define cache path
		define( 'IPS_CACHE_PATH', ( ! empty( ipsRegistry::$settings['ipb_cache_path'] ) ) ? ipsRegistry::$settings['ipb_cache_path'] : DOC_IPS_ROOT_PATH );

		if( !defined( 'HTML_PURIFIER_PATH' ) )
		{
			define( 'HTML_PURIFIER_PATH', IPS_CACHE_PATH );
		}
		
		# Make sure ENFORCE ACCESS is defined
		if ( ! defined( 'IPS_ENFORCE_ACCESS' ) )
		{
			define( 'IPS_ENFORCE_ACCESS', FALSE );
		}
		
		/* Set up default status update settings */
		ipsRegistry::$settings['su_max_chars']   = ( isset( ipsRegistry::$settings['su_max_chars'] ) )   ? ipsRegistry::$settings['su_max_chars']   : 500;
		ipsRegistry::$settings['su_max_replies'] = ( isset( ipsRegistry::$settings['su_max_replies'] ) ) ? ipsRegistry::$settings['su_max_replies'] : 100;
		ipsRegistry::$settings['su_enabled']	 = ( isset( ipsRegistry::$settings['su_enabled'] ) )     ? ipsRegistry::$settings['su_enabled']     : 1;
		
		# If htaccess mod rewrite is on, enforce path_info usage
		
		ipsRegistry::$settings['url_type'] = ( ipsRegistry::$settings['htaccess_mod_rewrite'] ) ? 'path_info' : ipsRegistry::$settings['url_type'];

		# Facebook receiver location
		ipsRegistry::$settings['fbc_xdlocation'] = ipsRegistry::$settings['_original_base_url'] . '/interface/facebook/xd_receiver.php';
		ipsRegistry::$settings['fb_req_perms']   = 'email,publish_stream,read_stream';
		ipsRegistry::$settings['fb_locale']      = ( ipsRegistry::$settings['fb_locale'] ) ? ipsRegistry::$settings['fb_locale'] : 'en_US';
		
		# If we can't write to our /cache/tmp directory, turn off minify., It would still work, but be horribly inefficient.
		if ( ipsRegistry::$settings['use_minify'] AND ! is_writeable( IPS_CACHE_PATH . 'cache/tmp' ) )
		{
			ipsRegistry::$settings['_use_minify'] = ipsRegistry::$settings['use_minify'];
			ipsRegistry::$settings['use_minify']  = 0;
		}
		# Also turn it off if set_include_path is not enabled
		if ( ipsRegistry::$settings['use_minify'] and !function_exists( 'set_include_path' ) )
		{
			ipsRegistry::$settings['use_minify']  = 0;
		}
		
		# If we've turned on 301 redirects, then ensure headers are printed
		if ( ! ipsRegistry::$settings['print_headers'] )
		{
			if ( ipsRegistry::$settings['use_friendly_urls'] AND ipsRegistry::$settings['seo_r_on'] )
			{
				ipsRegistry::$settings['print_headers'] = 1;
			}
		}
		
		# If rep is set to likes, force show points or cache never builds
		if ( ipsRegistry::$settings['reputation_point_types'] == 'like' )
		{
			ipsRegistry::$settings['reputation_show_content'] = 1;
		}
		
		# URL shortener
		if ( ! defined( 'IPS_URL_SHORTEN_SERVICE') )
		{
			define( 'IPS_URL_SHORTEN_SERVICE', 'bitly' );
		}
	}

	/**
	 * Check caches
	 *
	 * @return	@e void
	 * @author	MattMecham
	 */
	protected function checkCaches()
	{
		//-----------------------------------------
		// Check app cache data
		//-----------------------------------------

		# Apps
		$app_cache = self::$handles['caches']->getCache('app_cache');

		# Modules...
		self::$modules = self::$handles['caches']->getCache('module_cache');

		if ( ! count( $app_cache ) OR ! count( self::$modules ) )
		{
			self::$handles['caches']->rebuildCache( 'app_cache', 'global' );
			self::$handles['caches']->rebuildCache( 'module_cache', 'global' );

			$app_cache     = self::$handles['caches']->getCache('app_cache');
			self::$modules = self::$handles['caches']->getCache('module_cache');
		}

		//-----------------------------------------
		// Build app data and APP specific load requests
		//-----------------------------------------

		foreach( $app_cache as $_app_dir => $_app_data )
		{
			if ( !IPS_IS_TASK AND ( IPS_AREA == 'public' && ! $_app_data['app_public_title'] ) )
			{
				continue;
			}

			$_app_data['app_public_title'] = ( $_app_data['app_public_title'] ) ? $_app_data['app_public_title'] : $_app_data['app_title'];
			self::$applications[ $_app_dir ] = $_app_data;
		}
		
		/* Sort by position */
		uasort( self::$applications, 'ipsRegistry::_appPositionSort' );
		
		# Modules by section...
		foreach( self::$modules as $_app_dir => $_modules )
		{
			foreach( $_modules as $_data )
			{
				self::$modules_by_section[ $_app_dir ][ $_data['sys_module_key'] ] = $_data;
			}
		}

		//-----------------------------------------
		// System vars and group
		//-----------------------------------------

		$systemvars_cache = self::$handles['caches']->getCache('systemvars');

		if ( ! isset( $systemvars_cache ) OR ! isset( $systemvars_cache['task_next_run']) )
		{
			$update 						= array( 'task_next_run' => time() );
			$update['loadlimit'] 			= ( $systemvars_cache['loadlimit'] )           ? $systemvars_cache['loadlimit'] : 0;
			$update['mail_queue'] 			= ( $systemvars_cache['mail_queue'] )          ? $systemvars_cache['mail_queue'] : 0;
			$update['last_virus_check'] 	= ( $systemvars_cache['last_virus_check'] )    ? $systemvars_cache['last_virus_check'] : 0;
			$update['last_deepscan_check'] 	= ( $systemvars_cache['last_deepscan_check'] ) ? $systemvars_cache['last_deepscan_check'] : 0;

			self::$handles['caches']->setCache( 'systemvars', $update, array( 'donow' => 1, 'array' => 1 ) );
		}

		$group_cache = self::$handles['caches']->getCache('group_cache');

		if ( ! is_array( $group_cache ) OR ! count( $group_cache ) )
		{
			$this->cache()->rebuildCache( 'group_cache', 'global' );
		}

		//-----------------------------------------
		// User agent caches
		//-----------------------------------------

		$useragent_cache = $this->cache()->getCache('useragents');

		if ( ! is_array( $useragent_cache ) OR ! count( $useragent_cache ) )
		{
			$this->cache()->rebuildCache( 'useragents', 'global' );
		}

		//-----------------------------------------
		// Output formats
		//-----------------------------------------

		$outputformats_cache = $this->cache()->getCache('outputformats');

		if ( ! is_array( $outputformats_cache ) OR ! count( $outputformats_cache ) )
		{
			$this->cache()->rebuildCache( 'outputformats', 'global' );
		}

		//-----------------------------------------
		// Hooks cache
		//-----------------------------------------

		if ( $this->cache()->exists('hooks') !== TRUE )
		{
			$this->cache()->rebuildCache( 'hooks', 'global' );
		}
		
		//-----------------------------------------
		// Version numbers
		//-----------------------------------------

		$version_numbers = $this->cache()->getCache('vnums');

		if ( ! is_array( $version_numbers ) OR ! count( $version_numbers ) )
		{
			$this->cache()->rebuildCache( 'vnums', 'global' );
			
			$version_numbers = $this->cache()->getCache('vnums');
		}
		
		/* Set them */
		if ( ! defined('IPB_VERSION' ) )
		{
			define( 'IPB_VERSION'     , $version_numbers['human'] );
			ipsRegistry::$version	= IPB_VERSION;
		}
		
		if ( ! defined('IPB_LONG_VERSION') )
		{
			define( 'IPB_LONG_VERSION', $version_numbers['long'] );
			ipsRegistry::$acpversion	= IPB_LONG_VERSION;
			ipsRegistry::$vn_full		= IPB_LONG_VERSION;
		}
		
		//-----------------------------------------
		// Any to rebuild?
		//-----------------------------------------
		
		if( count($this->cache()->fetchRebuildList()) )
		{
			foreach( $this->cache()->fetchRebuildList() as $_toRebuild )
			{
				$this->cache()->rebuildCache( $_toRebuild );
			}
		}
	}
	
	/**
	 * Sort by position
	 *
	 * @return	integer
	 */
	protected static function _appPositionSort( $a, $b )
	{
		$a['app_position'] = ( isset( $a['app_position'] ) ) ? $a['app_position'] : 0;
		$b['app_position'] = ( isset( $b['app_position'] ) ) ? $b['app_position'] : 0;
		
		return ( $a['app_position'] > $b['app_position'] ) ? +1 : -1;
	}
}


/**
* Base Database class
*/
class ips_DBRegistry
{
	/**
	 * Database instance
	 *
	 * @var		object
	 */
	private static $instance;

	/**
	 * Generic data storage for each class that extends ips_base_Registry
	 *
	 * @var		array
	 */
	protected static $data_store = array();

	/**
	 * Variables array
	 *
	 * @var		array
	 */
	protected static $vars = array();

	/**
	 * DB Objects
	 *
	 * @var		array
	 */
	protected static $dbObjects = array();

	/**
	 * DB Prefixes
	 *
	 * @var		array
	 */
	protected static $dbPrefixes = array( '__default__' => '' );

	/**
	 * DB Drivers
	 *
	 * @var		array
	 */
	protected static $dbDrivers = array( '__default__' => '' );

	/**
	 * DB key
	 *
	 * @var		string
	 */
	protected static $defaultKey = '__default__';
	
	/**
	 * Cache files tried to load
	 *
	 * @var		array
	 */
	protected static $_queryFilesTriedToLoad = array();
	
	/**
	 * Cache file parsed names
	 *
	 * @var		array
	 */
	protected static $_queryFilesNames = array();

	/**
	 * Loaded query files
	 * Initialize singleton
	 *
	 * @return	object
	 */
	static public function instance()
	{
		if ( ! self::$instance )
		{
			self::$instance = new self();
		}

		return self::$instance;
	}

	/**
	 * Fetch query file, if available
	 *
	 * @param	string		'public' or 'admin'
	 * @param	string		App dir
	 * @param	string		Key
	 * @return	boolean
	 */
	static public function loadQueryFile( $where, $app, $key='' )
	{
		$key    = ( $key ) ? $key : self::$defaultKey;
		$where  = ( $where == 'admin' ) ? 'admin' : 'public';
		$driver = self::getDriverType( $key );
		
		/* Already tried to load? */
		if ( isset( self::$_queryFilesTriedToLoad[ $app . '-' . $driver ] ) )
		{
			return;
		}
		else
		{
			self::$_queryFilesTriedToLoad[ $app . '-' . $driver ] = 1;
		}
		
		$file   = self::fetchQueryFileName( $where, $app, $key );
		$class  = self::fetchQueryFileClassName( $where, $app, $key );
				
		IPSDebug::addMessage( "* Checking for query cache file: " . $file );
				
		if ( is_file( $file ) )
		{
			self::getDB( $key )->loadCacheFile( $file, $class, TRUE );
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}
	
	/**
	 * Fetch query file, if available
	 *
	 * @param	string		'public' or 'admin'
	 * @param	string		App dir
	 * @param	string		Key
	 * @return	File name
	 */
	static public function fetchQueryFileClassName( $where, $app, $key='' )
	{
		$key    = ( $key ) ? $key : self::$defaultKey;
		$where  = ( $where == 'admin' ) ? 'admin' : 'public';
		
		return $where . '_' . $app . '_sql_queries';
	}
	
	/**
	 * Fetch query file, if available
	 *
	 * @param	string		'public' or 'admin'
	 * @param	string		App dir
	 * @param	string		Key
	 * @return	File name
	 */
	static public function fetchQueryFileName( $where, $app, $key='' )
	{
		$key    = ( $key ) ? $key : self::$defaultKey;
		$where  = ( $where == 'admin' ) ? 'admin' : 'public';
		$driver = self::getDriverType( $key );
		
		/* Already tried to load? */
		if ( ! isset( self::$_queryFilesNames[ $app . '-' . $driver ] ) )
		{
			self::$_queryFilesNames[ $app . '-' . $driver ] = IPSLib::getAppDir( $app ) . '/sql/' . $driver . '_' . $where . '.php';
		}
		
		return self::$_queryFilesNames[ $app . '-' . $driver ];
	}
	
	/**
	 * Get DB reference based on key
	 *
	 * @param	string	Key
	 * @return	object
	 */
	static public function getDB( $key='' )
	{
		$key = ( $key ) ? $key : self::$defaultKey;

		return self::$data_store[ $key ];
	}

	/**
	 * Reset log file paths in case they've changed
	 *
	 * @return void
	 */
	static public function resetLogPaths()
	{
		# Error log
		self::getDB()->obj['error_log']				= IPS_CACHE_PATH . 'cache/sql_error_log_'.date('m_d_y').'.cgi';
		
		# Debug log - Don't use this on a production board!
		self::getDB()->obj['debug_log']				= IPS_CACHE_PATH . 'cache/sql_debug_log_'.date('m_d_y').'.cgi';
		
		# Bad log - Don't use this on a production board!
		self::getDB()->obj['bad_log']				= IPS_CACHE_PATH . 'cache/sql_bad_log_'.date('m_d_y').'.cgi';
		
		# Slow log - Don't use this on a production board!
		self::getDB()->obj['slow_log']				= IPS_CACHE_PATH . 'cache/sql_slow_log_'.date('m_d_y').'.cgi';
	}

	/**
	 * Get the prefix for this connection
	 *
	 * @param	string	Key
	 * @return	string	Prefix
	 */
	static public function getPrefix( $key='' )
	{
		$key = ( $key ) ? $key : self::$defaultKey;

		if ( isset(self::$dbPrefixes[$key]) )
		{
			return self::$dbPrefixes[ $key ];
		}
		else
		{
			throw new Exception( "Database connection key $key does not exist" );
		}
	}

	/**
	 * Returns the driver type currently in use, ex: mysql
	 *
	 * @param	string  $key  Key of the db connection to check
	 * @return	string
	 */
	static public function getDriverType( $key= '' )
	{
		/* Set Key */
		$key = ( $key ) ? $key : self::$defaultKey;

		return self::$dbDrivers[ $key ];
	}
	
	/**
	 * @param string $key
	 * @return string
	 */
	static public function getDatabaseName( $key='' )
	{
		/* Set Key */
		$key = ( $key ) ? $key : self::$defaultKey;
		
		return self::$dbObjects[ $key ]->obj['sql_database'];
	}

	/**
	 * Set a database object
	 *
	 * @param	string	DB driver
	 * @param	string	Key
	 * @param	array   Array of settings( sql_database, sql_user, sql_pass, sql_host, sql_charset, sql_tbl_prefix ) and any other connection vars
	 * @return	@e void
	 */
	static public function setDB( $db_driver, $key='', $settings=array() )
	{
		/* INIT */
		$db_driver        = strtolower( $db_driver );
		$query_file_extra = ( IPS_AREA == 'admin' ) ? '_admin' : '';
		$key              = ( $key ) ? $key : self::$defaultKey;
		$classname		  =  "db_driver_" . $db_driver;
		
		/* Fix up settings */
		foreach( array( 'sql_database', 'sql_user', 'sql_pass', 'sql_host', 'sql_port', 'sql_socket', 'sql_charset', 'sql_tbl_prefix' ) as $_s )
		{
			if ( ! isset( $settings[ $_s ] ) )
			{
				$settings[ $_s ] = isset( ipsRegistry::$settings[ $_s ] ) ? ipsRegistry::$settings[ $_s ] : '';
			}
		}
		
		/* Load main class core */
		if ( ! class_exists( 'db_driver_' . $db_driver ) )
		{
			require_once( IPS_KERNEL_PATH . 'classDb' . ucwords($db_driver) . '.php' );/*noLibHook*/
			
			if ( defined( 'IPS_BACK_UP_ENABLED' ) && IPS_BACK_UP_ENABLED )
			{
				require_once( IPS_ROOT_PATH . 'sources/classes/backup/db.php' );
			
				$classname = 'ipsBackup_db';
			}
		}
		
		/* INIT Object */
						
		self::$dbObjects[ $key ] = new $classname;

		self::$dbObjects[ $key ]->obj['sql_database']			= $settings['sql_database'];
		self::$dbObjects[ $key ]->obj['sql_user']				= $settings['sql_user'];
		self::$dbObjects[ $key ]->obj['sql_port']				= $settings['sql_port'];
		self::$dbObjects[ $key ]->obj['sql_socket']				= isset($settings['sql_socket']) ? $settings['sql_socket'] : null;
		self::$dbObjects[ $key ]->obj['sql_pass']				= str_replace( '\\\'', '\'', $settings['sql_pass'] );
		self::$dbObjects[ $key ]->obj['sql_host']				= $settings['sql_host'];
		self::$dbObjects[ $key ]->obj['sql_charset']			= $settings['sql_charset'];
		self::$dbObjects[ $key ]->obj['sql_tbl_prefix']			= $settings['sql_tbl_prefix'] ? $settings['sql_tbl_prefix'] : '';
		self::$dbObjects[ $key ]->obj['force_new_connection']	= ( $key != self::$defaultKey ) ? 1 : 0;
		self::$dbObjects[ $key ]->obj['persistent']				= isset($settings['persistent']) ? $settings['persistent'] : 0;
		self::$dbObjects[ $key ]->obj['use_shutdown']			= IPS_USE_SHUTDOWN;
		
		# Error log
		self::$dbObjects[ $key ]->obj['error_log']				= IPS_TEMP_CACHE_PATH . 'cache/sql_error_log_'.date('m_d_y').'.cgi';
		self::$dbObjects[ $key ]->obj['use_error_log']			= IN_DEV ? 0 : 1;
		
		# Debug log - Don't use this on a production board!
		self::$dbObjects[ $key ]->obj['debug_log']				= IPS_TEMP_CACHE_PATH . 'cache/sql_debug_log_'.date('m_d_y').'.cgi';
		self::$dbObjects[ $key ]->obj['use_debug_log']			= ( defined('IPS_SQL_DEBUG_LOG') AND IPS_SQL_DEBUG_LOG ) ? 1 : 0;
		
		# Bad log - Don't use this on a production board!
		self::$dbObjects[ $key ]->obj['bad_log']				= IPS_TEMP_CACHE_PATH . 'cache/sql_bad_log_'.date('m_d_y').'.cgi';
		self::$dbObjects[ $key ]->obj['use_bad_log']			= ( defined( 'IPS_SQL_FIND_EVIL_MODE' ) AND IPS_SQL_FIND_EVIL_MODE ) ? TRUE : FALSE;
		
		# Slow log - Don't use this on a production board!
		self::$dbObjects[ $key ]->obj['slow_log']				= IPS_TEMP_CACHE_PATH . 'cache/sql_slow_log_'.date('m_d_y').'.cgi';
		self::$dbObjects[ $key ]->obj['use_slow_log']			= ( defined( 'IPS_SQL_FIND_SLOW_MODE' ) AND IPS_SQL_FIND_SLOW_MODE ) ? (float) IPS_SQL_FIND_SLOW_MODE : FALSE;

		/* Required vars? */
		if ( is_array( self::$dbObjects[ $key ]->connect_vars ) and count( self::$dbObjects[ $key ]->connect_vars ) )
		{
			foreach( self::$dbObjects[ $key ]->connect_vars as $k => $v )
			{
				self::$dbObjects[ $key ]->connect_vars[ $k ] = ( isset( $settings[ $k ] ) ) ? $settings[ $k ] : ipsRegistry::$settings[ $k ];
			}
		}

		/* Backwards compat */
		if ( ! self::$dbObjects[ $key ]->connect_vars['mysql_tbl_type'] )
		{
			self::$dbObjects[ $key ]->connect_vars['mysql_tbl_type'] = 'myisam';
		}

		/* Update settings */
		self::$dbPrefixes[ $key ]	= self::$dbObjects[ $key ]->obj['sql_tbl_prefix'];
		self::$dbDrivers[ $key ]	= $db_driver;
		
		/* Want to catch errors? */
		if ( ! empty( $settings['catchConnectionError'] ) )
		{
			self::$dbObjects[ $key ]->return_die = true;
		}
		
		/* Get a DB connection */
		self::$dbObjects[ $key ]->connect();
		
		/* ensure it's switched off now */
		self::$dbObjects[ $key ]->return_die = false;

		self::$data_store[ $key ] = self::$dbObjects[ $key ];
	}
	
	/**
	 * Unset a DB
	 */
	static public function unsetDB( $key=false )
	{
		if ( ! empty( self::$dbObjects[ $key ] ) )
		{
			unset( self::$dbObjects[ $key ] );
		}
	}
}

/**
* Base application class
*/
class ips_CacheRegistry
{
	/**
	 * Database instance
	 *
	 * @var		object
	 */
	private static $instance;

	/**
	 * Database instance
	 *
	 * @var		array
	 */
	protected $save_options  = array();

	/**
	 * Static var for cache library
	 */
	protected static $cacheLib;

	/**
	 * Debug information
	 *
	 * @var		array
	 */
	public $debugInfo = array();

	/**
	 * Initialized flag
	 *
	 * @var		bool
	 */
	protected static $initiated = FALSE;
	
	/**
	 * Generic data storage
	 *
	 * @var		array
	 */
	protected static $data_store	= array();

	/**
	 * List of caches to rebuild
	 *
	 * @var		array
	 */
	protected static $rebuild_list	= array();

	/**
	 * Singleton init
	 *
	 * @return	object
	 */
	static public function instance()
	{
		if ( ! self::$instance )
		{
			self::$instance = new self();
			self::$instance->init();
		}

		return self::$instance;
	}
	
	/**
	 * Retrieve the list of caches to be rebuilt
	 *
	 * @return	@e array
	 */
	public function fetchRebuildList()
	{
		return self::$rebuild_list;
	}

	/**
	 * Initiate class
	 *
	 * @return	@e void
	 */
	private function init()
	{
		if ( self::$initiated !== TRUE )
		{
			//--------------------------------
			// Eaccelerator...
			//--------------------------------

			if( function_exists('eaccelerator_get') AND ipsRegistry::$settings['use_eaccelerator'] == 1 )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheEaccelerator.php' );/*noLibHook*/
				self::$cacheLib = new classCacheEaccelerator( ipsRegistry::$settings['board_url'] );
			}

			//--------------------------------
			// Memcache
			//--------------------------------

			else if( function_exists('memcache_connect') AND ipsRegistry::$settings['use_memcache'] == 1 )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheMemcache.php' );/*noLibHook*/
				self::$cacheLib = new classCacheMemcache( ipsRegistry::$settings['board_url'], ipsRegistry::$settings );
			}

			//--------------------------------
			// XCache...
			//--------------------------------

			else if( function_exists('xcache_get') AND ipsRegistry::$settings['use_xcache'] == 1 )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheXcache.php' );/*noLibHook*/
				self::$cacheLib = new classCacheXcache( ipsRegistry::$settings['board_url'] );
			}

			//--------------------------------
			// APC...
			//--------------------------------

			else if( function_exists('apc_fetch') AND ipsRegistry::$settings['use_apc'] == 1 )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheApc.php' );/*noLibHook*/
				self::$cacheLib = new classCacheApc( ipsRegistry::$settings['board_url'] );
			}
			
			//--------------------------------
			// Wincache...
			//--------------------------------

			else if( function_exists('wincache_ucache_get') AND ipsRegistry::$settings['use_wincache'] == 1 )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheWincache.php' );/*noLibHook*/
				self::$cacheLib = new classCacheWincache( ipsRegistry::$settings['board_url'] );
			}

			//--------------------------------
			// Diskcache
			//--------------------------------

			else if( !empty( ipsRegistry::$settings['use_diskcache'] ) )
			{
				require( IPS_KERNEL_PATH.'interfaces/interfaceCache.php' );/*noLibHook*/
				require( IPS_KERNEL_PATH.'classCacheDiskcache.php' );/*noLibHook*/
				self::$cacheLib = new classCacheDiskcache( ipsRegistry::$settings['board_url'] );
			}

			if( is_object(self::$cacheLib) AND self::$cacheLib->crashed )
			{
				// There was a problem - not installed maybe?
				// unset(self::$cacheLib);
				self::$cacheLib = NULL;
			}

			$caches         = array();
			$_caches		= array();
			$_load			= array();
			$_pre_load      = IPSDebug::getMemoryDebugFlag();

			//-----------------------------------------
			// Get default cache list
			//-----------------------------------------

			$CACHE = ipsRegistry::_fetchCoreVariables( 'cache' );
			$_LOAD = ipsRegistry::_fetchCoreVariables( 'cacheload' );

			if ( is_array( $CACHE ) )
			{
				foreach( $CACHE as $key => $data )
				{
					if ( !empty($data['acp_only']) AND IPS_AREA != 'admin' )
					{
						continue;
					}

					$_caches[ $key ]	= $CACHE;

					if ( $data['default_load'] )
					{
						$caches[ $key ] = $key;
					}
				}

				if( count($_LOAD) )
				{
					foreach( $_LOAD as $key => $one )
					{
						$_load[ $key ] = $key;
					}
				}
			}

			//-----------------------------------------
			// Get application cache list
			//-----------------------------------------

			if ( IPS_APP_COMPONENT )
			{
				$CACHE = ipsRegistry::_fetchAppCoreVariables( IPS_APP_COMPONENT, 'cache' );
				$_LOAD = ipsRegistry::_fetchAppCoreVariables( IPS_APP_COMPONENT, 'cacheload' );

				if ( is_array( $CACHE ) )
				{
					foreach( $CACHE as $key => $data )
					{
						if ( !empty( $data['acp_only'] ) AND IPS_AREA != 'admin' )
						{
							continue;
						}

						$_caches[ $key ]	= $CACHE;

						if ( $data['default_load'] )
						{
							$caches[ $key ] = $key;
						}
					}

					if( count($_LOAD) )
					{
						foreach( $_LOAD as $key => $one )
						{
							$_load[ $key ] = $key;
						}
					}
				}
			}

			//-----------------------------------------
			// Get global caches list
			//-----------------------------------------
			
			if ( is_file( GLOBAL_CACHE_PATH ) )
			{
				$GLOBAL_CACHES = array();
				include( GLOBAL_CACHE_PATH );/*noLibHook*/
				
				if( is_array($GLOBAL_CACHES) AND count($GLOBAL_CACHES) )
				{
					foreach( $GLOBAL_CACHES as $key )
					{
						$_load[ $key ] = $key;
					}
				}
			}
			
			//-----------------------------------------
			// Add caches to the load list
			//-----------------------------------------
			
			if( is_array($_load) AND count($_load) )
			{
				foreach( $_load as $key )
				{
					$caches[ $key ] = $key;
				}
			}

			//-----------------------------------------
			// Load 'em
			//-----------------------------------------

			self::_loadCaches( $caches );
		}

		self::$initiated = TRUE;
	}

	/**
	 * Load cache(s)
	 *
	 * @param	array 	Array of caches to load: array( 'group_cache', 'forum_cache' )
	 * @param	boolean	Set to FALSE to skip trying to load the caches from DB
	 * @return	mixed	Loaded Cache
	 * @author	MattMecham
	 */
	protected static function _loadCaches( $caches=array(), $loadFromDb=true, $init=false )
	{
		if ( ! is_array( $caches ) OR ! count( $caches ) )
		{
			return NULL;
		}

		$requestedCaches	= $caches;
		$_seenKeys			= array();
		
		//--------------------------------
		// Eaccelerator...
		//--------------------------------

		if ( is_object( self::$cacheLib ) )
		{
			$temp_cache 	 = array();
			$new_cache_array = array();

			foreach( $caches as $key )
			{
				$temp_cache[$key] = self::$cacheLib->getFromCache( $key );

				if ( ! $temp_cache[$key] OR $temp_cache[$key] == 'rebuildCache' )
				{
					$new_cache_array[] = $key;
				}
				else
				{
					if ( is_string($temp_cache[$key]) AND strpos( $temp_cache[$key], "a:" ) !== false )
					{
						self::$data_store[ $key ] = unserialize( $temp_cache[$key] );
						
						//-----------------------------------------
						// Fallback fix if unserialize fails
						//-----------------------------------------
						
						if( !is_array(self::$data_store[ $key ]) )
						{
							$new_cache_array[] = $key;
							continue;
						}
					}
					else if( $temp_cache[$key] == "EMPTY" )
					{
						self::$data_store[ $key ] = NULL;
					}
					else
					{
						self::$data_store[ $key ] = $temp_cache[$key];
					}
					
					$_seenKeys[ $key ]	= $key;
				}
			}

			$caches = $new_cache_array;

			unset($new_cache_array, $temp_cache);
		}

		$_rebuild	= array();

		if( count($caches) && $loadFromDb )
		{
			//--------------------------------
			// Get from DB...
			//--------------------------------
	
			ipsRegistry::DB()->build( array( 'select' => '*', 'from' => 'cache_store', 'where' => "cs_key IN ( '" . implode( "','", $caches ) . "' )" ) );
			$exe = ipsRegistry::DB()->execute();

			while ( $r = ipsRegistry::DB()->fetch( $exe ) )
			{
				$_seenKeys[ $r['cs_key'] ] = $r['cs_key'];

				//-----------------------------------------
				// Forcing rebuild?
				//-----------------------------------------
				
				if( $r['cs_rebuild'] )
				{
					if ( $r['cs_key'] == 'licenseData' )
					{
						self::instance()->rebuildCache( $r['cs_key'] );
						self::$data_store[ $r['cs_key'] ] = self::instance()->getCache( $r['cs_key'] );
					}
					else
					{
						self::$rebuild_list[]	= $r['cs_key'];
					}
					continue;
				}
				
				//-----------------------------------------
				// Debug?
				//-----------------------------------------
				
				if ( IN_DEV )
				{
					self::instance()->debugInfo[ $r['cs_key'] ] = array( 'size' => IPSLib::strlenToBytes( strlen($r['cs_value']) ) );
				}
	
				//-----------------------------------------
				// Serialized array?
				//-----------------------------------------
				
				if ( $r['cs_array'] OR substr( $r['cs_value'], 0, 2 ) == "a:" )
				{
					self::$data_store[ $r['cs_key'] ] = unserialize( $r['cs_value'] );
	
					if ( ! is_array( self::$data_store[ $r['cs_key'] ] ) )
					{
						self::$data_store[ $r['cs_key'] ] = array();
					}
				}
				else
				{
					self::$data_store[ $r['cs_key'] ] = ( $r['cs_value'] ) ? $r['cs_value'] : NULL;
				}
	
				//-----------------------------------------
				// Push to alt cache store
				//-----------------------------------------
				
				if ( is_object( self::$cacheLib ) )
				{
					if ( ! $r['cs_value'] )
					{
						$r['cs_value'] = "EMPTY";
					}
	
					self::$cacheLib->putInCache( $r['cs_key'], $r['cs_value'], 86400 );
				}
			}
		}

		//-----------------------------------------
		// Make sure each key is in data_store otherwise
		// repeated calls will keep trying to load it
		//-----------------------------------------

		foreach( $requestedCaches as $_cache )
		{
			if ( ! in_array( $_cache, $_seenKeys ) )
			{
				self::$data_store[ $_cache ] = NULL;
			}
		}
	}

	/**
	 * Set a cache
	 *
	 * @param	string	Cache Key
	 * @param	mixed	Cache value (typically an array)
	 * @return	@e void
	 */
	protected function cacheSet( $key, $val )
	{
		/* Update in_memory cache */
		self::$data_store[ $key ] = $val;

		$this->save_options['donow'] = isset($this->save_options['donow']) ? $this->save_options['donow'] : 0;

		//-----------------------------------------
		// Next...
		//-----------------------------------------

		if ( $key )
		{
			if ( empty($val) )
			{
				if ( !empty($this->save_options['array']) )
				{
					$value = serialize(self::$data_store[ $key ]);
				}
				else
				{
					$value = self::$data_store[ $key ];
				}
			}
			else
			{
				if ( !empty($this->save_options['array']) )
				{
					$value = serialize($val);
				}
				else
				{
					$value = $val;
				}
			}

			ipsRegistry::DB()->preventAddSlashes( array( 'cs_key' => 1 ) );

			if ( $this->save_options['donow'] )
			{
				ipsRegistry::DB()->replace( 'cache_store', array( 'cs_array' => intval($this->save_options['array']), 'cs_key' => $key, 'cs_value' => $value, 'cs_updated' => time(), 'cs_rebuild' => 0 ), array( 'cs_key' ) );
			}
			else
			{
				ipsRegistry::DB()->replace( 'cache_store', array( 'cs_array' => intval($this->save_options['array']), 'cs_key' => $key, 'cs_value' => $value, 'cs_updated' => time(), 'cs_rebuild' => 0 ), array( 'cs_key' ), true );
			}

			if ( is_object( self::$cacheLib ) )
			{
				if ( ! $val )
				{
					/* if we're using an alternative cache - storing EMPTY for a cache that expects to always contain and array can cause foreach errors when accessed */
					if ( ! empty( $this->save_options['array'] ) )
					{
						$val = serialize( array() );
					}
					else
					{
						$val = "EMPTY";
					}
				}

				self::$cacheLib->updateInCache( $key, $val, 86400 );
			}
		}

		/* Reest... */
		$this->save_options = array();
	}

	/**
	 * Grab item from cache library
	 *
	 * @param	string	Key
	 * @return	mixed	false, or cached value
	 */
	static public function getWithCacheLib( $key )
	{
		if ( is_object( self::$cacheLib ) )
		{
			return self::$cacheLib->getFromCache( $key );
		}
		else
		{
			return FALSE;
		}
	}

	/**
	 * Store into cache library
	 *
	 * @param	string	Key
	 * @param	mixed	Item to cache
	 * @param	int		Time to live
	 * @return	mixed
	 */
	static public function putWithCacheLib( $key, $value, $ttl=0 )
	{
		if ( is_object( self::$cacheLib ) )
		{
			return self::$cacheLib->putInCache( $key, $value, $ttl );
		}
		else
		{
			return FALSE;
		}
	}

	/**
	 * Check to see if the cache exists or not
	 *
	 * @param	string		Cache name
	 * @return	boolean
	 */
	static public function exists( $key )
	{
		return ( isset( self::$data_store[ $key ] ) AND ( self::$data_store[ $key ] !== NULL ) ) ? TRUE : FALSE;
	}

	/**
 	 * Get the cache
	 * If the cache has not been loaded during init(), then it'll load the
	 * cache.
	 *
	 * @param	mixed	Cache key, or array of cache keys
	 * @param	boolean	Set to FALSE to skip trying to load the caches from DB [if not loaded already]
	 * @return	mixed	Cache value if $keys is a string, else boolean (loaded successfully or not)
	 */
	static public function getCache( $keys, $loadFromDb=true )
	{
		if ( is_string( $keys ) )
		{
			if ( ! isset( self::$data_store[ $keys ] ) )
			{
				self::_loadCaches( array( $keys ), $loadFromDb );
			}
		
			return self::$data_store[ $keys ];
		}
		elseif ( is_array( $keys ) && count( $keys ) )
		{
			$toLoad = array();
			
			foreach( $keys as $key )
			{
				if ( ! isset( self::$data_store[ $key ] ) )
				{
					$toLoad[] = $key;
				}
			}
			
			if ( count($toLoad) )
			{
				self::_loadCaches( $toLoad, $loadFromDb );
			}
			
			return TRUE;
		}
		
		return FALSE;
	}

	/**
 	 * Delete a cache from DB and alt cache store
	 *
	 * @param	mixed	Cache key, or array of cache keys
	 * @return	@e boolean
	 */
	static public function deleteCache( $keys )
	{
		if ( is_string( $keys ) )
		{
			ipsRegistry::DB()->delete( 'cache_store', "cs_key='{$keys}'" );
		
			if ( is_object( self::$cacheLib ) )
			{
				self::$cacheLib->removeFromCache( $keys );
			}
		}
		elseif ( is_array( $keys ) && count( $keys ) )
		{
			ipsRegistry::DB()->delete( 'cache_store', "cs_key IN('" . implode( "','", $keys ) . "')" );

			if ( is_object( self::$cacheLib ) )
			{
				foreach( $keys as $key )
				{
					self::$cacheLib->removeFromCache( $keys );
				}
			}
		}
		
		return TRUE;
	}

	/**
	 * Fetch all the caches as a reference
	 *
	 * @return	array
	 */
	static public function &fetchCaches()
	{
		return self::$data_store;
	}

	/**
 	 * Update a cache key without saving it to the db
	 *
	 * @param	string	Cache key
	 * @param	mixed	Value
	 * @return	@e void
	 */
	static public function updateCacheWithoutSaving( $key, $value )
	{
		self::$data_store[ $key ] = $value;
	}

	/**
 	 * Store an updated cache value
	 *
	 * @param	string	Cache key
	 * @param	mixed	Value
	 * @param	array 	Options
	 * @return	mixed
	 */
	static public function setCache( $key, $value, $options=array() )
	{
		if ( ! $key )
		{
			throw new Exception( "Key missing in setCache" );
		}

		self::instance()->save_options = $options;
		return self::instance()->cacheSet( $key, $value );
	}

	/**
 	 * Rebuild a cache using defined $CACHE settings in it's extensions file
	 *
	 * @param	string	Cache key
	 * @param	string	Application
	 * @return	@e void
	 */
	static public function rebuildCache( $key, $app='' )
	{
		if ( defined( 'IPS_NO_CACHE_REBUILD' ) )
		{
			return true;
		}
		
		/* INIT */
		$app		= IPSText::alphanumericalClean( $app );
		$_caches	= array();
    
		if( $app )
		{
			if ( $app == 'global' )
			{
				$_caches = ipsRegistry::_fetchCoreVariables( 'cache' );
			}
			else
			{
				/* isset there is needed to prevent issues on applications installation */
				if( isset( ipsRegistry::$applications[$app] ) && !IPSLib::appIsInstalled($app) )
				{
					return;
				}

				$_caches = ipsRegistry::_fetchAppCoreVariables( $app, 'cache' );
			}
		}
		else
		{
			/* Get all caches from all apps */
			$_caches = ipsRegistry::_fetchCoreVariables( 'cache' );
			
			foreach( ipsRegistry::$applications as $appDir => $appData )
			{
				$CACHE = ipsRegistry::_fetchAppCoreVariables( $appDir, 'cache' );
				
				if ( is_array( $CACHE ) )
				{
					$_caches = array_merge( $_caches, $CACHE );

					/* @link http://community.invisionpower.com/resources/bugs.html/_/ip-board/automatic-cache-reloads-do-not-take-application-directories-into-account-r41762 */
					if( isset($CACHE[ $key ]) )
					{
						$app	= $appDir;
					}
				}
			}
		}
		
		/* Rebuild the cache, if found */
		if( isset( $_caches[ $key ] ) )
		{
			$file_to_check = $_caches[ $key ]['recache_file'];

			if ( $file_to_check AND is_file( $file_to_check ) )
			{
				$_func  = $_caches[ $key ]['recache_function'];
				
				/* Hackish way to check for action overloader */
				if ( strpos( $file_to_check, '/modules_' ) !== FALSE )
				{
					$_class = IPSLib::loadActionOverloader( $file_to_check, $_caches[ $key ]['recache_class'] );
				}
				elseif ( $app )
				{
					$_class = IPSLib::loadLibrary( $file_to_check, $_caches[ $key ]['recache_class'], ($app == 'global') ? 'core' : $app );
				}
				
				/* Fallback */
				if ( !$_class )
				{
					$_class = $_caches[ $key ]['recache_class'];
				}
				
				$recache = new $_class( ipsRegistry::instance() );

				if( method_exists( $recache, 'makeRegistryShortcuts' ) )
				{
					$recache->makeRegistryShortcuts( ipsRegistry::instance() );
				}

				$recache->$_func();
			}
		}
	}
}

/**
* Dummy class to prevent errors if cache()->rebuild is required during init()
*/
class ips_MemberRegistryDummy
{
	/**
	 * Database instance
	 *
	 * @var		object
	 */
	private static $instance;
	
	static public function instance()
	{
		if ( ! self::$instance )
		{
			self::$instance = new self();
		}

		return self::$instance;
	}
	
	/**
	 * Fetch all the member data as a reference
	 *
	 * @return	array
	 */
	static public function &fetchMemberData()
	{
		return array();
	}

	/**
	 * Post output class setup
	 *
	 * @return	@e void
	 */
 	public static function postOutput()
 	{
	}
}

/**
* Base application class
*/
class ips_MemberRegistry
{
	/**
	 * Database instance
	 *
	 * @var		object
	 */
	private static $instance;

	/**
	 * Member settings
	 *
	 * @var		array
	 */
	protected $settings = array();

	/**
	 * Member info
	 *
	 * @var		array
	 */
	protected static $member   = array();

	/**
	 * Member ID
	 *
	 * @var		integer
	 */
	public $member_id;

	/**
	 * Perm mask ID
	 *
	 * @var		integer
	 */
	public $perm_id;

	/**
	 * Array of perm masks
	 *
	 * @var		array
	 */
	public $perm_id_array = array();

	/**
	 * Unique form hash
	 *
	 * @var		string
	 */
	public $form_hash     = '';

	/**
	 * Language ID to use
	 *
	 * @var		integer
	 */
	public $language_id   = '1';

	/**
	 * Skin ID to use
	 *
	 * @var		integer
	 */
	public $skin_id       = '';

	/**
	 * Preferences
	 *
	 * @var		array
	 */
	public $preferences   = array();

	/**#@+
	 * Member's session data
	 *
	 * @var		mixed	integer, string, array
	 */
	public $session_id   = 0;
	public $session_type = '';
	public $ip_address   = '';
	public $last_click   = 0;
	public $location     = '';
	public $acp_tab_data = array();
	/**#@-*/

	/**#@+
	 * Environment/browser data
	 *
	 * @var		mixed	integer, string, array
	 */
	public $user_agent;
	public $browser          = array();
	public $operating_system = 'unknown';
	public $is_not_human     = 0;
	/**#@-*/

	/**
	 * Ignored users
	 *
	 * @var		array
	 */
	public $ignored_users	= array();

	/**
	 * Sessions class
	 *
	 * @var		object
	 */
	protected static $session_class;

	/**
	 * Initiated
	 *
	 * @var		false
	 */
	protected static $initiated    = FALSE;
	
	/**
	 * Data store
	 *
	 * @var		array
	 */
	protected static $data_store = array( 'member_group_id' => 0, 'mgroup_others' => '' );
	
	/**
	 * Is this Facebook?
	 * @var 	bool
	 */
	public $iAmFacebook = false;
	
	/**
	 * Initialization method
	 *
	 * @return	object
	 */
	static public function instance()
	{
		if ( ! self::$instance )
		{
			self::$instance = new self();
			self::init();
		}

		return self::$instance;
	}

	/**
	 * Destructor
	 *
	 * @return	@e void
	 */
	public function __myDestruct()
	{
		/* Item marking clean up */
		if( ipsRegistry::isClassLoaded('classItemMarking') )
		{
			ipsRegistry::getClass('classItemMarking')->__myDestruct();
		}

		/* Session clean up */
		self::sessionClass()->__myDestruct();
	}

	/**
	 * Singleton init method
	 *
	 * @return	@e void
	 */
	protected static function init()
	{
		if ( self::$initiated !== TRUE )
		{
			//-----------------------------------------
			// IP Address
			//-----------------------------------------

			if ( ipsRegistry::$settings['xforward_matching'] )
			{
				//foreach( array_reverse( explode( ',', my_getenv('HTTP_X_FORWARDED_FOR') ) ) as $x_f ) // See http://community.invisionpower.com/resources/bugs.html/_/ip-board/multiple-ips-in-x-forwarded-for-r37844
				foreach( explode( ',', my_getenv('HTTP_X_FORWARDED_FOR') ) as $x_f )
				{
					$addrs[] = trim($x_f);
				}

				$addrs[] = my_getenv('HTTP_CLIENT_IP');
				$addrs[] = my_getenv('HTTP_X_CLUSTER_CLIENT_IP');
				$addrs[] = my_getenv('HTTP_PROXY_USER');
			}

			$addrs[] = my_getenv('REMOTE_ADDR');

			//-----------------------------------------
			// Do we have one yet?
			//-----------------------------------------

			foreach ( $addrs as $ip )
			{
				//-----------------------------------------
				// IP v4
				//-----------------------------------------
				
				if ( IPSLib::validateIPv4( $ip ) )
				{
					self::instance()->ip_address	= $ip;
					break;
				}
				
				//-----------------------------------------
				// IP v6
				//-----------------------------------------
				
				else if ( IPSLib::validateIPv6( $ip ) )
				{
					self::instance()->ip_address = $ip;
					break;
				}
			}

			//-----------------------------------------
			// Make sure we take a valid IP address
			//-----------------------------------------

			if ( !self::instance()->ip_address AND ! isset( $_SERVER['SHELL'] ) AND $_SERVER['SESSIONNAME'] != 'Console' )
			{
				if ( ! defined('IPS_IS_SHELL') OR ! IPS_IS_SHELL )
				{
					print "Could not determine your IP address";
					exit();
				}
			}

			//-----------------------------------------
			// Get user-agent, browser and OS
			//-----------------------------------------

			self::instance()->user_agent       = IPSText::parseCleanValue( my_getenv('HTTP_USER_AGENT') );
			self::instance()->operating_system = self::_fetch_os();

			if ( IPS_AREA == 'admin' )
			{
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/session/adminSessions.php', 'adminSessions' );

				/**
				 * Support for extending the session class
				 */
				if( is_file( IPS_ROOT_PATH . "sources/classes/session/ssoAdminSessions.php" ) )
				{
					$classToLoadA = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/classes/session/ssoAdminSessions.php", 'ssoAdminSessions' );

					/**
					 * Does the ssoAdminSessions class exist?
					 */
					if( class_exists( $classToLoadA ) )
					{
						$parent = get_parent_class( $classToLoadA );

						/**
						 * Is it a child of adminSessions
						 */
						if( $parent == $classToLoad )
						{
							self::$session_class = new $classToLoadA();
						}
						else
						{
							self::$session_class = new $classToLoad();
						}
					}
				}
				else
				{
					self::$session_class = new $classToLoad();
				}
			}
			else
			{
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . 'sources/classes/session/publicSessions.php', 'publicSessions' );

				/**
				 * Support for extending the session class
				 */
				if( is_file( IPS_ROOT_PATH . "sources/classes/session/ssoPublicSessions.php" ) )
				{
					$classToLoadA = IPSLib::loadLibrary( IPS_ROOT_PATH . "sources/classes/session/ssoPublicSessions.php", 'ssoPublicSessions' );

					/**
					 * Does the ssoPublicSessions class exist?
					 */
					if( class_exists( $classToLoadA ) )
					{
						$parent = get_parent_class( $classToLoadA );

						/**
						 * Is it a child of publicSessions
						 */
						if( $parent == $classToLoad )
						{
							self::$session_class = new $classToLoadA();
						}
						else
						{
							self::$session_class = new $classToLoad();
						}
					}
				}
				else
				{
					self::$session_class = new $classToLoad();
				}

				//-----------------------------------------
				// Set other
				//-----------------------------------------

				self::$data_store['publicSessionID']  = self::$session_class->session_data['id'];
			}
			
			//-----------------------------------------
			// Set user agent
			//-----------------------------------------
			
			$_cookie = IPSCookie::get("uagent_bypass");
			
			self::$data_store['userAgentKey']     = isset(self::$session_class->session_data['uagent_key']) ? self::$session_class->session_data['uagent_key'] : '';
			self::$data_store['userAgentType']    = isset(self::$data_store['uagent_type'] ) ? self::$data_store['uagent_type'] : self::$session_class->session_data['uagent_type'];
			self::$data_store['userAgentVersion'] = isset(self::$session_class->session_data['uagent_version']) ? self::$session_class->session_data['uagent_version'] : '';
			self::$data_store['userAgentBypass']  = ( $_cookie ) ? true : ( isset(self::$session_class->session_data['uagent_bypass']) ? self::$session_class->session_data['uagent_bypass'] : '' );
		
			self::$data_store['forumsModeratorData']	= array();
			
			/* Some mobile app set up */
			if ( self::$data_store['userAgentType'] == 'mobileApp' )
			{
				/* This converts non UTF-8 POST/GET data in __construct */
				$classToLoad = IPSLib::loadLibrary( IPS_ROOT_PATH . '/sources/base/ipsMobileApp.php', 'ipsMobileApp' );
				ipsRegistry::setClass( 'isMobileApp', new $classToLoad() );
			}
   		}
	}
	
	/**
	 * Post output class setup
	 *
	 * @return	@e void
	 */
 	public static function postOutput()
 	{
 		if( !self::$data_store['member_id'] )
 		{
 			self::$data_store	= IPSMember::buildNoPhoto( self::$data_store );
 		}
 	}

	/**
	 * Sessions class interface
	 *
	 * @return	object
	 */
	public static function sessionClass()
	{
		return self::$session_class;
	}

	/**
	 * Get a member property
	 *
	 * @param	string	Key
	 * @return	mixed	Member data
	 */
	static public function getProperty( $key )
	{
		return ( isset( self::$data_store[ $key ] ) ) ? self::$data_store[ $key ] : null;
	}

	/**
	 * Set a member property.  DOES NOTE SAVE IT TO DB.
	 *
	 * @param	string	Key
	 * @param	mixed	Value
	 * @return	mixed	Member data
	 */
	static public function setProperty( $key, $value )
	{
		return self::$data_store[ $key ] = $value;
	}

	/**
	 * Fetch all the member data as a reference
	 *
	 * @return	array
	 */
	static public function &fetchMemberData()
	{
		return self::$data_store;
	}

	/**
	 * Sets up a search engine's data and permissions
	 *
	 * @param	array 		array of useragent information
	 * @return	@e void
	 */
	static public function setSearchEngine( $uAgent )
	{
		$cache = ipsRegistry::cache()->getCache('group_cache');
		$group = $cache[ intval( ipsRegistry::$settings['guest_group'] ) ];
		
		/* Are we facebook? */
		if ( $uAgent['uagent_key'] == 'facebook' )
		{
			self::instance()->iAmFacebook = true;
			
			$group = ( ipsRegistry::$settings['fbc_bot_group'] ) ? $cache[ intval( ipsRegistry::$settings['fbc_bot_group'] ) ] : $group;
		}
		
		/* Are we mobile? */
		if ( $uAgent['uagent_key'] == 'googlemobile' )
		{
			self::$data_store['uagent_type'] = 'mobileBot';
		}
				
		foreach ( $group as $k => $v )
		{
			self::$data_store[ $k ] = $v;
		}

		/* Fix up member and group data */
		self::$data_store['members_display_name']	= $uAgent['uagent_name'];
		self::$data_store['_members_display_name']	= $uAgent['uagent_name'];
		self::$data_store['name']					= $uAgent['uagent_name'];
		self::$data_store['member_group_id']		= self::$data_store['g_id'];
		self::$data_store['view_sigs']				= ipsRegistry::$settings['guests_sig'];
		self::$data_store['restrict_post']			= 1;
		self::$data_store['g_use_search']			= 0;
		self::$data_store['g_edit_profile']			= 0;
		self::$data_store['g_use_pm']				= 0;
		self::$data_store['g_is_supmod']			= 0;
		self::$data_store['g_access_cp']			= 0;
		self::$data_store['g_access_offline']		= 0;
		self::$data_store['g_avoid_flood']			= 0;
		self::$data_store['g_post_new_topics']		= 0;
		self::$data_store['g_reply_own_topics']		= 0;
		self::$data_store['g_reply_other_topics']	= 0;
		self::$data_store['member_id']				= 0;
		self::$data_store['_cache']					= array();
		self::$data_store['_cache']['friends']		= array();

		/* Fix up permission strings */
		self::instance()->perm_id       = $group['g_perm_id'];
        self::instance()->perm_id_array = explode( ",", $group['g_perm_id'] );
		
		# Form hash
		self::instance()->form_hash = md5("this is only here to prevent it breaking on guests");
			
		/* It's allliiiiiiveeeeee */
		self::instance()->is_not_human  = true;
		
		/* Disable relative time */
		ipsRegistry::$settings['time_use_relative'] = 0;
		
		/* Logging? */
		if ( ipsRegistry::$settings['spider_visit'] )
		{
			ipsRegistry::DB()->insert( 'spider_logs', array ( 'bot'		     => $uAgent['uagent_key'],
															  'request_addr' => htmlentities( strip_tags( str_replace( '\\', '',  str_replace( "'", "", my_getenv('REQUEST_URI')) ) ) ),
															  'ip_address'   => self::instance()->ip_address,
															  'entry_date'   => time() ), true );
		}
	}

	/**
	 * Set current member to the member ID specified
	 *
	 * @param	integer		Member ID
	 * @return	@e void
	 */
	static public function setMember( $member_id )
	{
		//-----------------------------------------
		// INIT
		//-----------------------------------------

		$member_id = intval( $member_id );
		$addrs     = array();

		//-----------------------------------------
		// If we have a member ID, set up the member
		//-----------------------------------------

		if ( $member_id )
		{
			self::$data_store = IPSMember::load( $member_id, 'extendedProfile,customFields,groups' );
		}
				
		/* Got a member ID? */
		if ( !empty($member_id) && self::$data_store['member_id'] )
		{
			self::setUpMember();
			
			self::instance()->language_id = self::$data_store['language'];
			
			# Form hash
			self::instance()->form_hash = md5( self::$data_store['email'].'&'.self::$data_store['member_login_key'].'&'.self::$data_store['joined'] );
		}
		else
		{
			self::$data_store = IPSMember::setUpGuest();

			self::instance()->perm_id       = !empty(self::$data_store['org_perm_id']) ? self::$data_store['org_perm_id'] : self::$data_store['g_perm_id'];
			self::instance()->perm_id_array = explode( ',', self::instance()->perm_id );
			
			if( IPSCookie::get('language') )
			{
				self::instance()->language_id = IPSCookie::get('language');
			}
			
			# Form hash
			self::instance()->form_hash = md5("this is only here to prevent it breaking on guests");
		}

		/* Get the ignored users */
		if( IPS_AREA == 'public' )
		{
			/* Ok, Fetch ignored users */
			self::instance()->ignored_users = IPSMember::fetchIgnoredUsers( self::$data_store );
		}
		 	
		//-----------------------------------------
		// Set member data
		//-----------------------------------------

		self::instance()->member_id = $member_id;
	}

	/**
	 * Fetches the user's operating system
	 *
	 * @return	string
	 */
	static protected function _fetch_os()
	{
		$useragent = strtolower(my_getenv('HTTP_USER_AGENT'));

		if ( strstr( $useragent, 'mac' ) )
		{
			return 'mac';
		}

		if ( preg_match( '#wi(n|n32|ndows)#', $useragent ) )
		{
			return 'windows';
		}

		return 'unknown';
	}

	/**
	 * Set up a member's secondary groups
	 *
	 * @param	array		$data		Member data
	 * @return	@e array	Member data with secondary group perms set properly
	 */
	static public function setUpSecondaryGroups( $data )
    {
    	if ( !empty($data['mgroup_others']) )
		{
			$cache			= ipsRegistry::cache()->getCache('group_cache');
			$groups_id		= explode( ',', $data['mgroup_others'] );
			$exclude		= array( 'g_id', 'g_title', 'g_icon', 'prefix', 'suffix', 'g_promotion', 'g_photo_max_vars' );
			$less_is_more	= array( 'g_search_flood' );
			$neg1_is_best	= array();
			$zero_is_best	= array( 'g_attach_max', 'g_attach_per_post', 'g_edit_cutoff', 'g_max_messages', 'g_pm_perday', 'g_pm_flood_mins', 'g_displayname_unit', 'g_sig_unit', 'g_mod_preview', 'g_ppd_limit', 'g_ppd_unit', 'gbw_no_status_update', 'g_max_bgimg_upload', 'gbw_disable_prefixes', 'gbw_disable_tagging' );
			$special		= array( 'g_signature_limits', 'g_dname_date', 'g_dname_changes', 'g_mod_post_unit', 'gbw_mod_post_unit_type' );
			
			//-----------------------------------------
			// Merge in per-app group overrides
			//-----------------------------------------
			
			foreach( IPSLib::getEnabledApplications() as $application )
			{
				if( !empty($application['extensions']['groupOptions']) )
				{
					if( !empty($application['extensions']['groupOptions']['neg1_is_best']) AND is_array($application['extensions']['groupOptions']['neg1_is_best']) )
					{
						$neg1_is_best	= array_merge( $neg1_is_best, $application['extensions']['groupOptions']['neg1_is_best'] );
					}
					
					if( !empty($application['extensions']['groupOptions']['zero_is_best']) AND is_array($application['extensions']['groupOptions']['zero_is_best']) )
					{
						$zero_is_best	= array_merge( $zero_is_best, $application['extensions']['groupOptions']['zero_is_best'] );
					}
					
					if( !empty($application['extensions']['groupOptions']['less_is_more']) AND is_array($application['extensions']['groupOptions']['less_is_more']) )
					{
						$less_is_more	= array_merge( $less_is_more, $application['extensions']['groupOptions']['less_is_more'] );
					}
					
					if( !empty($application['extensions']['groupOptions']['exclude']) AND is_array($application['extensions']['groupOptions']['exclude']) )
					{
						$exclude		= array_merge( $exclude, $application['extensions']['groupOptions']['exclude'] );
					}
				}
			}

			//-----------------------------------------
			// Merge permissions
			//-----------------------------------------
			
			if ( count( $groups_id ) )
			{
				foreach( $groups_id as $pid )
				{
					if ( empty($cache[ $pid ]['g_id']) )
					{
						continue;
					}
					
					//-----------------------------------------
					// Loop through and mix
					//-----------------------------------------

					foreach( $cache[ $pid ] as $k => $v )
					{
						if ( ! in_array( $k, $exclude ) )
						{
							//-----------------------------------------
							// Add to perm id list
							//-----------------------------------------

							if ( $k == 'g_perm_id' )
							{
								$data['g_perm_id'] .= ','.$v;
							}
							else if ( in_array( $k, $zero_is_best ) )
							{
								if ( empty( $data[ $k ] ) )
								{
									continue;
								}
								else if( $v == 0 )
								{
									$data[ $k ] = 0;
								}
								else if ( $v > $data[ $k ] )
								{
									$data[ $k ] = $v;
								}
							}
							else if( in_array( $k, $neg1_is_best ) )
							{
								if ( $data[ $k ] == -1 )
								{
									continue;
								}
								else if( $v == -1 )
								{
									$data[ $k ] = -1;
								}
								else if ( $v > $data[ $k ] )
								{
									$data[ $k ] = $v;
								}
							}
							else if ( in_array( $k, $less_is_more ) )
							{
								if ( $v < $data[ $k ] )
								{
									$data[ $k ] = $v;
								}
							}
							else if( in_array( $k, $special ) )
							{
								switch( $k )
								{
									case 'g_signature_limits':
									
										//-----------------------------------------
										// No limits should win out
										//-----------------------------------------
										
										if( !$data[ $k ] )
										{
											continue;
										}
										
										//-----------------------------------------
										// We have limits
										//-----------------------------------------

										if( $v )
										{
											$values	= explode( ':', $v );
											$_cur	= explode( ':', $data[ $k ] );
											$_new 	= array();
											
											foreach( $values as $index => $value )
											{
												if( $_cur[ $index ] === null OR $values[ $index ] === null OR $_cur[ $index ] === '' OR $values[ $index ] === '' )
												{
													$_new[ $index ]	= null;
												}
												/* If signatures are currently disabled but aren't in the group we are checking, signatures should not be disabled (1=disabled, 0=enabled) */
												elseif ( $index == 0 and $_cur[ $index ] > $values[ $index ] )
												{
													$_new[ $index ]	= $values[ $index ];
												}
												/* If signatures are currently enabled but aren't in the group we are checking, signatures should not be disabled */
												elseif ( $index == 0 and $_cur[ $index ] < $values[ $index ] )
												{
													$_new[ $index ]	= $_cur[ $index ];
												}
												else if( $_cur[ $index ] > $values[ $index ] )
												{
													$_new[ $index ]	= $_cur[ $index ];
												}
												else
												{
													$_new[ $index ]	= $values[ $index ];
												}
											}
																						
											ksort($_new);
											$data[ $k ]	= implode( ':', $_new );
										}
										else
										{
											//-----------------------------------------
											// Set no limits
											//-----------------------------------------
											
											$data[ $k ]	= null;
										}
									break;
									
									case 'g_dname_date':
										//-----------------------------------------
										// We'll handle this in g_dname_changes
										//-----------------------------------------
										
										continue;
									break;
									
									case 'g_dname_changes':
										$changes	= $v;
										$timeFrame	= $cache[ $pid ]['g_dname_date'];

										//-----------------------------------------
										// No time frame restriction
										//-----------------------------------------
										
										if( !$timeFrame )
										{
											//-----------------------------------------
											// This group allows more changes
											//-----------------------------------------

											if( $changes > $data[ $k ] )
											{
												$data[ $k ]				= $changes;
												$data['g_dname_date']	= 0;
											}
											
											//-----------------------------------------
											// Existing data is date restricted
											//-----------------------------------------
											
											else if( $data['g_dname_date'] )
											{
												if( $data[ $k ] )
												{
													$_compare	= round($data['g_dname_date'] / $data[ $k ]);
													
													if( $_compare > $changes )
													{
														$data[ $k ]				= $changes;
														$data['g_dname_date']	= 0;
													}
												}
											}
										}
										
										//-----------------------------------------
										// Time frame restriction
										//-----------------------------------------
										
										else if( $changes )
										{
											$_compare	= round($timeFrame / $changes);

											//-----------------------------------------
											// Existing has no time frame restriction
											//-----------------------------------------
											
											if( !$data['g_dname_date'] AND $data[ $k ] )
											{
												if( $_compare < $data[ $k ] )
												{
													$data[ $k ]				= $changes;
													$data['g_dname_date']	= $timeFrame;
												}
											}
											else if( !$data['g_dname_date'] )
											{
												$data[ $k ]				= $changes;
												$data['g_dname_date']	= $timeFrame;
											}
											else if( $data['g_dname_date'] AND $data[ $k ] )
											{
												$_oldCompare	= $data['g_dname_date'] / $data[ $k ];

												if( $_compare < $_oldCompare )
												{
													$data[ $k ]				= $changes;
													$data['g_dname_date']	= $timeFrame;
												}
											}
										}
									break;
									
									case 'g_mod_post_unit':
										/* Have we met the current requirements? */
										if ( ( !$data['gbw_mod_post_unit_type'] and $data['g_mod_post_unit'] >= $data['posts'] ) or ( $data['gbw_mod_post_unit_type'] and time() >= ( $data['joined'] + ( $data['g_mod_post_unit'] * 3600 ) ) ) )
										{
											// Yes - so let's stick with this
											continue;
										}
										else
										{
											// No - go with the new group
											$data['g_mod_post_unit'] = $cache[ $pid ]['g_mod_post_unit'];
											$data['gbw_mod_post_unit_type'] = $cache[ $pid ]['gbw_mod_post_unit_type'];
										}
									break;
									
									case 'gbw_mod_post_unit_type':
										// We handle this with g_mod_post_unit - so do nothing
									break;
								}
							}
							else
							{
								if ( !isset($data[ $k ]) OR $v > $data[ $k ] )
								{
									$data[ $k ] = $v;
								}
							}
						}
					}
				}
			}
									
			//-----------------------------------------
			// Tidy perms_id
			//-----------------------------------------

			$rmp = array();
			$tmp = explode( ',', IPSText::cleanPermString($data['g_perm_id']) );

			if ( count( $tmp ) )
			{
				foreach( $tmp as $t )
				{
					$rmp[ $t ] = $t;
				}
			}

			if ( count( $rmp ) )
			{
				$data['g_perm_id'] = implode( ',', $rmp );
			}
		}

		return $data;
	}

	/**
	 * Update the member's session
	 *
	 * @param	array 		Array of data to update
	 * @return	@e void
	 */
	static public function updateMySession( $data )
	{
		self::$session_class->updateSession( self::$data_store['publicSessionID'], self::$data_store['member_id'], $data );
	}

	/**
	 * Finalize public member
	 *
	 * Now that everything has loaded, lets do the final set up
	 *
	 * @return	@e void
	 */
	static public function finalizePublicMember()
	{
		/* Build profile picture */
		self::$data_store = IPSMember::buildProfilePhoto( self::$data_store );

		/* SEO Name */
		if ( ! self::$data_store['members_seo_name'] )
		{
			self::$data_store['members_seo_name'] = IPSMember::fetchSeoName( self::$data_store );
		}
		
		/* Rebuild messenger count if triggered */
		if( self::$data_store['msg_count_reset'] )
		{
			/* Just instantiating the class will perform reset */
			$classToLoad	= IPSLib::loadLibrary( IPSLib::getAppDir( 'members' ) . '/sources/classes/messaging/messengerFunctions.php', 'messengerFunctions', 'members' );
			$messenger		= new $classToLoad( ipsRegistry::instance() );
		}
	}

	/**
	 * Set up a member
	 *
	 * @return	@e void
	 */
	static protected function setUpMember()
    {
		//-----------------------------------------
        // INIT
        //-----------------------------------------

        $cache = ipsRegistry::cache()->getCache('group_cache');

		//-----------------------------------------
		// Unpack cache
		//-----------------------------------------
		
		if ( isset(self::$data_store['members_cache']) )
		{
			self::$data_store['_cache'] = IPSMember::unpackMemberCache( self::$data_store['members_cache'] );
		}
		else
		{
			self::$data_store['_cache'] = array();
		}

		if ( ! isset( self::$data_store['_cache']['friends'] ) or ! is_array( self::$data_store['_cache']['friends'] ) )
		{
			self::$data_store['_cache']['friends'] = array();
		}
		
		//-----------------------------------------
		// Unpack ignored users
		//-----------------------------------------
		
		if ( isset(self::$data_store['ignored_users']) )
		{
			self::$data_store['_ignoredUsers'] = @unserialize( self::$data_store['ignored_users'] );
		}
		else
		{
			self::$data_store['_ignoredUsers'] = array();
		}
	
		//-----------------------------------------
        // Set up main 'display' group
        //-----------------------------------------
        
        if( is_array( $cache[ self::$data_store['member_group_id'] ] ) )
        {
        	self::$data_store = array_merge( self::$data_store, $cache[ self::$data_store['member_group_id'] ] );
    	}
    	
		//-----------------------------------------
		// Work out permissions
		//-----------------------------------------

		self::$data_store = self::instance()->setUpSecondaryGroups( self::$data_store );
		
		/* Ensure we don't have a ,, string */
		self::$data_store['org_perm_id'] = IPSText::cleanPermString( self::$data_store['org_perm_id'] );
		
		self::instance()->perm_id       = ! empty(self::$data_store['org_perm_id']) ? self::$data_store['org_perm_id'] : self::$data_store['g_perm_id'];
        self::instance()->perm_id_array = explode( ",", self::instance()->perm_id );

        //-----------------------------------------
        // Synchronise the last visit and activity times if
        // we have some in the member profile
        //-----------------------------------------

        if ( ! self::$data_store['last_activity'] )
       	{
       		self::$data_store['last_activity'] = IPS_UNIX_TIME_NOW;
       	}

		//-----------------------------------------
		// If there hasn't been a cookie update in 2 hours,
		// we assume that they've gone and come back
		//-----------------------------------------

		if ( ! self::$data_store['last_visit'] )
		{
			//-----------------------------------------
			// No last visit set, do so now!
			//-----------------------------------------

			ipsRegistry::DB()->update( 'members', array( 'last_visit' => self::$data_store['last_activity'], 'last_activity' => IPS_UNIX_TIME_NOW ), "member_id=".self::$data_store['member_id'], true );
			self::$data_store['last_visit'] = self::$data_store['last_activity'];

		}
		else if ( ( IPS_UNIX_TIME_NOW  - self::$data_store['last_activity']) > 300 )
		{
			//-----------------------------------------
			// If the last click was longer than 5 mins ago and this is a member
			// Update their profile.
			//-----------------------------------------

			$be_anon = IPSMember::isLoggedInAnon( self::$data_store );

			ipsRegistry::DB()->update( 'members', array( 'login_anonymous' => "{$be_anon}&1", 'last_activity' => IPS_UNIX_TIME_NOW ), 'member_id=' . self::$data_store['member_id'], true );
		}
		
		//-----------------------------------------
		// Group promotion based on time since joining
		//-----------------------------------------
		
		/* Are we checking for auto promotion? */
		if ( self::$data_store['g_promotion'] != '-1&-1' )
		{
			/* Are we checking for post based auto incrementation? 0 is post based, 1 is date based, so...  */
			if ( self::$data_store['gbw_promote_unit_type'] )
			{
				list($gid, $gdate) = explode( '&', self::$data_store['g_promotion'] );
			
				if ( $gid > 0 and $gdate > 0 )
				{
					if ( self::$data_store['joined'] <= ( time() - ( $gdate * 86400 ) ) )
					{
						IPSMember::save( self::$data_store['member_id'], array( 'core' => array( 'member_group_id' => $gid ) ) );
						
						/* Now reset the members group stuff */
						self::$data_store = array_merge( self::$data_store, $cache[ $gid ] );
						
						self::$data_store = self::instance()->setUpSecondaryGroups( self::$data_store );

						self::instance()->perm_id       = !empty(self::$data_store['org_perm_id']) ? self::$data_store['org_perm_id'] : self::$data_store['g_perm_id'];
				        self::instance()->perm_id_array = explode( ",", self::instance()->perm_id );
					}
				}
			}
		}
	}
}

/**
 * Class used to track visits from search enginesw
 */
class seoTracker
{
	/**
	 * Engines we check for
	 *
	 * @var array
	 */
	protected static $engines  = array(
		array( 'name' => 'Google', 'match' => '/http\:\/\/www\.google/', 'parser' => 'parseGoogleUrl' ),
		array( 'name' => 'Bing', 'match' => '/http\:\/\/www\.bing/', 'parser' => 'parseBingUrl' ),
		array( 'name' => 'Bing', 'match' => '/http\:\/\/www\.bing/', 'parser' => 'parseBingUrl' ),
		array( 'name' => 'Facebook', 'match' => '/http\:\/\/www\.facebook/', 'parser' => 'parseFacebookUrl' )
	);
	
	/**
	 * Track
	 *
	 * @param	string	HTTP Referrer
	 * @param	string	Query string
	 * @param	int		Member ID
	 */
	public static function track( $referrer, $queryString, $memberId )
	{
		foreach( self::$engines as $engine )
		{
			if( preg_match( $engine['match'], $referrer ) )
			{
				$obj = new self;
			
				$keywords = trim( $obj->{$engine['parser']}( $referrer ) );
				if ( !empty( $keywords ) )
				{
					ipsRegistry::DB()->allow_sub_select = 1;
					ipsRegistry::DB()->loadCacheFile( IPSLib::getAppDir('core') . '/sql/' . ips_DBRegistry::getDriverType() . '_seo_queries.php', 'core_seo_queries' );
					ipsRegistry::DB()->buildFromCache( 'ipseo_increment_keyword_count', IPSText::parseCleanValue( $keywords ), 'core_seo_queries' );
					ipsRegistry::DB()->allow_sub_select = 1;
					
					ipsRegistry::DB()->execute();
					
					ipsRegistry::DB()->insert( 'search_visitors', array( 'date' => time(), 'member' => intval( $memberId ), 'engine' => $engine['name'], 'keywords' => $keywords, 'url' => $queryString ) );
				}
			
				break;
			}
		}
	}

	protected function parseGoogleUrl($url)
	{
		$matches = array();
		if(preg_match('/imgres/', $url) && preg_match('/prev\=([^\&]+)/', $url, $matches))
		{
			$url = urldecode($matches[1]);
		}
		
		$matches = array();
		if(preg_match('/q\=([^\&]+)/', $url, $matches))
		{
			if(isset($matches[1]))
			{
				$keywords = $matches[1];
				$keywords = urldecode($keywords);
				$keywords = str_replace('+', ' ', $keywords);
				$keywords = str_replace('%20', ' ', $keywords);
				return $keywords;
			}
		}
		
		return null;
	}
	
	protected function parseBingUrl($url)
	{
		$matches = array();
		if(preg_match('/q\=([^\&]+)/', $url, $matches))
		{
			if(isset($matches[1]))
			{
				$keywords = $matches[1];
				$keywords = urldecode($keywords);
				$keywords = str_replace('+', ' ', $keywords);
				$keywords = str_replace('%20', ' ', $keywords);
				return $keywords;
			}
		}
		
		return null;
	}
	
	protected function parseYahooUrl($url)
	{
		$matches = array();
		if(preg_match('/p\=([^\&]+)/', $url, $matches))
		{
			if(isset($matches[1]))
			{
				$keywords = $matches[1];
				$keywords = urldecode($keywords);
				$keywords = str_replace('+', ' ', $keywords);
				$keywords = str_replace('%20', ' ', $keywords);
				return $keywords;
			}
		}
		
		return null;
	}
	
	protected function parseFacebookUrl($url)
	{
		$matches = array();
		if(preg_match('/l\.php\?u\=([^\&]+)/', $url, $matches))
		{
			if(isset($matches[1]))
			{
				$keywords = $matches[1];
				$keywords = urldecode($keywords);
				$keywords = str_replace('http://', '', $keywords);
				$keywords = str_replace('https://', '', $keywords);
				$keywords = str_replace('+', ' ', $keywords);
				$keywords = str_replace('%20', ' ', $keywords);
				return $keywords;
			}
		}
		elseif(preg_match('/end\.php/', $url))
		{
			return 'Sponsored Ad';
		}
		
		return null;
	}
}