diff options
Diffstat (limited to 'phpBB')
| -rw-r--r-- | phpBB/includes/classes/session.php | 1826 | 
1 files changed, 448 insertions, 1378 deletions
diff --git a/phpBB/includes/classes/session.php b/phpBB/includes/classes/session.php index 7e7fbfb8ac..72bc7c9a6b 100644 --- a/phpBB/includes/classes/session.php +++ b/phpBB/includes/classes/session.php @@ -3,7 +3,7 @@  *  * @package phpBB3  * @version $Id$ -* @copyright (c) 2005 phpBB Group +* @copyright (c) 2005, 2008 phpBB Group  * @license http://opensource.org/licenses/gpl-license.php GNU Public License  *  */ @@ -20,172 +20,88 @@ if (!defined('IN_PHPBB'))  * Session class  * @package phpBB3  */ -class session +class phpbb_session  { -	var $cookie_data = array(); -	var $page = array(); -	var $data = array(); -	var $browser = ''; -	var $forwarded_for = ''; -	var $host = ''; -	var $session_id = ''; -	var $ip = ''; -	var $load = 0; -	var $time_now = 0; -	var $update_session_page = true; -  	/** -	* Extract current session page -	* -	* @param string $root_path current root path (phpbb_root_path) +	* @var array Cookie informations  	*/ -	public static function extract_current_page($root_path) -	{ -		$page_array = array(); +	private $cookie_data = array(); -		// First of all, get the request uri... -		$script_name = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : getenv('PHP_SELF'); -		$args = (!empty($_SERVER['QUERY_STRING'])) ? explode('&', $_SERVER['QUERY_STRING']) : explode('&', getenv('QUERY_STRING')); - -		// If we are unable to get the script name we use REQUEST_URI as a failover and note it within the page array for easier support... -		if (!$script_name) -		{ -			$script_name = (!empty($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : getenv('REQUEST_URI'); -			$script_name = (($pos = strpos($script_name, '?')) !== false) ? substr($script_name, 0, $pos) : $script_name; -			$page_array['failover'] = 1; -		} - -		// Replace backslashes and doubled slashes (could happen on some proxy setups) -		$script_name = str_replace(array('\\', '//'), '/', $script_name); - -		// Now, remove the sid and let us get a clean query string... -		$use_args = array(); - -		// Since some browser do not encode correctly we need to do this with some "special" characters... -		// " -> %22, ' => %27, < -> %3C, > -> %3E -		$find = array('"', "'", '<', '>'); -		$replace = array('%22', '%27', '%3C', '%3E'); - -		foreach ($args as $argument) -		{ -			if (strpos($argument, 'sid=') === 0) -			{ -				continue; -			} - -			$use_args[] = str_replace($find, $replace, $argument); -		} -		unset($args); - -		// The following examples given are for an request uri of {path to the phpbb directory}/adm/index.php?i=10&b=2 - -		// The current query string -		$query_string = trim(implode('&', $use_args)); - -		// basenamed page name (for example: index.php) -		$page_name = basename($script_name); -		$page_name = urlencode(htmlspecialchars($page_name)); - -		// current directory within the phpBB root (for example: adm) -		$root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path))); -		$page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./'))); -		$intersection = array_intersect_assoc($root_dirs, $page_dirs); +	/** +	* @var array User data +	*/ +	public $data = array(); -		$root_dirs = array_diff_assoc($root_dirs, $intersection); -		$page_dirs = array_diff_assoc($page_dirs, $intersection); +	/** +	* @var string Session id +	*/ +	public $session_id = ''; -		$page_dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs); +	/** +	* @var int Current time +	*/ +	public $time_now = 0; -		if ($page_dir && substr($page_dir, -1, 1) == '/') -		{ -			$page_dir = substr($page_dir, 0, -1); -		} +	/** +	* @var bool True if the current page is updated within the sessions table +	*/ +	public $update_session_page = true; -		// Current page from phpBB root (for example: adm/index.php?i=10&b=2) -		$page = (($page_dir) ? $page_dir . '/' : '') . $page_name . (($query_string) ? "?$query_string" : ''); +	/** +	* @var object Reference to authentication system. +	*/ +	public $auth = NULL; -		// The script path from the webroot to the current directory (for example: /phpBB3/adm/) : always prefixed with / and ends in / -		$script_path = trim(str_replace('\\', '/', dirname($script_name))); +	/** +	* @var object Reference to system array for obtaining server/system information +	*/ +	public $system = NULL; -		// The script path from the webroot to the phpBB root (for example: /phpBB3/) -		$script_dirs = explode('/', $script_path); -		array_splice($script_dirs, -sizeof($page_dirs)); -		$root_script_path = implode('/', $script_dirs) . (sizeof($root_dirs) ? '/' . implode('/', $root_dirs) : ''); +	/** +	* @var bool Is true if the user is a logged in registered user +	*/ +	public $is_registered = false; -		// We are on the base level (phpBB root == webroot), lets adjust the variables a bit... -		if (!$root_script_path) -		{ -			$root_script_path = ($page_dir) ? str_replace($page_dir, '', $script_path) : $script_path; -		} +	/** +	* @var bool Is true if the user is logged in and a search engine/bot +	*/ +	public $is_bot = false; -		$script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/'; -		$root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/'; +	/** +	* @var array Extra url parameter to append to every URL in phpBB +	*/ +	public $extra_url = array(); -		$page_array += array( -			'page_name'			=> $page_name, -			'page_dir'			=> $page_dir, +	/** +	* @var bool If this is true then the session id (?sid=[session_id]) is required +	*/ +	public $need_sid = false; -			'query_string'		=> $query_string, -			'script_path'		=> str_replace(' ', '%20', htmlspecialchars($script_path)), -			'root_script_path'	=> str_replace(' ', '%20', htmlspecialchars($root_script_path)), +	/** +	* Init session. Empties the user data and assigns the system object (phpbb::$instances['system']) +	*/ +	public function __construct() +	{ +		// Reset data array ;) +		$this->data = array(); -			'page'				=> $page -		); +		// Set auth to false (only valid for an user object) +		$this->auth = false; -		return $page_array; +		// Some system/server variables, directly generated by phpbb_system methods. Used like an array. +		// We use the phpbb:: one, because it could've been modified and being a completely different class +		$this->system = &phpbb::$instances['system'];  	}  	/** -	* Get valid hostname/port. HTTP_HOST is used, SERVER_NAME if HTTP_HOST not present. +	* Specifiy the need for a session id within the URL +	* +	* @param bool $need_sid[optional] Specify if the session id is needed or not. Default is false. +	* @access public  	*/ -	function extract_current_hostname() +	public function need_sid($need_sid = false)  	{ -		global $config; - -		// Get hostname -		$host = (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : ((!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME')); - -		// Should be a string and lowered -		$host = (string) strtolower($host); - -		// If host is equal the cookie domain or the server name (if config is set), then we assume it is valid -		if ((isset($config['cookie_domain']) && $host === $config['cookie_domain']) || (isset($config['server_name']) && $host === $config['server_name'])) -		{ -			return $host; -		} - -		// Is the host actually a IP? If so, we use the IP... (IPv4) -		if (long2ip(ip2long($host)) === $host) -		{ -			return $host; -		} - -		// Now return the hostname (this also removes any port definition). The http:// is prepended to construct a valid URL, hosts never have a scheme assigned -		$host = @parse_url('http://' . $host, PHP_URL_HOST); - -		// Remove any portions not removed by parse_url (#) -		$host = str_replace('#', '', $host); - -		// If, by any means, the host is now empty, we will use a "best approach" way to guess one -		if (empty($host)) -		{ -			if (!empty($config['server_name'])) -			{ -				$host = $config['server_name']; -			} -			else if (!empty($config['cookie_domain'])) -			{ -				$host = (strpos($config['cookie_domain'], '.') === 0) ? substr($config['cookie_domain'], 1) : $config['cookie_domain']; -			} -			else -			{ -				// Set to OS hostname or localhost -				$host = (function_exists('php_uname')) ? php_uname('n') : 'localhost'; -			} -		} - -		// It may be still no valid host, but for sure only a hostname (we may further expand on the cookie domain... if set) -		return $host; +		$this->need_sid = $need_sid;  	}  	/** @@ -198,229 +114,42 @@ class session  	* running on a system which makes such information readily available) and  	* halt if it's above an admin definable limit.  	* -	* @param bool $update_session_page if true the session page gets updated. -	*			This can be set to circumvent certain scripts to update the users last visited page. +	* @param bool $update_session_page[optional] If true the session page gets updated. This can be set to false to circumvent certain scripts to update the users last visited page. +	* @return bool True if the session exist or has been created, else False. +	* @access public  	*/ -	function session_begin($update_session_page = true) +	public function session_begin($update_session_page = true)  	{ -		global $SID, $_SID, $_EXTRA_URL, $db, $config; -  		// Give us some basic information  		$this->time_now				= time();  		$this->cookie_data			= array('u' => 0, 'k' => '');  		$this->update_session_page	= $update_session_page; -		$this->browser				= (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : ''; -		$this->referer				= (!empty($_SERVER['HTTP_REFERER'])) ? htmlspecialchars((string) $_SERVER['HTTP_REFERER']) : ''; -		$this->forwarded_for		= (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? (string) $_SERVER['HTTP_X_FORWARDED_FOR'] : ''; - -		$this->host					= self::extract_current_hostname(); -		$this->page					= self::extract_current_page(PHPBB_ROOT_PATH); - -		// if the forwarded for header shall be checked we have to validate its contents -		if ($config['forwarded_for_check']) -		{ -			$this->forwarded_for = preg_replace('#, +#', ', ', $this->forwarded_for); - -			// split the list of IPs -			$ips = explode(', ', $this->forwarded_for); -			foreach ($ips as $ip) -			{ -				// check IPv4 first, the IPv6 is hopefully only going to be used very seldomly -				if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip)) -				{ -					// contains invalid data, don't use the forwarded for header -					$this->forwarded_for = ''; -					break; -				} -			} -		} -		else -		{ -			$this->forwarded_for = ''; -		} - -		// Add forum to the page for tracking online users - also adding a "x" to the end to properly identify the number -		$forum = request_var('f', 0); -		$this->page['page'] .= ($forum) ? ((strpos($this->page['page'], '?') !== false) ? '&' : '?') . '_f_=' . $forum . 'x' : ''; -		if (request::is_set($config['cookie_name'] . '_sid', request::COOKIE) || request::is_set($config['cookie_name'] . '_u', request::COOKIE)) +		if (request::is_set(phpbb::$config['cookie_name'] . '_sid', request::COOKIE) || request::is_set(phpbb::$config['cookie_name'] . '_u', request::COOKIE))  		{ -			$this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true); -			$this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true); -			$this->session_id 		= request_var($config['cookie_name'] . '_sid', '', false, true); - -			$SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid='; -			$_SID = (defined('NEED_SID')) ? $this->session_id : ''; +			$this->cookie_data['u'] = request_var(phpbb::$config['cookie_name'] . '_u', 0, false, true); +			$this->cookie_data['k'] = request_var(phpbb::$config['cookie_name'] . '_k', '', false, true); +			$this->session_id 		= request_var(phpbb::$config['cookie_name'] . '_sid', '', false, true);  			if (empty($this->session_id))  			{ -				$this->session_id = $_SID = request_var('sid', ''); -				$SID = '?sid=' . $this->session_id; +				$this->session_id = request_var('sid', '');  				$this->cookie_data = array('u' => 0, 'k' => ''); +				$this->need_sid = true;  			}  		}  		else  		{ -			$this->session_id = $_SID = request_var('sid', ''); -			$SID = '?sid=' . $this->session_id; +			$this->session_id = request_var('sid', ''); +			$this->need_sid = true;  		} -		$_EXTRA_URL = array(); +		$this->extra_url = array(); -		// Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests -		// it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip. -		$this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? htmlspecialchars($_SERVER['REMOTE_ADDR']) : ''; -		$this->load = false; - -		// Load limit check (if applicable) -		if ($config['limit_load'] || $config['limit_search_load']) +		// Now check for an existing session +		if ($this->session_exist())  		{ -			if ((function_exists('sys_getloadavg') && $load = sys_getloadavg()) || ($load = explode(' ', @file_get_contents('/proc/loadavg')))) -			{ -				$this->load = array_slice($load, 0, 1); -				$this->load = floatval($this->load[0]); -			} -			else -			{ -				set_config('limit_load', '0'); -				set_config('limit_search_load', '0'); -			} -		} - -		// Is session_id is set or session_id is set and matches the url param if required -		if (!empty($this->session_id) && (!defined('NEED_SID') || $this->session_id === request::variable('sid', '', false, request::GET))) -		{ -			$sql = 'SELECT u.*, s.* -				FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u -				WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "' -					AND u.user_id = s.session_user_id"; -			$result = $db->sql_query($sql); -			$this->data = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); - -			// Did the session exist in the DB? -			if (isset($this->data['user_id'])) -			{ -				// Validate IP length according to admin ... enforces an IP -				// check on bots if admin requires this -//				$quadcheck = ($config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : $config['ip_check']; - -				if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false) -				{ -					$s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']); -					$u_ip = short_ipv6($this->ip, $config['ip_check']); -				} -				else -				{ -					$s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check'])); -					$u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check'])); -				} - -				$s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : ''; -				$u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : ''; - -				$s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : ''; -				$u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : ''; - -				// referer checks -				$check_referer_path = $config['referer_validation'] == REFERER_VALIDATE_PATH; -				$referer_valid = true; -				// we assume HEAD and TRACE to be foul play and thus only whitelist GET -				if ($config['referer_validation'] && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) !== 'get') -				{ -					$referer_valid = $this->validate_referer($check_referer_path); -				} - - -				if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for && $referer_valid) -				{ -					$session_expired = false; - -					// Check whether the session is still valid if we have one -					$method = basename(trim($config['auth_method'])); -					include_once(PHPBB_ROOT_PATH . 'includes/auth/auth_' . $method . '.' . PHP_EXT); - -					$method = 'validate_session_' . $method; -					if (function_exists($method)) -					{ -						if (!$method($this->data)) -						{ -							$session_expired = true; -						} -					} - -					if (!$session_expired) -					{ -						// Check the session length timeframe if autologin is not enabled. -						// Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide. -						if (!$this->data['session_autologin']) -						{ -							if ($this->data['session_time'] < $this->time_now - ($config['session_length'] + 60)) -							{ -								$session_expired = true; -							} -						} -						else if (!$config['allow_autologin'] || ($config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) $config['max_autologin_time']) + 60)) -						{ -							$session_expired = true; -						} -					} - -					if (!$session_expired) -					{ -						// Only update session DB a minute or so after last update or if page changes -						if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page'])) -						{ -							$sql_ary = array('session_time' => $this->time_now); - -							if ($this->update_session_page) -							{ -								$sql_ary['session_page'] = substr($this->page['page'], 0, 199); -							} - -							$db->sql_return_on_error(true); - -							$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . " -								WHERE session_id = '" . $db->sql_escape($this->session_id) . "'"; -							$result = $db->sql_query($sql); - -							$db->sql_return_on_error(false); - -							// If the database is not yet updated, there will be an error due to the session_forum_id -							// @todo REMOVE for 3.0.2 -							if ($result === false) -							{ -								unset($sql_ary['session_forum_id']); - -								$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . " -									WHERE session_id = '" . $db->sql_escape($this->session_id) . "'"; -								$db->sql_query($sql); -							} -						} - -						$this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false; -						$this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false; -						$this->data['user_lang'] = basename($this->data['user_lang']); - -						return true; -					} -				} -				else -				{ -					// Added logging temporarly to help debug bugs... -					if (defined('DEBUG_EXTRA') && $this->data['user_id'] != ANONYMOUS) -					{ -						if ($referer_valid) -						{ -							add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for)); -						} -						else -						{ -							add_log('critical', 'LOG_REFERER_INVALID', $this->referer); -						} -					} -				} -			} +			return true;  		}  		// If we reach here then no (valid) session exists. So we'll create a new one @@ -435,72 +164,43 @@ class session  	* the session for the specific user. In this method we carry out a number of tasks;  	* garbage collection, (search)bot checking, banned user comparison. Basically  	* though this method will result in a new session for a specific user. +	* +	* @param int $user_id[optional] The user id to create the session for. +	* @param bool $set_admin[optional] Set the users admin field to identify him/her as an admin? +	* @param bool $persist_login[optional] Allow persistent login +	* @param bool $viewonline[optional] If false then the user will be logged in as hidden +	* @return bool True if session got created successfully. +	* @access public  	*/ -	function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true) +	public function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)  	{ -		global $SID, $_SID, $db, $config, $cache; - -		$this->data = array(); - -		/* Garbage collection ... remove old sessions updating user information -		// if necessary. It means (potentially) 11 queries but only infrequently -		if ($this->time_now > $config['session_last_gc'] + $config['session_gc']) +		// There is one case where we need to add a "failsafe" user... when we are not able to query the database +		if (!phpbb::registered('db'))  		{ -			$this->session_gc(); -		}*/ +			$this->data = $this->default_data(); +			return true; +		} -		// Do we allow autologin on this board? No? Then override anything -		// that may be requested here -		if (!$config['allow_autologin']) +		// If the data array is filled, chances are high that there was a different session active +		if (sizeof($this->data))  		{ -			$this->cookie_data['k'] = $persist_login = false; +			// Kill the session and do not create a new one +			$this->session_kill(false);  		} -		/** -		* Here we do a bot check, oh er saucy! No, not that kind of bot -		* check. We loop through the list of bots defined by the admin and -		* see if we have any useragent and/or IP matches. If we do, this is a -		* bot, act accordingly -		*/ -		$bot = false; -		$active_bots = cache::obtain_bots(); +		$this->data = array(); -		foreach ($active_bots as $row) +		// Do we allow autologin on this board? No? Then override anything +		// that may be requested here +		if (!phpbb::$config['allow_autologin'])  		{ -			if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->browser)) -			{ -				$bot = $row['user_id']; -			} - -			// If ip is supplied, we will make sure the ip is matching too... -			if ($row['bot_ip'] && ($bot || !$row['bot_agent'])) -			{ -				// Set bot to false, then we only have to set it to true if it is matching -				$bot = false; - -				foreach (explode(',', $row['bot_ip']) as $bot_ip) -				{ -					if (strpos($this->ip, $bot_ip) === 0) -					{ -						$bot = (int) $row['user_id']; -						break; -					} -				} -			} - -			if ($bot) -			{ -				break; -			} +			$this->cookie_data['k'] = $persist_login = false;  		} -		$method = basename(trim($config['auth_method'])); -		include_once(PHPBB_ROOT_PATH . 'includes/auth/auth_' . $method . '.' . PHP_EXT); - -		$method = 'autologin_' . $method; -		if (function_exists($method)) +		// Check for autologin key. ;) +		if ($this->auth !== false && method_exists($this->auth, 'autologin'))  		{ -			$this->data = $method(); +			$this->data = $this->auth->autologin();  			if (sizeof($this->data))  			{ @@ -509,6 +209,9 @@ class session  			}  		} +		// NULL indicates we need to check for a bot later. Sometimes it is apparant that it is not a bot. ;) No need to always check this. +		$bot = NULL; +  		// If we're presented with an autologin key we'll join against it.  		// Else if we've been passed a user_id we'll grab data based on that  		if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data)) @@ -518,10 +221,11 @@ class session  				WHERE u.user_id = ' . (int) $this->cookie_data['u'] . '  					AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ")  					AND k.user_id = u.user_id -					AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'"; -			$result = $db->sql_query($sql); -			$this->data = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); +					AND k.key_id = '" . phpbb::$db->sql_escape(md5($this->cookie_data['k'])) . "'"; +			$result = phpbb::$db->sql_query($sql); +			$this->data = phpbb::$db->sql_fetchrow($result); +			phpbb::$db->sql_freeresult($result); +  			$bot = false;  		}  		else if ($user_id !== false && !sizeof($this->data)) @@ -533,12 +237,18 @@ class session  				FROM ' . USERS_TABLE . '  				WHERE user_id = ' . (int) $this->cookie_data['u'] . '  					AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')'; -			$result = $db->sql_query($sql); -			$this->data = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); +			$result = phpbb::$db->sql_query($sql); +			$this->data = phpbb::$db->sql_fetchrow($result); +			phpbb::$db->sql_freeresult($result); +  			$bot = false;  		} +		if ($bot === NULL) +		{ +			$bot = $this->check_bot(); +		} +  		// If no data was returned one or more of the following occurred:  		// Key didn't match one in the DB  		// User does not exist @@ -564,106 +274,95 @@ class session  					WHERE u.user_id = ' . (int) $bot;  			} -			$result = $db->sql_query($sql); -			$this->data = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); -		} +			$result = phpbb::$db->sql_query($sql); +			$this->data = phpbb::$db->sql_fetchrow($result); +			phpbb::$db->sql_freeresult($result); -		if ($this->data['user_id'] != ANONYMOUS && !$bot) -		{ -			$this->data['session_last_visit'] = (isset($this->data['session_time']) && $this->data['session_time']) ? $this->data['session_time'] : (($this->data['user_lastvisit']) ? $this->data['user_lastvisit'] : time()); +			$this->is_registered = false;  		}  		else  		{ -			$this->data['session_last_visit'] = $this->time_now; +			$this->is_registered = true;  		}  		// Force user id to be integer...  		$this->data['user_id'] = (int) $this->data['user_id']; -		// At this stage we should have a filled data array, defined cookie u and k data. -		// data array should contain recent session info if we're a real user and a recent -		// session exists in which case session_id will also be set - -		// Is user banned? Are they excluded? Won't return on ban, exists within method -		if ($this->data['user_type'] != USER_FOUNDER) +		// Code for ANONYMOUS user, INACTIVE user and BOTS +		if (!$this->is_registered)  		{ -			if (!$config['forwarded_for_check']) -			{ -				$this->check_ban($this->data['user_id'], $this->ip); -			} -			else -			{ -				$ips = explode(', ', $this->forwarded_for); -				$ips[] = $this->ip; -				$this->check_ban($this->data['user_id'], $ips); -			} -		} - -		$this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false; -		$this->data['is_bot'] = ($bot) ? true : false; +			// Set last visit date to 'now' +			$this->data['session_last_visit'] = $this->time_now; +			$this->is_bot = ($bot) ? true : false; -		// If our friend is a bot, we re-assign a previously assigned session -		if ($this->data['is_bot'] && $bot == $this->data['user_id'] && $this->data['session_id']) -		{ -			// Only assign the current session if the ip, browser and forwarded_for match... -			if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false) +			// If our friend is a bot, we re-assign a previously assigned session +			if ($this->is_bot && $bot == $this->data['user_id'] && $this->data['session_id'])  			{ -				$s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']); -				$u_ip = short_ipv6($this->ip, $config['ip_check']); -			} -			else -			{ -				$s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check'])); -				$u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check'])); -			} - -			$s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : ''; -			$u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : ''; - -			$s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : ''; -			$u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : ''; +				if ($this->session_valid(false)) +				{ +					$this->session_id = $this->data['session_id']; -			if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for) -			{ -				$this->session_id = $this->data['session_id']; +					// Only update session DB a minute or so after last update or if page changes +					if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->system['page']['page'])) +					{ +						$this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now; +						$sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0); -				// Only update session DB a minute or so after last update or if page changes -				if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page'])) -				{ -					$this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now; +						if ($this->update_session_page) +						{ +							$sql_ary['session_page'] = substr($this->system['page']['page'], 0, 199); +						} -					$sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0); +						$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . phpbb::$db->sql_build_array('UPDATE', $sql_ary) . " +							WHERE session_id = '" . phpbb::$db->sql_escape($this->session_id) . "'"; +						phpbb::$db->sql_query($sql); -					if ($this->update_session_page) -					{ -						$sql_ary['session_page'] = substr($this->page['page'], 0, 199); +						// Update the last visit time +						$sql = 'UPDATE ' . USERS_TABLE . ' +							SET user_lastvisit = ' . (int) $this->data['session_time'] . ' +							WHERE user_id = ' . (int) $this->data['user_id']; +						phpbb::$db->sql_query($sql);  					} -					$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . " -						WHERE session_id = '" . $db->sql_escape($this->session_id) . "'"; -					$db->sql_query($sql); +					$this->session_id = ''; -					// Update the last visit time -					$sql = 'UPDATE ' . USERS_TABLE . ' -						SET user_lastvisit = ' . (int) $this->data['session_time'] . ' -						WHERE user_id = ' . (int) $this->data['user_id']; -					$db->sql_query($sql); +					return true; +				} +				else +				{ +					// If the ip and browser does not match make sure we only have one bot assigned to one session +					phpbb::$db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']);  				} +			} +		} +		else +		{ +			// Code for registered users +			$this->data['session_last_visit'] = (!empty($this->data['session_time'])) ? $this->data['session_time'] : (($this->data['user_lastvisit']) ? $this->data['user_lastvisit'] : $this->time_now); +			$this->is_bot = false; +		} + +		// At this stage we should have a filled data array, defined cookie u and k data. +		// data array should contain recent session info if we're a real user and a recent +		// session exists in which case session_id will also be set -				$SID = '?sid='; -				$_SID = ''; -				return true; +		// Is user banned? Are they excluded? Won't return on ban, exists within method +		if ($this->data['user_type'] != USER_FOUNDER) +		{ +			if (!phpbb::$config['forwarded_for_check']) +			{ +				$this->check_ban($this->data['user_id'], $this->system['ip']);  			}  			else  			{ -				// If the ip and browser does not match make sure we only have one bot assigned to one session -				$db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']); +				$ips = explode(', ', $this->forwarded_for); +				$ips[] = $this->system['ip']; +				$this->check_ban($this->data['user_id'], $ips);  			}  		} -		$session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false; -		$set_admin = ($set_admin && $this->data['is_registered']) ? true : false; +		$session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->is_registered) ? true : false; +		$set_admin = ($set_admin && $this->is_registered) ? true : false;  		// Create or update the session  		$sql_ary = array( @@ -671,9 +370,9 @@ class session  			'session_start'			=> (int) $this->time_now,  			'session_last_visit'	=> (int) $this->data['session_last_visit'],  			'session_time'			=> (int) $this->time_now, -			'session_browser'		=> (string) trim(substr($this->browser, 0, 149)), -			'session_forwarded_for'	=> (string) $this->forwarded_for, -			'session_ip'			=> (string) $this->ip, +			'session_browser'		=> (string) trim(substr($this->system['browser'], 0, 149)), +			'session_forwarded_for'	=> (string) $this->system['forwarded_for'], +			'session_ip'			=> (string) $this->system['ip'],  			'session_autologin'		=> ($session_autologin) ? 1 : 0,  			'session_admin'			=> ($set_admin) ? 1 : 0,  			'session_viewonline'	=> ($viewonline) ? 1 : 0, @@ -681,29 +380,31 @@ class session  		if ($this->update_session_page)  		{ -			$sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199); +			$sql_ary['session_page'] = (string) substr($this->system['page']['page'], 0, 199);  		} -		$db->sql_return_on_error(true); +		phpbb::$db->sql_return_on_error(true); -		$sql = 'DELETE -			FROM ' . SESSIONS_TABLE . ' -			WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\' -				AND session_user_id = ' . ANONYMOUS; - -		if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows())) +		// Delete old session, if user id now different from anonymous +		if (!defined('IN_ERROR_HANDLER'))  		{ -			// Limit new sessions in 1 minute period (if required) -			if (empty($this->data['session_time']) && $config['active_sessions']) +			// We do not care about the user id, because we assign a new session later +			$sql = 'DELETE +				FROM ' . SESSIONS_TABLE . " +				WHERE session_id = '" . phpbb::$db->sql_escape($this->session_id) . "'"; +			$result = phpbb::$db->sql_query($sql); + +			// If there were no sessions or the session id empty (then the affected rows will be empty too), then we have a brand new session and can check the active sessions limit +			if ((!$result || !phpbb::$db->sql_affectedrows()) && (empty($this->data['session_time']) && phpbb::$config['active_sessions']))  			{  				$sql = 'SELECT COUNT(session_id) AS sessions  					FROM ' . SESSIONS_TABLE . '  					WHERE session_time >= ' . ($this->time_now - 60); -				$result = $db->sql_query($sql); -				$row = $db->sql_fetchrow($result); -				$db->sql_freeresult($result); +				$result = phpbb::$db->sql_query($sql); +				$row = phpbb::$db->sql_fetchrow($result); +				phpbb::$db->sql_freeresult($result); -				if ((int) $row['sessions'] > (int) $config['active_sessions']) +				if ((int) $row['sessions'] > (int) phpbb::$config['active_sessions'])  				{  					header('HTTP/1.1 503 Service Unavailable');  					trigger_error('BOARD_UNAVAILABLE'); @@ -711,15 +412,14 @@ class session  			}  		} -		$this->session_id = $this->data['session_id'] = md5(unique_id()); +		$this->session_id = $this->data['session_id'] = md5(phpbb::$security->unique_id());  		$sql_ary['session_id'] = (string) $this->session_id; -		$sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199); -		$sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); -		$db->sql_query($sql); +		$sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . phpbb::$db->sql_build_array('INSERT', $sql_ary); +		phpbb::$db->sql_query($sql); -		$db->sql_return_on_error(false); +		phpbb::$db->sql_return_on_error(false);  		// Regenerate autologin/persistent login key  		if ($session_autologin) @@ -728,13 +428,11 @@ class session  		}  		// refresh data -		$SID = '?sid=' . $this->session_id; -		$_SID = $this->session_id;  		$this->data = array_merge($this->data, $sql_ary);  		if (!$bot)  		{ -			$cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000); +			$cookie_expire = $this->time_now + ((phpbb::$config['max_autologin_time']) ? 86400 * (int) phpbb::$config['max_autologin_time'] : 31536000);  			$this->set_cookie('u', $this->cookie_data['u'], $cookie_expire);  			$this->set_cookie('k', $this->cookie_data['k'], $cookie_expire); @@ -742,22 +440,24 @@ class session  			unset($cookie_expire); +			// Only one session entry present...  			$sql = 'SELECT COUNT(session_id) AS sessions -					FROM ' . SESSIONS_TABLE . ' -					WHERE session_user_id = ' . (int) $this->data['user_id'] . ' -					AND session_time >= ' . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime']))); -			$result = $db->sql_query($sql); -			$row = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); +				FROM ' . SESSIONS_TABLE . ' +				WHERE session_user_id = ' . (int) $this->data['user_id'] . ' +					AND session_time >= ' . (int) ($this->time_now - (max(phpbb::$config['session_length'], phpbb::$config['form_token_lifetime']))); +			$result = phpbb::$db->sql_query($sql); +			$row = phpbb::$db->sql_fetchrow($result); +			phpbb::$db->sql_freeresult($result);  			if ((int) $row['sessions'] <= 1 || empty($this->data['user_form_salt']))  			{ -				$this->data['user_form_salt'] = unique_id(); +				$this->data['user_form_salt'] = phpbb::$security->unique_id(); +  				// Update the form key  				$sql = 'UPDATE ' . USERS_TABLE . ' -					SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\' +					SET user_form_salt = \'' . phpbb::$db->sql_escape($this->data['user_form_salt']) . '\'  					WHERE user_id = ' . (int) $this->data['user_id']; -				$db->sql_query($sql); +				phpbb::$db->sql_query($sql);  			}  		}  		else @@ -768,10 +468,9 @@ class session  			$sql = 'UPDATE ' . USERS_TABLE . '  				SET user_lastvisit = ' . (int) $this->data['session_time'] . '  				WHERE user_id = ' . (int) $this->data['user_id']; -			$db->sql_query($sql); +			phpbb::$db->sql_query($sql); -			$SID = '?sid='; -			$_SID = ''; +			$this->session_id = '';  		}  		return true; @@ -784,24 +483,21 @@ class session  	* It resets cookie information (destroying any autologin key within that cookie data)  	* and update the users information from the relevant session data. It will then  	* grab guest user information. +	* +	* @param bool $new_session[optional] If true a new session will be generated after the original one got killed. +	* @access public  	*/ -	function session_kill($new_session = true) +	public function session_kill($new_session = true)  	{ -		global $SID, $_SID, $db, $config; -  		$sql = 'DELETE FROM ' . SESSIONS_TABLE . " -			WHERE session_id = '" . $db->sql_escape($this->session_id) . "' +			WHERE session_id = '" . phpbb::$db->sql_escape($this->session_id) . "'  				AND session_user_id = " . (int) $this->data['user_id']; -		$db->sql_query($sql); +		phpbb::$db->sql_query($sql);  		// Allow connecting logout with external auth method logout -		$method = basename(trim($config['auth_method'])); -		include_once(PHPBB_ROOT_PATH . 'includes/auth/auth_' . $method . '.' . PHP_EXT); - -		$method = 'logout_' . $method; -		if (function_exists($method)) +		if ($this->auth !== false && method_exists($this->auth, 'logout'))  		{ -			$method($this->data, $new_session); +			$this->auth->logout($this, $new_session);  		}  		if ($this->data['user_id'] != ANONYMOUS) @@ -815,14 +511,14 @@ class session  			$sql = 'UPDATE ' . USERS_TABLE . '  				SET user_lastvisit = ' . (int) $this->data['session_time'] . '  				WHERE user_id = ' . (int) $this->data['user_id']; -			$db->sql_query($sql); +			phpbb::$db->sql_query($sql);  			if ($this->cookie_data['k'])  			{  				$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '  					WHERE user_id = ' . (int) $this->data['user_id'] . " -						AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'"; -				$db->sql_query($sql); +						AND key_id = '" . phpbb::$db->sql_escape(md5($this->cookie_data['k'])) . "'"; +				phpbb::$db->sql_query($sql);  			}  			// Reset the data array @@ -831,9 +527,9 @@ class session  			$sql = 'SELECT *  				FROM ' . USERS_TABLE . '  				WHERE user_id = ' . ANONYMOUS; -			$result = $db->sql_query($sql); -			$this->data = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); +			$result = phpbb::$db->sql_query($sql); +			$this->data = phpbb::$db->sql_fetchrow($result); +			phpbb::$db->sql_freeresult($result);  		}  		$cookie_expire = $this->time_now - 31536000; @@ -842,16 +538,13 @@ class session  		$this->set_cookie('sid', '', $cookie_expire);  		unset($cookie_expire); -		$SID = '?sid='; -		$this->session_id = $_SID = ''; +		$this->session_id = '';  		// To make sure a valid session is created we create one for the anonymous user  		if ($new_session)  		{  			$this->session_create(ANONYMOUS);  		} - -		return true;  	}  	/** @@ -863,11 +556,11 @@ class session  	* data before those sessions are destroyed. In addition this method  	* removes autologin key information that is older than an admin defined  	* limit. +	* +	* @access public  	*/ -	function session_gc() +	public function session_gc()  	{ -		global $db, $config; -  		$batch_size = 10;  		if (!$this->time_now) @@ -878,38 +571,38 @@ class session  		// Firstly, delete guest sessions  		$sql = 'DELETE FROM ' . SESSIONS_TABLE . '  			WHERE session_user_id = ' . ANONYMOUS . ' -				AND session_time < ' . (int) ($this->time_now - $config['session_length']); -		$db->sql_query($sql); +				AND session_time < ' . (int) ($this->time_now - phpbb::$config['session_length']); +		phpbb::$db->sql_query($sql);  		// Get expired sessions, only most recent for each user  		$sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time  			FROM ' . SESSIONS_TABLE . ' -			WHERE session_time < ' . ($this->time_now - $config['session_length']) . ' +			WHERE session_time < ' . ($this->time_now - phpbb::$config['session_length']) . '  			GROUP BY session_user_id, session_page'; -		$result = $db->sql_query_limit($sql, $batch_size); +		$result = phpbb::$db->sql_query_limit($sql, $batch_size);  		$del_user_id = array();  		$del_sessions = 0; -		while ($row = $db->sql_fetchrow($result)) +		while ($row = phpbb::$db->sql_fetchrow($result))  		{  			$sql = 'UPDATE ' . USERS_TABLE . ' -				SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "' +				SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . phpbb::$db->sql_escape($row['session_page']) . "'  				WHERE user_id = " . (int) $row['session_user_id']; -			$db->sql_query($sql); +			phpbb::$db->sql_query($sql);  			$del_user_id[] = (int) $row['session_user_id'];  			$del_sessions++;  		} -		$db->sql_freeresult($result); +		phpbb::$db->sql_freeresult($result);  		if (sizeof($del_user_id))  		{  			// Delete expired sessions  			$sql = 'DELETE FROM ' . SESSIONS_TABLE . ' -				WHERE ' . $db->sql_in_set('session_user_id', $del_user_id) . ' -					AND session_time < ' . ($this->time_now - $config['session_length']); -			$db->sql_query($sql); +				WHERE ' . phpbb::$db->sql_in_set('session_user_id', $del_user_id) . ' +					AND session_time < ' . ($this->time_now - phpbb::$config['session_length']); +			phpbb::$db->sql_query($sql);  		}  		if ($del_sessions < $batch_size) @@ -918,19 +611,19 @@ class session  			// called again to delete other sessions  			set_config('session_last_gc', $this->time_now, true); -			if ($config['max_autologin_time']) +			if (phpbb::$config['max_autologin_time'])  			{  				$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' -					WHERE last_login < ' . (time() - (86400 * (int) $config['max_autologin_time'])); -				$db->sql_query($sql); +					WHERE last_login < ' . (time() - (86400 * (int) phpbb::$config['max_autologin_time'])); +				phpbb::$db->sql_query($sql);  			} -			 +  			// only called from CRON; should be a safe workaround until the infrastructure gets going  			if (!class_exists('captcha_factory'))  			{  				include(PHPBB_ROOT_PATH . "includes/captcha/captcha_factory." . PHP_EXT);  			} -			captcha_factory::garbage_collect($config['captcha_plugin']); +			captcha_factory::garbage_collect(phpbb::$config['captcha_plugin']);  		}  		return; @@ -938,23 +631,20 @@ class session  	/** -	* Sets a cookie -	*  	* Sets a cookie of the given name with the specified data for the given length of time. If no time is specified, a session cookie will be set.  	*  	* @param string $name		Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.  	* @param string $cookiedata	The data to hold within the cookie  	* @param int $cookietime	The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set. +	* @access public  	*/ -	function set_cookie($name, $cookiedata, $cookietime) +	public function set_cookie($name, $cookiedata, $cookietime)  	{ -		global $config; - -		$name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata); +		$name_data = rawurlencode(phpbb::$config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);  		$expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime); -		$domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain']; +		$domain = (!phpbb::$config['cookie_domain'] || phpbb::$config['cookie_domain'] == 'localhost' || phpbb::$config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . phpbb::$config['cookie_domain']; -		header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false); +		header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . phpbb::$config['cookie_path'] . $domain . ((!phpbb::$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);  	}  	/** @@ -965,12 +655,15 @@ class session  	* this routine does not return on finding a banned user, it outputs a relevant  	* message and stops execution.  	* -	* @param string|array	$user_ips	Can contain a string with one IP or an array of multiple IPs +	* @param int $user_id[optional] The user id to check. If false then do not check user ids +	* @param mixed $user_ips[optional] Can contain a string with one IP or an array of multiple IPs. If false then no ips are checked. +	* @param int $user_email[optional] The email address to check +	* @param bool $return If false then the banned message is displayed and script halted +	* @return mixed True if banned and no reason given. False if not banned. A ban reason if banned and ban reason given. Check for !== false. +	* @access public  	*/ -	function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false) +	public function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)  	{ -		global $config, $db; -  		if (defined('IN_CHECK_BAN'))  		{  			return; @@ -1020,10 +713,10 @@ class session  		}  		$sql .= (sizeof($where_sql)) ? implode(' AND ', $where_sql) : ''; -		$result = $db->sql_query($sql, $cache_ttl); +		$result = phpbb::$db->sql_query($sql, $cache_ttl);  		$ban_triggered_by = 'user'; -		while ($row = $db->sql_fetchrow($result)) +		while ($row = phpbb::$db->sql_fetchrow($result))  		{  			if ($row['ban_end'] && $row['ban_end'] < time())  			{ @@ -1081,12 +774,10 @@ class session  				}  			}  		} -		$db->sql_freeresult($result); +		phpbb::$db->sql_freeresult($result);  		if ($banned && !$return)  		{ -			global $template; -  			// If the session is empty we need to create a valid one...  			if (empty($this->session_id))  			{ @@ -1107,7 +798,7 @@ class session  			if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)  			{  				$this->setup('ucp'); -				$this->data['is_registered'] = $this->data['is_bot'] = false; +				$this->is_registered = $this->is_bot = false;  				// Set as a precaution to allow login_box() handling this case correctly as well as this function not being executed again.  				define('IN_CHECK_BAN', 1); @@ -1130,7 +821,7 @@ class session  			$till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';  			$message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM'; -			$message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . $config['board_contact'] . '">', '</a>'); +			$message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . phpbb::$config['board_contact'] . '">', '</a>');  			$message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';  			$message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>'; @@ -1159,13 +850,15 @@ class session  	*  	* @author satmd (from the php manual)  	* @param string $mode register/post - spamcop for example is ommitted for posting -	* @return false if ip is not blacklisted, else an array([checked server], [lookup]) +	* @param string $ip[optional] The ip to check. If false then the current IP is used +	* @return mixed false if ip is not blacklisted, else an array([checked server], [lookup]) +	* @access public  	*/ -	function check_dnsbl($mode, $ip = false) +	public function check_dnsbl($mode, $ip = false)  	{  		if ($ip === false)  		{ -			$ip = $this->ip; +			$ip = $this->system['ip'];  		}  		$dnsbl_check = array( @@ -1208,41 +901,6 @@ class session  	}  	/** -	* Check if URI is blacklisted -	* This should be called only where absolutly necessary, for example on the submitted website field -	* This function is not in use at the moment and is only included for testing purposes, it may not work at all! -	* This means it is untested at the moment and therefore commented out -	* -	* @param string $uri URI to check -	* @return true if uri is on blacklist, else false. Only blacklist is checked (~zero FP), no grey lists -	function check_uribl($uri) -	{ -		// Normally parse_url() is not intended to parse uris -		// We need to get the top-level domain name anyway... change. -		$uri = parse_url($uri); - -		if ($uri === false || empty($uri['host'])) -		{ -			return false; -		} - -		$uri = trim($uri['host']); - -		if ($uri) -		{ -			// One problem here... the return parameter for the "windows" method is different from what -			// we expect... this may render this check useless... -			if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true) -			{ -				return true; -			} -		} - -		return false; -	} -	*/ - -	/**  	* Set/Update a persistent login key  	*  	* This method creates or updates a persistent session key. When a user makes @@ -1251,21 +909,24 @@ class session  	* DB and cookie. Multiple keys may exist for each user representing different  	* browsers or locations. As with _any_ non-secure-socket no passphrase login this  	* remains vulnerable to exploit. +	* +	* @param int $user_id[optional] The user id. If false the current users user id will be used +	* @param object $key[optional] A login key. If false then the current users login key stored within the cookie will be used +	* @param object $user_ip[optional] The users ip. If false, then the current users IP will be used +	* @access public  	*/ -	function set_login_key($user_id = false, $key = false, $user_ip = false) +	public function set_login_key($user_id = false, $key = false, $user_ip = false)  	{ -		global $config, $db; -  		$user_id = ($user_id === false) ? $this->data['user_id'] : $user_id; -		$user_ip = ($user_ip === false) ? $this->ip : $user_ip; +		$user_ip = ($user_ip === false) ? $this->system['ip'] : $user_ip;  		$key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key; -		$key_id = unique_id(hexdec(substr($this->session_id, 0, 8))); +		$key_id = phpbb::$security->unique_id(hexdec(substr($this->session_id, 0, 8)));  		$sql_ary = array(  			'key_id'		=> (string) md5($key_id), -			'last_ip'		=> (string) $this->ip, -			'last_login'	=> (int) time() +			'last_ip'		=> (string) $this->system['ip'], +			'last_login'	=> (int) $this->time_now,  		);  		if (!$key) @@ -1278,19 +939,17 @@ class session  		if ($key)  		{  			$sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . ' -				SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' +				SET ' . phpbb::$db->sql_build_array('UPDATE', $sql_ary) . '  				WHERE user_id = ' . (int) $user_id . " -					AND key_id = '" . $db->sql_escape(md5($key)) . "'"; +					AND key_id = '" . phpbb::$db->sql_escape(md5($key)) . "'";  		}  		else  		{ -			$sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary); +			$sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . phpbb::$db->sql_build_array('INSERT', $sql_ary);  		} -		$db->sql_query($sql); +		phpbb::$db->sql_query($sql);  		$this->cookie_data['k'] = $key_id; - -		return false;  	}  	/** @@ -1298,25 +957,26 @@ class session  	*  	* This method removes all current login keys for a specified (or the current)  	* user. It will be called on password change to render old keys unusable +	* +	* @param int $user_id[optional] The user id. If false then the current users user id is used. +	* @access public  	*/ -	function reset_login_keys($user_id = false) +	public function reset_login_keys($user_id = false)  	{ -		global $config, $db; -  		$user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;  		$sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '  			WHERE user_id = ' . (int) $user_id; -		$db->sql_query($sql); +		phpbb::$db->sql_query($sql);  		// Let's also clear any current sessions for the specified user_id  		// If it's the current user then we'll leave this session intact  		$sql_where = 'session_user_id = ' . (int) $user_id; -		$sql_where .= ($user_id === $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : ''; +		$sql_where .= ($user_id === $this->data['user_id']) ? " AND session_id <> '" . phpbb::$db->sql_escape($this->session_id) . "'" : '';  		$sql = 'DELETE FROM ' . SESSIONS_TABLE . "  			WHERE $sql_where"; -		$db->sql_query($sql); +		phpbb::$db->sql_query($sql);  		// We're changing the password of the current user and they have a key  		// Lets regenerate it to be safe @@ -1326,861 +986,271 @@ class session  		}  	} - -	/** -	* Check if the request originated from the same page. -	* @param bool $check_script_path If true, the path will be checked as well -	*/ -	function validate_referer($check_script_path = false) -	{ -		// no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason) -		if (empty($this->referer) || empty($this->host)) -		{ -			return true; -		} - -		$host = htmlspecialchars($this->host); -		$ref = substr($this->referer, strpos($this->referer, '://') + 3); - -		if (!(stripos($ref, $host) === 0)) -		{ -			return false; -		} -		else if ($check_script_path && rtrim($this->page['root_script_path'], '/') !== '') -		{ -			$ref = substr($ref, strlen($host)); -			$server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT'); - -			if ($server_port !== 80 && $server_port !== 443 && stripos($ref, ":$server_port") === 0) -			{ -				$ref = substr($ref, strlen(":$server_port")); -			} - -			if (!(stripos(rtrim($ref, '/'), rtrim($this->page['root_script_path'], '/')) === 0)) -			{ -				return false; -			} -		} - -		return true; -	} - - -	function unset_admin() -	{ -		global $db; -		$sql = 'UPDATE ' . SESSIONS_TABLE . ' -			SET session_admin = 0 -			WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\''; -		$db->sql_query($sql); -	} -} - - -/** -* Base user class -* -* This is the overarching class which contains (through session extend) -* all methods utilised for user functionality during a session. -* -* @package phpBB3 -*/ -class user extends session -{ -	var $lang = array(); -	var $help = array(); -	var $theme = array(); -	var $date_format; -	var $timezone; -	var $dst; - -	var $lang_name = false; -	var $lang_id = false; -	var $lang_path; -	var $img_lang; -	var $img_array = array(); - -	// Able to add new option (id 7) -	var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10); -	var $keyvalues = array(); -  	/** -	* Constructor to set the lang path -	*/ -	public function __construct() -	{ -		$this->lang_path = PHPBB_ROOT_PATH . 'language/'; -	} - -	/** -	* Function to set custom language path (able to use directory outside of phpBB) +	* Reset all admin sessions  	* -	* @param string $lang_path New language path used.  	* @access public  	*/ -	function set_custom_lang_path($lang_path) +	public function unset_admin()  	{ -		$this->lang_path = $lang_path; - -		if (substr($this->lang_path, -1) != '/') -		{ -			$this->lang_path .= '/'; -		} +		$sql = 'UPDATE ' . SESSIONS_TABLE . " +			SET session_admin = 0 +			WHERE session_id = '" . phpbb::$db->sql_escape($this->session_id) . "'"; +		phpbb::$db->sql_query($sql);  	}  	/** -	* Setup basic user-specific items (style, language, ...) +	* Check if a valid, non-expired session exist. Also make sure it errors out correctly if we do not have a db-setup yet. ;) +	* +	* @return bool true if a valid, non-expired session exist +	* @access private  	*/ -	function setup($lang_set = false, $style = false) +	private function session_exist()  	{ -		global $db, $template, $config, $auth, $cache; - -		if ($this->data['user_id'] != ANONYMOUS) +		// If session is empty or does not match the session within the URL (if required - set by NEED_SID), then we need a new session +		if (empty($this->session_id) || ($this->need_sid && $this->session_id !== request::variable('sid', '', false, request::GET)))  		{ -			$this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common." . PHP_EXT)) ? $this->data['user_lang'] : basename($config['default_lang']); -			$this->date_format = $this->data['user_dateformat']; -			$this->timezone = $this->data['user_timezone'] * 3600; -			$this->dst = $this->data['user_dst'] * 3600; -		} -		else -		{ -			$this->lang_name = basename($config['default_lang']); -			$this->date_format = $config['default_dateformat']; -			$this->timezone = $config['board_timezone'] * 3600; -			$this->dst = $config['board_dst'] * 3600; - -			/** -			* If a guest user is surfing, we try to guess his/her language first by obtaining the browser language -			* If re-enabled we need to make sure only those languages installed are checked -			* Commented out so we do not loose the code. - -			if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) -			{ -				$accept_lang_ary = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']); - -				foreach ($accept_lang_ary as $accept_lang) -				{ -					// Set correct format ... guess full xx_YY form -					$accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2)); -					$accept_lang = basename($accept_lang); - -					if (file_exists($this->lang_path . $accept_lang . "/common." . PHP_EXT)) -					{ -						$this->lang_name = $config['default_lang'] = $accept_lang; -						break; -					} -					else -					{ -						// No match on xx_YY so try xx -						$accept_lang = substr($accept_lang, 0, 2); -						$accept_lang = basename($accept_lang); - -						if (file_exists($this->lang_path . $accept_lang . "/common." . PHP_EXT)) -						{ -							$this->lang_name = $config['default_lang'] = $accept_lang; -							break; -						} -					} -				} -			} -			*/ -		} - -		// We include common language file here to not load it every time a custom language file is included -		$lang = &$this->lang; - -		if ((@include $this->lang_path . $this->lang_name . "/common." . PHP_EXT) === false) -		{ -			die('Language file ' . $this->lang_path . $this->lang_name . "/common." . PHP_EXT . " couldn't be opened."); +			return false;  		} -		$this->add_lang($lang_set); -		unset($lang_set); - -		if (request::variable('style', false, false, request::GET) && $auth->acl_get('a_styles')) -		{ -			global $SID, $_EXTRA_URL; - -			$style = request_var('style', 0); -			$SID .= '&style=' . $style; -			$_EXTRA_URL = array('style=' . $style); -		} -		else +		// If the db is not initialized/registered, then we also need a new session (we are not able to go forward then... +		if (!phpbb::registered('db'))  		{ -			// Set up style -			$style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']); +			return false;  		} -		$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name -			FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i -			WHERE s.style_id = $style -				AND t.template_id = s.template_id -				AND c.theme_id = s.theme_id -				AND i.imageset_id = s.imageset_id"; -		$result = $db->sql_query($sql, 3600); -		$this->theme = $db->sql_fetchrow($result); -		$db->sql_freeresult($result); - -		// User has wrong style -		if (!$this->theme && $style == $this->data['user_style']) -		{ -			$style = $this->data['user_style'] = $config['default_style']; - -			$sql = 'UPDATE ' . USERS_TABLE . " -				SET user_style = $style -				WHERE user_id = {$this->data['user_id']}"; -			$db->sql_query($sql); - -			$sql = 'SELECT s.style_id, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name -				FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i -				WHERE s.style_id = $style -					AND t.template_id = s.template_id -					AND c.theme_id = s.theme_id -					AND i.imageset_id = s.imageset_id"; -			$result = $db->sql_query($sql, 3600); -			$this->theme = $db->sql_fetchrow($result); -			$db->sql_freeresult($result); -		} +		// Now finally check the db for our provided session +		$sql = 'SELECT u.*, s.* +			FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u +			WHERE s.session_id = '" . phpbb::$db->sql_escape($this->session_id) . "' +				AND u.user_id = s.session_user_id"; +		$result = phpbb::$db->sql_query($sql); +		$row = phpbb::$db->sql_fetchrow($result); +		phpbb::$db->sql_freeresult($result); -		if (!$this->theme) +		// Also new session if it has not been found. ;) +		if (!$row)  		{ -			trigger_error('Could not get style data', E_USER_ERROR); +			return false;  		} -		// Now parse the cfg file and cache it, -		// we are only interested in the theme configuration for now -		$parsed_items = cache::obtain_cfg_item($this->theme, 'theme'); - -		$check_for = array( -			'parse_css_file'	=> (int) 0, -			'pagination_sep'	=> (string) ', ' -		); - -		foreach ($check_for as $key => $default_value) -		{ -			$this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value; -			settype($this->theme[$key], gettype($default_value)); - -			if (is_string($default_value)) -			{ -				$this->theme[$key] = htmlspecialchars($this->theme[$key]); -			} -		} +		$this->data = $row; -		// If the style author specified the theme needs to be cached -		// (because of the used paths and variables) than make sure it is the case. -		// For example, if the theme uses language-specific images it needs to be stored in db. -		if (!$this->theme['theme_storedb'] && $this->theme['parse_css_file']) +		// Now check the ip, the browser, forwarded for and referer +		if (!$this->session_valid())  		{ -			$this->theme['theme_storedb'] = 1; - -			$stylesheet = file_get_contents(PHPBB_ROOT_PATH . "styles/{$this->theme['theme_path']}/theme/stylesheet.css"); -			// Match CSS imports -			$matches = array(); -			preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches); - -			if (sizeof($matches)) -			{ -				$content = ''; -				foreach ($matches[0] as $idx => $match) -				{ -					if ($content = @file_get_contents(PHPBB_ROOT_PATH . "styles/{$this->theme['theme_path']}/theme/" . $matches[1][$idx])) -					{ -						$content = trim($content); -					} -					else -					{ -						$content = ''; -					} -					$stylesheet = str_replace($match, $content, $stylesheet); -				} -				unset($content); -			} - -			$stylesheet = str_replace('./', 'styles/' . $this->theme['theme_path'] . '/theme/', $stylesheet); - -			$sql_ary = array( -				'theme_data'	=> $stylesheet, -				'theme_mtime'	=> time(), -				'theme_storedb'	=> 1 -			); - -			$sql = 'UPDATE ' . STYLES_THEME_TABLE . ' -				SET ' . $db->sql_build_array('UPDATE', $sql_ary) . ' -				WHERE theme_id = ' . $this->theme['theme_id']; -			$db->sql_query($sql); - -			unset($sql_ary); +			return false;  		} -		$template->set_template(); +		// Ok, we are not finished yet. We need to know if the session is expired -		$this->img_lang = (file_exists(PHPBB_ROOT_PATH . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . $this->lang_name)) ? $this->lang_name : $config['default_lang']; - -		$sql = 'SELECT image_name, image_filename, image_lang, image_height, image_width -			FROM ' . STYLES_IMAGESET_DATA_TABLE . ' -			WHERE imageset_id = ' . $this->theme['imageset_id'] . " -			AND image_filename <> '' -			AND image_lang IN ('" . $db->sql_escape($this->img_lang) . "', '')"; -		$result = $db->sql_query($sql, 3600); - -		$localised_images = false; -		while ($row = $db->sql_fetchrow($result)) +		// Check whether the session is still valid if we have one +		if ($this->auth !== false && method_exists($this->auth, 'validate_session'))  		{ -			if ($row['image_lang']) +			if (!$this->auth->validate_session($this))  			{ -				$localised_images = true; +				return false;  			} - -			$row['image_filename'] = rawurlencode($row['image_filename']); -			$this->img_array[$row['image_name']] = $row;  		} -		$db->sql_freeresult($result); -		// there were no localised images, try to refresh the localised imageset for the user's language -		if (!$localised_images) +		// Check the session length timeframe if autologin is not enabled. +		// Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide. +		if (!$this->data['session_autologin'])  		{ -			// Attention: this code ignores the image definition list from acp_styles and just takes everything -			// that the config file contains -			$sql_ary = array(); - -			$db->sql_transaction('begin'); - -			$sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . ' -				WHERE imageset_id = ' . $this->theme['imageset_id'] . ' -					AND image_lang = \'' . $db->sql_escape($this->img_lang) . '\''; -			$result = $db->sql_query($sql); - -			if (@file_exists(PHPBB_ROOT_PATH . "styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg")) +			if ($this->data['session_time'] < $this->time_now - (phpbb::$config['session_length'] + 60))  			{ -				$cfg_data_imageset_data = parse_cfg_file(PHPBB_ROOT_PATH . "styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg"); -				foreach ($cfg_data_imageset_data as $image_name => $value) -				{ -					if (strpos($value, '*') !== false) -					{ -						if (substr($value, -1, 1) === '*') -						{ -							list($image_filename, $image_height) = explode('*', $value); -							$image_width = 0; -						} -						else -						{ -							list($image_filename, $image_height, $image_width) = explode('*', $value); -						} -					} -					else -					{ -						$image_filename = $value; -						$image_height = $image_width = 0; -					} - -					if (strpos($image_name, 'img_') === 0 && $image_filename) -					{ -						$image_name = substr($image_name, 4); -						$sql_ary[] = array( -							'image_name'		=> (string) $image_name, -							'image_filename'	=> (string) $image_filename, -							'image_height'		=> (int) $image_height, -							'image_width'		=> (int) $image_width, -							'imageset_id'		=> (int) $this->theme['imageset_id'], -							'image_lang'		=> (string) $this->img_lang, -						); -					} -				} -			} - -			if (sizeof($sql_ary)) -			{ -				$db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary); -				$db->sql_transaction('commit'); -				$cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE); - -				add_log('admin', 'LOG_IMAGESET_LANG_REFRESHED', $this->theme['imageset_name'], $this->img_lang); -			} -			else -			{ -				$db->sql_transaction('commit'); -				add_log('admin', 'LOG_IMAGESET_LANG_MISSING', $this->theme['imageset_name'], $this->img_lang); +				return false;  			}  		} - -		// Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes... -		// After calling it we continue script execution... -		phpbb_user_session_handler(); - -		// If this function got called from the error handler we are finished here. -		if (defined('IN_ERROR_HANDLER')) -		{ -			return; -		} - -		// Disable board if the install/ directory is still present -		// For the brave development army we do not care about this, else we need to comment out this everytime we develop locally -		if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists(PHPBB_ROOT_PATH . 'install')) +		else if (!phpbb::$config['allow_autologin'] || (phpbb::$config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) phpbb::$config['max_autologin_time']) + 60))  		{ -			// Adjust the message slightly according to the permissions -			if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_')) -			{ -				$message = 'REMOVE_INSTALL'; -			} -			else -			{ -				$message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; -			} -			trigger_error($message); +			return false;  		} -		// Is board disabled and user not an admin or moderator? -		if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) +		// Only update session DB a minute or so after last update or if page changes +		if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->system['page']['page']))  		{ -			header('HTTP/1.1 503 Service Unavailable'); - -			$message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE'; -			trigger_error($message); -		} +			$sql_ary = array('session_time' => $this->time_now); -		// Is load exceeded? -		if ($config['limit_load'] && $this->load !== false) -		{ -			if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN')) +			if ($this->update_session_page)  			{ -				// Set board disabled to true to let the admins/mods get the proper notification -				$config['board_disable'] = '1'; - -				if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_')) -				{ -					header('HTTP/1.1 503 Service Unavailable'); -					trigger_error('BOARD_UNAVAILABLE'); -				} +				$sql_ary['session_page'] = substr($this->system['page']['page'], 0, 199); +				$sql_ary['session_forum_id'] = $this->system['page']['forum'];  			} -		} -		if (isset($this->data['session_viewonline'])) -		{ -			// Make sure the user is able to hide his session -			if (!$this->data['session_viewonline']) -			{ -				// Reset online status if not allowed to hide the session... -				if (!$auth->acl_get('u_hideonline')) -				{ -					$sql = 'UPDATE ' . SESSIONS_TABLE . ' -						SET session_viewonline = 1 -						WHERE session_user_id = ' . $this->data['user_id']; -					$db->sql_query($sql); -					$this->data['session_viewonline'] = 1; -				} -			} -			else if (!$this->data['user_allow_viewonline']) -			{ -				// the user wants to hide and is allowed to  -> cloaking device on. -				if ($auth->acl_get('u_hideonline')) -				{ -					$sql = 'UPDATE ' . SESSIONS_TABLE . ' -						SET session_viewonline = 0 -						WHERE session_user_id = ' . $this->data['user_id']; -					$db->sql_query($sql); -					$this->data['session_viewonline'] = 0; -				} -			} +			$sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . phpbb::$db->sql_build_array('UPDATE', $sql_ary) . " +				WHERE session_id = '" . phpbb::$db->sql_escape($this->session_id) . "'"; +			phpbb::$db->sql_query($sql);  		} +		$this->is_registered = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false; +		$this->is_bot = (!$this->is_registered && $this->data['user_id'] != ANONYMOUS) ? true : false; +		$this->data['user_lang'] = basename($this->data['user_lang']); -		// Does the user need to change their password? If so, redirect to the -		// ucp profile reg_details page ... of course do not redirect if we're already in the ucp -		if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && $this->data['is_registered'] && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400)) -		{ -			if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != 'ucp.' . PHP_EXT) -			{ -				redirect(append_sid('ucp', 'i=profile&mode=reg_details')); -			} -		} - -		return; +		return true;  	}  	/** -	* More advanced language substitution -	* Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms. -	* Params are the language key and the parameters to be substituted. -	* This function/functionality is inspired by SHS` and Ashe. +	* Check if the request originated from the same page.  	* -	* Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp> +	* @param bool $check_script_path[optional] If true, the path will be checked as well +	* @return bool True if the referer is valid +	* @access private  	*/ -	function lang() +	private function validate_referer($check_script_path = false)  	{ -		$args = func_get_args(); -		$key = $args[0]; - -		if (is_array($key)) -		{ -			$lang = &$this->lang[array_shift($key)]; - -			foreach ($key as $_key) -			{ -				$lang = &$lang[$_key]; -			} -		} -		else +		// no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason) +		if (empty($this->system['referer']) || empty($this->system['host']))  		{ -			$lang = &$this->lang[$key]; +			return true;  		} -		// Return if language string does not exist -		if (!isset($lang) || (!is_string($lang) && !is_array($lang))) +		// Specialchar host, because it's the only one not specialchared +		$host = htmlspecialchars($this->system['host']); +		$ref = substr($this->system['referer'], strpos($this->system['referer'], '://') + 3); + +		if (!(stripos($ref, $host) === 0))  		{ -			return $key; +			return false;  		} - -		// If the language entry is a string, we simply mimic sprintf() behaviour -		if (is_string($lang)) +		else if ($check_script_path && rtrim($this->system['page']['root_script_path'], '/') !== '')  		{ -			if (sizeof($args) == 1) +			$ref = substr($ref, strlen($host)); +			$server_port = $this->system['port']; + +			if ($server_port !== 80 && $server_port !== 443 && stripos($ref, ":$server_port") === 0)  			{ -				return $lang; +				$ref = substr($ref, strlen(":$server_port"));  			} -			// Replace key with language entry and simply pass along... -			$args[0] = $lang; -			return call_user_func_array('sprintf', $args); -		} - -		// It is an array... now handle different nullar/singular/plural forms -		$key_found = false; - -		// We now get the first number passed and will select the key based upon this number -		for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++) -		{ -			if (is_int($args[$i])) +			if (!(stripos(rtrim($ref, '/'), rtrim($this->system['page']['root_script_path'], '/')) === 0))  			{ -				$numbers = array_keys($lang); - -				foreach ($numbers as $num) -				{ -					if ($num > $args[$i]) -					{ -						break; -					} - -					$key_found = $num; -				} +				return false;  			}  		} -		// Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form) -		if ($key_found === false) -		{ -			$numbers = array_keys($lang); -			$key_found = end($numbers); -		} - -		// Use the language string we determined and pass it to sprintf() -		$args[0] = $lang[$key_found]; -		return call_user_func_array('sprintf', $args); +		return true;  	}  	/** -	* Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion) -	* -	* @param mixed $lang_set specifies the language entries to include -	* @param bool $use_db internal variable for recursion, do not use -	* @param bool $use_help internal variable for recursion, do not use +	* Fill data array with a "faked" user account  	* -	* Examples: -	* <code> -	* $lang_set = array('posting', 'help' => 'faq'); -	* $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq')) -	* $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq')) -	* $lang_set = 'posting' -	* $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting')) -	* </code> -	*/ -	function add_lang($lang_set, $use_db = false, $use_help = false) -	{ -		if (is_array($lang_set)) -		{ -			foreach ($lang_set as $key => $lang_file) -			{ -				// Please do not delete this line. -				// We have to force the type here, else [array] language inclusion will not work -				$key = (string) $key; - -				if ($key == 'db') -				{ -					$this->add_lang($lang_file, true, $use_help); -				} -				else if ($key == 'help') -				{ -					$this->add_lang($lang_file, $use_db, true); -				} -				else if (!is_array($lang_file)) -				{ -					$this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help); -				} -				else -				{ -					$this->add_lang($lang_file, $use_db, $use_help); -				} -			} -			unset($lang_set); -		} -		else if ($lang_set) -		{ -			$this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help); -		} -	} - -	/** -	* Set language entry (called by add_lang) +	* @return array default user data array  	* @access private  	*/ -	function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false) +	private function default_data()  	{ -		// Make sure the language name is set (if the user setup did not happen it is not set) -		if (!$this->lang_name) -		{ -			global $config; -			$this->lang_name = basename($config['default_lang']); -		} - -		// $lang == $this->lang -		// $help == $this->help -		// - add appropriate variables here, name them as they are used within the language file... -		if (!$use_db) -		{ -			if ($use_help && strpos($lang_file, '/') !== false) -			{ -				$language_filename = $this->lang_path . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . PHP_EXT; -			} -			else -			{ -				$language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . PHP_EXT; -			} - -			if ((@include $language_filename) === false) -			{ -				trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR); -			} -		} -		else if ($use_db) -		{ -			// Get Database Language Strings -			// Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed -			// For example: help:faq, posting -		} +		return array( +			'user_id'		=> ANONYMOUS, +		);  	}  	/** -	* Format user date +	* Check for a bot by comparing user agent and ip  	* -	* @param int $gmepoch unix timestamp -	* @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i. -	* @param bool $forcedate force non-relative date format. +	* Here we do a bot check, oh er saucy! No, not that kind of bot +	* check. We loop through the list of bots defined by the admin and +	* see if we have any useragent and/or IP matches. If we do, this is a +	* bot, act accordingly  	* -	* @return mixed translated date +	* @return bool True if it is a bot. +	* @access private  	*/ -	function format_date($gmepoch, $format = false, $forcedate = false) +	private function check_bot()  	{ -		static $midnight; -		static $date_cache; - -		$format = (!$format) ? $this->date_format : $format; -		$now = time(); -		$delta = $now - $gmepoch; +		$bot = false; -		if (!isset($date_cache[$format])) +		foreach (phpbb_cache::obtain_bots() as $row)  		{ -			// Is the user requesting a friendly date format (i.e. 'Today 12:42')? -			$date_cache[$format] = array( -				'is_short'		=> strpos($format, '|'), -				'zone_offset'	=> $this->timezone + $this->dst, -				'format_short'	=> substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1), -				'format_long'	=> str_replace('|', '', $format), -				'lang'			=> $this->lang['datetime'], -			); - -			// Short representation of month in format? Some languages use different terms for the long and short format of May -			if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false)) +			if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->system['browser']))  			{ -				$date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short']; +				$bot = $row['user_id'];  			} -		} - -		// Show date <= 1 hour ago as 'xx min ago' -		// A small tolerence is given for times in the future and times in the future but in the same minute are displayed as '< than a minute ago' -		if ($delta <= 3600 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO'])) -		{ -			return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60))); -		} -		if (!$midnight) -		{ -			list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $date_cache[$format]['zone_offset'])); -			$midnight = gmmktime(0, 0, 0, $m, $d, $y) - $date_cache[$format]['zone_offset']; -		} - -		if ($date_cache[$format]['is_short'] !== false && !$forcedate) -		{ -			$day = false; - -			if ($gmepoch > $midnight + 86400) -			{ -				$day = 'TOMORROW'; -			} -			else if ($gmepoch > $midnight) -			{ -				$day = 'TODAY'; -			} -			else if ($gmepoch > $midnight - 86400) +			// If ip is supplied, we will make sure the ip is matching too... +			if ($row['bot_ip'] && ($bot || !$row['bot_agent']))  			{ -				$day = 'YESTERDAY'; +				// Set bot to false, then we only have to set it to true if it is matching +				$bot = false; + +				foreach (explode(',', $row['bot_ip']) as $bot_ip) +				{ +					if (strpos($this->system['ip'], $bot_ip) === 0) +					{ +						$bot = (int) $row['user_id']; +						break; +					} +				}  			} -			if ($day !== false) +			if ($bot)  			{ -				return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $date_cache[$format]['zone_offset']), $date_cache[$format]['lang'])); +				break;  			}  		} -		return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $date_cache[$format]['zone_offset']), $date_cache[$format]['lang']); +		return $bot;  	}  	/** -	* Get language id currently used by the user +	* Check if session is valid by comparing ip, forwarded for, browser and referer +	* +	* @param bool $log_failure[optional] If true then a non-match will be logged. Can cause huge logs. +	* @return bool true if the session is valid +	* @access private  	*/ -	function get_iso_lang_id() +	private function session_valid($log_failure = true)  	{ -		global $config, $db; +		// Validate IP length according to admin ... enforces an IP +		// check on bots if admin requires this +//		$quadcheck = (phpbb::$config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : phpbb::$config['ip_check']; -		if (!empty($this->lang_id)) +		if (strpos($this->system['ip'], ':') !== false && strpos($this->data['session_ip'], ':') !== false)  		{ -			return $this->lang_id; +			$session_ip = short_ipv6($this->data['session_ip'], phpbb::$config['ip_check']); +			$user_ip = short_ipv6($this->system['ip'], phpbb::$config['ip_check']);  		} - -		if (!$this->lang_name) +		else  		{ -			$this->lang_name = $config['default_lang']; +			$session_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, phpbb::$config['ip_check'])); +			$user_ip = implode('.', array_slice(explode('.', $this->system['ip']), 0, phpbb::$config['ip_check']));  		} -		$sql = 'SELECT lang_id -			FROM ' . LANG_TABLE . " -			WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'"; -		$result = $db->sql_query($sql); -		$this->lang_id = (int) $db->sql_fetchfield('lang_id'); -		$db->sql_freeresult($result); +		$session_browser = (phpbb::$config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : ''; +		$user_browser = (phpbb::$config['browser_check']) ? trim(strtolower(substr($this->system['browser'], 0, 149))) : ''; -		return $this->lang_id; -	} +		$session_forwarded_for = (phpbb::$config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : ''; +		$user_forwarded_for = (phpbb::$config['forwarded_for_check']) ? substr($this->system['forwarded_for'], 0, 254) : ''; -	/** -	* Get users profile fields -	*/ -	function get_profile_fields($user_id) -	{ -		global $db; +		// referer checks +		$check_referer_path = phpbb::$config['referer_validation'] == REFERER_VALIDATE_PATH; +		$referer_valid = true; -		if (isset($this->profile_fields)) +		// we assume HEAD and TRACE to be foul play and thus only whitelist GET +		if (phpbb::$config['referer_validation'] && $this->system['request_method'])  		{ -			return; +			$referer_valid = $this->validate_referer($check_referer_path);  		} -		$sql = 'SELECT * -			FROM ' . PROFILE_FIELDS_DATA_TABLE . " -			WHERE user_id = $user_id"; -		$result = $db->sql_query_limit($sql, 1); -		$this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row; -		$db->sql_freeresult($result); -	} - -	/** -	* Specify/Get image -	*/ -	function img($img, $alt = '', $type = 'full_tag', $width = false) -	{ -		static $imgs; - -		$img_data = &$imgs[$img]; - -		if (empty($img_data)) +		if ($user_ip !== $session_ip || $user_browser !== $session_browser || $user_forwarded_for !== $session_forwarded_for || !$referer_valid)  		{ -			if (!isset($this->img_array[$img])) +			// Added logging temporarly to help debug bugs... +			if (defined('DEBUG_EXTRA') && $this->data['user_id'] != ANONYMOUS && $log_failure)  			{ -				// Do not fill the image to let designers decide what to do if the image is empty -				$img_data = ''; -				return $img_data; +				if ($referer_valid) +				{ +					add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $user_ip, $session_ip, $user_browser, $session_browser, htmlspecialchars($user_forwarded_for), htmlspecialchars($session_forwarded_for)); +				} +				else +				{ +					add_log('critical', 'LOG_REFERER_INVALID', $this->system['referer']); +				}  			} -			$img_data['src'] = PHPBB_ROOT_PATH . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . ($this->img_array[$img]['image_lang'] ? $this->img_array[$img]['image_lang'] .'/' : '') . $this->img_array[$img]['image_filename']; -			$img_data['width'] = $this->img_array[$img]['image_width']; -			$img_data['height'] = $this->img_array[$img]['image_height']; -		} - -		$alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt; - -		switch ($type) -		{ -			case 'src': -				return $img_data['src']; -			break; - -			case 'width': -				return ($width === false) ? $img_data['width'] : $width; -			break; - -			case 'height': -				return $img_data['height']; -			break; - -			default: -				$use_width = ($width === false) ? $img_data['width'] : $width; - -				return '<img src="' . $img_data['src'] . '"' . (($use_width) ? ' width="' . $use_width . '"' : '') . (($img_data['height']) ? ' height="' . $img_data['height'] . '"' : '') . ' alt="' . $alt . '" title="' . $alt . '" />'; -			break; -		} -	} - -	/** -	* Get option bit field from user options -	*/ -	function optionget($key, $data = false) -	{ -		if (!isset($this->keyvalues[$key])) -		{ -			$var = ($data) ? $data : $this->data['user_options']; -			$this->keyvalues[$key] = ($var & 1 << $this->keyoptions[$key]) ? true : false; -		} - -		return $this->keyvalues[$key]; -	} - -	/** -	* Set option bit field for user options -	*/ -	function optionset($key, $value, $data = false) -	{ -		$var = ($data) ? $data : $this->data['user_options']; - -		if ($value && !($var & 1 << $this->keyoptions[$key])) -		{ -			$var += 1 << $this->keyoptions[$key]; -		} -		else if (!$value && ($var & 1 << $this->keyoptions[$key])) -		{ -			$var -= 1 << $this->keyoptions[$key]; -		} -		else -		{ -			return ($data) ? $var : false; +			return false;  		} -		if (!$data) -		{ -			$this->data['user_options'] = $var; -			return true; -		} -		else -		{ -			return $var; -		} +		return true;  	}  }  | 
