[ Index ]

PHP Cross Reference of phpBB 3.0 Beta 3

title

Body

[close]

/includes/ -> functions.php (source)

   1  <?php
   2  /** 
   3  *
   4  * @package phpBB3
   5  * @version $Id: functions.php,v 1.487 2006/11/12 19:45:36 davidmj Exp $
   6  * @copyright (c) 2005 phpBB Group 
   7  * @license http://opensource.org/licenses/gpl-license.php GNU Public License 
   8  *
   9  */
  10  
  11  // Common global functions
  12  
  13  /**
  14  * set_var
  15  *
  16  * Set variable, used by {@link request_var the request_var function}
  17  *
  18  * @access private
  19  */
  20  function set_var(&$result, $var, $type, $multibyte = false)
  21  {
  22      settype($var, $type);
  23      $result = $var;
  24  
  25      if ($type == 'string')
  26      {
  27          $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r"), array("\n", "\n"), $result), ENT_COMPAT, 'UTF-8'));
  28  
  29          if (!empty($result))
  30          {
  31              // Make sure multibyte characters are wellformed
  32              if ($multibyte)
  33              {
  34                  if (!preg_match('/^./u', $result))
  35                  {
  36                      $result = '';
  37                  }
  38              }
  39              else
  40              {
  41                  // no multibyte, allow only ASCII (0-127)
  42                  $result = preg_replace('/[\x80-\xFF]/', '?', $result);
  43              }
  44          }
  45  
  46          $result = (STRIP) ? stripslashes($result) : $result;
  47      }
  48  }
  49  
  50  /**
  51  * request_var
  52  *
  53  * Used to get passed variable
  54  */
  55  function request_var($var_name, $default, $multibyte = false, $cookie = false)
  56  {
  57      if (!$cookie && isset($_COOKIE[$var_name]))
  58      {
  59          if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
  60          {
  61              return (is_array($default)) ? array() : $default;
  62          }
  63          $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
  64      }
  65  
  66      if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
  67      {
  68          return (is_array($default)) ? array() : $default;
  69      }
  70  
  71      $var = $_REQUEST[$var_name];
  72      if (!is_array($default))
  73      {
  74          $type = gettype($default);
  75      }
  76      else
  77      {
  78          list($key_type, $type) = each($default);
  79          $type = gettype($type);
  80          $key_type = gettype($key_type);
  81      }
  82  
  83      if (is_array($var))
  84      {
  85          $_var = $var;
  86          $var = array();
  87  
  88          foreach ($_var as $k => $v)
  89          {
  90              if (is_array($v))
  91              {
  92                  foreach ($v as $_k => $_v)
  93                  {
  94                      set_var($k, $k, $key_type);
  95                      set_var($_k, $_k, $key_type);
  96                      set_var($var[$k][$_k], $_v, $type, $multibyte);
  97                  }
  98              }
  99              else
 100              {
 101                  set_var($k, $k, $key_type);
 102                  set_var($var[$k], $v, $type, $multibyte);
 103              }
 104          }
 105      }
 106      else
 107      {
 108          set_var($var, $var, $type, $multibyte);
 109      }
 110  
 111      return $var;
 112  }
 113  
 114  /**
 115  * Set config value. Creates missing config entry.
 116  */
 117  function set_config($config_name, $config_value, $is_dynamic = false)
 118  {
 119      global $db, $cache, $config;
 120  
 121      $sql = 'UPDATE ' . CONFIG_TABLE . "
 122          SET config_value = '" . $db->sql_escape($config_value) . "'
 123          WHERE config_name = '" . $db->sql_escape($config_name) . "'";
 124      $db->sql_query($sql);
 125  
 126      if (!$db->sql_affectedrows() && !isset($config[$config_name]))
 127      {
 128          $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
 129              'config_name'    => $config_name,
 130              'config_value'    => $config_value,
 131              'is_dynamic'    => ($is_dynamic) ? 1 : 0));
 132          $db->sql_query($sql);
 133      }
 134  
 135      $config[$config_name] = $config_value;
 136  
 137      if (!$is_dynamic)
 138      {
 139          $cache->destroy('config');
 140      }
 141  }
 142  
 143  /**
 144  * Generates an alphanumeric random string of given length
 145  */
 146  function gen_rand_string($num_chars = 8)
 147  {
 148      $rand_str = unique_id();
 149      $rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
 150  
 151      return substr($rand_str, 0, $num_chars);
 152  }
 153  
 154  /**
 155  * Return unique id
 156  * @param $extra additional entropy
 157  */
 158  function unique_id($extra = 'c')
 159  {
 160      static $dss_seeded = false;
 161      global $config;
 162  
 163      $val = $config['rand_seed'] . microtime();
 164      $val = md5($val);
 165      $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
 166  
 167      if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
 168      {
 169          set_config('rand_seed', $config['rand_seed'], true);
 170          set_config('rand_seed_last_update', time(), true);
 171          $dss_seeded = true;
 172      }
 173  
 174      return substr($val, 4, 16);
 175  }
 176  
 177  /**
 178  * Generate sort selection fields
 179  */
 180  function gen_sort_selects(&$limit_days, &$sort_by_text, &$sort_days, &$sort_key, &$sort_dir, &$s_limit_days, &$s_sort_key, &$s_sort_dir, &$u_sort_param)
 181  {
 182      global $user;
 183  
 184      $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
 185  
 186      // Check if the key is selectable. If not, we reset to the first key found.
 187      // This ensures the values are always valid.
 188      if (!isset($limit_days[$sort_days]))
 189      {
 190          @reset($limit_days);
 191          $sort_days = key($limit_days);
 192      }
 193  
 194      if (!isset($sort_by_text[$sort_key]))
 195      {
 196          @reset($sort_by_text);
 197          $sort_key = key($sort_by_text);
 198      }
 199  
 200      if (!isset($sort_dir_text[$sort_dir]))
 201      {
 202          @reset($sort_dir_text);
 203          $sort_dir = key($sort_dir_text);
 204      }
 205  
 206      $s_limit_days = '<select name="st">';
 207      foreach ($limit_days as $day => $text)
 208      {
 209          $selected = ($sort_days == $day) ? ' selected="selected"' : '';
 210          $s_limit_days .= '<option value="' . $day . '"' . $selected . '>' . $text . '</option>';
 211      }
 212      $s_limit_days .= '</select>';
 213  
 214      $s_sort_key = '<select name="sk">';
 215      foreach ($sort_by_text as $key => $text)
 216      {
 217          $selected = ($sort_key == $key) ? ' selected="selected"' : '';
 218          $s_sort_key .= '<option value="' . $key . '"' . $selected . '>' . $text . '</option>';
 219      }
 220      $s_sort_key .= '</select>';
 221  
 222      $s_sort_dir = '<select name="sd">';
 223      foreach ($sort_dir_text as $key => $value)
 224      {
 225          $selected = ($sort_dir == $key) ? ' selected="selected"' : '';
 226          $s_sort_dir .= '<option value="' . $key . '"' . $selected . '>' . $value . '</option>';
 227      }
 228      $s_sort_dir .= '</select>';
 229  
 230      $u_sort_param = "st=$sort_days&amp;sk=$sort_key&amp;sd=$sort_dir";
 231  
 232      return;
 233  }
 234  
 235  /**
 236  * Generate Jumpbox
 237  */
 238  function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false)
 239  {
 240      global $config, $auth, $template, $user, $db, $phpEx;
 241  
 242      if (!$config['load_jumpbox'])
 243      {
 244          return;
 245      }
 246  
 247      $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
 248          FROM ' . FORUMS_TABLE . '
 249          ORDER BY left_id ASC';
 250      $result = $db->sql_query($sql, 600);
 251  
 252      $right = $padding = 0;
 253      $padding_store = array('0' => 0);
 254      $display_jumpbox = false;
 255      $iteration = 0;
 256  
 257      // Sometimes it could happen that forums will be displayed here not be displayed within the index page
 258      // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
 259      // If this happens, the padding could be "broken"
 260  
 261      while ($row = $db->sql_fetchrow($result))
 262      {
 263          if ($row['left_id'] < $right)
 264          {
 265              $padding++;
 266              $padding_store[$row['parent_id']] = $padding;
 267          }
 268          else if ($row['left_id'] > $right + 1)
 269          {
 270              $padding = $padding_store[$row['parent_id']];
 271          }
 272  
 273          $right = $row['right_id'];
 274  
 275          if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
 276          {
 277              // Non-postable forum with no subforums, don't display
 278              continue;
 279          }
 280  
 281          if (!$auth->acl_get('f_list', $row['forum_id']))
 282          {
 283              // if the user does not have permissions to list this forum skip
 284              continue;
 285          }
 286  
 287          if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
 288          {
 289              continue;
 290          }
 291  
 292          if (!$display_jumpbox)
 293          {
 294              $template->assign_block_vars('jumpbox_forums', array(
 295                  'FORUM_ID'        => ($select_all) ? 0 : -1,
 296                  'FORUM_NAME'    => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
 297                  'S_FORUM_COUNT'    => $iteration)
 298              );
 299  
 300              $iteration++;
 301              $display_jumpbox = true;
 302          }
 303  
 304          $template->assign_block_vars('jumpbox_forums', array(
 305              'FORUM_ID'        => $row['forum_id'],
 306              'FORUM_NAME'    => $row['forum_name'],
 307              'SELECTED'        => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
 308              'S_FORUM_COUNT'    => $iteration,
 309              'S_IS_CAT'        => ($row['forum_type'] == FORUM_CAT) ? true : false,
 310              'S_IS_LINK'        => ($row['forum_type'] == FORUM_LINK) ? true : false,
 311              'S_IS_POST'        => ($row['forum_type'] == FORUM_POST) ? true : false)
 312          );
 313  
 314          for ($i = 0; $i < $padding; $i++)
 315          {
 316              $template->assign_block_vars('jumpbox_forums.level', array());
 317          }
 318          $iteration++;
 319      }
 320      $db->sql_freeresult($result);
 321      unset($padding_store);
 322  
 323      $template->assign_vars(array(
 324          'S_DISPLAY_JUMPBOX'    => $display_jumpbox,
 325          'S_JUMPBOX_ACTION'    => $action)
 326      );
 327  
 328      return;
 329  }
 330  
 331  
 332  // Compatibility functions
 333  
 334  if (!function_exists('array_combine'))
 335  {
 336      /**
 337      * A wrapper for the PHP5 function array_combine()
 338      * @param array $keys contains keys for the resulting array
 339      * @param array $values contains values for the resulting array
 340      *
 341      * @return Returns an array by using the values from the keys array as keys and the
 342      *     values from the values array as the corresponding values. Returns false if the
 343      *     number of elements for each array isn't equal or if the arrays are empty.
 344      */
 345  	function array_combine($keys, $values)
 346      {
 347          $keys = array_values($keys);
 348          $values = array_values($values);
 349  
 350          $n = sizeof($keys);
 351          $m = sizeof($values);
 352          if (!$n || !$m || ($n != $m))
 353          {
 354              return false;
 355          }
 356  
 357          $combined = array();
 358          for ($i = 0; $i < $n; $i++)
 359          {
 360              $combined[$keys[$i]] = $values[$i];
 361          }
 362          return $combined;
 363      }
 364  }
 365  
 366  if (!function_exists('str_split'))
 367  {
 368      /**
 369      * A wrapper for the PHP5 function str_split()
 370      * @param array $string contains the string to be converted
 371      * @param array $split_length contains the length of each chunk
 372      *
 373      * @return  Converts a string to an array. If the optional split_length parameter is specified,
 374      *      the returned array will be broken down into chunks with each being split_length in length,
 375      *      otherwise each chunk will be one character in length. FALSE is returned if split_length is
 376      *      less than 1. If the split_length length exceeds the length of string, the entire string is
 377      *      returned as the first (and only) array element. 
 378      */
 379  	function str_split($string, $split_length = 1)
 380      {
 381          if ($split_length < 1)
 382          {
 383              return false;
 384          }
 385          else if ($split_length >= strlen($string))
 386          {
 387              return array($string);
 388          }
 389          else
 390          {
 391              preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
 392              return $matches[0];
 393          }
 394      }
 395  }
 396  
 397  if (!function_exists('stripos'))
 398  {
 399      /**
 400      * A wrapper for the PHP5 function stripos
 401      * Find position of first occurrence of a case-insensitive string
 402      *
 403      * @param string $haystack is the string to search in
 404      * @param string needle is the string to search for
 405      *
 406      * @return Returns the numeric position of the first occurrence of needle in the haystack  string. Unlike strpos(), stripos() is case-insensitive.
 407      * Note that the needle may be a string of one or more characters.
 408      * If needle is not found, stripos() will return boolean FALSE. 
 409      */
 410  	function stripos($haystack, $needle)
 411      {
 412          if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
 413          {
 414              return strpos($haystack, $m[0]);
 415          }
 416  
 417          return false;
 418      }
 419  }
 420  
 421  if (!function_exists('realpath'))
 422  {
 423      if (substr(PHP_OS, 0, 3) != 'WIN' && !(bool) ini_get('safe_mode') && function_exists('shell_exec') && trim(`realpath .`))
 424      {
 425          /**
 426          * @author Chris Smith <chris@project-minerva.org>
 427          * @copyright 2006 Project Minerva Team
 428          * @param string $path The path which we should attempt to resolve.
 429          * @return mixed
 430          * @ignore
 431          */
 432  		function phpbb_realpath($path)
 433          {
 434              $arg = escapeshellarg($path);
 435              return trim(`realpath '$arg'`);
 436          }
 437      }
 438      else
 439      {
 440          /**
 441          * Checks if a path ($path) is absolute or relative
 442          *
 443          * @param string $path Path to check absoluteness of
 444          * @return boolean
 445          */
 446  		function is_absolute($path)
 447          {
 448              return ($path[0] == '/' || (substr(PHP_OS, 0, 3) == 'WIN' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
 449          }
 450  
 451          /**
 452          * @author Chris Smith <chris@project-minerva.org>
 453          * @copyright 2006 Project Minerva Team
 454          * @param string $path The path which we should attempt to resolve.
 455          * @return mixed
 456          */
 457  		function phpbb_realpath($path)
 458          {
 459              // Now to perform funky shizzle
 460  
 461              // Switch to use UNIX slashes
 462              $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
 463              $path_prefix = '';
 464  
 465              // Determine what sort of path we have
 466              if (is_absolute($path))
 467              {
 468                  $absolute = true;
 469  
 470                  if ($path[0] == '/')
 471                  {
 472                      // Absolute path, *NIX style
 473                      $path_prefix = '';
 474                  }
 475                  else
 476                  {
 477                      // Absolute path, Windows style
 478                      // Remove the drive letter and colon
 479                      $path_prefix = $path[0] . ':';
 480                      $path = substr($path, 2);
 481                  }
 482              }
 483              else
 484              {
 485                  // Relative Path
 486                  // Prepend the current working directory
 487                  if (function_exists('getcwd'))
 488                  {
 489                      // This is the best method, hopefully it is enabled!
 490                      $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
 491                      $absolute = true;
 492                      if (preg_match('#^[a-z]:#i', $path))
 493                      {
 494                          $path_prefix = $path[0] . ':';
 495                          $path = substr($path, 2);
 496                      }
 497                      else
 498                      {
 499                          $path_prefix = '';
 500                      }
 501                  }
 502                  else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
 503                  {
 504                      // Warning: If chdir() has been used this will lie!
 505                      // @todo This has some problems sometime (CLI can create them easily)
 506                      $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
 507                      $absolute = true;
 508                      $path_prefix = '';
 509                  }
 510                  else
 511                  {
 512                      // We have no way of getting the absolute path, just run on using relative ones.
 513                      $absolute = false;
 514                      $path_prefix = '.';
 515                  }
 516              }
 517  
 518              // Remove any repeated slashes
 519              $path = preg_replace('#/{2,}#', '/', $path);
 520  
 521              // Remove the slashes from the start and end of the path
 522              $path = trim($path, '/');
 523  
 524              // Break the string into little bits for us to nibble on
 525              $bits = explode('/', $path);
 526  
 527              // Remove any . in the path
 528              $bits = array_diff($bits, array('.'));
 529  
 530              // Lets get looping, run over and resolve any .. (up directory)
 531              for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
 532              {
 533                  // @todo Optimise
 534                  if ($bits[$i] == '..' )
 535                  {
 536                      if (isset($bits[$i - 1]))
 537                      {
 538                          if ($bits[$i - 1] != '..')
 539                          {
 540                              // We found a .. and we are able to traverse upwards, lets do it!
 541                              unset($bits[$i]);
 542                              unset($bits[$i - 1]);
 543                              $i -= 2;
 544                              $max -= 2;
 545                              $bits = array_values($bits);
 546                          }
 547                      }
 548                      else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
 549                      {
 550                          // We have an absolute path trying to descend above the root of the filesystem
 551                          // ... Error!
 552                          return false;
 553                      }
 554                  }
 555              }
 556  
 557              // Prepend the path prefix
 558              array_unshift($bits, $path_prefix); 
 559  
 560              $resolved = '';
 561  
 562              $max = sizeof($bits) - 1;
 563  
 564              // Check if we are able to resolve symlinks, Windows cannot.
 565              $symlink_resolve = (function_exists('readlink')) ? true : false;
 566  
 567              foreach ($bits as $i => $bit)
 568              {
 569                  if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
 570                  {
 571                      // Path Exists
 572                      if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
 573                      {
 574                          // Resolved a symlink.
 575                          $resolved = $link . (($i == $max) ? '' : '/');
 576                          continue;
 577                      }
 578                  }
 579                  else
 580                  {
 581                      // Something doesn't exist here!
 582                      // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
 583                      // return false;
 584                  }
 585                  $resolved .= $bit . (($i == $max) ? '' : '/');
 586              }
 587  
 588              // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
 589              // because we must be inside that basedir, the question is where...
 590              // @internal The slash in is_dir() gets around an open_basedir restriction
 591              if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
 592              {
 593                  return false;
 594              }
 595  
 596              // Put the slashes back to the native operating systems slashes
 597              $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
 598  
 599              return $resolved; // We got here, in the end!
 600          }
 601      }
 602  }
 603  else
 604  {
 605      /**
 606      * A wrapper for realpath
 607      * @ignore
 608      */
 609  	function phpbb_realpath($path)
 610      {
 611          return realpath($path);
 612      }
 613  }
 614  
 615  if (!function_exists('htmlspecialchars_decode'))
 616  {
 617  	function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
 618      {
 619          return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
 620      }
 621  }
 622  
 623  // functions used for building option fields
 624  
 625  /**
 626  * Pick a language, any language ...
 627  */
 628  function language_select($default = '')
 629  {
 630      global $db;
 631  
 632      $sql = 'SELECT lang_iso, lang_local_name
 633          FROM ' . LANG_TABLE . '
 634          ORDER BY lang_english_name';
 635      $result = $db->sql_query($sql, 600);
 636  
 637      $lang_options = '';
 638      while ($row = $db->sql_fetchrow($result))
 639      {
 640          $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
 641          $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
 642      }
 643      $db->sql_freeresult($result);
 644  
 645      return $lang_options;
 646  }
 647  
 648  /** 
 649  * Pick a template/theme combo,
 650  */
 651  function style_select($default = '', $all = false)
 652  {
 653      global $db;
 654  
 655      $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
 656      $sql = 'SELECT style_id, style_name
 657          FROM ' . STYLES_TABLE . "
 658          $sql_where
 659          ORDER BY style_name";
 660      $result = $db->sql_query($sql);
 661  
 662      $style_options = '';
 663      while ($row = $db->sql_fetchrow($result))
 664      {
 665          $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
 666          $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
 667      }
 668      $db->sql_freeresult($result);
 669  
 670      return $style_options;
 671  }
 672  
 673  /**
 674  * Pick a timezone
 675  */
 676  function tz_select($default = '', $truncate = false)
 677  {
 678      global $sys_timezone, $user;
 679  
 680      $tz_select = '';
 681      foreach ($user->lang['tz_zones'] as $offset => $zone)
 682      {
 683          if ($truncate)
 684          {
 685              $zone = (utf8_strlen($zone) > 70) ? utf8_substr($zone, 0, 70) . '...' : $zone;
 686          }
 687  
 688          if (is_numeric($offset))
 689          {
 690              $selected = ($offset == $default) ? ' selected="selected"' : '';
 691              $tz_select .= '<option value="' . $offset . '"' . $selected . '>' . $zone . '</option>';
 692          }
 693      }
 694  
 695      return $tz_select;
 696  }
 697  
 698  // Functions handling topic/post tracking/marking
 699  
 700  /**
 701  * Marks a topic/forum as read
 702  * Marks a topic as posted to
 703  *
 704  * @param int $user_id can only be used with $mode == 'post'
 705  */
 706  function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
 707  {
 708      global $db, $user, $config;
 709  
 710      if ($mode == 'all')
 711      {
 712          if ($forum_id === false || !sizeof($forum_id))
 713          {
 714              if ($config['load_db_lastread'] && $user->data['is_registered'])
 715              {
 716                  // Mark all forums read (index page)
 717                  $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
 718                  $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
 719                  $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
 720              }
 721              else if ($config['load_anon_lastread'] || $user->data['is_registered'])
 722              {
 723                  $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
 724                  $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
 725  
 726                  unset($tracking_topics['tf']);
 727                  unset($tracking_topics['t']);
 728                  unset($tracking_topics['f']);
 729                  $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
 730      
 731                  $user->set_cookie('track', serialize($tracking_topics), time() + 31536000);
 732                  unset($tracking_topics);
 733  
 734                  if ($user->data['is_registered'])
 735                  {
 736                      $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
 737                  }
 738              }
 739          }
 740  
 741          return;
 742      }
 743      else if ($mode == 'topics')
 744      {
 745          // Mark all topics in forums read
 746          if (!is_array($forum_id))
 747          {
 748              $forum_id = array($forum_id);
 749          }
 750  
 751          // Add 0 to forums array to mark global announcements correctly
 752          $forum_id[] = 0;
 753  
 754          if ($config['load_db_lastread'] && $user->data['is_registered'])
 755          {
 756              $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . " 
 757                  WHERE user_id = {$user->data['user_id']}
 758                      AND " . $db->sql_in_set('forum_id', $forum_id);
 759              $db->sql_query($sql);
 760  
 761              $sql = 'SELECT forum_id
 762                  FROM ' . FORUMS_TRACK_TABLE . "
 763                  WHERE user_id = {$user->data['user_id']}
 764                      AND " . $db->sql_in_set('forum_id', $forum_id);
 765              $result = $db->sql_query($sql);
 766  
 767              $sql_update = array();
 768              while ($row = $db->sql_fetchrow($result))
 769              {
 770                  $sql_update[] = $row['forum_id'];
 771              }
 772              $db->sql_freeresult($result);
 773  
 774              if (sizeof($sql_update))
 775              {
 776                  $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
 777                      SET mark_time = ' . time() . "
 778                      WHERE user_id = {$user->data['user_id']}
 779                          AND " . $db->sql_in_set('forum_id', $sql_update);
 780                  $db->sql_query($sql);
 781              }
 782  
 783              if ($sql_insert = array_diff($forum_id, $sql_update))
 784              {
 785                  $sql_ary = array();
 786                  foreach ($sql_insert as $f_id)
 787                  {
 788                      $sql_ary[] = array(
 789                          'user_id'    => $user->data['user_id'],
 790                          'forum_id'    => $f_id,
 791                          'mark_time'    => time()
 792                      );
 793                  }
 794  
 795                  $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
 796              }
 797          }
 798          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
 799          {
 800              $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
 801              $tracking = ($tracking) ? unserialize($tracking) : array();
 802  
 803              foreach ($forum_id as $f_id)
 804              {
 805                  $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
 806  
 807                  if (isset($tracking['tf'][$f_id]))
 808                  {
 809                      unset($tracking['tf'][$f_id]);
 810                  }
 811  
 812                  foreach ($topic_ids36 as $topic_id36)
 813                  {
 814                      unset($tracking['t'][$topic_id36]);
 815                  }
 816  
 817                  if (isset($tracking['f'][$f_id]))
 818                  {
 819                      unset($tracking['f'][$f_id]);
 820                  }
 821  
 822                  $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
 823              }
 824  
 825              $user->set_cookie('track', serialize($tracking), time() + 31536000);
 826              unset($tracking);
 827          }
 828  
 829          return;
 830      }
 831      else if ($mode == 'topic')
 832      {
 833          if ($topic_id === false || $forum_id === false)
 834          {
 835              return;
 836          }
 837  
 838          if ($config['load_db_lastread'] && $user->data['is_registered'])
 839          {
 840              $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
 841                  SET mark_time = ' . (($post_time) ? $post_time : time()) . "
 842                  WHERE user_id = {$user->data['user_id']}
 843                      AND topic_id = $topic_id";
 844              $db->sql_query($sql);
 845  
 846              // insert row
 847              if (!$db->sql_affectedrows())
 848              {
 849                  $db->sql_return_on_error(true);
 850  
 851                  $sql_ary = array(
 852                      'user_id'        => $user->data['user_id'],
 853                      'topic_id'        => $topic_id,
 854                      'forum_id'        => (int) $forum_id,
 855                      'mark_time'        => ($post_time) ? $post_time : time(),
 856                  );
 857  
 858                  $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
 859  
 860                  $db->sql_return_on_error(false);
 861              }
 862          }
 863          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
 864          {
 865              $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
 866              $tracking = ($tracking) ? unserialize($tracking) : array();
 867  
 868              $topic_id36 = base_convert($topic_id, 10, 36);
 869  
 870              if (!isset($tracking['t'][$topic_id36]))
 871              {
 872                  $tracking['tf'][$forum_id][$topic_id36] = true;
 873              }
 874  
 875              $post_time = ($post_time) ? $post_time : time();
 876              $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
 877  
 878              // If the cookie grows larger than 10000 characters we will remove the smallest value
 879              // This can result in old topics being unread - but most of the time it should be accurate...
 880              if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
 881              {
 882                  //echo 'Cookie grown too large' . print_r($tracking, true);
 883  
 884                  // We get the ten most minimum stored time offsets and its associated topic ids
 885                  $time_keys = array();
 886                  for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
 887                  {
 888                      $min_value = min($tracking['t']);
 889                      $m_tkey = array_search($min_value, $tracking['t']);
 890                      unset($tracking['t'][$m_tkey]);
 891  
 892                      $time_keys[$m_tkey] = $min_value;
 893                  }
 894  
 895                  // Now remove the topic ids from the array...
 896                  foreach ($tracking['tf'] as $f_id => $topic_id_ary)
 897                  {
 898                      foreach ($time_keys as $m_tkey => $min_value)
 899                      {
 900                          if (isset($topic_id_ary[$m_tkey]))
 901                          {
 902                              $tracking['f'][$f_id] = $min_value;
 903                              unset($tracking['tf'][$f_id][$m_tkey]);
 904                          }
 905                      }
 906                  }
 907  
 908                  if ($user->data['is_registered'])
 909                  {
 910                      $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
 911                      $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
 912                  }
 913                  else
 914                  {
 915                      $tracking['l'] = max($time_keys);
 916                  }
 917              }
 918  
 919              $user->set_cookie('track', serialize($tracking), time() + 31536000);
 920          }
 921  
 922          return;
 923      }
 924      else if ($mode == 'post')
 925      {
 926          if ($topic_id === false)
 927          {
 928              return;
 929          }
 930  
 931          $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
 932  
 933          if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
 934          {
 935              $db->sql_return_on_error(true);
 936  
 937              $sql_ary = array(
 938                  'user_id'        => $use_user_id,
 939                  'topic_id'        => $topic_id,
 940                  'topic_posted'    => 1
 941              );
 942  
 943              $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
 944  
 945              $db->sql_return_on_error(false);
 946          }
 947  
 948          return;
 949      }
 950  }
 951  
 952  /**
 953  * Get topic tracking info by using already fetched info
 954  */
 955  function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
 956  {
 957      global $config, $user;
 958  
 959      $last_read = array();
 960  
 961      if (!is_array($topic_ids))
 962      {
 963          $topic_ids = array($topic_ids);
 964      }
 965  
 966      foreach ($topic_ids as $topic_id)
 967      {
 968          if (!empty($rowset[$topic_id]['mark_time']))
 969          {
 970              $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
 971          }
 972      }
 973  
 974      $topic_ids = array_diff($topic_ids, array_keys($last_read));
 975  
 976      if (sizeof($topic_ids))
 977      {
 978          $mark_time = array();
 979  
 980          // Get global announcement info
 981          if ($global_announce_list && sizeof($global_announce_list))
 982          {
 983              if (!isset($forum_mark_time[0]))
 984              {
 985                  global $db;
 986  
 987                  $sql = 'SELECT mark_time
 988                      FROM ' . FORUMS_TRACK_TABLE . "
 989                      WHERE user_id = {$user->data['user_id']}
 990                          AND forum_id = 0";
 991                  $result = $db->sql_query($sql);
 992                  $row = $db->sql_fetchrow($result);
 993                  $db->sql_freeresult($result);
 994  
 995                  if ($row)
 996                  {
 997                      $mark_time[0] = $row['mark_time'];
 998                  }
 999              }
1000              else
1001              {
1002                  if ($forum_mark_time[0] !== false)
1003                  {
1004                      $mark_time[0] = $forum_mark_time[0];
1005                  }
1006              }
1007          }
1008  
1009          if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
1010          {
1011              $mark_time[$forum_id] = $forum_mark_time[$forum_id];
1012          }
1013              
1014          $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1015  
1016          foreach ($topic_ids as $topic_id)
1017          {
1018              if ($global_announce_list && isset($global_announce_list[$topic_id]))
1019              {
1020                  $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1021              }
1022              else
1023              {
1024                  $last_read[$topic_id] = $user_lastmark;
1025              }
1026          }
1027      }
1028  
1029      return $last_read;
1030  }
1031  
1032  /**
1033  * Get topic tracking info from db (for cookie based tracking only this function is used)
1034  */
1035  function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
1036  {
1037      global $config, $user;
1038  
1039      $last_read = array();
1040  
1041      if (!is_array($topic_ids))
1042      {
1043          $topic_ids = array($topic_ids);
1044      }
1045  
1046      if ($config['load_db_lastread'] && $user->data['is_registered'])
1047      {
1048          global $db;
1049  
1050          $sql = 'SELECT topic_id, mark_time
1051              FROM ' . TOPICS_TRACK_TABLE . "
1052              WHERE user_id = {$user->data['user_id']}
1053                  AND " . $db->sql_in_set('topic_id', $topic_ids);
1054          $result = $db->sql_query($sql);
1055  
1056          while ($row = $db->sql_fetchrow($result))
1057          {
1058              $last_read[$row['topic_id']] = $row['mark_time'];
1059          }
1060          $db->sql_freeresult($result);
1061      
1062          $topic_ids = array_diff($topic_ids, array_keys($last_read));
1063  
1064          if (sizeof($topic_ids))
1065          {
1066              $sql = 'SELECT forum_id, mark_time 
1067                  FROM ' . FORUMS_TRACK_TABLE . "
1068                  WHERE user_id = {$user->data['user_id']}
1069                      AND forum_id " . 
1070                      (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
1071              $result = $db->sql_query($sql);
1072          
1073              $mark_time = array();
1074              while ($row = $db->sql_fetchrow($result))
1075              {
1076                  $mark_time[$row['forum_id']] = $row['mark_time'];
1077              }
1078              $db->sql_freeresult($result);
1079  
1080              $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
1081  
1082              foreach ($topic_ids as $topic_id)
1083              {
1084                  if ($global_announce_list && isset($global_announce_list[$topic_id]))
1085                  {
1086                      $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1087                  }
1088                  else
1089                  {
1090                      $last_read[$topic_id] = $user_lastmark;
1091                  }
1092              }
1093          }
1094      }
1095      else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1096      {
1097          global $tracking_topics;
1098  
1099          if (!isset($tracking_topics) || !sizeof($tracking_topics))
1100          {
1101              $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1102              $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1103          }
1104  
1105          if (!$user->data['is_registered'])
1106          {
1107              $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
1108          }
1109          else
1110          {
1111              $user_lastmark = $user->data['user_lastmark'];
1112          }
1113  
1114          foreach ($topic_ids as $topic_id)
1115          {
1116              $topic_id36 = base_convert($topic_id, 10, 36);
1117  
1118              if (isset($tracking_topics['t'][$topic_id36]))
1119              {
1120                  $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
1121              }
1122          }
1123  
1124          $topic_ids = array_diff($topic_ids, array_keys($last_read));
1125  
1126          if (sizeof($topic_ids))
1127          {
1128              $mark_time = array();
1129              if ($global_announce_list && sizeof($global_announce_list))
1130              {
1131                  if (isset($tracking_topics['f'][0]))
1132                  {
1133                      $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
1134                  }
1135              }
1136  
1137              if (isset($tracking_topics['f'][$forum_id]))
1138              {
1139                  $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
1140              }
1141  
1142              $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
1143  
1144              foreach ($topic_ids as $topic_id)
1145              {
1146                  if ($global_announce_list && isset($global_announce_list[$topic_id]))
1147                  {
1148                      $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
1149                  }
1150                  else
1151                  {
1152                      $last_read[$topic_id] = $user_lastmark;
1153                  }
1154              }
1155          }
1156      }
1157  
1158      return $last_read;
1159  }
1160  
1161  /**
1162  * Check for read forums and update topic tracking info accordingly
1163  *
1164  * @param int $forum_id the forum id to check
1165  * @param int $forum_last_post_time the forums last post time
1166  * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
1167  * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
1168  *
1169  * @return true if complete forum got marked read, else false.
1170  */
1171  function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
1172  {
1173      global $db, $tracking_topics, $user, $config;
1174  
1175      // Determine the users last forum mark time if not given.
1176      if ($mark_time_forum === false)
1177      {
1178          if ($config['load_db_lastread'] && $user->data['is_registered'])
1179          {
1180              $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
1181          }
1182          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1183          {
1184              if (!isset($tracking_topics) || !sizeof($tracking_topics))
1185              {
1186                  $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
1187                  $tracking_topics = ($tracking_topics) ? unserialize($tracking_topics) : array();
1188              }
1189  
1190              if (!$user->data['is_registered'])
1191              {
1192                  $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
1193              }
1194  
1195              $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'];
1196          }
1197      }
1198  
1199      // Check the forum for any left unread topics.
1200      // If there are none, we mark the forum as read.
1201      if ($config['load_db_lastread'] && $user->data['is_registered'])
1202      {
1203          if ($mark_time_forum >= $forum_last_post_time)
1204          {
1205              // We do not need to mark read, this happened before. Therefore setting this to true
1206              $row = true;
1207          }
1208          else
1209          {
1210              $sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
1211                  LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
1212                  WHERE t.forum_id = ' . $forum_id . '
1213                      AND t.topic_last_post_time > ' . $mark_time_forum . '
1214                      AND t.topic_moved_id = 0
1215                      AND tt.topic_id IS NULL
1216                  GROUP BY t.forum_id';
1217              $result = $db->sql_query_limit($sql, 1);
1218              $row = $db->sql_fetchrow($result);
1219              $db->sql_freeresult($result);
1220          }
1221      }
1222      else if ($config['load_anon_lastread'] || $user->data['is_registered'])
1223      {
1224          // Get information from cookie
1225          $row = false;
1226  
1227          if (!isset($tracking_topics['tf'][$forum_id]))
1228          {
1229              // We do not need to mark read, this happened before. Therefore setting this to true
1230              $row = true;
1231          }
1232          else
1233          {
1234              $sql = 'SELECT topic_id
1235                  FROM ' . TOPICS_TABLE . '
1236                  WHERE forum_id = ' . $forum_id . '
1237                      AND topic_last_post_time > ' . $mark_time_forum . '
1238                      AND topic_moved_id = 0';
1239              $result = $db->sql_query($sql);
1240  
1241              $check_forum = $tracking_topics['tf'][$forum_id];
1242              $unread = false;
1243              while ($row = $db->sql_fetchrow($result))
1244              {
1245                  if (!in_array(base_convert($row['topic_id'], 10, 36), array_keys($check_forum)))
1246                  {
1247                      $unread = true;
1248                      break;
1249                  }
1250              }
1251              $db->sql_freeresult($result);
1252  
1253              $row = $unread;
1254          }
1255      }
1256      else
1257      {
1258          $row = true;
1259      }
1260  
1261      if (!$row)
1262      {
1263          markread('topics', $forum_id);
1264          return true;
1265      }
1266  
1267      return false;
1268  }
1269  
1270  // Pagination functions
1271  
1272  /**
1273  * Pagination routine, generates page number sequence
1274  * tpl_prefix is for using different pagination blocks at one page
1275  */
1276  function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
1277  {
1278      global $template, $user;
1279  
1280      $seperator = $user->theme['pagination_sep'];
1281      $total_pages = ceil($num_items/$per_page);
1282  
1283      if ($total_pages == 1 || !$num_items)
1284      {
1285          return false;
1286      }
1287  
1288      $on_page = floor($start_item / $per_page) + 1;
1289      $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
1290  
1291      if ($total_pages > 5)
1292      {
1293          $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
1294          $end_cnt = max(min($total_pages, $on_page + 4), 6);
1295  
1296          $page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
1297  
1298          for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
1299          {
1300              $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1301              if ($i < $end_cnt - 1)
1302              {
1303                  $page_string .= $seperator;
1304              }
1305          }
1306  
1307          $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
1308      }
1309      else
1310      {
1311          $page_string .= $seperator;
1312  
1313          for ($i = 2; $i < $total_pages; $i++)
1314          {
1315              $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "&amp;start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
1316              if ($i < $total_pages)
1317              {
1318                  $page_string .= $seperator;
1319              }
1320          }
1321      }
1322  
1323      $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . '&amp;start=' . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
1324  
1325      if ($add_prevnext_text)
1326      {
1327          if ($on_page != 1) 
1328          {
1329              $page_string = '<a href="' . $base_url . '&amp;start=' . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
1330          }
1331  
1332          if ($on_page != $total_pages)
1333          {
1334              $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . '&amp;start=' . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
1335          }
1336      }
1337  
1338      $template->assign_vars(array(
1339          $tpl_prefix . 'BASE_URL'    => $base_url,
1340          $tpl_prefix . 'PER_PAGE'    => $per_page,
1341  
1342          $tpl_prefix . 'PREVIOUS_PAGE'    => ($on_page == 1) ? '' : $base_url . '&amp;start=' . (($on_page - 2) * $per_page),
1343          $tpl_prefix . 'NEXT_PAGE'        => ($on_page == $total_pages) ? '' : $base_url . '&amp;start=' . ($on_page * $per_page))
1344      );
1345  
1346      return $page_string;
1347  }
1348  
1349  /**
1350  * Return current page (pagination)
1351  */
1352  function on_page($num_items, $per_page, $start)
1353  {
1354      global $template, $user;
1355  
1356      $on_page = floor($start / $per_page) + 1;
1357  
1358      $template->assign_vars(array(
1359          'ON_PAGE'    => $on_page)
1360      );
1361  
1362      return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
1363  }
1364  
1365  // Server functions (building urls, redirecting...)
1366  
1367  /**
1368  * Append session id to url
1369  *
1370  * @param string $url The url the session id needs to be appended to (can have params)
1371  * @param mixed $params String or array of additional url parameters
1372  * @param bool $is_amp Is url using &amp; (true) or & (false)
1373  * @param string $session_id Possibility to use a custom session id instead of the global one
1374  *
1375  * Examples:
1376  * <code>
1377  * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&amp;f=2");
1378  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&amp;f=2');
1379  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
1380  * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
1381  * </code>
1382  */
1383  function append_sid($url, $params = false, $is_amp = true, $session_id = false)
1384  {
1385      global $_SID, $_EXTRA_URL;
1386  
1387      // Assign sid if session id is not specified
1388      if ($session_id === false)
1389      {
1390          $session_id = $_SID;
1391      }
1392  
1393      $amp_delim = ($is_amp) ? '&amp;' : '&';
1394      $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
1395  
1396      // Appending custom url parameter?
1397      $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
1398  
1399      // Use the short variant if possible ;)
1400      if ($params === false)
1401      {
1402          // Append session id
1403          return (!$session_id) ? $url . (($append_url) ? $url_delim . $append_url : '') : $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id;
1404      }
1405  
1406      // Build string if parameters are specified as array
1407      if (is_array($params))
1408      {
1409          $output = array();
1410  
1411          foreach ($params as $key => $item)
1412          {
1413              if ($item === NULL)
1414              {
1415                  continue;
1416              }
1417  
1418              $output[] = $key . '=' . $item;
1419          }
1420  
1421          $params = implode($amp_delim, $output);
1422      }
1423  
1424      // Append session id and parameters (even if they are empty)
1425      // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
1426      return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id);
1427  }
1428  
1429  /**
1430  * Generate board url (example: http://www.foo.bar/phpBB)
1431  * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.foo.bar)
1432  */
1433  function generate_board_url($without_script_path = false)
1434  {
1435      global $config, $user;
1436  
1437      $server_name = (!empty($_SERVER['SERVER_NAME'])) ? $_SERVER['SERVER_NAME'] : getenv('SERVER_NAME');
1438      $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
1439  
1440      // Forcing server vars is the only way to specify/override the protocol
1441      if ($config['force_server_vars'] || !$server_name)
1442      {
1443          $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
1444          $server_name = $config['server_name'];
1445          $server_port = (int) $config['server_port'];
1446  
1447          $url = $server_protocol . $server_name;
1448      }
1449      else
1450      {
1451          // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
1452          $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
1453          $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
1454      }
1455  
1456      if ($server_port && (($config['cookie_secure'] && $server_port <> 443) || (!$config['cookie_secure'] && $server_port <> 80)))
1457      {
1458          $url .= ':' . $server_port;
1459      }
1460  
1461      if ($without_script_path)
1462      {
1463          return $url;
1464      }
1465  
1466      // Strip / from the end
1467      return $url . substr($user->page['root_script_path'], 0, -1);
1468  }
1469  
1470  /**
1471  * Redirects the user to another page then exits the script nicely
1472  */
1473  function redirect($url, $return = false)
1474  {
1475      global $db, $cache, $config, $user, $phpbb_root_path;
1476  
1477      if (empty($user->lang))
1478      {
1479          $user->add_lang('common');
1480      }
1481  
1482      if (!$return)
1483      {
1484          garbage_collection();
1485      }
1486  
1487      // Make sure no &amp;'s are in, this will break the redirect
1488      $url = str_replace('&amp;', '&', $url);
1489  
1490      // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1491      if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false)
1492      {
1493          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1494      }
1495  
1496      // Determine which type of redirect we need to handle...
1497      $url_parts = parse_url($url);
1498  
1499      if ($url_parts === false)
1500      {
1501          // Malformed url, redirect to current page...
1502          $url = generate_board_url() . '/' . $user->page['page'];
1503      }
1504      else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
1505      {
1506          // Full URL
1507      }
1508      else if ($url[0] == '/')
1509      {
1510          // Absolute uri, prepend direct url...
1511          $url = generate_board_url(true) . $url;
1512      }
1513      else
1514      {
1515          // Relative uri
1516          $pathinfo = pathinfo($url);
1517  
1518          // Is the uri pointing to the current directory?
1519          if ($pathinfo['dirname'] == '.')
1520          {
1521              if ($user->page['page_dir'])
1522              {
1523                  $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . str_replace('./', '', $url);
1524              }
1525              else
1526              {
1527                  $url = generate_board_url() . '/' . str_replace('./', '', $url);
1528              }
1529          }
1530          else
1531          {
1532              // Used ./ before, but $phpbb_root_path is working better with urls within another root path
1533              $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
1534              $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
1535              $intersection = array_intersect_assoc($root_dirs, $page_dirs);
1536  
1537              $root_dirs = array_diff_assoc($root_dirs, $intersection);
1538              $page_dirs = array_diff_assoc($page_dirs, $intersection);
1539  
1540              $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
1541  
1542              if ($dir && substr($dir, -1, 1) == '/')
1543              {
1544                  $dir = substr($dir, 0, -1);
1545              }
1546  
1547              $url = $dir . '/' . str_replace($pathinfo['dirname'] . '/', '', $url);
1548              $url = generate_board_url() . '/' . $url;
1549          }
1550      }
1551  
1552      // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
1553      if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
1554      {
1555          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
1556      }
1557  
1558      if ($return)
1559      {
1560          return $url;
1561      }
1562  
1563      // Redirect via an HTML form for PITA webservers
1564      if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
1565      {
1566          header('Refresh: 0; URL=' . $url);
1567          echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html><head><meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"><meta http-equiv="refresh" content="0; url=' . $url . '"><title>Redirect</title></head><body><div align="center">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . $url . '">', '</a>') . '</div></body></html>';
1568  
1569          exit;
1570      }
1571  
1572      // Behave as per HTTP/1.1 spec for others
1573      header('Location: ' . $url);
1574      exit;
1575  }
1576  
1577  /**
1578  * Re-Apply session id after page reloads
1579  */
1580  function reapply_sid($url)
1581  {
1582      global $phpEx, $phpbb_root_path;
1583  
1584      if ($url === "index.$phpEx")
1585      {
1586          return append_sid("index.$phpEx");
1587      }
1588      else if ($url === "{$phpbb_root_path}index.$phpEx")
1589      {
1590          return append_sid("{$phpbb_root_path}index.$phpEx");
1591      }
1592  
1593      // Remove previously added sid
1594      if (strpos($url, '?sid=') !== false)
1595      {
1596          $url = preg_replace('/(\?)sid=[a-z0-9]+(&amp;|&)?/', '\1', $url);
1597      }
1598      else if (strpos($url, '&sid=') !== false)
1599      {
1600          $url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
1601      }
1602      else if (strpos($url, '&amp;sid=') !== false)
1603      {
1604          $url = preg_replace('/&amp;sid=[a-z0-9]+(&amp;)?/', '\1', $url);
1605      }
1606  
1607      return append_sid($url);
1608  }
1609  
1610  /**
1611  * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
1612  */
1613  function build_url($strip_vars = false)
1614  {
1615      global $user, $phpbb_root_path;
1616  
1617      // Append SID
1618      $redirect = (($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '') . $user->page['page_name'] . (($user->page['query_string']) ? "?{$user->page['query_string']}" : '');
1619      $redirect = append_sid($redirect, false, false);
1620  
1621      // Add delimiter if not there...
1622      if (strpos($redirect, '?') === false)
1623      {
1624          $redirect .= '?';
1625      }
1626  
1627      // Strip vars...
1628      if ($strip_vars !== false && strpos($redirect, '?') !== false)
1629      {
1630          if (!is_array($strip_vars))
1631          {
1632              $strip_vars = array($strip_vars);
1633          }
1634  
1635          $query = $_query = array();
1636  
1637          $args = substr($redirect, strpos($redirect, '?') + 1);
1638          $args = ($args) ? explode('&', $args) : array();
1639          $redirect = substr($redirect, 0, strpos($redirect, '?'));
1640  
1641          foreach ($args as $argument)
1642          {
1643              $arguments = explode('=', $argument);
1644              $key = $arguments[0];
1645              unset($arguments[0]);
1646  
1647              $query[$key] = implode('=', $arguments);
1648          }
1649  
1650          // Strip the vars off
1651          foreach ($strip_vars as $strip)
1652          {
1653              if (isset($query[$strip]))
1654              {
1655                  unset($query[$strip]);
1656              }
1657          }
1658      
1659          // Glue the remaining parts together... already urlencoded
1660          foreach ($query as $key => $value)
1661          {
1662              $_query[] = $key . '=' . $value;
1663          }
1664          $query = implode('&', $_query);
1665  
1666          $redirect .= ($query) ? '?' . $query : '';
1667      }
1668  
1669      return $phpbb_root_path . str_replace('&', '&amp;', $redirect);
1670  }
1671  
1672  /**
1673  * Meta refresh assignment
1674  */
1675  function meta_refresh($time, $url)
1676  {
1677      global $template;
1678  
1679      $url = redirect($url, true);
1680  
1681      $template->assign_vars(array(
1682          'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
1683      );
1684  }
1685  
1686  // Message/Login boxes
1687  
1688  /**
1689  * Build Confirm box
1690  * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
1691  * @param string $title Title/Message used for confirm box.
1692  *        message text is _CONFIRM appended to title. 
1693  *        If title can not be found in user->lang a default one is displayed
1694  *        If title_CONFIRM can not be found in user->lang the text given is used.
1695  * @param string $hidden Hidden variables
1696  * @param string $html_body Template used for confirm box
1697  * @param string $u_action Custom form action
1698  */
1699  function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
1700  {
1701      global $user, $template, $db;
1702      global $phpEx, $phpbb_root_path;
1703  
1704      if (isset($_POST['cancel']))
1705      {
1706          return false;
1707      }
1708  
1709      $confirm = false;
1710      if (isset($_POST['confirm']))
1711      {
1712          // language frontier
1713          if ($_POST['confirm'] == $user->lang['YES'])
1714          {
1715              $confirm = true;
1716          }
1717      }
1718  
1719      if ($check && $confirm)
1720      {
1721          $user_id = request_var('user_id', 0);
1722          $session_id = request_var('sess', '');
1723          $confirm_key = request_var('confirm_key', '');
1724  
1725          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'])
1726          {
1727              return false;
1728          }
1729  
1730          // Reset user_last_confirm_key
1731          $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
1732              WHERE user_id = " . $user->data['user_id'];
1733          $db->sql_query($sql);
1734  
1735          return true;
1736      }
1737      else if ($check)
1738      {
1739          return false;
1740      }
1741  
1742      $s_hidden_fields = build_hidden_fields(array(
1743          'user_id'    => $user->data['user_id'],
1744          'sess'        => $user->session_id,
1745          'sid'        => $user->session_id)
1746      );
1747  
1748      // generate activation key
1749      $confirm_key = gen_rand_string(10);
1750  
1751      if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1752      {
1753          adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1754      }
1755      else
1756      {
1757          page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
1758      }
1759  
1760      $template->set_filenames(array(
1761          'body' => $html_body)
1762      );
1763  
1764      // If activation key already exist, we better do not re-use the key (something very strange is going on...)
1765      if (request_var('confirm_key', ''))
1766      {
1767          // This should not occur, therefore we cancel the operation to safe the user
1768          return false;
1769      }
1770  
1771      // re-add sid / transform & to &amp; for user->page (user->page is always using &)
1772      $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&amp;', $user->page['page']);
1773      $u_action = reapply_sid($use_page);
1774      $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&amp;') . 'confirm_key=' . $confirm_key;
1775  
1776      $template->assign_vars(array(
1777          'MESSAGE_TITLE'        => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
1778          'MESSAGE_TEXT'        => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
1779  
1780          'YES_VALUE'            => $user->lang['YES'],
1781          'S_CONFIRM_ACTION'    => $u_action,
1782          'S_HIDDEN_FIELDS'    => $hidden . $s_hidden_fields)
1783      );
1784  
1785      $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
1786          WHERE user_id = " . $user->data['user_id'];
1787      $db->sql_query($sql);
1788  
1789      if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
1790      {
1791          adm_page_footer();
1792      }
1793      else
1794      {
1795          page_footer();
1796      }
1797  }
1798  
1799  /**
1800  * Generate login box or verify password
1801  */
1802  function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
1803  {
1804      global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
1805  
1806      $err = '';
1807  
1808      // Make sure user->setup() has been called
1809      if (empty($user->lang))
1810      {
1811          $user->setup();
1812      }
1813  
1814      // Print out error if user tries to authenticate as an administrator without having the privileges...
1815      if ($admin && !$auth->acl_get('a_'))
1816      {
1817          // Not authd
1818          // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1819          if ($user->data['is_registered'])
1820          {
1821              add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1822          }
1823          trigger_error('NO_AUTH_ADMIN');
1824      }
1825  
1826      if (isset($_POST['login']))
1827      {
1828          $username    = request_var('username', '', true);
1829          $password    = request_var('password', '', true);
1830          $autologin    = (!empty($_POST['autologin'])) ? true : false;
1831          $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
1832          $admin         = ($admin) ? 1 : 0;
1833  
1834          // Check if the supplied username is equal to the one stored within the database if re-authenticating
1835          if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
1836          {
1837              // We log the attempt to use a different username...
1838              add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1839              trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
1840          }
1841  
1842          // If authentication is successful we redirect user to previous page
1843          $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
1844  
1845          // If admin authentication and login, we will log if it was a success or not...
1846          // We also break the operation on the first non-success login - it could be argued that the user already knows 
1847          if ($admin)
1848          {
1849              if ($result['status'] == LOGIN_SUCCESS)
1850              {
1851                  add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
1852              }
1853              else
1854              {
1855                  // Only log the failed attempt if a real user tried to.
1856                  // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
1857                  if ($user->data['is_registered'])
1858                  {
1859                      add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
1860                  }
1861              }
1862          }
1863  
1864          // The result parameter is always an array, holding the relevant informations...
1865          if ($result['status'] == LOGIN_SUCCESS)
1866          {
1867              $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
1868              $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
1869              $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
1870  
1871              // append/replace SID (may change during the session for AOL users)
1872              $redirect = reapply_sid($redirect);
1873  
1874              meta_refresh(3, $redirect);
1875              trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
1876          }
1877  
1878          // Something failed, determine what...
1879          if ($result['status'] == LOGIN_BREAK)
1880          {
1881              trigger_error($result['error_msg'], E_USER_ERROR);
1882          }
1883  
1884          // Special cases... determine
1885          switch ($result['status'])
1886          {
1887              case LOGIN_ERROR_ATTEMPTS:
1888  
1889                  // Show confirm image
1890                  $sql = 'DELETE FROM ' . CONFIRM_TABLE . "
1891                      WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
1892                          AND confirm_type = " . CONFIRM_LOGIN;
1893                  $db->sql_query($sql);
1894  
1895                  // Generate code
1896                  $code = gen_rand_string(mt_rand(5, 8));
1897                  $confirm_id = md5(unique_id($user->ip));
1898  
1899                  $sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
1900                      'confirm_id'    => (string) $confirm_id,
1901                      'session_id'    => (string) $user->session_id,
1902                      'confirm_type'    => (int) CONFIRM_LOGIN,
1903                      'code'            => (string) $code)
1904                  );
1905                  $db->sql_query($sql);
1906  
1907                  $template->assign_vars(array(
1908                      'S_CONFIRM_CODE'            => true,
1909                      'CONFIRM_ID'                => $confirm_id,
1910                      'CONFIRM_IMAGE'                => '<img src="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=confirm&amp;id=' . $confirm_id . '&amp;type=' . CONFIRM_LOGIN) . '" alt="" title="" />',
1911                      'L_LOGIN_CONFIRM_EXPLAIN'    => sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>'),
1912                  ));
1913  
1914                  $err = $user->lang[$result['error_msg']];
1915  
1916              break;
1917  
1918              // Username, password, etc...
1919              default:
1920                  $err = $user->lang[$result['error_msg']];
1921  
1922                  // Assign admin contact to some error messages
1923                  if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
1924                  {
1925                      $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
1926                  }
1927              break;
1928          }
1929      }
1930  
1931      if (!$redirect)
1932      {
1933          // We just use what the session code determined...
1934          // If we are not within the admin directory we use the page dir...
1935          $redirect = '';
1936  
1937          if (!$admin)
1938          {
1939              $redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
1940          }
1941  
1942          $redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . $user->page['query_string'] : '');
1943      }
1944  
1945      $s_hidden_fields = build_hidden_fields(array('redirect' => $redirect, 'sid' => $user->session_id));
1946  
1947      $template->assign_vars(array(
1948          'LOGIN_ERROR'        => $err,
1949          'LOGIN_EXPLAIN'        => $l_explain,
1950  
1951          'U_SEND_PASSWORD'         => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
1952          'U_RESEND_ACTIVATION'    => ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
1953          'U_TERMS_USE'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
1954          'U_PRIVACY'                => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
1955  
1956          'S_DISPLAY_FULL_LOGIN'    => ($s_display) ? true : false,
1957          'S_AUTOLOGIN_ENABLED'    => ($config['allow_autologin']) ? true : false,
1958          '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
1959          'S_HIDDEN_FIELDS'         => $s_hidden_fields,
1960  
1961          'S_ADMIN_AUTH'            => $admin,
1962          'USERNAME'                => ($admin) ? $user->data['username'] : '')
1963      );
1964  
1965      page_header($user->lang['LOGIN']);
1966  
1967      $template->set_filenames(array(
1968          'body' => 'login_body.html')
1969      );
1970      make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
1971  
1972      page_footer();
1973  }
1974  
1975  /**
1976  * Generate forum login box
1977  */
1978  function login_forum_box($forum_data)
1979  {
1980      global $db, $config, $user, $template, $phpEx;
1981  
1982      $password = request_var('password', '', true);
1983  
1984      $sql = 'SELECT forum_id
1985          FROM ' . FORUMS_ACCESS_TABLE . '
1986          WHERE forum_id = ' . $forum_data['forum_id'] . '
1987              AND user_id = ' . $user->data['user_id'] . "
1988              AND session_id = '" . $db->sql_escape($user->session_id) . "'";
1989      $result = $db->sql_query($sql);
1990      $row = $db->sql_fetchrow($result);
1991      $db->sql_freeresult($result);
1992  
1993      if ($row)
1994      {
1995          return true;
1996      }
1997  
1998      if ($password)
1999      {
2000          // Remove expired authorised sessions
2001          $sql = 'SELECT session_id
2002              FROM ' . SESSIONS_TABLE;
2003          $result = $db->sql_query($sql);
2004  
2005          if ($row = $db->sql_fetchrow($result))
2006          {
2007              $sql_in = array();
2008              do
2009              {
2010                  $sql_in[] = (string) $row['session_id'];
2011              }
2012              while ($row = $db->sql_fetchrow($result));
2013  
2014              // Remove expired sessions
2015              $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
2016                  WHERE ' . $db->sql_in_set('session_id', $sql_in, true);
2017              $db->sql_query($sql);
2018          }
2019          $db->sql_freeresult($result);
2020  
2021          if ($password == $forum_data['forum_password'])
2022          {
2023              $sql_ary = array(
2024                  'forum_id'        => (int) $forum_data['forum_id'],
2025                  'user_id'        => (int) $user->data['user_id'],
2026                  'session_id'    => (string) $user->session_id,
2027              );
2028  
2029              $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2030  
2031              return true;
2032          }
2033  
2034          $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
2035      }
2036  
2037      page_header($user->lang['LOGIN']);
2038  
2039      $template->assign_vars(array(
2040          'S_HIDDEN_FIELDS'        => build_hidden_fields(array('f' => $forum_data['forum_id'])))
2041      );
2042  
2043      $template->set_filenames(array(
2044          'body' => 'login_forum.html')
2045      );
2046      
2047      page_footer();
2048  }
2049  
2050  // Content related functions
2051  
2052  /**
2053  * Bump Topic Check - used by posting and viewtopic
2054  */
2055  function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
2056  {
2057      global $config, $auth, $user;
2058  
2059      // Check permission and make sure the last post was not already bumped
2060      if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
2061      {
2062          return false;
2063      }
2064  
2065      // Check bump time range, is the user really allowed to bump the topic at this time?
2066      $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
2067  
2068      // Check bump time
2069      if ($last_post_time + $bump_time > time())
2070      {
2071          return false;
2072      }
2073  
2074      // Check bumper, only topic poster and last poster are allowed to bump
2075      if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'] && !$auth->acl_get('m_', $forum_id))
2076      {
2077          return false;
2078      }
2079  
2080      // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
2081      return $bump_time;
2082  }
2083  
2084  /**
2085  * Generates a text with approx. the specified length which contains the specified words and their context
2086  *
2087  * @param    string    $text    The full text from which context shall be extracted
2088  * @param    string    $words    An array of words which should be contained in the result, has to be a valid part of a PCRE pattern (escape with preg_quote!)
2089  * @param    int        $length    The desired length of the resulting text, however the result might be shorter or longer than this value
2090  *
2091  * @return    string            Context of the specified words seperated by "..."
2092  */
2093  function get_context($text, $words, $length = 400)
2094  {
2095      // first replace all whitespaces with single spaces
2096      $text = preg_replace('/\s+/', ' ', $text);
2097  
2098      $word_indizes = array();
2099      if (sizeof($words))
2100      {
2101          $match = '';
2102          // find the starting indizes of all words
2103          foreach ($words as $word)
2104          {
2105              if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
2106              {
2107                  $pos = strpos($text, $match[1]);
2108                  if ($pos !== false)
2109                  {
2110                      $word_indizes[] = $pos;
2111                  }
2112              }
2113          }
2114          unset($match);
2115  
2116          if (sizeof($word_indizes))
2117          {
2118              $word_indizes = array_unique($word_indizes);
2119              sort($word_indizes);
2120  
2121              $wordnum = sizeof($word_indizes);
2122              // number of characters on the right and left side of each word
2123              $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
2124              $final_text = '';
2125              $word = $j = 0;
2126              $final_text_index = -1;
2127  
2128              // cycle through every character in the original text
2129              for ($i = $word_indizes[$word], $n = strlen($text); $i < $n; $i++)
2130              {
2131                  // if the current position is the start of one of the words then append $sequence_length characters to the final text
2132                  if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
2133                  {
2134                      if ($final_text_index < $i - $sequence_length - 1)
2135                      {
2136                          $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', substr($text, $i - $sequence_length, $sequence_length));
2137                      }
2138                      else
2139                      {
2140                          // if the final text is already nearer to the current word than $sequence_length we only append the text
2141                          // from its current index on and distribute the unused length to all other sequenes
2142                          $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
2143                          $final_text .= substr($text, $final_text_index + 1, $i - $final_text_index - 1);
2144                      }
2145                      $final_text_index = $i - 1;
2146  
2147                      // add the following characters to the final text (see below)
2148                      $word++;
2149                      $j = 1;
2150                  }
2151  
2152                  if ($j > 0)
2153                  {
2154                      // add the character to the final text and increment the sequence counter
2155                      $final_text .= $text[$i];
2156                      $final_text_index++;
2157                      $j++;
2158  
2159                      // if this is a whitespace then check whether we are done with this sequence
2160                      if ($text[$i] == ' ')
2161                      {
2162                          // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
2163                          if ($i + 4 < $n)
2164                          {
2165                              if (($j > $sequence_length && $word >= $wordnum) || strlen($final_text) > $length)
2166                              {
2167                                  $final_text .= ' ...';
2168                                  break;
2169                              }
2170                          }
2171                          else
2172                          {
2173                              // make sure the text really reaches the end
2174                              $j -= 4;
2175                          }
2176  
2177                          // stop context generation and wait for the next word
2178                          if ($j > $sequence_length)
2179                          {
2180                              $j = 0;
2181                          }
2182                      }
2183                  }
2184              }
2185              return $final_text;
2186          }
2187      }
2188  
2189      if (!sizeof($words) || !sizeof($word_indizes))
2190      {
2191          return (strlen($text) >= $length + 3) ? substr($text, 0, $length) . '...' : $text;
2192      }
2193  }
2194  
2195  /**
2196  * Decode text whereby text is coming from the db and expected to be pre-parsed content
2197  * We are placing this outside of the message parser because we are often in need of it...
2198  */
2199  function decode_message(&$message, $bbcode_uid = '')
2200  {
2201      global $config;
2202  
2203      if ($bbcode_uid)
2204      {
2205          $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
2206          $replace = array("\n", '', '', '', '');
2207      }
2208      else
2209      {
2210          $match = array('<br />');
2211          $replace = array("\n");
2212      }
2213  
2214      $message = str_replace($match, $replace, $message);
2215  
2216      $match = get_preg_expression('bbcode_htm');
2217      $replace = array('\1', '\2', '\1', '', '');
2218  
2219      $message = preg_replace($match, $replace, $message);
2220  }
2221  
2222  /**
2223  * Strips all bbcode from a text and returns the plain content
2224  */
2225  function strip_bbcode(&$text, $uid = '')
2226  {
2227      if (!$uid)
2228      {
2229          $uid = '[0-9a-z]{5,}';
2230      }
2231  
2232      $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=.*?)?(?::[a-z])?(\:?$uid)\]#", ' ', $text);
2233  
2234      $match = get_preg_expression('bbcode_htm');
2235      $replace = array('\1', '\2', '\1', '', '');
2236      
2237      $text = preg_replace($match, $replace, $text);
2238  }
2239  
2240  /**
2241  * For display of custom parsed text on user-facing pages
2242  * Expects $text to be the value directly from the database (stored value)
2243  */
2244  function generate_text_for_display($text, $uid, $bitfield, $flags)
2245  {
2246      static $bbcode;
2247  
2248      if (!$text)
2249      {
2250          return '';
2251      }
2252  
2253      $text = str_replace("\n", '<br />', censor_text($text));
2254  
2255      // Parse bbcode if bbcode uid stored and bbcode enabled
2256      if ($uid && ($flags & OPTION_FLAG_BBCODE))
2257      {
2258          if (!class_exists('bbcode'))
2259          {
2260              global $phpbb_root_path, $phpEx;
2261              include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
2262          }
2263  
2264          if (empty($bbcode))
2265          {
2266              $bbcode = new bbcode($bitfield);
2267          }
2268          else
2269          {
2270              $bbcode->bbcode($bitfield);
2271          }
2272          
2273          $bbcode->bbcode_second_pass($text, $uid);
2274      }
2275  
2276      $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
2277  
2278      return $text;
2279  }
2280  
2281  /**
2282  * For parsing custom parsed text to be stored within the database.
2283  * This function additionally returns the uid and bitfield that needs to be stored.
2284  * Expects $text to be the value directly from request_var() and in it's non-parsed form
2285  */
2286  function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
2287  {
2288      global $phpbb_root_path, $phpEx;
2289  
2290      $uid = $bitfield = '';
2291  
2292      if (!$text)
2293      {
2294          return;
2295      }
2296  
2297      if (!class_exists('parse_message'))
2298      {
2299          include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
2300      }
2301  
2302      $message_parser = new parse_message($text);
2303      $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
2304  
2305      $text = $message_parser->message;
2306      $uid = $message_parser->bbcode_uid;
2307  
2308      // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
2309      if (!$message_parser->bbcode_bitfield)
2310      {
2311          $uid = '';
2312      }
2313  
2314      $flags = (($allow_bbcode) ? 1 : 0) + (($allow_smilies) ? 2 : 0) + (($allow_urls) ? 4 : 0);
2315      $bitfield = $message_parser->bbcode_bitfield;
2316  
2317      return;
2318  }
2319  
2320  /**
2321  * For decoding custom parsed text for edits as well as extracting the flags
2322  * Expects $text to be the value directly from the database (pre-parsed content)
2323  */
2324  function generate_text_for_edit($text, $uid, $flags)
2325  {
2326      global $phpbb_root_path, $phpEx;
2327  
2328      decode_message($text, $uid);
2329  
2330      return array(
2331          'allow_bbcode'    => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
2332          'allow_smilies'    => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
2333          'allow_urls'    => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
2334          'text'            => $text
2335      );
2336  }
2337  
2338  /**
2339  * make_clickable function
2340  *
2341  * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
2342  * Cuts down displayed size of link if over 50 chars, turns absolute links
2343  * into relative versions when the server/script path matches the link
2344  */
2345  function make_clickable($text, $server_url = false)
2346  {
2347      if ($server_url === false)
2348      {
2349          $server_url = generate_board_url();
2350      }
2351  
2352      static $magic_url_match;
2353      static $magic_url_replace;
2354  
2355      if (!is_array($magic_url_match))
2356      {
2357          $magic_url_match = $magic_url_replace = array();
2358          // Be sure to not let the matches cross over. ;)
2359  
2360          // relative urls for this board
2361          $magic_url_match[] = '#(^|[\n ]|\()(' . preg_quote($server_url, '#') . ')/(([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2362          $magic_url_replace[] = "'\$1<!-- l --><a href=\"\$2/' . preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}/', '\\1', '\$3') . '\">' . preg_replace('/(&amp;|\?)sid=[0-9a-f]{32}/', '\\1', '\$3') . '</a><!-- l -->'";
2363  
2364          // matches a xxxx://aaaaa.bbb.cccc. ...
2365          $magic_url_match[] = '#(^|[\n ]|\()([\w]+:/{2}.*?([^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2366          $magic_url_replace[] = "'\$1<!-- m --><a href=\"\$2\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- m -->'";
2367  
2368          // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
2369          $magic_url_match[] = '#(^|[\n ]|\()(w{3}\.[\w\-]+\.[\w\-.\~]+(?:[^[ \t\n\r<"\'\)&]+|&(?!lt;|quot;))*)#ie';
2370          $magic_url_replace[] = "'\$1<!-- w --><a href=\"http://\$2\">' . ((strlen('\$2') > 55) ? substr(str_replace('&amp;', '&', '\$2'), 0, 39) . ' ... ' . substr(str_replace('&amp;', '&', '\$2'), -10) : '\$2') . '</a><!-- w -->'";
2371  
2372          // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
2373          $magic_url_match[] = '/(^|[\n ]|\()(' . get_preg_expression('email') . ')/ie';
2374          $magic_url_replace[] = "'\$1<!-- e --><a href=\"mailto:\$2\">' . ((strlen('\$2') > 55) ? substr('\$2', 0, 39) . ' ... ' . substr('\$2', -10) : '\$2') . '</a><!-- e -->'";
2375      }
2376  
2377      return preg_replace($magic_url_match, $magic_url_replace, $text);
2378  }
2379  
2380  /**
2381  * Censoring
2382  */
2383  function censor_text($text)
2384  {
2385      static $censors;
2386      global $cache;
2387  
2388      if (!isset($censors) || !is_array($censors))
2389      {
2390          // obtain_word_list is taking care of the users censor option and the board-wide option
2391          $censors = $cache->obtain_word_list();
2392      }
2393  
2394      if (sizeof($censors))
2395      {
2396          return preg_replace($censors['match'], $censors['replace'], $text);
2397      }
2398  
2399      return $text;
2400  }
2401  
2402  /**
2403  * Smiley processing
2404  */
2405  function smiley_text($text, $force_option = false)
2406  {
2407      global $config, $user, $phpbb_root_path;
2408  
2409      if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
2410      {
2411          return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
2412      }
2413      else
2414      {
2415          return str_replace('<img src="{SMILIES_PATH}', '<img src="' . $phpbb_root_path . $config['smilies_path'], $text);
2416      }
2417  }
2418  
2419  /**
2420  * Inline Attachment processing
2421  */
2422  function parse_inline_attachments(&$text, &$attachments, &$update_count, $forum_id = 0, $preview = false)
2423  {
2424      global $config, $user;
2425  
2426      if (!function_exists('display_attachments'))
2427      {
2428          global $phpbb_root_path, $phpEx;
2429          include("{$phpbb_root_path}includes/functions_display.$phpEx");
2430      }
2431  
2432      $attachments = display_attachments($forum_id, NULL, $attachments, $update_count, false, true);
2433      $tpl_size = sizeof($attachments);
2434  
2435      $unset_tpl = array();
2436  
2437      preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $text, $matches, PREG_PATTERN_ORDER);
2438  
2439      $replace = array();
2440      foreach ($matches[0] as $num => $capture)
2441      {
2442          // Flip index if we are displaying the reverse way
2443          $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
2444  
2445          $replace['from'][] = $matches[0][$num];
2446          $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
2447  
2448          $unset_tpl[] = $index;
2449      }
2450  
2451      if (isset($replace['from']))
2452      {
2453          $text = str_replace($replace['from'], $replace['to'], $text);
2454      }
2455  
2456      return array_unique($unset_tpl);
2457  }
2458  
2459  /**
2460  * Check if extension is allowed to be posted within forum X (forum_id 0 == private messaging)
2461  */
2462  function extension_allowed($forum_id, $extension, &$extensions)
2463  {
2464      if (!sizeof($extensions))
2465      {
2466          global $cache;
2467          $extensions = $cache->obtain_attach_extensions();
2468      }
2469  
2470      if (!isset($extensions['_allowed_'][$extension]))
2471      {
2472          return false;
2473      }
2474  
2475      $check = $extensions['_allowed_'][$extension];
2476  
2477      if (is_array($check))
2478      {
2479          // Check for private messaging AND all forums allowed
2480          if (sizeof($check) == 1 && $check[0] == 0)
2481          {
2482              return true;
2483          }
2484  
2485          return (!in_array($forum_id, $check)) ? false : true;
2486      }
2487  
2488      return ($forum_id == 0) ? false : true;
2489  }
2490  
2491  // Little helpers
2492  
2493  /**
2494  * Little helper for the build_hidden_fields function
2495  */
2496  function _build_hidden_fields($key, $value, $specialchar)
2497  {
2498      $hidden_fields = '';
2499  
2500      if (!is_array($value))
2501      {
2502          $key = ($specialchar) ? htmlspecialchars($key) : $key;
2503          $value = ($specialchar) ? htmlspecialchars($value) : $value;
2504  
2505          $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
2506      }
2507      else
2508      {
2509          foreach ($value as $_key => $_value)
2510          {
2511              $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar);
2512          }
2513      }
2514  
2515      return $hidden_fields;
2516  }
2517  
2518  /**
2519  * Build simple hidden fields from array
2520  */
2521  function build_hidden_fields($field_ary, $specialchar = false)
2522  {
2523      $s_hidden_fields = '';
2524  
2525      foreach ($field_ary as $name => $vars)
2526      {
2527          $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar);
2528      }
2529  
2530      return $s_hidden_fields;
2531  }
2532  
2533  /**
2534  * Parse cfg file
2535  */
2536  function parse_cfg_file($filename, $lines = false)
2537  {
2538      $parsed_items = array();
2539  
2540      if ($lines === false)
2541      {
2542          $lines = file($filename);
2543      }
2544  
2545      foreach ($lines as $line)
2546      {
2547          $line = trim($line);
2548  
2549          if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
2550          {
2551              continue;
2552          }
2553  
2554          // Determine first occurrence, since in values the equal sign is allowed
2555          $key = strtolower(trim(substr($line, 0, $delim_pos)));
2556          $value = trim(substr($line, $delim_pos + 1));
2557  
2558          if (in_array($value, array('off', 'false', '0')))
2559          {
2560              $value = false;
2561          }
2562          else if (in_array($value, array('on', 'true', '1')))
2563          {
2564              $value = true;
2565          }
2566          else if (!trim($value))
2567          {
2568              $value = '';
2569          }
2570          else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
2571          {
2572              $value = substr($value, 1, sizeof($value)-2);
2573          }
2574      
2575          $parsed_items[$key] = $value;
2576      }
2577      
2578      return $parsed_items;
2579  }
2580  
2581  /**
2582  * Add log event
2583  */
2584  function add_log()
2585  {
2586      global $db, $user;
2587  
2588      $args = func_get_args();
2589  
2590      $mode            = array_shift($args);
2591      $reportee_id    = ($mode == 'user') ? intval(array_shift($args)) : '';
2592      $forum_id        = ($mode == 'mod') ? intval(array_shift($args)) : '';
2593      $topic_id        = ($mode == 'mod') ? intval(array_shift($args)) : '';
2594      $action            = array_shift($args);
2595      $data            = (!sizeof($args)) ? '' : serialize($args);
2596  
2597      $sql_ary = array(
2598          'user_id'        => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
2599          'log_ip'        => $user->ip,
2600          'log_time'        => time(),
2601          'log_operation'    => $action,
2602          'log_data'        => $data,
2603      );
2604      
2605      switch ($mode)
2606      {
2607          case 'admin':
2608              $sql_ary['log_type'] = LOG_ADMIN;
2609          break;
2610          
2611          case 'mod':
2612              $sql_ary += array(
2613                  'log_type'    => LOG_MOD,
2614                  'forum_id'    => $forum_id,
2615                  'topic_id'    => $topic_id
2616              );
2617          break;
2618  
2619          case 'user':
2620              $sql_ary += array(
2621                  'log_type'        => LOG_USERS,
2622                  'reportee_id'    => $reportee_id
2623              );
2624          break;
2625  
2626          case 'critical':
2627              $sql_ary['log_type'] = LOG_CRITICAL;
2628          break;
2629          
2630          default:
2631              return false;
2632      }
2633  
2634      $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
2635  
2636      return $db->sql_nextid();
2637  }
2638  
2639  /**
2640  * Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
2641  */
2642  function get_backtrace()
2643  {
2644      global $phpbb_root_path;
2645  
2646      $output = '<div style="font-family: monospace;">';
2647      $backtrace = debug_backtrace();
2648      $path = phpbb_realpath($phpbb_root_path);
2649  
2650      foreach ($backtrace as $number => $trace)
2651      {
2652          // We skip the first one, because it only shows this file/function
2653          if ($number == 0)
2654          {
2655              continue;
2656          }
2657  
2658          // Strip the current directory from path
2659          $trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
2660          $trace['file'] = substr($trace['file'], 1);
2661          $args = array();
2662  
2663          // If include/require/include_once is not called, do not show arguments - they may contain sensible informations
2664          if (!in_array($trace['function'], array('include', 'require', 'include_once')))
2665          {
2666              unset($trace['args']);
2667          }
2668          else
2669          {
2670              // Path...
2671              if (!empty($trace['args'][0]))
2672              {
2673                  $argument = htmlspecialchars($trace['args'][0]);
2674                  $argument = str_replace(array($path, '\\'), array('', '/'), $argument);
2675                  $argument = substr($argument, 1);
2676                  $args[] = "'{$argument}'";
2677              }
2678          }
2679  
2680          $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
2681          $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
2682  
2683          $output .= '<br />';
2684          $output .= '<b>FILE:</b> ' . htmlspecialchars($trace['file']) . '<br />';
2685          $output .= '<b>LINE:</b> ' . $trace['line'] . '<br />';
2686  
2687          $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')<br />';
2688      }
2689      $output .= '</div>';
2690      return $output;
2691  }
2692  
2693  /**
2694  * This function returns a regular expression pattern for commonly used expressions
2695  * Use with / as delimiter for email mode
2696  * mode can be: email|bbcode_htm
2697  */
2698  function get_preg_expression($mode)
2699  {
2700      switch ($mode)
2701      {
2702          case 'email':
2703              return '[a-z0-9&\'\.\-_\+]+@[a-z0-9\-]+\.([a-z0-9\-]+\.)*[a-z]+';
2704          break;
2705  
2706          case 'bbcode_htm':
2707              return array(
2708                  '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
2709                  '#<!\-\- (l|m|w) \-\-><a href="(.*?)">.*?</a><!\-\- \1 \-\->#',
2710                  '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
2711                  '#<!\-\- .*? \-\->#s',
2712                  '#<.*?>#s',
2713              );
2714          break;
2715      }
2716  
2717      return '';
2718  }
2719  
2720  /**
2721  * Truncates string while retaining special characters if going over the max length
2722  * The default max length is 60 at the moment
2723  */
2724  function truncate_string($string, $max_length = 60, $allow_reply = true)
2725  {
2726      $chars = array();
2727  
2728      $strip_reply = false;
2729      if ($allow_reply && strpos($string, 'Re: ') === 0)
2730      {
2731          $strip_reply = true;
2732          $string = substr($string, 4);
2733      }
2734  
2735      $_chars = utf8_str_split(htmlspecialchars_decode($string));
2736      $chars = array_map('htmlspecialchars', $_chars);
2737  
2738      // Now check the length ;)
2739      if (sizeof($chars) > $max_length)
2740      {
2741          // Cut off the last elements from the array
2742          $string = implode('', array_slice($chars, 0, $max_length));
2743      }
2744  
2745      if ($strip_reply)
2746      {
2747          $string = 'Re: ' . $string;
2748      }
2749  
2750      return $string;
2751  }
2752  
2753  
2754  /**
2755  * Wrapper for php's checkdnsrr function.
2756  *
2757  * The windows failover is from the php manual
2758  * Please make sure to check the return value for === true and === false, since NULL could
2759  * be returned too.
2760  * 
2761  * @return true if entry found, false if not, NULL if this function is not supported by this environment
2762  */
2763  function phpbb_checkdnsrr($host, $type = '')
2764  {
2765      $type = (!$type) ? 'MX' : $type;
2766  
2767      if (strpos(PHP_OS, 'WIN') !== false)
2768      {
2769          if (!function_exists('exec'))
2770          {
2771              return NULL;
2772          }
2773  
2774          @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
2775  
2776          foreach ($output as $line)
2777          {
2778              if (!trim($line))
2779              {
2780                  continue;
2781              }
2782  
2783              // Valid records begin with host name:
2784              if (strpos($line, $host) === 0)
2785              {
2786                  return true;
2787              }
2788          }
2789  
2790          return false;
2791      }
2792      else if (function_exists('checkdnsrr'))
2793      {
2794          return (checkdnsrr($host, $type)) ? true : false;
2795      }
2796  
2797      return NULL;
2798  }
2799  
2800  // Handler, header and footer
2801  
2802  /**
2803  * Error and message handler, call with trigger_error if reqd
2804  */
2805  function msg_handler($errno, $msg_text, $errfile, $errline)
2806  {
2807      global $cache, $db, $auth, $template, $config, $user;
2808      global $phpEx, $phpbb_root_path, $starttime, $msg_title, $msg_long_text;
2809  
2810      // Message handler is stripping text. In case we need it, we are possible to define long text...
2811      if (isset($msg_long_text) && $msg_long_text && !$msg_text)
2812      {
2813          $msg_text = $msg_long_text;
2814      }
2815  
2816      switch ($errno)
2817      {
2818          case E_NOTICE:
2819          case E_WARNING:
2820  
2821              // Check the error reporting level and return if the error level does not match
2822              // Additionally do not display notices if we suppress them via @
2823              // If DEBUG_EXTRA is defined the default level is E_ALL
2824              if (($errno & ((defined('DEBUG_EXTRA') && error_reporting()) ? E_ALL : error_reporting())) == 0)
2825              {
2826                  return;
2827              }
2828  
2829              /**
2830              * @todo Think about removing the if-condition within the final product, since we no longer enable DEBUG by default and we will maybe adjust the error reporting level
2831              */            
2832              if (defined('DEBUG'))
2833              {
2834                  if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
2835                  {
2836                      // remove complete path to installation, with the risk of changing backslashes meant to be there
2837                      $errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
2838                      $msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
2839  
2840                      echo '<b>[phpBB Debug] PHP Notice</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
2841                  }
2842              }
2843          
2844          break;
2845  
2846          case E_USER_ERROR:
2847  
2848              garbage_collection();
2849  
2850              echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
2851              echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
2852              echo '<head>';
2853              echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
2854              echo '<title>' . $msg_title . '</title>';
2855              echo '<link href="' . $phpbb_root_path . 'adm/style/admin.css" rel="stylesheet" type="text/css" media="screen" />';
2856              echo '</head>';
2857              echo '<body id="errorpage">';
2858              echo '<div id="wrap">';
2859              echo '    <div id="page-header">';
2860              echo '        <a href="' . $phpbb_root_path . '">Return to forum index</a>';
2861              echo '    </div>';
2862              echo '    <div id="page-body">';
2863              echo '        <div class="panel">';
2864              echo '            <span class="corners-top"><span></span></span>';
2865              echo '            <div id="content">';
2866              echo '                <h1>General Error</h1>';
2867              
2868              echo '                <h2>' . $msg_text . '</h2>';
2869              
2870              if (!empty($config['board_contact']))
2871              {
2872                  echo '                <p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
2873              }
2874              
2875              echo '            </div>';
2876              echo '            <span class="corners-bottom"><span></span></span>';
2877              echo '        </div>';
2878              echo '    </div>';
2879              echo '    <div id="page-footer">';
2880              echo '        Powered by phpBB &copy; ' . date('Y') . ' <a href="http://www.phpbb.com/">phpBB Group</a>';
2881              echo '    </div>';
2882              echo '</div>';
2883              echo '</body>';
2884              echo '</html>';
2885              
2886              exit;
2887          break;
2888  
2889          case E_USER_WARNING:
2890          case E_USER_NOTICE:
2891  
2892              define('IN_ERROR_HANDLER', true);
2893  
2894              if (empty($user->data))
2895              {
2896                  $user->session_begin();
2897              }
2898  
2899              // We re-init the auth array to get correct results on login/logout
2900              $auth->acl($user->data);
2901  
2902              if (empty($user->lang))
2903              {
2904                  $user->setup();
2905              }
2906  
2907              $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
2908              $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
2909  
2910              if (!defined('HEADER_INC'))
2911              {
2912                  if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2913                  {
2914                      adm_page_header($msg_title);
2915                  }
2916                  else
2917                  {
2918                      page_header($msg_title);
2919                  }
2920              }
2921  
2922              $template->set_filenames(array(
2923                  'body' => 'message_body.html')
2924              );
2925  
2926              $template->assign_vars(array(
2927                  'MESSAGE_TITLE'        => $msg_title,
2928                  'MESSAGE_TEXT'        => $msg_text,
2929                  'S_USER_WARNING'    => ($errno == E_USER_WARNING) ? true : false,
2930                  'S_USER_NOTICE'        => ($errno == E_USER_NOTICE) ? true : false)
2931              );
2932  
2933              // We do not want the cron script to be called on error messages
2934              define('IN_CRON', true);
2935              
2936              if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
2937              {
2938                  adm_page_footer();
2939              }
2940              else
2941              {
2942                  page_footer();
2943              }
2944  
2945              exit;
2946          break;
2947      }
2948  }
2949  
2950  /**
2951  * Generate page header
2952  */
2953  function page_header($page_title = '', $display_online_list = true)
2954  {
2955      global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
2956  
2957      if (defined('HEADER_INC'))
2958      {
2959          return;
2960      }
2961      
2962      define('HEADER_INC', true);
2963  
2964      // gzip_compression
2965      if ($config['gzip_compress'])
2966      {
2967          if (@extension_loaded('zlib') && !headers_sent())
2968          {
2969              ob_start('ob_gzhandler');
2970          }
2971      }
2972  
2973      // Generate logged in/logged out status
2974      if ($user->data['user_id'] != ANONYMOUS)
2975      {
2976          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout');
2977          $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
2978      }
2979      else
2980      {
2981          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
2982          $l_login_logout = $user->lang['LOGIN'];
2983      }
2984  
2985      // Last visit date/time
2986      $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
2987  
2988      // Get users online list ... if required
2989      $l_online_users = $online_userlist = $l_online_record = '';
2990  
2991      if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
2992      {
2993          $userlist_ary = $userlist_visible = array();
2994          $logged_visible_online = $logged_hidden_online = $guests_online = $prev_user_id = 0;
2995          $prev_session_ip = $reading_sql = '';
2996  
2997          if (!empty($_REQUEST['f']))
2998          {
2999              $f = request_var('f', 0);
3000  
3001              // Do not change this (it is defined as _f_={forum_id}x within session.php)
3002              $reading_sql = " AND s.session_page LIKE '%\_f\_={$f}x%'";
3003  
3004              // Specify escape character for MSSQL
3005              if ($db->sql_layer == 'mssql' || $db->sql_layer == 'mssql_odbc')
3006              {
3007                  $reading_sql .= " ESCAPE '\\'";
3008              }
3009          }
3010  
3011          // Get number of online guests
3012          if (!$config['load_online_guests'])
3013          {
3014              if ($db->sql_layer === 'sqlite')
3015              {
3016                  $sql = 'SELECT COUNT(session_ip) as num_guests
3017                      FROM (
3018                          SELECT DISTINCT s.session_ip
3019                              FROM ' . SESSIONS_TABLE . ' s
3020                              WHERE s.session_user_id = ' . ANONYMOUS . '
3021                                  AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) . 
3022                                  $reading_sql .
3023                      ')';
3024              }
3025              else
3026              {
3027                  $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
3028                      FROM ' . SESSIONS_TABLE . ' s
3029                      WHERE s.session_user_id = ' . ANONYMOUS . '
3030                          AND s.session_time >= ' . (time() - ($config['load_online_time'] * 60)) . 
3031                      $reading_sql;
3032              }
3033              $result = $db->sql_query($sql);
3034              $guests_online = (int) $db->sql_fetchfield('num_guests');
3035              $db->sql_freeresult($result);
3036          }
3037  
3038          $sql = 'SELECT u.username, u.user_id, u.user_type, u.user_allow_viewonline, u.user_colour, s.session_ip, s.session_viewonline
3039              FROM ' . USERS_TABLE . ' u, ' . SESSIONS_TABLE . ' s
3040              WHERE s.session_time >= ' . (time() - (intval($config['load_online_time']) * 60)) . 
3041                  $reading_sql .
3042                  ((!$config['load_online_guests']) ? ' AND s.session_user_id <> ' . ANONYMOUS : '') . '
3043                  AND u.user_id = s.session_user_id 
3044              ORDER BY u.username ASC, s.session_ip ASC';
3045          $result = $db->sql_query($sql);
3046  
3047          while ($row = $db->sql_fetchrow($result))
3048          {
3049              // User is logged in and therefore not a guest
3050              if ($row['user_id'] != ANONYMOUS)
3051              {
3052                  // Skip multiple sessions for one user
3053                  if ($row['user_id'] != $prev_user_id)
3054                  {
3055                      if ($row['user_colour'])
3056                      {
3057                          $user_colour = ' style="color:#' . $row['user_colour'] . '"';
3058                          $row['username'] = '<strong>' . $row['username'] . '</strong>';
3059                      }
3060                      else
3061                      {
3062                          $user_colour = '';
3063                      }
3064  
3065                      if ($row['user_allow_viewonline'] && $row['session_viewonline'])
3066                      {
3067                          $user_online_link = $row['username'];
3068                          $logged_visible_online++;
3069                      }
3070                      else
3071                      {
3072                          $user_online_link = '<em>' . $row['username'] . '</em>';
3073                          $logged_hidden_online++;
3074                      }
3075  
3076                      if (($row['user_allow_viewonline'] && $row['session_viewonline']) || $auth->acl_get('u_viewonline'))
3077                      {
3078                          if ($row['user_type'] <> USER_IGNORE)
3079                          {
3080                              $user_online_link = '<a href="' . append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&amp;u=' . $row['user_id']) . '"' . $user_colour . '>' . $user_online_link . '</a>';
3081                          }
3082                          else
3083                          {
3084                              $user_online_link = ($user_colour) ? '<span' . $user_colour . '>' . $user_online_link . '</span>' : $user_online_link;
3085                          }
3086  
3087                          $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
3088                      }
3089                  }
3090  
3091                  $prev_user_id = $row['user_id'];
3092              }
3093              else
3094              {
3095                  // Skip multiple sessions for one user
3096                  if ($row['session_ip'] != $prev_session_ip)
3097                  {
3098                      $guests_online++;
3099                  }
3100              }
3101  
3102              $prev_session_ip = $row['session_ip'];
3103          }
3104          $db->sql_freeresult($result);
3105  
3106          if (!$online_userlist)
3107          {
3108              $online_userlist = $user->lang['NO_ONLINE_USERS'];
3109          }
3110  
3111          if (empty($_REQUEST['f']))
3112          {
3113              $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
3114          }
3115          else
3116          {
3117              $l_online = ($guests_online == 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
3118              $online_userlist = sprintf($l_online, $online_userlist, $guests_online);
3119          }
3120  
3121          $total_online_users = $logged_visible_online + $logged_hidden_online + $guests_online;
3122  
3123          if ($total_online_users > $config['record_online_users'])
3124          {
3125              set_config('record_online_users', $total_online_users, true);
3126              set_config('record_online_date', time(), true);
3127          }
3128  
3129          // Build online listing
3130          $vars_online = array(
3131              'ONLINE'    => array('total_online_users', 'l_t_user_s'),
3132              'REG'        => array('logged_visible_online', 'l_r_user_s'),
3133              'HIDDEN'    => array('logged_hidden_online', 'l_h_user_s'),
3134              'GUEST'        => array('guests_online', 'l_g_user_s')
3135          );
3136  
3137          foreach ($vars_online as $l_prefix => $var_ary)
3138          {
3139              switch (${$var_ary[0]})
3140              {
3141                  case 0:
3142                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL'];
3143                  break;
3144  
3145                  case 1:
3146                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL'];
3147                  break;
3148  
3149                  default:
3150                      ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL'];
3151                  break;
3152              }
3153          }
3154          unset($vars_online);
3155  
3156          $l_online_users = sprintf($l_t_user_s, $total_online_users);
3157          $l_online_users .= sprintf($l_r_user_s, $logged_visible_online);
3158          $l_online_users .= sprintf($l_h_user_s, $logged_hidden_online);
3159          $l_online_users .= sprintf($l_g_user_s, $guests_online);
3160  
3161          $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
3162  
3163          $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
3164          $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
3165      }
3166      else
3167      {
3168          $l_online_time = '';
3169      }
3170  
3171      $l_privmsgs_text = $l_privmsgs_text_unread = '';
3172      $s_privmsg_new = false;
3173  
3174      // Obtain number of new private messages if user is logged in
3175      if (isset($user->data['is_registered']) && $user->data['is_registered'])
3176      {
3177          if ($user->data['user_new_privmsg'])
3178          {
3179              $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
3180              $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
3181  
3182              if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
3183              {
3184                  $sql = 'UPDATE ' . USERS_TABLE . '
3185                      SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
3186                      WHERE user_id = ' . $user->data['user_id'];
3187                  $db->sql_query($sql);
3188  
3189                  $s_privmsg_new = true;
3190              }
3191              else
3192              {
3193                  $s_privmsg_new = false;
3194              }
3195          }
3196          else
3197          {
3198              $l_privmsgs_text = $user->lang['NO_NEW_PM'];
3199              $s_privmsg_new = false;
3200          }
3201  
3202          $l_privmsgs_text_unread = '';
3203  
3204          if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
3205          {
3206              $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
3207              $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
3208          }
3209      }
3210  
3211      // Which timezone?
3212      $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
3213      
3214      // The following assigns all _common_ variables that may be used at any point in a template.
3215      $template->assign_vars(array(
3216          'SITENAME'                        => $config['sitename'],
3217          'SITE_DESCRIPTION'                => $config['site_desc'],
3218          'PAGE_TITLE'                    => $page_title,
3219          'SCRIPT_NAME'                    => str_replace('.' . $phpEx, '', $user->page['page_name']),
3220          'LAST_VISIT_DATE'                => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
3221          'CURRENT_TIME'                    => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
3222          'TOTAL_USERS_ONLINE'            => $l_online_users,
3223          'LOGGED_IN_USER_LIST'            => $online_userlist,
3224          'RECORD_USERS'                    => $l_online_record,
3225          'PRIVATE_MESSAGE_INFO'            => $l_privmsgs_text,
3226          'PRIVATE_MESSAGE_INFO_UNREAD'    => $l_privmsgs_text_unread,
3227  
3228          'SID'                => $SID,
3229          '_SID'                => $_SID,
3230          'SESSION_ID'        => $user->session_id,
3231          'ROOT_PATH'            => $phpbb_root_path,
3232  
3233          'L_LOGIN_LOGOUT'    => $l_login_logout,
3234          'L_INDEX'            => $user->lang['FORUM_INDEX'],
3235          'L_ONLINE_EXPLAIN'    => $l_online_time,
3236  
3237          'U_PRIVATEMSGS'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3238          'U_RETURN_INBOX'        => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;folder=inbox'),
3239          'UA_RETURN_INBOX'        => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox', false),
3240          'U_POPUP_PM'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&amp;mode=popup'),
3241          'UA_POPUP_PM'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup', false),
3242          'U_MEMBERLIST'            => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3243          'U_MEMBERSLIST'            => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
3244          'U_VIEWONLINE'            => append_sid("{$phpbb_root_path}viewonline.$phpEx"),
3245          'U_LOGIN_LOGOUT'        => $u_login_logout,
3246          'U_INDEX'                => append_sid("{$phpbb_root_path}index.$phpEx"),
3247          'U_SEARCH'                => append_sid("{$phpbb_root_path}search.$phpEx"),
3248          'U_REGISTER'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
3249          'U_PROFILE'                => append_sid("{$phpbb_root_path}ucp.$phpEx"),
3250          'U_MODCP'                => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
3251          'U_FAQ'                    => append_sid("{$phpbb_root_path}faq.$phpEx"),
3252          'U_SEARCH_SELF'            => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
3253          'U_SEARCH_NEW'            => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
3254          'U_SEARCH_UNANSWERED'    => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
3255          'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
3256          'U_DELETE_COOKIES'        => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
3257          'U_TEAM'                => append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
3258          'U_RESTORE_PERMISSIONS'    => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
3259  
3260          'S_USER_LOGGED_IN'        => ($user->data['user_id'] != ANONYMOUS) ? true : false,
3261          'S_BOARD_DISABLED'        => ($config['board_disable'] && !defined('IN_LOGIN') && !$auth->acl_gets('a_', 'm_')) ? true : false,
3262          'S_REGISTERED_USER'        => $user->data['is_registered'],
3263          'S_IS_BOT'                => $user->data['is_bot'],
3264          'S_USER_PM_POPUP'        => $user->optionget('popuppm'),
3265          'S_USER_LANG'            => $user->lang['USER_LANG'],
3266          'S_USER_BROWSER'        => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
3267          'S_USERNAME'            => $user->data['username'],
3268          'S_CONTENT_DIRECTION'    => $user->lang['DIRECTION'],
3269          'S_CONTENT_ENCODING'    => 'UTF-8',
3270          'S_CONTENT_DIR_LEFT'    => $user->lang['LEFT'],
3271          'S_CONTENT_DIR_RIGHT'    => $user->lang['RIGHT'],
3272          '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], ''),
3273          'S_DISPLAY_ONLINE_LIST'    => ($l_online_time) ? 1 : 0,
3274          'S_DISPLAY_SEARCH'        => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
3275          'S_DISPLAY_PM'            => ($config['allow_privmsg'] && $user->data['is_registered']) ? 1 : 0,
3276          'S_DISPLAY_MEMBERLIST'    => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
3277          'S_NEW_PM'                => ($s_privmsg_new) ? 1 : 0,
3278  
3279          'T_THEME_PATH'            => "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
3280          'T_TEMPLATE_PATH'        => "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
3281          'T_IMAGESET_PATH'        => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
3282          'T_IMAGESET_LANG_PATH'    => "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
3283          'T_IMAGES_PATH'            => "{$phpbb_root_path}images/",
3284          'T_SMILIES_PATH'        => "{$phpbb_root_path}{$config['smilies_path']}/",
3285          'T_AVATAR_PATH'            => "{$phpbb_root_path}{$config['avatar_path']}/",
3286          'T_AVATAR_GALLERY_PATH'    => "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
3287          'T_ICONS_PATH'            => "{$phpbb_root_path}{$config['icons_path']}/",
3288          'T_RANKS_PATH'            => "{$phpbb_root_path}{$config['ranks_path']}/",
3289          'T_UPLOAD_PATH'            => "{$phpbb_root_path}{$config['upload_path']}/",
3290          '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'],
3291          'T_STYLESHEET_NAME'        => $user->theme['theme_name'],
3292          'T_THEME_DATA'            => (!$user->theme['theme_storedb']) ? '' : $user->theme['theme_data'],
3293  
3294          'SITE_LOGO_IMG'            => $user->img('site_logo'))
3295      );
3296  
3297      if ($config['send_encoding'])
3298      {
3299          header('Content-type: text/html; charset=UTF-8');
3300      }
3301      header('Cache-Control: private, no-cache="set-cookie"');
3302      header('Expires: 0');
3303      header('Pragma: no-cache');
3304  
3305      return;
3306  }
3307  
3308  /**
3309  * Generate page footer
3310  */
3311  function page_footer($run_cron = true)
3312  {
3313      global $db, $config, $template, $user, $auth, $cache, $messenger, $starttime, $phpbb_root_path, $phpEx;
3314  
3315      // Output page creation time
3316      if (defined('DEBUG'))
3317      {
3318          $mtime = explode(' ', microtime());
3319          $totaltime = $mtime[0] + $mtime[1] - $starttime;
3320  
3321          if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
3322          {
3323              $db->sql_report('display');
3324          }
3325  
3326          $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' .  (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
3327  
3328          if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
3329          {
3330              if (function_exists('memory_get_usage'))
3331              {
3332                  if ($memory_usage = memory_get_usage())
3333                  {
3334                      global $base_memory_usage;
3335                      $memory_usage -= $base_memory_usage;
3336                      $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']);
3337  
3338                      $debug_output .= ' | Memory Usage: ' . $memory_usage;
3339                  }
3340              }
3341  
3342              $debug_output .= ' | <a href="' . build_url() . '&amp;explain=1">Explain</a>';
3343          }
3344      }
3345  
3346      $template->assign_vars(array(
3347          'DEBUG_OUTPUT'    => (defined('DEBUG')) ? $debug_output : '',
3348  
3349          'U_ACP' => ($auth->acl_get('a_') && $user->data['is_registered']) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", '', true, $user->session_id) : '')
3350      );
3351  
3352      // Call cron-type script
3353      if (!defined('IN_CRON') && $run_cron)
3354      {
3355          $cron_type = '';
3356      
3357          if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
3358          {
3359              // Process email queue
3360              $cron_type = 'queue';
3361          }
3362          else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
3363          {
3364              // Tidy the cache
3365              $cron_type = 'tidy_cache';
3366          }
3367          else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
3368          {
3369              $cron_type = 'tidy_warnings';
3370          }
3371          else if (time() - $config['database_gc'] > $config['database_last_gc'])
3372          {
3373              // Tidy the database
3374              $cron_type = 'tidy_database';
3375          }
3376          else if (time() - $config['search_gc'] > $config['search_last_gc'])
3377          {
3378              // Tidy the search
3379              $cron_type = 'tidy_search';
3380          }
3381          else if (time() - $config['session_gc'] > $config['session_last_gc'])
3382          {
3383              $cron_type = 'tidy_sessions';
3384          }
3385  
3386          if ($cron_type)
3387          {
3388              $template->assign_var('RUN_CRON_TASK', '<img src="' . $phpbb_root_path . 'cron.' . $phpEx . '?cron_type=' . $cron_type . '" width="1" height="1" alt="cron" />');
3389          }
3390      }
3391  
3392      $template->display('body');
3393  
3394      garbage_collection();
3395  
3396      exit;
3397  }
3398  
3399  /**
3400  * Closing the cache object and the database
3401  * Cool function name, eh? We might want to add operations to it later
3402  */
3403  function garbage_collection()
3404  {
3405      global $cache, $db;
3406  
3407      // Unload cache, must be done before the DB connection if closed
3408      if (!empty($cache))
3409      {
3410          $cache->unload();
3411      }
3412  
3413      // Close our DB connection.
3414      if (!empty($db))
3415      {
3416          $db->sql_close();
3417      }
3418  }
3419  
3420  /**
3421  * @package phpBB3
3422  */
3423  class bitfield
3424  {
3425      var $data;
3426  
3427  	function bitfield($bitfield = '')
3428      {
3429          $this->data = base64_decode($bitfield);
3430      }
3431  
3432      /**
3433      */
3434  	function get($n)
3435      {
3436          // Get the ($n / 8)th char
3437          $byte = $n >> 3;
3438  
3439          if (!isset($this->data[$byte]))
3440          {
3441              // Of course, if it doesn't exist then the result if FALSE
3442              return false;
3443          }
3444  
3445          $c = $this->data[$byte];
3446  
3447          // Lookup the ($n % 8)th bit of the byte
3448          $bit = 7 - ($n & 7);
3449          return (bool) (ord($c) & (1 << $bit));
3450      }
3451  
3452  	function set($n)
3453      {
3454          $byte = $n >> 3;
3455          $bit = 7 - ($n & 7);
3456  
3457          if (isset($this->data[$byte]))
3458          {
3459              $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
3460          }
3461          else
3462          {
3463              if ($byte - strlen($this->data) > 0)
3464              {
3465                  $this->data .= str_repeat("\0", $byte - strlen($this->data));
3466              }
3467              $this->data .= chr(1 << $bit);
3468          }
3469      }
3470  
3471  	function clear($n)
3472      {
3473          $byte = $n >> 3;
3474  
3475          if (!isset($this->data[$byte]))
3476          {
3477              return;
3478          }
3479  
3480          $bit = 7 - ($n & 7);
3481          $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
3482      }
3483  
3484  	function get_blob()
3485      {
3486          return $this->data;
3487      }
3488  
3489  	function get_base64()
3490      {
3491          return base64_encode($this->data);
3492      }
3493  
3494  	function get_bin()
3495      {
3496          $bin = '';
3497          $len = strlen($this->data);
3498  
3499          for ($i = 0; $i < $len; ++$i)
3500          {
3501              $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
3502          }
3503  
3504          return $bin;
3505      }
3506  
3507  	function get_all_set()
3508      {
3509          return array_keys(array_filter(str_split($this->get_bin())));
3510      }
3511  
3512  	function merge($bitfield)
3513      {
3514          $this->data = $this->data | $bitfield->get_blob();
3515      }
3516  }
3517  
3518  ?>


Generated: Wed Nov 22 00:35:05 2006 Cross-referenced by PHPXref 0.6