Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

session.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 48.73 KiB


 0001  <?php
 0002  /**
 0003  *
 0004  * This file is part of the phpBB Forum Software package.
 0005  *
 0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
 0007  * @license GNU General Public License, version 2 (GPL-2.0)
 0008  *
 0009  * For full copyright and license information, please see
 0010  * the docs/CREDITS.txt file.
 0011  *
 0012  */
 0013   
 0014  namespace phpbb;
 0015   
 0016  /**
 0017  * Session class
 0018  */
 0019  class session
 0020  {
 0021      var $cookie_data = array();
 0022      var $page = array();
 0023      var $data = array();
 0024      var $browser = '';
 0025      var $forwarded_for = '';
 0026      var $host = '';
 0027      var $session_id = '';
 0028      var $ip = '';
 0029      var $load = 0;
 0030      var $time_now = 0;
 0031      var $update_session_page = true;
 0032   
 0033      /**
 0034      * Extract current session page
 0035      *
 0036      * @param string $root_path current root path (phpbb_root_path)
 0037      */
 0038      static function extract_current_page($root_path)
 0039      {
 0040          global $request, $symfony_request, $phpbb_filesystem;
 0041   
 0042          $page_array = array();
 0043   
 0044          // First of all, get the request uri...
 0045          $script_name = $symfony_request->getScriptName();
 0046          $args = explode('&', $symfony_request->getQueryString());
 0047   
 0048          // 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...
 0049          if (!$script_name)
 0050          {
 0051              $script_name = htmlspecialchars_decode($request->server('REQUEST_URI'));
 0052              $script_name = (($pos = strpos($script_name, '?')) !== false) ? substr($script_name, 0, $pos) : $script_name;
 0053              $page_array['failover'] = 1;
 0054          }
 0055   
 0056          // Replace backslashes and doubled slashes (could happen on some proxy setups)
 0057          $script_name = str_replace(array('\\', '//'), '/', $script_name);
 0058   
 0059          // Now, remove the sid and let us get a clean query string...
 0060          $use_args = array();
 0061   
 0062          // Since some browser do not encode correctly we need to do this with some "special" characters...
 0063          // " -> %22, ' => %27, < -> %3C, > -> %3E
 0064          $find = array('"', "'", '<', '>');
 0065          $replace = array('%22', '%27', '%3C', '%3E');
 0066   
 0067          foreach ($args as $key => $argument)
 0068          {
 0069              if (strpos($argument, 'sid=') === 0)
 0070              {
 0071                  continue;
 0072              }
 0073   
 0074              $use_args[] = str_replace($find, $replace, $argument);
 0075          }
 0076          unset($args);
 0077   
 0078          // The following examples given are for an request uri of {path to the phpbb directory}/adm/index.php?i=10&b=2
 0079   
 0080          // The current query string
 0081          $query_string = trim(implode('&', $use_args));
 0082   
 0083          // basenamed page name (for example: index.php)
 0084          $page_name = (substr($script_name, -1, 1) == '/') ? '' : basename($script_name);
 0085          $page_name = urlencode(htmlspecialchars($page_name));
 0086   
 0087          $symfony_request_path = $phpbb_filesystem->clean_path($symfony_request->getPathInfo());
 0088          if ($symfony_request_path !== '/')
 0089          {
 0090              $page_name .= $symfony_request_path;
 0091          }
 0092   
 0093          // current directory within the phpBB root (for example: adm)
 0094          $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path)));
 0095          $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
 0096          $intersection = array_intersect_assoc($root_dirs, $page_dirs);
 0097   
 0098          $root_dirs = array_diff_assoc($root_dirs, $intersection);
 0099          $page_dirs = array_diff_assoc($page_dirs, $intersection);
 0100   
 0101          $page_dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
 0102   
 0103          if ($page_dir && substr($page_dir, -1, 1) == '/')
 0104          {
 0105              $page_dir = substr($page_dir, 0, -1);
 0106          }
 0107   
 0108          // Current page from phpBB root (for example: adm/index.php?i=10&b=2)
 0109          $page = (($page_dir) ? $page_dir . '/' : '') . $page_name;
 0110          if ($query_string)
 0111          {
 0112              $page .= '?' . $query_string;
 0113          }
 0114   
 0115          // The script path from the webroot to the current directory (for example: /phpBB3/adm/) : always prefixed with / and ends in /
 0116          $script_path = $symfony_request->getBasePath();
 0117   
 0118          // The script path from the webroot to the phpBB root (for example: /phpBB3/)
 0119          $script_dirs = explode('/', $script_path);
 0120          array_splice($script_dirs, -sizeof($page_dirs));
 0121          $root_script_path = implode('/', $script_dirs) . (sizeof($root_dirs) ? '/' . implode('/', $root_dirs) : '');
 0122   
 0123          // We are on the base level (phpBB root == webroot), lets adjust the variables a bit...
 0124          if (!$root_script_path)
 0125          {
 0126              $root_script_path = ($page_dir) ? str_replace($page_dir, '', $script_path) : $script_path;
 0127          }
 0128   
 0129          $script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/';
 0130          $root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/';
 0131   
 0132          $page_array += array(
 0133              'page_name'            => $page_name,
 0134              'page_dir'            => $page_dir,
 0135   
 0136              'query_string'        => $query_string,
 0137              'script_path'        => str_replace(' ', '%20', htmlspecialchars($script_path)),
 0138              'root_script_path'    => str_replace(' ', '%20', htmlspecialchars($root_script_path)),
 0139   
 0140              'page'                => $page,
 0141              'forum'                => request_var('f', 0),
 0142          );
 0143   
 0144          return $page_array;
 0145      }
 0146   
 0147      /**
 0148      * Get valid hostname/port. HTTP_HOST is used, SERVER_NAME if HTTP_HOST not present.
 0149      */
 0150      function extract_current_hostname()
 0151      {
 0152          global $config, $request;
 0153   
 0154          // Get hostname
 0155          $host = htmlspecialchars_decode($request->header('Host', $request->server('SERVER_NAME')));
 0156   
 0157          // Should be a string and lowered
 0158          $host = (string) strtolower($host);
 0159   
 0160          // If host is equal the cookie domain or the server name (if config is set), then we assume it is valid
 0161          if ((isset($config['cookie_domain']) && $host === $config['cookie_domain']) || (isset($config['server_name']) && $host === $config['server_name']))
 0162          {
 0163              return $host;
 0164          }
 0165   
 0166          // Is the host actually a IP? If so, we use the IP... (IPv4)
 0167          if (long2ip(ip2long($host)) === $host)
 0168          {
 0169              return $host;
 0170          }
 0171   
 0172          // 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
 0173          $host = @parse_url('http://' . $host);
 0174          $host = (!empty($host['host'])) ? $host['host'] : '';
 0175   
 0176          // Remove any portions not removed by parse_url (#)
 0177          $host = str_replace('#', '', $host);
 0178   
 0179          // If, by any means, the host is now empty, we will use a "best approach" way to guess one
 0180          if (empty($host))
 0181          {
 0182              if (!empty($config['server_name']))
 0183              {
 0184                  $host = $config['server_name'];
 0185              }
 0186              else if (!empty($config['cookie_domain']))
 0187              {
 0188                  $host = (strpos($config['cookie_domain'], '.') === 0) ? substr($config['cookie_domain'], 1) : $config['cookie_domain'];
 0189              }
 0190              else
 0191              {
 0192                  // Set to OS hostname or localhost
 0193                  $host = (function_exists('php_uname')) ? php_uname('n') : 'localhost';
 0194              }
 0195          }
 0196   
 0197          // It may be still no valid host, but for sure only a hostname (we may further expand on the cookie domain... if set)
 0198          return $host;
 0199      }
 0200   
 0201      /**
 0202      * Start session management
 0203      *
 0204      * This is where all session activity begins. We gather various pieces of
 0205      * information from the client and server. We test to see if a session already
 0206      * exists. If it does, fine and dandy. If it doesn't we'll go on to create a
 0207      * new one ... pretty logical heh? We also examine the system load (if we're
 0208      * running on a system which makes such information readily available) and
 0209      * halt if it's above an admin definable limit.
 0210      *
 0211      * @param bool $update_session_page if true the session page gets updated.
 0212      *            This can be set to circumvent certain scripts to update the users last visited page.
 0213      */
 0214      function session_begin($update_session_page = true)
 0215      {
 0216          global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path;
 0217          global $request, $phpbb_container;
 0218   
 0219          // Give us some basic information
 0220          $this->time_now                = time();
 0221          $this->cookie_data            = array('u' => 0, 'k' => '');
 0222          $this->update_session_page    = $update_session_page;
 0223          $this->browser                = $request->header('User-Agent');
 0224          $this->referer                = $request->header('Referer');
 0225          $this->forwarded_for        = $request->header('X-Forwarded-For');
 0226   
 0227          $this->host                    = $this->extract_current_hostname();
 0228          $this->page                    = $this->extract_current_page($phpbb_root_path);
 0229   
 0230          // if the forwarded for header shall be checked we have to validate its contents
 0231          if ($config['forwarded_for_check'])
 0232          {
 0233              $this->forwarded_for = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->forwarded_for));
 0234   
 0235              // split the list of IPs
 0236              $ips = explode(' ', $this->forwarded_for);
 0237              foreach ($ips as $ip)
 0238              {
 0239                  // check IPv4 first, the IPv6 is hopefully only going to be used very seldomly
 0240                  if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip))
 0241                  {
 0242                      // contains invalid data, don't use the forwarded for header
 0243                      $this->forwarded_for = '';
 0244                      break;
 0245                  }
 0246              }
 0247          }
 0248          else
 0249          {
 0250              $this->forwarded_for = '';
 0251          }
 0252   
 0253          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))
 0254          {
 0255              $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
 0256              $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
 0257              $this->session_id         = request_var($config['cookie_name'] . '_sid', '', false, true);
 0258   
 0259              $SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid=';
 0260              $_SID = (defined('NEED_SID')) ? $this->session_id : '';
 0261   
 0262              if (empty($this->session_id))
 0263              {
 0264                  $this->session_id = $_SID = request_var('sid', '');
 0265                  $SID = '?sid=' . $this->session_id;
 0266                  $this->cookie_data = array('u' => 0, 'k' => '');
 0267              }
 0268          }
 0269          else
 0270          {
 0271              $this->session_id = $_SID = request_var('sid', '');
 0272              $SID = '?sid=' . $this->session_id;
 0273          }
 0274   
 0275          $_EXTRA_URL = array();
 0276   
 0277          // Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests
 0278          // it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip.
 0279          $this->ip = htmlspecialchars_decode($request->server('REMOTE_ADDR'));
 0280          $this->ip = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->ip));
 0281   
 0282          // split the list of IPs
 0283          $ips = explode(' ', trim($this->ip));
 0284   
 0285          // Default IP if REMOTE_ADDR is invalid
 0286          $this->ip = '127.0.0.1';
 0287   
 0288          foreach ($ips as $ip)
 0289          {
 0290              if (function_exists('phpbb_ip_normalise'))
 0291              {
 0292                  // Normalise IP address
 0293                  $ip = phpbb_ip_normalise($ip);
 0294   
 0295                  if (empty($ip))
 0296                  {
 0297                      // IP address is invalid.
 0298                      break;
 0299                  }
 0300   
 0301                  // IP address is valid.
 0302                  $this->ip = $ip;
 0303   
 0304                  // Skip legacy code.
 0305                  continue;
 0306              }
 0307   
 0308              if (preg_match(get_preg_expression('ipv4'), $ip))
 0309              {
 0310                  $this->ip = $ip;
 0311              }
 0312              else if (preg_match(get_preg_expression('ipv6'), $ip))
 0313              {
 0314                  // Quick check for IPv4-mapped address in IPv6
 0315                  if (stripos($ip, '::ffff:') === 0)
 0316                  {
 0317                      $ipv4 = substr($ip, 7);
 0318   
 0319                      if (preg_match(get_preg_expression('ipv4'), $ipv4))
 0320                      {
 0321                          $ip = $ipv4;
 0322                      }
 0323                  }
 0324   
 0325                  $this->ip = $ip;
 0326              }
 0327              else
 0328              {
 0329                  // We want to use the last valid address in the chain
 0330                  // Leave foreach loop when address is invalid
 0331                  break;
 0332              }
 0333          }
 0334   
 0335          $this->load = false;
 0336   
 0337          // Load limit check (if applicable)
 0338          if ($config['limit_load'] || $config['limit_search_load'])
 0339          {
 0340              if ((function_exists('sys_getloadavg') && $load = sys_getloadavg()) || ($load = explode(' ', @file_get_contents('/proc/loadavg'))))
 0341              {
 0342                  $this->load = array_slice($load, 0, 1);
 0343                  $this->load = floatval($this->load[0]);
 0344              }
 0345              else
 0346              {
 0347                  set_config('limit_load', '0');
 0348                  set_config('limit_search_load', '0');
 0349              }
 0350          }
 0351   
 0352          // if no session id is set, redirect to index.php
 0353          $session_id = $request->variable('sid', '');
 0354          if (defined('NEED_SID') && (empty($session_id) || $this->session_id !== $session_id))
 0355          {
 0356              send_status_line(401, 'Unauthorized');
 0357              redirect(append_sid("{$phpbb_root_path}index.$phpEx"));
 0358          }
 0359   
 0360          // if session id is set
 0361          if (!empty($this->session_id))
 0362          {
 0363              $sql = 'SELECT u.*, s.*
 0364                  FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u
 0365                  WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "'
 0366                      AND u.user_id = s.session_user_id";
 0367              $result = $db->sql_query($sql);
 0368              $this->data = $db->sql_fetchrow($result);
 0369              $db->sql_freeresult($result);
 0370   
 0371              // Did the session exist in the DB?
 0372              if (isset($this->data['user_id']))
 0373              {
 0374                  // Validate IP length according to admin ... enforces an IP
 0375                  // check on bots if admin requires this
 0376  //                $quadcheck = ($config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : $config['ip_check'];
 0377   
 0378                  if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
 0379                  {
 0380                      $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
 0381                      $u_ip = short_ipv6($this->ip, $config['ip_check']);
 0382                  }
 0383                  else
 0384                  {
 0385                      $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
 0386                      $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
 0387                  }
 0388   
 0389                  $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
 0390                  $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
 0391   
 0392                  $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
 0393                  $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
 0394   
 0395                  // referer checks
 0396                  // The @ before $config['referer_validation'] suppresses notices present while running the updater
 0397                  $check_referer_path = (@$config['referer_validation'] == REFERER_VALIDATE_PATH);
 0398                  $referer_valid = true;
 0399   
 0400                  // we assume HEAD and TRACE to be foul play and thus only whitelist GET
 0401                  if (@$config['referer_validation'] && strtolower($request->server('REQUEST_METHOD')) !== 'get')
 0402                  {
 0403                      $referer_valid = $this->validate_referer($check_referer_path);
 0404                  }
 0405   
 0406                  if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for && $referer_valid)
 0407                  {
 0408                      $session_expired = false;
 0409   
 0410                      // Check whether the session is still valid if we have one
 0411                      $provider_collection = $phpbb_container->get('auth.provider_collection');
 0412                      $provider = $provider_collection->get_provider();
 0413   
 0414                      if (!($provider instanceof \phpbb\auth\provider\provider_interface))
 0415                      {
 0416                          throw new \RuntimeException($provider . ' must implement \phpbb\auth\provider\provider_interface');
 0417                      }
 0418   
 0419                      $ret = $provider->validate_session($this->data);
 0420                      if ($ret !== null && !$ret)
 0421                      {
 0422                          $session_expired = true;
 0423                      }
 0424   
 0425                      if (!$session_expired)
 0426                      {
 0427                          // Check the session length timeframe if autologin is not enabled.
 0428                          // Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide.
 0429                          if (!$this->data['session_autologin'])
 0430                          {
 0431                              if ($this->data['session_time'] < $this->time_now - ($config['session_length'] + 60))
 0432                              {
 0433                                  $session_expired = true;
 0434                              }
 0435                          }
 0436                          else if (!$config['allow_autologin'] || ($config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) $config['max_autologin_time']) + 60))
 0437                          {
 0438                              $session_expired = true;
 0439                          }
 0440                      }
 0441   
 0442                      if (!$session_expired)
 0443                      {
 0444                          // Only update session DB a minute or so after last update or if page changes
 0445                          if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
 0446                          {
 0447                              $sql_ary = array('session_time' => $this->time_now);
 0448   
 0449                              // Do not update the session page for ajax requests, so the view online still works as intended
 0450                              if ($this->update_session_page && !$request->is_ajax())
 0451                              {
 0452                                  $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
 0453                                  $sql_ary['session_forum_id'] = $this->page['forum'];
 0454                              }
 0455   
 0456                              $db->sql_return_on_error(true);
 0457   
 0458                              $this->update_session($sql_ary);
 0459   
 0460                              $db->sql_return_on_error(false);
 0461   
 0462                              // If the database is not yet updated, there will be an error due to the session_forum_id
 0463                              // @todo REMOVE for 3.0.2
 0464                              if ($result === false)
 0465                              {
 0466                                  unset($sql_ary['session_forum_id']);
 0467   
 0468                                  $this->update_session($sql_ary);
 0469                              }
 0470   
 0471                              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'])
 0472                              {
 0473                                  $this->leave_newly_registered();
 0474                              }
 0475                          }
 0476   
 0477                          $this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
 0478                          $this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false;
 0479                          $this->data['user_lang'] = basename($this->data['user_lang']);
 0480   
 0481                          return true;
 0482                      }
 0483                  }
 0484                  else
 0485                  {
 0486                      // Added logging temporarly to help debug bugs...
 0487                      if (defined('DEBUG') && $this->data['user_id'] != ANONYMOUS)
 0488                      {
 0489                          if ($referer_valid)
 0490                          {
 0491                              add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for));
 0492                          }
 0493                          else
 0494                          {
 0495                              add_log('critical', 'LOG_REFERER_INVALID', $this->referer);
 0496                          }
 0497                      }
 0498                  }
 0499              }
 0500          }
 0501   
 0502          // If we reach here then no (valid) session exists. So we'll create a new one
 0503          return $this->session_create();
 0504      }
 0505   
 0506      /**
 0507      * Create a new session
 0508      *
 0509      * If upon trying to start a session we discover there is nothing existing we
 0510      * jump here. Additionally this method is called directly during login to regenerate
 0511      * the session for the specific user. In this method we carry out a number of tasks;
 0512      * garbage collection, (search)bot checking, banned user comparison. Basically
 0513      * though this method will result in a new session for a specific user.
 0514      */
 0515      function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
 0516      {
 0517          global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx, $phpbb_container;
 0518   
 0519          $this->data = array();
 0520   
 0521          /* Garbage collection ... remove old sessions updating user information
 0522          // if necessary. It means (potentially) 11 queries but only infrequently
 0523          if ($this->time_now > $config['session_last_gc'] + $config['session_gc'])
 0524          {
 0525              $this->session_gc();
 0526          }*/
 0527   
 0528          // Do we allow autologin on this board? No? Then override anything
 0529          // that may be requested here
 0530          if (!$config['allow_autologin'])
 0531          {
 0532              $this->cookie_data['k'] = $persist_login = false;
 0533          }
 0534   
 0535          /**
 0536          * Here we do a bot check, oh er saucy! No, not that kind of bot
 0537          * check. We loop through the list of bots defined by the admin and
 0538          * see if we have any useragent and/or IP matches. If we do, this is a
 0539          * bot, act accordingly
 0540          */
 0541          $bot = false;
 0542          $active_bots = $cache->obtain_bots();
 0543   
 0544          foreach ($active_bots as $row)
 0545          {
 0546              if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->browser))
 0547              {
 0548                  $bot = $row['user_id'];
 0549              }
 0550   
 0551              // If ip is supplied, we will make sure the ip is matching too...
 0552              if ($row['bot_ip'] && ($bot || !$row['bot_agent']))
 0553              {
 0554                  // Set bot to false, then we only have to set it to true if it is matching
 0555                  $bot = false;
 0556   
 0557                  foreach (explode(',', $row['bot_ip']) as $bot_ip)
 0558                  {
 0559                      $bot_ip = trim($bot_ip);
 0560   
 0561                      if (!$bot_ip)
 0562                      {
 0563                          continue;
 0564                      }
 0565   
 0566                      if (strpos($this->ip, $bot_ip) === 0)
 0567                      {
 0568                          $bot = (int) $row['user_id'];
 0569                          break;
 0570                      }
 0571                  }
 0572              }
 0573   
 0574              if ($bot)
 0575              {
 0576                  break;
 0577              }
 0578          }
 0579   
 0580          $provider_collection = $phpbb_container->get('auth.provider_collection');
 0581          $provider = $provider_collection->get_provider();
 0582          $this->data = $provider->autologin();
 0583   
 0584          if ($user_id !== false && sizeof($this->data) && $this->data['user_id'] != $user_id)
 0585          {
 0586              $this->data = array();
 0587          }
 0588   
 0589          if (sizeof($this->data))
 0590          {
 0591              $this->cookie_data['k'] = '';
 0592              $this->cookie_data['u'] = $this->data['user_id'];
 0593          }
 0594   
 0595          // If we're presented with an autologin key we'll join against it.
 0596          // Else if we've been passed a user_id we'll grab data based on that
 0597          if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data))
 0598          {
 0599              $sql = 'SELECT u.*
 0600                  FROM ' . USERS_TABLE . ' u, ' . SESSIONS_KEYS_TABLE . ' k
 0601                  WHERE u.user_id = ' . (int) $this->cookie_data['u'] . '
 0602                      AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ")
 0603                      AND k.user_id = u.user_id
 0604                      AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
 0605              $result = $db->sql_query($sql);
 0606              $user_data = $db->sql_fetchrow($result);
 0607   
 0608              if ($user_id === false || (isset($user_data['user_id']) && $user_id == $user_data['user_id']))
 0609              {
 0610                  $this->data = $user_data;
 0611                  $bot = false;
 0612              }
 0613   
 0614              $db->sql_freeresult($result);
 0615          }
 0616   
 0617          if ($user_id !== false && !sizeof($this->data))
 0618          {
 0619              $this->cookie_data['k'] = '';
 0620              $this->cookie_data['u'] = $user_id;
 0621   
 0622              $sql = 'SELECT *
 0623                  FROM ' . USERS_TABLE . '
 0624                  WHERE user_id = ' . (int) $this->cookie_data['u'] . '
 0625                      AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
 0626              $result = $db->sql_query($sql);
 0627              $this->data = $db->sql_fetchrow($result);
 0628              $db->sql_freeresult($result);
 0629              $bot = false;
 0630          }
 0631   
 0632          // Bot user, if they have a SID in the Request URI we need to get rid of it
 0633          // otherwise they'll index this page with the SID, duplicate content oh my!
 0634          if ($bot && isset($_GET['sid']))
 0635          {
 0636              send_status_line(301, 'Moved Permanently');
 0637              redirect(build_url(array('sid')));
 0638          }
 0639   
 0640          // If no data was returned one or more of the following occurred:
 0641          // Key didn't match one in the DB
 0642          // User does not exist
 0643          // User is inactive
 0644          // User is bot
 0645          if (!sizeof($this->data) || !is_array($this->data))
 0646          {
 0647              $this->cookie_data['k'] = '';
 0648              $this->cookie_data['u'] = ($bot) ? $bot : ANONYMOUS;
 0649   
 0650              if (!$bot)
 0651              {
 0652                  $sql = 'SELECT *
 0653                      FROM ' . USERS_TABLE . '
 0654                      WHERE user_id = ' . (int) $this->cookie_data['u'];
 0655              }
 0656              else
 0657              {
 0658                  // We give bots always the same session if it is not yet expired.
 0659                  $sql = 'SELECT u.*, s.*
 0660                      FROM ' . USERS_TABLE . ' u
 0661                      LEFT JOIN ' . SESSIONS_TABLE . ' s ON (s.session_user_id = u.user_id)
 0662                      WHERE u.user_id = ' . (int) $bot;
 0663              }
 0664   
 0665              $result = $db->sql_query($sql);
 0666              $this->data = $db->sql_fetchrow($result);
 0667              $db->sql_freeresult($result);
 0668          }
 0669   
 0670          if ($this->data['user_id'] != ANONYMOUS && !$bot)
 0671          {
 0672              $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());
 0673          }
 0674          else
 0675          {
 0676              $this->data['session_last_visit'] = $this->time_now;
 0677          }
 0678   
 0679          // Force user id to be integer...
 0680          $this->data['user_id'] = (int) $this->data['user_id'];
 0681   
 0682          // At this stage we should have a filled data array, defined cookie u and k data.
 0683          // data array should contain recent session info if we're a real user and a recent
 0684          // session exists in which case session_id will also be set
 0685   
 0686          // Is user banned? Are they excluded? Won't return on ban, exists within method
 0687          if ($this->data['user_type'] != USER_FOUNDER)
 0688          {
 0689              if (!$config['forwarded_for_check'])
 0690              {
 0691                  $this->check_ban($this->data['user_id'], $this->ip);
 0692              }
 0693              else
 0694              {
 0695                  $ips = explode(' ', $this->forwarded_for);
 0696                  $ips[] = $this->ip;
 0697                  $this->check_ban($this->data['user_id'], $ips);
 0698              }
 0699          }
 0700   
 0701          $this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
 0702          $this->data['is_bot'] = ($bot) ? true : false;
 0703   
 0704          // If our friend is a bot, we re-assign a previously assigned session
 0705          if ($this->data['is_bot'] && $bot == $this->data['user_id'] && $this->data['session_id'])
 0706          {
 0707              // Only assign the current session if the ip, browser and forwarded_for match...
 0708              if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
 0709              {
 0710                  $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
 0711                  $u_ip = short_ipv6($this->ip, $config['ip_check']);
 0712              }
 0713              else
 0714              {
 0715                  $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
 0716                  $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
 0717              }
 0718   
 0719              $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
 0720              $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
 0721   
 0722              $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
 0723              $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
 0724   
 0725              if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for)
 0726              {
 0727                  $this->session_id = $this->data['session_id'];
 0728   
 0729                  // Only update session DB a minute or so after last update or if page changes
 0730                  if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
 0731                  {
 0732                      $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
 0733   
 0734                      $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0);
 0735   
 0736                      if ($this->update_session_page)
 0737                      {
 0738                          $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
 0739                          $sql_ary['session_forum_id'] = $this->page['forum'];
 0740                      }
 0741   
 0742                      $this->update_session($sql_ary);
 0743   
 0744                      // Update the last visit time
 0745                      $sql = 'UPDATE ' . USERS_TABLE . '
 0746                          SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 0747                          WHERE user_id = ' . (int) $this->data['user_id'];
 0748                      $db->sql_query($sql);
 0749                  }
 0750   
 0751                  $SID = '?sid=';
 0752                  $_SID = '';
 0753                  return true;
 0754              }
 0755              else
 0756              {
 0757                  // If the ip and browser does not match make sure we only have one bot assigned to one session
 0758                  $db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']);
 0759              }
 0760          }
 0761   
 0762          $session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false;
 0763          $set_admin = ($set_admin && $this->data['is_registered']) ? true : false;
 0764   
 0765          // Create or update the session
 0766          $sql_ary = array(
 0767              'session_user_id'        => (int) $this->data['user_id'],
 0768              'session_start'            => (int) $this->time_now,
 0769              'session_last_visit'    => (int) $this->data['session_last_visit'],
 0770              'session_time'            => (int) $this->time_now,
 0771              'session_browser'        => (string) trim(substr($this->browser, 0, 149)),
 0772              'session_forwarded_for'    => (string) $this->forwarded_for,
 0773              'session_ip'            => (string) $this->ip,
 0774              'session_autologin'        => ($session_autologin) ? 1 : 0,
 0775              'session_admin'            => ($set_admin) ? 1 : 0,
 0776              'session_viewonline'    => ($viewonline) ? 1 : 0,
 0777          );
 0778   
 0779          if ($this->update_session_page)
 0780          {
 0781              $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
 0782              $sql_ary['session_forum_id'] = $this->page['forum'];
 0783          }
 0784   
 0785          $db->sql_return_on_error(true);
 0786   
 0787          $sql = 'DELETE
 0788              FROM ' . SESSIONS_TABLE . '
 0789              WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'
 0790                  AND session_user_id = ' . ANONYMOUS;
 0791   
 0792          if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows()))
 0793          {
 0794              // Limit new sessions in 1 minute period (if required)
 0795              if (empty($this->data['session_time']) && $config['active_sessions'])
 0796              {
 0797  //                $db->sql_return_on_error(false);
 0798   
 0799                  $sql = 'SELECT COUNT(session_id) AS sessions
 0800                      FROM ' . SESSIONS_TABLE . '
 0801                      WHERE session_time >= ' . ($this->time_now - 60);
 0802                  $result = $db->sql_query($sql);
 0803                  $row = $db->sql_fetchrow($result);
 0804                  $db->sql_freeresult($result);
 0805   
 0806                  if ((int) $row['sessions'] > (int) $config['active_sessions'])
 0807                  {
 0808                      send_status_line(503, 'Service Unavailable');
 0809                      trigger_error('BOARD_UNAVAILABLE');
 0810                  }
 0811              }
 0812          }
 0813   
 0814          // Since we re-create the session id here, the inserted row must be unique. Therefore, we display potential errors.
 0815          // Commented out because it will not allow forums to update correctly
 0816  //        $db->sql_return_on_error(false);
 0817   
 0818          // Something quite important: session_page always holds the *last* page visited, except for the *first* visit.
 0819          // We are not able to simply have an empty session_page btw, therefore we need to tell phpBB how to detect this special case.
 0820          // If the session id is empty, we have a completely new one and will set an "identifier" here. This identifier is able to be checked later.
 0821          if (empty($this->data['session_id']))
 0822          {
 0823              // This is a temporary variable, only set for the very first visit
 0824              $this->data['session_created'] = true;
 0825          }
 0826   
 0827          $this->session_id = $this->data['session_id'] = md5(unique_id());
 0828   
 0829          $sql_ary['session_id'] = (string) $this->session_id;
 0830          $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
 0831          $sql_ary['session_forum_id'] = $this->page['forum'];
 0832   
 0833          $sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 0834          $db->sql_query($sql);
 0835   
 0836          $db->sql_return_on_error(false);
 0837   
 0838          // Regenerate autologin/persistent login key
 0839          if ($session_autologin)
 0840          {
 0841              $this->set_login_key();
 0842          }
 0843   
 0844          // refresh data
 0845          $SID = '?sid=' . $this->session_id;
 0846          $_SID = $this->session_id;
 0847          $this->data = array_merge($this->data, $sql_ary);
 0848   
 0849          if (!$bot)
 0850          {
 0851              $cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000);
 0852   
 0853              $this->set_cookie('u', $this->cookie_data['u'], $cookie_expire);
 0854              $this->set_cookie('k', $this->cookie_data['k'], $cookie_expire);
 0855              $this->set_cookie('sid', $this->session_id, $cookie_expire);
 0856   
 0857              unset($cookie_expire);
 0858   
 0859              $sql = 'SELECT COUNT(session_id) AS sessions
 0860                      FROM ' . SESSIONS_TABLE . '
 0861                      WHERE session_user_id = ' . (int) $this->data['user_id'] . '
 0862                      AND session_time >= ' . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime'])));
 0863              $result = $db->sql_query($sql);
 0864              $row = $db->sql_fetchrow($result);
 0865              $db->sql_freeresult($result);
 0866   
 0867              if ((int) $row['sessions'] <= 1 || empty($this->data['user_form_salt']))
 0868              {
 0869                  $this->data['user_form_salt'] = unique_id();
 0870                  // Update the form key
 0871                  $sql = 'UPDATE ' . USERS_TABLE . '
 0872                      SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
 0873                      WHERE user_id = ' . (int) $this->data['user_id'];
 0874                  $db->sql_query($sql);
 0875              }
 0876          }
 0877          else
 0878          {
 0879              $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
 0880   
 0881              // Update the last visit time
 0882              $sql = 'UPDATE ' . USERS_TABLE . '
 0883                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 0884                  WHERE user_id = ' . (int) $this->data['user_id'];
 0885              $db->sql_query($sql);
 0886   
 0887              $SID = '?sid=';
 0888              $_SID = '';
 0889          }
 0890   
 0891          return true;
 0892      }
 0893   
 0894      /**
 0895      * Kills a session
 0896      *
 0897      * This method does what it says on the tin. It will delete a pre-existing session.
 0898      * It resets cookie information (destroying any autologin key within that cookie data)
 0899      * and update the users information from the relevant session data. It will then
 0900      * grab guest user information.
 0901      */
 0902      function session_kill($new_session = true)
 0903      {
 0904          global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx, $phpbb_container;
 0905   
 0906          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
 0907              WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
 0908                  AND session_user_id = " . (int) $this->data['user_id'];
 0909          $db->sql_query($sql);
 0910   
 0911          // Allow connecting logout with external auth method logout
 0912          $provider_collection = $phpbb_container->get('auth.provider_collection');
 0913          $provider = $provider_collection->get_provider();
 0914          $provider->logout($this->data, $new_session);
 0915   
 0916          if ($this->data['user_id'] != ANONYMOUS)
 0917          {
 0918              // Delete existing session, update last visit info first!
 0919              if (!isset($this->data['session_time']))
 0920              {
 0921                  $this->data['session_time'] = time();
 0922              }
 0923   
 0924              $sql = 'UPDATE ' . USERS_TABLE . '
 0925                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 0926                  WHERE user_id = ' . (int) $this->data['user_id'];
 0927              $db->sql_query($sql);
 0928   
 0929              if ($this->cookie_data['k'])
 0930              {
 0931                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
 0932                      WHERE user_id = ' . (int) $this->data['user_id'] . "
 0933                          AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
 0934                  $db->sql_query($sql);
 0935              }
 0936   
 0937              // Reset the data array
 0938              $this->data = array();
 0939   
 0940              $sql = 'SELECT *
 0941                  FROM ' . USERS_TABLE . '
 0942                  WHERE user_id = ' . ANONYMOUS;
 0943              $result = $db->sql_query($sql);
 0944              $this->data = $db->sql_fetchrow($result);
 0945              $db->sql_freeresult($result);
 0946          }
 0947   
 0948          $cookie_expire = $this->time_now - 31536000;
 0949          $this->set_cookie('u', '', $cookie_expire);
 0950          $this->set_cookie('k', '', $cookie_expire);
 0951          $this->set_cookie('sid', '', $cookie_expire);
 0952          unset($cookie_expire);
 0953   
 0954          $SID = '?sid=';
 0955          $this->session_id = $_SID = '';
 0956   
 0957          // To make sure a valid session is created we create one for the anonymous user
 0958          if ($new_session)
 0959          {
 0960              $this->session_create(ANONYMOUS);
 0961          }
 0962   
 0963          return true;
 0964      }
 0965   
 0966      /**
 0967      * Session garbage collection
 0968      *
 0969      * This looks a lot more complex than it really is. Effectively we are
 0970      * deleting any sessions older than an admin definable limit. Due to the
 0971      * way in which we maintain session data we have to ensure we update user
 0972      * data before those sessions are destroyed. In addition this method
 0973      * removes autologin key information that is older than an admin defined
 0974      * limit.
 0975      */
 0976      function session_gc()
 0977      {
 0978          global $db, $config, $phpbb_root_path, $phpEx, $phpbb_container;
 0979   
 0980          $batch_size = 10;
 0981   
 0982          if (!$this->time_now)
 0983          {
 0984              $this->time_now = time();
 0985          }
 0986   
 0987          // Firstly, delete guest sessions
 0988          $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 0989              WHERE session_user_id = ' . ANONYMOUS . '
 0990                  AND session_time < ' . (int) ($this->time_now - $config['session_length']);
 0991          $db->sql_query($sql);
 0992   
 0993          // Get expired sessions, only most recent for each user
 0994          $sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time
 0995              FROM ' . SESSIONS_TABLE . '
 0996              WHERE session_time < ' . ($this->time_now - $config['session_length']) . '
 0997              GROUP BY session_user_id, session_page';
 0998          $result = $db->sql_query_limit($sql, $batch_size);
 0999   
 1000          $del_user_id = array();
 1001          $del_sessions = 0;
 1002   
 1003          while ($row = $db->sql_fetchrow($result))
 1004          {
 1005              $sql = 'UPDATE ' . USERS_TABLE . '
 1006                  SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
 1007                  WHERE user_id = " . (int) $row['session_user_id'];
 1008              $db->sql_query($sql);
 1009   
 1010              $del_user_id[] = (int) $row['session_user_id'];
 1011              $del_sessions++;
 1012          }
 1013          $db->sql_freeresult($result);
 1014   
 1015          if (sizeof($del_user_id))
 1016          {
 1017              // Delete expired sessions
 1018              $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 1019                  WHERE ' . $db->sql_in_set('session_user_id', $del_user_id) . '
 1020                      AND session_time < ' . ($this->time_now - $config['session_length']);
 1021              $db->sql_query($sql);
 1022          }
 1023   
 1024          if ($del_sessions < $batch_size)
 1025          {
 1026              // Less than 10 users, update gc timer ... else we want gc
 1027              // called again to delete other sessions
 1028              set_config('session_last_gc', $this->time_now, true);
 1029   
 1030              if ($config['max_autologin_time'])
 1031              {
 1032                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
 1033                      WHERE last_login < ' . (time() - (86400 * (int) $config['max_autologin_time']));
 1034                  $db->sql_query($sql);
 1035              }
 1036   
 1037              // only called from CRON; should be a safe workaround until the infrastructure gets going
 1038              $captcha_factory = $phpbb_container->get('captcha.factory');
 1039              $captcha_factory->garbage_collect($config['captcha_plugin']);
 1040   
 1041              $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
 1042                  WHERE attempt_time < ' . (time() - (int) $config['ip_login_limit_time']);
 1043              $db->sql_query($sql);
 1044          }
 1045   
 1046          return;
 1047      }
 1048   
 1049      /**
 1050      * Sets a cookie
 1051      *
 1052      * 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.
 1053      *
 1054      * @param string $name        Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.
 1055      * @param string $cookiedata    The data to hold within the cookie
 1056      * @param int $cookietime    The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set.
 1057      * @param bool $httponly        Use HttpOnly. Defaults to true. Use false to make cookie accessible by client-side scripts.
 1058      */
 1059      function set_cookie($name, $cookiedata, $cookietime, $httponly = true)
 1060      {
 1061          global $config;
 1062   
 1063          $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
 1064          $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
 1065          $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
 1066   
 1067          header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . ';' . (($httponly) ? ' HttpOnly' : ''), false);
 1068      }
 1069   
 1070      /**
 1071      * Check for banned user
 1072      *
 1073      * Checks whether the supplied user is banned by id, ip or email. If no parameters
 1074      * are passed to the method pre-existing session data is used.
 1075      *
 1076      * @param int|false        $user_id        The user id
 1077      * @param mixed            $user_ips        Can contain a string with one IP or an array of multiple IPs
 1078      * @param string|false    $user_email        The user email
 1079      * @param bool            $return            If $return is false this routine does not return on finding a banned user,
 1080      *    it outputs a relevant message and stops execution.
 1081      */
 1082      function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
 1083      {
 1084          global $config, $db;
 1085   
 1086          if (defined('IN_CHECK_BAN') || defined('SKIP_CHECK_BAN'))
 1087          {
 1088              return;
 1089          }
 1090   
 1091          $banned = false;
 1092          $cache_ttl = 3600;
 1093          $where_sql = array();
 1094   
 1095          $sql = 'SELECT ban_ip, ban_userid, ban_email, ban_exclude, ban_give_reason, ban_end
 1096              FROM ' . BANLIST_TABLE . '
 1097              WHERE ';
 1098   
 1099          // Determine which entries to check, only return those
 1100          if ($user_email === false)
 1101          {
 1102              $where_sql[] = "ban_email = ''";
 1103          }
 1104   
 1105          if ($user_ips === false)
 1106          {
 1107              $where_sql[] = "(ban_ip = '' OR ban_exclude = 1)";
 1108          }
 1109   
 1110          if ($user_id === false)
 1111          {
 1112              $where_sql[] = '(ban_userid = 0 OR ban_exclude = 1)';
 1113          }
 1114          else
 1115          {
 1116              $cache_ttl = ($user_id == ANONYMOUS) ? 3600 : 0;
 1117              $_sql = '(ban_userid = ' . $user_id;
 1118   
 1119              if ($user_email !== false)
 1120              {
 1121                  $_sql .= " OR ban_email <> ''";
 1122              }
 1123   
 1124              if ($user_ips !== false)
 1125              {
 1126                  $_sql .= " OR ban_ip <> ''";
 1127              }
 1128   
 1129              $_sql .= ')';
 1130   
 1131              $where_sql[] = $_sql;
 1132          }
 1133   
 1134          $sql .= (sizeof($where_sql)) ? implode(' AND ', $where_sql) : '';
 1135          $result = $db->sql_query($sql, $cache_ttl);
 1136   
 1137          $ban_triggered_by = 'user';
 1138          while ($row = $db->sql_fetchrow($result))
 1139          {
 1140              if ($row['ban_end'] && $row['ban_end'] < time())
 1141              {
 1142                  continue;
 1143              }
 1144   
 1145              $ip_banned = false;
 1146              if (!empty($row['ban_ip']))
 1147              {
 1148                  if (!is_array($user_ips))
 1149                  {
 1150                      $ip_banned = preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ips);
 1151                  }
 1152                  else
 1153                  {
 1154                      foreach ($user_ips as $user_ip)
 1155                      {
 1156                          if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ip))
 1157                          {
 1158                              $ip_banned = true;
 1159                              break;
 1160                          }
 1161                      }
 1162                  }
 1163              }
 1164   
 1165              if ((!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id) ||
 1166                  $ip_banned ||
 1167                  (!empty($row['ban_email']) && preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_email'], '#')) . '$#i', $user_email)))
 1168              {
 1169                  if (!empty($row['ban_exclude']))
 1170                  {
 1171                      $banned = false;
 1172                      break;
 1173                  }
 1174                  else
 1175                  {
 1176                      $banned = true;
 1177                      $ban_row = $row;
 1178   
 1179                      if (!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id)
 1180                      {
 1181                          $ban_triggered_by = 'user';
 1182                      }
 1183                      else if ($ip_banned)
 1184                      {
 1185                          $ban_triggered_by = 'ip';
 1186                      }
 1187                      else
 1188                      {
 1189                          $ban_triggered_by = 'email';
 1190                      }
 1191   
 1192                      // Don't break. Check if there is an exclude rule for this user
 1193                  }
 1194              }
 1195          }
 1196          $db->sql_freeresult($result);
 1197   
 1198          if ($banned && !$return)
 1199          {
 1200              global $template, $phpbb_root_path, $phpEx;
 1201   
 1202              // If the session is empty we need to create a valid one...
 1203              if (empty($this->session_id))
 1204              {
 1205                  // This seems to be no longer needed? - #14971
 1206  //                $this->session_create(ANONYMOUS);
 1207              }
 1208   
 1209              // Initiate environment ... since it won't be set at this stage
 1210              $this->setup();
 1211   
 1212              // Logout the user, banned users are unable to use the normal 'logout' link
 1213              if ($this->data['user_id'] != ANONYMOUS)
 1214              {
 1215                  $this->session_kill();
 1216              }
 1217   
 1218              // We show a login box here to allow founders accessing the board if banned by IP
 1219              if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)
 1220              {
 1221                  $this->setup('ucp');
 1222                  $this->data['is_registered'] = $this->data['is_bot'] = false;
 1223   
 1224                  // Set as a precaution to allow login_box() handling this case correctly as well as this function not being executed again.
 1225                  define('IN_CHECK_BAN', 1);
 1226   
 1227                  login_box("index.$phpEx");
 1228   
 1229                  // The false here is needed, else the user is able to circumvent the ban.
 1230                  $this->session_kill(false);
 1231              }
 1232   
 1233              // Ok, we catch the case of an empty session id for the anonymous user...
 1234              // This can happen if the user is logging in, banned by username and the login_box() being called "again".
 1235              if (empty($this->session_id) && defined('IN_CHECK_BAN'))
 1236              {
 1237                  $this->session_create(ANONYMOUS);
 1238              }
 1239   
 1240              // Determine which message to output
 1241              $till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';
 1242              $message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
 1243   
 1244              $contact_link = phpbb_get_board_contact_link($config, $phpbb_root_path, $phpEx);
 1245              $message = sprintf($this->lang[$message], $till_date, '<a href="' . $contact_link . '">', '</a>');
 1246              $message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';
 1247              $message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>';
 1248   
 1249              // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again
 1250              $this->session_kill(false);
 1251   
 1252              // A very special case... we are within the cron script which is not supposed to print out the ban message... show blank page
 1253              if (defined('IN_CRON'))
 1254              {
 1255                  garbage_collection();
 1256                  exit_handler();
 1257                  exit;
 1258              }
 1259   
 1260              trigger_error($message);
 1261          }
 1262   
 1263          return ($banned && $ban_row['ban_give_reason']) ? $ban_row['ban_give_reason'] : $banned;
 1264      }
 1265   
 1266      /**
 1267      * Check if ip is blacklisted
 1268      * This should be called only where absolutely necessary
 1269      *
 1270      * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
 1271      *
 1272      * @author satmd (from the php manual)
 1273      * @param string         $mode    register/post - spamcop for example is ommitted for posting
 1274      * @param string|false    $ip        the IPv4 address to check
 1275      *
 1276      * @return false if ip is not blacklisted, else an array([checked server], [lookup])
 1277      */
 1278      function check_dnsbl($mode, $ip = false)
 1279      {
 1280          if ($ip === false)
 1281          {
 1282              $ip = $this->ip;
 1283          }
 1284   
 1285          // Neither Spamhaus nor Spamcop supports IPv6 addresses.
 1286          if (strpos($ip, ':') !== false)
 1287          {
 1288              return false;
 1289          }
 1290   
 1291          $dnsbl_check = array(
 1292              'sbl.spamhaus.org'    => 'http://www.spamhaus.org/query/bl?ip=',
 1293          );
 1294   
 1295          if ($mode == 'register')
 1296          {
 1297              $dnsbl_check['bl.spamcop.net'] = 'http://spamcop.net/bl.shtml?';
 1298          }
 1299   
 1300          if ($ip)
 1301          {
 1302              $quads = explode('.', $ip);
 1303              $reverse_ip = $quads[3] . '.' . $quads[2] . '.' . $quads[1] . '.' . $quads[0];
 1304   
 1305              // Need to be listed on all servers...
 1306              $listed = true;
 1307              $info = array();
 1308   
 1309              foreach ($dnsbl_check as $dnsbl => $lookup)
 1310              {
 1311                  if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true)
 1312                  {
 1313                      $info = array($dnsbl, $lookup . $ip);
 1314                  }
 1315                  else
 1316                  {
 1317                      $listed = false;
 1318                  }
 1319              }
 1320   
 1321              if ($listed)
 1322              {
 1323                  return $info;
 1324              }
 1325          }
 1326   
 1327          return false;
 1328      }
 1329   
 1330      /**
 1331      * Check if URI is blacklisted
 1332      * This should be called only where absolutly necessary, for example on the submitted website field
 1333      * This function is not in use at the moment and is only included for testing purposes, it may not work at all!
 1334      * This means it is untested at the moment and therefore commented out
 1335      *
 1336      * @param string $uri URI to check
 1337      * @return true if uri is on blacklist, else false. Only blacklist is checked (~zero FP), no grey lists
 1338      function check_uribl($uri)
 1339      {
 1340          // Normally parse_url() is not intended to parse uris
 1341          // We need to get the top-level domain name anyway... change.
 1342          $uri = parse_url($uri);
 1343   
 1344          if ($uri === false || empty($uri['host']))
 1345          {
 1346              return false;
 1347          }
 1348   
 1349          $uri = trim($uri['host']);
 1350   
 1351          if ($uri)
 1352          {
 1353              // One problem here... the return parameter for the "windows" method is different from what
 1354              // we expect... this may render this check useless...
 1355              if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true)
 1356              {
 1357                  return true;
 1358              }
 1359          }
 1360   
 1361          return false;
 1362      }
 1363      */
 1364   
 1365      /**
 1366      * Set/Update a persistent login key
 1367      *
 1368      * This method creates or updates a persistent session key. When a user makes
 1369      * use of persistent (formerly auto-) logins a key is generated and stored in the
 1370      * DB. When they revisit with the same key it's automatically updated in both the
 1371      * DB and cookie. Multiple keys may exist for each user representing different
 1372      * browsers or locations. As with _any_ non-secure-socket no passphrase login this
 1373      * remains vulnerable to exploit.
 1374      */
 1375      function set_login_key($user_id = false, $key = false, $user_ip = false)
 1376      {
 1377          global $config, $db;
 1378   
 1379          $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
 1380          $user_ip = ($user_ip === false) ? $this->ip : $user_ip;
 1381          $key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key;
 1382   
 1383          $key_id = unique_id(hexdec(substr($this->session_id, 0, 8)));
 1384   
 1385          $sql_ary = array(
 1386              'key_id'        => (string) md5($key_id),
 1387              'last_ip'        => (string) $this->ip,
 1388              'last_login'    => (int) time()
 1389          );
 1390   
 1391          if (!$key)
 1392          {
 1393              $sql_ary += array(
 1394                  'user_id'    => (int) $user_id
 1395              );
 1396          }
 1397   
 1398          if ($key)
 1399          {
 1400              $sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . '
 1401                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 1402                  WHERE user_id = ' . (int) $user_id . "
 1403                      AND key_id = '" . $db->sql_escape(md5($key)) . "'";
 1404          }
 1405          else
 1406          {
 1407              $sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 1408          }
 1409          $db->sql_query($sql);
 1410   
 1411          $this->cookie_data['k'] = $key_id;
 1412   
 1413          return false;
 1414      }
 1415   
 1416      /**
 1417      * Reset all login keys for the specified user
 1418      *
 1419      * This method removes all current login keys for a specified (or the current)
 1420      * user. It will be called on password change to render old keys unusable
 1421      */
 1422      function reset_login_keys($user_id = false)
 1423      {
 1424          global $config, $db;
 1425   
 1426          $user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id;
 1427   
 1428          $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
 1429              WHERE user_id = ' . (int) $user_id;
 1430          $db->sql_query($sql);
 1431   
 1432          // If the user is logged in, update last visit info first before deleting sessions
 1433          $sql = 'SELECT session_time, session_page
 1434              FROM ' . SESSIONS_TABLE . '
 1435              WHERE session_user_id = ' . (int) $user_id . '
 1436              ORDER BY session_time DESC';
 1437          $result = $db->sql_query_limit($sql, 1);
 1438          $row = $db->sql_fetchrow($result);
 1439          $db->sql_freeresult($result);
 1440   
 1441          if ($row)
 1442          {
 1443              $sql = 'UPDATE ' . USERS_TABLE . '
 1444                  SET user_lastvisit = ' . (int) $row['session_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
 1445                  WHERE user_id = " . (int) $user_id;
 1446              $db->sql_query($sql);
 1447          }
 1448   
 1449          // Let's also clear any current sessions for the specified user_id
 1450          // If it's the current user then we'll leave this session intact
 1451          $sql_where = 'session_user_id = ' . (int) $user_id;
 1452          $sql_where .= ($user_id === (int) $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : '';
 1453   
 1454          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
 1455              WHERE $sql_where";
 1456          $db->sql_query($sql);
 1457   
 1458          // We're changing the password of the current user and they have a key
 1459          // Lets regenerate it to be safe
 1460          if ($user_id === (int) $this->data['user_id'] && $this->cookie_data['k'])
 1461          {
 1462              $this->set_login_key($user_id);
 1463          }
 1464      }
 1465   
 1466   
 1467      /**
 1468      * Check if the request originated from the same page.
 1469      * @param bool $check_script_path If true, the path will be checked as well
 1470      */
 1471      function validate_referer($check_script_path = false)
 1472      {
 1473          global $config, $request;
 1474   
 1475          // no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason)
 1476          if (empty($this->referer) || empty($this->host))
 1477          {
 1478              return true;
 1479          }
 1480   
 1481          $host = htmlspecialchars($this->host);
 1482          $ref = substr($this->referer, strpos($this->referer, '://') + 3);
 1483   
 1484          if (!(stripos($ref, $host) === 0) && (!$config['force_server_vars'] || !(stripos($ref, $config['server_name']) === 0)))
 1485          {
 1486              return false;
 1487          }
 1488          else if ($check_script_path && rtrim($this->page['root_script_path'], '/') !== '')
 1489          {
 1490              $ref = substr($ref, strlen($host));
 1491              $server_port = $request->server('SERVER_PORT', 0);
 1492   
 1493              if ($server_port !== 80 && $server_port !== 443 && stripos($ref, ":$server_port") === 0)
 1494              {
 1495                  $ref = substr($ref, strlen(":$server_port"));
 1496              }
 1497   
 1498              if (!(stripos(rtrim($ref, '/'), rtrim($this->page['root_script_path'], '/')) === 0))
 1499              {
 1500                  return false;
 1501              }
 1502          }
 1503   
 1504          return true;
 1505      }
 1506   
 1507   
 1508      function unset_admin()
 1509      {
 1510          global $db;
 1511          $sql = 'UPDATE ' . SESSIONS_TABLE . '
 1512              SET session_admin = 0
 1513              WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'';
 1514          $db->sql_query($sql);
 1515      }
 1516   
 1517      /**
 1518      * Update the session data
 1519      *
 1520      * @param array $session_data associative array of session keys to be updated
 1521      * @param string $session_id optional session_id, defaults to current user's session_id
 1522      */
 1523      public function update_session($session_data, $session_id = null)
 1524      {
 1525          global $db;
 1526   
 1527          $session_id = ($session_id) ? $session_id : $this->session_id;
 1528   
 1529          $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $session_data) . "
 1530              WHERE session_id = '" . $db->sql_escape($session_id) . "'";
 1531          $db->sql_query($sql);
 1532      }
 1533  }
 1534