[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/includes/ -> session.php (source)

   1  <?php
   2  /**
   3  *
   4  * @package phpBB3
   5  * @version $Id$
   6  * @copyright (c) 2005 phpBB Group
   7  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
   8  *
   9  */
  10  
  11  /**
  12  * @ignore
  13  */
  14  if (!defined('IN_PHPBB'))
  15  {
  16      exit;
  17  }
  18  
  19  /**
  20  * Session class
  21  * @package phpBB3
  22  */
  23  class session
  24  {
  25      var $cookie_data = array();
  26      var $page = array();
  27      var $data = array();
  28      var $browser = '';
  29      var $forwarded_for = '';
  30      var $host = '';
  31      var $session_id = '';
  32      var $ip = '';
  33      var $load = 0;
  34      var $time_now = 0;
  35      var $update_session_page = true;
  36  
  37      /**
  38      * Extract current session page
  39      *
  40      * @param string $root_path current root path (phpbb_root_path)
  41      */
  42  	function extract_current_page($root_path)
  43      {
  44          $page_array = array();
  45  
  46          // First of all, get the request uri...
  47          $script_name = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : getenv('PHP_SELF');
  48          $args = (!empty($_SERVER['QUERY_STRING'])) ? explode('&', $_SERVER['QUERY_STRING']) : explode('&', getenv('QUERY_STRING'));
  49  
  50          // 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...
  51          if (!$script_name)
  52          {
  53              $script_name = (!empty($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : getenv('REQUEST_URI');
  54              $script_name = (($pos = strpos($script_name, '?')) !== false) ? substr($script_name, 0, $pos) : $script_name;
  55              $page_array['failover'] = 1;
  56          }
  57  
  58          // Replace backslashes and doubled slashes (could happen on some proxy setups)
  59          $script_name = str_replace(array('\\', '//'), '/', $script_name);
  60  
  61          // Now, remove the sid and let us get a clean query string...
  62          $use_args = array();
  63  
  64          // Since some browser do not encode correctly we need to do this with some "special" characters...
  65          // " -> %22, ' => %27, < -> %3C, > -> %3E
  66          $find = array('"', "'", '<', '>');
  67          $replace = array('%22', '%27', '%3C', '%3E');
  68  
  69          foreach ($args as $key => $argument)
  70          {
  71              if (strpos($argument, 'sid=') === 0)
  72              {
  73                  continue;
  74              }
  75  
  76              $use_args[] = str_replace($find, $replace, $argument);
  77          }
  78          unset($args);
  79  
  80          // The following examples given are for an request uri of {path to the phpbb directory}/adm/index.php?i=10&b=2
  81  
  82          // The current query string
  83          $query_string = trim(implode('&', $use_args));
  84  
  85          // basenamed page name (for example: index.php)
  86          $page_name = (substr($script_name, -1, 1) == '/') ? '' : basename($script_name);
  87          $page_name = urlencode(htmlspecialchars($page_name));
  88  
  89          // current directory within the phpBB root (for example: adm)
  90          $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path)));
  91          $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
  92          $intersection = array_intersect_assoc($root_dirs, $page_dirs);
  93  
  94          $root_dirs = array_diff_assoc($root_dirs, $intersection);
  95          $page_dirs = array_diff_assoc($page_dirs, $intersection);
  96  
  97          $page_dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
  98  
  99          if ($page_dir && substr($page_dir, -1, 1) == '/')
 100          {
 101              $page_dir = substr($page_dir, 0, -1);
 102          }
 103  
 104          // Current page from phpBB root (for example: adm/index.php?i=10&b=2)
 105          $page = (($page_dir) ? $page_dir . '/' : '') . $page_name . (($query_string) ? "?$query_string" : '');
 106  
 107          // The script path from the webroot to the current directory (for example: /phpBB3/adm/) : always prefixed with / and ends in /
 108          $script_path = trim(str_replace('\\', '/', dirname($script_name)));
 109  
 110          // The script path from the webroot to the phpBB root (for example: /phpBB3/)
 111          $script_dirs = explode('/', $script_path);
 112          array_splice($script_dirs, -sizeof($page_dirs));
 113          $root_script_path = implode('/', $script_dirs) . (sizeof($root_dirs) ? '/' . implode('/', $root_dirs) : '');
 114  
 115          // We are on the base level (phpBB root == webroot), lets adjust the variables a bit...
 116          if (!$root_script_path)
 117          {
 118              $root_script_path = ($page_dir) ? str_replace($page_dir, '', $script_path) : $script_path;
 119          }
 120  
 121          $script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/';
 122          $root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/';
 123  
 124          $page_array += array(
 125              'page_name'            => $page_name,
 126              'page_dir'            => $page_dir,
 127  
 128              'query_string'        => $query_string,
 129              'script_path'        => str_replace(' ', '%20', htmlspecialchars($script_path)),
 130              'root_script_path'    => str_replace(' ', '%20', htmlspecialchars($root_script_path)),
 131  
 132              'page'                => $page,
 133              'forum'                => (isset($_REQUEST['f']) && $_REQUEST['f'] > 0) ? (int) $_REQUEST['f'] : 0,
 134          );
 135  
 136          return $page_array;
 137      }
 138  
 139      /**
 140      * Get valid hostname/port. HTTP_HOST is used, SERVER_NAME if HTTP_HOST not present.
 141      */
 142  	function extract_current_hostname()
 143      {
 144          global $config;
 145  
 146          // Get hostname
 147          $host = (!empty($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : ((!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME'));
 148  
 149          // Should be a string and lowered
 150          $host = (string) strtolower($host);
 151  
 152          // If host is equal the cookie domain or the server name (if config is set), then we assume it is valid
 153          if ((isset($config['cookie_domain']) && $host === $config['cookie_domain']) || (isset($config['server_name']) && $host === $config['server_name']))
 154          {
 155              return $host;
 156          }
 157  
 158          // Is the host actually a IP? If so, we use the IP... (IPv4)
 159          if (long2ip(ip2long($host)) === $host)
 160          {
 161              return $host;
 162          }
 163  
 164          // 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
 165          $host = @parse_url('http://' . $host);
 166          $host = (!empty($host['host'])) ? $host['host'] : '';
 167  
 168          // Remove any portions not removed by parse_url (#)
 169          $host = str_replace('#', '', $host);
 170  
 171          // If, by any means, the host is now empty, we will use a "best approach" way to guess one
 172          if (empty($host))
 173          {
 174              if (!empty($config['server_name']))
 175              {
 176                  $host = $config['server_name'];
 177              }
 178              else if (!empty($config['cookie_domain']))
 179              {
 180                  $host = (strpos($config['cookie_domain'], '.') === 0) ? substr($config['cookie_domain'], 1) : $config['cookie_domain'];
 181              }
 182              else
 183              {
 184                  // Set to OS hostname or localhost
 185                  $host = (function_exists('php_uname')) ? php_uname('n') : 'localhost';
 186              }
 187          }
 188  
 189          // It may be still no valid host, but for sure only a hostname (we may further expand on the cookie domain... if set)
 190          return $host;
 191      }
 192  
 193      /**
 194      * Start session management
 195      *
 196      * This is where all session activity begins. We gather various pieces of
 197      * information from the client and server. We test to see if a session already
 198      * exists. If it does, fine and dandy. If it doesn't we'll go on to create a
 199      * new one ... pretty logical heh? We also examine the system load (if we're
 200      * running on a system which makes such information readily available) and
 201      * halt if it's above an admin definable limit.
 202      *
 203      * @param bool $update_session_page if true the session page gets updated.
 204      *            This can be set to circumvent certain scripts to update the users last visited page.
 205      */
 206  	function session_begin($update_session_page = true)
 207      {
 208          global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path;
 209  
 210          // Give us some basic information
 211          $this->time_now                = time();
 212          $this->cookie_data            = array('u' => 0, 'k' => '');
 213          $this->update_session_page    = $update_session_page;
 214          $this->browser                = (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : '';
 215          $this->referer                = (!empty($_SERVER['HTTP_REFERER'])) ? htmlspecialchars((string) $_SERVER['HTTP_REFERER']) : '';
 216          $this->forwarded_for        = (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? htmlspecialchars((string) $_SERVER['HTTP_X_FORWARDED_FOR']) : '';
 217  
 218          $this->host                    = $this->extract_current_hostname();
 219          $this->page                    = $this->extract_current_page($phpbb_root_path);
 220  
 221          // if the forwarded for header shall be checked we have to validate its contents
 222          if ($config['forwarded_for_check'])
 223          {
 224              $this->forwarded_for = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->forwarded_for));
 225  
 226              // split the list of IPs
 227              $ips = explode(' ', $this->forwarded_for);
 228              foreach ($ips as $ip)
 229              {
 230                  // check IPv4 first, the IPv6 is hopefully only going to be used very seldomly
 231                  if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip))
 232                  {
 233                      // contains invalid data, don't use the forwarded for header
 234                      $this->forwarded_for = '';
 235                      break;
 236                  }
 237              }
 238          }
 239          else
 240          {
 241              $this->forwarded_for = '';
 242          }
 243  
 244          if (isset($_COOKIE[$config['cookie_name'] . '_sid']) || isset($_COOKIE[$config['cookie_name'] . '_u']))
 245          {
 246              $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
 247              $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
 248              $this->session_id         = request_var($config['cookie_name'] . '_sid', '', false, true);
 249  
 250              $SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid=';
 251              $_SID = (defined('NEED_SID')) ? $this->session_id : '';
 252  
 253              if (empty($this->session_id))
 254              {
 255                  $this->session_id = $_SID = request_var('sid', '');
 256                  $SID = '?sid=' . $this->session_id;
 257                  $this->cookie_data = array('u' => 0, 'k' => '');
 258              }
 259          }
 260          else
 261          {
 262              $this->session_id = $_SID = request_var('sid', '');
 263              $SID = '?sid=' . $this->session_id;
 264          }
 265  
 266          $_EXTRA_URL = array();
 267  
 268          // Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests
 269          // it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip.
 270          $this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? (string) $_SERVER['REMOTE_ADDR'] : '';
 271          $this->ip = preg_replace('# {2,}#', ' ', str_replace(',', ' ', $this->ip));
 272  
 273          // split the list of IPs
 274          $ips = explode(' ', trim($this->ip));
 275  
 276          // Default IP if REMOTE_ADDR is invalid
 277          $this->ip = '127.0.0.1';
 278  
 279          foreach ($ips as $ip)
 280          {
 281              if (preg_match(get_preg_expression('ipv4'), $ip))
 282              {
 283                  $this->ip = $ip;
 284              }
 285              else if (preg_match(get_preg_expression('ipv6'), $ip))
 286              {
 287                  // Quick check for IPv4-mapped address in IPv6
 288                  if (stripos($ip, '::ffff:') === 0)
 289                  {
 290                      $ipv4 = substr($ip, 7);
 291  
 292                      if (preg_match(get_preg_expression('ipv4'), $ipv4))
 293                      {
 294                          $ip = $ipv4;
 295                      }
 296                  }
 297  
 298                  $this->ip = $ip;
 299              }
 300              else
 301              {
 302                  // We want to use the last valid address in the chain
 303                  // Leave foreach loop when address is invalid
 304                  break;
 305              }
 306          }
 307  
 308          $this->load = false;
 309  
 310          // Load limit check (if applicable)
 311          if ($config['limit_load'] || $config['limit_search_load'])
 312          {
 313              if ((function_exists('sys_getloadavg') && $load = sys_getloadavg()) || ($load = explode(' ', @file_get_contents('/proc/loadavg'))))
 314              {
 315                  $this->load = array_slice($load, 0, 1);
 316                  $this->load = floatval($this->load[0]);
 317              }
 318              else
 319              {
 320                  set_config('limit_load', '0');
 321                  set_config('limit_search_load', '0');
 322              }
 323          }
 324  
 325          // if no session id is set, redirect to index.php
 326          if (defined('NEED_SID') && (!isset($_GET['sid']) || $this->session_id !== $_GET['sid']))
 327          {
 328              send_status_line(401, 'Unauthorized');
 329              redirect(append_sid("{$phpbb_root_path}index.$phpEx"));
 330          }
 331  
 332          // if session id is set
 333          if (!empty($this->session_id))
 334          {
 335              $sql = 'SELECT u.*, s.*
 336                  FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u
 337                  WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "'
 338                      AND u.user_id = s.session_user_id";
 339              $result = $db->sql_query($sql);
 340              $this->data = $db->sql_fetchrow($result);
 341              $db->sql_freeresult($result);
 342  
 343              // Did the session exist in the DB?
 344              if (isset($this->data['user_id']))
 345              {
 346                  // Validate IP length according to admin ... enforces an IP
 347                  // check on bots if admin requires this
 348  //                $quadcheck = ($config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : $config['ip_check'];
 349  
 350                  if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
 351                  {
 352                      $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
 353                      $u_ip = short_ipv6($this->ip, $config['ip_check']);
 354                  }
 355                  else
 356                  {
 357                      $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
 358                      $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
 359                  }
 360  
 361                  $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
 362                  $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
 363  
 364                  $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
 365                  $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
 366  
 367                  // referer checks
 368                  // The @ before $config['referer_validation'] suppresses notices present while running the updater
 369                  $check_referer_path = (@$config['referer_validation'] == REFERER_VALIDATE_PATH);
 370                  $referer_valid = true;
 371  
 372                  // we assume HEAD and TRACE to be foul play and thus only whitelist GET
 373                  if (@$config['referer_validation'] && isset($_SERVER['REQUEST_METHOD']) && strtolower($_SERVER['REQUEST_METHOD']) !== 'get')
 374                  {
 375                      $referer_valid = $this->validate_referer($check_referer_path);
 376                  }
 377  
 378                  if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for && $referer_valid)
 379                  {
 380                      $session_expired = false;
 381  
 382                      // Check whether the session is still valid if we have one
 383                      $method = basename(trim($config['auth_method']));
 384                      include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
 385  
 386                      $method = 'validate_session_' . $method;
 387                      if (function_exists($method))
 388                      {
 389                          if (!$method($this->data))
 390                          {
 391                              $session_expired = true;
 392                          }
 393                      }
 394  
 395                      if (!$session_expired)
 396                      {
 397                          // Check the session length timeframe if autologin is not enabled.
 398                          // Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide.
 399                          if (!$this->data['session_autologin'])
 400                          {
 401                              if ($this->data['session_time'] < $this->time_now - ($config['session_length'] + 60))
 402                              {
 403                                  $session_expired = true;
 404                              }
 405                          }
 406                          else if (!$config['allow_autologin'] || ($config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) $config['max_autologin_time']) + 60))
 407                          {
 408                              $session_expired = true;
 409                          }
 410                      }
 411  
 412                      if (!$session_expired)
 413                      {
 414                          // Only update session DB a minute or so after last update or if page changes
 415                          if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
 416                          {
 417                              $sql_ary = array('session_time' => $this->time_now);
 418  
 419                              if ($this->update_session_page)
 420                              {
 421                                  $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
 422                                  $sql_ary['session_forum_id'] = $this->page['forum'];
 423                              }
 424  
 425                              $db->sql_return_on_error(true);
 426  
 427                              $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
 428                                  WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
 429                              $result = $db->sql_query($sql);
 430  
 431                              $db->sql_return_on_error(false);
 432  
 433                              // If the database is not yet updated, there will be an error due to the session_forum_id
 434                              // @todo REMOVE for 3.0.2
 435                              if ($result === false)
 436                              {
 437                                  unset($sql_ary['session_forum_id']);
 438  
 439                                  $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
 440                                      WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
 441                                  $db->sql_query($sql);
 442                              }
 443  
 444                              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'])
 445                              {
 446                                  $this->leave_newly_registered();
 447                              }
 448                          }
 449  
 450                          $this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
 451                          $this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false;
 452                          $this->data['user_lang'] = basename($this->data['user_lang']);
 453  
 454                          return true;
 455                      }
 456                  }
 457                  else
 458                  {
 459                      // Added logging temporarly to help debug bugs...
 460                      if (defined('DEBUG_EXTRA') && $this->data['user_id'] != ANONYMOUS)
 461                      {
 462                          if ($referer_valid)
 463                          {
 464                              add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for));
 465                          }
 466                          else
 467                          {
 468                              add_log('critical', 'LOG_REFERER_INVALID', $this->referer);
 469                          }
 470                      }
 471                  }
 472              }
 473          }
 474  
 475          // If we reach here then no (valid) session exists. So we'll create a new one
 476          return $this->session_create();
 477      }
 478  
 479      /**
 480      * Create a new session
 481      *
 482      * If upon trying to start a session we discover there is nothing existing we
 483      * jump here. Additionally this method is called directly during login to regenerate
 484      * the session for the specific user. In this method we carry out a number of tasks;
 485      * garbage collection, (search)bot checking, banned user comparison. Basically
 486      * though this method will result in a new session for a specific user.
 487      */
 488  	function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
 489      {
 490          global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx;
 491  
 492          $this->data = array();
 493  
 494          /* Garbage collection ... remove old sessions updating user information
 495          // if necessary. It means (potentially) 11 queries but only infrequently
 496          if ($this->time_now > $config['session_last_gc'] + $config['session_gc'])
 497          {
 498              $this->session_gc();
 499          }*/
 500  
 501          // Do we allow autologin on this board? No? Then override anything
 502          // that may be requested here
 503          if (!$config['allow_autologin'])
 504          {
 505              $this->cookie_data['k'] = $persist_login = false;
 506          }
 507  
 508          /**
 509          * Here we do a bot check, oh er saucy! No, not that kind of bot
 510          * check. We loop through the list of bots defined by the admin and
 511          * see if we have any useragent and/or IP matches. If we do, this is a
 512          * bot, act accordingly
 513          */
 514          $bot = false;
 515          $active_bots = $cache->obtain_bots();
 516  
 517          foreach ($active_bots as $row)
 518          {
 519              if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->browser))
 520              {
 521                  $bot = $row['user_id'];
 522              }
 523  
 524              // If ip is supplied, we will make sure the ip is matching too...
 525              if ($row['bot_ip'] && ($bot || !$row['bot_agent']))
 526              {
 527                  // Set bot to false, then we only have to set it to true if it is matching
 528                  $bot = false;
 529  
 530                  foreach (explode(',', $row['bot_ip']) as $bot_ip)
 531                  {
 532                      $bot_ip = trim($bot_ip);
 533  
 534                      if (!$bot_ip)
 535                      {
 536                          continue;
 537                      }
 538  
 539                      if (strpos($this->ip, $bot_ip) === 0)
 540                      {
 541                          $bot = (int) $row['user_id'];
 542                          break;
 543                      }
 544                  }
 545              }
 546  
 547              if ($bot)
 548              {
 549                  break;
 550              }
 551          }
 552  
 553          $method = basename(trim($config['auth_method']));
 554          include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
 555  
 556          $method = 'autologin_' . $method;
 557          if (function_exists($method))
 558          {
 559              $this->data = $method();
 560  
 561              if (sizeof($this->data))
 562              {
 563                  $this->cookie_data['k'] = '';
 564                  $this->cookie_data['u'] = $this->data['user_id'];
 565              }
 566          }
 567  
 568          // If we're presented with an autologin key we'll join against it.
 569          // Else if we've been passed a user_id we'll grab data based on that
 570          if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data))
 571          {
 572              $sql = 'SELECT u.*
 573                  FROM ' . USERS_TABLE . ' u, ' . SESSIONS_KEYS_TABLE . ' k
 574                  WHERE u.user_id = ' . (int) $this->cookie_data['u'] . '
 575                      AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ")
 576                      AND k.user_id = u.user_id
 577                      AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
 578              $result = $db->sql_query($sql);
 579              $this->data = $db->sql_fetchrow($result);
 580              $db->sql_freeresult($result);
 581              $bot = false;
 582          }
 583          else if ($user_id !== false && !sizeof($this->data))
 584          {
 585              $this->cookie_data['k'] = '';
 586              $this->cookie_data['u'] = $user_id;
 587  
 588              $sql = 'SELECT *
 589                  FROM ' . USERS_TABLE . '
 590                  WHERE user_id = ' . (int) $this->cookie_data['u'] . '
 591                      AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
 592              $result = $db->sql_query($sql);
 593              $this->data = $db->sql_fetchrow($result);
 594              $db->sql_freeresult($result);
 595              $bot = false;
 596          }
 597  
 598          // Bot user, if they have a SID in the Request URI we need to get rid of it
 599          // otherwise they'll index this page with the SID, duplicate content oh my!
 600          if ($bot && isset($_GET['sid']))
 601          {
 602              send_status_line(301, 'Moved Permanently');
 603              redirect(build_url(array('sid')));
 604          }
 605  
 606          // If no data was returned one or more of the following occurred:
 607          // Key didn't match one in the DB
 608          // User does not exist
 609          // User is inactive
 610          // User is bot
 611          if (!sizeof($this->data) || !is_array($this->data))
 612          {
 613              $this->cookie_data['k'] = '';
 614              $this->cookie_data['u'] = ($bot) ? $bot : ANONYMOUS;
 615  
 616              if (!$bot)
 617              {
 618                  $sql = 'SELECT *
 619                      FROM ' . USERS_TABLE . '
 620                      WHERE user_id = ' . (int) $this->cookie_data['u'];
 621              }
 622              else
 623              {
 624                  // We give bots always the same session if it is not yet expired.
 625                  $sql = 'SELECT u.*, s.*
 626                      FROM ' . USERS_TABLE . ' u
 627                      LEFT JOIN ' . SESSIONS_TABLE . ' s ON (s.session_user_id = u.user_id)
 628                      WHERE u.user_id = ' . (int) $bot;
 629              }
 630  
 631              $result = $db->sql_query($sql);
 632              $this->data = $db->sql_fetchrow($result);
 633              $db->sql_freeresult($result);
 634          }
 635  
 636          if ($this->data['user_id'] != ANONYMOUS && !$bot)
 637          {
 638              $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());
 639          }
 640          else
 641          {
 642              $this->data['session_last_visit'] = $this->time_now;
 643          }
 644  
 645          // Force user id to be integer...
 646          $this->data['user_id'] = (int) $this->data['user_id'];
 647  
 648          // At this stage we should have a filled data array, defined cookie u and k data.
 649          // data array should contain recent session info if we're a real user and a recent
 650          // session exists in which case session_id will also be set
 651  
 652          // Is user banned? Are they excluded? Won't return on ban, exists within method
 653          if ($this->data['user_type'] != USER_FOUNDER)
 654          {
 655              if (!$config['forwarded_for_check'])
 656              {
 657                  $this->check_ban($this->data['user_id'], $this->ip);
 658              }
 659              else
 660              {
 661                  $ips = explode(' ', $this->forwarded_for);
 662                  $ips[] = $this->ip;
 663                  $this->check_ban($this->data['user_id'], $ips);
 664              }
 665          }
 666  
 667          $this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
 668          $this->data['is_bot'] = ($bot) ? true : false;
 669  
 670          // If our friend is a bot, we re-assign a previously assigned session
 671          if ($this->data['is_bot'] && $bot == $this->data['user_id'] && $this->data['session_id'])
 672          {
 673              // Only assign the current session if the ip, browser and forwarded_for match...
 674              if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
 675              {
 676                  $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
 677                  $u_ip = short_ipv6($this->ip, $config['ip_check']);
 678              }
 679              else
 680              {
 681                  $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
 682                  $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
 683              }
 684  
 685              $s_browser = ($config['browser_check']) ? trim(strtolower(substr($this->data['session_browser'], 0, 149))) : '';
 686              $u_browser = ($config['browser_check']) ? trim(strtolower(substr($this->browser, 0, 149))) : '';
 687  
 688              $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
 689              $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
 690  
 691              if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for)
 692              {
 693                  $this->session_id = $this->data['session_id'];
 694  
 695                  // Only update session DB a minute or so after last update or if page changes
 696                  if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
 697                  {
 698                      $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
 699  
 700                      $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0);
 701  
 702                      if ($this->update_session_page)
 703                      {
 704                          $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
 705                          $sql_ary['session_forum_id'] = $this->page['forum'];
 706                      }
 707  
 708                      $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
 709                          WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
 710                      $db->sql_query($sql);
 711  
 712                      // Update the last visit time
 713                      $sql = 'UPDATE ' . USERS_TABLE . '
 714                          SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 715                          WHERE user_id = ' . (int) $this->data['user_id'];
 716                      $db->sql_query($sql);
 717                  }
 718  
 719                  $SID = '?sid=';
 720                  $_SID = '';
 721                  return true;
 722              }
 723              else
 724              {
 725                  // If the ip and browser does not match make sure we only have one bot assigned to one session
 726                  $db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']);
 727              }
 728          }
 729  
 730          $session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false;
 731          $set_admin = ($set_admin && $this->data['is_registered']) ? true : false;
 732  
 733          // Create or update the session
 734          $sql_ary = array(
 735              'session_user_id'        => (int) $this->data['user_id'],
 736              'session_start'            => (int) $this->time_now,
 737              'session_last_visit'    => (int) $this->data['session_last_visit'],
 738              'session_time'            => (int) $this->time_now,
 739              'session_browser'        => (string) trim(substr($this->browser, 0, 149)),
 740              'session_forwarded_for'    => (string) $this->forwarded_for,
 741              'session_ip'            => (string) $this->ip,
 742              'session_autologin'        => ($session_autologin) ? 1 : 0,
 743              'session_admin'            => ($set_admin) ? 1 : 0,
 744              'session_viewonline'    => ($viewonline) ? 1 : 0,
 745          );
 746  
 747          if ($this->update_session_page)
 748          {
 749              $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
 750              $sql_ary['session_forum_id'] = $this->page['forum'];
 751          }
 752  
 753          $db->sql_return_on_error(true);
 754  
 755          $sql = 'DELETE
 756              FROM ' . SESSIONS_TABLE . '
 757              WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'
 758                  AND session_user_id = ' . ANONYMOUS;
 759  
 760          if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows()))
 761          {
 762              // Limit new sessions in 1 minute period (if required)
 763              if (empty($this->data['session_time']) && $config['active_sessions'])
 764              {
 765  //                $db->sql_return_on_error(false);
 766  
 767                  $sql = 'SELECT COUNT(session_id) AS sessions
 768                      FROM ' . SESSIONS_TABLE . '
 769                      WHERE session_time >= ' . ($this->time_now - 60);
 770                  $result = $db->sql_query($sql);
 771                  $row = $db->sql_fetchrow($result);
 772                  $db->sql_freeresult($result);
 773  
 774                  if ((int) $row['sessions'] > (int) $config['active_sessions'])
 775                  {
 776                      send_status_line(503, 'Service Unavailable');
 777                      trigger_error('BOARD_UNAVAILABLE');
 778                  }
 779              }
 780          }
 781  
 782          // Since we re-create the session id here, the inserted row must be unique. Therefore, we display potential errors.
 783          // Commented out because it will not allow forums to update correctly
 784  //        $db->sql_return_on_error(false);
 785  
 786          // Something quite important: session_page always holds the *last* page visited, except for the *first* visit.
 787          // We are not able to simply have an empty session_page btw, therefore we need to tell phpBB how to detect this special case.
 788          // 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.
 789          if (empty($this->data['session_id']))
 790          {
 791              // This is a temporary variable, only set for the very first visit
 792              $this->data['session_created'] = true;
 793          }
 794  
 795          $this->session_id = $this->data['session_id'] = md5(unique_id());
 796  
 797          $sql_ary['session_id'] = (string) $this->session_id;
 798          $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
 799          $sql_ary['session_forum_id'] = $this->page['forum'];
 800  
 801          $sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 802          $db->sql_query($sql);
 803  
 804          $db->sql_return_on_error(false);
 805  
 806          // Regenerate autologin/persistent login key
 807          if ($session_autologin)
 808          {
 809              $this->set_login_key();
 810          }
 811  
 812          // refresh data
 813          $SID = '?sid=' . $this->session_id;
 814          $_SID = $this->session_id;
 815          $this->data = array_merge($this->data, $sql_ary);
 816  
 817          if (!$bot)
 818          {
 819              $cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000);
 820  
 821              $this->set_cookie('u', $this->cookie_data['u'], $cookie_expire);
 822              $this->set_cookie('k', $this->cookie_data['k'], $cookie_expire);
 823              $this->set_cookie('sid', $this->session_id, $cookie_expire);
 824  
 825              unset($cookie_expire);
 826  
 827              $sql = 'SELECT COUNT(session_id) AS sessions
 828                      FROM ' . SESSIONS_TABLE . '
 829                      WHERE session_user_id = ' . (int) $this->data['user_id'] . '
 830                      AND session_time >= ' . (int) ($this->time_now - (max($config['session_length'], $config['form_token_lifetime'])));
 831              $result = $db->sql_query($sql);
 832              $row = $db->sql_fetchrow($result);
 833              $db->sql_freeresult($result);
 834  
 835              if ((int) $row['sessions'] <= 1 || empty($this->data['user_form_salt']))
 836              {
 837                  $this->data['user_form_salt'] = unique_id();
 838                  // Update the form key
 839                  $sql = 'UPDATE ' . USERS_TABLE . '
 840                      SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
 841                      WHERE user_id = ' . (int) $this->data['user_id'];
 842                  $db->sql_query($sql);
 843              }
 844          }
 845          else
 846          {
 847              $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
 848  
 849              // Update the last visit time
 850              $sql = 'UPDATE ' . USERS_TABLE . '
 851                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 852                  WHERE user_id = ' . (int) $this->data['user_id'];
 853              $db->sql_query($sql);
 854  
 855              $SID = '?sid=';
 856              $_SID = '';
 857          }
 858  
 859          return true;
 860      }
 861  
 862      /**
 863      * Kills a session
 864      *
 865      * This method does what it says on the tin. It will delete a pre-existing session.
 866      * It resets cookie information (destroying any autologin key within that cookie data)
 867      * and update the users information from the relevant session data. It will then
 868      * grab guest user information.
 869      */
 870  	function session_kill($new_session = true)
 871      {
 872          global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx;
 873  
 874          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
 875              WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
 876                  AND session_user_id = " . (int) $this->data['user_id'];
 877          $db->sql_query($sql);
 878  
 879          // Allow connecting logout with external auth method logout
 880          $method = basename(trim($config['auth_method']));
 881          include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
 882  
 883          $method = 'logout_' . $method;
 884          if (function_exists($method))
 885          {
 886              $method($this->data, $new_session);
 887          }
 888  
 889          if ($this->data['user_id'] != ANONYMOUS)
 890          {
 891              // Delete existing session, update last visit info first!
 892              if (!isset($this->data['session_time']))
 893              {
 894                  $this->data['session_time'] = time();
 895              }
 896  
 897              $sql = 'UPDATE ' . USERS_TABLE . '
 898                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
 899                  WHERE user_id = ' . (int) $this->data['user_id'];
 900              $db->sql_query($sql);
 901  
 902              if ($this->cookie_data['k'])
 903              {
 904                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
 905                      WHERE user_id = ' . (int) $this->data['user_id'] . "
 906                          AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
 907                  $db->sql_query($sql);
 908              }
 909  
 910              // Reset the data array
 911              $this->data = array();
 912  
 913              $sql = 'SELECT *
 914                  FROM ' . USERS_TABLE . '
 915                  WHERE user_id = ' . ANONYMOUS;
 916              $result = $db->sql_query($sql);
 917              $this->data = $db->sql_fetchrow($result);
 918              $db->sql_freeresult($result);
 919          }
 920  
 921          $cookie_expire = $this->time_now - 31536000;
 922          $this->set_cookie('u', '', $cookie_expire);
 923          $this->set_cookie('k', '', $cookie_expire);
 924          $this->set_cookie('sid', '', $cookie_expire);
 925          unset($cookie_expire);
 926  
 927          $SID = '?sid=';
 928          $this->session_id = $_SID = '';
 929  
 930          // To make sure a valid session is created we create one for the anonymous user
 931          if ($new_session)
 932          {
 933              $this->session_create(ANONYMOUS);
 934          }
 935  
 936          return true;
 937      }
 938  
 939      /**
 940      * Session garbage collection
 941      *
 942      * This looks a lot more complex than it really is. Effectively we are
 943      * deleting any sessions older than an admin definable limit. Due to the
 944      * way in which we maintain session data we have to ensure we update user
 945      * data before those sessions are destroyed. In addition this method
 946      * removes autologin key information that is older than an admin defined
 947      * limit.
 948      */
 949  	function session_gc()
 950      {
 951          global $db, $config, $phpbb_root_path, $phpEx;
 952  
 953          $batch_size = 10;
 954  
 955          if (!$this->time_now)
 956          {
 957              $this->time_now = time();
 958          }
 959  
 960          // Firstly, delete guest sessions
 961          $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 962              WHERE session_user_id = ' . ANONYMOUS . '
 963                  AND session_time < ' . (int) ($this->time_now - $config['session_length']);
 964          $db->sql_query($sql);
 965  
 966          // Get expired sessions, only most recent for each user
 967          $sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time
 968              FROM ' . SESSIONS_TABLE . '
 969              WHERE session_time < ' . ($this->time_now - $config['session_length']) . '
 970              GROUP BY session_user_id, session_page';
 971          $result = $db->sql_query_limit($sql, $batch_size);
 972  
 973          $del_user_id = array();
 974          $del_sessions = 0;
 975  
 976          while ($row = $db->sql_fetchrow($result))
 977          {
 978              $sql = 'UPDATE ' . USERS_TABLE . '
 979                  SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
 980                  WHERE user_id = " . (int) $row['session_user_id'];
 981              $db->sql_query($sql);
 982  
 983              $del_user_id[] = (int) $row['session_user_id'];
 984              $del_sessions++;
 985          }
 986          $db->sql_freeresult($result);
 987  
 988          if (sizeof($del_user_id))
 989          {
 990              // Delete expired sessions
 991              $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 992                  WHERE ' . $db->sql_in_set('session_user_id', $del_user_id) . '
 993                      AND session_time < ' . ($this->time_now - $config['session_length']);
 994              $db->sql_query($sql);
 995          }
 996  
 997          if ($del_sessions < $batch_size)
 998          {
 999              // Less than 10 users, update gc timer ... else we want gc
1000              // called again to delete other sessions
1001              set_config('session_last_gc', $this->time_now, true);
1002  
1003              if ($config['max_autologin_time'])
1004              {
1005                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
1006                      WHERE last_login < ' . (time() - (86400 * (int) $config['max_autologin_time']));
1007                  $db->sql_query($sql);
1008              }
1009  
1010              // only called from CRON; should be a safe workaround until the infrastructure gets going
1011              if (!class_exists('phpbb_captcha_factory'))
1012              {
1013                  include($phpbb_root_path . "includes/captcha/captcha_factory." . $phpEx);
1014              }
1015              phpbb_captcha_factory::garbage_collect($config['captcha_plugin']);
1016  
1017              $sql = 'DELETE FROM ' . LOGIN_ATTEMPT_TABLE . '
1018                  WHERE attempt_time < ' . (time() - (int) $config['ip_login_limit_time']);
1019              $db->sql_query($sql);
1020          }
1021  
1022          return;
1023      }
1024  
1025      /**
1026      * Sets a cookie
1027      *
1028      * 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.
1029      *
1030      * @param string $name        Name of the cookie, will be automatically prefixed with the phpBB cookie name. track becomes [cookie_name]_track then.
1031      * @param string $cookiedata    The data to hold within the cookie
1032      * @param int $cookietime    The expiration time as UNIX timestamp. If 0 is provided, a session cookie is set.
1033      */
1034  	function set_cookie($name, $cookiedata, $cookietime)
1035      {
1036          global $config;
1037  
1038          $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
1039          $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
1040          $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
1041  
1042          header('Set-Cookie: ' . $name_data . (($cookietime) ? '; expires=' . $expire : '') . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
1043      }
1044  
1045      /**
1046      * Check for banned user
1047      *
1048      * Checks whether the supplied user is banned by id, ip or email. If no parameters
1049      * are passed to the method pre-existing session data is used. If $return is false
1050      * this routine does not return on finding a banned user, it outputs a relevant
1051      * message and stops execution.
1052      *
1053      * @param string|array    $user_ips    Can contain a string with one IP or an array of multiple IPs
1054      */
1055  	function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
1056      {
1057          global $config, $db;
1058  
1059          if (defined('IN_CHECK_BAN'))
1060          {
1061              return;
1062          }
1063  
1064          $banned = false;
1065          $cache_ttl = 3600;
1066          $where_sql = array();
1067  
1068          $sql = 'SELECT ban_ip, ban_userid, ban_email, ban_exclude, ban_give_reason, ban_end
1069              FROM ' . BANLIST_TABLE . '
1070              WHERE ';
1071  
1072          // Determine which entries to check, only return those
1073          if ($user_email === false)
1074          {
1075              $where_sql[] = "ban_email = ''";
1076          }
1077  
1078          if ($user_ips === false)
1079          {
1080              $where_sql[] = "(ban_ip = '' OR ban_exclude = 1)";
1081          }
1082  
1083          if ($user_id === false)
1084          {
1085              $where_sql[] = '(ban_userid = 0 OR ban_exclude = 1)';
1086          }
1087          else
1088          {
1089              $cache_ttl = ($user_id == ANONYMOUS) ? 3600 : 0;
1090              $_sql = '(ban_userid = ' . $user_id;
1091  
1092              if ($user_email !== false)
1093              {
1094                  $_sql .= " OR ban_email <> ''";
1095              }
1096  
1097              if ($user_ips !== false)
1098              {
1099                  $_sql .= " OR ban_ip <> ''";
1100              }
1101  
1102              $_sql .= ')';
1103  
1104              $where_sql[] = $_sql;
1105          }
1106  
1107          $sql .= (sizeof($where_sql)) ? implode(' AND ', $where_sql) : '';
1108          $result = $db->sql_query($sql, $cache_ttl);
1109  
1110          $ban_triggered_by = 'user';
1111          while ($row = $db->sql_fetchrow($result))
1112          {
1113              if ($row['ban_end'] && $row['ban_end'] < time())
1114              {
1115                  continue;
1116              }
1117  
1118              $ip_banned = false;
1119              if (!empty($row['ban_ip']))
1120              {
1121                  if (!is_array($user_ips))
1122                  {
1123                      $ip_banned = preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ips);
1124                  }
1125                  else
1126                  {
1127                      foreach ($user_ips as $user_ip)
1128                      {
1129                          if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ip))
1130                          {
1131                              $ip_banned = true;
1132                              break;
1133                          }
1134                      }
1135                  }
1136              }
1137  
1138              if ((!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id) ||
1139                  $ip_banned ||
1140                  (!empty($row['ban_email']) && preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_email'], '#')) . '$#i', $user_email)))
1141              {
1142                  if (!empty($row['ban_exclude']))
1143                  {
1144                      $banned = false;
1145                      break;
1146                  }
1147                  else
1148                  {
1149                      $banned = true;
1150                      $ban_row = $row;
1151  
1152                      if (!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id)
1153                      {
1154                          $ban_triggered_by = 'user';
1155                      }
1156                      else if ($ip_banned)
1157                      {
1158                          $ban_triggered_by = 'ip';
1159                      }
1160                      else
1161                      {
1162                          $ban_triggered_by = 'email';
1163                      }
1164  
1165                      // Don't break. Check if there is an exclude rule for this user
1166                  }
1167              }
1168          }
1169          $db->sql_freeresult($result);
1170  
1171          if ($banned && !$return)
1172          {
1173              global $template;
1174  
1175              // If the session is empty we need to create a valid one...
1176              if (empty($this->session_id))
1177              {
1178                  // This seems to be no longer needed? - #14971
1179  //                $this->session_create(ANONYMOUS);
1180              }
1181  
1182              // Initiate environment ... since it won't be set at this stage
1183              $this->setup();
1184  
1185              // Logout the user, banned users are unable to use the normal 'logout' link
1186              if ($this->data['user_id'] != ANONYMOUS)
1187              {
1188                  $this->session_kill();
1189              }
1190  
1191              // We show a login box here to allow founders accessing the board if banned by IP
1192              if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)
1193              {
1194                  global $phpEx;
1195  
1196                  $this->setup('ucp');
1197                  $this->data['is_registered'] = $this->data['is_bot'] = false;
1198  
1199                  // Set as a precaution to allow login_box() handling this case correctly as well as this function not being executed again.
1200                  define('IN_CHECK_BAN', 1);
1201  
1202                  login_box("index.$phpEx");
1203  
1204                  // The false here is needed, else the user is able to circumvent the ban.
1205                  $this->session_kill(false);
1206              }
1207  
1208              // Ok, we catch the case of an empty session id for the anonymous user...
1209              // This can happen if the user is logging in, banned by username and the login_box() being called "again".
1210              if (empty($this->session_id) && defined('IN_CHECK_BAN'))
1211              {
1212                  $this->session_create(ANONYMOUS);
1213              }
1214  
1215  
1216              // Determine which message to output
1217              $till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';
1218              $message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
1219  
1220              $message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . $config['board_contact'] . '">', '</a>');
1221              $message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';
1222              $message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>';
1223  
1224              // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again
1225              $this->session_kill(false);
1226  
1227              // A very special case... we are within the cron script which is not supposed to print out the ban message... show blank page
1228              if (defined('IN_CRON'))
1229              {
1230                  garbage_collection();
1231                  exit_handler();
1232                  exit;
1233              }
1234  
1235              trigger_error($message);
1236          }
1237  
1238          return ($banned && $ban_row['ban_give_reason']) ? $ban_row['ban_give_reason'] : $banned;
1239      }
1240  
1241      /**
1242      * Check if ip is blacklisted
1243      * This should be called only where absolutly necessary
1244      *
1245      * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
1246      *
1247      * @author satmd (from the php manual)
1248      * @param string $mode register/post - spamcop for example is ommitted for posting
1249      * @return false if ip is not blacklisted, else an array([checked server], [lookup])
1250      */
1251  	function check_dnsbl($mode, $ip = false)
1252      {
1253          if ($ip === false)
1254          {
1255              $ip = $this->ip;
1256          }
1257  
1258          // Neither Spamhaus nor Spamcop supports IPv6 addresses.
1259          if (strpos($ip, ':') !== false)
1260          {
1261              return false;
1262          }
1263  
1264          $dnsbl_check = array(
1265              'sbl.spamhaus.org'    => 'http://www.spamhaus.org/query/bl?ip=',
1266          );
1267  
1268          if ($mode == 'register')
1269          {
1270              $dnsbl_check['bl.spamcop.net'] = 'http://spamcop.net/bl.shtml?';
1271          }
1272  
1273          if ($ip)
1274          {
1275              $quads = explode('.', $ip);
1276              $reverse_ip = $quads[3] . '.' . $quads[2] . '.' . $quads[1] . '.' . $quads[0];
1277  
1278              // Need to be listed on all servers...
1279              $listed = true;
1280              $info = array();
1281  
1282              foreach ($dnsbl_check as $dnsbl => $lookup)
1283              {
1284                  if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true)
1285                  {
1286                      $info = array($dnsbl, $lookup . $ip);
1287                  }
1288                  else
1289                  {
1290                      $listed = false;
1291                  }
1292              }
1293  
1294              if ($listed)
1295              {
1296                  return $info;
1297              }
1298          }
1299  
1300          return false;
1301      }
1302  
1303      /**
1304      * Check if URI is blacklisted
1305      * This should be called only where absolutly necessary, for example on the submitted website field
1306      * This function is not in use at the moment and is only included for testing purposes, it may not work at all!
1307      * This means it is untested at the moment and therefore commented out
1308      *
1309      * @param string $uri URI to check
1310      * @return true if uri is on blacklist, else false. Only blacklist is checked (~zero FP), no grey lists
1311      function check_uribl($uri)
1312      {
1313          // Normally parse_url() is not intended to parse uris
1314          // We need to get the top-level domain name anyway... change.
1315          $uri = parse_url($uri);
1316  
1317          if ($uri === false || empty($uri['host']))
1318          {
1319              return false;
1320          }
1321  
1322          $uri = trim($uri['host']);
1323  
1324          if ($uri)
1325          {
1326              // One problem here... the return parameter for the "windows" method is different from what
1327              // we expect... this may render this check useless...
1328              if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true)
1329              {
1330                  return true;
1331              }
1332          }
1333  
1334          return false;
1335      }
1336      */
1337  
1338      /**
1339      * Set/Update a persistent login key
1340      *
1341      * This method creates or updates a persistent session key. When a user makes
1342      * use of persistent (formerly auto-) logins a key is generated and stored in the
1343      * DB. When they revisit with the same key it's automatically updated in both the
1344      * DB and cookie. Multiple keys may exist for each user representing different
1345      * browsers or locations. As with _any_ non-secure-socket no passphrase login this
1346      * remains vulnerable to exploit.
1347      */
1348  	function set_login_key($user_id = false, $key = false, $user_ip = false)
1349      {
1350          global $config, $db;
1351  
1352          $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
1353          $user_ip = ($user_ip === false) ? $this->ip : $user_ip;
1354          $key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key;
1355  
1356          $key_id = unique_id(hexdec(substr($this->session_id, 0, 8)));
1357  
1358          $sql_ary = array(
1359              'key_id'        => (string) md5($key_id),
1360              'last_ip'        => (string) $this->ip,
1361              'last_login'    => (int) time()
1362          );
1363  
1364          if (!$key)
1365          {
1366              $sql_ary += array(
1367                  'user_id'    => (int) $user_id
1368              );
1369          }
1370  
1371          if ($key)
1372          {
1373              $sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . '
1374                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1375                  WHERE user_id = ' . (int) $user_id . "
1376                      AND key_id = '" . $db->sql_escape(md5($key)) . "'";
1377          }
1378          else
1379          {
1380              $sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
1381          }
1382          $db->sql_query($sql);
1383  
1384          $this->cookie_data['k'] = $key_id;
1385  
1386          return false;
1387      }
1388  
1389      /**
1390      * Reset all login keys for the specified user
1391      *
1392      * This method removes all current login keys for a specified (or the current)
1393      * user. It will be called on password change to render old keys unusable
1394      */
1395  	function reset_login_keys($user_id = false)
1396      {
1397          global $config, $db;
1398  
1399          $user_id = ($user_id === false) ? (int) $this->data['user_id'] : (int) $user_id;
1400  
1401          $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
1402              WHERE user_id = ' . (int) $user_id;
1403          $db->sql_query($sql);
1404  
1405          // If the user is logged in, update last visit info first before deleting sessions
1406          $sql = 'SELECT session_time, session_page
1407              FROM ' . SESSIONS_TABLE . '
1408              WHERE session_user_id = ' . (int) $user_id . '
1409              ORDER BY session_time DESC';
1410          $result = $db->sql_query_limit($sql, 1);
1411          $row = $db->sql_fetchrow($result);
1412          $db->sql_freeresult($result);
1413  
1414          if ($row)
1415          {
1416              $sql = 'UPDATE ' . USERS_TABLE . '
1417                  SET user_lastvisit = ' . (int) $row['session_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
1418                  WHERE user_id = " . (int) $user_id;
1419              $db->sql_query($sql);
1420          }
1421  
1422          // Let's also clear any current sessions for the specified user_id
1423          // If it's the current user then we'll leave this session intact
1424          $sql_where = 'session_user_id = ' . (int) $user_id;
1425          $sql_where .= ($user_id === (int) $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : '';
1426  
1427          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1428              WHERE $sql_where";
1429          $db->sql_query($sql);
1430  
1431          // We're changing the password of the current user and they have a key
1432          // Lets regenerate it to be safe
1433          if ($user_id === (int) $this->data['user_id'] && $this->cookie_data['k'])
1434          {
1435              $this->set_login_key($user_id);
1436          }
1437      }
1438  
1439  
1440      /**
1441      * Check if the request originated from the same page.
1442      * @param bool $check_script_path If true, the path will be checked as well
1443      */
1444  	function validate_referer($check_script_path = false)
1445      {
1446          global $config;
1447  
1448          // no referer - nothing to validate, user's fault for turning it off (we only check on POST; so meta can't be the reason)
1449          if (empty($this->referer) || empty($this->host))
1450          {
1451              return true;
1452          }
1453  
1454          $host = htmlspecialchars($this->host);
1455          $ref = substr($this->referer, strpos($this->referer, '://') + 3);
1456  
1457          if (!(stripos($ref, $host) === 0) && (!$config['force_server_vars'] || !(stripos($ref, $config['server_name']) === 0)))
1458          {
1459              return false;
1460          }
1461          else if ($check_script_path && rtrim($this->page['root_script_path'], '/') !== '')
1462          {
1463              $ref = substr($ref, strlen($host));
1464              $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1465  
1466              if ($server_port !== 80 && $server_port !== 443 && stripos($ref, ":$server_port") === 0)
1467              {
1468                  $ref = substr($ref, strlen(":$server_port"));
1469              }
1470  
1471              if (!(stripos(rtrim($ref, '/'), rtrim($this->page['root_script_path'], '/')) === 0))
1472              {
1473                  return false;
1474              }
1475          }
1476  
1477          return true;
1478      }
1479  
1480  
1481  	function unset_admin()
1482      {
1483          global $db;
1484          $sql = 'UPDATE ' . SESSIONS_TABLE . '
1485              SET session_admin = 0
1486              WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'';
1487          $db->sql_query($sql);
1488      }
1489  }
1490  
1491  
1492  /**
1493  * Base user class
1494  *
1495  * This is the overarching class which contains (through session extend)
1496  * all methods utilised for user functionality during a session.
1497  *
1498  * @package phpBB3
1499  */
1500  class user extends session
1501  {
1502      var $lang = array();
1503      var $help = array();
1504      var $theme = array();
1505      var $date_format;
1506      var $timezone;
1507      var $dst;
1508  
1509      var $lang_name = false;
1510      var $lang_id = false;
1511      var $lang_path;
1512      var $img_lang;
1513      var $img_array = array();
1514  
1515      // Able to add new options (up to id 31)
1516      var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10, 'sig_bbcode' => 15, 'sig_smilies' => 16, 'sig_links' => 17);
1517  
1518      /**
1519      * Constructor to set the lang path
1520      */
1521  	function user()
1522      {
1523          global $phpbb_root_path;
1524  
1525          $this->lang_path = $phpbb_root_path . 'language/';
1526      }
1527  
1528      /**
1529      * Function to set custom language path (able to use directory outside of phpBB)
1530      *
1531      * @param string $lang_path New language path used.
1532      * @access public
1533      */
1534  	function set_custom_lang_path($lang_path)
1535      {
1536          $this->lang_path = $lang_path;
1537  
1538          if (substr($this->lang_path, -1) != '/')
1539          {
1540              $this->lang_path .= '/';
1541          }
1542      }
1543  
1544      /**
1545      * Setup basic user-specific items (style, language, ...)
1546      */
1547  	function setup($lang_set = false, $style = false)
1548      {
1549          global $db, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache;
1550  
1551          if ($this->data['user_id'] != ANONYMOUS)
1552          {
1553              $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
1554  
1555              $this->date_format = $this->data['user_dateformat'];
1556              $this->timezone = $this->data['user_timezone'] * 3600;
1557              $this->dst = $this->data['user_dst'] * 3600;
1558          }
1559          else
1560          {
1561              $this->lang_name = basename($config['default_lang']);
1562              $this->date_format = $config['default_dateformat'];
1563              $this->timezone = $config['board_timezone'] * 3600;
1564              $this->dst = $config['board_dst'] * 3600;
1565  
1566              /**
1567              * If a guest user is surfing, we try to guess his/her language first by obtaining the browser language
1568              * If re-enabled we need to make sure only those languages installed are checked
1569              * Commented out so we do not loose the code.
1570  
1571              if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
1572              {
1573                  $accept_lang_ary = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
1574  
1575                  foreach ($accept_lang_ary as $accept_lang)
1576                  {
1577                      // Set correct format ... guess full xx_YY form
1578                      $accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2));
1579                      $accept_lang = basename($accept_lang);
1580  
1581                      if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx"))
1582                      {
1583                          $this->lang_name = $config['default_lang'] = $accept_lang;
1584                          break;
1585                      }
1586                      else
1587                      {
1588                          // No match on xx_YY so try xx
1589                          $accept_lang = substr($accept_lang, 0, 2);
1590                          $accept_lang = basename($accept_lang);
1591  
1592                          if (file_exists($this->lang_path . $accept_lang . "/common.$phpEx"))
1593                          {
1594                              $this->lang_name = $config['default_lang'] = $accept_lang;
1595                              break;
1596                          }
1597                      }
1598                  }
1599              }
1600              */
1601          }
1602  
1603          // We include common language file here to not load it every time a custom language file is included
1604          $lang = &$this->lang;
1605  
1606          // Do not suppress error if in DEBUG_EXTRA mode
1607          $include_result = (defined('DEBUG_EXTRA')) ? (include $this->lang_path . $this->lang_name . "/common.$phpEx") : (@include $this->lang_path . $this->lang_name . "/common.$phpEx");
1608  
1609          if ($include_result === false)
1610          {
1611              die('Language file ' . $this->lang_path . $this->lang_name . "/common.$phpEx" . " couldn't be opened.");
1612          }
1613  
1614          $this->add_lang($lang_set);
1615          unset($lang_set);
1616  
1617          if (!empty($_GET['style']) && $auth->acl_get('a_styles') && !defined('ADMIN_START'))
1618          {
1619              global $SID, $_EXTRA_URL;
1620  
1621              $style = request_var('style', 0);
1622              $SID .= '&amp;style=' . $style;
1623              $_EXTRA_URL = array('style=' . $style);
1624          }
1625          else
1626          {
1627              // Set up style
1628              $style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']);
1629          }
1630  
1631          $sql = 'SELECT s.style_id, t.template_storedb, t.template_path, t.template_id, t.bbcode_bitfield, t.template_inherits_id, t.template_inherit_path, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
1632              FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1633              WHERE s.style_id = $style
1634                  AND t.template_id = s.template_id
1635                  AND c.theme_id = s.theme_id
1636                  AND i.imageset_id = s.imageset_id";
1637          $result = $db->sql_query($sql, 3600);
1638          $this->theme = $db->sql_fetchrow($result);
1639          $db->sql_freeresult($result);
1640  
1641          // User has wrong style
1642          if (!$this->theme && $style == $this->data['user_style'])
1643          {
1644              $style = $this->data['user_style'] = $config['default_style'];
1645  
1646              $sql = 'UPDATE ' . USERS_TABLE . "
1647                  SET user_style = $style
1648                  WHERE user_id = {$this->data['user_id']}";
1649              $db->sql_query($sql);
1650  
1651              $sql = 'SELECT s.style_id, t.template_storedb, t.template_path, t.template_id, t.bbcode_bitfield, c.theme_path, c.theme_name, c.theme_storedb, c.theme_id, i.imageset_path, i.imageset_id, i.imageset_name
1652                  FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1653                  WHERE s.style_id = $style
1654                      AND t.template_id = s.template_id
1655                      AND c.theme_id = s.theme_id
1656                      AND i.imageset_id = s.imageset_id";
1657              $result = $db->sql_query($sql, 3600);
1658              $this->theme = $db->sql_fetchrow($result);
1659              $db->sql_freeresult($result);
1660          }
1661  
1662          if (!$this->theme)
1663          {
1664              trigger_error('NO_STYLE_DATA', E_USER_ERROR);
1665          }
1666  
1667          // Now parse the cfg file and cache it
1668          $parsed_items = $cache->obtain_cfg_items($this->theme);
1669  
1670          // We are only interested in the theme configuration for now
1671          $parsed_items = $parsed_items['theme'];
1672  
1673          $check_for = array(
1674              'parse_css_file'    => (int) 0,
1675              'pagination_sep'    => (string) ', '
1676          );
1677  
1678          foreach ($check_for as $key => $default_value)
1679          {
1680              $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
1681              settype($this->theme[$key], gettype($default_value));
1682  
1683              if (is_string($default_value))
1684              {
1685                  $this->theme[$key] = htmlspecialchars($this->theme[$key]);
1686              }
1687          }
1688  
1689          // If the style author specified the theme needs to be cached
1690          // (because of the used paths and variables) than make sure it is the case.
1691          // For example, if the theme uses language-specific images it needs to be stored in db.
1692          if (!$this->theme['theme_storedb'] && $this->theme['parse_css_file'])
1693          {
1694              $this->theme['theme_storedb'] = 1;
1695  
1696              $stylesheet = file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/stylesheet.css");
1697              // Match CSS imports
1698              $matches = array();
1699              preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
1700  
1701              if (sizeof($matches))
1702              {
1703                  $content = '';
1704                  foreach ($matches[0] as $idx => $match)
1705                  {
1706                      if ($content = @file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/" . $matches[1][$idx]))
1707                      {
1708                          $content = trim($content);
1709                      }
1710                      else
1711                      {
1712                          $content = '';
1713                      }
1714                      $stylesheet = str_replace($match, $content, $stylesheet);
1715                  }
1716                  unset($content);
1717              }
1718  
1719              $stylesheet = str_replace('./', 'styles/' . $this->theme['theme_path'] . '/theme/', $stylesheet);
1720  
1721              $sql_ary = array(
1722                  'theme_data'    => $stylesheet,
1723                  'theme_mtime'    => time(),
1724                  'theme_storedb'    => 1
1725              );
1726  
1727              $sql = 'UPDATE ' . STYLES_THEME_TABLE . '
1728                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1729                  WHERE theme_id = ' . $this->theme['theme_id'];
1730              $db->sql_query($sql);
1731  
1732              unset($sql_ary);
1733          }
1734  
1735          $template->set_template();
1736  
1737          $this->img_lang = (file_exists($phpbb_root_path . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . $this->lang_name)) ? $this->lang_name : $config['default_lang'];
1738  
1739          // Same query in style.php
1740          $sql = 'SELECT *
1741              FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1742              WHERE imageset_id = ' . $this->theme['imageset_id'] . "
1743              AND image_filename <> ''
1744              AND image_lang IN ('" . $db->sql_escape($this->img_lang) . "', '')";
1745          $result = $db->sql_query($sql, 3600);
1746  
1747          $localised_images = false;
1748          while ($row = $db->sql_fetchrow($result))
1749          {
1750              if ($row['image_lang'])
1751              {
1752                  $localised_images = true;
1753              }
1754  
1755              $row['image_filename'] = rawurlencode($row['image_filename']);
1756              $this->img_array[$row['image_name']] = $row;
1757          }
1758          $db->sql_freeresult($result);
1759  
1760          // there were no localised images, try to refresh the localised imageset for the user's language
1761          if (!$localised_images)
1762          {
1763              // Attention: this code ignores the image definition list from acp_styles and just takes everything
1764              // that the config file contains
1765              $sql_ary = array();
1766  
1767              $db->sql_transaction('begin');
1768  
1769              $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1770                  WHERE imageset_id = ' . $this->theme['imageset_id'] . '
1771                      AND image_lang = \'' . $db->sql_escape($this->img_lang) . '\'';
1772              $result = $db->sql_query($sql);
1773  
1774              if (@file_exists("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg"))
1775              {
1776                  $cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg");
1777                  foreach ($cfg_data_imageset_data as $image_name => $value)
1778                  {
1779                      if (strpos($value, '*') !== false)
1780                      {
1781                          if (substr($value, -1, 1) === '*')
1782                          {
1783                              list($image_filename, $image_height) = explode('*', $value);
1784                              $image_width = 0;
1785                          }
1786                          else
1787                          {
1788                              list($image_filename, $image_height, $image_width) = explode('*', $value);
1789                          }
1790                      }
1791                      else
1792                      {
1793                          $image_filename = $value;
1794                          $image_height = $image_width = 0;
1795                      }
1796  
1797                      if (strpos($image_name, 'img_') === 0 && $image_filename)
1798                      {
1799                          $image_name = substr($image_name, 4);
1800                          $sql_ary[] = array(
1801                              'image_name'        => (string) $image_name,
1802                              'image_filename'    => (string) $image_filename,
1803                              'image_height'        => (int) $image_height,
1804                              'image_width'        => (int) $image_width,
1805                              'imageset_id'        => (int) $this->theme['imageset_id'],
1806                              'image_lang'        => (string) $this->img_lang,
1807                          );
1808                      }
1809                  }
1810              }
1811  
1812              if (sizeof($sql_ary))
1813              {
1814                  $db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
1815                  $db->sql_transaction('commit');
1816                  $cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
1817  
1818                  add_log('admin', 'LOG_IMAGESET_LANG_REFRESHED', $this->theme['imageset_name'], $this->img_lang);
1819              }
1820              else
1821              {
1822                  $db->sql_transaction('commit');
1823                  add_log('admin', 'LOG_IMAGESET_LANG_MISSING', $this->theme['imageset_name'], $this->img_lang);
1824              }
1825          }
1826  
1827          // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes...
1828          // After calling it we continue script execution...
1829          phpbb_user_session_handler();
1830  
1831          // If this function got called from the error handler we are finished here.
1832          if (defined('IN_ERROR_HANDLER'))
1833          {
1834              return;
1835          }
1836  
1837          // Disable board if the install/ directory is still present
1838          // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally
1839          if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists($phpbb_root_path . 'install') && !is_file($phpbb_root_path . 'install'))
1840          {
1841              // Adjust the message slightly according to the permissions
1842              if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))
1843              {
1844                  $message = 'REMOVE_INSTALL';
1845              }
1846              else
1847              {
1848                  $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1849              }
1850              trigger_error($message);
1851          }
1852  
1853          // Is board disabled and user not an admin or moderator?
1854          if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1855          {
1856              if ($this->data['is_bot'])
1857              {
1858                  send_status_line(503, 'Service Unavailable');
1859              }
1860  
1861              $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1862              trigger_error($message);
1863          }
1864  
1865          // Is load exceeded?
1866          if ($config['limit_load'] && $this->load !== false)
1867          {
1868              if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN') && !defined('IN_ADMIN'))
1869              {
1870                  // Set board disabled to true to let the admins/mods get the proper notification
1871                  $config['board_disable'] = '1';
1872  
1873                  if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1874                  {
1875                      if ($this->data['is_bot'])
1876                      {
1877                          send_status_line(503, 'Service Unavailable');
1878                      }
1879                      trigger_error('BOARD_UNAVAILABLE');
1880                  }
1881              }
1882          }
1883  
1884          if (isset($this->data['session_viewonline']))
1885          {
1886              // Make sure the user is able to hide his session
1887              if (!$this->data['session_viewonline'])
1888              {
1889                  // Reset online status if not allowed to hide the session...
1890                  if (!$auth->acl_get('u_hideonline'))
1891                  {
1892                      $sql = 'UPDATE ' . SESSIONS_TABLE . '
1893                          SET session_viewonline = 1
1894                          WHERE session_user_id = ' . $this->data['user_id'];
1895                      $db->sql_query($sql);
1896                      $this->data['session_viewonline'] = 1;
1897                  }
1898              }
1899              else if (!$this->data['user_allow_viewonline'])
1900              {
1901                  // the user wants to hide and is allowed to  -> cloaking device on.
1902                  if ($auth->acl_get('u_hideonline'))
1903                  {
1904                      $sql = 'UPDATE ' . SESSIONS_TABLE . '
1905                          SET session_viewonline = 0
1906                          WHERE session_user_id = ' . $this->data['user_id'];
1907                      $db->sql_query($sql);
1908                      $this->data['session_viewonline'] = 0;
1909                  }
1910              }
1911          }
1912  
1913  
1914          // Does the user need to change their password? If so, redirect to the
1915          // ucp profile reg_details page ... of course do not redirect if we're already in the ucp
1916          if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && !empty($this->data['is_registered']) && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400))
1917          {
1918              if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != "ucp.$phpEx")
1919              {
1920                  redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&amp;mode=reg_details'));
1921              }
1922          }
1923  
1924          return;
1925      }
1926  
1927      /**
1928      * More advanced language substitution
1929      * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
1930      * Params are the language key and the parameters to be substituted.
1931      * This function/functionality is inspired by SHS` and Ashe.
1932      *
1933      * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp>
1934      */
1935  	function lang()
1936      {
1937          $args = func_get_args();
1938          $key = $args[0];
1939  
1940          if (is_array($key))
1941          {
1942              $lang = &$this->lang[array_shift($key)];
1943  
1944              foreach ($key as $_key)
1945              {
1946                  $lang = &$lang[$_key];
1947              }
1948          }
1949          else
1950          {
1951              $lang = &$this->lang[$key];
1952          }
1953  
1954          // Return if language string does not exist
1955          if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
1956          {
1957              return $key;
1958          }
1959  
1960          // If the language entry is a string, we simply mimic sprintf() behaviour
1961          if (is_string($lang))
1962          {
1963              if (sizeof($args) == 1)
1964              {
1965                  return $lang;
1966              }
1967  
1968              // Replace key with language entry and simply pass along...
1969              $args[0] = $lang;
1970              return call_user_func_array('sprintf', $args);
1971          }
1972  
1973          // It is an array... now handle different nullar/singular/plural forms
1974          $key_found = false;
1975  
1976          // We now get the first number passed and will select the key based upon this number
1977          for ($i = 1, $num_args = sizeof($args); $i < $num_args; $i++)
1978          {
1979              if (is_int($args[$i]))
1980              {
1981                  $numbers = array_keys($lang);
1982  
1983                  foreach ($numbers as $num)
1984                  {
1985                      if ($num > $args[$i])
1986                      {
1987                          break;
1988                      }
1989  
1990                      $key_found = $num;
1991                  }
1992                  break;
1993              }
1994          }
1995  
1996          // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
1997          if ($key_found === false)
1998          {
1999              $numbers = array_keys($lang);
2000              $key_found = end($numbers);
2001          }
2002  
2003          // Use the language string we determined and pass it to sprintf()
2004          $args[0] = $lang[$key_found];
2005          return call_user_func_array('sprintf', $args);
2006      }
2007  
2008      /**
2009      * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
2010      *
2011      * @param mixed $lang_set specifies the language entries to include
2012      * @param bool $use_db internal variable for recursion, do not use
2013      * @param bool $use_help internal variable for recursion, do not use
2014      *
2015      * Examples:
2016      * <code>
2017      * $lang_set = array('posting', 'help' => 'faq');
2018      * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq'))
2019      * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq'))
2020      * $lang_set = 'posting'
2021      * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
2022      * </code>
2023      */
2024  	function add_lang($lang_set, $use_db = false, $use_help = false)
2025      {
2026          global $phpEx;
2027  
2028          if (is_array($lang_set))
2029          {
2030              foreach ($lang_set as $key => $lang_file)
2031              {
2032                  // Please do not delete this line.
2033                  // We have to force the type here, else [array] language inclusion will not work
2034                  $key = (string) $key;
2035  
2036                  if ($key == 'db')
2037                  {
2038                      $this->add_lang($lang_file, true, $use_help);
2039                  }
2040                  else if ($key == 'help')
2041                  {
2042                      $this->add_lang($lang_file, $use_db, true);
2043                  }
2044                  else if (!is_array($lang_file))
2045                  {
2046                      $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
2047                  }
2048                  else
2049                  {
2050                      $this->add_lang($lang_file, $use_db, $use_help);
2051                  }
2052              }
2053              unset($lang_set);
2054          }
2055          else if ($lang_set)
2056          {
2057              $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
2058          }
2059      }
2060  
2061      /**
2062      * Set language entry (called by add_lang)
2063      * @access private
2064      */
2065  	function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
2066      {
2067          global $phpEx;
2068  
2069          // Make sure the language name is set (if the user setup did not happen it is not set)
2070          if (!$this->lang_name)
2071          {
2072              global $config;
2073              $this->lang_name = basename($config['default_lang']);
2074          }
2075  
2076          // $lang == $this->lang
2077          // $help == $this->help
2078          // - add appropriate variables here, name them as they are used within the language file...
2079          if (!$use_db)
2080          {
2081              if ($use_help && strpos($lang_file, '/') !== false)
2082              {
2083                  $language_filename = $this->lang_path . $this->lang_name . '/' . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . $phpEx;
2084              }
2085              else
2086              {
2087                  $language_filename = $this->lang_path . $this->lang_name . '/' . (($use_help) ? 'help_' : '') . $lang_file . '.' . $phpEx;
2088              }
2089  
2090              if (!file_exists($language_filename))
2091              {
2092                  global $config;
2093  
2094                  if ($this->lang_name == 'en')
2095                  {
2096                      // The user's selected language is missing the file, the board default's language is missing the file, and the file doesn't exist in /en.
2097                      $language_filename = str_replace($this->lang_path . 'en', $this->lang_path . $this->data['user_lang'], $language_filename);
2098                      trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
2099                  }
2100                  else if ($this->lang_name == basename($config['default_lang']))
2101                  {
2102                      // Fall back to the English Language
2103                      $this->lang_name = 'en';
2104                      $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
2105                  }
2106                  else if ($this->lang_name == $this->data['user_lang'])
2107                  {
2108                      // Fall back to the board default language
2109                      $this->lang_name = basename($config['default_lang']);
2110                      $this->set_lang($lang, $help, $lang_file, $use_db, $use_help);
2111                  }
2112  
2113                  // Reset the lang name
2114                  $this->lang_name = (file_exists($this->lang_path . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
2115                  return;
2116              }
2117  
2118              // Do not suppress error if in DEBUG_EXTRA mode
2119              $include_result = (defined('DEBUG_EXTRA')) ? (include $language_filename) : (@include $language_filename);
2120  
2121              if ($include_result === false)
2122              {
2123                  trigger_error('Language file ' . $language_filename . ' couldn\'t be opened.', E_USER_ERROR);
2124              }
2125          }
2126          else if ($use_db)
2127          {
2128              // Get Database Language Strings
2129              // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
2130              // For example: help:faq, posting
2131          }
2132      }
2133  
2134      /**
2135      * Format user date
2136      *
2137      * @param int $gmepoch unix timestamp
2138      * @param string $format date format in date() notation. | used to indicate relative dates, for example |d m Y|, h:i is translated to Today, h:i.
2139      * @param bool $forcedate force non-relative date format.
2140      *
2141      * @return mixed translated date
2142      */
2143  	function format_date($gmepoch, $format = false, $forcedate = false)
2144      {
2145          static $midnight;
2146          static $date_cache;
2147  
2148          $format = (!$format) ? $this->date_format : $format;
2149          $now = time();
2150          $delta = $now - $gmepoch;
2151  
2152          if (!isset($date_cache[$format]))
2153          {
2154              // Is the user requesting a friendly date format (i.e. 'Today 12:42')?
2155              $date_cache[$format] = array(
2156                  'is_short'        => strpos($format, '|'),
2157                  'format_short'    => substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1),
2158                  'format_long'    => str_replace('|', '', $format),
2159                  // Filter out values that are not strings (e.g. arrays) for strtr().
2160                  'lang'            => array_filter($this->lang['datetime'], 'is_string'),
2161              );
2162  
2163              // Short representation of month in format? Some languages use different terms for the long and short format of May
2164              if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
2165              {
2166                  $date_cache[$format]['lang']['May'] = $this->lang['datetime']['May_short'];
2167              }
2168          }
2169  
2170          // Zone offset
2171          $zone_offset = $this->timezone + $this->dst;
2172  
2173          // Show date <= 1 hour ago as 'xx min ago' but not greater than 60 seconds in the future
2174          // A small tolerence is given for times in the future but in the same minute are displayed as '< than a minute ago'
2175          if ($delta <= 3600 && $delta > -60 && ($delta >= -5 || (($now / 60) % 60) == (($gmepoch / 60) % 60)) && $date_cache[$format]['is_short'] !== false && !$forcedate && isset($this->lang['datetime']['AGO']))
2176          {
2177              return $this->lang(array('datetime', 'AGO'), max(0, (int) floor($delta / 60)));
2178          }
2179  
2180          if (!$midnight)
2181          {
2182              list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $zone_offset));
2183              $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $zone_offset;
2184          }
2185  
2186          if ($date_cache[$format]['is_short'] !== false && !$forcedate && !($gmepoch < $midnight - 86400 || $gmepoch > $midnight + 172800))
2187          {
2188              $day = false;
2189  
2190              if ($gmepoch > $midnight + 86400)
2191              {
2192                  $day = 'TOMORROW';
2193              }
2194              else if ($gmepoch > $midnight)
2195              {
2196                  $day = 'TODAY';
2197              }
2198              else if ($gmepoch > $midnight - 86400)
2199              {
2200                  $day = 'YESTERDAY';
2201              }
2202  
2203              if ($day !== false)
2204              {
2205                  return str_replace('||', $this->lang['datetime'][$day], strtr(@gmdate($date_cache[$format]['format_short'], $gmepoch + $zone_offset), $date_cache[$format]['lang']));
2206              }
2207          }
2208  
2209          return strtr(@gmdate($date_cache[$format]['format_long'], $gmepoch + $zone_offset), $date_cache[$format]['lang']);
2210      }
2211  
2212      /**
2213      * Get language id currently used by the user
2214      */
2215  	function get_iso_lang_id()
2216      {
2217          global $config, $db;
2218  
2219          if (!empty($this->lang_id))
2220          {
2221              return $this->lang_id;
2222          }
2223  
2224          if (!$this->lang_name)
2225          {
2226              $this->lang_name = $config['default_lang'];
2227          }
2228  
2229          $sql = 'SELECT lang_id
2230              FROM ' . LANG_TABLE . "
2231              WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'";
2232          $result = $db->sql_query($sql);
2233          $this->lang_id = (int) $db->sql_fetchfield('lang_id');
2234          $db->sql_freeresult($result);
2235  
2236          return $this->lang_id;
2237      }
2238  
2239      /**
2240      * Get users profile fields
2241      */
2242  	function get_profile_fields($user_id)
2243      {
2244          global $db;
2245  
2246          if (isset($this->profile_fields))
2247          {
2248              return;
2249          }
2250  
2251          $sql = 'SELECT *
2252              FROM ' . PROFILE_FIELDS_DATA_TABLE . "
2253              WHERE user_id = $user_id";
2254          $result = $db->sql_query_limit($sql, 1);
2255          $this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row;
2256          $db->sql_freeresult($result);
2257      }
2258  
2259      /**
2260      * Specify/Get image
2261      * $suffix is no longer used - we know it. ;) It is there for backward compatibility.
2262      */
2263  	function img($img, $alt = '', $width = false, $suffix = '', $type = 'full_tag')
2264      {
2265          static $imgs;
2266          global $phpbb_root_path;
2267  
2268          $img_data = &$imgs[$img];
2269  
2270          if (empty($img_data))
2271          {
2272              if (!isset($this->img_array[$img]))
2273              {
2274                  // Do not fill the image to let designers decide what to do if the image is empty
2275                  $img_data = '';
2276                  return $img_data;
2277              }
2278  
2279              // Use URL if told so
2280              $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path;
2281  
2282              $path = 'styles/' . rawurlencode($this->theme['imageset_path']) . '/imageset/' . ($this->img_array[$img]['image_lang'] ? $this->img_array[$img]['image_lang'] .'/' : '') . $this->img_array[$img]['image_filename'];
2283  
2284              $img_data['src'] = $root_path . $path;
2285              $img_data['width'] = $this->img_array[$img]['image_width'];
2286              $img_data['height'] = $this->img_array[$img]['image_height'];
2287  
2288              // We overwrite the width and height to the phpbb logo's width
2289              // and height here if the contents of the site_logo file are
2290              // really equal to the phpbb_logo
2291              // This allows us to change the dimensions of the phpbb_logo without
2292              // modifying the imageset.cfg and causing a conflict for everyone
2293              // who modified it for their custom logo on updating
2294              if ($img == 'site_logo' && file_exists($phpbb_root_path . $path))
2295              {
2296                  global $cache;
2297  
2298                  $img_file_hashes = $cache->get('imageset_site_logo_md5');
2299  
2300                  if ($img_file_hashes === false)
2301                  {
2302                      $img_file_hashes = array();
2303                  }
2304  
2305                  $key = $this->theme['imageset_path'] . '::' . $this->img_array[$img]['image_lang'];
2306                  if (!isset($img_file_hashes[$key]))
2307                  {
2308                      $img_file_hashes[$key] = md5(file_get_contents($phpbb_root_path . $path));
2309                      $cache->put('imageset_site_logo_md5', $img_file_hashes);
2310                  }
2311  
2312                  $phpbb_logo_hash = '0c461a32cd3621643105f0d02a772c10';
2313  
2314                  if ($phpbb_logo_hash == $img_file_hashes[$key])
2315                  {
2316                      $img_data['width'] = '149';
2317                      $img_data['height'] = '52';
2318                  }
2319              }
2320          }
2321  
2322          $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt;
2323  
2324          switch ($type)
2325          {
2326              case 'src':
2327                  return $img_data['src'];
2328              break;
2329  
2330              case 'width':
2331                  return ($width === false) ? $img_data['width'] : $width;
2332              break;
2333  
2334              case 'height':
2335                  return $img_data['height'];
2336              break;
2337  
2338              default:
2339                  $use_width = ($width === false) ? $img_data['width'] : $width;
2340  
2341                  return '<img src="' . $img_data['src'] . '"' . (($use_width) ? ' width="' . $use_width . '"' : '') . (($img_data['height']) ? ' height="' . $img_data['height'] . '"' : '') . ' alt="' . $alt . '" title="' . $alt . '" />';
2342              break;
2343          }
2344      }
2345  
2346      /**
2347      * Get option bit field from user options.
2348      *
2349      * @param int $key option key, as defined in $keyoptions property.
2350      * @param int $data bit field value to use, or false to use $this->data['user_options']
2351      * @return bool true if the option is set in the bit field, false otherwise
2352      */
2353  	function optionget($key, $data = false)
2354      {
2355          $var = ($data !== false) ? $data : $this->data['user_options'];
2356          return phpbb_optionget($this->keyoptions[$key], $var);
2357      }
2358  
2359      /**
2360      * Set option bit field for user options.
2361      *
2362      * @param int $key Option key, as defined in $keyoptions property.
2363      * @param bool $value True to set the option, false to clear the option.
2364      * @param int $data Current bit field value, or false to use $this->data['user_options']
2365      * @return int|bool If $data is false, the bit field is modified and
2366      *                  written back to $this->data['user_options'], and
2367      *                  return value is true if the bit field changed and
2368      *                  false otherwise. If $data is not false, the new
2369      *                  bitfield value is returned.
2370      */
2371  	function optionset($key, $value, $data = false)
2372      {
2373          $var = ($data !== false) ? $data : $this->data['user_options'];
2374  
2375          $new_var = phpbb_optionset($this->keyoptions[$key], $value, $var);
2376  
2377          if ($data === false)
2378          {
2379              if ($new_var != $var)
2380              {
2381                  $this->data['user_options'] = $new_var;
2382                  return true;
2383              }
2384              else
2385              {
2386                  return false;
2387              }
2388          }
2389          else
2390          {
2391              return $new_var;
2392          }
2393      }
2394  
2395      /**
2396      * Funtion to make the user leave the NEWLY_REGISTERED system group.
2397      * @access public
2398      */
2399  	function leave_newly_registered()
2400      {
2401          global $db;
2402  
2403          if (empty($this->data['user_new']))
2404          {
2405              return false;
2406          }
2407  
2408          if (!function_exists('remove_newly_registered'))
2409          {
2410              global $phpbb_root_path, $phpEx;
2411  
2412              include($phpbb_root_path . 'includes/functions_user.' . $phpEx);
2413          }
2414          if ($group = remove_newly_registered($this->data['user_id'], $this->data))
2415          {
2416              $this->data['group_id'] = $group;
2417  
2418          }
2419          $this->data['user_permissions'] = '';
2420          $this->data['user_new'] = 0;
2421  
2422          return true;
2423      }
2424  
2425      /**
2426      * Returns all password protected forum ids the user is currently NOT authenticated for.
2427      *
2428      * @return array        Array of forum ids
2429      * @access public
2430      */
2431  	function get_passworded_forums()
2432      {
2433          global $db;
2434  
2435          $sql = 'SELECT f.forum_id, fa.user_id
2436              FROM ' . FORUMS_TABLE . ' f
2437              LEFT JOIN ' . FORUMS_ACCESS_TABLE . " fa
2438                  ON (fa.forum_id = f.forum_id
2439                      AND fa.session_id = '" . $db->sql_escape($this->session_id) . "')
2440              WHERE f.forum_password <> ''";
2441          $result = $db->sql_query($sql);
2442  
2443          $forum_ids = array();
2444          while ($row = $db->sql_fetchrow($result))
2445          {
2446              $forum_id = (int) $row['forum_id'];
2447  
2448              if ($row['user_id'] != $this->data['user_id'])
2449              {
2450                  $forum_ids[$forum_id] = $forum_id;
2451              }
2452          }
2453          $db->sql_freeresult($result);
2454  
2455          return $forum_ids;
2456      }
2457  }
2458  
2459  ?>


Generated: Wed Oct 2 15:03:47 2013 Cross-referenced by PHPXref 0.7.1