diff options
Diffstat (limited to 'phpBB/phpbb/session.php')
-rw-r--r-- | phpBB/phpbb/session.php | 309 |
1 files changed, 199 insertions, 110 deletions
diff --git a/phpBB/phpbb/session.php b/phpBB/phpbb/session.php index f530d30f1f..3f7146c59b 100644 --- a/phpBB/phpbb/session.php +++ b/phpBB/phpbb/session.php @@ -1,9 +1,13 @@ <?php /** * -* @package phpBB3 -* @copyright (c) 2005 phpBB Group -* @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License v2 +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. * */ @@ -11,7 +15,6 @@ namespace phpbb; /** * Session class -* @package phpBB3 */ class session { @@ -28,10 +31,11 @@ class session var $update_session_page = true; /** - * Extract current session page - * - * @param string $root_path current root path (phpbb_root_path) - */ + * Extract current session page + * + * @param string $root_path current root path (phpbb_root_path) + * @return array + */ static function extract_current_page($root_path) { global $request, $symfony_request, $phpbb_filesystem; @@ -39,8 +43,8 @@ class session $page_array = array(); // First of all, get the request uri... - $script_name = $symfony_request->getScriptName(); - $args = explode('&', $symfony_request->getQueryString()); + $script_name = $request->escape($symfony_request->getScriptName(), true); + $args = $request->escape(explode('&', $symfony_request->getQueryString()), true); // 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) @@ -58,8 +62,8 @@ class session // 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'); + $find = array('"', "'", '<', '>', '"', '<', '>'); + $replace = array('%22', '%27', '%3C', '%3E', '%22', '%3C', '%3E'); foreach ($args as $key => $argument) { @@ -84,12 +88,12 @@ class session $symfony_request_path = $phpbb_filesystem->clean_path($symfony_request->getPathInfo()); if ($symfony_request_path !== '/') { - $page_name .= $symfony_request_path; + $page_name .= str_replace('%2F', '/', urlencode($symfony_request_path)); } // 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('./'))); + $root_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath($root_path))); + $page_dirs = explode('/', str_replace('\\', '/', $phpbb_filesystem->realpath('./'))); $intersection = array_intersect_assoc($root_dirs, $page_dirs); $root_dirs = array_diff_assoc($root_dirs, $intersection); @@ -126,6 +130,10 @@ class session $script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/'; $root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/'; + $forum_id = $request->variable('f', 0); + // maximum forum id value is maximum value of mediumint unsigned column + $forum_id = ($forum_id > 0 && $forum_id < 16777215) ? $forum_id : 0; + $page_array += array( 'page_name' => $page_name, 'page_dir' => $page_dir, @@ -135,7 +143,7 @@ class session 'root_script_path' => str_replace(' ', '%20', htmlspecialchars($root_script_path)), 'page' => $page, - 'forum' => request_var('f', 0), + 'forum' => $forum_id, ); return $page_array; @@ -211,7 +219,7 @@ class session function session_begin($update_session_page = true) { global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path; - global $request, $phpbb_container; + global $request, $phpbb_container, $user, $phpbb_log; // Give us some basic information $this->time_now = time(); @@ -249,23 +257,23 @@ class session if ($request->is_set($config['cookie_name'] . '_sid', \phpbb\request\request_interface::COOKIE) || $request->is_set($config['cookie_name'] . '_u', \phpbb\request\request_interface::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); + $this->cookie_data['u'] = $request->variable($config['cookie_name'] . '_u', 0, false, \phpbb\request\request_interface::COOKIE); + $this->cookie_data['k'] = $request->variable($config['cookie_name'] . '_k', '', false, \phpbb\request\request_interface::COOKIE); + $this->session_id = $request->variable($config['cookie_name'] . '_sid', '', false, \phpbb\request\request_interface::COOKIE); $SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid='; $_SID = (defined('NEED_SID')) ? $this->session_id : ''; if (empty($this->session_id)) { - $this->session_id = $_SID = request_var('sid', ''); + $this->session_id = $_SID = $request->variable('sid', ''); $SID = '?sid=' . $this->session_id; $this->cookie_data = array('u' => 0, 'k' => ''); } } else { - $this->session_id = $_SID = request_var('sid', ''); + $this->session_id = $_SID = $request->variable('sid', ''); $SID = '?sid=' . $this->session_id; } @@ -341,8 +349,8 @@ class session } else { - set_config('limit_load', '0'); - set_config('limit_search_load', '0'); + $config->set('limit_load', '0'); + $config->set('limit_search_load', '0'); } } @@ -405,9 +413,9 @@ class session $session_expired = false; // Check whether the session is still valid if we have one - $method = basename(trim($config['auth_method'])); - - $provider = $phpbb_container->get('auth.provider.' . $method); + /* @var $provider_collection \phpbb\auth\provider_collection */ + $provider_collection = $phpbb_container->get('auth.provider_collection'); + $provider = $provider_collection->get_provider(); if (!($provider instanceof \phpbb\auth\provider\provider_interface)) { @@ -439,38 +447,6 @@ class session 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); - $sql_ary['session_forum_id'] = $this->page['forum']; - } - - $db->sql_return_on_error(true); - - $this->update_session($sql_ary); - - $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']); - - $this->update_session($sql_ary); - } - - if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts']) - { - $this->leave_newly_registered(); - } - } - $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']); @@ -485,11 +461,18 @@ class session { 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)); + $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_IP_BROWSER_FORWARDED_CHECK', false, array( + $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); + $phpbb_log->add('critical', $user->data['user_id'], $user->ip, 'LOG_REFERER_INVALID', false, array($this->referer)); } } } @@ -511,7 +494,7 @@ class session */ function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true) { - global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx, $phpbb_container; + global $SID, $_SID, $db, $config, $cache, $phpbb_container, $phpbb_dispatcher; $this->data = array(); @@ -574,11 +557,16 @@ class session } } - $method = basename(trim($config['auth_method'])); - - $provider = $phpbb_container->get('auth.provider.' . $method); + /* @var $provider_collection \phpbb\auth\provider_collection */ + $provider_collection = $phpbb_container->get('auth.provider_collection'); + $provider = $provider_collection->get_provider(); $this->data = $provider->autologin(); + if ($user_id !== false && sizeof($this->data) && $this->data['user_id'] != $user_id) + { + $this->data = array(); + } + if (sizeof($this->data)) { $this->cookie_data['k'] = ''; @@ -596,11 +584,18 @@ class session 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); + $user_data = $db->sql_fetchrow($result); + + if ($user_id === false || (isset($user_data['user_id']) && $user_id == $user_data['user_id'])) + { + $this->data = $user_data; + $bot = false; + } + $db->sql_freeresult($result); - $bot = false; } - else if ($user_id !== false && !sizeof($this->data)) + + if ($user_id !== false && !sizeof($this->data)) { $this->cookie_data['k'] = ''; $this->cookie_data['u'] = $user_id; @@ -715,18 +710,6 @@ class session // 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; - - $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0); - - if ($this->update_session_page) - { - $sql_ary['session_page'] = substr($this->page['page'], 0, 199); - $sql_ary['session_forum_id'] = $this->page['forum']; - } - - $this->update_session($sql_ary); - // Update the last visit time $sql = 'UPDATE ' . USERS_TABLE . ' SET user_lastvisit = ' . (int) $this->data['session_time'] . ' @@ -874,6 +857,19 @@ class session $_SID = ''; } + $session_data = $sql_ary; + /** + * Event to send new session data to extension + * Read-only event + * + * @event core.session_create_after + * @var array session_data Associative array of session keys to be updated + * @since 3.1.6-RC1 + */ + $vars = array('session_data'); + extract($phpbb_dispatcher->trigger_event('core.session_create_after', compact($vars))); + unset($session_data); + return true; } @@ -887,17 +883,34 @@ class session */ function session_kill($new_session = true) { - global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx, $phpbb_container; + global $SID, $_SID, $db, $phpbb_container, $phpbb_dispatcher; $sql = 'DELETE FROM ' . SESSIONS_TABLE . " WHERE session_id = '" . $db->sql_escape($this->session_id) . "' AND session_user_id = " . (int) $this->data['user_id']; $db->sql_query($sql); - // Allow connecting logout with external auth method logout - $method = basename(trim($config['auth_method'])); + $user_id = (int) $this->data['user_id']; + $session_id = $this->session_id; + /** + * Event to send session kill information to extension + * Read-only event + * + * @event core.session_kill_after + * @var int user_id user_id of the session user. + * @var string session_id current user's session_id + * @var bool new_session should we create new session for user + * @since 3.1.6-RC1 + */ + $vars = array('user_id', 'session_id', 'new_session'); + extract($phpbb_dispatcher->trigger_event('core.session_kill_after', compact($vars))); + unset($user_id); + unset($session_id); - $provider = $phpbb_container->get('auth.provider.' . $method); + // Allow connecting logout with external auth method logout + /* @var $provider_collection \phpbb\auth\provider_collection */ + $provider_collection = $phpbb_container->get('auth.provider_collection'); + $provider = $provider_collection->get_provider(); $provider->logout($this->data, $new_session); if ($this->data['user_id'] != ANONYMOUS) @@ -962,7 +975,7 @@ class session */ function session_gc() { - global $db, $config, $phpbb_root_path, $phpEx; + global $db, $config, $phpbb_container, $phpbb_dispatcher; $batch_size = 10; @@ -1012,7 +1025,7 @@ class session { // Less than 10 users, update gc timer ... else we want gc // called again to delete other sessions - set_config('session_last_gc', $this->time_now, true); + $config->set('session_last_gc', $this->time_now, false); if ($config['max_autologin_time']) { @@ -1022,11 +1035,8 @@ class session } // only called from CRON; should be a safe workaround until the infrastructure gets going - if (!class_exists('phpbb_captcha_factory', false)) - { - include($phpbb_root_path . "includes/captcha/captcha_factory." . $phpEx); - } - $captcha_factory = new \phpbb_captcha_factory(); + /* @var $captcha_factory \phpbb\captcha\factory */ + $captcha_factory = $phpbb_container->get('captcha.factory'); $captcha_factory->garbage_collect($config['captcha_plugin']); $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . ' @@ -1034,6 +1044,14 @@ class session $db->sql_query($sql); } + /** + * Event to trigger extension on session_gc + * + * @event core.session_gc_after + * @since 3.1.6-RC1 + */ + $phpbb_dispatcher->dispatch('core.session_gc_after'); + return; } @@ -1045,33 +1063,42 @@ class session * @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. + * @param bool $httponly Use HttpOnly. Defaults to true. Use false to make cookie accessible by client-side scripts. */ - function set_cookie($name, $cookiedata, $cookietime) + function set_cookie($name, $cookiedata, $cookietime, $httponly = true) { global $config; + // If headers are already set, we just return + if (headers_sent()) + { + return; + } + $name_data = rawurlencode($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 = (!$config['cookie_domain'] || $config['cookie_domain'] == '127.0.0.1' || strpos($config['cookie_domain'], '.') === false) ? '' : '; domain=' . $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=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . ';' . (($httponly) ? ' HttpOnly' : ''), false); } /** * Check for banned user * * Checks whether the supplied user is banned by id, ip or email. If no parameters - * are passed to the method pre-existing session data is used. If $return is false - * this routine does not return on finding a banned user, it outputs a relevant - * message and stops execution. + * are passed to the method pre-existing session data is used. * - * @param string|array $user_ips Can contain a string with one IP or an array of multiple IPs + * @param int|false $user_id The user id + * @param mixed $user_ips Can contain a string with one IP or an array of multiple IPs + * @param string|false $user_email The user email + * @param bool $return If $return is false this routine does not return on finding a banned user, + * it outputs a relevant message and stops execution. */ function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false) { - global $config, $db; + global $config, $db, $phpbb_dispatcher; - if (defined('IN_CHECK_BAN')) + if (defined('IN_CHECK_BAN') || defined('SKIP_CHECK_BAN')) { return; } @@ -1183,9 +1210,23 @@ class session } $db->sql_freeresult($result); + /** + * Event to set custom ban type + * + * @event core.session_set_custom_ban + * @var bool return If $return is false this routine does not return on finding a banned user, it outputs a relevant message and stops execution + * @var bool banned Check if user already banned + * @var array|false ban_row Ban data + * @var string ban_triggered_by Method that caused ban, can be your custom method + * @since 3.1.3-RC1 + */ + $ban_row = isset($ban_row) ? $ban_row : false; + $vars = array('return', 'banned', 'ban_row', 'ban_triggered_by'); + extract($phpbb_dispatcher->trigger_event('core.session_set_custom_ban', compact($vars))); + if ($banned && !$return) { - global $template; + global $phpbb_root_path, $phpEx; // If the session is empty we need to create a valid one... if (empty($this->session_id)) @@ -1206,8 +1247,6 @@ class session // We show a login box here to allow founders accessing the board if banned by IP if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS) { - global $phpEx; - $this->setup('ucp'); $this->data['is_registered'] = $this->data['is_bot'] = false; @@ -1231,7 +1270,8 @@ 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>'); + $contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx); + $message = sprintf($this->lang[$message], $till_date, '<a href="' . $contact_link . '">', '</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>'; @@ -1254,12 +1294,14 @@ class session /** * Check if ip is blacklisted - * This should be called only where absolutly necessary + * This should be called only where absolutely necessary * * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups) * * @author satmd (from the php manual) - * @param string $mode register/post - spamcop for example is ommitted for posting + * @param string $mode register/post - spamcop for example is ommitted for posting + * @param string|false $ip the IPv4 address to check + * * @return false if ip is not blacklisted, else an array([checked server], [lookup]) */ function check_dnsbl($mode, $ip = false) @@ -1361,7 +1403,7 @@ class session */ function set_login_key($user_id = false, $key = false, $user_ip = false) { - global $config, $db; + global $db; $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id; $user_ip = ($user_ip === false) ? $this->ip : $user_ip; @@ -1371,7 +1413,7 @@ class session $sql_ary = array( 'key_id' => (string) md5($key_id), - 'last_ip' => (string) $this->ip, + 'last_ip' => (string) $user_ip, 'last_login' => (int) time() ); @@ -1408,7 +1450,7 @@ class session */ function reset_login_keys($user_id = false) { - global $config, $db; + global $db; $user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id; @@ -1509,12 +1551,59 @@ class session */ public function update_session($session_data, $session_id = null) { - global $db; + global $db, $phpbb_dispatcher; $session_id = ($session_id) ? $session_id : $this->session_id; $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $session_data) . " WHERE session_id = '" . $db->sql_escape($session_id) . "'"; $db->sql_query($sql); + + /** + * Event to send update session information to extension + * Read-only event + * + * @event core.update_session_after + * @var array session_data Associative array of session keys to be updated + * @var string session_id current user's session_id + * @since 3.1.6-RC1 + */ + $vars = array('session_data', 'session_id'); + extract($phpbb_dispatcher->trigger_event('core.update_session_after', compact($vars))); + } + + public function update_session_infos() + { + global $config, $db, $request; + + // No need to update if it's a new session. Informations are already inserted by session_create() + if (isset($this->data['session_created']) && $this->data['session_created']) + { + return; + } + + // 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); + + // Do not update the session page for ajax requests, so the view online still works as intended + if ($this->update_session_page && !$request->is_ajax()) + { + $sql_ary['session_page'] = substr($this->page['page'], 0, 199); + $sql_ary['session_forum_id'] = $this->page['forum']; + } + + $db->sql_return_on_error(true); + + $this->update_session($sql_ary); + + $db->sql_return_on_error(false); + + if ($this->data['user_id'] != ANONYMOUS && !empty($config['new_member_post_limit']) && $this->data['user_new'] && $config['new_member_post_limit'] <= $this->data['user_posts']) + { + $this->leave_newly_registered(); + } + } } } |