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

functions.php

Zuletzt modifiziert: 09.10.2024, 12:50 - Dateigröße: 99.96 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  // Common global functions
0020   
0021  /**
0022  * set_var
0023  *
0024  * Set variable, used by {@link request_var the request_var function}
0025  *
0026  * @access private
0027  */
0028  function set_var(&$result, $var, $type, $multibyte = false)
0029  {
0030      settype($var, $type);
0031      $result = $var;
0032   
0033      if ($type == 'string')
0034      {
0035          $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r"), array("\n", "\n"), $result), ENT_COMPAT, 'UTF-8'));
0036   
0037          if (!empty($result))
0038          {
0039              // Make sure multibyte characters are wellformed
0040              if ($multibyte)
0041              {
0042                  if (!preg_match('/^./u', $result))
0043                  {
0044                      $result = '';
0045                  }
0046              }
0047              else
0048              {
0049                  // no multibyte, allow only ASCII (0-127)
0050                  $result = preg_replace('/[\x80-\xFF]/', '?', $result);
0051              }
0052          }
0053   
0054          $result = (STRIP) ? stripslashes($result) : $result;
0055      }
0056  }
0057   
0058  /**
0059  * request_var
0060  *
0061  * Used to get passed variable
0062  */
0063  function request_var($var_name, $default, $multibyte = false, $cookie = false)
0064  {
0065      if (!$cookie && isset($_COOKIE[$var_name]))
0066      {
0067          if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
0068          {
0069              return (is_array($default)) ? array() : $default;
0070          }
0071          $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
0072      }
0073   
0074      if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
0075      {
0076          return (is_array($default)) ? array() : $default;
0077      }
0078   
0079      $var = $_REQUEST[$var_name];
0080      if (!is_array($default))
0081      {
0082          $type = gettype($default);
0083      }
0084      else
0085      {
0086          list($key_type, $type) = each($default);
0087          $type = gettype($type);
0088          $key_type = gettype($key_type);
0089          if ($type == 'array')
0090          {
0091              reset($default);
0092              $default = current($default);
0093              list($sub_key_type, $sub_type) = each($default);
0094              $sub_type = gettype($sub_type);
0095              $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
0096              $sub_key_type = gettype($sub_key_type);
0097          }
0098      }
0099   
0100      if (is_array($var))
0101      {
0102          $_var = $var;
0103          $var = array();
0104   
0105          foreach ($_var as $k => $v)
0106          {
0107              set_var($k, $k, $key_type);
0108              if ($type == 'array' && is_array($v))
0109              {
0110                  foreach ($v as $_k => $_v)
0111                  {
0112                      if (is_array($_v))
0113                      {
0114                          $_v = null;
0115                      }
0116                      set_var($_k, $_k, $sub_key_type);
0117                      set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
0118                  }
0119              }
0120              else
0121              {
0122                  if ($type == 'array' || is_array($v))
0123                  {
0124                      $v = null;
0125                  }
0126                  set_var($var[$k], $v, $type, $multibyte);
0127              }
0128          }
0129      }
0130      else
0131      {
0132          set_var($var, $var, $type, $multibyte);
0133      }
0134   
0135      return $var;
0136  }
0137   
0138  /**
0139  * Set config value. Creates missing config entry.
0140  */
0141  function set_config($config_name, $config_value, $is_dynamic = false)
0142  {
0143      global $db, $cache, $config;
0144   
0145      $sql = 'UPDATE ' . CONFIG_TABLE . "
0146          SET config_value = '" . $db->sql_escape($config_value) . "'
0147          WHERE config_name = '" . $db->sql_escape($config_name) . "'";
0148      $db->sql_query($sql);
0149   
0150      if (!$db->sql_affectedrows() && !isset($config[$config_name]))
0151      {
0152          $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
0153              'config_name'    => $config_name,
0154              'config_value'    => $config_value,
0155              'is_dynamic'    => ($is_dynamic) ? 1 : 0));
0156          $db->sql_query($sql);
0157      }
0158   
0159      $config[$config_name] = $config_value;
0160   
0161      if (!$is_dynamic)
0162      {
0163          $cache->destroy('config');
0164      }
0165  }
0166   
0167  /**
0168  * Generates an alphanumeric random string of given length
0169  */
0170  function gen_rand_string($num_chars = 8)
0171  {
0172      $rand_str = unique_id();
0173      $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
0174   
0175      return substr($rand_str, 0, $num_chars);
0176  }
0177   
0178  /**
0179  * Return unique id
0180  * @param string $extra additional entropy
0181  */
0182  function unique_id($extra = 'c')
0183  {
0184      static $dss_seeded = false;
0185      global $config;
0186   
0187      $val = $config['rand_seed'] . microtime();
0188      $val = md5($val);
0189      $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
0190   
0191      if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
0192      {
0193          set_config('rand_seed', $config['rand_seed'], true);
0194          set_config('rand_seed_last_update', time(), true);
0195          $dss_seeded = true;
0196      }
0197   
0198      return substr($val, 4, 16);
0199  }
0200   
0201  /**
0202  * Determine whether we are approaching the maximum execution time. Should be called once
0203  * at the beginning of the script in which it's used.
0204  * @return    bool    Either true if the maximum execution time is nearly reached, or false
0205  *                    if some time is still left.
0206  */
0207  function still_on_time($extra_time = 15)
0208  {
0209      static $max_execution_time, $start_time;
0210   
0211      $time = explode(' ', microtime());
0212      $current_time = $time[0] + $time[1];
0213   
0214      if (empty($max_execution_time))
0215      {
0216          $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
0217   
0218          // If zero, then set to something higher to not let the user catch the ten seconds barrier.
0219          if ($max_execution_time === 0)
0220          {
0221              $max_execution_time = 50 + $extra_time;
0222          }
0223   
0224          $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
0225   
0226          // For debugging purposes
0227          // $max_execution_time = 10;
0228   
0229          global $starttime;
0230          $start_time = (empty($starttime)) ? $current_time : $starttime;
0231      }
0232   
0233      return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
0234  }
0235   
0236  /**
0237  *
0238  * @version Version 0.1 / $Id$
0239  *
0240  * Portable PHP password hashing framework.
0241  *
0242  * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
0243  * the public domain.
0244  *
0245  * There's absolutely no warranty.
0246  *
0247  * The homepage URL for this framework is:
0248  *
0249  *    http://www.openwall.com/phpass/
0250  *
0251  * Please be sure to update the Version line if you edit this file in any way.
0252  * It is suggested that you leave the main version number intact, but indicate
0253  * your project name (after the slash) and add your own revision information.
0254  *
0255  * Please do not change the "private" password hashing method implemented in
0256  * here, thereby making your hashes incompatible.  However, if you must, please
0257  * change the hash type identifier (the "$P$") to something different.
0258  *
0259  * Obviously, since this code is in the public domain, the above are not
0260  * requirements (there can be none), but merely suggestions.
0261  *
0262  *
0263  * Hash the password
0264  */
0265  function phpbb_hash($password)
0266  {
0267      $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
0268   
0269      $random_state = unique_id();
0270      $random = '';
0271      $count = 6;
0272   
0273      if (($fh = @fopen('/dev/urandom', 'rb')))
0274      {
0275          $random = fread($fh, $count);
0276          fclose($fh);
0277      }
0278   
0279      if (strlen($random) < $count)
0280      {
0281          $random = '';
0282   
0283          for ($i = 0; $i < $count; $i += 16)
0284          {
0285              $random_state = md5(unique_id() . $random_state);
0286              $random .= pack('H*', md5($random_state));
0287          }
0288          $random = substr($random, 0, $count);
0289      }
0290      
0291      $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
0292   
0293      if (strlen($hash) == 34)
0294      {
0295          return $hash;
0296      }
0297   
0298      return md5($password);
0299  }
0300   
0301  /**
0302  * Check for correct password
0303  */
0304  function phpbb_check_hash($password, $hash)
0305  {
0306      $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
0307      if (strlen($hash) == 34)
0308      {
0309          return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
0310      }
0311   
0312      return (md5($password) === $hash) ? true : false;
0313  }
0314   
0315  /**
0316  * Generate salt for hash generation
0317  */
0318  function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
0319  {
0320      if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
0321      {
0322          $iteration_count_log2 = 8;
0323      }
0324   
0325      $output = '$H$';
0326      $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
0327      $output .= _hash_encode64($input, 6, $itoa64);
0328   
0329      return $output;
0330  }
0331   
0332  /**
0333  * Encode hash
0334  */
0335  function _hash_encode64($input, $count, &$itoa64)
0336  {
0337      $output = '';
0338      $i = 0;
0339   
0340      do
0341      {
0342          $value = ord($input[$i++]);
0343          $output .= $itoa64[$value & 0x3f];
0344   
0345          if ($i < $count)
0346          {
0347              $value |= ord($input[$i]) << 8;
0348          }
0349   
0350          $output .= $itoa64[($value >> 6) & 0x3f];
0351   
0352          if ($i++ >= $count)
0353          {
0354              break;
0355          }
0356   
0357          if ($i < $count)
0358          {
0359              $value |= ord($input[$i]) << 16;
0360          }
0361   
0362          $output .= $itoa64[($value >> 12) & 0x3f];
0363          
0364          if ($i++ >= $count)
0365          {
0366              break;
0367          }
0368   
0369          $output .= $itoa64[($value >> 18) & 0x3f];
0370      }
0371      while ($i < $count);
0372   
0373      return $output;
0374  }
0375   
0376  /**
0377  * The crypt function/replacement
0378  */
0379  function _hash_crypt_private($password, $setting, &$itoa64)
0380  {
0381      $output = '*';
0382   
0383      // Check for correct hash
0384      if (substr($setting, 0, 3) != '$H$')
0385      {
0386          return $output;
0387      }
0388   
0389      $count_log2 = strpos($itoa64, $setting[3]);
0390   
0391      if ($count_log2 < 7 || $count_log2 > 30)
0392      {
0393          return $output;
0394      }
0395   
0396      $count = 1 << $count_log2;
0397      $salt = substr($setting, 4, 8);
0398   
0399      if (strlen($salt) != 8)
0400      {
0401          return $output;
0402      }
0403   
0404      /**
0405      * We're kind of forced to use MD5 here since it's the only
0406      * cryptographic primitive available in all versions of PHP
0407      * currently in use.  To implement our own low-level crypto
0408      * in PHP would result in much worse performance and
0409      * consequently in lower iteration counts and hashes that are
0410      * quicker to crack (by non-PHP code).
0411      */
0412      if (PHP_VERSION >= 5)
0413      {
0414          $hash = md5($salt . $password, true);
0415          do
0416          {
0417              $hash = md5($hash . $password, true);
0418          }
0419          while (--$count);
0420      }
0421      else
0422      {
0423          $hash = pack('H*', md5($salt . $password));
0424          do
0425          {
0426              $hash = pack('H*', md5($hash . $password));
0427          }
0428          while (--$count);
0429      }
0430   
0431      $output = substr($setting, 0, 12);
0432      $output .= _hash_encode64($hash, 16, $itoa64);
0433   
0434      return $output;
0435  }
0436   
0437  // Compatibility functions
0438   
0439  if (!function_exists('array_combine'))
0440  {
0441      /**
0442      * A wrapper for the PHP5 function array_combine()
0443      * @param array $keys contains keys for the resulting array
0444      * @param array $values contains values for the resulting array
0445      *
0446      * @return Returns an array by using the values from the keys array as keys and the
0447      *     values from the values array as the corresponding values. Returns false if the
0448      *     number of elements for each array isn't equal or if the arrays are empty.
0449      */
0450      function array_combine($keys, $values)
0451      {
0452          $keys = array_values($keys);
0453          $values = array_values($values);
0454   
0455          $n = sizeof($keys);
0456          $m = sizeof($values);
0457          if (!$n || !$m || ($n != $m))
0458          {
0459              return false;
0460          }
0461   
0462          $combined = array();
0463          for ($i = 0; $i < $n; $i++)
0464          {
0465              $combined[$keys[$i]] = $values[$i];
0466          }
0467          return $combined;
0468      }
0469  }
0470   
0471  if (!function_exists('str_split'))
0472  {
0473      /**
0474      * A wrapper for the PHP5 function str_split()
0475      * @param array $string contains the string to be converted
0476      * @param array $split_length contains the length of each chunk
0477      *
0478      * @return  Converts a string to an array. If the optional split_length parameter is specified,
0479      *      the returned array will be broken down into chunks with each being split_length in length,
0480      *      otherwise each chunk will be one character in length. FALSE is returned if split_length is
0481      *      less than 1. If the split_length length exceeds the length of string, the entire string is
0482      *      returned as the first (and only) array element.
0483      */
0484      function str_split($string, $split_length = 1)
0485      {
0486          if ($split_length < 1)
0487          {
0488              return false;
0489          }
0490          else if ($split_length >= strlen($string))
0491          {
0492              return array($string);
0493          }
0494          else
0495          {
0496              preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
0497              return $matches[0];
0498          }
0499      }
0500  }
0501   
0502  if (!function_exists('stripos'))
0503  {
0504      /**
0505      * A wrapper for the PHP5 function stripos
0506      * Find position of first occurrence of a case-insensitive string
0507      *
0508      * @param string $haystack is the string to search in
0509      * @param string $needle is the string to search for
0510      *
0511      * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
0512      * Note that the needle may be a string of one or more characters.
0513      * If needle is not found, stripos() will return boolean FALSE.
0514      */
0515      function stripos($haystack, $needle)
0516      {
0517          if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
0518          {
0519              return strpos($haystack, $m[0]);
0520          }
0521   
0522          return false;
0523      }
0524  }
0525   
0526  if (!function_exists('realpath'))
0527  {
0528      /**
0529      * Checks if a path ($path) is absolute or relative
0530      *
0531      * @param string $path Path to check absoluteness of
0532      * @return boolean
0533      */
0534      function is_absolute($path)
0535      {
0536          return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
0537      }
0538   
0539      /**
0540      * @author Chris Smith <chris@project-minerva.org>
0541      * @copyright 2006 Project Minerva Team
0542      * @param string $path The path which we should attempt to resolve.
0543      * @return mixed
0544      */
0545      function phpbb_realpath($path)
0546      {
0547          // Now to perform funky shizzle
0548   
0549          // Switch to use UNIX slashes
0550          $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
0551          $path_prefix = '';
0552   
0553          // Determine what sort of path we have
0554          if (is_absolute($path))
0555          {
0556              $absolute = true;
0557   
0558              if ($path[0] == '/')
0559              {
0560                  // Absolute path, *NIX style
0561                  $path_prefix = '';
0562              }
0563              else
0564              {
0565                  // Absolute path, Windows style
0566                  // Remove the drive letter and colon
0567                  $path_prefix = $path[0] . ':';
0568                  $path = substr($path, 2);
0569              }
0570          }
0571          else
0572          {
0573              // Relative Path
0574              // Prepend the current working directory
0575              if (function_exists('getcwd'))
0576              {
0577                  // This is the best method, hopefully it is enabled!
0578                  $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
0579                  $absolute = true;
0580                  if (preg_match('#^[a-z]:#i', $path))
0581                  {
0582                      $path_prefix = $path[0] . ':';
0583                      $path = substr($path, 2);
0584                  }
0585                  else
0586                  {
0587                      $path_prefix = '';
0588                  }
0589              }
0590              else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
0591              {
0592                  // Warning: If chdir() has been used this will lie!
0593                  // Warning: This has some problems sometime (CLI can create them easily)
0594                  $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
0595                  $absolute = true;
0596                  $path_prefix = '';
0597              }
0598              else
0599              {
0600                  // We have no way of getting the absolute path, just run on using relative ones.
0601                  $absolute = false;
0602                  $path_prefix = '.';
0603              }
0604          }
0605   
0606          // Remove any repeated slashes
0607          $path = preg_replace('#/{2,}#', '/', $path);
0608   
0609          // Remove the slashes from the start and end of the path
0610          $path = trim($path, '/');
0611   
0612          // Break the string into little bits for us to nibble on
0613          $bits = explode('/', $path);
0614   
0615          // Remove any . in the path, renumber array for the loop below
0616          $bits = array_values(array_diff($bits, array('.')));
0617   
0618          // Lets get looping, run over and resolve any .. (up directory)
0619          for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
0620          {
0621              // @todo Optimise
0622              if ($bits[$i] == '..' )
0623              {
0624                  if (isset($bits[$i - 1]))
0625                  {
0626                      if ($bits[$i - 1] != '..')
0627                      {
0628                          // We found a .. and we are able to traverse upwards, lets do it!
0629                          unset($bits[$i]);
0630                          unset($bits[$i - 1]);
0631                          $i -= 2;
0632                          $max -= 2;
0633                          $bits = array_values($bits);
0634                      }
0635                  }
0636                  else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
0637                  {
0638                      // We have an absolute path trying to descend above the root of the filesystem
0639                      // ... Error!
0640                      return false;
0641                  }
0642              }
0643          }
0644   
0645          // Prepend the path prefix
0646          array_unshift($bits, $path_prefix);
0647   
0648          $resolved = '';
0649   
0650          $max = sizeof($bits) - 1;
0651   
0652          // Check if we are able to resolve symlinks, Windows cannot.
0653          $symlink_resolve = (function_exists('readlink')) ? true : false;
0654   
0655          foreach ($bits as $i => $bit)
0656          {
0657              if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
0658              {
0659                  // Path Exists
0660                  if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
0661                  {
0662                      // Resolved a symlink.
0663                      $resolved = $link . (($i == $max) ? '' : '/');
0664                      continue;
0665                  }
0666              }
0667              else
0668              {
0669                  // Something doesn't exist here!
0670                  // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
0671                  // return false;
0672              }
0673              $resolved .= $bit . (($i == $max) ? '' : '/');
0674          }
0675   
0676          // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
0677          // because we must be inside that basedir, the question is where...
0678          // @internal The slash in is_dir() gets around an open_basedir restriction
0679          if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
0680          {
0681              return false;
0682          }
0683   
0684          // Put the slashes back to the native operating systems slashes
0685          $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
0686   
0687          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
0688          if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
0689          {
0690              return substr($resolved, 0, -1);
0691          }
0692   
0693          return $resolved; // We got here, in the end!
0694      }
0695  }
0696  else
0697  {
0698      /**
0699      * A wrapper for realpath
0700      * @ignore
0701      */
0702      function phpbb_realpath($path)
0703      {
0704          $path = realpath($path);
0705   
0706          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
0707          if (substr($path, -1) == DIRECTORY_SEPARATOR)
0708          {
0709              return substr($path, 0, -1);
0710          }
0711   
0712          return $path;
0713      }
0714  }
0715   
0716  if (!function_exists('htmlspecialchars_decode'))
0717  {
0718      /**
0719      * A wrapper for htmlspecialchars_decode
0720      * @ignore
0721      */
0722      function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
0723      {
0724          return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
0725      }
0726  }
0727   
0728  // functions used for building option fields
0729   
0730  /**
0731  * Pick a language, any language ...
0732  */
0733  function language_select($default = '')
0734  {
0735      global $db;
0736   
0737      $sql = 'SELECT lang_iso, lang_local_name
0738          FROM ' . LANG_TABLE . '
0739          ORDER BY lang_english_name';
0740      $result = $db->sql_query($sql);
0741   
0742      $lang_options = '';
0743      while ($row = $db->sql_fetchrow($result))
0744      {
0745          $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
0746          $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
0747      }
0748      $db->sql_freeresult($result);
0749   
0750      return $lang_options;
0751  }
0752   
0753  /**
0754  * Pick a template/theme combo,
0755  */
0756  function style_select($default = '', $all = false)
0757  {
0758      global $db;
0759   
0760      $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
0761      $sql = 'SELECT style_id, style_name
0762          FROM ' . STYLES_TABLE . "
0763          $sql_where
0764          ORDER BY style_name";
0765      $result = $db->sql_query($sql);
0766   
0767      $style_options = '';
0768      while ($row = $db->sql_fetchrow($result))
0769      {
0770          $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
0771          $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
0772      }
0773      $db->sql_freeresult($result);
0774   
0775      return $style_options;
0776  }
0777   
0778  /**
0779  * Pick a timezone
0780  */
0781  function tz_select($default = '', $truncate = false)
0782  {
0783      global $user;
0784   
0785      $tz_select = '';
0786      foreach ($user->lang['tz_zones'] as $offset => $zone)
0787      {
0788          if ($truncate)
0789          {
0790              $zone_trunc = truncate_string($zone, 50, false, '...');
0791          }
0792          else
0793          {
0794              $zone_trunc = $zone;
0795          }
0796   
0797          if (is_numeric($offset))
0798          {
0799              $selected = ($offset == $default) ? ' selected="selected"' : '';
0800              $tz_select .= '<option title="'.$zone.'" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
0801          }
0802      }
0803   
0804      return $tz_select;
0805  }
0806   
0807  // Functions handling topic/post tracking/marking
0808   
0809  /**
0810  * Marks a topic/forum as read
0811  * Marks a topic as posted to
0812  *
0813  * @param int $user_id can only be used with $mode == 'post'
0814  */
0815  function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
0816  {
0817      global $db, $user, $config;
0818   
0819      if ($mode == 'all')
0820      {
0821          if ($forum_id === false || !sizeof($forum_id))
0822          {
0823              if ($config['load_db_lastread'] && $user->data['is_registered'])
0824              {
0825                  // Mark all forums read (index page)
0826                  $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
0827                  $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
0828                  $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
0829              }
0830              else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0831              {
0832                  $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0833                  $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
0834   
0835                  unset($tracking_topics['tf']);
0836                  unset($tracking_topics['t']);
0837                  unset($tracking_topics['f']);
0838                  $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
0839      
0840                  $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
0841                  $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
0842   
0843                  unset($tracking_topics);
0844   
0845                  if ($user->data['is_registered'])
0846                  {
0847                      $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
0848                  }
0849              }
0850          }
0851   
0852          return;
0853      }
0854      else if ($mode == 'topics')
0855      {
0856          // Mark all topics in forums read
0857          if (!is_array($forum_id))
0858          {
0859              $forum_id = array($forum_id);
0860          }
0861   
0862          // Add 0 to forums array to mark global announcements correctly
0863          $forum_id[] = 0;
0864   
0865          if ($config['load_db_lastread'] && $user->data['is_registered'])
0866          {
0867              $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
0868                  WHERE user_id = {$user->data['user_id']}
0869                      AND " . $db->sql_in_set('forum_id', $forum_id);
0870              $db->sql_query($sql);
0871   
0872              $sql = 'SELECT forum_id
0873                  FROM ' . FORUMS_TRACK_TABLE . "
0874                  WHERE user_id = {$user->data['user_id']}
0875                      AND " . $db->sql_in_set('forum_id', $forum_id);
0876              $result = $db->sql_query($sql);
0877   
0878              $sql_update = array();
0879              while ($row = $db->sql_fetchrow($result))
0880              {
0881                  $sql_update[] = $row['forum_id'];
0882              }
0883              $db->sql_freeresult($result);
0884   
0885              if (sizeof($sql_update))
0886              {
0887                  $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
0888                      SET mark_time = ' . time() . "
0889                      WHERE user_id = {$user->data['user_id']}
0890                          AND " . $db->sql_in_set('forum_id', $sql_update);
0891                  $db->sql_query($sql);
0892              }
0893   
0894              if ($sql_insert = array_diff($forum_id, $sql_update))
0895              {
0896                  $sql_ary = array();
0897                  foreach ($sql_insert as $f_id)
0898                  {
0899                      $sql_ary[] = array(
0900                          'user_id'    => (int) $user->data['user_id'],
0901                          'forum_id'    => (int) $f_id,
0902                          'mark_time'    => time()
0903                      );
0904                  }
0905   
0906                  $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
0907              }
0908          }
0909          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0910          {
0911              $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0912              $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
0913   
0914              foreach ($forum_id as $f_id)
0915              {
0916                  $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
0917   
0918                  if (isset($tracking['tf'][$f_id]))
0919                  {
0920                      unset($tracking['tf'][$f_id]);
0921                  }
0922   
0923                  foreach ($topic_ids36 as $topic_id36)
0924                  {
0925                      unset($tracking['t'][$topic_id36]);
0926                  }
0927   
0928                  if (isset($tracking['f'][$f_id]))
0929                  {
0930                      unset($tracking['f'][$f_id]);
0931                  }
0932   
0933                  $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
0934              }
0935   
0936              if (isset($tracking['tf']) && empty($tracking['tf']))
0937              {
0938                  unset($tracking['tf']);
0939              }
0940   
0941              $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
0942              $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
0943   
0944              unset($tracking);
0945          }
0946   
0947          return;
0948      }
0949      else if ($mode == 'topic')
0950      {
0951          if ($topic_id === false || $forum_id === false)
0952          {
0953              return;
0954          }
0955   
0956          if ($config['load_db_lastread'] && $user->data['is_registered'])
0957          {
0958              $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
0959                  SET mark_time = ' . (($post_time) ? $post_time : time()) . "
0960                  WHERE user_id = {$user->data['user_id']}
0961                      AND topic_id = $topic_id";
0962              $db->sql_query($sql);
0963   
0964              // insert row
0965              if (!$db->sql_affectedrows())
0966              {
0967                  $db->sql_return_on_error(true);
0968   
0969                  $sql_ary = array(
0970                      'user_id'        => (int) $user->data['user_id'],
0971                      'topic_id'        => (int) $topic_id,
0972                      'forum_id'        => (int) $forum_id,
0973                      'mark_time'        => ($post_time) ? (int) $post_time : time(),
0974                  );
0975   
0976                  $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
0977   
0978                  $db->sql_return_on_error(false);
0979              }
0980          }
0981          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
0982          {
0983              $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
0984              $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
0985   
0986              $topic_id36 = base_convert($topic_id, 10, 36);
0987   
0988              if (!isset($tracking['t'][$topic_id36]))
0989              {
0990                  $tracking['tf'][$forum_id][$topic_id36] = true;
0991              }
0992   
0993              $post_time = ($post_time) ? $post_time : time();
0994              $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
0995   
0996              // If the cookie grows larger than 10000 characters we will remove the smallest value
0997              // This can result in old topics being unread - but most of the time it should be accurate...
0998              if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
0999              {
1000                  //echo 'Cookie grown too large' . print_r($tracking, true);
1001   
1002                  // We get the ten most minimum stored time offsets and its associated topic ids
1003                  $time_keys = array();
1004                  for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
1005                  {
1006                      $min_value = min($tracking['t']);
1007                      $m_tkey = array_search($min_value, $tracking['t']);
1008                      unset($tracking['t'][$m_tkey]);
1009   
1010                      $time_keys[$m_tkey] = $min_value;
1011                  }
1012   
1013                  // Now remove the topic ids from the array...
1014                  foreach ($tracking['tf'] as $f_id => $topic_id_ary)
1015                  {
1016                      foreach ($time_keys as $m_tkey => $min_value)
1017                      {
1018                          if (isset($topic_id_ary[$m_tkey]))
1019                          {
1020                              $tracking['f'][$f_id] = $min_value;
1021                              unset($tracking['tf'][$f_id][$m_tkey]);
1022                          }
1023                      }
1024                  }
1025   
1026                  if ($user->data['is_registered'])
1027                  {
1028                      $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
1029                      $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
1030                  }
1031                  else
1032                  {
1033                      $tracking['l'] = max($time_keys);
1034                  }
1035              }
1036   
1037              $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
1038              $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
1039          }
1040   
1041          return;
1042      }
1043      else if ($mode == 'post')
1044      {
1045          if ($topic_id === false)
1046          {
1047              return;
1048          }
1049   
1050          $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
1051   
1052          if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
1053          {
1054              $db->sql_return_on_error(true);
1055   
1056              $sql_ary = array(
1057                  'user_id'        => (int) $use_user_id,
1058                  'topic_id'        => (int) $topic_id,
1059                  'topic_posted'    => 1
1060              );
1061   
1062              $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
1063   
1064              $db->sql_return_on_error(false);
1065          }
1066   
1067          return;
1068      }
1069  }
1070   
1071  /**
1072  * Get topic tracking info by using already fetched info
1073  */
1074  function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
1075  {
1076      global $config, $user;
1077   
1078      $last_read = array();
1079   
1080      if (!is_array($topic_ids))
1081      {
1082          $topic_ids = array($topic_ids);
1083      }
1084   
1085      foreach ($topic_ids as $topic_id)
1086      {
1087          if (!empty($rowset[$topic_id]['mark_time']))
1088          {
1089              $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
1090          }
1091      }
1092   
1093      $topic_ids = array_diff($topic_ids, array_keys($last_read));
1094   
1095      if (sizeof($topic_ids))
1096      {
1097          $mark_time = array();
1098   
1099          // Get global announcement info
1100          if ($global_announce_list && sizeof($global_announce_list))
1101          {
1102              if (!isset($forum_mark_time[0]))
1103              {
1104                  global $db;
1105   
1106                  $sql = 'SELECT mark_time
1107                      FROM ' . FORUMS_TRACK_TABLE . "
1108                      WHERE user_id = {$user->data['user_id']}
1109                          AND forum_id = 0";
1110                  $result = $db->sql_query($sql);
1111                  $row = $db->sql_fetchrow($result);
1112                  $db->sql_freeresult($result);
1113   
1114                  if ($row)
1115                  {
1116                      $mark_time[0] = $row['mark_time'];
1117                  }
1118              }
1119              else
1120              {
1121                  if ($forum_mark_time[0] !== false)
1122                  {
1123                      $mark_time[0] = $forum_mark_time[0];
1124                  }
1125              }
1126          }
1127   
1128          if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
1129          {
1130              $mark_time[$forum_id] = $forum_mark_time[$forum_id];
1131          }
1132              
1133          $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1134   
1135          foreach ($topic_ids as $topic_id)
1136          {
1137              if ($global_announce_list && isset($global_announce_list[$topic_id]))
1138              {
1139                  $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1140              }
1141              else
1142              {
1143                  $last_read[$topic_id] = $user_lastmark;
1144              }
1145          }
1146      }
1147   
1148      return $last_read;
1149  }
1150   
1151  /**
1152  * Get topic tracking info from db (for cookie based tracking only this function is used)
1153  */
1154  function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1155  {
1156      global $config, $user;
1157   
1158      $last_read = array();
1159   
1160      if (!is_array($topic_ids))
1161      {
1162          $topic_ids = array($topic_ids);
1163      }
1164   
1165      if ($config['load_db_lastread'] && $user->data['is_registered'])
1166      {
1167          global $db;
1168   
1169          $sql = 'SELECT topic_id, mark_time
1170              FROM ' . TOPICS_TRACK_TABLE . "
1171              WHERE user_id = {$user->data['user_id']}
1172                  AND " . $db->sql_in_set('topic_id', $topic_ids);
1173          $result = $db->sql_query($sql);
1174   
1175          while ($row = $db->sql_fetchrow($result))
1176          {
1177              $last_read[$row['topic_id']] = $row['mark_time'];
1178          }
1179          $db->sql_freeresult($result);
1180      
1181          $topic_ids = array_diff($topic_ids, array_keys($last_read));
1182   
1183          if (sizeof($topic_ids))
1184          {
1185              $sql = 'SELECT forum_id, mark_time
1186                  FROM ' . FORUMS_TRACK_TABLE . "
1187                  WHERE user_id = {$user->data['user_id']}
1188                      AND forum_id " .
1189                      (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "$forum_id");
1190              $result = $db->sql_query($sql);
1191          
1192              $mark_time = array();
1193              while ($row = $db->sql_fetchrow($result))
1194              {
1195                  $mark_time[$row['forum_id']] = $row['mark_time'];
1196              }
1197              $db->sql_freeresult($result);
1198   
1199              $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1200   
1201              foreach ($topic_ids as $topic_id)
1202              {
1203                  if ($global_announce_list && isset($global_announce_list[$topic_id]))
1204                  {
1205                      $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1206                  }
1207                  else
1208                  {
1209                      $last_read[$topic_id] = $user_lastmark;
1210                  }
1211              }
1212          }
1213      }
1214      else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1215      {
1216          global $tracking_topics;
1217   
1218          if (!isset($tracking_topics) || !sizeof($tracking_topics))
1219          {
1220              $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1221              $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1222          }
1223   
1224          if (!$user->data['is_registered'])
1225          {
1226              $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1227          }
1228          else
1229          {
1230              $user_lastmark = $user->data['user_lastmark'];
1231          }
1232   
1233          foreach ($topic_ids as $topic_id)
1234          {
1235              $topic_id36 = base_convert($topic_id, 10, 36);
1236   
1237              if (isset($tracking_topics['t'][$topic_id36]))
1238              {
1239                  $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1240              }
1241          }
1242   
1243          $topic_ids = array_diff($topic_ids, array_keys($last_read));
1244   
1245          if (sizeof($topic_ids))
1246          {
1247              $mark_time = array();
1248              if ($global_announce_list && sizeof($global_announce_list))
1249              {
1250                  if (isset($tracking_topics['f'][0]))
1251                  {
1252                      $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1253                  }
1254              }
1255   
1256              if (isset($tracking_topics['f'][$forum_id]))
1257              {
1258                  $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1259              }
1260   
1261              $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1262   
1263              foreach ($topic_ids as $topic_id)
1264              {
1265                  if ($global_announce_list && isset($global_announce_list[$topic_id]))
1266                  {
1267                      $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1268                  }
1269                  else
1270                  {
1271                      $last_read[$topic_id] = $user_lastmark;
1272                  }
1273              }
1274          }
1275      }
1276   
1277      return $last_read;
1278  }
1279   
1280  /**
1281  * Check for read forums and update topic tracking info accordingly
1282  *
1283  * @param int $forum_id the forum id to check
1284  * @param int $forum_last_post_time the forums last post time
1285  * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1286  * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1287  *
1288  * @return true if complete forum got marked read, else false.
1289  */
1290  function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1291  {
1292      global $db, $tracking_topics, $user, $config;
1293   
1294      // Determine the users last forum mark time if not given.
1295      if ($mark_time_forum === false)
1296      {
1297          if ($config['load_db_lastread'] && $user->data['is_registered'])
1298          {
1299              $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1300          }
1301          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1302          {
1303              $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1304              $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
1305   
1306              if (!$user->data['is_registered'])
1307              {
1308                  $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1309              }
1310   
1311              $mark_time_forum = (isset($tracking_topics['f'][$forum_id])) ? (int) (base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate']) : $user->data['user_lastmark'];
1312          }
1313      }
1314   
1315      // Check the forum for any left unread topics.
1316      // If there are none, we mark the forum as read.
1317      if ($config['load_db_lastread'] && $user->data['is_registered'])
1318      {
1319          if ($mark_time_forum >= $forum_last_post_time)
1320          {
1321              // We do not need to mark read, this happened before. Therefore setting this to true
1322              $row = true;
1323          }
1324          else
1325          {
1326              $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1327                  LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1328                  WHERE t.forum_id = ' . $forum_id . '
1329                      AND t.topic_last_post_time > ' . $mark_time_forum . '
1330                      AND t.topic_moved_id = 0
1331                      AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time)
1332                  GROUP BY t.forum_id';
1333              $result = $db->sql_query_limit($sql, 1);
1334              $row = $db->sql_fetchrow($result);
1335              $db->sql_freeresult($result);
1336          }
1337      }
1338      else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1339      {
1340          // Get information from cookie
1341          $row = false;
1342   
1343          if (!isset($tracking_topics['tf'][$forum_id]))
1344          {
1345              // We do not need to mark read, this happened before. Therefore setting this to true
1346              $row = true;
1347          }
1348          else
1349          {
1350              $sql = 'SELECT topic_id
1351                  FROM ' . TOPICS_TABLE . '
1352                  WHERE forum_id = ' . $forum_id . '
1353                      AND topic_last_post_time > ' . $mark_time_forum . '
1354                      AND topic_moved_id = 0';
1355              $result = $db->sql_query($sql);
1356   
1357              $check_forum = $tracking_topics['tf'][$forum_id];
1358              $unread = false;
1359   
1360              while ($row = $db->sql_fetchrow($result))
1361              {
1362                  if (!in_array(base_convert($row['topic_id'], 10, 36), array_keys($check_forum)))
1363                  {
1364                      $unread = true;
1365                      break;
1366                  }
1367              }
1368              $db->sql_freeresult($result);
1369   
1370              $row = $unread;
1371          }
1372      }
1373      else
1374      {
1375          $row = true;
1376      }
1377   
1378      if (!$row)
1379      {
1380          markread('topics', $forum_id);
1381          return true;
1382      }
1383   
1384      return false;
1385  }
1386   
1387  /**
1388  * Transform an array into a serialized format
1389  */
1390  function tracking_serialize($input)
1391  {
1392      $out = '';
1393      foreach ($input as $key => $value)
1394      {
1395          if (is_array($value))
1396          {
1397              $out .= $key . ':(' . tracking_serialize($value) . ');';
1398          }
1399          else
1400          {
1401              $out .= $key . ':' . $value . ';';
1402          }
1403      }
1404      return $out;
1405  }
1406   
1407  /**
1408  * Transform a serialized array into an actual array
1409  */
1410  function tracking_unserialize($string, $max_depth = 3)
1411  {
1412      $n = strlen($string);
1413      if ($n > 10010)
1414      {
1415          die('Invalid data supplied');
1416      }
1417      $data = $stack = array();
1418      $key = '';
1419      $mode = 0;
1420      $level = &$data;
1421      for ($i = 0; $i < $n; ++$i)
1422      {
1423          switch ($mode)
1424          {
1425              case 0:
1426                  switch ($string[$i])
1427                  {
1428                      case ':':
1429                          $level[$key] = 0;
1430                          $mode = 1;
1431                      break;
1432                      case ')':
1433                          unset($level);
1434                          $level = array_pop($stack);
1435                          $mode = 3;
1436                      break;
1437                      default:
1438                          $key .= $string[$i];
1439                  }
1440              break;
1441   
1442              case 1:
1443                  switch ($string[$i])
1444                  {
1445                      case '(':
1446                          if (sizeof($stack) >= $max_depth)
1447                          {
1448                              die('Invalid data supplied');
1449                          }
1450                          $stack[] = &$level;
1451                          $level[$key] = array();
1452                          $level = &$level[$key];
1453                          $key = '';
1454                          $mode = 0;
1455                      break;
1456                      default:
1457                          $level[$key] = $string[$i];
1458                          $mode = 2;
1459                      break;
1460                  }
1461              break;
1462              
1463              case 2:
1464                  switch ($string[$i])
1465                  {
1466                      case ')':
1467                          unset($level);
1468                          $level = array_pop($stack);
1469                          $mode = 3;
1470                      break;
1471                      case ';':
1472                          $key = '';
1473                          $mode = 0;
1474                      break;
1475                      default:
1476                          $level[$key] .= $string[$i];
1477                      break;
1478                  }
1479              break;
1480              
1481              case 3:
1482                  switch ($string[$i])
1483                  {
1484                      case ')':
1485                          unset($level);
1486                          $level = array_pop($stack);
1487                      break;
1488                      case ';':
1489                          $key = '';
1490                          $mode = 0;
1491                      break;
1492                      default:
1493                          die('Invalid data supplied');
1494                      break;
1495                  }
1496              break;
1497          }
1498      }
1499   
1500      if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
1501      {
1502          die('Invalid data supplied');
1503      }
1504      
1505      return $level;
1506  }
1507   
1508  // Pagination functions
1509   
1510  /**
1511  * Pagination routine, generates page number sequence
1512  * tpl_prefix is for using different pagination blocks at one page
1513  */
1514  function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
1515  {
1516      global $template, $user;
1517   
1518      // Make sure $per_page is a valid value
1519      $per_page = ($per_page <= 0) ? 1 : $per_page;
1520   
1521      $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>';
1522      $total_pages = ceil($num_items / $per_page);
1523   
1524      if ($total_pages == 1 || !$num_items)
1525      {
1526          return false;
1527      }
1528   
1529      $on_page = floor($start_item / $per_page) + 1;
1530      $url_delim = (strpos($base_url, '?') === false) ? '?' : '&amp;';
1531   
1532      $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
1533   
1534      if ($total_pages > 5)
1535      {
1536          $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
1537          $end_cnt = max(min($total_pages, $on_page + 4), 6);
1538   
1539          $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
1540   
1541          for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
1542          {
1543              $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1544              if ($i < $end_cnt - 1)
1545              {
1546                  $page_string .= $seperator;
1547              }
1548          }
1549   
1550          $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
1551      }
1552      else
1553      {
1554          $page_string .= $seperator;
1555   
1556          for ($i = 2; $i < $total_pages; $i++)
1557          {
1558              $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1559              if ($i < $total_pages)
1560              {
1561                  $page_string .= $seperator;
1562              }
1563          }
1564      }
1565   
1566      $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
1567   
1568      if ($add_prevnext_text)
1569      {
1570          if ($on_page != 1)
1571          {
1572              $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
1573          }
1574   
1575          if ($on_page != $total_pages)
1576          {
1577              $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
1578          }
1579      }
1580   
1581      $template->assign_vars(array(
1582          $tpl_prefix . 'BASE_URL'        => $base_url,
1583          'A_' . $tpl_prefix . 'BASE_URL'    => addslashes($base_url),
1584          $tpl_prefix . 'PER_PAGE'        => $per_page,
1585   
1586          $tpl_prefix . 'PREVIOUS_PAGE'    => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
1587          $tpl_prefix . 'NEXT_PAGE'        => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
1588          $tpl_prefix . 'TOTAL_PAGES'        => $total_pages,
1589      ));
1590   
1591      return $page_string;
1592  }
1593   
1594  /**
1595  * Return current page (pagination)
1596  */
1597  function on_page($num_items, $per_page, $start)
1598  {
1599      global $template, $user;
1600   
1601      // Make sure $per_page is a valid value
1602      $per_page = ($per_page <= 0) ? 1 : $per_page;
1603   
1604      $on_page = floor($start / $per_page) + 1;
1605   
1606      $template->assign_vars(array(
1607          'ON_PAGE'        => $on_page)
1608      );
1609   
1610      return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
1611  }
1612   
1613  // Server functions (building urls, redirecting...)
1614   
1615  /**
1616  * Append session id to url.
1617  * This function supports hooks.
1618  *
1619  * @param string $url The url the session id needs to be appended to (can have params)
1620  * @param mixed $params String or array of additional url parameters
1621  * @param bool $is_amp Is url using &amp; (true) or & (false)
1622  * @param string $session_id Possibility to use a custom session id instead of the global one
1623  *
1624  * Examples:
1625  * <code>
1626  * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
1627  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
1628  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
1629  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
1630  * </code>
1631  *
1632  */
1633  function append_sid($url, $params = false, $is_amp = true, $session_id = false)
1634  {
1635      global $_SID, $_EXTRA_URL, $phpbb_hook;
1636   
1637      // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropiatly.
1638      // They could mimick most of what is within this function
1639      if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
1640      {
1641          if ($phpbb_hook->hook_return(__FUNCTION__))
1642          {
1643              return $phpbb_hook->hook_return_result(__FUNCTION__);
1644          }
1645      }
1646   
1647      // Assign sid if session id is not specified
1648      if ($session_id === false)
1649      {
1650          $session_id = $_SID;
1651      }
1652   
1653      $amp_delim = ($is_amp) ? '&amp;' : '&';
1654      $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
1655   
1656      // Appending custom url parameter?
1657      $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
1658   
1659      $anchor = '';
1660      if (strpos($url, '#') !== false)
1661      {
1662          list($url, $anchor) = explode('#', $url, 2);
1663          $anchor = '#' . $anchor;
1664      }
1665      else if (!is_array($params) && strpos($params, '#') !== false)
1666      {
1667          list($params, $anchor) = explode('#', $params, 2);
1668          $anchor = '#' . $anchor;
1669      }
1670   
1671      // Use the short variant if possible ;)
1672      if ($params === false)
1673      {
1674          // Append session id
1675          if (!$session_id)
1676          {
1677              return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
1678          }
1679          else
1680          {
1681              return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
1682          }
1683      }
1684   
1685      // Build string if parameters are specified as array
1686      if (is_array($params))
1687      {
1688          $output = array();
1689   
1690          foreach ($params as $key => $item)
1691          {
1692              if ($item === NULL)
1693              {
1694                  continue;
1695              }
1696   
1697              if ($key == '#')
1698              {
1699                  $anchor = '#' . $item;
1700                  continue;
1701              }
1702   
1703              $output[] = $key . '=' . $item;
1704          }
1705   
1706          $params = implode($amp_delim, $output);
1707      }
1708   
1709      // Append session id and parameters (even if they are empty)
1710      // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
1711      return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
1712  }
1713   
1714  /**
1715  * Generate board url (example: http://www.example.com/phpBB)
1716  * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
1717  */
1718  function generate_board_url($without_script_path = false)
1719  {
1720      global $config, $user;
1721   
1722      $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
1723      $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1724   
1725      // Forcing server vars is the only way to specify/override the protocol
1726      if ($config['force_server_vars'] || !$server_name)
1727      {
1728          $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
1729          $server_name = $config['server_name'];
1730          $server_port = (int) $config['server_port'];
1731          $script_path = $config['script_path'];
1732   
1733          $url = $server_protocol . $server_name;
1734      }
1735      else
1736      {
1737          // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
1738          $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
1739          $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
1740   
1741          $script_path = $user->page['root_script_path'];
1742      }
1743   
1744      if ($server_port && (($config['cookie_secure'] && $server_port <> 443) || (!$config['cookie_secure'] && $server_port <> 80)))
1745      {
1746          $url .= ':' . $server_port;
1747      }
1748   
1749      if (!$without_script_path)
1750      {
1751          $url .= $script_path;
1752      }
1753   
1754      // Strip / from the end
1755      if (substr($url, -1, 1) == '/')
1756      {
1757          $url = substr($url, 0, -1);
1758      }
1759   
1760      return $url;
1761  }
1762   
1763  /**
1764  * Redirects the user to another page then exits the script nicely
1765  */
1766  function redirect($url, $return = false)
1767  {
1768      global $db, $cache, $config, $user, $phpbb_root_path;
1769   
1770      if (empty($user->lang))
1771      {
1772          $user->add_lang('common');
1773      }
1774   
1775      if (!$return)
1776      {
1777          garbage_collection();
1778      }
1779   
1780      // Make sure no &amp;'s are in, this will break the redirect
1781      $url = str_replace('&amp;', '&', $url);
1782   
1783      // Determine which type of redirect we need to handle...
1784      $url_parts = parse_url($url);
1785   
1786      if ($url_parts === false)
1787      {
1788          // Malformed url, redirect to current page...
1789          $url = generate_board_url() . '/' . $user->page['page'];
1790      }
1791      else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
1792      {
1793          // Full URL
1794      }
1795      else if ($url[0] == '/')
1796      {
1797          // Absolute uri, prepend direct url...
1798          $url = generate_board_url(true) . $url;
1799      }
1800      else
1801      {
1802          // Relative uri
1803          $pathinfo = pathinfo($url);
1804   
1805          // Is the uri pointing to the current directory?
1806          if ($pathinfo['dirname'] == '.')
1807          {
1808              $url = str_replace('./', '', $url);
1809   
1810              // Strip / from the beginning
1811              if ($url && substr($url, 0, 1) == '/')
1812              {
1813                  $url = substr($url, 1);
1814              }
1815   
1816              if ($user->page['page_dir'])
1817              {
1818                  $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
1819              }
1820              else
1821              {
1822                  $url = generate_board_url() . '/' . $url;
1823              }
1824          }
1825          else
1826          {
1827              // Used ./ before, but $phpbb_root_path is working better with urls within another root path
1828              $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
1829              $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
1830              $intersection = array_intersect_assoc($root_dirs, $page_dirs);
1831   
1832              $root_dirs = array_diff_assoc($root_dirs, $intersection);
1833              $page_dirs = array_diff_assoc($page_dirs, $intersection);
1834   
1835              $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
1836   
1837              // Strip / from the end
1838              if ($dir && substr($dir, -1, 1) == '/')
1839              {
1840                  $dir = substr($dir, 0, -1);
1841              }
1842   
1843              // Strip / from the beginning
1844              if ($dir && substr($dir, 0, 1) == '/')
1845              {
1846                  $dir = substr($dir, 1);
1847              }
1848   
1849              $url = str_replace($pathinfo['dirname'] . '/', '', $url);
1850   
1851              // Strip / from the beginning
1852              if (substr($url, 0, 1) == '/')
1853              {
1854                  $url = substr($url, 1);
1855              }
1856   
1857              $url = $dir . '/' . $url;
1858              $url = generate_board_url() . '/' . $url;
1859          }
1860      }
1861   
1862      // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1863      if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
1864      {
1865          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1866      }
1867   
1868      // Now, also check the protocol and for a valid url the last time...
1869      $allowed_protocols = array('http', 'https', 'ftp', 'ftps');
1870      $url_parts = parse_url($url);
1871   
1872      if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))
1873      {
1874          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1875      }
1876   
1877      if ($return)
1878      {
1879          return $url;
1880      }
1881   
1882      // Redirect via an HTML form for PITA webservers
1883      if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
1884      {
1885          header('Refresh: 0; URL=' . $url);
1886   
1887          echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
1888          echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">';
1889          echo '<head>';
1890          echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
1891          echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&amp;', $url) . '" />';
1892          echo '<title>' . $user->lang['REDIRECT'] . '</title>';
1893          echo '</head>';
1894          echo '<body>';
1895          echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&amp;', $url) . '">', '</a>') . '</div>';
1896          echo '</body>';
1897          echo '</html>';
1898   
1899          exit;
1900      }
1901   
1902      // Behave as per HTTP/1.1 spec for others
1903      header('Location: ' . $url);
1904      exit;
1905  }
1906   
1907  /**
1908  * Re-Apply session id after page reloads
1909  */
1910  function reapply_sid($url)
1911  {
1912      global $phpEx, $phpbb_root_path;
1913   
1914      if ($url === "index.$phpEx")
1915      {
1916          return append_sid("index.$phpEx");
1917      }
1918      else if ($url === "{$phpbb_root_path}index.$phpEx")
1919      {
1920          return append_sid("{$phpbb_root_path}index.$phpEx");
1921      }
1922   
1923      // Remove previously added sid
1924      if (strpos($url, '?sid=') !== false)
1925      {
1926          $url = preg_replace('/(\?)sid=[a-z0-9]+(&amp;|&)?/', '\1', $url);
1927      }
1928      else if (strpos($url, '&sid=') !== false)
1929      {
1930          $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1931      }
1932      else if (strpos($url, '&amp;sid=') !== false)
1933      {
1934          $url = preg_replace('/&amp;sid=[a-z0-9]+(&amp;)?/', '\1', $url);
1935      }
1936   
1937      return append_sid($url);
1938  }
1939   
1940  /**
1941  * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
1942  */
1943  function build_url($strip_vars = false)
1944  {
1945      global $user, $phpbb_root_path;
1946   
1947      // Append SID
1948      $redirect = append_sid($user->page['page'], false, false);
1949   
1950      // Add delimiter if not there...
1951      if (strpos($redirect, '?') === false)
1952      {
1953          $redirect .= '?';
1954      }
1955   
1956      // Strip vars...
1957      if ($strip_vars !== false && strpos($redirect, '?') !== false)
1958      {
1959          if (!is_array($strip_vars))
1960          {
1961              $strip_vars = array($strip_vars);
1962          }
1963   
1964          $query = $_query = array();
1965   
1966          $args = substr($redirect, strpos($redirect, '?') + 1);
1967          $args = ($args) ? explode('&', $args) : array();
1968          $redirect = substr($redirect, 0, strpos($redirect, '?'));
1969   
1970          foreach ($args as $argument)
1971          {
1972              $arguments = explode('=', $argument);
1973              $key = $arguments[0];
1974              unset($arguments[0]);
1975   
1976              $query[$key] = implode('=', $arguments);
1977          }
1978   
1979          // Strip the vars off
1980          foreach ($strip_vars as $strip)
1981          {
1982              if (isset($query[$strip]))
1983              {
1984                  unset($query[$strip]);
1985              }
1986          }
1987      
1988          // Glue the remaining parts together... already urlencoded
1989          foreach ($query as $key => $value)
1990          {
1991              $_query[] = $key . '=' . $value;
1992          }
1993          $query = implode('&', $_query);
1994   
1995          $redirect .= ($query) ? '?' . $query : '';
1996      }
1997   
1998      return $phpbb_root_path . str_replace('&', '&amp;', $redirect);
1999  }
2000   
2001  /**
2002  * Meta refresh assignment
2003  */
2004  function meta_refresh($time, $url)
2005  {
2006      global $template;
2007   
2008      $url = redirect($url, true);
2009   
2010      // For XHTML compatibility we change back & to &amp;
2011      $template->assign_vars(array(
2012          'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . str_replace('&', '&amp;', $url) . '" />')
2013      );
2014  }
2015   
2016  //Form validation
2017   
2018  /**
2019  * Add a secret token to the form (requires the S_FORM_TOKEN template variable)
2020  * @param string  $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
2021  */
2022  function add_form_key($form_name)
2023  {
2024      global $config, $template, $user;
2025      $now = time();
2026      $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2027      $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
2028   
2029      $s_fields = build_hidden_fields(array(
2030              'creation_time' => $now,
2031              'form_token'    => $token,
2032      ));
2033      $template->assign_vars(array(
2034              'S_FORM_TOKEN'    => $s_fields,
2035      ));
2036  }
2037   
2038  /**
2039  * Check the form key. Required for all altering actions not secured by confirm_box
2040  * @param string  $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply
2041  * @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting.
2042  * @param string $return_page The address for the return link
2043  * @param bool $trigger If true, the function will triger an error when encountering an invalid form
2044  * @param int $minimum_time The minimum acceptable age for a submitted form in seconds
2045  */
2046  function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false, $minimum_time = false)
2047  {
2048      global $config, $user;
2049   
2050      if ($timespan === false)
2051      {
2052          // we enforce a minimum value of half a minute here.
2053          $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
2054      }
2055      if ($minimum_time === false)
2056      {
2057          $minimum_time = (int) $config['form_token_mintime'];
2058      }
2059      
2060      if (isset($_POST['creation_time']) && isset($_POST['form_token']))
2061      {
2062          $creation_time    = abs(request_var('creation_time', 0));
2063          $token = request_var('form_token', '');
2064   
2065          $diff = (time() - $creation_time);
2066   
2067          if (($diff >= $minimum_time) && (($diff <= $timespan) || $timespan == -1))
2068          {
2069              $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
2070              
2071              $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
2072              if ($key === $token)
2073              {
2074                  return true;
2075              }
2076          }
2077      }
2078      if ($trigger)
2079      {
2080          trigger_error($user->lang['FORM_INVALID'] . $return_page);
2081      }
2082      return false;
2083  }
2084   
2085  // Message/Login boxes
2086   
2087  /**
2088  * Build Confirm box
2089  * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
2090  * @param string $title Title/Message used for confirm box.
2091  *        message text is _CONFIRM appended to title.
2092  *        If title cannot be found in user->lang a default one is displayed
2093  *        If title_CONFIRM cannot be found in user->lang the text given is used.
2094  * @param string $hidden Hidden variables
2095  * @param string $html_body Template used for confirm box
2096  * @param string $u_action Custom form action
2097  */
2098  function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
2099  {
2100      global $user, $template, $db;
2101      global $phpEx, $phpbb_root_path;
2102   
2103      if (isset($_POST['cancel']))
2104      {
2105          return false;
2106      }
2107   
2108      $confirm = false;
2109      if (isset($_POST['confirm']))
2110      {
2111          // language frontier
2112          if ($_POST['confirm'] === $user->lang['YES'])
2113          {
2114              $confirm = true;
2115          }
2116      }
2117   
2118      if ($check && $confirm)
2119      {
2120          $user_id = request_var('user_id', 0);
2121          $session_id = request_var('sess', '');
2122          $confirm_key = request_var('confirm_key', '');
2123   
2124          if ($user_id != $user->data['user_id'] || $session_id != $user->session_id || !$confirm_key || !$user->data['user_last_confirm_key'] || $confirm_key != $user->data['user_last_confirm_key'])
2125          {
2126              return false;
2127          }
2128   
2129          // Reset user_last_confirm_key
2130          $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
2131              WHERE user_id = " . $user->data['user_id'];
2132          $db->sql_query($sql);
2133   
2134          return true;
2135      }
2136      else if ($check)
2137      {
2138          return false;
2139      }
2140   
2141      $s_hidden_fields = build_hidden_fields(array(
2142          'user_id'    => $user->data['user_id'],
2143          'sess'        => $user->session_id,
2144          'sid'        => $user->session_id)
2145      );
2146   
2147      // generate activation key
2148      $confirm_key = gen_rand_string(10);
2149   
2150      if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2151      {
2152          adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
2153      }
2154      else
2155      {
2156          page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
2157      }
2158   
2159      $template->set_filenames(array(
2160          'body' => $html_body)
2161      );
2162   
2163      // If activation key already exist, we better do not re-use the key (something very strange is going on...)
2164      if (request_var('confirm_key', ''))
2165      {
2166          // This should not occur, therefore we cancel the operation to safe the user
2167          return false;
2168      }
2169   
2170      // re-add sid / transform & to &amp; for user->page (user->page is always using &)
2171      $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&amp;', $user->page['page']);
2172      $u_action = reapply_sid($use_page);
2173      $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&amp;') . 'confirm_key=' . $confirm_key;
2174   
2175      $template->assign_vars(array(
2176          'MESSAGE_TITLE'        => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
2177          'MESSAGE_TEXT'        => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
2178   
2179          'YES_VALUE'            => $user->lang['YES'],
2180          'S_CONFIRM_ACTION'    => $u_action,
2181          'S_HIDDEN_FIELDS'    => $hidden . $s_hidden_fields)
2182      );
2183   
2184      $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
2185          WHERE user_id = " . $user->data['user_id'];
2186      $db->sql_query($sql);
2187   
2188      if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2189      {
2190          adm_page_footer();
2191      }
2192      else
2193      {
2194          page_footer();
2195      }
2196  }
2197   
2198  /**
2199  * Generate login box or verify password
2200  */
2201  function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
2202  {
2203      global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
2204   
2205      $err = '';
2206   
2207      // Make sure user->setup() has been called
2208      if (empty($user->lang))
2209      {
2210          $user->setup();
2211      }
2212   
2213      // Print out error if user tries to authenticate as an administrator without having the privileges...
2214      if ($admin && !$auth->acl_get('a_'))
2215      {
2216          // Not authd
2217          // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2218          if ($user->data['is_registered'])
2219          {
2220              add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2221          }
2222          trigger_error('NO_AUTH_ADMIN');
2223      }
2224   
2225      if (isset($_POST['login']))
2226      {
2227          // Get credential
2228          if ($admin)
2229          {
2230              $credential = request_var('credential', '');
2231   
2232              if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
2233              {
2234                  if ($user->data['is_registered'])
2235                  {
2236                      add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2237                  }
2238                  trigger_error('NO_AUTH_ADMIN');
2239              }
2240   
2241              $password    = request_var('password_' . $credential, '', true);
2242          }
2243          else
2244          {
2245              $password    = request_var('password', '', true);
2246          }
2247   
2248          $username    = request_var('username', '', true);
2249          $autologin    = (!empty($_POST['autologin'])) ? true : false;
2250          $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
2251          $admin         = ($admin) ? 1 : 0;
2252          $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
2253   
2254          // Check if the supplied username is equal to the one stored within the database if re-authenticating
2255          if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
2256          {
2257              // We log the attempt to use a different username...
2258              add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2259              trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
2260          }
2261   
2262          // If authentication is successful we redirect user to previous page
2263          $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
2264   
2265          // If admin authentication and login, we will log if it was a success or not...
2266          // We also break the operation on the first non-success login - it could be argued that the user already knows
2267          if ($admin)
2268          {
2269              if ($result['status'] == LOGIN_SUCCESS)
2270              {
2271                  add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
2272              }
2273              else
2274              {
2275                  // Only log the failed attempt if a real user tried to.
2276                  // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
2277                  if ($user->data['is_registered'])
2278                  {
2279                      add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
2280                  }
2281              }
2282          }
2283   
2284          // The result parameter is always an array, holding the relevant information...
2285          if ($result['status'] == LOGIN_SUCCESS)
2286          {
2287              $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
2288              $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
2289              $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx" || $redirect === "index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
2290   
2291              // append/replace SID (may change during the session for AOL users)
2292              $redirect = reapply_sid($redirect);
2293   
2294              // Special case... the user is effectively banned, but we allow founders to login
2295              if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
2296              {
2297                  return;
2298              }
2299   
2300              meta_refresh(3, $redirect);
2301              trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
2302          }
2303   
2304          // Something failed, determine what...
2305          if ($result['status'] == LOGIN_BREAK)
2306          {
2307              trigger_error($result['error_msg'], E_USER_ERROR);
2308          }
2309   
2310          // Special cases... determine
2311          switch ($result['status'])
2312          {
2313              case LOGIN_ERROR_ATTEMPTS:
2314   
2315                  // Show confirm image
2316                  $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
2317                      WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
2318                          AND confirm_type = " . CONFIRM_LOGIN;
2319                  $db->sql_query($sql);
2320   
2321                  // Generate code
2322                  $code = gen_rand_string(mt_rand(5, 8));
2323                  $confirm_id = md5(unique_id($user->ip));
2324                  $seed = hexdec(substr(unique_id(), 4, 10));
2325   
2326                  // compute $seed % 0x7fffffff
2327                  $seed -= 0x7fffffff * floor($seed / 0x7fffffff);
2328   
2329                  $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
2330                      'confirm_id'    => (string) $confirm_id,
2331                      'session_id'    => (string) $user->session_id,
2332                      'confirm_type'    => (int) CONFIRM_LOGIN,
2333                      'code'            => (string) $code,
2334                      'seed'            => (int) $seed)
2335                  );
2336                  $db->sql_query($sql);
2337   
2338                  $template->assign_vars(array(
2339                      'S_CONFIRM_CODE'            => true,
2340                      'CONFIRM_ID'                => $confirm_id,
2341                      'CONFIRM_IMAGE'                => '<img src="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=confirm&amp;id=' . $confirm_id . '&amp;type=' . CONFIRM_LOGIN) . '" alt="" title="" />',
2342                      'L_LOGIN_CONFIRM_EXPLAIN'    => sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'),
2343                  ));
2344   
2345                  $err = $user->lang[$result['error_msg']];
2346   
2347              break;
2348   
2349              case LOGIN_ERROR_PASSWORD_CONVERT:
2350                  $err = sprintf(
2351                      $user->lang[$result['error_msg']],
2352                      ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '',
2353                      ($config['email_enable']) ? '</a>' : '',
2354                      ($config['board_contact']) ? '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">' : '',
2355                      ($config['board_contact']) ? '</a>' : ''
2356                  );
2357              break;
2358   
2359              // Username, password, etc...
2360              default:
2361                  $err = $user->lang[$result['error_msg']];
2362   
2363                  // Assign admin contact to some error messages
2364                  if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
2365                  {
2366                      $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
2367                  }
2368                  
2369              break;
2370          }
2371      }
2372   
2373      if (!$redirect)
2374      {
2375          // We just use what the session code determined...
2376          // If we are not within the admin directory we use the page dir...
2377          $redirect = '';
2378   
2379          if (!$admin)
2380          {
2381              $redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
2382          }
2383   
2384          $redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . htmlspecialchars($user->page['query_string']) : '');
2385      }
2386   
2387      // Assign credential for username/password pair
2388      $credential = ($admin) ? md5(unique_id()) : false;
2389   
2390      $s_hidden_fields = array(
2391          'redirect'    => $redirect,
2392          'sid'        => $user->session_id,
2393      );
2394   
2395      if ($admin)
2396      {
2397          $s_hidden_fields['credential'] = $credential;
2398      }
2399   
2400      $s_hidden_fields = build_hidden_fields($s_hidden_fields);
2401   
2402      $template->assign_vars(array(
2403          'LOGIN_ERROR'        => $err,
2404          'LOGIN_EXPLAIN'        => $l_explain,
2405   
2406          'U_SEND_PASSWORD'         => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
2407          'U_RESEND_ACTIVATION'    => ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
2408          'U_TERMS_USE'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
2409          'U_PRIVACY'                => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
2410   
2411          'S_DISPLAY_FULL_LOGIN'    => ($s_display) ? true : false,
2412          'S_LOGIN_ACTION'        => (!$admin) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id), // Needs to stay index.$phpEx because we are within the admin directory
2413          'S_HIDDEN_FIELDS'         => $s_hidden_fields,
2414   
2415          'S_ADMIN_AUTH'            => $admin,
2416          'USERNAME'                => ($admin) ? $user->data['username'] : '',
2417   
2418          'USERNAME_CREDENTIAL'    => 'username',
2419          'PASSWORD_CREDENTIAL'    => ($admin) ? 'password_' . $credential : 'password',
2420      ));
2421   
2422      page_header($user->lang['LOGIN']);
2423   
2424      $template->set_filenames(array(
2425          'body' => 'login_body.html')
2426      );
2427      make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
2428   
2429      page_footer();
2430  }
2431   
2432  /**
2433  * Generate forum login box
2434  */
2435  function login_forum_box($forum_data)
2436  {
2437      global $db, $config, $user, $template, $phpEx;
2438   
2439      $password = request_var('password', '', true);
2440   
2441      $sql = 'SELECT forum_id
2442          FROM ' . FORUMS_ACCESS_TABLE . '
2443          WHERE forum_id = ' . $forum_data['forum_id'] . '
2444              AND user_id = ' . $user->data['user_id'] . "
2445              AND session_id = '" . $db->sql_escape($user->session_id) . "'";
2446      $result = $db->sql_query($sql);
2447      $row = $db->sql_fetchrow($result);
2448      $db->sql_freeresult($result);
2449   
2450      if ($row)
2451      {
2452          return true;
2453      }
2454   
2455      if ($password)
2456      {
2457          // Remove expired authorised sessions
2458          $sql = 'SELECT f.session_id
2459              FROM ' . FORUMS_ACCESS_TABLE . ' f
2460              LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
2461              WHERE s.session_id IS NULL';
2462          $result = $db->sql_query($sql);
2463   
2464          if ($row = $db->sql_fetchrow($result))
2465          {
2466              $sql_in = array();
2467              do
2468              {
2469                  $sql_in[] = (string) $row['session_id'];
2470              }
2471              while ($row = $db->sql_fetchrow($result));
2472   
2473              // Remove expired sessions
2474              $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
2475                  WHERE ' . $db->sql_in_set('session_id', $sql_in);
2476              $db->sql_query($sql);
2477          }
2478          $db->sql_freeresult($result);
2479   
2480          if (phpbb_check_hash($password, $forum_data['forum_password']))
2481          {
2482              $sql_ary = array(
2483                  'forum_id'        => (int) $forum_data['forum_id'],
2484                  'user_id'        => (int) $user->data['user_id'],
2485                  'session_id'    => (string) $user->session_id,
2486              );
2487   
2488              $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2489   
2490              return true;
2491          }
2492   
2493          $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
2494      }
2495   
2496      page_header($user->lang['LOGIN']);
2497   
2498      $template->assign_vars(array(
2499          'S_HIDDEN_FIELDS'        => build_hidden_fields(array('f' => $forum_data['forum_id'])))
2500      );
2501   
2502      $template->set_filenames(array(
2503          'body' => 'login_forum.html')
2504      );
2505      
2506      page_footer();
2507  }
2508   
2509  // Little helpers
2510   
2511  /**
2512  * Little helper for the build_hidden_fields function
2513  */
2514  function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
2515  {
2516      $hidden_fields = '';
2517   
2518      if (!is_array($value))
2519      {
2520          $value = ($stripslashes) ? stripslashes($value) : $value;
2521          $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
2522   
2523          $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
2524      }
2525      else
2526      {
2527          foreach ($value as $_key => $_value)
2528          {
2529              $_key = ($stripslashes) ? stripslashes($_key) : $_key;
2530              $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
2531   
2532              $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
2533          }
2534      }
2535   
2536      return $hidden_fields;
2537  }
2538   
2539  /**
2540  * Build simple hidden fields from array
2541  *
2542  * @param array $field_ary an array of values to build the hidden field from
2543  * @param bool $specialchar if true, keys and values get specialchared
2544  * @param bool $stripslashes if true, keys and values get stripslashed
2545  *
2546  * @return string the hidden fields
2547  */
2548  function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
2549  {
2550      $s_hidden_fields = '';
2551   
2552      foreach ($field_ary as $name => $vars)
2553      {
2554          $name = ($stripslashes) ? stripslashes($name) : $name;
2555          $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
2556   
2557          $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
2558      }
2559   
2560      return $s_hidden_fields;
2561  }
2562   
2563  /**
2564  * Parse cfg file
2565  */
2566  function parse_cfg_file($filename, $lines = false)
2567  {
2568      $parsed_items = array();
2569   
2570      if ($lines === false)
2571      {
2572          $lines = file($filename);
2573      }
2574   
2575      foreach ($lines as $line)
2576      {
2577          $line = trim($line);
2578   
2579          if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
2580          {
2581              continue;
2582          }
2583   
2584          // Determine first occurrence, since in values the equal sign is allowed
2585          $key = strtolower(trim(substr($line, 0, $delim_pos)));
2586          $value = trim(substr($line, $delim_pos + 1));
2587   
2588          if (in_array($value, array('off', 'false', '0')))
2589          {
2590              $value = false;
2591          }
2592          else if (in_array($value, array('on', 'true', '1')))
2593          {
2594              $value = true;
2595          }
2596          else if (!trim($value))
2597          {
2598              $value = '';
2599          }
2600          else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
2601          {
2602              $value = substr($value, 1, sizeof($value)-2);
2603          }
2604      
2605          $parsed_items[$key] = $value;
2606      }
2607      
2608      return $parsed_items;
2609  }
2610   
2611  /**
2612  * Add log event
2613  */
2614  function add_log()
2615  {
2616      global $db, $user;
2617   
2618      $args = func_get_args();
2619   
2620      $mode            = array_shift($args);
2621      $reportee_id    = ($mode == 'user') ? intval(array_shift($args)) : '';
2622      $forum_id        = ($mode == 'mod') ? intval(array_shift($args)) : '';
2623      $topic_id        = ($mode == 'mod') ? intval(array_shift($args)) : '';
2624      $action            = array_shift($args);
2625      $data            = (!sizeof($args)) ? '' : serialize($args);
2626   
2627      $sql_ary = array(
2628          'user_id'        => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
2629          'log_ip'        => $user->ip,
2630          'log_time'        => time(),
2631          'log_operation'    => $action,
2632          'log_data'        => $data,
2633      );
2634      
2635      switch ($mode)
2636      {
2637          case 'admin':
2638              $sql_ary['log_type'] = LOG_ADMIN;
2639          break;
2640          
2641          case 'mod':
2642              $sql_ary += array(
2643                  'log_type'    => LOG_MOD,
2644                  'forum_id'    => $forum_id,
2645                  'topic_id'    => $topic_id
2646              );
2647          break;
2648   
2649          case 'user':
2650              $sql_ary += array(
2651                  'log_type'        => LOG_USERS,
2652                  'reportee_id'    => $reportee_id
2653              );
2654          break;
2655   
2656          case 'critical':
2657              $sql_ary['log_type'] = LOG_CRITICAL;
2658          break;
2659          
2660          default:
2661              return false;
2662      }
2663   
2664      $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2665   
2666      return $db->sql_nextid();
2667  }
2668   
2669  /**
2670  * Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
2671  */
2672  function get_backtrace()
2673  {
2674      global $phpbb_root_path;
2675   
2676      $output = '<div style="font-family: monospace;">';
2677      $backtrace = debug_backtrace();
2678      $path = phpbb_realpath($phpbb_root_path);
2679   
2680      foreach ($backtrace as $number => $trace)
2681      {
2682          // We skip the first one, because it only shows this file/function
2683          if ($number == 0)
2684          {
2685              continue;
2686          }
2687   
2688          // Strip the current directory from path
2689          if (empty($trace['file']))
2690          {
2691              $trace['file'] = '';
2692          }
2693          else
2694          {
2695              $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
2696              $trace['file'] = substr($trace['file'], 1);
2697          }
2698          $args = array();
2699   
2700          // If include/require/include_once is not called, do not show arguments - they may contain sensible information
2701          if (!in_array($trace['function'], array('include', 'require', 'include_once')))
2702          {
2703              unset($trace['args']);
2704          }
2705          else
2706          {
2707              // Path...
2708              if (!empty($trace['args'][0]))
2709              {
2710                  $argument = htmlspecialchars($trace['args'][0]);
2711                  $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
2712                  $argument = substr($argument, 1);
2713                  $args[] = "'{$argument}'";
2714              }
2715          }
2716   
2717          $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
2718          $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
2719   
2720          $output .= '<br />';
2721          $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
2722          $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />';
2723   
2724          $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
2725      }
2726      $output .= '</div>';
2727      return $output;
2728  }
2729   
2730  /**
2731  * This function returns a regular expression pattern for commonly used expressions
2732  * Use with / as delimiter for email mode and # for url modes
2733  * mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
2734  */
2735  function get_preg_expression($mode)
2736  {
2737      switch ($mode)
2738      {
2739          case 'email':
2740              return '[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*[a-z]+';
2741          break;
2742   
2743          case 'bbcode_htm':
2744              return array(
2745                  '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2746                  '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&amp;|\?)sid=[0-9a-f]{32})?">.*?</a><!\-\- l \-\->#',
2747                  '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)">.*?</a><!\-\- \1 \-\->#',
2748                  '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2749                  '#<!\-\- .*? \-\->#s',
2750                  '#<.*?>#s',
2751              );
2752          break;
2753   
2754          // Whoa these look impressive!
2755          // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses
2756          // can be found in the develop directory
2757          case 'ipv4':
2758              return '#^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$#';
2759          break;
2760   
2761          case 'ipv6':
2762              return '#^(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){5}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:))$#i';
2763          break;
2764   
2765          case 'url':
2766          case 'url_inline':
2767              $inline = ($mode == 'url') ? ')' : '';
2768              $scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..."
2769              // generated with regex generation file in the develop folder
2770              return "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2771          break;
2772   
2773          case 'www_url':
2774          case 'www_url_inline':
2775              $inline = ($mode == 'www_url') ? ')' : '';
2776              return "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2777          break;
2778   
2779          case 'relative_url':
2780          case 'relative_url_inline':
2781              $inline = ($mode == 'relative_url') ? ')' : '';
2782              return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
2783          break;
2784      }
2785   
2786      return '';
2787  }
2788   
2789  /**
2790  * Returns the first block of the specified IPv6 address and as many additional
2791  * ones as specified in the length paramater.
2792  * If length is zero, then an empty string is returned.
2793  * If length is greater than 3 the complete IP will be returned
2794  */
2795  function short_ipv6($ip, $length)
2796  {
2797      if ($length < 1)
2798      {
2799          return '';
2800      }
2801   
2802      // extend IPv6 addresses
2803      $blocks = substr_count($ip, ':') + 1;
2804      if ($blocks < 9)
2805      {
2806          $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip);
2807      }
2808      if ($ip[0] == ':')
2809      {
2810          $ip = '0000' . $ip;
2811      }
2812      if ($length < 4)
2813      {
2814          $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length));
2815      }
2816   
2817      return $ip;
2818  }
2819   
2820  /**
2821  * Wrapper for php's checkdnsrr function.
2822  *
2823  * The windows failover is from the php manual
2824  * Please make sure to check the return value for === true and === false, since NULL could
2825  * be returned too.
2826  *
2827  * @return true if entry found, false if not, NULL if this function is not supported by this environment
2828  */
2829  function phpbb_checkdnsrr($host, $type = '')
2830  {
2831      $type = (!$type) ? 'MX' : $type;
2832   
2833      if (DIRECTORY_SEPARATOR == '\\')
2834      {
2835          if (!function_exists('exec'))
2836          {
2837              return NULL;
2838          }
2839   
2840          // @exec('nslookup -retry=1 -timout=1 -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
2841          @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
2842   
2843          // If output is empty, the nslookup failed
2844          if (empty($output))
2845          {
2846              return NULL;
2847          }
2848   
2849          foreach ($output as $line)
2850          {
2851              if (!trim($line))
2852              {
2853                  continue;
2854              }
2855   
2856              // Valid records begin with host name:
2857              if (strpos($line, $host) === 0)
2858              {
2859                  return true;
2860              }
2861          }
2862   
2863          return false;
2864      }
2865      else if (function_exists('checkdnsrr'))
2866      {
2867          return (checkdnsrr($host, $type)) ? true : false;
2868      }
2869   
2870      return NULL;
2871  }
2872   
2873  // Handler, header and footer
2874   
2875  /**
2876  * Error and message handler, call with trigger_error if reqd
2877  */
2878  function msg_handler($errno, $msg_text, $errfile, $errline)
2879  {
2880      global $cache, $db, $auth, $template, $config, $user;
2881      global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text;
2882   
2883      // Do not display notices if we suppress them via @
2884      if (error_reporting() == 0)
2885      {
2886          return;
2887      }
2888   
2889      // Message handler is stripping text. In case we need it, we are possible to define long text...
2890      if (isset($msg_long_text) && $msg_long_text && !$msg_text)
2891      {
2892          $msg_text = $msg_long_text;
2893      }
2894   
2895      switch ($errno)
2896      {
2897          case E_NOTICE:
2898          case E_WARNING:
2899   
2900              // Check the error reporting level and return if the error level does not match
2901              // If DEBUG is defined the default level is E_ALL
2902              if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
2903              {
2904                  return;
2905              }
2906   
2907              if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
2908              {
2909                  // flush the content, else we get a white page if output buffering is on
2910                  if ($config['gzip_compress'])
2911                  {
2912                      if (@extension_loaded('zlib') && !headers_sent())
2913                      {
2914                          ob_flush();
2915                      }
2916                  }
2917   
2918                  // remove complete path to installation, with the risk of changing backslashes meant to be there
2919                  $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
2920                  $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
2921   
2922                  echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
2923              }
2924   
2925              return;
2926   
2927          break;
2928   
2929          case E_USER_ERROR:
2930   
2931              if (!empty($user) && !empty($user->lang))
2932              {
2933                  $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
2934                  $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
2935   
2936                  $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>');
2937                  $l_notify = '';
2938   
2939                  if (!empty($config['board_contact']))
2940                  {
2941                      $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>';
2942                  }
2943              }
2944              else
2945              {
2946                  $msg_title = 'General Error';
2947                  $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>';
2948                  $l_notify = '';
2949   
2950                  if (!empty($config['board_contact']))
2951                  {
2952                      $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
2953                  }
2954              }
2955   
2956              garbage_collection();
2957   
2958              // Try to not call the adm page data...
2959   
2960              echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2961              echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
2962              echo '<head>';
2963              echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2964              echo '<title>' . $msg_title . '</title>';
2965              echo '<style type="text/css">' . "\n" . '<!--' . "\n";
2966              echo '* { margin: 0; padding: 0; } html { font-size: 100%; height: 100%; margin-bottom: 1px; background-color: #E4EDF0; } body { font-family: "Lucida Grande", Verdana, Helvetica, Arial, sans-serif; color: #536482; background: #E4EDF0; font-size: 62.5%; margin: 0; } ';
2967              echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } ';
2968              echo '#wrap { padding: 0 20px 15px 20px; min-width: 615px; } #page-header { text-align: right; height: 40px; } #page-footer { clear: both; font-size: 1em; text-align: center; } ';
2969              echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px  #A9B8C2; } ';
2970              echo '#errorpage #page-header a { font-weight: bold; line-height: 6em; } #errorpage #content { padding: 10px; } #errorpage #content h1 { line-height: 1.2em; margin-bottom: 0; color: #DF075C; } ';
2971              echo '#errorpage #content div { margin-top: 20px; margin-bottom: 5px; border-bottom: 1px solid #CCCCCC; padding-bottom: 5px; color: #333333; font: bold 1.2em "Lucida Grande", Arial, Helvetica, sans-serif; text-decoration: none; line-height: 120%; text-align: left; } ';
2972              echo "\n" . '//-->' . "\n";
2973              echo '</style>';
2974              echo '</head>';
2975              echo '<body id="errorpage">';
2976              echo '<div id="wrap">';
2977              echo '    <div id="page-header">';
2978              echo '        ' . $l_return_index;
2979              echo '    </div>';
2980              echo '    <div id="acp">';
2981              echo '    <div class="panel">';
2982              echo '        <div id="content">';
2983              echo '            <h1>' . $msg_title . '</h1>';
2984              
2985              echo '            <div>' . $msg_text . '</div>';
2986              
2987              echo $l_notify;
2988   
2989              echo '        </div>';
2990              echo '    </div>';
2991              echo '    </div>';
2992              echo '    <div id="page-footer">';
2993              echo '        Powered by phpBB &copy; 2000, 2002, 2005, 2007 <a href="http://www.phpbb.com/">phpBB Group</a>';
2994              echo '    </div>';
2995              echo '</div>';
2996              echo '</body>';
2997              echo '</html>';
2998              
2999              exit_handler();
3000          break;
3001   
3002          case E_USER_WARNING:
3003          case E_USER_NOTICE:
3004   
3005              define('IN_ERROR_HANDLER', true);
3006   
3007              if (empty($user->data))
3008              {
3009                  $user->session_begin();
3010              }
3011   
3012              // We re-init the auth array to get correct results on login/logout
3013              $auth->acl($user->data);
3014   
3015              if (empty($user->lang))
3016              {
3017                  $user->setup();
3018              }
3019   
3020              $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
3021              $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
3022   
3023              if (!defined('HEADER_INC'))
3024              {
3025                  if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3026                  {
3027                      adm_page_header($msg_title);
3028                  }
3029                  else
3030                  {
3031                      page_header($msg_title);
3032                  }
3033              }
3034   
3035              $template->set_filenames(array(
3036                  'body' => 'message_body.html')
3037              );
3038   
3039              $template->assign_vars(array(
3040                  'MESSAGE_TITLE'        => $msg_title,
3041                  'MESSAGE_TEXT'        => $msg_text,
3042                  'S_USER_WARNING'    => ($errno == E_USER_WARNING) ? true : false,
3043                  'S_USER_NOTICE'        => ($errno == E_USER_NOTICE) ? true : false)
3044              );
3045   
3046              // We do not want the cron script to be called on error messages
3047              define('IN_CRON', true);
3048              
3049              if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
3050              {
3051                  adm_page_footer();
3052              }
3053              else
3054              {
3055                  page_footer();
3056              }
3057   
3058              exit_handler();
3059          break;
3060      }
3061   
3062      // If we notice an error not handled here we pass this back to PHP by returning false
3063      // This may not work for all php versions
3064      return false;
3065  }
3066   
3067  /**
3068  * Generate page header
3069  */
3070  function page_header($page_title = '', $display_online_list = true)
3071  {
3072      global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
3073   
3074      if (defined('HEADER_INC'))
3075      {
3076          return;
3077      }
3078      
3079      define('HEADER_INC', true);
3080   
3081      // gzip_compression
3082      if ($config['gzip_compress'])
3083      {
3084          if (@extension_loaded('zlib') && !headers_sent())
3085          {
3086              ob_start('ob_gzhandler');
3087          }
3088      }
3089   
3090      // Generate logged in/logged out status
3091      if ($user->data['user_id'] != ANONYMOUS)
3092      {
3093          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id);
3094          $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
3095      }
3096      else
3097      {
3098          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
3099          $l_login_logout = $user->lang['LOGIN'];
3100      }
3101   
3102      // Last visit date/time
3103      $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
3104   
3105      // Get users online list ... if required
3106      $l_online_users = $online_userlist = $l_online_record = '';
3107   
3108      if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
3109      {
3110          $logged_visible_online = $logged_hidden_online = $guests_online = $prev_user_id = 0;
3111          $prev_session_ip = $reading_sql = '';
3112   
3113          if (!empty($_REQUEST['f']))
3114          {
3115              $f = request_var('f', 0);
3116   
3117              $reading_sql = ' AND s.session_page ' . $db->sql_like_expression("{$db->any_char}_f_={$f}x{$db->any_char}");
3118          }
3119   
3120          // Get number of online guests
3121          if (!$config['load_online_guests'])
3122          {
3123              if ($db->sql_layer === 'sqlite')
3124              {
3125                  $sql = 'SELECT COUNT(session_ip) as num_guests
3126                      FROM (
3127                          SELECT DISTINCT s.session_ip
3128                              FROM ' . SESSIONS_TABLE . ' s
3129                              WHERE s.session_user_id = ' . ANONYMOUS . '
3130                                  AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
3131                                  $reading_sql .
3132                      ')';
3133              }
3134              else
3135              {
3136                  $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
3137                      FROM ' . SESSIONS_TABLE . ' s
3138                      WHERE s.session_user_id = ' . ANONYMOUS . '
3139                          AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) .
3140                      $reading_sql;
3141              }
3142              $result = $db->sql_query($sql);
3143              $guests_online = (int) $db->sql_fetchfield('num_guests');
3144              $db->sql_freeresult($result);
3145          }
3146   
3147          $sql = 'SELECT u.username, u.username_clean, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour, s.session_ip, s.session_viewonline
3148              FROM ' . USERS_TABLE . ' u, ' . SESSIONS_TABLE . ' s
3149              WHERE s.session_time >= ' . (time() - (intval($config['load_online_time']) * 60)) .
3150                  $reading_sql .
3151                  ((!$config['load_online_guests']) ? ' AND s.session_user_id <> ' . ANONYMOUS : '') . '
3152                  AND u.user_id = s.session_user_id
3153              ORDER BY u.username_clean ASC, s.session_ip ASC';
3154          $result = $db->sql_query($sql);
3155   
3156          while ($row = $db->sql_fetchrow($result))
3157          {
3158              // User is logged in and therefore not a guest
3159              if ($row['user_id'] != ANONYMOUS)
3160              {
3161                  // Skip multiple sessions for one user
3162                  if ($row['user_id'] != $prev_user_id)
3163                  {
3164                      if ($row['session_viewonline'])
3165                      {
3166                          $logged_visible_online++;
3167                      }
3168                      else
3169                      {
3170                          $row['username'] = '<em>' . $row['username'] . '</em>';
3171                          $logged_hidden_online++;
3172                      }
3173   
3174                      if (($row['session_viewonline']) || $auth->acl_get('u_viewonline'))
3175                      {
3176                          $user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']);
3177                          $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
3178                      }
3179                  }
3180   
3181                  $prev_user_id = $row['user_id'];
3182              }
3183              else
3184              {
3185                  // Skip multiple sessions for one user
3186                  if ($row['session_ip'] != $prev_session_ip)
3187                  {
3188                      $guests_online++;
3189                  }
3190              }
3191   
3192              $prev_session_ip = $row['session_ip'];
3193          }
3194          $db->sql_freeresult($result);
3195   
3196          if (!$online_userlist)
3197          {
3198              $online_userlist = $user->lang['NO_ONLINE_USERS'];
3199          }
3200   
3201          if (empty($_REQUEST['f']))
3202          {
3203              $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
3204          }
3205          else
3206          {
3207              $l_online = ($guests_online == 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
3208              $online_userlist = sprintf($l_online, $online_userlist, $guests_online);
3209          }
3210   
3211          $total_online_users = $logged_visible_online + $logged_hidden_online + $guests_online;
3212   
3213          if ($total_online_users > $config['record_online_users'])
3214          {
3215              set_config('record_online_users', $total_online_users, true);
3216              set_config('record_online_date', time(), true);
3217          }
3218   
3219          // Build online listing
3220          $vars_online = array(
3221              'ONLINE'    => array('total_online_users', 'l_t_user_s'),
3222              'REG'        => array('logged_visible_online', 'l_r_user_s'),
3223              'HIDDEN'    => array('logged_hidden_online', 'l_h_user_s'),
3224              'GUEST'        => array('guests_online', 'l_g_user_s')
3225          );
3226   
3227          foreach ($vars_online as $l_prefix => $var_ary)
3228          {
3229              switch (${$var_ary[0]})
3230              {
3231                  case 0:
3232                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL'];
3233                  break;
3234   
3235                  case 1:
3236                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL'];
3237                  break;
3238   
3239                  default:
3240                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL'];
3241                  break;
3242              }
3243          }
3244          unset($vars_online);
3245   
3246          $l_online_users = sprintf($l_t_user_s, $total_online_users);
3247          $l_online_users .= sprintf($l_r_user_s, $logged_visible_online);
3248          $l_online_users .= sprintf($l_h_user_s, $logged_hidden_online);
3249          $l_online_users .= sprintf($l_g_user_s, $guests_online);
3250   
3251          $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
3252   
3253          $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
3254          $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
3255      }
3256      else
3257      {
3258          $l_online_time = '';
3259      }
3260   
3261      $l_privmsgs_text = $l_privmsgs_text_unread = '';
3262      $s_privmsg_new = false;
3263   
3264      // Obtain number of new private messages if user is logged in
3265      if (isset($user->data['is_registered']) && $user->data['is_registered'])
3266      {
3267          if ($user->data['user_new_privmsg'])
3268          {
3269              $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
3270              $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
3271   
3272              if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
3273              {
3274                  $sql = 'UPDATE ' . USERS_TABLE . '
3275                      SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
3276                      WHERE user_id = ' . $user->data['user_id'];
3277                  $db->sql_query($sql);
3278   
3279                  $s_privmsg_new = true;
3280              }
3281              else
3282              {
3283                  $s_privmsg_new = false;
3284              }
3285          }
3286          else
3287          {
3288              $l_privmsgs_text = $user->lang['NO_NEW_PM'];
3289              $s_privmsg_new = false;
3290          }
3291   
3292          $l_privmsgs_text_unread = '';
3293   
3294          if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
3295          {
3296              $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
3297              $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
3298          }
3299      }
3300   
3301      // Which timezone?
3302      $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
3303      
3304      // The following assigns all _common_ variables that may be used at any point in a template.
3305      $template->assign_vars(array(
3306          'SITENAME'                        => $config['sitename'],
3307          'SITE_DESCRIPTION'                => $config['site_desc'],
3308          'PAGE_TITLE'                    => $page_title,
3309          'SCRIPT_NAME'                    => str_replace('.' . $phpEx, '', $user->page['page_name']),
3310          'LAST_VISIT_DATE'                => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
3311          'LAST_VISIT_YOU'                => $s_last_visit,
3312          'CURRENT_TIME'                    => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
3313          'TOTAL_USERS_ONLINE'            => $l_online_users,
3314          'LOGGED_IN_USER_LIST'            => $online_userlist,
3315          'RECORD_USERS'                    => $l_online_record,
3316          'PRIVATE_MESSAGE_INFO'            => $l_privmsgs_text,
3317          'PRIVATE_MESSAGE_INFO_UNREAD'    => $l_privmsgs_text_unread,
3318   
3319          'S_USER_NEW_PRIVMSG'            => $user->data['user_new_privmsg'],
3320          'S_USER_UNREAD_PRIVMSG'            => $user->data['user_unread_privmsg'],
3321   
3322          'SID'                => $SID,
3323          '_SID'                => $_SID,
3324          'SESSION_ID'        => $user->session_id,
3325          'ROOT_PATH'            => $phpbb_root_path,
3326   
3327          'L_LOGIN_LOGOUT'    => $l_login_logout,
3328          'L_INDEX'            => $user->lang['FORUM_INDEX'],
3329          'L_ONLINE_EXPLAIN'    => $l_online_time,
3330   
3331          'U_PRIVATEMSGS'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3332          'U_RETURN_INBOX'        => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3333          'U_POPUP_PM'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup'),
3334          'UA_POPUP_PM'            => addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup')),
3335          'U_MEMBERLIST'            => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3336          'U_MEMBERSLIST'            => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3337          'U_VIEWONLINE'            => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '',
3338          'U_LOGIN_LOGOUT'        => $u_login_logout,
3339          'U_INDEX'                => append_sid("{$phpbb_root_path}index.$phpEx"),
3340          'U_SEARCH'                => append_sid("{$phpbb_root_path}search.$phpEx"),
3341          'U_REGISTER'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
3342          'U_PROFILE'                => append_sid("{$phpbb_root_path}ucp.$phpEx"),
3343          'U_MODCP'                => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
3344          'U_FAQ'                    => append_sid("{$phpbb_root_path}faq.$phpEx"),
3345          'U_SEARCH_SELF'            => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
3346          'U_SEARCH_NEW'            => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
3347          'U_SEARCH_UNANSWERED'    => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
3348          'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
3349          'U_DELETE_COOKIES'        => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
3350          'U_TEAM'                => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
3351          'U_RESTORE_PERMISSIONS'    => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
3352   
3353          'S_USER_LOGGED_IN'        => ($user->data['user_id'] != ANONYMOUS) ? true : false,
3354          'S_AUTOLOGIN_ENABLED'    => ($config['allow_autologin']) ? true : false,
3355          'S_BOARD_DISABLED'        => ($config['board_disable']) ? true : false,
3356          'S_REGISTERED_USER'        => $user->data['is_registered'],
3357          'S_IS_BOT'                => $user->data['is_bot'],
3358          'S_USER_PM_POPUP'        => $user->optionget('popuppm'),
3359          'S_USER_LANG'            => $user->lang['USER_LANG'],
3360          'S_USER_BROWSER'        => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
3361          'S_USERNAME'            => $user->data['username'],
3362          'S_CONTENT_DIRECTION'    => $user->lang['DIRECTION'],
3363          'S_CONTENT_FLOW_BEGIN'    => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right',
3364          'S_CONTENT_FLOW_END'    => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left',
3365          'S_CONTENT_ENCODING'    => 'UTF-8',
3366          'S_TIMEZONE'            => ($user->data['user_dst'] || ($user->data['user_id'] == ANONYMOUS && $config['board_dst'])) ? sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], $user->lang['tz']['dst']) : sprintf($user->lang['ALL_TIMES'], $user->lang['tz'][$tz], ''),
3367          'S_DISPLAY_ONLINE_LIST'    => ($l_online_time) ? 1 : 0,
3368          'S_DISPLAY_SEARCH'        => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
3369          'S_DISPLAY_PM'            => ($config['allow_privmsg'] && $user->data['is_registered'] && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false,
3370          'S_DISPLAY_MEMBERLIST'    => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
3371          'S_NEW_PM'                => ($s_privmsg_new) ? 1 : 0,
3372   
3373          'T_THEME_PATH'            => "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
3374          'T_TEMPLATE_PATH'        => "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
3375          'T_IMAGESET_PATH'        => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
3376          'T_IMAGESET_LANG_PATH'    => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
3377          'T_IMAGES_PATH'            => "{$phpbb_root_path}images/",
3378          'T_SMILIES_PATH'        => "{$phpbb_root_path}{$config['smilies_path']}/",
3379          'T_AVATAR_PATH'            => "{$phpbb_root_path}{$config['avatar_path']}/",
3380          'T_AVATAR_GALLERY_PATH'    => "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
3381          'T_ICONS_PATH'            => "{$phpbb_root_path}{$config['icons_path']}/",
3382          'T_RANKS_PATH'            => "{$phpbb_root_path}{$config['ranks_path']}/",
3383          'T_UPLOAD_PATH'            => "{$phpbb_root_path}{$config['upload_path']}/",
3384          'T_STYLESHEET_LINK'        => (!$user->theme['theme_storedb']) ? "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme/stylesheet.css' : "{$phpbb_root_path}style.$phpEx?sid=$user->session_id&amp;id=" . $user->theme['style_id'] . '&amp;lang=' . $user->data['user_lang'],
3385          'T_STYLESHEET_NAME'        => $user->theme['theme_name'],
3386   
3387          'SITE_LOGO_IMG'            => $user->img('site_logo'))
3388      );
3389   
3390      // application/xhtml+xml not used because of IE
3391      header('Content-type: text/html; charset=UTF-8');
3392   
3393      header('Cache-Control: private, no-cache="set-cookie"');
3394      header('Expires: 0');
3395      header('Pragma: no-cache');
3396   
3397      return;
3398  }
3399   
3400  /**
3401  * Generate page footer
3402  */
3403  function page_footer($run_cron = true)
3404  {
3405      global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
3406   
3407      // Output page creation time
3408      if (defined('DEBUG'))
3409      {
3410          $mtime = explode(' ', microtime());
3411          $totaltime = $mtime[0] + $mtime[1] - $starttime;
3412   
3413          if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
3414          {
3415              $db->sql_report('display');
3416          }
3417   
3418          $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
3419   
3420          if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
3421          {
3422              if (function_exists('memory_get_usage'))
3423              {
3424                  if ($memory_usage = memory_get_usage())
3425                  {
3426                      global $base_memory_usage;
3427                      $memory_usage -= $base_memory_usage;
3428                      $memory_usage = ($memory_usage >= 1048576) ? round((round($memory_usage / 1048576 * 100) / 100), 2) . ' ' . $user->lang['MB'] : (($memory_usage >= 1024) ? round((round($memory_usage / 1024 * 100) / 100), 2) . ' ' . $user->lang['KB'] : $memory_usage . ' ' . $user->lang['BYTES']);
3429   
3430                      $debug_output .= ' | Memory Usage: ' . $memory_usage;
3431                  }
3432              }
3433   
3434              $debug_output .= ' | <a href="' . build_url() . '&amp;explain=1">Explain</a>';
3435          }
3436      }
3437   
3438      $template->assign_vars(array(
3439          'DEBUG_OUTPUT'            => (defined('DEBUG')) ? $debug_output : '',
3440          'TRANSLATION_INFO'        => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
3441   
3442          'U_ACP' => ($auth->acl_get('a_') && $user->data['is_registered']) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
3443      );
3444   
3445      // Call cron-type script
3446      if (!defined('IN_CRON') && $run_cron && !$config['board_disable'])
3447      {
3448          $cron_type = '';
3449      
3450          if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
3451          {
3452              // Process email queue
3453              $cron_type = 'queue';
3454          }
3455          else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
3456          {
3457              // Tidy the cache
3458              $cron_type = 'tidy_cache';
3459          }
3460          else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
3461          {
3462              $cron_type = 'tidy_warnings';
3463          }
3464          else if (time() - $config['database_gc'] > $config['database_last_gc'])
3465          {
3466              // Tidy the database
3467              $cron_type = 'tidy_database';
3468          }
3469          else if (time() - $config['search_gc'] > $config['search_last_gc'])
3470          {
3471              // Tidy the search
3472              $cron_type = 'tidy_search';
3473          }
3474          else if (time() - $config['session_gc'] > $config['session_last_gc'])
3475          {
3476              $cron_type = 'tidy_sessions';
3477          }
3478   
3479          if ($cron_type)
3480          {
3481              $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />');
3482          }
3483      }
3484   
3485      $template->display('body');
3486   
3487      garbage_collection();
3488      exit_handler();
3489  }
3490   
3491  /**
3492  * Closing the cache object and the database
3493  * Cool function name, eh? We might want to add operations to it later
3494  */
3495  function garbage_collection()
3496  {
3497      global $cache, $db;
3498   
3499      // Unload cache, must be done before the DB connection if closed
3500      if (!empty($cache))
3501      {
3502          $cache->unload();
3503      }
3504   
3505      // Close our DB connection.
3506      if (!empty($db))
3507      {
3508          $db->sql_close();
3509      }
3510  }
3511   
3512  /**
3513  * Handler for exit calls in phpBB.
3514  * This function supports hooks.
3515  *
3516  * Note: This function is called after the template has been outputted.
3517  */
3518  function exit_handler()
3519  {
3520      global $phpbb_hook;
3521   
3522      if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
3523      {
3524          if ($phpbb_hook->hook_return(__FUNCTION__))
3525          {
3526              return $phpbb_hook->hook_return_result(__FUNCTION__);
3527          }
3528      }
3529   
3530      exit;
3531  }
3532   
3533  /**
3534  * Handler for init calls in phpBB. This function is called in user::setup();
3535  * This function supports hooks.
3536  */
3537  function phpbb_user_session_handler()
3538  {
3539      global $phpbb_hook;
3540   
3541      if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
3542      {
3543          if ($phpbb_hook->hook_return(__FUNCTION__))
3544          {
3545              return $phpbb_hook->hook_return_result(__FUNCTION__);
3546          }
3547      }
3548   
3549      return;
3550  }
3551   
3552  ?>