Verzeichnisstruktur phpBB-3.0.0


Veröffentlicht
12.12.2007

So funktioniert es


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

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

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

session.php

Zuletzt modifiziert: 09.10.2024, 12:50 - Dateigröße: 60.83 KiB


0001  <?php
0002  /**
0003  *
0004  * @package phpBB3
0005  * @version $Id$
0006  * @copyright (c) 2005 phpBB Group
0007  * @license http://opensource.org/licenses/gpl-license.php GNU Public License
0008  *
0009  */
0010   
0011  /**
0012  * @ignore
0013  */
0014  if (!defined('IN_PHPBB'))
0015  {
0016      exit;
0017  }
0018   
0019  /**
0020  * Session class
0021  * @package phpBB3
0022  */
0023  class session
0024  {
0025      var $cookie_data = array();
0026      var $page = array();
0027      var $data = array();
0028      var $browser = '';
0029      var $forwarded_for = '';
0030      var $host = '';
0031      var $session_id = '';
0032      var $ip = '';
0033      var $load = 0;
0034      var $time_now = 0;
0035      var $update_session_page = true;
0036   
0037      /**
0038      * Extract current session page
0039      *
0040      * @param string $root_path current root path (phpbb_root_path)
0041      */
0042      function extract_current_page($root_path)
0043      {
0044          $page_array = array();
0045   
0046          // First of all, get the request uri...
0047          $script_name = (!empty($_SERVER['PHP_SELF'])) ? $_SERVER['PHP_SELF'] : getenv('PHP_SELF');
0048          $args = (!empty($_SERVER['QUERY_STRING'])) ? explode('&', $_SERVER['QUERY_STRING']) : explode('&', getenv('QUERY_STRING'));
0049   
0050          // 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...
0051          if (!$script_name)
0052          {
0053              $script_name = (!empty($_SERVER['REQUEST_URI'])) ? $_SERVER['REQUEST_URI'] : getenv('REQUEST_URI');
0054              $script_name = (($pos = strpos($script_name, '?')) !== false) ? substr($script_name, 0, $pos) : $script_name;
0055              $page_array['failover'] = 1;
0056          }
0057   
0058          // Replace backslashes and doubled slashes (could happen on some proxy setups)
0059          $script_name = str_replace(array('\\', '//'), '/', $script_name);
0060   
0061          // Now, remove the sid and let us get a clean query string...
0062          $use_args = array();
0063   
0064          // Since some browser do not encode correctly we need to do this with some "special" characters...
0065          // " -> %22, ' => %27, < -> %3C, > -> %3E
0066          $find = array('"', "'", '<', '>');
0067          $replace = array('%22', '%27', '%3C', '%3E');
0068   
0069          foreach ($args as $key => $argument)
0070          {
0071              if (strpos($argument, 'sid=') === 0 || strpos($argument, '_f_=') === 0)
0072              {
0073                  continue;
0074              }
0075   
0076              $use_args[str_replace($find, $replace, $key)] = str_replace($find, $replace, $argument);
0077          }
0078          unset($args);
0079   
0080          // The following examples given are for an request uri of {path to the phpbb directory}/adm/index.php?i=10&b=2
0081   
0082          // The current query string
0083          $query_string = trim(implode('&', $use_args));
0084   
0085          // basenamed page name (for example: index.php)
0086          $page_name = basename($script_name);
0087          $page_name = urlencode(htmlspecialchars($page_name));
0088   
0089          // current directory within the phpBB root (for example: adm)
0090          $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($root_path)));
0091          $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath('./')));
0092          $intersection = array_intersect_assoc($root_dirs, $page_dirs);
0093   
0094          $root_dirs = array_diff_assoc($root_dirs, $intersection);
0095          $page_dirs = array_diff_assoc($page_dirs, $intersection);
0096   
0097          $page_dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
0098   
0099          if ($page_dir && substr($page_dir, -1, 1) == '/')
0100          {
0101              $page_dir = substr($page_dir, 0, -1);
0102          }
0103   
0104          // Current page from phpBB root (for example: adm/index.php?i=10&b=2)
0105          $page = (($page_dir) ? $page_dir . '/' : '') . $page_name . (($query_string) ? "?$query_string" : '');
0106   
0107          // The script path from the webroot to the current directory (for example: /phpBB3/adm/) : always prefixed with / and ends in /
0108          $script_path = trim(str_replace('\\', '/', dirname($script_name)));
0109   
0110          // The script path from the webroot to the phpBB root (for example: /phpBB3/)
0111          $script_dirs = explode('/', $script_path);
0112          array_splice($script_dirs, -sizeof($page_dirs));
0113          $root_script_path = implode('/', $script_dirs) . (sizeof($root_dirs) ? '/' . implode('/', $root_dirs) : '');
0114   
0115          // We are on the base level (phpBB root == webroot), lets adjust the variables a bit...
0116          if (!$root_script_path)
0117          {
0118              $root_script_path = ($page_dir) ? str_replace($page_dir, '', $script_path) : $script_path;
0119          }
0120   
0121          $script_path .= (substr($script_path, -1, 1) == '/') ? '' : '/';
0122          $root_script_path .= (substr($root_script_path, -1, 1) == '/') ? '' : '/';
0123   
0124          $page_array += array(
0125              'page_name'            => $page_name,
0126              'page_dir'            => $page_dir,
0127   
0128              'query_string'        => $query_string,
0129              'script_path'        => str_replace(' ', '%20', htmlspecialchars($script_path)),
0130              'root_script_path'    => str_replace(' ', '%20', htmlspecialchars($root_script_path)),
0131   
0132              'page'                => $page
0133          );
0134   
0135          return $page_array;
0136      }
0137   
0138      /**
0139      * Start session management
0140      *
0141      * This is where all session activity begins. We gather various pieces of
0142      * information from the client and server. We test to see if a session already
0143      * exists. If it does, fine and dandy. If it doesn't we'll go on to create a
0144      * new one ... pretty logical heh? We also examine the system load (if we're
0145      * running on a system which makes such information readily available) and
0146      * halt if it's above an admin definable limit.
0147      *
0148      * @param bool $update_session_page if true the session page gets updated.
0149      *            This can be set to circumvent certain scripts to update the users last visited page.
0150      */
0151      function session_begin($update_session_page = true)
0152      {
0153          global $phpEx, $SID, $_SID, $_EXTRA_URL, $db, $config, $phpbb_root_path;
0154   
0155          // Give us some basic information
0156          $this->time_now                = time();
0157          $this->cookie_data            = array('u' => 0, 'k' => '');
0158          $this->update_session_page    = $update_session_page;
0159          $this->browser                = (!empty($_SERVER['HTTP_USER_AGENT'])) ? htmlspecialchars((string) $_SERVER['HTTP_USER_AGENT']) : '';
0160          $this->forwarded_for        = (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) ? (string) $_SERVER['HTTP_X_FORWARDED_FOR'] : '';
0161          $this->host                    = (!empty($_SERVER['HTTP_HOST'])) ? (string) $_SERVER['HTTP_HOST'] : 'localhost';
0162          $this->page                    = $this->extract_current_page($phpbb_root_path);
0163   
0164          // if the forwarded for header shall be checked we have to validate its contents
0165          if ($config['forwarded_for_check'])
0166          {
0167              $this->forwarded_for = preg_replace('#, +#', ', ', $this->forwarded_for);
0168   
0169              // split the list of IPs
0170              $ips = explode(', ', $this->forwarded_for);
0171              foreach ($ips as $ip)
0172              {
0173                  // check IPv4 first, the IPv6 is hopefully only going to be used very seldomly
0174                  if (!empty($ip) && !preg_match(get_preg_expression('ipv4'), $ip) && !preg_match(get_preg_expression('ipv6'), $ip))
0175                  {
0176                      // contains invalid data, don't use the forwarded for header
0177                      $this->forwarded_for = '';
0178                      break;
0179                  }
0180              }
0181          }
0182   
0183          // Add forum to the page for tracking online users - also adding a "x" to the end to properly identify the number
0184          $this->page['page'] .= (isset($_REQUEST['f'])) ? ((strpos($this->page['page'], '?') !== false) ? '&' : '?') . '_f_=' . (int) $_REQUEST['f'] . 'x' : '';
0185   
0186          if (isset($_COOKIE[$config['cookie_name'] . '_sid']) || isset($_COOKIE[$config['cookie_name'] . '_u']))
0187          {
0188              $this->cookie_data['u'] = request_var($config['cookie_name'] . '_u', 0, false, true);
0189              $this->cookie_data['k'] = request_var($config['cookie_name'] . '_k', '', false, true);
0190              $this->session_id         = request_var($config['cookie_name'] . '_sid', '', false, true);
0191   
0192              $SID = (defined('NEED_SID')) ? '?sid=' . $this->session_id : '?sid=';
0193              $_SID = (defined('NEED_SID')) ? $this->session_id : '';
0194   
0195              if (empty($this->session_id))
0196              {
0197                  $this->session_id = $_SID = request_var('sid', '');
0198                  $SID = '?sid=' . $this->session_id;
0199                  $this->cookie_data = array('u' => 0, 'k' => '');
0200              }
0201          }
0202          else
0203          {
0204              $this->session_id = $_SID = request_var('sid', '');
0205              $SID = '?sid=' . $this->session_id;
0206          }
0207   
0208          $_EXTRA_URL = array();
0209   
0210          // Why no forwarded_for et al? Well, too easily spoofed. With the results of my recent requests
0211          // it's pretty clear that in the majority of cases you'll at least be left with a proxy/cache ip.
0212          $this->ip = (!empty($_SERVER['REMOTE_ADDR'])) ? htmlspecialchars($_SERVER['REMOTE_ADDR']) : '';
0213          $this->load = false;
0214   
0215          // Load limit check (if applicable)
0216          if ($config['limit_load'] || $config['limit_search_load'])
0217          {
0218              if ($load = @file_get_contents('/proc/loadavg'))
0219              {
0220                  $this->load = array_slice(explode(' ', $load), 0, 1);
0221                  $this->load = floatval($this->load[0]);
0222              }
0223              else
0224              {
0225                  set_config('limit_load', '0');
0226                  set_config('limit_search_load', '0');
0227              }
0228          }
0229   
0230          // Is session_id is set or session_id is set and matches the url param if required
0231          if (!empty($this->session_id) && (!defined('NEED_SID') || (isset($_GET['sid']) && $this->session_id === $_GET['sid'])))
0232          {
0233              $sql = 'SELECT u.*, s.*
0234                  FROM ' . SESSIONS_TABLE . ' s, ' . USERS_TABLE . " u
0235                  WHERE s.session_id = '" . $db->sql_escape($this->session_id) . "'
0236                      AND u.user_id = s.session_user_id";
0237              $result = $db->sql_query($sql);
0238              $this->data = $db->sql_fetchrow($result);
0239              $db->sql_freeresult($result);
0240   
0241              // Did the session exist in the DB?
0242              if (isset($this->data['user_id']))
0243              {
0244                  // Validate IP length according to admin ... enforces an IP
0245                  // check on bots if admin requires this
0246  //                $quadcheck = ($config['ip_check_bot'] && $this->data['user_type'] & USER_BOT) ? 4 : $config['ip_check'];
0247   
0248                  if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
0249                  {
0250                      $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
0251                      $u_ip = short_ipv6($this->ip, $config['ip_check']);
0252                  }
0253                  else
0254                  {
0255                      $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
0256                      $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
0257                  }
0258   
0259                  $s_browser = ($config['browser_check']) ? strtolower(substr($this->data['session_browser'], 0, 149)) : '';
0260                  $u_browser = ($config['browser_check']) ? strtolower(substr($this->browser, 0, 149)) : '';
0261   
0262                  $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
0263                  $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
0264   
0265                  if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for)
0266                  {
0267                      $session_expired = false;
0268   
0269                      // Check whether the session is still valid if we have one
0270                      $method = basename(trim($config['auth_method']));
0271                      include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
0272   
0273                      $method = 'validate_session_' . $method;
0274                      if (function_exists($method))
0275                      {
0276                          if (!$method($this->data))
0277                          {
0278                              $session_expired = true;
0279                          }
0280                      }
0281   
0282                      if (!$session_expired)
0283                      {
0284                          // Check the session length timeframe if autologin is not enabled.
0285                          // Else check the autologin length... and also removing those having autologin enabled but no longer allowed board-wide.
0286                          if (!$this->data['session_autologin'])
0287                          {
0288                              if ($this->data['session_time'] < $this->time_now - ($config['session_length'] + 60))
0289                              {
0290                                  $session_expired = true;
0291                              }
0292                          }
0293                          else if (!$config['allow_autologin'] || ($config['max_autologin_time'] && $this->data['session_time'] < $this->time_now - (86400 * (int) $config['max_autologin_time']) + 60))
0294                          {
0295                              $session_expired = true;
0296                          }
0297                      }
0298   
0299                      if (!$session_expired)
0300                      {
0301                          // Only update session DB a minute or so after last update or if page changes
0302                          if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
0303                          {
0304                              $sql_ary = array('session_time' => $this->time_now);
0305   
0306                              if ($this->update_session_page)
0307                              {
0308                                  $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
0309                              }
0310   
0311                              $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
0312                                  WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
0313                              $db->sql_query($sql);
0314                          }
0315   
0316                          $this->data['is_registered'] = ($this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
0317                          $this->data['is_bot'] = (!$this->data['is_registered'] && $this->data['user_id'] != ANONYMOUS) ? true : false;
0318                          $this->data['user_lang'] = basename($this->data['user_lang']);
0319   
0320                          return true;
0321                      }
0322                  }
0323                  else
0324                  {
0325                      // Added logging temporarly to help debug bugs...
0326                      if (defined('DEBUG_EXTRA') && $this->data['user_id'] != ANONYMOUS)
0327                      {
0328                          add_log('critical', 'LOG_IP_BROWSER_FORWARDED_CHECK', $u_ip, $s_ip, $u_browser, $s_browser, htmlspecialchars($u_forwarded_for), htmlspecialchars($s_forwarded_for));
0329                      }
0330                  }
0331              }
0332          }
0333   
0334          // If we reach here then no (valid) session exists. So we'll create a new one
0335          return $this->session_create();
0336      }
0337   
0338      /**
0339      * Create a new session
0340      *
0341      * If upon trying to start a session we discover there is nothing existing we
0342      * jump here. Additionally this method is called directly during login to regenerate
0343      * the session for the specific user. In this method we carry out a number of tasks;
0344      * garbage collection, (search)bot checking, banned user comparison. Basically
0345      * though this method will result in a new session for a specific user.
0346      */
0347      function session_create($user_id = false, $set_admin = false, $persist_login = false, $viewonline = true)
0348      {
0349          global $SID, $_SID, $db, $config, $cache, $phpbb_root_path, $phpEx;
0350   
0351          $this->data = array();
0352   
0353          /* Garbage collection ... remove old sessions updating user information
0354          // if necessary. It means (potentially) 11 queries but only infrequently
0355          if ($this->time_now > $config['session_last_gc'] + $config['session_gc'])
0356          {
0357              $this->session_gc();
0358          }*/
0359   
0360          // Do we allow autologin on this board? No? Then override anything
0361          // that may be requested here
0362          if (!$config['allow_autologin'])
0363          {
0364              $this->cookie_data['k'] = $persist_login = false;
0365          }
0366   
0367          /**
0368          * Here we do a bot check, oh er saucy! No, not that kind of bot
0369          * check. We loop through the list of bots defined by the admin and
0370          * see if we have any useragent and/or IP matches. If we do, this is a
0371          * bot, act accordingly
0372          */
0373          $bot = false;
0374          $active_bots = $cache->obtain_bots();
0375   
0376          foreach ($active_bots as $row)
0377          {
0378              if ($row['bot_agent'] && preg_match('#' . str_replace('\*', '.*?', preg_quote($row['bot_agent'], '#')) . '#i', $this->browser))
0379              {
0380                  $bot = $row['user_id'];
0381              }
0382   
0383              // If ip is supplied, we will make sure the ip is matching too...
0384              if ($row['bot_ip'] && ($bot || !$row['bot_agent']))
0385              {
0386                  // Set bot to false, then we only have to set it to true if it is matching
0387                  $bot = false;
0388   
0389                  foreach (explode(',', $row['bot_ip']) as $bot_ip)
0390                  {
0391                      if (strpos($this->ip, $bot_ip) === 0)
0392                      {
0393                          $bot = (int) $row['user_id'];
0394                          break;
0395                      }
0396                  }
0397              }
0398   
0399              if ($bot)
0400              {
0401                  break;
0402              }
0403          }
0404   
0405          $method = basename(trim($config['auth_method']));
0406          include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
0407   
0408          $method = 'autologin_' . $method;
0409          if (function_exists($method))
0410          {
0411              $this->data = $method();
0412   
0413              if (sizeof($this->data))
0414              {
0415                  $this->cookie_data['k'] = '';
0416                  $this->cookie_data['u'] = $this->data['user_id'];
0417              }
0418          }
0419   
0420          // If we're presented with an autologin key we'll join against it.
0421          // Else if we've been passed a user_id we'll grab data based on that
0422          if (isset($this->cookie_data['k']) && $this->cookie_data['k'] && $this->cookie_data['u'] && !sizeof($this->data))
0423          {
0424              $sql = 'SELECT u.*
0425                  FROM ' . USERS_TABLE . ' u, ' . SESSIONS_KEYS_TABLE . ' k
0426                  WHERE u.user_id = ' . (int) $this->cookie_data['u'] . '
0427                      AND u.user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ")
0428                      AND k.user_id = u.user_id
0429                      AND k.key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
0430              $result = $db->sql_query($sql);
0431              $this->data = $db->sql_fetchrow($result);
0432              $db->sql_freeresult($result);
0433              $bot = false;
0434          }
0435          else if ($user_id !== false && !sizeof($this->data))
0436          {
0437              $this->cookie_data['k'] = '';
0438              $this->cookie_data['u'] = $user_id;
0439   
0440              $sql = 'SELECT *
0441                  FROM ' . USERS_TABLE . '
0442                  WHERE user_id = ' . (int) $this->cookie_data['u'] . '
0443                      AND user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')';
0444              $result = $db->sql_query($sql);
0445              $this->data = $db->sql_fetchrow($result);
0446              $db->sql_freeresult($result);
0447              $bot = false;
0448          }
0449   
0450          // If no data was returned one or more of the following occurred:
0451          // Key didn't match one in the DB
0452          // User does not exist
0453          // User is inactive
0454          // User is bot
0455          if (!sizeof($this->data) || !is_array($this->data))
0456          {
0457              $this->cookie_data['k'] = '';
0458              $this->cookie_data['u'] = ($bot) ? $bot : ANONYMOUS;
0459   
0460              if (!$bot)
0461              {
0462                  $sql = 'SELECT *
0463                      FROM ' . USERS_TABLE . '
0464                      WHERE user_id = ' . (int) $this->cookie_data['u'];
0465              }
0466              else
0467              {
0468                  // We give bots always the same session if it is not yet expired.
0469                  $sql = 'SELECT u.*, s.*
0470                      FROM ' . USERS_TABLE . ' u
0471                      LEFT JOIN ' . SESSIONS_TABLE . ' s ON (s.session_user_id = u.user_id)
0472                      WHERE u.user_id = ' . (int) $bot;
0473              }
0474   
0475              $result = $db->sql_query($sql);
0476              $this->data = $db->sql_fetchrow($result);
0477              $db->sql_freeresult($result);
0478          }
0479   
0480          if ($this->data['user_id'] != ANONYMOUS && !$bot)
0481          {
0482              $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());
0483          }
0484          else
0485          {
0486              $this->data['session_last_visit'] = $this->time_now;
0487          }
0488   
0489          // Force user id to be integer...
0490          $this->data['user_id'] = (int) $this->data['user_id'];
0491   
0492          // At this stage we should have a filled data array, defined cookie u and k data.
0493          // data array should contain recent session info if we're a real user and a recent
0494          // session exists in which case session_id will also be set
0495   
0496          // Is user banned? Are they excluded? Won't return on ban, exists within method
0497          if ($this->data['user_type'] != USER_FOUNDER)
0498          {
0499              if (!$config['forwarded_for_check'])
0500              {
0501                  $this->check_ban($this->data['user_id'], $this->ip);
0502              }
0503              else
0504              {
0505                  $ips = explode(', ', $this->forwarded_for);
0506                  $ips[] = $this->ip;
0507                  $this->check_ban($this->data['user_id'], $ips);
0508              }
0509          }
0510   
0511          $this->data['is_registered'] = (!$bot && $this->data['user_id'] != ANONYMOUS && ($this->data['user_type'] == USER_NORMAL || $this->data['user_type'] == USER_FOUNDER)) ? true : false;
0512          $this->data['is_bot'] = ($bot) ? true : false;
0513   
0514          // If our friend is a bot, we re-assign a previously assigned session
0515          if ($this->data['is_bot'] && $bot == $this->data['user_id'] && $this->data['session_id'])
0516          {
0517              // Only assign the current session if the ip, browser and forwarded_for match...
0518              if (strpos($this->ip, ':') !== false && strpos($this->data['session_ip'], ':') !== false)
0519              {
0520                  $s_ip = short_ipv6($this->data['session_ip'], $config['ip_check']);
0521                  $u_ip = short_ipv6($this->ip, $config['ip_check']);
0522              }
0523              else
0524              {
0525                  $s_ip = implode('.', array_slice(explode('.', $this->data['session_ip']), 0, $config['ip_check']));
0526                  $u_ip = implode('.', array_slice(explode('.', $this->ip), 0, $config['ip_check']));
0527              }
0528   
0529              $s_browser = ($config['browser_check']) ? strtolower(substr($this->data['session_browser'], 0, 149)) : '';
0530              $u_browser = ($config['browser_check']) ? strtolower(substr($this->browser, 0, 149)) : '';
0531   
0532              $s_forwarded_for = ($config['forwarded_for_check']) ? substr($this->data['session_forwarded_for'], 0, 254) : '';
0533              $u_forwarded_for = ($config['forwarded_for_check']) ? substr($this->forwarded_for, 0, 254) : '';
0534   
0535              if ($u_ip === $s_ip && $s_browser === $u_browser && $s_forwarded_for === $u_forwarded_for)
0536              {
0537                  $this->session_id = $this->data['session_id'];
0538   
0539                  // Only update session DB a minute or so after last update or if page changes
0540                  if ($this->time_now - $this->data['session_time'] > 60 || ($this->update_session_page && $this->data['session_page'] != $this->page['page']))
0541                  {
0542                      $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
0543   
0544                      $sql_ary = array('session_time' => $this->time_now, 'session_last_visit' => $this->time_now, 'session_admin' => 0);
0545   
0546                      if ($this->update_session_page)
0547                      {
0548                          $sql_ary['session_page'] = substr($this->page['page'], 0, 199);
0549                      }
0550   
0551                      $sql = 'UPDATE ' . SESSIONS_TABLE . ' SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
0552                          WHERE session_id = '" . $db->sql_escape($this->session_id) . "'";
0553                      $db->sql_query($sql);
0554   
0555                      // Update the last visit time
0556                      $sql = 'UPDATE ' . USERS_TABLE . '
0557                          SET user_lastvisit = ' . (int) $this->data['session_time'] . '
0558                          WHERE user_id = ' . (int) $this->data['user_id'];
0559                      $db->sql_query($sql);
0560                  }
0561   
0562                  $SID = '?sid=';
0563                  $_SID = '';
0564                  return true;
0565              }
0566              else
0567              {
0568                  // If the ip and browser does not match make sure we only have one bot assigned to one session
0569                  $db->sql_query('DELETE FROM ' . SESSIONS_TABLE . ' WHERE session_user_id = ' . $this->data['user_id']);
0570              }
0571          }
0572   
0573          $session_autologin = (($this->cookie_data['k'] || $persist_login) && $this->data['is_registered']) ? true : false;
0574          $set_admin = ($set_admin && $this->data['is_registered']) ? true : false;
0575   
0576          // Create or update the session
0577          $sql_ary = array(
0578              'session_user_id'        => (int) $this->data['user_id'],
0579              'session_start'            => (int) $this->time_now,
0580              'session_last_visit'    => (int) $this->data['session_last_visit'],
0581              'session_time'            => (int) $this->time_now,
0582              'session_browser'        => (string) substr($this->browser, 0, 149),
0583              'session_forwarded_for'    => (string) $this->forwarded_for,
0584              'session_ip'            => (string) $this->ip,
0585              'session_autologin'        => ($session_autologin) ? 1 : 0,
0586              'session_admin'            => ($set_admin) ? 1 : 0,
0587              'session_viewonline'    => ($viewonline) ? 1 : 0,
0588          );
0589   
0590          if ($this->update_session_page)
0591          {
0592              $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
0593          }
0594   
0595          $db->sql_return_on_error(true);
0596   
0597          $sql = 'DELETE
0598              FROM ' . SESSIONS_TABLE . '
0599              WHERE session_id = \'' . $db->sql_escape($this->session_id) . '\'
0600                  AND session_user_id = ' . ANONYMOUS;
0601   
0602          if (!defined('IN_ERROR_HANDLER') && (!$this->session_id || !$db->sql_query($sql) || !$db->sql_affectedrows()))
0603          {
0604              // Limit new sessions in 1 minute period (if required)
0605              if (empty($this->data['session_time']) && $config['active_sessions'])
0606              {
0607                  $sql = 'SELECT COUNT(session_id) AS sessions
0608                      FROM ' . SESSIONS_TABLE . '
0609                      WHERE session_time >= ' . ($this->time_now - 60);
0610                  $result = $db->sql_query($sql);
0611                  $row = $db->sql_fetchrow($result);
0612                  $db->sql_freeresult($result);
0613   
0614                  if ((int) $row['sessions'] > (int) $config['active_sessions'])
0615                  {
0616                      header('HTTP/1.1 503 Service Unavailable');
0617                      trigger_error('BOARD_UNAVAILABLE');
0618                  }
0619              }
0620          }
0621   
0622          $this->session_id = $this->data['session_id'] = md5(unique_id());
0623   
0624          $sql_ary['session_id'] = (string) $this->session_id;
0625          $sql_ary['session_page'] = (string) substr($this->page['page'], 0, 199);
0626   
0627          $sql = 'INSERT INTO ' . SESSIONS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
0628          $db->sql_query($sql);
0629   
0630          $db->sql_return_on_error(false);
0631   
0632          // Regenerate autologin/persistent login key
0633          if ($session_autologin)
0634          {
0635              $this->set_login_key();
0636          }
0637   
0638          // refresh data
0639          $SID = '?sid=' . $this->session_id;
0640          $_SID = $this->session_id;
0641          $this->data = array_merge($this->data, $sql_ary);
0642   
0643          if (!$bot)
0644          {
0645              $cookie_expire = $this->time_now + (($config['max_autologin_time']) ? 86400 * (int) $config['max_autologin_time'] : 31536000);
0646   
0647              $this->set_cookie('u', $this->cookie_data['u'], $cookie_expire);
0648              $this->set_cookie('k', $this->cookie_data['k'], $cookie_expire);
0649              $this->set_cookie('sid', $this->session_id, $cookie_expire);
0650   
0651              unset($cookie_expire);
0652              
0653              $sql = 'SELECT COUNT(session_id) AS sessions
0654                      FROM ' . SESSIONS_TABLE . '
0655                      WHERE session_user_id = ' . (int) $this->data['user_id'] . '
0656                      AND session_time >= ' . ($this->time_now - $config['form_token_lifetime']);
0657              $result = $db->sql_query($sql);
0658              $row = $db->sql_fetchrow($result);
0659              $db->sql_freeresult($result);
0660   
0661              if ((int) $row['sessions'] <= 1 || empty($this->data['user_form_salt']))
0662              {
0663                  $this->data['user_form_salt'] = unique_id();
0664                  // Update the form key
0665                  $sql = 'UPDATE ' . USERS_TABLE . '
0666                      SET user_form_salt = \'' . $db->sql_escape($this->data['user_form_salt']) . '\'
0667                      WHERE user_id = ' . (int) $this->data['user_id'];
0668                  $db->sql_query($sql);
0669              }
0670          }
0671          else
0672          {
0673              $this->data['session_time'] = $this->data['session_last_visit'] = $this->time_now;
0674   
0675              // Update the last visit time
0676              $sql = 'UPDATE ' . USERS_TABLE . '
0677                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
0678                  WHERE user_id = ' . (int) $this->data['user_id'];
0679              $db->sql_query($sql);
0680   
0681              $SID = '?sid=';
0682              $_SID = '';
0683          }
0684   
0685          return true;
0686      }
0687   
0688      /**
0689      * Kills a session
0690      *
0691      * This method does what it says on the tin. It will delete a pre-existing session.
0692      * It resets cookie information (destroying any autologin key within that cookie data)
0693      * and update the users information from the relevant session data. It will then
0694      * grab guest user information.
0695      */
0696      function session_kill($new_session = true)
0697      {
0698          global $SID, $_SID, $db, $config, $phpbb_root_path, $phpEx;
0699   
0700          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
0701              WHERE session_id = '" . $db->sql_escape($this->session_id) . "'
0702                  AND session_user_id = " . (int) $this->data['user_id'];
0703          $db->sql_query($sql);
0704   
0705          // Allow connecting logout with external auth method logout
0706          $method = basename(trim($config['auth_method']));
0707          include_once($phpbb_root_path . 'includes/auth/auth_' . $method . '.' . $phpEx);
0708   
0709          $method = 'logout_' . $method;
0710          if (function_exists($method))
0711          {
0712              $method($this->data, $new_session);
0713          }
0714   
0715          if ($this->data['user_id'] != ANONYMOUS)
0716          {
0717              // Delete existing session, update last visit info first!
0718              if (!isset($this->data['session_time']))
0719              {
0720                  $this->data['session_time'] = time();
0721              }
0722   
0723              $sql = 'UPDATE ' . USERS_TABLE . '
0724                  SET user_lastvisit = ' . (int) $this->data['session_time'] . '
0725                  WHERE user_id = ' . (int) $this->data['user_id'];
0726              $db->sql_query($sql);
0727   
0728              if ($this->cookie_data['k'])
0729              {
0730                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
0731                      WHERE user_id = ' . (int) $this->data['user_id'] . "
0732                          AND key_id = '" . $db->sql_escape(md5($this->cookie_data['k'])) . "'";
0733                  $db->sql_query($sql);
0734              }
0735   
0736              // Reset the data array
0737              $this->data = array();
0738   
0739              $sql = 'SELECT *
0740                  FROM ' . USERS_TABLE . '
0741                  WHERE user_id = ' . ANONYMOUS;
0742              $result = $db->sql_query($sql);
0743              $this->data = $db->sql_fetchrow($result);
0744              $db->sql_freeresult($result);
0745          }
0746   
0747          $cookie_expire = $this->time_now - 31536000;
0748          $this->set_cookie('u', '', $cookie_expire);
0749          $this->set_cookie('k', '', $cookie_expire);
0750          $this->set_cookie('sid', '', $cookie_expire);
0751          unset($cookie_expire);
0752   
0753          $SID = '?sid=';
0754          $this->session_id = $_SID = '';
0755   
0756          // To make sure a valid session is created we create one for the anonymous user
0757          if ($new_session)
0758          {
0759              $this->session_create(ANONYMOUS);
0760          }
0761   
0762          return true;
0763      }
0764   
0765      /**
0766      * Session garbage collection
0767      *
0768      * This looks a lot more complex than it really is. Effectively we are
0769      * deleting any sessions older than an admin definable limit. Due to the
0770      * way in which we maintain session data we have to ensure we update user
0771      * data before those sessions are destroyed. In addition this method
0772      * removes autologin key information that is older than an admin defined
0773      * limit.
0774      */
0775      function session_gc()
0776      {
0777          global $db, $config;
0778   
0779          $batch_size = 10;
0780          
0781          if (!$this->time_now)
0782          {
0783              $this->time_now = time();
0784          }
0785   
0786          // Firstly, delete guest sessions
0787          $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
0788              WHERE session_user_id = ' . ANONYMOUS . '
0789                  AND session_time < ' . (int) ($this->time_now - $config['session_length']);
0790          $db->sql_query($sql);
0791   
0792          // Get expired sessions, only most recent for each user
0793          $sql = 'SELECT session_user_id, session_page, MAX(session_time) AS recent_time
0794              FROM ' . SESSIONS_TABLE . '
0795              WHERE session_time < ' . ($this->time_now - $config['session_length']) . '
0796              GROUP BY session_user_id, session_page';
0797          $result = $db->sql_query_limit($sql, $batch_size);
0798   
0799          $del_user_id = array();
0800          $del_sessions = 0;
0801   
0802          while ($row = $db->sql_fetchrow($result))
0803          {
0804              $sql = 'UPDATE ' . USERS_TABLE . '
0805                  SET user_lastvisit = ' . (int) $row['recent_time'] . ", user_lastpage = '" . $db->sql_escape($row['session_page']) . "'
0806                  WHERE user_id = " . (int) $row['session_user_id'];
0807              $db->sql_query($sql);
0808   
0809              $del_user_id[] = (int) $row['session_user_id'];
0810              $del_sessions++;
0811          }
0812          $db->sql_freeresult($result);
0813   
0814          if (sizeof($del_user_id))
0815          {
0816              // Delete expired sessions
0817              $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
0818                  WHERE ' . $db->sql_in_set('session_user_id', $del_user_id) . '
0819                      AND session_time < ' . ($this->time_now - $config['session_length']);
0820              $db->sql_query($sql);
0821          }
0822   
0823          if ($del_sessions < $batch_size)
0824          {
0825              // Less than 10 users, update gc timer ... else we want gc
0826              // called again to delete other sessions
0827              set_config('session_last_gc', $this->time_now, true);
0828              
0829              if ($config['max_autologin_time'])
0830              {
0831                  $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
0832                      WHERE last_login < ' . (time() - (86400 * (int) $config['max_autologin_time']));
0833                  $db->sql_query($sql);
0834              }
0835              $this->confirm_gc();
0836          }
0837          
0838          return;
0839      }
0840      
0841      function confirm_gc($type = 0)
0842      {
0843          global $db, $config;
0844          
0845          $sql = 'SELECT DISTINCT c.session_id
0846                  FROM ' . CONFIRM_TABLE . ' c
0847                  LEFT JOIN ' . SESSIONS_TABLE . ' s ON (c.session_id = s.session_id)
0848                  WHERE s.session_id IS NULL' .
0849                      ((empty($type)) ? '' : ' AND c.confirm_type = ' . (int) $type);
0850          $result = $db->sql_query($sql);
0851   
0852          if ($row = $db->sql_fetchrow($result))
0853          {
0854              $sql_in = array();
0855              do
0856              {
0857                  $sql_in[] = (string) $row['session_id'];
0858              }
0859              while ($row = $db->sql_fetchrow($result));
0860   
0861              if (sizeof($sql_in))
0862              {
0863                  $sql = 'DELETE FROM ' . CONFIRM_TABLE . '
0864                      WHERE ' . $db->sql_in_set('session_id', $sql_in);
0865                  $db->sql_query($sql);
0866              }
0867          }
0868          $db->sql_freeresult($result);
0869      }
0870      
0871      
0872      /**
0873      * Sets a cookie
0874      *
0875      * Sets a cookie of the given name with the specified data for the given length of time.
0876      */
0877      function set_cookie($name, $cookiedata, $cookietime)
0878      {
0879          global $config;
0880   
0881          $name_data = rawurlencode($config['cookie_name'] . '_' . $name) . '=' . rawurlencode($cookiedata);
0882          $expire = gmdate('D, d-M-Y H:i:s \\G\\M\\T', $cookietime);
0883          $domain = (!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain'];
0884   
0885          header('Set-Cookie: ' . $name_data . '; expires=' . $expire . '; path=' . $config['cookie_path'] . $domain . ((!$config['cookie_secure']) ? '' : '; secure') . '; HttpOnly', false);
0886      }
0887   
0888      /**
0889      * Check for banned user
0890      *
0891      * Checks whether the supplied user is banned by id, ip or email. If no parameters
0892      * are passed to the method pre-existing session data is used. If $return is false
0893      * this routine does not return on finding a banned user, it outputs a relevant
0894      * message and stops execution.
0895      *
0896      * @param string|array    $user_ips    Can contain a string with one IP or an array of multiple IPs
0897      */
0898      function check_ban($user_id = false, $user_ips = false, $user_email = false, $return = false)
0899      {
0900          global $config, $db;
0901   
0902          if (defined('IN_CHECK_BAN'))
0903          {
0904              return;
0905          }
0906   
0907          $banned = false;
0908          $cache_ttl = 3600;
0909          $where_sql = array();
0910   
0911          $sql = 'SELECT ban_ip, ban_userid, ban_email, ban_exclude, ban_give_reason, ban_end
0912              FROM ' . BANLIST_TABLE . '
0913              WHERE ';
0914   
0915          // Determine which entries to check, only return those
0916          if ($user_email === false)
0917          {
0918              $where_sql[] = "ban_email = ''";
0919          }
0920   
0921          if ($user_ips === false)
0922          {
0923              $where_sql[] = "(ban_ip = '' OR ban_exclude = 1)";
0924          }
0925   
0926          if ($user_id === false)
0927          {
0928              $where_sql[] = '(ban_userid = 0 OR ban_exclude = 1)';
0929          }
0930          else
0931          {
0932              $cache_ttl = ($user_id == ANONYMOUS) ? 3600 : 0;
0933              $_sql = '(ban_userid = ' . $user_id;
0934   
0935              if ($user_email !== false)
0936              {
0937                  $_sql .= " OR ban_email <> ''";
0938              }
0939   
0940              if ($user_ips !== false)
0941              {
0942                  $_sql .= " OR ban_ip <> ''";
0943              }
0944   
0945              $_sql .= ')';
0946   
0947              $where_sql[] = $_sql;
0948          }
0949   
0950          $sql .= (sizeof($where_sql)) ? implode(' AND ', $where_sql) : '';
0951          $result = $db->sql_query($sql, $cache_ttl);
0952   
0953          $ban_triggered_by = 'user';
0954          while ($row = $db->sql_fetchrow($result))
0955          {
0956              if ($row['ban_end'] && $row['ban_end'] < time())
0957              {
0958                  continue;
0959              }
0960   
0961              $ip_banned = false;
0962              if (!empty($row['ban_ip']))
0963              {
0964                  if (!is_array($user_ips))
0965                  {
0966                      $ip_banned = preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ips);
0967                  }
0968                  else
0969                  {
0970                      foreach ($user_ips as $user_ip)
0971                      {
0972                          if (preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_ip'], '#')) . '$#i', $user_ip))
0973                          {
0974                              $ip_banned = true;
0975                              break;
0976                          }
0977                      }
0978                  }
0979              }
0980   
0981              if ((!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id) ||
0982                  $ip_banned ||
0983                  (!empty($row['ban_email']) && preg_match('#^' . str_replace('\*', '.*?', preg_quote($row['ban_email'], '#')) . '$#i', $user_email)))
0984              {
0985                  if (!empty($row['ban_exclude']))
0986                  {
0987                      $banned = false;
0988                      break;
0989                  }
0990                  else
0991                  {
0992                      $banned = true;
0993                      $ban_row = $row;
0994   
0995                      if (!empty($row['ban_userid']) && intval($row['ban_userid']) == $user_id)
0996                      {
0997                          $ban_triggered_by = 'user';
0998                      }
0999                      else if ($ip_banned)
1000                      {
1001                          $ban_triggered_by = 'ip';
1002                      }
1003                      else
1004                      {
1005                          $ban_triggered_by = 'email';
1006                      }
1007   
1008                      // Don't break. Check if there is an exclude rule for this user
1009                  }
1010              }
1011          }
1012          $db->sql_freeresult($result);
1013   
1014          if ($banned && !$return)
1015          {
1016              global $template;
1017   
1018              // If the session is empty we need to create a valid one...
1019              if (empty($this->session_id))
1020              {
1021                  // This seems to be no longer needed? - #14971
1022  //                $this->session_create(ANONYMOUS);
1023              }
1024   
1025              // Initiate environment ... since it won't be set at this stage
1026              $this->setup();
1027   
1028              // Logout the user, banned users are unable to use the normal 'logout' link
1029              if ($this->data['user_id'] != ANONYMOUS)
1030              {
1031                  $this->session_kill();
1032              }
1033   
1034              // We show a login box here to allow founders accessing the board if banned by IP
1035              if (defined('IN_LOGIN') && $this->data['user_id'] == ANONYMOUS)
1036              {
1037                  global $phpEx;
1038   
1039                  $this->setup('ucp');
1040                  $this->data['is_registered'] = $this->data['is_bot'] = false;
1041   
1042                  // Set as a precaution to allow login_box() handling this case correctly as well as this function not being executed again.
1043                  define('IN_CHECK_BAN', 1);
1044   
1045                  login_box("index.$phpEx");
1046   
1047                  // The false here is needed, else the user is able to circumvent the ban.
1048                  $this->session_kill(false);
1049              }
1050   
1051              // Ok, we catch the case of an empty session id for the anonymous user...
1052              // This can happen if the user is logging in, banned by username and the login_box() being called "again".
1053              if (empty($this->session_id) && defined('IN_CHECK_BAN'))
1054              {
1055                  $this->session_create(ANONYMOUS);
1056              }
1057   
1058   
1059              // Determine which message to output
1060              $till_date = ($ban_row['ban_end']) ? $this->format_date($ban_row['ban_end']) : '';
1061              $message = ($ban_row['ban_end']) ? 'BOARD_BAN_TIME' : 'BOARD_BAN_PERM';
1062   
1063              $message = sprintf($this->lang[$message], $till_date, '<a href="mailto:' . $config['board_contact'] . '">', '</a>');
1064              $message .= ($ban_row['ban_give_reason']) ? '<br /><br />' . sprintf($this->lang['BOARD_BAN_REASON'], $ban_row['ban_give_reason']) : '';
1065              $message .= '<br /><br /><em>' . $this->lang['BAN_TRIGGERED_BY_' . strtoupper($ban_triggered_by)] . '</em>';
1066   
1067              // To circumvent session_begin returning a valid value and the check_ban() not called on second page view, we kill the session again
1068              $this->session_kill(false);
1069   
1070              trigger_error($message);
1071          }
1072   
1073          return ($banned) ? true : false;
1074      }
1075   
1076      /**
1077      * Check if ip is blacklisted
1078      * This should be called only where absolutly necessary
1079      *
1080      * Only IPv4 (rbldns does not support AAAA records/IPv6 lookups)
1081      *
1082      * @author satmd (from the php manual)
1083      * @param string $mode register/post - spamcop for example is ommitted for posting
1084      * @return false if ip is not blacklisted, else an array([checked server], [lookup])
1085      */
1086      function check_dnsbl($mode, $ip = false)
1087      {
1088          if ($ip === false)
1089          {
1090              $ip = $this->ip;
1091          }
1092   
1093          $dnsbl_check = array(
1094              'list.dsbl.org'            => 'http://dsbl.org/listing?',
1095              'sbl-xbl.spamhaus.org'    => 'http://www.spamhaus.org/query/bl?ip=',
1096          );
1097   
1098          if ($mode == 'register')
1099          {
1100              $dnsbl_check['bl.spamcop.net'] = 'http://spamcop.net/bl.shtml?';
1101          }
1102   
1103          if ($ip)
1104          {
1105              $quads = explode('.', $ip);
1106              $reverse_ip = $quads[3] . '.' . $quads[2] . '.' . $quads[1] . '.' . $quads[0];
1107   
1108              // Need to be listed on all servers...
1109              $listed = true;
1110              $info = array();
1111   
1112              foreach ($dnsbl_check as $dnsbl => $lookup)
1113              {
1114                  if (phpbb_checkdnsrr($reverse_ip . '.' . $dnsbl . '.', 'A') === true)
1115                  {
1116                      $info = array($dnsbl, $lookup . $ip);
1117                  }
1118                  else
1119                  {
1120                      $listed = false;
1121                  }
1122              }
1123   
1124              if ($listed)
1125              {
1126                  return $info;
1127              }
1128          }
1129   
1130          return false;
1131      }
1132   
1133      /**
1134      * Check if URI is blacklisted
1135      * This should be called only where absolutly necessary, for example on the submitted website field
1136      * This function is not in use at the moment and is only included for testing purposes, it may not work at all!
1137      * This means it is untested at the moment and therefore commented out
1138      *
1139      * @param string $uri URI to check
1140      * @return true if uri is on blacklist, else false. Only blacklist is checked (~zero FP), no grey lists
1141      function check_uribl($uri)
1142      {
1143          // Normally parse_url() is not intended to parse uris
1144          // We need to get the top-level domain name anyway... change.
1145          $uri = parse_url($uri);
1146   
1147          if ($uri === false || empty($uri['host']))
1148          {
1149              return false;
1150          }
1151   
1152          $uri = trim($uri['host']);
1153   
1154          if ($uri)
1155          {
1156              // One problem here... the return parameter for the "windows" method is different from what
1157              // we expect... this may render this check useless...
1158              if (phpbb_checkdnsrr($uri . '.multi.uribl.com.', 'A') === true)
1159              {
1160                  return true;
1161              }
1162          }
1163   
1164          return false;
1165      }
1166      */
1167   
1168      /**
1169      * Set/Update a persistent login key
1170      *
1171      * This method creates or updates a persistent session key. When a user makes
1172      * use of persistent (formerly auto-) logins a key is generated and stored in the
1173      * DB. When they revisit with the same key it's automatically updated in both the
1174      * DB and cookie. Multiple keys may exist for each user representing different
1175      * browsers or locations. As with _any_ non-secure-socket no passphrase login this
1176      * remains vulnerable to exploit.
1177      */
1178      function set_login_key($user_id = false, $key = false, $user_ip = false)
1179      {
1180          global $config, $db;
1181   
1182          $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
1183          $user_ip = ($user_ip === false) ? $this->ip : $user_ip;
1184          $key = ($key === false) ? (($this->cookie_data['k']) ? $this->cookie_data['k'] : false) : $key;
1185   
1186          $key_id = unique_id(hexdec(substr($this->session_id, 0, 8)));
1187   
1188          $sql_ary = array(
1189              'key_id'        => (string) md5($key_id),
1190              'last_ip'        => (string) $this->ip,
1191              'last_login'    => (int) time()
1192          );
1193   
1194          if (!$key)
1195          {
1196              $sql_ary += array(
1197                  'user_id'    => (int) $user_id
1198              );
1199          }
1200   
1201          if ($key)
1202          {
1203              $sql = 'UPDATE ' . SESSIONS_KEYS_TABLE . '
1204                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1205                  WHERE user_id = ' . (int) $user_id . "
1206                      AND key_id = '" . $db->sql_escape(md5($key)) . "'";
1207          }
1208          else
1209          {
1210              $sql = 'INSERT INTO ' . SESSIONS_KEYS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
1211          }
1212          $db->sql_query($sql);
1213   
1214          $this->cookie_data['k'] = $key_id;
1215   
1216          return false;
1217      }
1218   
1219      /**
1220      * Reset all login keys for the specified user
1221      *
1222      * This method removes all current login keys for a specified (or the current)
1223      * user. It will be called on password change to render old keys unusable
1224      */
1225      function reset_login_keys($user_id = false)
1226      {
1227          global $config, $db;
1228   
1229          $user_id = ($user_id === false) ? $this->data['user_id'] : $user_id;
1230   
1231          $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . '
1232              WHERE user_id = ' . (int) $user_id;
1233          $db->sql_query($sql);
1234   
1235          // Let's also clear any current sessions for the specified user_id
1236          // If it's the current user then we'll leave this session intact
1237          $sql_where = 'session_user_id = ' . (int) $user_id;
1238          $sql_where .= ($user_id === $this->data['user_id']) ? " AND session_id <> '" . $db->sql_escape($this->session_id) . "'" : '';
1239   
1240          $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1241              WHERE $sql_where";
1242          $db->sql_query($sql);
1243   
1244          // We're changing the password of the current user and they have a key
1245          // Lets regenerate it to be safe
1246          if ($user_id === $this->data['user_id'] && $this->cookie_data['k'])
1247          {
1248              $this->set_login_key($user_id);
1249          }
1250      }
1251  }
1252   
1253   
1254  /**
1255  * Base user class
1256  *
1257  * This is the overarching class which contains (through session extend)
1258  * all methods utilised for user functionality during a session.
1259  *
1260  * @package phpBB3
1261  */
1262  class user extends session
1263  {
1264      var $lang = array();
1265      var $help = array();
1266      var $theme = array();
1267      var $date_format;
1268      var $timezone;
1269      var $dst;
1270   
1271      var $lang_name;
1272      var $lang_id = false;
1273      var $lang_path;
1274      var $img_lang;
1275      var $img_array = array();
1276   
1277      // Able to add new option (id 7)
1278      var $keyoptions = array('viewimg' => 0, 'viewflash' => 1, 'viewsmilies' => 2, 'viewsigs' => 3, 'viewavatars' => 4, 'viewcensors' => 5, 'attachsig' => 6, 'bbcode' => 8, 'smilies' => 9, 'popuppm' => 10);
1279      var $keyvalues = array();
1280   
1281      /**
1282      * Setup basic user-specific items (style, language, ...)
1283      */
1284      function setup($lang_set = false, $style = false)
1285      {
1286          global $db, $template, $config, $auth, $phpEx, $phpbb_root_path, $cache;
1287   
1288          if ($this->data['user_id'] != ANONYMOUS)
1289          {
1290              $this->lang_name = (file_exists($phpbb_root_path . 'language/' . $this->data['user_lang'] . "/common.$phpEx")) ? $this->data['user_lang'] : basename($config['default_lang']);
1291              $this->lang_path = $phpbb_root_path . 'language/' . $this->lang_name . '/';
1292   
1293              $this->date_format = $this->data['user_dateformat'];
1294              $this->timezone = $this->data['user_timezone'] * 3600;
1295              $this->dst = $this->data['user_dst'] * 3600;
1296          }
1297          else
1298          {
1299              $this->lang_name = basename($config['default_lang']);
1300              $this->lang_path = $phpbb_root_path . 'language/' . $this->lang_name . '/';
1301              $this->date_format = $config['default_dateformat'];
1302              $this->timezone = $config['board_timezone'] * 3600;
1303              $this->dst = $config['board_dst'] * 3600;
1304   
1305              /**
1306              * If a guest user is surfing, we try to guess his/her language first by obtaining the browser language
1307              * If re-enabled we need to make sure only those languages installed are checked
1308              * Commented out so we do not loose the code.
1309   
1310              if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
1311              {
1312                  $accept_lang_ary = explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
1313   
1314                  foreach ($accept_lang_ary as $accept_lang)
1315                  {
1316                      // Set correct format ... guess full xx_YY form
1317                      $accept_lang = substr($accept_lang, 0, 2) . '_' . strtoupper(substr($accept_lang, 3, 2));
1318                      $accept_lang = basename($accept_lang);
1319   
1320                      if (file_exists($phpbb_root_path . 'language/' . $accept_lang . "/common.$phpEx"))
1321                      {
1322                          $this->lang_name = $config['default_lang'] = $accept_lang;
1323                          $this->lang_path = $phpbb_root_path . 'language/' . $accept_lang . '/';
1324                          break;
1325                      }
1326                      else
1327                      {
1328                          // No match on xx_YY so try xx
1329                          $accept_lang = substr($accept_lang, 0, 2);
1330                          $accept_lang = basename($accept_lang);
1331   
1332                          if (file_exists($phpbb_root_path . 'language/' . $accept_lang . "/common.$phpEx"))
1333                          {
1334                              $this->lang_name = $config['default_lang'] = $accept_lang;
1335                              $this->lang_path = $phpbb_root_path . 'language/' . $accept_lang . '/';
1336                              break;
1337                          }
1338                      }
1339                  }
1340              }
1341              */
1342          }
1343   
1344          // We include common language file here to not load it every time a custom language file is included
1345          $lang = &$this->lang;
1346   
1347          if ((@include $this->lang_path . "common.$phpEx") === false)
1348          {
1349              die('Language file ' . $this->lang_name . "/common.$phpEx" . " couldn't be opened.");
1350          }
1351   
1352          $this->add_lang($lang_set);
1353          unset($lang_set);
1354   
1355          if (!empty($_GET['style']) && $auth->acl_get('a_styles'))
1356          {
1357              global $SID, $_EXTRA_URL;
1358   
1359              $style = request_var('style', 0);
1360              $SID .= '&amp;style=' . $style;
1361              $_EXTRA_URL = array('style=' . $style);
1362          }
1363          else
1364          {
1365              // Set up style
1366              $style = ($style) ? $style : ((!$config['override_user_style']) ? $this->data['user_style'] : $config['default_style']);
1367          }
1368   
1369          $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
1370              FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1371              WHERE s.style_id = $style
1372                  AND t.template_id = s.template_id
1373                  AND c.theme_id = s.theme_id
1374                  AND i.imageset_id = s.imageset_id";
1375          $result = $db->sql_query($sql, 3600);
1376          $this->theme = $db->sql_fetchrow($result);
1377          $db->sql_freeresult($result);
1378   
1379          // User has wrong style
1380          if (!$this->theme && $style == $this->data['user_style'])
1381          {
1382              $style = $this->data['user_style'] = $config['default_style'];
1383   
1384              $sql = 'UPDATE ' . USERS_TABLE . "
1385                  SET user_style = $style
1386                  WHERE user_id = {$this->data['user_id']}";
1387              $db->sql_query($sql);
1388   
1389              $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
1390                  FROM ' . STYLES_TABLE . ' s, ' . STYLES_TEMPLATE_TABLE . ' t, ' . STYLES_THEME_TABLE . ' c, ' . STYLES_IMAGESET_TABLE . " i
1391                  WHERE s.style_id = $style
1392                      AND t.template_id = s.template_id
1393                      AND c.theme_id = s.theme_id
1394                      AND i.imageset_id = s.imageset_id";
1395              $result = $db->sql_query($sql, 3600);
1396              $this->theme = $db->sql_fetchrow($result);
1397              $db->sql_freeresult($result);
1398          }
1399   
1400          if (!$this->theme)
1401          {
1402              trigger_error('Could not get style data', E_USER_ERROR);
1403          }
1404   
1405          // Now parse the cfg file and cache it
1406          $parsed_items = $cache->obtain_cfg_items($this->theme);
1407   
1408          // We are only interested in the theme configuration for now
1409          $parsed_items = $parsed_items['theme'];
1410   
1411          $check_for = array(
1412              'parse_css_file'    => (int) 0,
1413              'pagination_sep'    => (string) ', '
1414          );
1415   
1416          foreach ($check_for as $key => $default_value)
1417          {
1418              $this->theme[$key] = (isset($parsed_items[$key])) ? $parsed_items[$key] : $default_value;
1419              settype($this->theme[$key], gettype($default_value));
1420   
1421              if (is_string($default_value))
1422              {
1423                  $this->theme[$key] = htmlspecialchars($this->theme[$key]);
1424              }
1425          }
1426   
1427          // If the style author specified the theme needs to be cached
1428          // (because of the used paths and variables) than make sure it is the case.
1429          // For example, if the theme uses language-specific images it needs to be stored in db.
1430          if (!$this->theme['theme_storedb'] && $this->theme['parse_css_file'])
1431          {
1432              $this->theme['theme_storedb'] = 1;
1433   
1434              $stylesheet = file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/stylesheet.css");
1435              // Match CSS imports
1436              $matches = array();
1437              preg_match_all('/@import url\(["\'](.*)["\']\);/i', $stylesheet, $matches);
1438   
1439              if (sizeof($matches))
1440              {
1441                  $content = '';
1442                  foreach ($matches[0] as $idx => $match)
1443                  {
1444                      if ($content = @file_get_contents("{$phpbb_root_path}styles/{$this->theme['theme_path']}/theme/" . $matches[1][$idx]))
1445                      {
1446                          $content = trim($content);
1447                      }
1448                      else
1449                      {
1450                          $content = '';
1451                      }
1452                      $stylesheet = str_replace($match, $content, $stylesheet);
1453                  }
1454                  unset($content);
1455              }
1456   
1457              $stylesheet = str_replace('./', 'styles/' . $this->theme['theme_path'] . '/theme/', $stylesheet);
1458   
1459              $sql_ary = array(
1460                  'theme_data'    => $stylesheet,
1461                  'theme_mtime'    => time(),
1462                  'theme_storedb'    => 1
1463              );
1464   
1465              $sql = 'UPDATE ' . STYLES_THEME_TABLE . '
1466                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
1467                  WHERE theme_id = ' . $this->theme['theme_id'];
1468              $db->sql_query($sql);
1469   
1470              unset($sql_ary);
1471          }
1472   
1473          $template->set_template();
1474   
1475          $this->img_lang = (file_exists($phpbb_root_path . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . $this->lang_name)) ? $this->lang_name : $config['default_lang'];
1476   
1477          $sql = 'SELECT image_name, image_filename, image_lang, image_height, image_width
1478              FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1479              WHERE imageset_id = ' . $this->theme['imageset_id'] . "
1480              AND image_lang IN ('" . $db->sql_escape($this->img_lang) . "', '')";
1481          $result = $db->sql_query($sql, 3600);
1482   
1483          $localised_images = false;
1484          while ($row = $db->sql_fetchrow($result))
1485          {
1486              if ($row['image_lang'])
1487              {
1488                  $localised_images = true;
1489              }
1490   
1491              $this->img_array[$row['image_name']] = $row;
1492          }
1493          $db->sql_freeresult($result);
1494   
1495          // there were no localised images, try to refresh the localised imageset for the user's language
1496          if (!$localised_images)
1497          {
1498              // Attention: this code ignores the image definition list from acp_styles and just takes everything
1499              // that the config file contains
1500              $sql_ary = array();
1501   
1502              $db->sql_transaction('begin');
1503   
1504              $sql = 'DELETE FROM ' . STYLES_IMAGESET_DATA_TABLE . '
1505                  WHERE imageset_id = ' . $this->theme['imageset_id'] . '
1506                      AND image_lang = \'' . $db->sql_escape($this->img_lang) . '\'';
1507              $result = $db->sql_query($sql);
1508   
1509              if (@file_exists("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg"))
1510              {
1511                  $cfg_data_imageset_data = parse_cfg_file("{$phpbb_root_path}styles/{$this->theme['imageset_path']}/imageset/{$this->img_lang}/imageset.cfg");
1512                  foreach ($cfg_data_imageset_data as $image_name => $value)
1513                  {
1514                      if (strpos($value, '*') !== false)
1515                      {
1516                          if (substr($value, -1, 1) === '*')
1517                          {
1518                              list($image_filename, $image_height) = explode('*', $value);
1519                              $image_width = 0;
1520                          }
1521                          else
1522                          {
1523                              list($image_filename, $image_height, $image_width) = explode('*', $value);
1524                          }
1525                      }
1526                      else
1527                      {
1528                          $image_filename = $value;
1529                          $image_height = $image_width = 0;
1530                      }
1531   
1532                      if (strpos($image_name, 'img_') === 0 && $image_filename)
1533                      {
1534                          $image_name = substr($image_name, 4);
1535                          $sql_ary[] = array(
1536                              'image_name'        => (string) $image_name,
1537                              'image_filename'    => (string) $image_filename,
1538                              'image_height'        => (int) $image_height,
1539                              'image_width'        => (int) $image_width,
1540                              'imageset_id'        => (int) $this->theme['imageset_id'],
1541                              'image_lang'        => (string) $this->img_lang,
1542                          );
1543                      }
1544                  }
1545              }
1546   
1547              if (sizeof($sql_ary))
1548              {
1549                  $db->sql_multi_insert(STYLES_IMAGESET_DATA_TABLE, $sql_ary);
1550                  $db->sql_transaction('commit');
1551                  $cache->destroy('sql', STYLES_IMAGESET_DATA_TABLE);
1552   
1553                  add_log('admin', 'LOG_IMAGESET_LANG_REFRESHED', $this->theme['imageset_name'], $this->img_lang);
1554              }
1555              else
1556              {
1557                  $db->sql_transaction('commit');
1558                  add_log('admin', 'LOG_IMAGESET_LANG_MISSING', $this->theme['imageset_name'], $this->img_lang);
1559              }
1560          }
1561   
1562          // Call phpbb_user_session_handler() in case external application want to "bend" some variables or replace classes...
1563          // After calling it we continue script execution...
1564          phpbb_user_session_handler();
1565   
1566          // If this function got called from the error handler we are finished here.
1567          if (defined('IN_ERROR_HANDLER'))
1568          {
1569              return;
1570          }
1571   
1572          // Disable board if the install/ directory is still present
1573          // For the brave development army we do not care about this, else we need to comment out this everytime we develop locally
1574          if (!defined('DEBUG_EXTRA') && !defined('ADMIN_START') && !defined('IN_INSTALL') && !defined('IN_LOGIN') && file_exists($phpbb_root_path . 'install'))
1575          {
1576              // Adjust the message slightly according to the permissions
1577              if ($auth->acl_gets('a_', 'm_') || $auth->acl_getf_global('m_'))
1578              {
1579                  $message = 'REMOVE_INSTALL';
1580              }
1581              else
1582              {
1583                  $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1584              }
1585              trigger_error($message);
1586          }
1587   
1588          // Is board disabled and user not an admin or moderator?
1589          if ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1590          {
1591              header('HTTP/1.1 503 Service Unavailable');
1592   
1593              $message = (!empty($config['board_disable_msg'])) ? $config['board_disable_msg'] : 'BOARD_DISABLE';
1594              trigger_error($message);
1595          }
1596   
1597          // Is load exceeded?
1598          if ($config['limit_load'] && $this->load !== false)
1599          {
1600              if ($this->load > floatval($config['limit_load']) && !defined('IN_LOGIN'))
1601              {
1602                  // Set board disabled to true to let the admins/mods get the proper notification
1603                  $config['board_disable'] = '1';
1604   
1605                  if (!$auth->acl_gets('a_', 'm_') && !$auth->acl_getf_global('m_'))
1606                  {
1607                      header('HTTP/1.1 503 Service Unavailable');
1608                      trigger_error('BOARD_UNAVAILABLE');
1609                  }
1610              }
1611          }
1612   
1613          if (isset($this->data['session_viewonline']))
1614          {
1615              // Make sure the user is able to hide his session
1616              if (!$this->data['session_viewonline'])
1617              {
1618                  // Reset online status if not allowed to hide the session...
1619                  if (!$auth->acl_get('u_hideonline'))
1620                  {
1621                      $sql = 'UPDATE ' . SESSIONS_TABLE . '
1622                          SET session_viewonline = 1
1623                          WHERE session_user_id = ' . $this->data['user_id'];
1624                      $db->sql_query($sql);
1625                      $this->data['session_viewonline'] = 1;
1626                  }
1627              }
1628              else if (!$this->data['user_allow_viewonline'])
1629              {
1630                  // the user wants to hide and is allowed to  -> cloaking device on.
1631                  if ($auth->acl_get('u_hideonline'))
1632                  {
1633                      $sql = 'UPDATE ' . SESSIONS_TABLE . '
1634                          SET session_viewonline = 0
1635                          WHERE session_user_id = ' . $this->data['user_id'];
1636                      $db->sql_query($sql);
1637                      $this->data['session_viewonline'] = 0;
1638                  }
1639              }
1640          }
1641   
1642   
1643          // Does the user need to change their password? If so, redirect to the
1644          // ucp profile reg_details page ... of course do not redirect if we're already in the ucp
1645          if (!defined('IN_ADMIN') && !defined('ADMIN_START') && $config['chg_passforce'] && $this->data['is_registered'] && $auth->acl_get('u_chgpasswd') && $this->data['user_passchg'] < time() - ($config['chg_passforce'] * 86400))
1646          {
1647              if (strpos($this->page['query_string'], 'mode=reg_details') === false && $this->page['page_name'] != "ucp.$phpEx")
1648              {
1649                  redirect(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=profile&amp;mode=reg_details'));
1650              }
1651          }
1652   
1653          return;
1654      }
1655   
1656      /**
1657      * Add Language Items - use_db and use_help are assigned where needed (only use them to force inclusion)
1658      *
1659      * @param mixed $lang_set specifies the language entries to include
1660      * @param bool $use_db internal variable for recursion, do not use
1661      * @param bool $use_help internal variable for recursion, do not use
1662      *
1663      * Examples:
1664      * <code>
1665      * $lang_set = array('posting', 'help' => 'faq');
1666      * $lang_set = array('posting', 'viewtopic', 'help' => array('bbcode', 'faq'))
1667      * $lang_set = array(array('posting', 'viewtopic'), 'help' => array('bbcode', 'faq'))
1668      * $lang_set = 'posting'
1669      * $lang_set = array('help' => 'faq', 'db' => array('help:faq', 'posting'))
1670      * </code>
1671      */
1672      function add_lang($lang_set, $use_db = false, $use_help = false)
1673      {
1674          global $phpEx;
1675   
1676          if (is_array($lang_set))
1677          {
1678              foreach ($lang_set as $key => $lang_file)
1679              {
1680                  // Please do not delete this line.
1681                  // We have to force the type here, else [array] language inclusion will not work
1682                  $key = (string) $key;
1683   
1684                  if ($key == 'db')
1685                  {
1686                      $this->add_lang($lang_file, true, $use_help);
1687                  }
1688                  else if ($key == 'help')
1689                  {
1690                      $this->add_lang($lang_file, $use_db, true);
1691                  }
1692                  else if (!is_array($lang_file))
1693                  {
1694                      $this->set_lang($this->lang, $this->help, $lang_file, $use_db, $use_help);
1695                  }
1696                  else
1697                  {
1698                      $this->add_lang($lang_file, $use_db, $use_help);
1699                  }
1700              }
1701              unset($lang_set);
1702          }
1703          else if ($lang_set)
1704          {
1705              $this->set_lang($this->lang, $this->help, $lang_set, $use_db, $use_help);
1706          }
1707      }
1708   
1709      /**
1710      * Set language entry (called by add_lang)
1711      * @access private
1712      */
1713      function set_lang(&$lang, &$help, $lang_file, $use_db = false, $use_help = false)
1714      {
1715          global $phpEx;
1716   
1717          // Make sure the language path is set (if the user setup did not happen it is not set)
1718          if (!$this->lang_path)
1719          {
1720              global $phpbb_root_path, $config;
1721   
1722              $this->lang_path = $phpbb_root_path . 'language/' . basename($config['default_lang']) . '/';
1723          }
1724   
1725          // $lang == $this->lang
1726          // $help == $this->help
1727          // - add appropriate variables here, name them as they are used within the language file...
1728          if (!$use_db)
1729          {
1730              if ($use_help && strpos($lang_file, '/') !== false)
1731              {
1732                  $language_filename = $this->lang_path . substr($lang_file, 0, stripos($lang_file, '/') + 1) . 'help_' . substr($lang_file, stripos($lang_file, '/') + 1) . '.' . $phpEx;
1733              }
1734              else
1735              {
1736                  $language_filename = $this->lang_path . (($use_help) ? 'help_' : '') . $lang_file . '.' . $phpEx;
1737              }
1738   
1739              if ((@include $language_filename) === false)
1740              {
1741                  trigger_error('Language file ' . basename($language_filename) . ' couldn\'t be opened.', E_USER_ERROR);
1742              }
1743          }
1744          else if ($use_db)
1745          {
1746              // Get Database Language Strings
1747              // Put them into $lang if nothing is prefixed, put them into $help if help: is prefixed
1748              // For example: help:faq, posting
1749          }
1750      }
1751   
1752      /**
1753      * Format user date
1754      */
1755      function format_date($gmepoch, $format = false, $forcedate = false)
1756      {
1757          static $midnight;
1758   
1759          $lang_dates = $this->lang['datetime'];
1760          $format = (!$format) ? $this->date_format : $format;
1761   
1762          // Short representation of month in format
1763          if ((strpos($format, '\M') === false && strpos($format, 'M') !== false) || (strpos($format, '\r') === false && strpos($format, 'r') !== false))
1764          {
1765              $lang_dates['May'] = $lang_dates['May_short'];
1766          }
1767   
1768          unset($lang_dates['May_short']);
1769   
1770          if (!$midnight)
1771          {
1772              list($d, $m, $y) = explode(' ', gmdate('j n Y', time() + $this->timezone + $this->dst));
1773              $midnight = gmmktime(0, 0, 0, $m, $d, $y) - $this->timezone - $this->dst;
1774          }
1775   
1776          if (strpos($format, '|') === false || ($gmepoch < $midnight - 86400 && !$forcedate) || ($gmepoch > $midnight + 172800 && !$forcedate))
1777          {
1778              return strtr(@gmdate(str_replace('|', '', $format), $gmepoch + $this->timezone + $this->dst), $lang_dates);
1779          }
1780   
1781          if ($gmepoch > $midnight + 86400 && !$forcedate)
1782          {
1783              $format = substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1);
1784              return str_replace('||', $this->lang['datetime']['TOMORROW'], strtr(@gmdate($format, $gmepoch + $this->timezone + $this->dst), $lang_dates));
1785          }
1786          else if ($gmepoch > $midnight && !$forcedate)
1787          {
1788              $format = substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1);
1789              return str_replace('||', $this->lang['datetime']['TODAY'], strtr(@gmdate($format, $gmepoch + $this->timezone + $this->dst), $lang_dates));
1790          }
1791          else if ($gmepoch > $midnight - 86400 && !$forcedate)
1792          {
1793              $format = substr($format, 0, strpos($format, '|')) . '||' . substr(strrchr($format, '|'), 1);
1794              return str_replace('||', $this->lang['datetime']['YESTERDAY'], strtr(@gmdate($format, $gmepoch + $this->timezone + $this->dst), $lang_dates));
1795          }
1796   
1797          return strtr(@gmdate(str_replace('|', '', $format), $gmepoch + $this->timezone + $this->dst), $lang_dates);
1798      }
1799   
1800      /**
1801      * Get language id currently used by the user
1802      */
1803      function get_iso_lang_id()
1804      {
1805          global $config, $db;
1806   
1807          if (!empty($this->lang_id))
1808          {
1809              return $this->lang_id;
1810          }
1811   
1812          if (!$this->lang_name)
1813          {
1814              $this->lang_name = $config['default_lang'];
1815          }
1816   
1817          $sql = 'SELECT lang_id
1818              FROM ' . LANG_TABLE . "
1819              WHERE lang_iso = '" . $db->sql_escape($this->lang_name) . "'";
1820          $result = $db->sql_query($sql);
1821          $this->lang_id = (int) $db->sql_fetchfield('lang_id');
1822          $db->sql_freeresult($result);
1823   
1824          return $this->lang_id;
1825      }
1826   
1827      /**
1828      * Get users profile fields
1829      */
1830      function get_profile_fields($user_id)
1831      {
1832          global $db;
1833   
1834          if (isset($this->profile_fields))
1835          {
1836              return;
1837          }
1838   
1839          $sql = 'SELECT *
1840              FROM ' . PROFILE_FIELDS_DATA_TABLE . "
1841              WHERE user_id = $user_id";
1842          $result = $db->sql_query_limit($sql, 1);
1843          $this->profile_fields = (!($row = $db->sql_fetchrow($result))) ? array() : $row;
1844          $db->sql_freeresult($result);
1845      }
1846   
1847      /**
1848      * Specify/Get image
1849      */
1850      function img($img, $alt = '', $width = false, $suffix = '', $type = 'full_tag')
1851      {
1852          static $imgs;
1853          global $phpbb_root_path;
1854   
1855          $img_data = &$imgs[$img];
1856   
1857          if (empty($img_data))
1858          {
1859              if (!isset($this->img_array[$img]))
1860              {
1861                  // Do not fill the image to let designers decide what to do if the image is empty
1862                  $img_data = '';
1863                  return $img_data;
1864              }
1865   
1866              $img_data['src'] = $phpbb_root_path . 'styles/' . $this->theme['imageset_path'] . '/imageset/' . ($this->img_array[$img]['image_lang'] ? $this->img_array[$img]['image_lang'] .'/' : '') . $this->img_array[$img]['image_filename'];
1867              $img_data['width'] = $this->img_array[$img]['image_width'];
1868              $img_data['height'] = $this->img_array[$img]['image_height'];
1869          }
1870   
1871          $alt = (!empty($this->lang[$alt])) ? $this->lang[$alt] : $alt;
1872   
1873          switch ($type)
1874          {
1875              case 'src':
1876                  return $img_data['src'];
1877              break;
1878   
1879              case 'width':
1880                  return ($width === false) ? $img_data['width'] : $width;
1881              break;
1882   
1883              case 'height':
1884                  return $img_data['height'];
1885              break;
1886   
1887              default:
1888                  $use_width = ($width === false) ? $img_data['width'] : $width;
1889   
1890                  return '<img src="' . $img_data['src'] . '"' . (($use_width) ? ' width="' . $use_width . '"' : '') . (($img_data['height']) ? ' height="' . $img_data['height'] . '"' : '') . ' alt="' . $alt . '" title="' . $alt . '" />';
1891              break;
1892          }
1893      }
1894   
1895      /**
1896      * Get option bit field from user options
1897      */
1898      function optionget($key, $data = false)
1899      {
1900          if (!isset($this->keyvalues[$key]))
1901          {
1902              $var = ($data) ? $data : $this->data['user_options'];
1903              $this->keyvalues[$key] = ($var & 1 << $this->keyoptions[$key]) ? true : false;
1904          }
1905   
1906          return $this->keyvalues[$key];
1907      }
1908   
1909      /**
1910      * Set option bit field for user options
1911      */
1912      function optionset($key, $value, $data = false)
1913      {
1914          $var = ($data) ? $data : $this->data['user_options'];
1915   
1916          if ($value && !($var & 1 << $this->keyoptions[$key]))
1917          {
1918              $var += 1 << $this->keyoptions[$key];
1919          }
1920          else if (!$value && ($var & 1 << $this->keyoptions[$key]))
1921          {
1922              $var -= 1 << $this->keyoptions[$key];
1923          }
1924          else
1925          {
1926              return ($data) ? $var : false;
1927          }
1928   
1929          if (!$data)
1930          {
1931              $this->data['user_options'] = $var;
1932              return true;
1933          }
1934          else
1935          {
1936              return $var;
1937          }
1938      }
1939  }
1940   
1941  ?>