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  ?>