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