Problemas com links (estilo Subsilver2)

Suporte aos Styles/Templates para phpBB.
gustavocave
Novato
Novato
Mensagens: 60
Registrado em: 16 Nov 2012, 10:24
MSG 50+

Re: Problemas com links (estilo Skymiles)

  • Favoritar esta postagem
  • Mensagem por gustavocave »

    Tema: Subsilver

    BBCode.html

    Código: Selecionar todos

    <!-- BEGIN ulist_open --><ul style="list-style-type: {LIST_TYPE}"><!-- END ulist_open -->
    <!-- BEGIN ulist_open_default --><ul><!-- END ulist_open_default -->
    <!-- BEGIN ulist_close --></ul><!-- END ulist_close -->
    
    <!-- BEGIN olist_open --><ol style="list-style-type: {LIST_TYPE}"><!-- END olist_open -->
    <!-- BEGIN olist_close --></ol><!-- END olist_close -->
    
    <!-- BEGIN listitem --><li><!-- END listitem -->
    <!-- BEGIN listitem_close --></li><!-- END listitem_close -->
    
    <!-- BEGIN quote_username_open -->
    <div class="quotetitle">{USERNAME} {L_WROTE}:</div><div class="quotecontent">
    <!-- END quote_username_open -->
    
    <!-- BEGIN quote_open -->
    <div class="quotetitle"><b>{L_QUOTE}:</b></div><div class="quotecontent">
    <!-- END quote_open -->
    
    <!-- BEGIN quote_close -->
    </div>
    
    <!-- END quote_close -->
    <!-- BEGIN code_open -->
    <div class="codetitle"><b>{L_CODE}:</b></div><div class="codecontent">
    <!-- END code_open -->
    
    <!-- BEGIN code_close -->
    </div>
    <!-- END code_close -->
    
    <!-- BEGIN inline_attachment_open -->
    <div class="attachtitle">{L_ATTACHMENT}:</div><div class="attachcontent">
    <!-- END inline_attachment_open -->
    
    <!-- BEGIN inline_attachment_close -->
    </div>
    <!-- END inline_attachment_close -->
    
    
    <!-- BEGIN b_open --><strong><!-- END b_open -->
    <!-- BEGIN b_close --></strong><!-- END b_close -->
    
    <!-- BEGIN u_open --><span style="text-decoration: underline"><!-- END u_open -->
    <!-- BEGIN u_close --></span><!-- END u_close -->
    
    <!-- BEGIN i_open --><em><!-- END i_open -->
    <!-- BEGIN i_close --></em><!-- END i_close -->
    
    <!-- BEGIN color --><span style="color: {COLOR}">{TEXT}</span><!-- END color -->
    
    <!-- BEGIN size --><span style="font-size: {SIZE}%; line-height: normal">{TEXT}</span><!-- END size -->
    
    <!-- BEGIN img --><img src="{URL}" alt="{L_IMAGE}" /><!-- END img -->
    
    <!-- BEGIN url --><a href="{URL}" onclick="window.open(this.href);return false;" class="postlink">{DESCRIPTION}</a><!-- END url -->
    
    <!-- BEGIN email --><a href="mailto:{EMAIL}">{DESCRIPTION}</a><!-- END email -->
    
    <!-- BEGIN flash -->
    	<object classid="clsid:D27CDB6E-AE6D-11CF-96B8-444553540000" codebase="http://active.macromedia.com/flash2/cabs/swflash.cab#version=5,0,0,0" width="{WIDTH}" height="{HEIGHT}">
    		<param name="movie" value="{URL}" />
    		<param name="play" value="false" />
    		<param name="loop" value="false" />
    		<param name="quality" value="high" />
    		<param name="allowScriptAccess" value="never" />
    		<param name="allowNetworking" value="internal" />
    		<embed src="{URL}" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/shockwave/download/index.cgi?P1_Prod_Version=ShockwaveFlash" width="{WIDTH}" height="{HEIGHT}" play="false" loop="false" quality="high" allowscriptaccess="never" allownetworking="internal"></embed>
    	</object>
    <!-- END flash -->
    
    Function.php

    Código: Selecionar todos

    <?php
    /**
    *
    * @package phpBB3
    * @version $Id$
    * @copyright (c) 2005 phpBB Group
    * @license http://opensource.org/licenses/gpl-license.php GNU Public License
    *
    */
    
    /**
    * @ignore
    */
    if (!defined('IN_PHPBB'))
    {
       exit;
    }
    
    // Common global functions
    
    /**
    * set_var
    *
    * Set variable, used by {@link request_var the request_var function}
    *
    * @access private
    */
    function set_var(&$result, $var, $type, $multibyte = false)
    {
       settype($var, $type);
       $result = $var;
    
       if ($type == 'string')
       {
          $result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8'));
    
          if (!empty($result))
          {
             // Make sure multibyte characters are wellformed
             if ($multibyte)
             {
                if (!preg_match('/^./u', $result))
                {
                   $result = '';
                }
             }
             else
             {
                // no multibyte, allow only ASCII (0-127)
                $result = preg_replace('/[\x80-\xFF]/', '?', $result);
             }
          }
    
          $result = (STRIP) ? stripslashes($result) : $result;
       }
    }
    
    /**
    * request_var
    *
    * Used to get passed variable
    */
    function request_var($var_name, $default, $multibyte = false, $cookie = false)
    {
       if (!$cookie && isset($_COOKIE[$var_name]))
       {
          if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
          {
             return (is_array($default)) ? array() : $default;
          }
          $_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
       }
    
       $super_global = ($cookie) ? '_COOKIE' : '_REQUEST';
       if (!isset($GLOBALS[$super_global][$var_name]) || is_array($GLOBALS[$super_global][$var_name]) != is_array($default))
       {
          return (is_array($default)) ? array() : $default;
       }
    
       $var = $GLOBALS[$super_global][$var_name];
       if (!is_array($default))
       {
          $type = gettype($default);
       }
       else
       {
          list($key_type, $type) = each($default);
          $type = gettype($type);
          $key_type = gettype($key_type);
          if ($type == 'array')
          {
             reset($default);
             $default = current($default);
             list($sub_key_type, $sub_type) = each($default);
             $sub_type = gettype($sub_type);
             $sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
             $sub_key_type = gettype($sub_key_type);
          }
       }
    
       if (is_array($var))
       {
          $_var = $var;
          $var = array();
    
          foreach ($_var as $k => $v)
          {
             set_var($k, $k, $key_type);
             if ($type == 'array' && is_array($v))
             {
                foreach ($v as $_k => $_v)
                {
                   if (is_array($_v))
                   {
                      $_v = null;
                   }
                   set_var($_k, $_k, $sub_key_type, $multibyte);
                   set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
                }
             }
             else
             {
                if ($type == 'array' || is_array($v))
                {
                   $v = null;
                }
                set_var($var[$k], $v, $type, $multibyte);
             }
          }
       }
       else
       {
          set_var($var, $var, $type, $multibyte);
       }
    
       return $var;
    }
    
    /**
    * Set config value. Creates missing config entry.
    */
    function set_config($config_name, $config_value, $is_dynamic = false)
    {
       global $db, $cache, $config;
    
       $sql = 'UPDATE ' . CONFIG_TABLE . "
          SET config_value = '" . $db->sql_escape($config_value) . "'
          WHERE config_name = '" . $db->sql_escape($config_name) . "'";
       $db->sql_query($sql);
    
       if (!$db->sql_affectedrows() && !isset($config[$config_name]))
       {
          $sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
             'config_name'   => $config_name,
             'config_value'   => $config_value,
             'is_dynamic'   => ($is_dynamic) ? 1 : 0));
          $db->sql_query($sql);
       }
    
       $config[$config_name] = $config_value;
    
       if (!$is_dynamic)
       {
          $cache->destroy('config');
       }
    }
    
    /**
    * Set dynamic config value with arithmetic operation.
    */
    function set_config_count($config_name, $increment, $is_dynamic = false)
    {
       global $db, $cache;
    
       switch ($db->sql_layer)
       {
          case 'firebird':
             // Precision must be from 1 to 18
             $sql_update = 'CAST(CAST(config_value as DECIMAL(18, 0)) + ' . (int) $increment . ' as VARCHAR(255))';
          break;
    
          case 'postgres':
             // Need to cast to text first for PostgreSQL 7.x
             $sql_update = 'CAST(CAST(config_value::text as DECIMAL(255, 0)) + ' . (int) $increment . ' as VARCHAR(255))';
          break;
    
          // MySQL, SQlite, mssql, mssql_odbc, oracle
          default:
             $sql_update = 'config_value + ' . (int) $increment;
          break;
       }
    
       $db->sql_query('UPDATE ' . CONFIG_TABLE . ' SET config_value = ' . $sql_update . " WHERE config_name = '" . $db->sql_escape($config_name) . "'");
    
       if (!$is_dynamic)
       {
          $cache->destroy('config');
       }
    }
    
    /**
    * Generates an alphanumeric random string of given length
    *
    * @return string
    */
    function gen_rand_string($num_chars = 8)
    {
       // [a, z] + [0, 9] = 36
       return substr(strtoupper(base_convert(unique_id(), 16, 36)), 0, $num_chars);
    }
    
    /**
    * Generates a user-friendly alphanumeric random string of given length
    * We remove 0 and O so users cannot confuse those in passwords etc.
    *
    * @return string
    */
    function gen_rand_string_friendly($num_chars = 8)
    {
       $rand_str = unique_id();
    
       // Remove Z and Y from the base_convert(), replace 0 with Z and O with Y
       // [a, z] + [0, 9] - {z, y} = [a, z] + [0, 9] - {0, o} = 34
       $rand_str = str_replace(array('0', 'O'), array('Z', 'Y'), strtoupper(base_convert($rand_str, 16, 34)));
    
       return substr($rand_str, 0, $num_chars);
    }
    
    /**
    * Return unique id
    * @param string $extra additional entropy
    */
    function unique_id($extra = 'c')
    {
       static $dss_seeded = false;
       global $config;
    
       $val = $config['rand_seed'] . microtime();
       $val = md5($val);
       $config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
    
       if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
       {
          set_config('rand_seed_last_update', time(), true);
          set_config('rand_seed', $config['rand_seed'], true);
          $dss_seeded = true;
       }
    
       return substr($val, 4, 16);
    }
    
    /**
    * Wrapper for mt_rand() which allows swapping $min and $max parameters.
    *
    * PHP does not allow us to swap the order of the arguments for mt_rand() anymore.
    * (since PHP 5.3.4, see http://bugs.php.net/46587)
    *
    * @param int $min      Lowest value to be returned
    * @param int $max      Highest value to be returned
    *
    * @return int         Random integer between $min and $max (or $max and $min)
    */
    function phpbb_mt_rand($min, $max)
    {
       return ($min > $max) ? mt_rand($max, $min) : mt_rand($min, $max);
    }
    
    /**
    * Wrapper for getdate() which returns the equivalent array for UTC timestamps.
    *
    * @param int $time      Unix timestamp (optional)
    *
    * @return array         Returns an associative array of information related to the timestamp.
    *                  See http://www.php.net/manual/en/function.getdate.php
    */
    function phpbb_gmgetdate($time = false)
    {
       if ($time === false)
       {
          $time = time();
       }
    
       // getdate() interprets timestamps in local time.
       // What follows uses the fact that getdate() and
       // date('Z') balance each other out.
       return getdate($time - date('Z'));
    }
    
    /**
    * Return formatted string for filesizes
    *
    * @param int   $value         filesize in bytes
    * @param bool   $string_only   true if language string should be returned
    * @param array   $allowed_units   only allow these units (data array indexes)
    *
    * @return mixed               data array if $string_only is false
    * @author bantu
    */
    function get_formatted_filesize($value, $string_only = true, $allowed_units = false)
    {
       global $user;
    
       $available_units = array(
          'gb' => array(
             'min'       => 1073741824, // pow(2, 30)
             'index'      => 3,
             'si_unit'   => 'GB',
             'iec_unit'   => 'GIB',
          ),
          'mb' => array(
             'min'      => 1048576, // pow(2, 20)
             'index'      => 2,
             'si_unit'   => 'MB',
             'iec_unit'   => 'MIB',
          ),
          'kb' => array(
             'min'      => 1024, // pow(2, 10)
             'index'      => 1,
             'si_unit'   => 'KB',
             'iec_unit'   => 'KIB',
          ),
          'b' => array(
             'min'      => 0,
             'index'      => 0,
             'si_unit'   => 'BYTES', // Language index
             'iec_unit'   => 'BYTES',  // Language index
          ),
       );
    
       foreach ($available_units as $si_identifier => $unit_info)
       {
          if (!empty($allowed_units) && $si_identifier != 'b' && !in_array($si_identifier, $allowed_units))
          {
             continue;
          }
    
          if ($value >= $unit_info['min'])
          {
             $unit_info['si_identifier'] = $si_identifier;
    
             break;
          }
       }
       unset($available_units);
    
       for ($i = 0; $i < $unit_info['index']; $i++)
       {
          $value /= 1024;
       }
       $value = round($value, 2);
    
       // Lookup units in language dictionary
       $unit_info['si_unit'] = (isset($user->lang[$unit_info['si_unit']])) ? $user->lang[$unit_info['si_unit']] : $unit_info['si_unit'];
       $unit_info['iec_unit'] = (isset($user->lang[$unit_info['iec_unit']])) ? $user->lang[$unit_info['iec_unit']] : $unit_info['iec_unit'];
    
       // Default to IEC
       $unit_info['unit'] = $unit_info['iec_unit'];
    
       if (!$string_only)
       {
          $unit_info['value'] = $value;
    
          return $unit_info;
       }
    
       return $value  . ' ' . $unit_info['unit'];
    }
    
    /**
    * Determine whether we are approaching the maximum execution time. Should be called once
    * at the beginning of the script in which it's used.
    * @return   bool   Either true if the maximum execution time is nearly reached, or false
    *               if some time is still left.
    */
    function still_on_time($extra_time = 15)
    {
       static $max_execution_time, $start_time;
    
       $time = explode(' ', microtime());
       $current_time = $time[0] + $time[1];
    
       if (empty($max_execution_time))
       {
          $max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
    
          // If zero, then set to something higher to not let the user catch the ten seconds barrier.
          if ($max_execution_time === 0)
          {
             $max_execution_time = 50 + $extra_time;
          }
    
          $max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
    
          // For debugging purposes
          // $max_execution_time = 10;
    
          global $starttime;
          $start_time = (empty($starttime)) ? $current_time : $starttime;
       }
    
       return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
    }
    
    /**
    *
    * @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier)
    *
    * Portable PHP password hashing framework.
    *
    * Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
    * the public domain.
    *
    * There's absolutely no warranty.
    *
    * The homepage URL for this framework is:
    *
    *   http://www.openwall.com/phpass/
    *
    * Please be sure to update the Version line if you edit this file in any way.
    * It is suggested that you leave the main version number intact, but indicate
    * your project name (after the slash) and add your own revision information.
    *
    * Please do not change the "private" password hashing method implemented in
    * here, thereby making your hashes incompatible.  However, if you must, please
    * change the hash type identifier (the "$P$") to something different.
    *
    * Obviously, since this code is in the public domain, the above are not
    * requirements (there can be none), but merely suggestions.
    *
    *
    * Hash the password
    */
    function phpbb_hash($password)
    {
       $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
    
       $random_state = unique_id();
       $random = '';
       $count = 6;
    
       if (($fh = @fopen('/dev/urandom', 'rb')))
       {
          $random = fread($fh, $count);
          fclose($fh);
       }
    
       if (strlen($random) < $count)
       {
          $random = '';
    
          for ($i = 0; $i < $count; $i += 16)
          {
             $random_state = md5(unique_id() . $random_state);
             $random .= pack('H*', md5($random_state));
          }
          $random = substr($random, 0, $count);
       }
    
       $hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
    
       if (strlen($hash) == 34)
       {
          return $hash;
       }
    
       return md5($password);
    }
    
    /**
    * Check for correct password
    *
    * @param string $password The password in plain text
    * @param string $hash The stored password hash
    *
    * @return bool Returns true if the password is correct, false if not.
    */
    function phpbb_check_hash($password, $hash)
    {
       $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
       if (strlen($hash) == 34)
       {
          return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
       }
    
       return (md5($password) === $hash) ? true : false;
    }
    
    /**
    * Generate salt for hash generation
    */
    function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
    {
       if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
       {
          $iteration_count_log2 = 8;
       }
    
       $output = '$H$';
       $output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
       $output .= _hash_encode64($input, 6, $itoa64);
    
       return $output;
    }
    
    /**
    * Encode hash
    */
    function _hash_encode64($input, $count, &$itoa64)
    {
       $output = '';
       $i = 0;
    
       do
       {
          $value = ord($input[$i++]);
          $output .= $itoa64[$value & 0x3f];
    
          if ($i < $count)
          {
             $value |= ord($input[$i]) << 8;
          }
    
          $output .= $itoa64[($value >> 6) & 0x3f];
    
          if ($i++ >= $count)
          {
             break;
          }
    
          if ($i < $count)
          {
             $value |= ord($input[$i]) << 16;
          }
    
          $output .= $itoa64[($value >> 12) & 0x3f];
    
          if ($i++ >= $count)
          {
             break;
          }
    
          $output .= $itoa64[($value >> 18) & 0x3f];
       }
       while ($i < $count);
    
       return $output;
    }
    
    /**
    * The crypt function/replacement
    */
    function _hash_crypt_private($password, $setting, &$itoa64)
    {
       $output = '*';
    
       // Check for correct hash
       if (substr($setting, 0, 3) != '$H$' && substr($setting, 0, 3) != '$P$')
       {
          return $output;
       }
    
       $count_log2 = strpos($itoa64, $setting[3]);
    
       if ($count_log2 < 7 || $count_log2 > 30)
       {
          return $output;
       }
    
       $count = 1 << $count_log2;
       $salt = substr($setting, 4, 8);
    
       if (strlen($salt) != 8)
       {
          return $output;
       }
    
       /**
       * We're kind of forced to use MD5 here since it's the only
       * cryptographic primitive available in all versions of PHP
       * currently in use.  To implement our own low-level crypto
       * in PHP would result in much worse performance and
       * consequently in lower iteration counts and hashes that are
       * quicker to crack (by non-PHP code).
       */
       if (PHP_VERSION >= 5)
       {
          $hash = md5($salt . $password, true);
          do
          {
             $hash = md5($hash . $password, true);
          }
          while (--$count);
       }
       else
       {
          $hash = pack('H*', md5($salt . $password));
          do
          {
             $hash = pack('H*', md5($hash . $password));
          }
          while (--$count);
       }
    
       $output = substr($setting, 0, 12);
       $output .= _hash_encode64($hash, 16, $itoa64);
    
       return $output;
    }
    
    /**
    * Hashes an email address to a big integer
    *
    * @param string $email      Email address
    *
    * @return string         Unsigned Big Integer
    */
    function phpbb_email_hash($email)
    {
       return sprintf('%u', crc32(strtolower($email))) . strlen($email);
    }
    
    /**
    * Wrapper for version_compare() that allows using uppercase A and B
    * for alpha and beta releases.
    *
    * See http://www.php.net/manual/en/function.version-compare.php
    *
    * @param string $version1      First version number
    * @param string $version2      Second version number
    * @param string $operator      Comparison operator (optional)
    *
    * @return mixed               Boolean (true, false) if comparison operator is specified.
    *                        Integer (-1, 0, 1) otherwise.
    */
    function phpbb_version_compare($version1, $version2, $operator = null)
    {
       $version1 = strtolower($version1);
       $version2 = strtolower($version2);
    
       if (is_null($operator))
       {
          return version_compare($version1, $version2);
       }
       else
       {
          return version_compare($version1, $version2, $operator);
       }
    }
    
    /**
    * Global function for chmodding directories and files for internal use
    *
    * This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
    * The function determines owner and group from common.php file and sets the same to the provided file.
    * The function uses bit fields to build the permissions.
    * The function sets the appropiate execute bit on directories.
    *
    * Supported constants representing bit fields are:
    *
    * CHMOD_ALL - all permissions (7)
    * CHMOD_READ - read permission (4)
    * CHMOD_WRITE - write permission (2)
    * CHMOD_EXECUTE - execute permission (1)
    *
    * NOTE: The function uses POSIX extension and fileowner()/filegroup() functions. If any of them is disabled, this function tries to build proper permissions, by calling is_readable() and is_writable() functions.
    *
    * @param string   $filename   The file/directory to be chmodded
    * @param int   $perms      Permissions to set
    *
    * @return bool   true on success, otherwise false
    * @author faw, phpBB Group
    */
    function phpbb_chmod($filename, $perms = CHMOD_READ)
    {
       static $_chmod_info;
    
       // Return if the file no longer exists.
       if (!file_exists($filename))
       {
          return false;
       }
    
       // Determine some common vars
       if (empty($_chmod_info))
       {
          if (!function_exists('fileowner') || !function_exists('filegroup'))
          {
             // No need to further determine owner/group - it is unknown
             $_chmod_info['process'] = false;
          }
          else
          {
             global $phpbb_root_path, $phpEx;
    
             // Determine owner/group of common.php file and the filename we want to change here
             $common_php_owner = @fileowner($phpbb_root_path . 'common.' . $phpEx);
             $common_php_group = @filegroup($phpbb_root_path . 'common.' . $phpEx);
    
             // And the owner and the groups PHP is running under.
             $php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
             $php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
    
             // If we are unable to get owner/group, then do not try to set them by guessing
             if (!$php_uid || empty($php_gids) || !$common_php_owner || !$common_php_group)
             {
                $_chmod_info['process'] = false;
             }
             else
             {
                $_chmod_info = array(
                   'process'      => true,
                   'common_owner'   => $common_php_owner,
                   'common_group'   => $common_php_group,
                   'php_uid'      => $php_uid,
                   'php_gids'      => $php_gids,
                );
             }
          }
       }
    
       if ($_chmod_info['process'])
       {
          $file_uid = @fileowner($filename);
          $file_gid = @filegroup($filename);
    
          // Change owner
          if (@chown($filename, $_chmod_info['common_owner']))
          {
             clearstatcache();
             $file_uid = @fileowner($filename);
          }
    
          // Change group
          if (@chgrp($filename, $_chmod_info['common_group']))
          {
             clearstatcache();
             $file_gid = @filegroup($filename);
          }
    
          // If the file_uid/gid now match the one from common.php we can process further, else we are not able to change something
          if ($file_uid != $_chmod_info['common_owner'] || $file_gid != $_chmod_info['common_group'])
          {
             $_chmod_info['process'] = false;
          }
       }
    
       // Still able to process?
       if ($_chmod_info['process'])
       {
          if ($file_uid == $_chmod_info['php_uid'])
          {
             $php = 'owner';
          }
          else if (in_array($file_gid, $_chmod_info['php_gids']))
          {
             $php = 'group';
          }
          else
          {
             // Since we are setting the everyone bit anyway, no need to do expensive operations
             $_chmod_info['process'] = false;
          }
       }
    
       // We are not able to determine or change something
       if (!$_chmod_info['process'])
       {
          $php = 'other';
       }
    
       // Owner always has read/write permission
       $owner = CHMOD_READ | CHMOD_WRITE;
       if (is_dir($filename))
       {
          $owner |= CHMOD_EXECUTE;
    
          // Only add execute bit to the permission if the dir needs to be readable
          if ($perms & CHMOD_READ)
          {
             $perms |= CHMOD_EXECUTE;
          }
       }
    
       switch ($php)
       {
          case 'owner':
             $result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0));
    
             clearstatcache();
    
             if (is_readable($filename) && phpbb_is_writable($filename))
             {
                break;
             }
    
          case 'group':
             $result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0));
    
             clearstatcache();
    
             if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename)))
             {
                break;
             }
    
          case 'other':
             $result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0));
    
             clearstatcache();
    
             if ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || phpbb_is_writable($filename)))
             {
                break;
             }
    
          default:
             return false;
          break;
       }
    
       return $result;
    }
    
    /**
    * Test if a file/directory is writable
    *
    * This function calls the native is_writable() when not running under
    * Windows and it is not disabled.
    *
    * @param string $file Path to perform write test on
    * @return bool True when the path is writable, otherwise false.
    */
    function phpbb_is_writable($file)
    {
       if (strtolower(substr(PHP_OS, 0, 3)) === 'win' || !function_exists('is_writable'))
       {
          if (file_exists($file))
          {
             // Canonicalise path to absolute path
             $file = phpbb_realpath($file);
    
             if (is_dir($file))
             {
                // Test directory by creating a file inside the directory
                $result = @tempnam($file, 'i_w');
    
                if (is_string($result) && file_exists($result))
                {
                   unlink($result);
    
                   // Ensure the file is actually in the directory (returned realpathed)
                   return (strpos($result, $file) === 0) ? true : false;
                }
             }
             else
             {
                $handle = @fopen($file, 'r+');
    
                if (is_resource($handle))
                {
                   fclose($handle);
                   return true;
                }
             }
          }
          else
          {
             // file does not exist test if we can write to the directory
             $dir = dirname($file);
    
             if (file_exists($dir) && is_dir($dir) && phpbb_is_writable($dir))
             {
                return true;
             }
          }
    
          return false;
       }
       else
       {
          return is_writable($file);
       }
    }
    
    // Compatibility functions
    
    if (!function_exists('array_combine'))
    {
       /**
       * A wrapper for the PHP5 function array_combine()
       * @param array $keys contains keys for the resulting array
       * @param array $values contains values for the resulting array
       *
       * @return Returns an array by using the values from the keys array as keys and the
       *    values from the values array as the corresponding values. Returns false if the
       *    number of elements for each array isn't equal or if the arrays are empty.
       */
       function array_combine($keys, $values)
       {
          $keys = array_values($keys);
          $values = array_values($values);
    
          $n = sizeof($keys);
          $m = sizeof($values);
          if (!$n || !$m || ($n != $m))
          {
             return false;
          }
    
          $combined = array();
          for ($i = 0; $i < $n; $i++)
          {
             $combined[$keys[$i]] = $values[$i];
          }
          return $combined;
       }
    }
    
    if (!function_exists('str_split'))
    {
       /**
       * A wrapper for the PHP5 function str_split()
       * @param array $string contains the string to be converted
       * @param array $split_length contains the length of each chunk
       *
       * @return  Converts a string to an array. If the optional split_length parameter is specified,
       *     the returned array will be broken down into chunks with each being split_length in length,
       *     otherwise each chunk will be one character in length. FALSE is returned if split_length is
       *     less than 1. If the split_length length exceeds the length of string, the entire string is
       *     returned as the first (and only) array element.
       */
       function str_split($string, $split_length = 1)
       {
          if ($split_length < 1)
          {
             return false;
          }
          else if ($split_length >= strlen($string))
          {
             return array($string);
          }
          else
          {
             preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
             return $matches[0];
          }
       }
    }
    
    if (!function_exists('stripos'))
    {
       /**
       * A wrapper for the PHP5 function stripos
       * Find position of first occurrence of a case-insensitive string
       *
       * @param string $haystack is the string to search in
       * @param string $needle is the string to search for
       *
       * @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
       * Note that the needle may be a string of one or more characters.
       * If needle is not found, stripos() will return boolean FALSE.
       */
       function stripos($haystack, $needle)
       {
          if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
          {
             return strpos($haystack, $m[0]);
          }
    
          return false;
       }
    }
    
    /**
    * Checks if a path ($path) is absolute or relative
    *
    * @param string $path Path to check absoluteness of
    * @return boolean
    */
    function is_absolute($path)
    {
       return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:[/\\\]#i', $path))) ? true : false;
    }
    
    /**
    * @author Chris Smith <chris@project-minerva.org>
    * @copyright 2006 Project Minerva Team
    * @param string $path The path which we should attempt to resolve.
    * @return mixed
    */
    function phpbb_own_realpath($path)
    {
       // Now to perform funky shizzle
    
       // Switch to use UNIX slashes
       $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
       $path_prefix = '';
    
       // Determine what sort of path we have
       if (is_absolute($path))
       {
          $absolute = true;
    
          if ($path[0] == '/')
          {
             // Absolute path, *NIX style
             $path_prefix = '';
          }
          else
          {
             // Absolute path, Windows style
             // Remove the drive letter and colon
             $path_prefix = $path[0] . ':';
             $path = substr($path, 2);
          }
       }
       else
       {
          // Relative Path
          // Prepend the current working directory
          if (function_exists('getcwd'))
          {
             // This is the best method, hopefully it is enabled!
             $path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
             $absolute = true;
             if (preg_match('#^[a-z]:#i', $path))
             {
                $path_prefix = $path[0] . ':';
                $path = substr($path, 2);
             }
             else
             {
                $path_prefix = '';
             }
          }
          else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
          {
             // Warning: If chdir() has been used this will lie!
             // Warning: This has some problems sometime (CLI can create them easily)
             $path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
             $absolute = true;
             $path_prefix = '';
          }
          else
          {
             // We have no way of getting the absolute path, just run on using relative ones.
             $absolute = false;
             $path_prefix = '.';
          }
       }
    
       // Remove any repeated slashes
       $path = preg_replace('#/{2,}#', '/', $path);
    
       // Remove the slashes from the start and end of the path
       $path = trim($path, '/');
    
       // Break the string into little bits for us to nibble on
       $bits = explode('/', $path);
    
       // Remove any . in the path, renumber array for the loop below
       $bits = array_values(array_diff($bits, array('.')));
    
       // Lets get looping, run over and resolve any .. (up directory)
       for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
       {
          // @todo Optimise
          if ($bits[$i] == '..' )
          {
             if (isset($bits[$i - 1]))
             {
                if ($bits[$i - 1] != '..')
                {
                   // We found a .. and we are able to traverse upwards, lets do it!
                   unset($bits[$i]);
                   unset($bits[$i - 1]);
                   $i -= 2;
                   $max -= 2;
                   $bits = array_values($bits);
                }
             }
             else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
             {
                // We have an absolute path trying to descend above the root of the filesystem
                // ... Error!
                return false;
             }
          }
       }
    
       // Prepend the path prefix
       array_unshift($bits, $path_prefix);
    
       $resolved = '';
    
       $max = sizeof($bits) - 1;
    
       // Check if we are able to resolve symlinks, Windows cannot.
       $symlink_resolve = (function_exists('readlink')) ? true : false;
    
       foreach ($bits as $i => $bit)
       {
          if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
          {
             // Path Exists
             if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
             {
                // Resolved a symlink.
                $resolved = $link . (($i == $max) ? '' : '/');
                continue;
             }
          }
          else
          {
             // Something doesn't exist here!
             // This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
             // return false;
          }
          $resolved .= $bit . (($i == $max) ? '' : '/');
       }
    
       // @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
       // because we must be inside that basedir, the question is where...
       // @internal The slash in is_dir() gets around an open_basedir restriction
       if (!@file_exists($resolved) || (!@is_dir($resolved . '/') && !is_file($resolved)))
       {
          return false;
       }
    
       // Put the slashes back to the native operating systems slashes
       $resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
    
       // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
       if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
       {
          return substr($resolved, 0, -1);
       }
    
       return $resolved; // We got here, in the end!
    }
    
    if (!function_exists('realpath'))
    {
       /**
       * A wrapper for realpath
       * @ignore
       */
       function phpbb_realpath($path)
       {
          return phpbb_own_realpath($path);
       }
    }
    else
    {
       /**
       * A wrapper for realpath
       */
       function phpbb_realpath($path)
       {
          $realpath = realpath($path);
    
          // Strangely there are provider not disabling realpath but returning strange values. :o
          // We at least try to cope with them.
          if ($realpath === $path || $realpath === false)
          {
             return phpbb_own_realpath($path);
          }
    
          // Check for DIRECTORY_SEPARATOR at the end (and remove it!)
          if (substr($realpath, -1) == DIRECTORY_SEPARATOR)
          {
             $realpath = substr($realpath, 0, -1);
          }
    
          return $realpath;
       }
    }
    
    if (!function_exists('htmlspecialchars_decode'))
    {
       /**
       * A wrapper for htmlspecialchars_decode
       * @ignore
       */
       function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
       {
          return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
       }
    }
    
    // functions used for building option fields
    
    /**
    * Pick a language, any language ...
    */
    function language_select($default = '')
    {
       global $db;
    
       $sql = 'SELECT lang_iso, lang_local_name
          FROM ' . LANG_TABLE . '
          ORDER BY lang_english_name';
       $result = $db->sql_query($sql);
    
       $lang_options = '';
       while ($row = $db->sql_fetchrow($result))
       {
          $selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
          $lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
       }
       $db->sql_freeresult($result);
    
       return $lang_options;
    }
    
    /**
    * Pick a template/theme combo,
    */
    function style_select($default = '', $all = false)
    {
       global $db;
    
       $sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
       $sql = 'SELECT style_id, style_name
          FROM ' . STYLES_TABLE . "
          $sql_where
          ORDER BY style_name";
       $result = $db->sql_query($sql);
    
       $style_options = '';
       while ($row = $db->sql_fetchrow($result))
       {
          $selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
          $style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
       }
       $db->sql_freeresult($result);
    
       return $style_options;
    }
    
    /**
    * Pick a timezone
    */
    function tz_select($default = '', $truncate = false)
    {
       global $user;
    
       $tz_select = '';
       foreach ($user->lang['tz_zones'] as $offset => $zone)
       {
          if ($truncate)
          {
             $zone_trunc = truncate_string($zone, 50, 255, false, '...');
          }
          else
          {
             $zone_trunc = $zone;
          }
    
          if (is_numeric($offset))
          {
             $selected = ($offset == $default) ? ' selected="selected"' : '';
             $tz_select .= '<option title="' . $zone . '" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
          }
       }
    
       return $tz_select;
    }
    
    // Functions handling topic/post tracking/marking
    
    /**
    * Marks a topic/forum as read
    * Marks a topic as posted to
    *
    * @param int $user_id can only be used with $mode == 'post'
    */
    function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
    {
       global $db, $user, $config;
    
       if ($mode == 'all')
       {
          if ($forum_id === false || !sizeof($forum_id))
          {
             if ($config['load_db_lastread'] && $user->data['is_registered'])
             {
                // Mark all forums read (index page)
                $db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
                $db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
                $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
             }
             else if ($config['load_anon_lastread'] || $user->data['is_registered'])
             {
                $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
                $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
    
                unset($tracking_topics['tf']);
                unset($tracking_topics['t']);
                unset($tracking_topics['f']);
                $tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
    
                $user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
                $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
    
                unset($tracking_topics);
    
                if ($user->data['is_registered'])
                {
                   $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
                }
             }
          }
    
          return;
       }
       else if ($mode == 'topics')
       {
          // Mark all topics in forums read
          if (!is_array($forum_id))
          {
             $forum_id = array($forum_id);
          }
    
          // Add 0 to forums array to mark global announcements correctly
          // $forum_id[] = 0;
    
          if ($config['load_db_lastread'] && $user->data['is_registered'])
          {
             $sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
                WHERE user_id = {$user->data['user_id']}
                   AND " . $db->sql_in_set('forum_id', $forum_id);
             $db->sql_query($sql);
    
             $sql = 'SELECT forum_id
                FROM ' . FORUMS_TRACK_TABLE . "
                WHERE user_id = {$user->data['user_id']}
                   AND " . $db->sql_in_set('forum_id', $forum_id);
             $result = $db->sql_query($sql);
    
             $sql_update = array();
             while ($row = $db->sql_fetchrow($result))
             {
                $sql_update[] = (int) $row['forum_id'];
             }
             $db->sql_freeresult($result);
    
             if (sizeof($sql_update))
             {
                $sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
                   SET mark_time = ' . time() . "
                   WHERE user_id = {$user->data['user_id']}
                      AND " . $db->sql_in_set('forum_id', $sql_update);
                $db->sql_query($sql);
             }
    
             if ($sql_insert = array_diff($forum_id, $sql_update))
             {
                $sql_ary = array();
                foreach ($sql_insert as $f_id)
                {
                   $sql_ary[] = array(
                      'user_id'   => (int) $user->data['user_id'],
                      'forum_id'   => (int) $f_id,
                      'mark_time'   => time()
                   );
                }
    
                $db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
             }
          }
          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
          {
             $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
             $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
    
             foreach ($forum_id as $f_id)
             {
                $topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
    
                if (isset($tracking['tf'][$f_id]))
                {
                   unset($tracking['tf'][$f_id]);
                }
    
                foreach ($topic_ids36 as $topic_id36)
                {
                   unset($tracking['t'][$topic_id36]);
                }
    
                if (isset($tracking['f'][$f_id]))
                {
                   unset($tracking['f'][$f_id]);
                }
    
                $tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
             }
    
             if (isset($tracking['tf']) && empty($tracking['tf']))
             {
                unset($tracking['tf']);
             }
    
             $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
             $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
    
             unset($tracking);
          }
    
          return;
       }
       else if ($mode == 'topic')
       {
          if ($topic_id === false || $forum_id === false)
          {
             return;
          }
    
          if ($config['load_db_lastread'] && $user->data['is_registered'])
          {
             $sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
                SET mark_time = ' . (($post_time) ? $post_time : time()) . "
                WHERE user_id = {$user->data['user_id']}
                   AND topic_id = $topic_id";
             $db->sql_query($sql);
    
             // insert row
             if (!$db->sql_affectedrows())
             {
                $db->sql_return_on_error(true);
    
                $sql_ary = array(
                   'user_id'      => (int) $user->data['user_id'],
                   'topic_id'      => (int) $topic_id,
                   'forum_id'      => (int) $forum_id,
                   'mark_time'      => ($post_time) ? (int) $post_time : time(),
                );
    
                $db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
    
                $db->sql_return_on_error(false);
             }
          }
          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
          {
             $tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
             $tracking = ($tracking) ? tracking_unserialize($tracking) : array();
    
             $topic_id36 = base_convert($topic_id, 10, 36);
    
             if (!isset($tracking['t'][$topic_id36]))
             {
                $tracking['tf'][$forum_id][$topic_id36] = true;
             }
    
             $post_time = ($post_time) ? $post_time : time();
             $tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
    
             // If the cookie grows larger than 10000 characters we will remove the smallest value
             // This can result in old topics being unread - but most of the time it should be accurate...
             if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
             {
                //echo 'Cookie grown too large' . print_r($tracking, true);
    
                // We get the ten most minimum stored time offsets and its associated topic ids
                $time_keys = array();
                for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
                {
                   $min_value = min($tracking['t']);
                   $m_tkey = array_search($min_value, $tracking['t']);
                   unset($tracking['t'][$m_tkey]);
    
                   $time_keys[$m_tkey] = $min_value;
                }
    
                // Now remove the topic ids from the array...
                foreach ($tracking['tf'] as $f_id => $topic_id_ary)
                {
                   foreach ($time_keys as $m_tkey => $min_value)
                   {
                      if (isset($topic_id_ary[$m_tkey]))
                      {
                         $tracking['f'][$f_id] = $min_value;
                         unset($tracking['tf'][$f_id][$m_tkey]);
                      }
                   }
                }
    
                if ($user->data['is_registered'])
                {
                   $user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
                   $db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
                }
                else
                {
                   $tracking['l'] = max($time_keys);
                }
             }
    
             $user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
             $_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
          }
    
          return;
       }
       else if ($mode == 'post')
       {
          if ($topic_id === false)
          {
             return;
          }
    
          $use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
    
          if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
          {
             $db->sql_return_on_error(true);
    
             $sql_ary = array(
                'user_id'      => (int) $use_user_id,
                'topic_id'      => (int) $topic_id,
                'topic_posted'   => 1
             );
    
             $db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
    
             $db->sql_return_on_error(false);
          }
    
          return;
       }
    }
    
    /**
    * Get topic tracking info by using already fetched info
    */
    function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
    {
       global $config, $user;
    
       $last_read = array();
    
       if (!is_array($topic_ids))
       {
          $topic_ids = array($topic_ids);
       }
    
       foreach ($topic_ids as $topic_id)
       {
          if (!empty($rowset[$topic_id]['mark_time']))
          {
             $last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
          }
       }
    
       $topic_ids = array_diff($topic_ids, array_keys($last_read));
    
       if (sizeof($topic_ids))
       {
          $mark_time = array();
    
          // Get global announcement info
          if ($global_announce_list && sizeof($global_announce_list))
          {
             if (!isset($forum_mark_time[0]))
             {
                global $db;
    
                $sql = 'SELECT mark_time
                   FROM ' . FORUMS_TRACK_TABLE . "
                   WHERE user_id = {$user->data['user_id']}
                      AND forum_id = 0";
                $result = $db->sql_query($sql);
                $row = $db->sql_fetchrow($result);
                $db->sql_freeresult($result);
    
                if ($row)
                {
                   $mark_time[0] = $row['mark_time'];
                }
             }
             else
             {
                if ($forum_mark_time[0] !== false)
                {
                   $mark_time[0] = $forum_mark_time[0];
                }
             }
          }
    
          if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
          {
             $mark_time[$forum_id] = $forum_mark_time[$forum_id];
          }
    
          $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
    
          foreach ($topic_ids as $topic_id)
          {
             if ($global_announce_list && isset($global_announce_list[$topic_id]))
             {
                $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
             }
             else
             {
                $last_read[$topic_id] = $user_lastmark;
             }
          }
       }
    
       return $last_read;
    }
    
    /**
    * Get topic tracking info from db (for cookie based tracking only this function is used)
    */
    function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
    {
       global $config, $user;
    
       $last_read = array();
    
       if (!is_array($topic_ids))
       {
          $topic_ids = array($topic_ids);
       }
    
       if ($config['load_db_lastread'] && $user->data['is_registered'])
       {
          global $db;
    
          $sql = 'SELECT topic_id, mark_time
             FROM ' . TOPICS_TRACK_TABLE . "
             WHERE user_id = {$user->data['user_id']}
                AND " . $db->sql_in_set('topic_id', $topic_ids);
          $result = $db->sql_query($sql);
    
          while ($row = $db->sql_fetchrow($result))
          {
             $last_read[$row['topic_id']] = $row['mark_time'];
          }
          $db->sql_freeresult($result);
    
          $topic_ids = array_diff($topic_ids, array_keys($last_read));
    
          if (sizeof($topic_ids))
          {
             $sql = 'SELECT forum_id, mark_time
                FROM ' . FORUMS_TRACK_TABLE . "
                WHERE user_id = {$user->data['user_id']}
                   AND forum_id " .
                   (($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
             $result = $db->sql_query($sql);
    
             $mark_time = array();
             while ($row = $db->sql_fetchrow($result))
             {
                $mark_time[$row['forum_id']] = $row['mark_time'];
             }
             $db->sql_freeresult($result);
    
             $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
    
             foreach ($topic_ids as $topic_id)
             {
                if ($global_announce_list && isset($global_announce_list[$topic_id]))
                {
                   $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
                }
                else
                {
                   $last_read[$topic_id] = $user_lastmark;
                }
             }
          }
       }
       else if ($config['load_anon_lastread'] || $user->data['is_registered'])
       {
          global $tracking_topics;
    
          if (!isset($tracking_topics) || !sizeof($tracking_topics))
          {
             $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
             $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
          }
    
          if (!$user->data['is_registered'])
          {
             $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
          }
          else
          {
             $user_lastmark = $user->data['user_lastmark'];
          }
    
          foreach ($topic_ids as $topic_id)
          {
             $topic_id36 = base_convert($topic_id, 10, 36);
    
             if (isset($tracking_topics['t'][$topic_id36]))
             {
                $last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
             }
          }
    
          $topic_ids = array_diff($topic_ids, array_keys($last_read));
    
          if (sizeof($topic_ids))
          {
             $mark_time = array();
             if ($global_announce_list && sizeof($global_announce_list))
             {
                if (isset($tracking_topics['f'][0]))
                {
                   $mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
                }
             }
    
             if (isset($tracking_topics['f'][$forum_id]))
             {
                $mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
             }
    
             $user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
    
             foreach ($topic_ids as $topic_id)
             {
                if ($global_announce_list && isset($global_announce_list[$topic_id]))
                {
                   $last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
                }
                else
                {
                   $last_read[$topic_id] = $user_lastmark;
                }
             }
          }
       }
    
       return $last_read;
    }
    
    /**
    * Get list of unread topics
    *
    * @param int $user_id         User ID (or false for current user)
    * @param string $sql_extra      Extra WHERE SQL statement
    * @param string $sql_sort      ORDER BY SQL sorting statement
    * @param string $sql_limit      Limits the size of unread topics list, 0 for unlimited query
    * @param string $sql_limit_offset  Sets the offset of the first row to search, 0 to search from the start
    *
    * @return array[int][int]      Topic ids as keys, mark_time of topic as value
    */
    function get_unread_topics($user_id = false, $sql_extra = '', $sql_sort = '', $sql_limit = 1001, $sql_limit_offset = 0)
    {
       global $config, $db, $user;
    
       $user_id = ($user_id === false) ? (int) $user->data['user_id'] : (int) $user_id;
    
       // Data array we're going to return
       $unread_topics = array();
    
       if (empty($sql_sort))
       {
          $sql_sort = 'ORDER BY t.topic_last_post_time DESC';
       }
    
       if ($config['load_db_lastread'] && $user->data['is_registered'])
       {
          // Get list of the unread topics
          $last_mark = (int) $user->data['user_lastmark'];
    
          $sql_array = array(
             'SELECT'      => 't.topic_id, t.topic_last_post_time, tt.mark_time as topic_mark_time, ft.mark_time as forum_mark_time',
    
             'FROM'         => array(TOPICS_TABLE => 't'),
    
             'LEFT_JOIN'      => array(
                array(
                   'FROM'   => array(TOPICS_TRACK_TABLE => 'tt'),
                   'ON'   => "tt.user_id = $user_id AND t.topic_id = tt.topic_id",
                ),
                array(
                   'FROM'   => array(FORUMS_TRACK_TABLE => 'ft'),
                   'ON'   => "ft.user_id = $user_id AND t.forum_id = ft.forum_id",
                ),
             ),
    
             'WHERE'         => "
                 t.topic_last_post_time > $last_mark AND
                (
                (tt.mark_time IS NOT NULL AND t.topic_last_post_time > tt.mark_time) OR
                (tt.mark_time IS NULL AND ft.mark_time IS NOT NULL AND t.topic_last_post_time > ft.mark_time) OR
                (tt.mark_time IS NULL AND ft.mark_time IS NULL)
                )
                $sql_extra
                $sql_sort",
          );
    
          $sql = $db->sql_build_query('SELECT', $sql_array);
          $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset);
    
          while ($row = $db->sql_fetchrow($result))
          {
             $topic_id = (int) $row['topic_id'];
             $unread_topics[$topic_id] = ($row['topic_mark_time']) ? (int) $row['topic_mark_time'] : (($row['forum_mark_time']) ? (int) $row['forum_mark_time'] : $last_mark);
          }
          $db->sql_freeresult($result);
       }
       else if ($config['load_anon_lastread'] || $user->data['is_registered'])
       {
          global $tracking_topics;
    
          if (empty($tracking_topics))
          {
             $tracking_topics = request_var($config['cookie_name'] . '_track', '', false, true);
             $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
          }
    
          if (!$user->data['is_registered'])
          {
             $user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
          }
          else
          {
             $user_lastmark = (int) $user->data['user_lastmark'];
          }
    
          $sql = 'SELECT t.topic_id, t.forum_id, t.topic_last_post_time
             FROM ' . TOPICS_TABLE . ' t
             WHERE t.topic_last_post_time > ' . $user_lastmark . "
             $sql_extra
             $sql_sort";
          $result = $db->sql_query_limit($sql, $sql_limit, $sql_limit_offset);
    
          while ($row = $db->sql_fetchrow($result))
          {
             $forum_id = (int) $row['forum_id'];
             $topic_id = (int) $row['topic_id'];
             $topic_id36 = base_convert($topic_id, 10, 36);
    
             if (isset($tracking_topics['t'][$topic_id36]))
             {
                $last_read = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
    
                if ($row['topic_last_post_time'] > $last_read)
                {
                   $unread_topics[$topic_id] = $last_read;
                }
             }
             else if (isset($tracking_topics['f'][$forum_id]))
             {
                $mark_time = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
    
                if ($row['topic_last_post_time'] > $mark_time)
                {
                   $unread_topics[$topic_id] = $mark_time;
                }
             }
             else
             {
                $unread_topics[$topic_id] = $user_lastmark;
             }
          }
          $db->sql_freeresult($result);
       }
    
       return $unread_topics;
    }
    
    /**
    * Check for read forums and update topic tracking info accordingly
    *
    * @param int $forum_id the forum id to check
    * @param int $forum_last_post_time the forums last post time
    * @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
    * @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
    *
    * @return true if complete forum got marked read, else false.
    */
    function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
    {
       global $db, $tracking_topics, $user, $config, $auth;
    
       // Determine the users last forum mark time if not given.
       if ($mark_time_forum === false)
       {
          if ($config['load_db_lastread'] && $user->data['is_registered'])
          {
             $mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
          }
          else if ($config['load_anon_lastread'] || $user->data['is_registered'])
          {
             $tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
             $tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
    
             if (!$user->data['is_registered'])
             {
                $user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
             }
    
             $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'];
          }
       }
    
       // Handle update of unapproved topics info.
       // Only update for moderators having m_approve permission for the forum.
       $sql_update_unapproved = ($auth->acl_get('m_approve', $forum_id)) ? '': 'AND t.topic_approved = 1';
    
       // Check the forum for any left unread topics.
       // If there are none, we mark the forum as read.
       if ($config['load_db_lastread'] && $user->data['is_registered'])
       {
          if ($mark_time_forum >= $forum_last_post_time)
          {
             // We do not need to mark read, this happened before. Therefore setting this to true
             $row = true;
          }
          else
          {
             $sql = 'SELECT t.forum_id
                FROM ' . TOPICS_TABLE . ' t
                LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt
                   ON (tt.topic_id = t.topic_id
                      AND tt.user_id = ' . $user->data['user_id'] . ')
                WHERE t.forum_id = ' . $forum_id . '
                   AND t.topic_last_post_time > ' . $mark_time_forum . '
                   AND t.topic_moved_id = 0 ' .
                   $sql_update_unapproved . '
                   AND (tt.topic_id IS NULL
                      OR tt.mark_time < t.topic_last_post_time)';
             $result = $db->sql_query_limit($sql, 1);
             $row = $db->sql_fetchrow($result);
             $db->sql_freeresult($result);
          }
       }
       else if ($config['load_anon_lastread'] || $user->data['is_registered'])
       {
          // Get information from cookie
          $row = false;
    
          if (!isset($tracking_topics['tf'][$forum_id]))
          {
             // We do not need to mark read, this happened before. Therefore setting this to true
             $row = true;
          }
          else
          {
             $sql = 'SELECT t.topic_id
                FROM ' . TOPICS_TABLE . ' t
                WHERE t.forum_id = ' . $forum_id . '
                   AND t.topic_last_post_time > ' . $mark_time_forum . '
                   AND t.topic_moved_id = 0 ' .
                   $sql_update_unapproved;
             $result = $db->sql_query($sql);
    
             $check_forum = $tracking_topics['tf'][$forum_id];
             $unread = false;
    
             while ($row = $db->sql_fetchrow($result))
             {
                if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)]))
                {
                   $unread = true;
                   break;
                }
             }
             $db->sql_freeresult($result);
    
             $row = $unread;
          }
       }
       else
       {
          $row = true;
       }
    
       if (!$row)
       {
          markread('topics', $forum_id);
          return true;
       }
    
       return false;
    }
    
    /**
    * Transform an array into a serialized format
    */
    function tracking_serialize($input)
    {
       $out = '';
       foreach ($input as $key => $value)
       {
          if (is_array($value))
          {
             $out .= $key . ':(' . tracking_serialize($value) . ');';
          }
          else
          {
             $out .= $key . ':' . $value . ';';
          }
       }
       return $out;
    }
    
    /**
    * Transform a serialized array into an actual array
    */
    function tracking_unserialize($string, $max_depth = 3)
    {
       $n = strlen($string);
       if ($n > 10010)
       {
          die('Invalid data supplied');
       }
       $data = $stack = array();
       $key = '';
       $mode = 0;
       $level = &$data;
       for ($i = 0; $i < $n; ++$i)
       {
          switch ($mode)
          {
             case 0:
                switch ($string[$i])
                {
                   case ':':
                      $level[$key] = 0;
                      $mode = 1;
                   break;
                   case ')':
                      unset($level);
                      $level = array_pop($stack);
                      $mode = 3;
                   break;
                   default:
                      $key .= $string[$i];
                }
             break;
    
             case 1:
                switch ($string[$i])
                {
                   case '(':
                      if (sizeof($stack) >= $max_depth)
                      {
                         die('Invalid data supplied');
                      }
                      $stack[] = &$level;
                      $level[$key] = array();
                      $level = &$level[$key];
                      $key = '';
                      $mode = 0;
                   break;
                   default:
                      $level[$key] = $string[$i];
                      $mode = 2;
                   break;
                }
             break;
    
             case 2:
                switch ($string[$i])
                {
                   case ')':
                      unset($level);
                      $level = array_pop($stack);
                      $mode = 3;
                   break;
                   case ';':
                      $key = '';
                      $mode = 0;
                   break;
                   default:
                      $level[$key] .= $string[$i];
                   break;
                }
             break;
    
             case 3:
                switch ($string[$i])
                {
                   case ')':
                      unset($level);
                      $level = array_pop($stack);
                   break;
                   case ';':
                      $key = '';
                      $mode = 0;
                   break;
                   default:
                      die('Invalid data supplied');
                   break;
                }
             break;
          }
       }
    
       if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
       {
          die('Invalid data supplied');
       }
    
       return $level;
    }
    
    // Pagination functions
    
    /**
    * Pagination routine, generates page number sequence
    * tpl_prefix is for using different pagination blocks at one page
    */
    function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
    {
       global $template, $user;
    
       // Make sure $per_page is a valid value
       $per_page = ($per_page <= 0) ? 1 : $per_page;
    
       $seperator = '<span class="page-sep">' . $user->lang['COMMA_SEPARATOR'] . '</span>';
       $total_pages = ceil($num_items / $per_page);
    
       if ($total_pages == 1 || !$num_items)
       {
          return false;
       }
    
       $on_page = floor($start_item / $per_page) + 1;
       $url_delim = (strpos($base_url, '?') === false) ? '?' : ((strpos($base_url, '?') === strlen($base_url) - 1) ? '' : '&');
    
       $page_string = ($on_page == 1) ? '<strong>1</strong>' : '<a href="' . $base_url . '">1</a>';
    
       if ($total_pages > 5)
       {
          $start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
          $end_cnt = max(min($total_pages, $on_page + 4), 6);
    
          $page_string .= ($start_cnt > 1) ? '<span class="page-dots"> ... </span>' : $seperator;
    
          for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
          {
             $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
             if ($i < $end_cnt - 1)
             {
                $page_string .= $seperator;
             }
          }
    
          $page_string .= ($end_cnt < $total_pages) ? '<span class="page-dots"> ... </span>' : $seperator;
       }
       else
       {
          $page_string .= $seperator;
    
          for ($i = 2; $i < $total_pages; $i++)
          {
             $page_string .= ($i == $on_page) ? '<strong>' . $i . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($i - 1) * $per_page) . '">' . $i . '</a>';
             if ($i < $total_pages)
             {
                $page_string .= $seperator;
             }
          }
       }
    
       $page_string .= ($on_page == $total_pages) ? '<strong>' . $total_pages . '</strong>' : '<a href="' . $base_url . "{$url_delim}start=" . (($total_pages - 1) * $per_page) . '">' . $total_pages . '</a>';
    
       if ($add_prevnext_text)
       {
          if ($on_page != 1)
          {
             $page_string = '<a href="' . $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page) . '">' . $user->lang['PREVIOUS'] . '</a>&nbsp;&nbsp;' . $page_string;
          }
    
          if ($on_page != $total_pages)
          {
             $page_string .= '&nbsp;&nbsp;<a href="' . $base_url . "{$url_delim}start=" . ($on_page * $per_page) . '">' . $user->lang['NEXT'] . '</a>';
          }
       }
    
       $template->assign_vars(array(
          $tpl_prefix . 'BASE_URL'      => $base_url,
          'A_' . $tpl_prefix . 'BASE_URL'   => addslashes($base_url),
          $tpl_prefix . 'PER_PAGE'      => $per_page,
    
          $tpl_prefix . 'PREVIOUS_PAGE'   => ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
          $tpl_prefix . 'NEXT_PAGE'      => ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
          $tpl_prefix . 'TOTAL_PAGES'      => $total_pages,
       ));
    
       return $page_string;
    }
    
    /**
    * Return current page (pagination)
    */
    function on_page($num_items, $per_page, $start)
    {
       global $template, $user;
    
       // Make sure $per_page is a valid value
       $per_page = ($per_page <= 0) ? 1 : $per_page;
    
       $on_page = floor($start / $per_page) + 1;
    
       $template->assign_vars(array(
          'ON_PAGE'      => $on_page)
       );
    
       return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
    }
    
    // Server functions (building urls, redirecting...)
    
    /**
    * Append session id to url.
    * This function supports hooks.
    *
    * @param string $url The url the session id needs to be appended to (can have params)
    * @param mixed $params String or array of additional url parameters
    * @param bool $is_amp Is url using & (true) or & (false)
    * @param string $session_id Possibility to use a custom session id instead of the global one
    *
    * Examples:
    * <code>
    * append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&f=2");
    * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2');
    * append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
    * append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
    * </code>
    *
    */
    function append_sid($url, $params = false, $is_amp = true, $session_id = false)
    {
       global $_SID, $_EXTRA_URL, $phpbb_hook;
    
       if ($params === '' || (is_array($params) && empty($params)))
       {
          // Do not append the ? if the param-list is empty anyway.
          $params = false;
       }
    
       // Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropriately.
       // They could mimic most of what is within this function
       if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
       {
          if ($phpbb_hook->hook_return(__FUNCTION__))
          {
             return $phpbb_hook->hook_return_result(__FUNCTION__);
          }
       }
    
       $params_is_array = is_array($params);
    
       // Get anchor
       $anchor = '';
       if (strpos($url, '#') !== false)
       {
          list($url, $anchor) = explode('#', $url, 2);
          $anchor = '#' . $anchor;
       }
       else if (!$params_is_array && strpos($params, '#') !== false)
       {
          list($params, $anchor) = explode('#', $params, 2);
          $anchor = '#' . $anchor;
       }
    
       // Handle really simple cases quickly
       if ($_SID == '' && $session_id === false && empty($_EXTRA_URL) && !$params_is_array && !$anchor)
       {
          if ($params === false)
          {
             return $url;
          }
    
          $url_delim = (strpos($url, '?') === false) ? '?' : (($is_amp) ? '&' : '&');
          return $url . ($params !== false ? $url_delim. $params : '');
       }
    
       // Assign sid if session id is not specified
       if ($session_id === false)
       {
          $session_id = $_SID;
       }
    
       $amp_delim = ($is_amp) ? '&' : '&';
       $url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
    
       // Appending custom url parameter?
       $append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
    
       // Use the short variant if possible ;)
       if ($params === false)
       {
          // Append session id
          if (!$session_id)
          {
             return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
          }
          else
          {
             return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
          }
       }
    
       // Build string if parameters are specified as array
       if (is_array($params))
       {
          $output = array();
    
          foreach ($params as $key => $item)
          {
             if ($item === NULL)
             {
                continue;
             }
    
             if ($key == '#')
             {
                $anchor = '#' . $item;
                continue;
             }
    
             $output[] = $key . '=' . $item;
          }
    
          $params = implode($amp_delim, $output);
       }
    
       // Append session id and parameters (even if they are empty)
       // If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
       return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
    }
    
    /**
    * Generate board url (example: http://www.example.com/phpBB)
    *
    * @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
    *
    * @return string the generated board url
    */
    function generate_board_url($without_script_path = false)
    {
       global $config, $user;
    
       $server_name = $user->host;
       $server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
    
       // Forcing server vars is the only way to specify/override the protocol
       if ($config['force_server_vars'] || !$server_name)
       {
          $server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
          $server_name = $config['server_name'];
          $server_port = (int) $config['server_port'];
          $script_path = $config['script_path'];
    
          $url = $server_protocol . $server_name;
          $cookie_secure = $config['cookie_secure'];
       }
       else
       {
          // Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
          $cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
          $url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
    
          $script_path = $user->page['root_script_path'];
       }
    
       if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80)))
       {
          // HTTP HOST can carry a port number (we fetch $user->host, but for old versions this may be true)
          if (strpos($server_name, ':') === false)
          {
             $url .= ':' . $server_port;
          }
       }
    
       if (!$without_script_path)
       {
          $url .= $script_path;
       }
    
       // Strip / from the end
       if (substr($url, -1, 1) == '/')
       {
          $url = substr($url, 0, -1);
       }
    
       return $url;
    }
    
    /**
    * Redirects the user to another page then exits the script nicely
    * This function is intended for urls within the board. It's not meant to redirect to cross-domains.
    *
    * @param string $url The url to redirect to
    * @param bool $return If true, do not redirect but return the sanitized URL. Default is no return.
    * @param bool $disable_cd_check If true, redirect() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false.
    */
    function redirect($url, $return = false, $disable_cd_check = false)
    {
       global $db, $cache, $config, $user, $phpbb_root_path;
    
       $failover_flag = false;
    
       if (empty($user->lang))
       {
          $user->add_lang('common');
       }
    
       if (!$return)
       {
          garbage_collection();
       }
    
       // Make sure no &'s are in, this will break the redirect
       $url = str_replace('&', '&', $url);
    
       // Determine which type of redirect we need to handle...
       $url_parts = @parse_url($url);
    
       if ($url_parts === false)
       {
          // Malformed url, redirect to current page...
          $url = generate_board_url() . '/' . $user->page['page'];
       }
       else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
       {
          // Attention: only able to redirect within the same domain if $disable_cd_check is false (yourdomain.com -> www.yourdomain.com will not work)
          if (!$disable_cd_check && $url_parts['host'] !== $user->host)
          {
             $url = generate_board_url();
          }
       }
       else if ($url[0] == '/')
       {
          // Absolute uri, prepend direct url...
          $url = generate_board_url(true) . $url;
       }
       else
       {
          // Relative uri
          $pathinfo = pathinfo($url);
    
          if (!$disable_cd_check && !file_exists($pathinfo['dirname'] . '/'))
          {
             $url = str_replace('../', '', $url);
             $pathinfo = pathinfo($url);
    
             if (!file_exists($pathinfo['dirname'] . '/'))
             {
                // fallback to "last known user page"
                // at least this way we know the user does not leave the phpBB root
                $url = generate_board_url() . '/' . $user->page['page'];
                $failover_flag = true;
             }
          }
    
          if (!$failover_flag)
          {
             // Is the uri pointing to the current directory?
             if ($pathinfo['dirname'] == '.')
             {
                $url = str_replace('./', '', $url);
    
                // Strip / from the beginning
                if ($url && substr($url, 0, 1) == '/')
                {
                   $url = substr($url, 1);
                }
    
                if ($user->page['page_dir'])
                {
                   $url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
                }
                else
                {
                   $url = generate_board_url() . '/' . $url;
                }
             }
             else
             {
                // Used ./ before, but $phpbb_root_path is working better with urls within another root path
                $root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
                $page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
                $intersection = array_intersect_assoc($root_dirs, $page_dirs);
    
                $root_dirs = array_diff_assoc($root_dirs, $intersection);
                $page_dirs = array_diff_assoc($page_dirs, $intersection);
    
                $dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
    
                // Strip / from the end
                if ($dir && substr($dir, -1, 1) == '/')
                {
                   $dir = substr($dir, 0, -1);
                }
    
                // Strip / from the beginning
                if ($dir && substr($dir, 0, 1) == '/')
                {
                   $dir = substr($dir, 1);
                }
    
                $url = str_replace($pathinfo['dirname'] . '/', '', $url);
    
                // Strip / from the beginning
                if (substr($url, 0, 1) == '/')
                {
                   $url = substr($url, 1);
                }
    
                $url = (!empty($dir) ? $dir . '/' : '') . $url;
                $url = generate_board_url() . '/' . $url;
             }
          }
       }
    
       // Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
       if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
       {
          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
       }
    
       // Now, also check the protocol and for a valid url the last time...
       $allowed_protocols = array('http', 'https', 'ftp', 'ftps');
       $url_parts = parse_url($url);
    
       if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))
       {
          trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
       }
    
       if ($return)
       {
          return $url;
       }
    
       // Redirect via an HTML form for PITA webservers
       if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
       {
          header('Refresh: 0; URL=' . $url);
    
          echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
          echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">';
          echo '<head>';
          echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
          echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&', $url) . '" />';
          echo '<title>' . $user->lang['REDIRECT'] . '</title>';
          echo '</head>';
          echo '<body>';
          echo '<div style="text-align: center;">' . sprintf($user->lang['URL_REDIRECT'], '<a href="' . str_replace('&', '&', $url) . '">', '</a>') . '</div>';
          echo '</body>';
          echo '</html>';
    
          exit;
       }
    
       // Behave as per HTTP/1.1 spec for others
       header('Location: ' . $url);
       exit;
    }
    
    /**
    * Re-Apply session id after page reloads
    */
    function reapply_sid($url)
    {
       global $phpEx, $phpbb_root_path;
    
       if ($url === "index.$phpEx")
       {
          return append_sid("index.$phpEx");
       }
       else if ($url === "{$phpbb_root_path}index.$phpEx")
       {
          return append_sid("{$phpbb_root_path}index.$phpEx");
       }
    
       // Remove previously added sid
       if (strpos($url, 'sid=') !== false)
       {
          // All kind of links
          $url = preg_replace('/(\?)?(&|&)?sid=[a-z0-9]+/', '', $url);
          // if the sid was the first param, make the old second as first ones
          $url = preg_replace("/$phpEx(&|&)+?/", "$phpEx?", $url);
       }
    
       return append_sid($url);
    }
    
    /**
    * Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
    */
    function build_url($strip_vars = false)
    {
       global $user, $phpbb_root_path;
    
       // Append SID
       $redirect = append_sid($user->page['page'], false, false);
    
       // Add delimiter if not there...
       if (strpos($redirect, '?') === false)
       {
          $redirect .= '?';
       }
    
       // Strip vars...
       if ($strip_vars !== false && strpos($redirect, '?') !== false)
       {
          if (!is_array($strip_vars))
          {
             $strip_vars = array($strip_vars);
          }
    
          $query = $_query = array();
    
          $args = substr($redirect, strpos($redirect, '?') + 1);
          $args = ($args) ? explode('&', $args) : array();
          $redirect = substr($redirect, 0, strpos($redirect, '?'));
    
          foreach ($args as $argument)
          {
             $arguments = explode('=', $argument);
             $key = $arguments[0];
             unset($arguments[0]);
    
             if ($key === '')
             {
                continue;
             }
    
             $query[$key] = implode('=', $arguments);
          }
    
          // Strip the vars off
          foreach ($strip_vars as $strip)
          {
             if (isset($query[$strip]))
             {
                unset($query[$strip]);
             }
          }
    
          // Glue the remaining parts together... already urlencoded
          foreach ($query as $key => $value)
          {
             $_query[] = $key . '=' . $value;
          }
          $query = implode('&', $_query);
    
          $redirect .= ($query) ? '?' . $query : '';
       }
    
       // We need to be cautious here.
       // On some situations, the redirect path is an absolute URL, sometimes a relative path
       // For a relative path, let's prefix it with $phpbb_root_path to point to the correct location,
       // else we use the URL directly.
       $url_parts = @parse_url($redirect);
    
       // URL
       if ($url_parts !== false && !empty($url_parts['scheme']) && !empty($url_parts['host']))
       {
          return str_replace('&', '&', $redirect);
       }
    
       return $phpbb_root_path . str_replace('&', '&', $redirect);
    }
    
    /**
    * Meta refresh assignment
    * Adds META template variable with meta http tag.
    *
    * @param int $time Time in seconds for meta refresh tag
    * @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned
    * @param bool $disable_cd_check If true, meta_refresh() will redirect to an external domain. If false, the redirect point to the boards url if it does not match the current domain. Default is false.
    */
    function meta_refresh($time, $url, $disable_cd_check = false)
    {
       global $template;
    
       $url = redirect($url, true, $disable_cd_check);
       $url = str_replace('&', '&', $url);
    
       // For XHTML compatibility we change back & to &
       $template->assign_vars(array(
          'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
       );
    
       return $url;
    }
    
    /**
    * Outputs correct status line header.
    *
    * Depending on php sapi one of the two following forms is used:
    *
    * Status: 404 Not Found
    *
    * HTTP/1.x 404 Not Found
    *
    * HTTP version is taken from HTTP_VERSION environment variable,
    * and defaults to 1.0.
    *
    * Sample usage:
    *
    * send_status_line(404, 'Not Found');
    *
    * @param int $code HTTP status code
    * @param string $message Message for the status code
    * @return void
    */
    function send_status_line($code, $message)
    {
       if (substr(strtolower(@php_sapi_name()), 0, 3) === 'cgi')
       {
          // in theory, we shouldn't need that due to php doing it. Reality offers a differing opinion, though
          header("Status: $code $message", true, $code);
       }
       else
       {
          if (!empty($_SERVER['SERVER_PROTOCOL']))
          {
             $version = $_SERVER['SERVER_PROTOCOL'];
          }
          else
          {
             $version = 'HTTP/1.0';
          }
          header("$version $code $message", true, $code);
       }
    }
    
    //Form validation
    
    
    /**
    * Add a secret hash   for use in links/GET requests
    * @param string  $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply
    * @return string the hash
    
    */
    function generate_link_hash($link_name)
    {
       global $user;
    
       if (!isset($user->data["hash_$link_name"]))
       {
          $user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8);
       }
    
       return $user->data["hash_$link_name"];
    }
    
    
    /**
    * checks a link hash - for GET requests
    * @param string $token the submitted token
    * @param string $link_name The name of the link
    * @return boolean true if all is fine
    */
    function check_link_hash($token, $link_name)
    {
       return $token === generate_link_hash($link_name);
    }
    
    /**
    * Add a secret token to the form (requires the S_FORM_TOKEN template variable)
    * @param string  $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
    */
    function add_form_key($form_name)
    {
       global $config, $template, $user;
    
       $now = time();
       $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
       $token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
    
       $s_fields = build_hidden_fields(array(
          'creation_time' => $now,
          'form_token'   => $token,
       ));
    
       $template->assign_vars(array(
          'S_FORM_TOKEN'   => $s_fields,
       ));
    }
    
    /**
    * Check the form key. Required for all altering actions not secured by confirm_box
    * @param string  $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply
    * @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting.
    * @param string $return_page The address for the return link
    * @param bool $trigger If true, the function will triger an error when encountering an invalid form
    */
    function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false)
    {
       global $config, $user;
    
       if ($timespan === false)
       {
          // we enforce a minimum value of half a minute here.
          $timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
       }
    
       if (isset($_POST['creation_time']) && isset($_POST['form_token']))
       {
          $creation_time   = abs(request_var('creation_time', 0));
          $token = request_var('form_token', '');
    
          $diff = time() - $creation_time;
    
          // If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)...
          if ($diff && ($diff <= $timespan || $timespan === -1))
          {
             $token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
             $key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
    
             if ($key === $token)
             {
                return true;
             }
          }
       }
    
       if ($trigger)
       {
          trigger_error($user->lang['FORM_INVALID'] . $return_page);
       }
    
       return false;
    }
    
    // Message/Login boxes
    
    /**
    * Build Confirm box
    * @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
    * @param string $title Title/Message used for confirm box.
    *      message text is _CONFIRM appended to title.
    *      If title cannot be found in user->lang a default one is displayed
    *      If title_CONFIRM cannot be found in user->lang the text given is used.
    * @param string $hidden Hidden variables
    * @param string $html_body Template used for confirm box
    * @param string $u_action Custom form action
    */
    function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
    {
       global $user, $template, $db;
       global $phpEx, $phpbb_root_path;
    
       if (isset($_POST['cancel']))
       {
          return false;
       }
    
       $confirm = false;
       if (isset($_POST['confirm']))
       {
          // language frontier
          if ($_POST['confirm'] === $user->lang['YES'])
          {
             $confirm = true;
          }
       }
    
       if ($check && $confirm)
       {
          $user_id = request_var('confirm_uid', 0);
          $session_id = request_var('sess', '');
          $confirm_key = request_var('confirm_key', '');
    
          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'])
          {
             return false;
          }
    
          // Reset user_last_confirm_key
          $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
             WHERE user_id = " . $user->data['user_id'];
          $db->sql_query($sql);
    
          return true;
       }
       else if ($check)
       {
          return false;
       }
    
       $s_hidden_fields = build_hidden_fields(array(
          'confirm_uid'   => $user->data['user_id'],
          'sess'         => $user->session_id,
          'sid'         => $user->session_id,
       ));
    
       // generate activation key
       $confirm_key = gen_rand_string(10);
    
       if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
       {
          adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
       }
       else
       {
          page_header(((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]), false);
       }
    
       $template->set_filenames(array(
          'body' => $html_body)
       );
    
       // If activation key already exist, we better do not re-use the key (something very strange is going on...)
       if (request_var('confirm_key', ''))
       {
          // This should not occur, therefore we cancel the operation to safe the user
          return false;
       }
    
       // re-add sid / transform & to & for user->page (user->page is always using &)
       $use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&', $user->page['page']);
       $u_action = reapply_sid($use_page);
       $u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key;
    
       $template->assign_vars(array(
          'MESSAGE_TITLE'      => (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
          'MESSAGE_TEXT'      => (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
    
          'YES_VALUE'         => $user->lang['YES'],
          'S_CONFIRM_ACTION'   => $u_action,
          'S_HIDDEN_FIELDS'   => $hidden . $s_hidden_fields)
       );
    
       $sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
          WHERE user_id = " . $user->data['user_id'];
       $db->sql_query($sql);
    
       if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
       {
          adm_page_footer();
       }
       else
       {
          page_footer();
       }
    }
    
    /**
    * Generate login box or verify password
    */
    function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
    {
       global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
    
       if (!class_exists('phpbb_captcha_factory'))
       {
          include($phpbb_root_path . 'includes/captcha/captcha_factory.' . $phpEx);
       }
    
       $err = '';
    
       // Make sure user->setup() has been called
       if (empty($user->lang))
       {
          $user->setup();
       }
    
       // Print out error if user tries to authenticate as an administrator without having the privileges...
       if ($admin && !$auth->acl_get('a_'))
       {
          // Not authd
          // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
          if ($user->data['is_registered'])
          {
             add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
          }
          trigger_error('NO_AUTH_ADMIN');
       }
    
       if (isset($_POST['login']))
       {
          // Get credential
          if ($admin)
          {
             $credential = request_var('credential', '');
    
             if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
             {
                if ($user->data['is_registered'])
                {
                   add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
                }
                trigger_error('NO_AUTH_ADMIN');
             }
    
             $password   = request_var('password_' . $credential, '', true);
          }
          else
          {
             $password   = request_var('password', '', true);
          }
    
          $username   = request_var('username', '', true);
          $autologin   = (!empty($_POST['autologin'])) ? true : false;
          $viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
          $admin       = ($admin) ? 1 : 0;
          $viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
    
          // Check if the supplied username is equal to the one stored within the database if re-authenticating
          if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
          {
             // We log the attempt to use a different username...
             add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
             trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
          }
    
          // If authentication is successful we redirect user to previous page
          $result = $auth->login($username, $password, $autologin, $viewonline, $admin);
    
          // If admin authentication and login, we will log if it was a success or not...
          // We also break the operation on the first non-success login - it could be argued that the user already knows
          if ($admin)
          {
             if ($result['status'] == LOGIN_SUCCESS)
             {
                add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
             }
             else
             {
                // Only log the failed attempt if a real user tried to.
                // anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
                if ($user->data['is_registered'])
                {
                   add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
                }
             }
          }
    
          // The result parameter is always an array, holding the relevant information...
          if ($result['status'] == LOGIN_SUCCESS)
          {
             $redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
             $message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
             $l_redirect = ($admin) ? $user->lang['PROCEED_TO_ACP'] : (($redirect === "{$phpbb_root_path}index.$phpEx" || $redirect === "index.$phpEx") ? $user->lang['RETURN_INDEX'] : $user->lang['RETURN_PAGE']);
    
             // append/replace SID (may change during the session for AOL users)
             $redirect = reapply_sid($redirect);
    
             // Special case... the user is effectively banned, but we allow founders to login
             if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
             {
                return;
             }
    
             $redirect = meta_refresh(3, $redirect);
             trigger_error($message . '<br /><br />' . sprintf($l_redirect, '<a href="' . $redirect . '">', '</a>'));
          }
    
          // Something failed, determine what...
          if ($result['status'] == LOGIN_BREAK)
          {
             trigger_error($result['error_msg']);
          }
    
          // Special cases... determine
          switch ($result['status'])
          {
             case LOGIN_ERROR_ATTEMPTS:
    
                $captcha = phpbb_captcha_factory::get_instance($config['captcha_plugin']);
                $captcha->init(CONFIRM_LOGIN);
                // $captcha->reset();
    
                $template->assign_vars(array(
                   'CAPTCHA_TEMPLATE'         => $captcha->get_template(),
                ));
    
                $err = $user->lang[$result['error_msg']];
             break;
    
             case LOGIN_ERROR_PASSWORD_CONVERT:
                $err = sprintf(
                   $user->lang[$result['error_msg']],
                   ($config['email_enable']) ? '<a href="' . append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') . '">' : '',
                   ($config['email_enable']) ? '</a>' : '',
                   ($config['board_contact']) ? '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">' : '',
                   ($config['board_contact']) ? '</a>' : ''
                );
             break;
    
             // Username, password, etc...
             default:
                $err = $user->lang[$result['error_msg']];
    
                // Assign admin contact to some error messages
                if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
                {
                   $err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '<a href="mailto:' . htmlspecialchars($config['board_contact']) . '">', '</a>');
                }
    
             break;
          }
       }
    
       // Assign credential for username/password pair
       $credential = ($admin) ? md5(unique_id()) : false;
    
       $s_hidden_fields = array(
          'sid'      => $user->session_id,
       );
    
       if ($redirect)
       {
          $s_hidden_fields['redirect'] = $redirect;
       }
    
       if ($admin)
       {
          $s_hidden_fields['credential'] = $credential;
       }
    
       $s_hidden_fields = build_hidden_fields($s_hidden_fields);
    
       $template->assign_vars(array(
          'LOGIN_ERROR'      => $err,
          'LOGIN_EXPLAIN'      => $l_explain,
    
          'U_SEND_PASSWORD'       => ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
          'U_RESEND_ACTIVATION'   => ($config['require_activation'] == USER_ACTIVATION_SELF && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
          'U_TERMS_USE'         => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
          'U_PRIVACY'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
    
          'S_DISPLAY_FULL_LOGIN'   => ($s_display) ? true : false,
          'S_HIDDEN_FIELDS'       => $s_hidden_fields,
    
          'S_ADMIN_AUTH'         => $admin,
          'USERNAME'            => ($admin) ? $user->data['username'] : '',
    
          'USERNAME_CREDENTIAL'   => 'username',
          'PASSWORD_CREDENTIAL'   => ($admin) ? 'password_' . $credential : 'password',
       ));
    
       page_header($user->lang['LOGIN'], false);
    
       $template->set_filenames(array(
          'body' => 'login_body.html')
       );
       make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
    
       page_footer();
    }
    
    /**
    * Generate forum login box
    */
    function login_forum_box($forum_data)
    {
       global $db, $config, $user, $template, $phpEx;
    
       $password = request_var('password', '', true);
    
       $sql = 'SELECT forum_id
          FROM ' . FORUMS_ACCESS_TABLE . '
          WHERE forum_id = ' . $forum_data['forum_id'] . '
             AND user_id = ' . $user->data['user_id'] . "
             AND session_id = '" . $db->sql_escape($user->session_id) . "'";
       $result = $db->sql_query($sql);
       $row = $db->sql_fetchrow($result);
       $db->sql_freeresult($result);
    
       if ($row)
       {
          return true;
       }
    
       if ($password)
       {
          // Remove expired authorised sessions
          $sql = 'SELECT f.session_id
             FROM ' . FORUMS_ACCESS_TABLE . ' f
             LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
             WHERE s.session_id IS NULL';
          $result = $db->sql_query($sql);
    
          if ($row = $db->sql_fetchrow($result))
          {
             $sql_in = array();
             do
             {
                $sql_in[] = (string) $row['session_id'];
             }
             while ($row = $db->sql_fetchrow($result));
    
             // Remove expired sessions
             $sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
                WHERE ' . $db->sql_in_set('session_id', $sql_in);
             $db->sql_query($sql);
          }
          $db->sql_freeresult($result);
    
          if (phpbb_check_hash($password, $forum_data['forum_password']))
          {
             $sql_ary = array(
                'forum_id'      => (int) $forum_data['forum_id'],
                'user_id'      => (int) $user->data['user_id'],
                'session_id'   => (string) $user->session_id,
             );
    
             $db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
    
             return true;
          }
    
          $template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
       }
    
       page_header($user->lang['LOGIN'], false);
    
       $template->assign_vars(array(
          'S_LOGIN_ACTION'      => build_url(array('f')),
          'S_HIDDEN_FIELDS'      => build_hidden_fields(array('f' => $forum_data['forum_id'])))
       );
    
       $template->set_filenames(array(
          'body' => 'login_forum.html')
       );
    
       page_footer();
    }
    
    // Little helpers
    
    /**
    * Little helper for the build_hidden_fields function
    */
    function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
    {
       $hidden_fields = '';
    
       if (!is_array($value))
       {
          $value = ($stripslashes) ? stripslashes($value) : $value;
          $value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
    
          $hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
       }
       else
       {
          foreach ($value as $_key => $_value)
          {
             $_key = ($stripslashes) ? stripslashes($_key) : $_key;
             $_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
    
             $hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
          }
       }
    
       return $hidden_fields;
    }
    
    /**
    * Build simple hidden fields from array
    *
    * @param array $field_ary an array of values to build the hidden field from
    * @param bool $specialchar if true, keys and values get specialchared
    * @param bool $stripslashes if true, keys and values get stripslashed
    *
    * @return string the hidden fields
    */
    function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
    {
       $s_hidden_fields = '';
    
       foreach ($field_ary as $name => $vars)
       {
          $name = ($stripslashes) ? stripslashes($name) : $name;
          $name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
    
          $s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
       }
    
       return $s_hidden_fields;
    }
    
    /**
    * Parse cfg file
    */
    function parse_cfg_file($filename, $lines = false)
    {
       $parsed_items = array();
    
       if ($lines === false)
       {
          $lines = file($filename);
       }
    
       foreach ($lines as $line)
       {
          $line = trim($line);
    
          if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
          {
             continue;
          }
    
          // Determine first occurrence, since in values the equal sign is allowed
          $key = strtolower(trim(substr($line, 0, $delim_pos)));
          $value = trim(substr($line, $delim_pos + 1));
    
          if (in_array($value, array('off', 'false', '0')))
          {
             $value = false;
          }
          else if (in_array($value, array('on', 'true', '1')))
          {
             $value = true;
          }
          else if (!trim($value))
          {
             $value = '';
          }
          else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
          {
             $value = substr($value, 1, sizeof($value)-2);
          }
    
          $parsed_items[$key] = $value;
       }
       
       if (isset($parsed_items['inherit_from']) && isset($parsed_items['name']) && $parsed_items['inherit_from'] == $parsed_items['name'])
       {
          unset($parsed_items['inherit_from']);
       }
    
       return $parsed_items;
    }
    
    /**
    * Add log event
    */
    function add_log()
    {
       global $db, $user;
    
       // In phpBB 3.1.x i want to have logging in a class to be able to control it
       // For now, we need a quite hakish approach to circumvent logging for some actions
       // @todo implement cleanly
       if (!empty($GLOBALS['skip_add_log']))
       {
          return false;
       }
    
       $args = func_get_args();
    
       $mode         = array_shift($args);
       $reportee_id   = ($mode == 'user') ? intval(array_shift($args)) : '';
       $forum_id      = ($mode == 'mod') ? intval(array_shift($args)) : '';
       $topic_id      = ($mode == 'mod') ? intval(array_shift($args)) : '';
       $action         = array_shift($args);
       $data         = (!sizeof($args)) ? '' : serialize($args);
    
       $sql_ary = array(
          'user_id'      => (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
          'log_ip'      => $user->ip,
          'log_time'      => time(),
          'log_operation'   => $action,
          'log_data'      => $data,
       );
    
       switch ($mode)
       {
          case 'admin':
             $sql_ary['log_type'] = LOG_ADMIN;
          break;
    
          case 'mod':
             $sql_ary += array(
                'log_type'   => LOG_MOD,
                'forum_id'   => $forum_id,
                'topic_id'   => $topic_id
             );
          break;
    
          case 'user':
             $sql_ary += array(
                'log_type'      => LOG_USERS,
                'reportee_id'   => $reportee_id
             );
          break;
    
          case 'critical':
             $sql_ary['log_type'] = LOG_CRITICAL;
          break;
    
          default:
             return false;
       }
    
       $db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
    
       return $db->sql_nextid();
    }
    
    /**
    * Return a nicely formatted backtrace.
    *
    * Turns the array returned by debug_backtrace() into HTML markup.
    * Also filters out absolute paths to phpBB root.
    *
    * @return string   HTML markup
    */
    function get_backtrace()
    {
       $output = '<div style="font-family: monospace;">';
       $backtrace = debug_backtrace();
    
       // We skip the first one, because it only shows this file/function
       unset($backtrace[0]);
    
       foreach ($backtrace as $trace)
       {
          // Strip the current directory from path
          $trace['file'] = (empty($trace['file'])) ? '(not given by php)' : htmlspecialchars(phpbb_filter_root_path($trace['file']));
          $trace['line'] = (empty($trace['line'])) ? '(not given by php)' : $trace['line'];
    
          // Only show function arguments for include etc.
          // Other parameters may contain sensible information
          $argument = '';
          if (!empty($trace['args'][0]) && in_array($trace['function'], array('include', 'require', 'include_once', 'require_once')))
          {
             $argument = htmlspecialchars(phpbb_filter_root_path($trace['args'][0]));
          }
    
          $trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
          $trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
    
          $output .= '<br />';
          $output .= '<b>FILE:</b> ' . $trace['file'] . '<br />';
          $output .= '<b>LINE:</b> ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '<br />';
    
          $output .= '<b>CALL:</b> ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']);
          $output .= '(' . (($argument !== '') ? "'$argument'" : '') . ')<br />';
       }
       $output .= '</div>';
       return $output;
    }
    
    /**
    * This function returns a regular expression pattern for commonly used expressions
    * Use with / as delimiter for email mode and # for url modes
    * mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
    */
    function get_preg_expression($mode)
    {
       switch ($mode)
       {
          case 'email':
             // Regex written by James Watts and Francisco Jose Martin Moreno
             // http://fightingforalostcause.net/misc/2006/compare-email-regex.php
             return '([\w\!\#$\%\&\'\*\+\-\/\=\?\^\`{\|\}\~]+\.)*(?:[\w\!\#$\%\'\*\+\-\/\=\?\^\`{\|\}\~]|&)+@((((([a-z0-9]{1}[a-z0-9\-]{0,62}[a-z0-9]{1})|[a-z])\.)+[a-z]{2,63})|(\d{1,3}\.){3}\d{1,3}(\:\d{1,5})?)';
          break;
    
          case 'bbcode_htm':
             return array(
                '#<!\-\- e \-\-><a href="mailto:(.*?)">.*?</a><!\-\- e \-\->#',
                '#<!\-\- l \-\-><a (?:class="[\w-]+" )?href="(.*?)(?:(&|\?)sid=[0-9a-f]{32})?" onclick="window\.open\(this\.href\);return false;">.*?</a><!\-\- l \-\->#',
                '#<!\-\- ([mw]) \-\-><a (?:class="[\w-]+" )?href="(.*?)" onclick="window\.open\(this\.href\);return false;">.*?</a><!\-\- \1 \-\->#',
                '#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#',
                '#<!\-\- .*? \-\->#s',
                '#<.*?>#s',
             );
          break;
    
          // Whoa these look impressive!
          // The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses
          // can be found in the develop directory
          case 'ipv4':
             return '#^(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$#';
          break;
    
          case 'ipv6':
             return '#^(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){0,5}(?:[\dA-F]{1,4}(?::[\dA-F]{1,4})?|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:)|(?:::))$#i';
          break;
    
          case 'url':
          case 'url_inline':
             $inline = ($mode == 'url') ? ')' : '';
             $scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..."
             // generated with regex generation file in the develop folder
             return "[a-z]$scheme*:/{2}(?:(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+|[0-9.]+|\[[a-z0-9.]+:[a-z0-9.]+:[a-z0-9.:]+\])(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
          break;
    
          case 'www_url':
          case 'www_url_inline':
             $inline = ($mode == 'www_url') ? ')' : '';
             return "www\.(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})+(?::\d*)?(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
          break;
    
          case 'relative_url':
          case 'relative_url_inline':
             $inline = ($mode == 'relative_url') ? ')' : '';
             return "(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*(?:/(?:[a-z0-9\-._~!$&'($inline*+,;=:@|]+|%[\dA-F]{2})*)*(?:\?(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?(?:\#(?:[a-z0-9\-._~!$&'($inline*+,;=:@/?|]+|%[\dA-F]{2})*)?";
          break;
    
          case 'table_prefix':
             return '#^[a-zA-Z][a-zA-Z0-9_]*$#';
          break;
       }
    
       return '';
    }
    
    /**
    * Generate regexp for naughty words censoring
    * Depends on whether installed PHP version supports unicode properties
    *
    * @param string   $word         word template to be replaced
    * @param bool   $use_unicode   whether or not to take advantage of PCRE supporting unicode
    *
    * @return string $preg_expr      regex to use with word censor
    */
    function get_censor_preg_expression($word, $use_unicode = true)
    {
       static $unicode_support = null;
    
       // Check whether PHP version supports unicode properties
       if (is_null($unicode_support))
       {
          $unicode_support = ((version_compare(PHP_VERSION, '5.1.0', '>=') || (version_compare(PHP_VERSION, '5.0.0-dev', '<=') && version_compare(PHP_VERSION, '4.4.0', '>='))) && @preg_match('/\p{L}/u', 'a') !== false) ? true : false;
       }
    
       // Unescape the asterisk to simplify further conversions
       $word = str_replace('\*', '*', preg_quote($word, '#'));
    
       if ($use_unicode && $unicode_support)
       {
          // Replace asterisk(s) inside the pattern, at the start and at the end of it with regexes
          $word = preg_replace(array('#(?<=[\p{Nd}\p{L}_])\*+(?=[\p{Nd}\p{L}_])#iu', '#^\*+#', '#\*+$#'), array('([\x20]*?|[\p{Nd}\p{L}_-]*?)', '[\p{Nd}\p{L}_-]*?', '[\p{Nd}\p{L}_-]*?'), $word);
    
          // Generate the final substitution
          $preg_expr = '#(?<![\p{Nd}\p{L}_-])(' . $word . ')(?![\p{Nd}\p{L}_-])#iu';
       }
       else
       {
          // Replace the asterisk inside the pattern, at the start and at the end of it with regexes
          $word = preg_replace(array('#(?<=\S)\*+(?=\S)#iu', '#^\*+#', '#\*+$#'), array('(\x20*?\S*?)', '\S*?', '\S*?'), $word);
    
          // Generate the final substitution
          $preg_expr = '#(?<!\S)(' . $word . ')(?!\S)#iu';
       }
    
       return $preg_expr;
    }
    
    /**
    * Returns the first block of the specified IPv6 address and as many additional
    * ones as specified in the length paramater.
    * If length is zero, then an empty string is returned.
    * If length is greater than 3 the complete IP will be returned
    */
    function short_ipv6($ip, $length)
    {
       if ($length < 1)
       {
          return '';
       }
    
       // extend IPv6 addresses
       $blocks = substr_count($ip, ':') + 1;
       if ($blocks < 9)
       {
          $ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip);
       }
       if ($ip[0] == ':')
       {
          $ip = '0000' . $ip;
       }
       if ($length < 4)
       {
          $ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length));
       }
    
       return $ip;
    }
    
    /**
    * Wrapper for php's checkdnsrr function.
    *
    * @param string $host   Fully-Qualified Domain Name
    * @param string $type   Resource record type to lookup
    *                  Supported types are: MX (default), A, AAAA, NS, TXT, CNAME
    *                  Other types may work or may not work
    *
    * @return mixed      true if entry found,
    *               false if entry not found,
    *               null if this function is not supported by this environment
    *
    * Since null can also be returned, you probably want to compare the result
    * with === true or === false,
    *
    * @author bantu
    */
    function phpbb_checkdnsrr($host, $type = 'MX')
    {
       // The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain)
       if (substr($host, -1) == '.')
       {
          $host_fqdn = $host;
          $host = substr($host, 0, -1);
       }
       else
       {
          $host_fqdn = $host . '.';
       }
       // $host      has format   some.host.example.com
       // $host_fqdn   has format   some.host.example.com.
    
       // If we're looking for an A record we can use gethostbyname()
       if ($type == 'A' && function_exists('gethostbyname'))
       {
          return (@gethostbyname($host_fqdn) == $host_fqdn) ? false : true;
       }
    
       // checkdnsrr() is available on Windows since PHP 5.3,
       // but until 5.3.3 it only works for MX records
       // See: http://bugs.php.net/bug.php?id=51844
    
       // Call checkdnsrr() if
       // we're looking for an MX record or
       // we're not on Windows or
       // we're running a PHP version where #51844 has been fixed
    
       // checkdnsrr() supports AAAA since 5.0.0
       // checkdnsrr() supports TXT since 5.2.4
       if (
          ($type == 'MX' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.3', '>=')) &&
          ($type != 'AAAA' || version_compare(PHP_VERSION, '5.0.0', '>=')) &&
          ($type != 'TXT' || version_compare(PHP_VERSION, '5.2.4', '>=')) &&
          function_exists('checkdnsrr')
       )
       {
          return checkdnsrr($host_fqdn, $type);
       }
    
       // dns_get_record() is available since PHP 5; since PHP 5.3 also on Windows,
       // but on Windows it does not work reliable for AAAA records before PHP 5.3.1
    
       // Call dns_get_record() if
       // we're not looking for an AAAA record or
       // we're not on Windows or
       // we're running a PHP version where AAAA lookups work reliable
       if (
          ($type != 'AAAA' || DIRECTORY_SEPARATOR != '\\' || version_compare(PHP_VERSION, '5.3.1', '>=')) &&
          function_exists('dns_get_record')
       )
       {
          // dns_get_record() expects an integer as second parameter
          // We have to convert the string $type to the corresponding integer constant.
          $type_constant = 'DNS_' . $type;
          $type_param = (defined($type_constant)) ? constant($type_constant) : DNS_ANY;
    
          // dns_get_record() might throw E_WARNING and return false for records that do not exist
          $resultset = @dns_get_record($host_fqdn, $type_param);
    
          if (empty($resultset) || !is_array($resultset))
          {
             return false;
          }
          else if ($type_param == DNS_ANY)
          {
             // $resultset is a non-empty array
             return true;
          }
    
          foreach ($resultset as $result)
          {
             if (
                isset($result['host']) && $result['host'] == $host &&
                isset($result['type']) && $result['type'] == $type
             )
             {
                return true;
             }
          }
    
          return false;
       }
    
       // If we're on Windows we can still try to call nslookup via exec() as a last resort
       if (DIRECTORY_SEPARATOR == '\\' && function_exists('exec'))
       {
          @exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host_fqdn), $output);
    
          // If output is empty, the nslookup failed
          if (empty($output))
          {
             return NULL;
          }
    
          foreach ($output as $line)
          {
             $line = trim($line);
    
             if (empty($line))
             {
                continue;
             }
    
             // Squash tabs and multiple whitespaces to a single whitespace.
             $line = preg_replace('/\s+/', ' ', $line);
    
             switch ($type)
             {
                case 'MX':
                   if (stripos($line, "$host MX") === 0)
                   {
                      return true;
                   }
                break;
    
                case 'NS':
                   if (stripos($line, "$host nameserver") === 0)
                   {
                      return true;
                   }
                break;
    
                case 'TXT':
                   if (stripos($line, "$host text") === 0)
                   {
                      return true;
                   }
                break;
    
                case 'CNAME':
                   if (stripos($line, "$host canonical name") === 0)
                   {
                      return true;
                   }
                break;
    
                default:
                case 'AAAA':
                   // AAAA records returned by nslookup on Windows XP/2003 have this format.
                   // Later Windows versions use the A record format below for AAAA records.
                   if (stripos($line, "$host AAAA IPv6 address") === 0)
                   {
                      return true;
                   }
                // No break
    
                case 'A':
                   if (!empty($host_matches))
                   {
                      // Second line
                      if (stripos($line, "Address: ") === 0)
                      {
                         return true;
                      }
                      else
                      {
                         $host_matches = false;
                      }
                   }
                   else if (stripos($line, "Name: $host") === 0)
                   {
                      // First line
                      $host_matches = true;
                   }
                break;
             }
          }
    
          return false;
       }
    
       return NULL;
    }
    
    // Handler, header and footer
    
    /**
    * Error and message handler, call with trigger_error if reqd
    */
    function msg_handler($errno, $msg_text, $errfile, $errline)
    {
       global $cache, $db, $auth, $template, $config, $user;
       global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text;
    
       // Do not display notices if we suppress them via @
       if (error_reporting() == 0 && $errno != E_USER_ERROR && $errno != E_USER_WARNING && $errno != E_USER_NOTICE)
       {
          return;
       }
    
       // Message handler is stripping text. In case we need it, we are possible to define long text...
       if (isset($msg_long_text) && $msg_long_text && !$msg_text)
       {
          $msg_text = $msg_long_text;
       }
    
       if (!defined('E_DEPRECATED'))
       {
          define('E_DEPRECATED', 8192);
       }
    
       switch ($errno)
       {
          case E_NOTICE:
          case E_WARNING:
    
             // Check the error reporting level and return if the error level does not match
             // If DEBUG is defined the default level is E_ALL
             if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
             {
                return;
             }
    
             if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
             {
                $errfile = phpbb_filter_root_path($errfile);
                $msg_text = phpbb_filter_root_path($msg_text);
                $error_name = ($errno === E_WARNING) ? 'PHP Warning' : 'PHP Notice';
                echo '<b>[phpBB Debug] ' . $error_name . '</b>: in file <b>' . $errfile . '</b> on line <b>' . $errline . '</b>: <b>' . $msg_text . '</b><br />' . "\n";
    
                // we are writing an image - the user won't see the debug, so let's place it in the log
                if (defined('IMAGE_OUTPUT') || defined('IN_CRON'))
                {
                   add_log('critical', 'LOG_IMAGE_GENERATION_ERROR', $errfile, $errline, $msg_text);
                }
                // echo '<br /><br />BACKTRACE<br />' . get_backtrace() . '<br />' . "\n";
             }
    
             return;
    
          break;
    
          case E_USER_ERROR:
    
             if (!empty($user) && !empty($user->lang))
             {
                $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
                $msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
    
                $l_return_index = sprintf($user->lang['RETURN_INDEX'], '<a href="' . $phpbb_root_path . '">', '</a>');
                $l_notify = '';
    
                if (!empty($config['board_contact']))
                {
                   $l_notify = '<p>' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '</p>';
                }
             }
             else
             {
                $msg_title = 'General Error';
                $l_return_index = '<a href="' . $phpbb_root_path . '">Return to index page</a>';
                $l_notify = '';
    
                if (!empty($config['board_contact']))
                {
                   $l_notify = '<p>Please notify the board administrator or webmaster: <a href="mailto:' . $config['board_contact'] . '">' . $config['board_contact'] . '</a></p>';
                }
             }
    
             $log_text = $msg_text;
             $backtrace = get_backtrace();
             if ($backtrace)
             {
                $log_text .= '<br /><br />BACKTRACE<br />' . $backtrace;
             }
    
             if (defined('IN_INSTALL') || defined('DEBUG_EXTRA') || isset($auth) && $auth->acl_get('a_'))
             {
                $msg_text = $log_text;
             }
    
             if ((defined('DEBUG') || defined('IN_CRON') || defined('IMAGE_OUTPUT')) && isset($db))
             {
                // let's avoid loops
                $db->sql_return_on_error(true);
                add_log('critical', 'LOG_GENERAL_ERROR', $msg_title, $log_text);
                $db->sql_return_on_error(false);
             }
    
             // Do not send 200 OK, but service unavailable on errors
             send_status_line(503, 'Service Unavailable');
    
             garbage_collection();
    
             // Try to not call the adm page data...
    
             echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">';
             echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
             echo '<head>';
             echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
             echo '<title>' . $msg_title . '</title>';
             echo '<style type="text/css">' . "\n" . '/* <![CDATA[ */' . "\n";
             echo '* { margin: 0; padding: 0; } html { font-size: 100%; height: 100%; margin-bottom: 1px; background-color: #E4EDF0; } body { font-family: "Lucida Grande", Verdana, Helvetica, Arial, sans-serif; color: #536482; background: #E4EDF0; font-size: 62.5%; margin: 0; } ';
             echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } ';
             echo '#wrap { padding: 0 20px 15px 20px; min-width: 615px; } #page-header { text-align: right; height: 40px; } #page-footer { clear: both; font-size: 1em; text-align: center; } ';
             echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px  #A9B8C2; } ';
             echo '#errorpage #page-header a { font-weight: bold; line-height: 6em; } #errorpage #content { padding: 10px; } #errorpage #content h1 { line-height: 1.2em; margin-bottom: 0; color: #DF075C; } ';
             echo '#errorpage #content div { margin-top: 20px; margin-bottom: 5px; border-bottom: 1px solid #CCCCCC; padding-bottom: 5px; color: #333333; font: bold 1.2em "Lucida Grande", Arial, Helvetica, sans-serif; text-decoration: none; line-height: 120%; text-align: left; } ';
             echo "\n" . '/* ]]> */' . "\n";
             echo '</style>';
             echo '</head>';
             echo '<body id="errorpage">';
             echo '<div id="wrap">';
             echo '   <div id="page-header">';
             echo '      ' . $l_return_index;
             echo '   </div>';
             echo '   <div id="acp">';
             echo '   <div class="panel">';
             echo '      <div id="content">';
             echo '         <h1>' . $msg_title . '</h1>';
    
             echo '         <div>' . $msg_text . '</div>';
    
             echo $l_notify;
    
             echo '      </div>';
             echo '   </div>';
             echo '   </div>';
             echo '   <div id="page-footer">';
             echo '      Powered by <a href="http://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Group';
             echo '   </div>';
             echo '</div>';
             echo '</body>';
             echo '</html>';
    
             exit_handler();
    
             // On a fatal error (and E_USER_ERROR *is* fatal) we never want other scripts to continue and force an exit here.
             exit;
          break;
    
          case E_USER_WARNING:
          case E_USER_NOTICE:
    
             define('IN_ERROR_HANDLER', true);
    
             if (empty($user->data))
             {
                $user->session_begin();
             }
    
             // We re-init the auth array to get correct results on login/logout
             $auth->acl($user->data);
    
             if (empty($user->lang))
             {
                $user->setup();
             }
    
             if ($msg_text == 'ERROR_NO_ATTACHMENT' || $msg_text == 'NO_FORUM' || $msg_text == 'NO_TOPIC' || $msg_text == 'NO_USER')
             {
                send_status_line(404, 'Not Found');
             }
    
             $msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
             $msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
    
             if (!defined('HEADER_INC'))
             {
                if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
                {
                   adm_page_header($msg_title);
                }
                else
                {
                   page_header($msg_title, false);
                }
             }
    
             $template->set_filenames(array(
                'body' => 'message_body.html')
             );
    
             $template->assign_vars(array(
                'MESSAGE_TITLE'      => $msg_title,
                'MESSAGE_TEXT'      => $msg_text,
                'S_USER_WARNING'   => ($errno == E_USER_WARNING) ? true : false,
                'S_USER_NOTICE'      => ($errno == E_USER_NOTICE) ? true : false)
             );
    
             // We do not want the cron script to be called on error messages
             define('IN_CRON', true);
    
             if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
             {
                adm_page_footer();
             }
             else
             {
                page_footer();
             }
    
             exit_handler();
          break;
    
          // PHP4 compatibility
          case E_DEPRECATED:
             return true;
          break;
       }
    
       // If we notice an error not handled here we pass this back to PHP by returning false
       // This may not work for all php versions
       return false;
    }
    
    /**
    * Removes absolute path to phpBB root directory from error messages
    * and converts backslashes to forward slashes.
    *
    * @param string $errfile   Absolute file path
    *                     (e.g. /var/www/phpbb3/phpBB/includes/functions.php)
    *                     Please note that if $errfile is outside of the phpBB root,
    *                     the root path will not be found and can not be filtered.
    * @return string         Relative file path
    *                     (e.g. /includes/functions.php)
    */
    function phpbb_filter_root_path($errfile)
    {
       static $root_path;
    
       if (empty($root_path))
       {
          $root_path = phpbb_realpath(dirname(__FILE__) . '/../');
       }
    
       return str_replace(array($root_path, '\\'), array('[ROOT]', '/'), $errfile);
    }
    
    /**
    * Queries the session table to get information about online guests
    * @param int $item_id Limits the search to the item with this id
    * @param string $item The name of the item which is stored in the session table as session_{$item}_id
    * @return int The number of active distinct guest sessions
    */
    function obtain_guest_count($item_id = 0, $item = 'forum')
    {
       global $db, $config;
    
       if ($item_id)
       {
          $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id;
       }
       else
       {
          $reading_sql = '';
       }
       $time = (time() - (intval($config['load_online_time']) * 60));
    
       // Get number of online guests
    
       if ($db->sql_layer === 'sqlite')
       {
          $sql = 'SELECT COUNT(session_ip) as num_guests
             FROM (
                SELECT DISTINCT s.session_ip
                FROM ' . SESSIONS_TABLE . ' s
                WHERE s.session_user_id = ' . ANONYMOUS . '
                   AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
                $reading_sql .
             ')';
       }
       else
       {
          $sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
             FROM ' . SESSIONS_TABLE . ' s
             WHERE s.session_user_id = ' . ANONYMOUS . '
                AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
             $reading_sql;
       }
       $result = $db->sql_query($sql);
       $guests_online = (int) $db->sql_fetchfield('num_guests');
       $db->sql_freeresult($result);
    
       return $guests_online;
    }
    
    /**
    * Queries the session table to get information about online users
    * @param int $item_id Limits the search to the item with this id
    * @param string $item The name of the item which is stored in the session table as session_{$item}_id
    * @return array An array containing the ids of online, hidden and visible users, as well as statistical info
    */
    function obtain_users_online($item_id = 0, $item = 'forum')
    {
       global $db, $config, $user;
    
       $reading_sql = '';
       if ($item_id !== 0)
       {
          $reading_sql = ' AND s.session_' . $item . '_id = ' . (int) $item_id;
       }
    
       $online_users = array(
          'online_users'         => array(),
          'hidden_users'         => array(),
          'total_online'         => 0,
          'visible_online'      => 0,
          'hidden_online'         => 0,
          'guests_online'         => 0,
       );
    
       if ($config['load_online_guests'])
       {
          $online_users['guests_online'] = obtain_guest_count($item_id, $item);
       }
    
       // a little discrete magic to cache this for 30 seconds
       $time = (time() - (intval($config['load_online_time']) * 60));
    
       $sql = 'SELECT s.session_user_id, s.session_ip, s.session_viewonline
          FROM ' . SESSIONS_TABLE . ' s
          WHERE s.session_time >= ' . ($time - ((int) ($time % 30))) .
             $reading_sql .
          ' AND s.session_user_id <> ' . ANONYMOUS;
       $result = $db->sql_query($sql);
    
       while ($row = $db->sql_fetchrow($result))
       {
          // Skip multiple sessions for one user
          if (!isset($online_users['online_users'][$row['session_user_id']]))
          {
             $online_users['online_users'][$row['session_user_id']] = (int) $row['session_user_id'];
             if ($row['session_viewonline'])
             {
                $online_users['visible_online']++;
             }
             else
             {
                $online_users['hidden_users'][$row['session_user_id']] = (int) $row['session_user_id'];
                $online_users['hidden_online']++;
             }
          }
       }
       $online_users['total_online'] = $online_users['guests_online'] + $online_users['visible_online'] + $online_users['hidden_online'];
       $db->sql_freeresult($result);
    
       return $online_users;
    }
    
    /**
    * Uses the result of obtain_users_online to generate a localized, readable representation.
    * @param mixed $online_users result of obtain_users_online - array with user_id lists for total, hidden and visible users, and statistics
    * @param int $item_id Indicate that the data is limited to one item and not global
    * @param string $item The name of the item which is stored in the session table as session_{$item}_id
    * @return array An array containing the string for output to the template
    */
    function obtain_users_online_string($online_users, $item_id = 0, $item = 'forum')
    {
       global $config, $db, $user, $auth;
    
       $user_online_link = $online_userlist = '';
       // Need caps version of $item for language-strings
       $item_caps = strtoupper($item);
    
       if (sizeof($online_users['online_users']))
       {
          $sql = 'SELECT username, username_clean, user_id, user_type, user_allow_viewonline, user_colour
                FROM ' . USERS_TABLE . '
                WHERE ' . $db->sql_in_set('user_id', $online_users['online_users']) . '
                ORDER BY username_clean ASC';
          $result = $db->sql_query($sql);
    
          while ($row = $db->sql_fetchrow($result))
          {
             // User is logged in and therefore not a guest
             if ($row['user_id'] != ANONYMOUS)
             {
                if (isset($online_users['hidden_users'][$row['user_id']]))
                {
                   $row['username'] = '<em>' . $row['username'] . '</em>';
                }
    
                if (!isset($online_users['hidden_users'][$row['user_id']]) || $auth->acl_get('u_viewonline'))
                {
                   $user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']);
                   $online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
                }
             }
          }
          $db->sql_freeresult($result);
       }
    
       if (!$online_userlist)
       {
          $online_userlist = $user->lang['NO_ONLINE_USERS'];
       }
    
       if ($item_id === 0)
       {
          $online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
       }
       else if ($config['load_online_guests'])
       {
          $l_online = ($online_users['guests_online'] === 1) ? $user->lang['BROWSING_' . $item_caps . '_GUEST'] : $user->lang['BROWSING_' . $item_caps . '_GUESTS'];
          $online_userlist = sprintf($l_online, $online_userlist, $online_users['guests_online']);
       }
       else
       {
          $online_userlist = sprintf($user->lang['BROWSING_' . $item_caps], $online_userlist);
       }
       // Build online listing
       $vars_online = array(
          'ONLINE'   => array('total_online', 'l_t_user_s', 0),
          'REG'      => array('visible_online', 'l_r_user_s', !$config['load_online_guests']),
          'HIDDEN'   => array('hidden_online', 'l_h_user_s', $config['load_online_guests']),
          'GUEST'      => array('guests_online', 'l_g_user_s', 0)
       );
    
       foreach ($vars_online as $l_prefix => $var_ary)
       {
          if ($var_ary[2])
          {
             $l_suffix = '_AND';
          }
          else
          {
             $l_suffix = '';
          }
          switch ($online_users[$var_ary[0]])
          {
             case 0:
                ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL' . $l_suffix];
             break;
    
             case 1:
                ${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL' . $l_suffix];
             break;
    
             default:
                ${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL' . $l_suffix];
             break;
          }
       }
       unset($vars_online);
    
       $l_online_users = sprintf($l_t_user_s, $online_users['total_online']);
       $l_online_users .= sprintf($l_r_user_s, $online_users['visible_online']);
       $l_online_users .= sprintf($l_h_user_s, $online_users['hidden_online']);
    
       if ($config['load_online_guests'])
       {
          $l_online_users .= sprintf($l_g_user_s, $online_users['guests_online']);
       }
    
    
    
       return array(
          'online_userlist'   => $online_userlist,
          'l_online_users'   => $l_online_users,
       );
    }
    
    /**
    * Get option bitfield from custom data
    *
    * @param int   $bit      The bit/value to get
    * @param int   $data      Current bitfield to check
    * @return bool   Returns true if value of constant is set in bitfield, else false
    */
    function phpbb_optionget($bit, $data)
    {
       return ($data & 1 << (int) $bit) ? true : false;
    }
    
    /**
    * Set option bitfield
    *
    * @param int   $bit      The bit/value to set/unset
    * @param bool   $set      True if option should be set, false if option should be unset.
    * @param int   $data      Current bitfield to change
    *
    * @return int   The new bitfield
    */
    function phpbb_optionset($bit, $set, $data)
    {
       if ($set && !($data & 1 << $bit))
       {
          $data += 1 << $bit;
       }
       else if (!$set && ($data & 1 << $bit))
       {
          $data -= 1 << $bit;
       }
    
       return $data;
    }
    
    /**
    * Login using http authenticate.
    *
    * @param array   $param      Parameter array, see $param_defaults array.
    *
    * @return void
    */
    function phpbb_http_login($param)
    {
       global $auth, $user;
       global $config;
    
       $param_defaults = array(
          'auth_message'   => '',
    
          'autologin'      => false,
          'viewonline'   => true,
          'admin'         => false,
       );
    
       // Overwrite default values with passed values
       $param = array_merge($param_defaults, $param);
    
       // User is already logged in
       // We will not overwrite his session
       if (!empty($user->data['is_registered']))
       {
          return;
       }
    
       // $_SERVER keys to check
       $username_keys = array(
          'PHP_AUTH_USER',
          'Authorization',
          'REMOTE_USER', 'REDIRECT_REMOTE_USER',
          'HTTP_AUTHORIZATION', 'REDIRECT_HTTP_AUTHORIZATION',
          'REMOTE_AUTHORIZATION', 'REDIRECT_REMOTE_AUTHORIZATION',
          'AUTH_USER',
       );
    
       $password_keys = array(
          'PHP_AUTH_PW',
          'REMOTE_PASSWORD',
          'AUTH_PASSWORD',
       );
    
       $username = null;
       foreach ($username_keys as $k)
       {
          if (isset($_SERVER[$k]))
          {
             $username = $_SERVER[$k];
             break;
          }
       }
    
       $password = null;
       foreach ($password_keys as $k)
       {
          if (isset($_SERVER[$k]))
          {
             $password = $_SERVER[$k];
             break;
          }
       }
    
       // Decode encoded information (IIS, CGI, FastCGI etc.)
       if (!is_null($username) && is_null($password) && strpos($username, 'Basic ') === 0)
       {
          list($username, $password) = explode(':', base64_decode(substr($username, 6)), 2);
       }
    
       if (!is_null($username) && !is_null($password))
       {
          set_var($username, $username, 'string', true);
          set_var($password, $password, 'string', true);
    
          $auth_result = $auth->login($username, $password, $param['autologin'], $param['viewonline'], $param['admin']);
    
          if ($auth_result['status'] == LOGIN_SUCCESS)
          {
             return;
          }
          else if ($auth_result['status'] == LOGIN_ERROR_ATTEMPTS)
          {
             send_status_line(401, 'Unauthorized');
    
             trigger_error('NOT_AUTHORISED');
          }
       }
    
       // Prepend sitename to auth_message
       $param['auth_message'] = ($param['auth_message'] === '') ? $config['sitename'] : $config['sitename'] . ' - ' . $param['auth_message'];
    
       // We should probably filter out non-ASCII characters - RFC2616
       $param['auth_message'] = preg_replace('/[\x80-\xFF]/', '?', $param['auth_message']);
    
       header('WWW-Authenticate: Basic realm="' . $param['auth_message'] . '"');
       send_status_line(401, 'Unauthorized');
    
       trigger_error('NOT_AUTHORISED');
    }
    
    /**
    * Generate page header
    */
    function page_header($page_title = '', $display_online_list = true, $item_id = 0, $item = 'forum')
    {
       global $db, $config, $template, $SID, $_SID, $_EXTRA_URL, $user, $auth, $phpEx, $phpbb_root_path;
    
       if (defined('HEADER_INC'))
       {
          return;
       }
    
       define('HEADER_INC', true);
    
       // gzip_compression
       if ($config['gzip_compress'])
       {
          // to avoid partially compressed output resulting in blank pages in
          // the browser or error messages, compression is disabled in a few cases:
          //
          // 1) if headers have already been sent, this indicates plaintext output
          //    has been started so further content must not be compressed
          // 2) the length of the current output buffer is non-zero. This means
          //    there is already some uncompressed content in this output buffer
          //    so further output must not be compressed
          // 3) if more than one level of output buffering is used because we
          //    cannot test all output buffer level content lengths. One level
          //    could be caused by php.ini output_buffering. Anything
          //    beyond that is manual, so the code wrapping phpBB in output buffering
          //    can easily compress the output itself.
          //
          if (@extension_loaded('zlib') && !headers_sent() && ob_get_level() <= 1 && ob_get_length() == 0)
          {
             ob_start('ob_gzhandler');
          }
       }
    
       // Generate logged in/logged out status
       if ($user->data['user_id'] != ANONYMOUS)
       {
          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id);
          $l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
       }
       else
       {
          $u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
          $l_login_logout = $user->lang['LOGIN'];
       }
    
       // Last visit date/time
       $s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
    
       // Get users online list ... if required
       $l_online_users = $online_userlist = $l_online_record = $l_online_time = '';
    
       if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
       {
          /**
          * Load online data:
          * For obtaining another session column use $item and $item_id in the function-parameter, whereby the column is session_{$item}_id.
          */
          $item_id = max($item_id, 0);
    
          $online_users = obtain_users_online($item_id, $item);
          $user_online_strings = obtain_users_online_string($online_users, $item_id, $item);
    
          $l_online_users = $user_online_strings['l_online_users'];
          $online_userlist = $user_online_strings['online_userlist'];
          $total_online_users = $online_users['total_online'];
    
          if ($total_online_users > $config['record_online_users'])
          {
             set_config('record_online_users', $total_online_users, true);
             set_config('record_online_date', time(), true);
          }
    
          $l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date'], false, true));
    
          $l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
          $l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
       }
    
       $l_privmsgs_text = $l_privmsgs_text_unread = '';
       $s_privmsg_new = false;
    
       // Obtain number of new private messages if user is logged in
       if (!empty($user->data['is_registered']))
       {
          if ($user->data['user_new_privmsg'])
          {
             $l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
             $l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
    
             if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
             {
                $sql = 'UPDATE ' . USERS_TABLE . '
                   SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
                   WHERE user_id = ' . $user->data['user_id'];
                $db->sql_query($sql);
    
                $s_privmsg_new = true;
             }
             else
             {
                $s_privmsg_new = false;
             }
          }
          else
          {
             $l_privmsgs_text = $user->lang['NO_NEW_PM'];
             $s_privmsg_new = false;
          }
    
          $l_privmsgs_text_unread = '';
    
          if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
          {
             $l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
             $l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
          }
       }
    
       $forum_id = request_var('f', 0);
       $topic_id = request_var('t', 0);
    
       $s_feed_news = false;
    
       // Get option for news
       if ($config['feed_enable'])
       {
          $sql = 'SELECT forum_id
             FROM ' . FORUMS_TABLE . '
             WHERE ' . $db->sql_bit_and('forum_options', FORUM_OPTION_FEED_NEWS, '<> 0');
          $result = $db->sql_query_limit($sql, 1, 0, 600);
          $s_feed_news = (int) $db->sql_fetchfield('forum_id');
          $db->sql_freeresult($result);
       }
    
       // Determine board url - we may need it later
       $board_url = generate_board_url() . '/';
       $web_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? $board_url : $phpbb_root_path;
    
       // Which timezone?
       $tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
    
       // Send a proper content-language to the output
       $user_lang = $user->lang['USER_LANG'];
       if (strpos($user_lang, '-x-') !== false)
       {
          $user_lang = substr($user_lang, 0, strpos($user_lang, '-x-'));
       }
    
       $s_search_hidden_fields = array();
       if ($_SID)
       {
          $s_search_hidden_fields['sid'] = $_SID;
       }
    
       if (!empty($_EXTRA_URL))
       {
          foreach ($_EXTRA_URL as $url_param)
          {
             $url_param = explode('=', $url_param, 2);
             $s_search_hidden_fields[$url_param[0]] = $url_param[1];
          }
       }
    
       // The following assigns all _common_ variables that may be used at any point in a template.
       $template->assign_vars(array(
          'SITENAME'                  => $config['sitename'],
          'SITE_DESCRIPTION'            => $config['site_desc'],
          'PAGE_TITLE'               => $page_title,
          'SCRIPT_NAME'               => str_replace('.' . $phpEx, '', $user->page['page_name']),
          'LAST_VISIT_DATE'            => sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
          'LAST_VISIT_YOU'            => $s_last_visit,
          'CURRENT_TIME'               => sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
          'TOTAL_USERS_ONLINE'         => $l_online_users,
          'LOGGED_IN_USER_LIST'         => $online_userlist,
          'RECORD_USERS'               => $l_online_record,
          'PRIVATE_MESSAGE_INFO'         => $l_privmsgs_text,
          'PRIVATE_MESSAGE_INFO_UNREAD'   => $l_privmsgs_text_unread,
    
          'S_USER_NEW_PRIVMSG'         => $user->data['user_new_privmsg'],
          'S_USER_UNREAD_PRIVMSG'         => $user->data['user_unread_privmsg'],
          'S_USER_NEW'               => $user->data['user_new'],
    
          'SID'            => $SID,
          '_SID'            => $_SID,
          'SESSION_ID'      => $user->session_id,
          'ROOT_PATH'         => $phpbb_root_path,
          'BOARD_URL'         => $board_url,
    
          'L_LOGIN_LOGOUT'   => $l_login_logout,
          'L_INDEX'         => $user->lang['FORUM_INDEX'],
          'L_ONLINE_EXPLAIN'   => $l_online_time,
    
          'U_PRIVATEMSGS'         => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
          'U_RETURN_INBOX'      => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
          'U_POPUP_PM'         => append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup'),
          'UA_POPUP_PM'         => addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup')),
          'U_MEMBERLIST'         => append_sid("{$phpbb_root_path}memberlist.$phpEx"),
          'U_VIEWONLINE'         => ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '',
          'U_LOGIN_LOGOUT'      => $u_login_logout,
          'U_INDEX'            => append_sid("{$phpbb_root_path}index.$phpEx"),
          'U_SEARCH'            => append_sid("{$phpbb_root_path}search.$phpEx"),
          'U_REGISTER'         => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
          'U_PROFILE'            => append_sid("{$phpbb_root_path}ucp.$phpEx"),
          'U_MODCP'            => append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
          'U_FAQ'               => append_sid("{$phpbb_root_path}faq.$phpEx"),
          'U_SEARCH_SELF'         => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
          'U_SEARCH_NEW'         => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
          'U_SEARCH_UNANSWERED'   => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
          'U_SEARCH_UNREAD'      => append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unreadposts'),
          'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
          'U_DELETE_COOKIES'      => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
          'U_TEAM'            => ($user->data['user_id'] != ANONYMOUS && !$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
          'U_TERMS_USE'         => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
          'U_PRIVACY'            => append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
          'U_RESTORE_PERMISSIONS'   => ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
          'U_FEED'            => generate_board_url() . "/feed.$phpEx",
    
          'S_USER_LOGGED_IN'      => ($user->data['user_id'] != ANONYMOUS) ? true : false,
          'S_AUTOLOGIN_ENABLED'   => ($config['allow_autologin']) ? true : false,
          'S_BOARD_DISABLED'      => ($config['board_disable']) ? true : false,
          'S_REGISTERED_USER'      => (!empty($user->data['is_registered'])) ? true : false,
          'S_IS_BOT'            => (!empty($user->data['is_bot'])) ? true : false,
          'S_USER_PM_POPUP'      => $user->optionget('popuppm'),
          'S_USER_LANG'         => $user_lang,
          'S_USER_BROWSER'      => (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
          'S_USERNAME'         => $user->data['username'],
          'S_CONTENT_DIRECTION'   => $user->lang['DIRECTION'],
          'S_CONTENT_FLOW_BEGIN'   => ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right',
          'S_CONTENT_FLOW_END'   => ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left',
          'S_CONTENT_ENCODING'   => 'UTF-8',
          '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], ''),
          'S_DISPLAY_ONLINE_LIST'   => ($l_online_time) ? 1 : 0,
          'S_DISPLAY_SEARCH'      => (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
          'S_DISPLAY_PM'         => ($config['allow_privmsg'] && !empty($user->data['is_registered']) && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false,
          'S_DISPLAY_MEMBERLIST'   => (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
          'S_NEW_PM'            => ($s_privmsg_new) ? 1 : 0,
          'S_REGISTER_ENABLED'   => ($config['require_activation'] != USER_ACTIVATION_DISABLE) ? true : false,
          'S_FORUM_ID'         => $forum_id,
          'S_TOPIC_ID'         => $topic_id,
    
          'S_LOGIN_ACTION'      => ((!defined('ADMIN_START')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id)),
          'S_LOGIN_REDIRECT'      => build_hidden_fields(array('redirect' => build_url())),
    
          'S_ENABLE_FEEDS'         => ($config['feed_enable']) ? true : false,
          'S_ENABLE_FEEDS_OVERALL'   => ($config['feed_overall']) ? true : false,
          'S_ENABLE_FEEDS_FORUMS'      => ($config['feed_overall_forums']) ? true : false,
          'S_ENABLE_FEEDS_TOPICS'      => ($config['feed_topics_new']) ? true : false,
          'S_ENABLE_FEEDS_TOPICS_ACTIVE'   => ($config['feed_topics_active']) ? true : false,
          'S_ENABLE_FEEDS_NEWS'      => ($s_feed_news) ? true : false,
    
          'S_LOAD_UNREADS'         => ($config['load_unreads_search'] && ($config['load_anon_lastread'] || $user->data['is_registered'])) ? true : false,
    
          'S_SEARCH_HIDDEN_FIELDS'   => build_hidden_fields($s_search_hidden_fields),
    
          'T_THEME_PATH'         => "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme',
          'T_TEMPLATE_PATH'      => "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template',
          'T_SUPER_TEMPLATE_PATH'   => (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? "{$web_path}styles/" . rawurlencode($user->theme['template_inherit_path']) . '/template' : "{$web_path}styles/" . rawurlencode($user->theme['template_path']) . '/template',
          'T_IMAGESET_PATH'      => "{$web_path}styles/" . rawurlencode($user->theme['imageset_path']) . '/imageset',
          'T_IMAGESET_LANG_PATH'   => "{$web_path}styles/" . rawurlencode($user->theme['imageset_path']) . '/imageset/' . $user->lang_name,
          'T_IMAGES_PATH'         => "{$web_path}images/",
          'T_SMILIES_PATH'      => "{$web_path}{$config['smilies_path']}/",
          'T_AVATAR_PATH'         => "{$web_path}{$config['avatar_path']}/",
          'T_AVATAR_GALLERY_PATH'   => "{$web_path}{$config['avatar_gallery_path']}/",
          'T_ICONS_PATH'         => "{$web_path}{$config['icons_path']}/",
          'T_RANKS_PATH'         => "{$web_path}{$config['ranks_path']}/",
          'T_UPLOAD_PATH'         => "{$web_path}{$config['upload_path']}/",
          'T_STYLESHEET_LINK'      => (!$user->theme['theme_storedb']) ? "{$web_path}styles/" . rawurlencode($user->theme['theme_path']) . '/theme/stylesheet.css' : append_sid("{$phpbb_root_path}style.$phpEx", 'id=' . $user->theme['style_id'] . '&lang=' . $user->lang_name),
          'T_STYLESHEET_NAME'      => $user->theme['theme_name'],
    
          'T_THEME_NAME'         => rawurlencode($user->theme['theme_path']),
          'T_TEMPLATE_NAME'      => rawurlencode($user->theme['template_path']),
          'T_SUPER_TEMPLATE_NAME'   => rawurlencode((isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? $user->theme['template_inherit_path'] : $user->theme['template_path']),
          'T_IMAGESET_NAME'      => rawurlencode($user->theme['imageset_path']),
          'T_IMAGESET_LANG_NAME'   => $user->data['user_lang'],
          'T_IMAGES'            => 'images',
          'T_SMILIES'            => $config['smilies_path'],
          'T_AVATAR'            => $config['avatar_path'],
          'T_AVATAR_GALLERY'      => $config['avatar_gallery_path'],
          'T_ICONS'            => $config['icons_path'],
          'T_RANKS'            => $config['ranks_path'],
          'T_UPLOAD'            => $config['upload_path'],
    
          'SITE_LOGO_IMG'         => $user->img('site_logo'),
    
          'A_COOKIE_SETTINGS'      => addslashes('; path=' . $config['cookie_path'] . ((!$config['cookie_domain'] || $config['cookie_domain'] == 'localhost' || $config['cookie_domain'] == '127.0.0.1') ? '' : '; domain=' . $config['cookie_domain']) . ((!$config['cookie_secure']) ? '' : '; secure')),
       ));
    
       // application/xhtml+xml not used because of IE
       header('Content-type: text/html; charset=UTF-8');
    
       header('Cache-Control: private, no-cache="set-cookie"');
       header('Expires: 0');
       header('Pragma: no-cache');
    
       if (!empty($user->data['is_bot']))
       {
          // Let reverse proxies know we detected a bot.
          header('X-PHPBB-IS-BOT: yes');
       }
    
       return;
    }
    
    /**
    * Generate page footer
    */
    function page_footer($run_cron = true)
    {
       global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
    
       // Output page creation time
       if (defined('DEBUG'))
       {
          $mtime = explode(' ', microtime());
          $totaltime = $mtime[0] + $mtime[1] - $starttime;
    
          if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
          {
             $db->sql_report('display');
          }
    
          $debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress'] && @extension_loaded('zlib')) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
    
          if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
          {
             if (function_exists('memory_get_usage'))
             {
                if ($memory_usage = memory_get_usage())
                {
                   global $base_memory_usage;
                   $memory_usage -= $base_memory_usage;
                   $memory_usage = get_formatted_filesize($memory_usage);
    
                   $debug_output .= ' | Memory Usage: ' . $memory_usage;
                }
             }
    
             $debug_output .= ' | <a href="' . build_url() . '&explain=1">Explain</a>';
          }
       }
    
       $template->assign_vars(array(
          'DEBUG_OUTPUT'         => (defined('DEBUG')) ? $debug_output : '',
          'TRANSLATION_INFO'      => (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
          'CREDIT_LINE'         => $user->lang('POWERED_BY', '<a href="http://www.phpbb.com/">phpBB</a>&reg; Forum Software &copy; phpBB Group'),
    
          'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
       );
    
       // Call cron-type script
       $call_cron = false;
       if (!defined('IN_CRON') && $run_cron && !$config['board_disable'] && !$user->data['is_bot'])
       {
          $call_cron = true;
          $time_now = (!empty($user->time_now) && is_int($user->time_now)) ? $user->time_now : time();
    
          // Any old lock present?
          if (!empty($config['cron_lock']))
          {
             $cron_time = explode(' ', $config['cron_lock']);
    
             // If 1 hour lock is present we do not call cron.php
             if ($cron_time[0] + 3600 >= $time_now)
             {
                $call_cron = false;
             }
          }
       }
    
       // Call cron job?
       if ($call_cron)
       {
          $cron_type = '';
    
          if ($time_now - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
          {
             // Process email queue
             $cron_type = 'queue';
          }
          else if (method_exists($cache, 'tidy') && $time_now - $config['cache_gc'] > $config['cache_last_gc'])
          {
             // Tidy the cache
             $cron_type = 'tidy_cache';
          }
          else if ($config['warnings_expire_days'] && ($time_now - $config['warnings_gc'] > $config['warnings_last_gc']))
          {
             $cron_type = 'tidy_warnings';
          }
          else if ($time_now - $config['database_gc'] > $config['database_last_gc'])
          {
             // Tidy the database
             $cron_type = 'tidy_database';
          }
          else if ($time_now - $config['search_gc'] > $config['search_last_gc'])
          {
             // Tidy the search
             $cron_type = 'tidy_search';
          }
          else if ($time_now - $config['session_gc'] > $config['session_last_gc'])
          {
             $cron_type = 'tidy_sessions';
          }
    
          if ($cron_type)
          {
             $template->assign_var('RUN_CRON_TASK', '<img src="' . append_sid($phpbb_root_path . 'cron.' . $phpEx, 'cron_type=' . $cron_type) . '" width="1" height="1" alt="cron" />');
          }
       }
    
       $template->display('body');
    
       garbage_collection();
       exit_handler();
    }
    
    /**
    * Closing the cache object and the database
    * Cool function name, eh? We might want to add operations to it later
    */
    function garbage_collection()
    {
       global $cache, $db;
    
       // Unload cache, must be done before the DB connection if closed
       if (!empty($cache))
       {
          $cache->unload();
       }
    
       // Close our DB connection.
       if (!empty($db))
       {
          $db->sql_close();
       }
    }
    
    /**
    * Handler for exit calls in phpBB.
    * This function supports hooks.
    *
    * Note: This function is called after the template has been outputted.
    */
    function exit_handler()
    {
       global $phpbb_hook, $config;
    
       if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
       {
          if ($phpbb_hook->hook_return(__FUNCTION__))
          {
             return $phpbb_hook->hook_return_result(__FUNCTION__);
          }
       }
    
       // As a pre-caution... some setups display a blank page if the flush() is not there.
       (ob_get_level() > 0) ? @ob_flush() : @flush();
    
       exit;
    }
    
    /**
    * Handler for init calls in phpBB. This function is called in user::setup();
    * This function supports hooks.
    */
    function phpbb_user_session_handler()
    {
       global $phpbb_hook;
    
       if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
       {
          if ($phpbb_hook->hook_return(__FUNCTION__))
          {
             return $phpbb_hook->hook_return_result(__FUNCTION__);
          }
       }
    
       return;
    }
    
    ?>
    Function_content.php

    Código: Selecionar todos

    <?php
    /**
    *
    * @package phpBB3
    * @version $Id$
    * @copyright (c) 2005 phpBB Group
    * @license http://opensource.org/licenses/gpl-license.php GNU Public License
    *
    */
    
    /**
    * @ignore
    */
    if (!defined('IN_PHPBB'))
    {
        exit;
    }
    
    /**
    * gen_sort_selects()
    * make_jumpbox()
    * bump_topic_allowed()
    * get_context()
    * decode_message()
    * strip_bbcode()
    * generate_text_for_display()
    * generate_text_for_storage()
    * generate_text_for_edit()
    * make_clickable_callback()
    * make_clickable()
    * censor_text()
    * bbcode_nl2br()
    * smiley_text()
    * parse_attachments()
    * extension_allowed()
    * truncate_string()
    * get_username_string()
    * class bitfield
    */
    
    /**
    * Generate sort selection fields
    */
    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, $def_st = false, $def_sk = false, $def_sd = false)
    {
        global $user;
    
        $sort_dir_text = array('a' => $user->lang['ASCENDING'], 'd' => $user->lang['DESCENDING']);
    
        $sorts = array(
            'st'    => array(
                'key'        => 'sort_days',
                'default'    => $def_st,
                'options'    => $limit_days,
                'output'    => &$s_limit_days,
            ),
    
            'sk'    => array(
                'key'        => 'sort_key',
                'default'    => $def_sk,
                'options'    => $sort_by_text,
                'output'    => &$s_sort_key,
            ),
    
            'sd'    => array(
                'key'        => 'sort_dir',
                'default'    => $def_sd,
                'options'    => $sort_dir_text,
                'output'    => &$s_sort_dir,
            ),
        );
        $u_sort_param  = '';
    
        foreach ($sorts as $name => $sort_ary)
        {
            $key = $sort_ary['key'];
            $selected = $$sort_ary['key'];
    
            // Check if the key is selectable. If not, we reset to the default or first key found.
            // This ensures the values are always valid. We also set $sort_dir/sort_key/etc. to the
            // correct value, else the protection is void. ;)
            if (!isset($sort_ary['options'][$selected]))
            {
                if ($sort_ary['default'] !== false)
                {
                    $selected = $$key = $sort_ary['default'];
                }
                else
                {
                    @reset($sort_ary['options']);
                    $selected = $$key = key($sort_ary['options']);
                }
            }
    
            $sort_ary['output'] = '<select name="' . $name . '" id="' . $name . '">';
            foreach ($sort_ary['options'] as $option => $text)
            {
                $sort_ary['output'] .= '<option value="' . $option . '"' . (($selected == $option) ? ' selected="selected"' : '') . '>' . $text . '</option>';
            }
            $sort_ary['output'] .= '</select>';
    
            $u_sort_param .= ($selected !== $sort_ary['default']) ? ((strlen($u_sort_param)) ? '&' : '') . "{$name}={$selected}" : '';
        }
    
        return;
    }
    
    /**
    * Generate Jumpbox
    */
    function make_jumpbox($action, $forum_id = false, $select_all = false, $acl_list = false, $force_display = false)
    {
        global $config, $auth, $template, $user, $db;
    
        // We only return if the jumpbox is not forced to be displayed (in case it is needed for functionality)
        if (!$config['load_jumpbox'] && $force_display === false)
        {
            return;
        }
    
        $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
            FROM ' . FORUMS_TABLE . '
            ORDER BY left_id ASC';
        $result = $db->sql_query($sql, 600);
    
        $right = $padding = 0;
        $padding_store = array('0' => 0);
        $display_jumpbox = false;
        $iteration = 0;
    
        // Sometimes it could happen that forums will be displayed here not be displayed within the index page
        // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
        // If this happens, the padding could be "broken"
    
        while ($row = $db->sql_fetchrow($result))
        {
            if ($row['left_id'] < $right)
            {
                $padding++;
                $padding_store[$row['parent_id']] = $padding;
            }
            else if ($row['left_id'] > $right + 1)
            {
                // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
                // @todo digging deep to find out "how" this can happen.
                $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
            }
    
            $right = $row['right_id'];
    
            if ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']))
            {
                // Non-postable forum with no subforums, don't display
                continue;
            }
    
            if (!$auth->acl_get('f_list', $row['forum_id']))
            {
                // if the user does not have permissions to list this forum skip
                continue;
            }
    
            if ($acl_list && !$auth->acl_gets($acl_list, $row['forum_id']))
            {
                continue;
            }
    
            if (!$display_jumpbox)
            {
                $template->assign_block_vars('jumpbox_forums', array(
                    'FORUM_ID'        => ($select_all) ? 0 : -1,
                    'FORUM_NAME'    => ($select_all) ? $user->lang['ALL_FORUMS'] : $user->lang['SELECT_FORUM'],
                    'S_FORUM_COUNT'    => $iteration)
                );
    
                $iteration++;
                $display_jumpbox = true;
            }
    
            $template->assign_block_vars('jumpbox_forums', array(
                'FORUM_ID'        => $row['forum_id'],
                'FORUM_NAME'    => $row['forum_name'],
                'SELECTED'        => ($row['forum_id'] == $forum_id) ? ' selected="selected"' : '',
                'S_FORUM_COUNT'    => $iteration,
                'S_IS_CAT'        => ($row['forum_type'] == FORUM_CAT) ? true : false,
                'S_IS_LINK'        => ($row['forum_type'] == FORUM_LINK) ? true : false,
                'S_IS_POST'        => ($row['forum_type'] == FORUM_POST) ? true : false)
            );
    
            for ($i = 0; $i < $padding; $i++)
            {
                $template->assign_block_vars('jumpbox_forums.level', array());
            }
            $iteration++;
        }
        $db->sql_freeresult($result);
        unset($padding_store);
    
        $template->assign_vars(array(
            'S_DISPLAY_JUMPBOX'    => $display_jumpbox,
            'S_JUMPBOX_ACTION'    => $action)
        );
    
        return;
    }
    
    /**
    * Bump Topic Check - used by posting and viewtopic
    */
    function bump_topic_allowed($forum_id, $topic_bumped, $last_post_time, $topic_poster, $last_topic_poster)
    {
        global $config, $auth, $user;
    
        // Check permission and make sure the last post was not already bumped
        if (!$auth->acl_get('f_bump', $forum_id) || $topic_bumped)
        {
            return false;
        }
    
        // Check bump time range, is the user really allowed to bump the topic at this time?
        $bump_time = ($config['bump_type'] == 'm') ? $config['bump_interval'] * 60 : (($config['bump_type'] == 'h') ? $config['bump_interval'] * 3600 : $config['bump_interval'] * 86400);
    
        // Check bump time
        if ($last_post_time + $bump_time > time())
        {
            return false;
        }
    
        // Check bumper, only topic poster and last poster are allowed to bump
        if ($topic_poster != $user->data['user_id'] && $last_topic_poster != $user->data['user_id'])
        {
            return false;
        }
    
        // A bump time of 0 will completely disable the bump feature... not intended but might be useful.
        return $bump_time;
    }
    
    /**
    * Generates a text with approx. the specified length which contains the specified words and their context
    *
    * @param    string    $text    The full text from which context shall be extracted
    * @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!)
    * @param    int        $length    The desired length of the resulting text, however the result might be shorter or longer than this value
    *
    * @return    string            Context of the specified words separated by "..."
    */
    function get_context($text, $words, $length = 400)
    {
        // first replace all whitespaces with single spaces
        $text = preg_replace('/ +/', ' ', strtr($text, "\t\n\r\x0C ", '     '));
    
        // we need to turn the entities back into their original form, to not cut the message in between them
        $entities = array('<', '>', '&#91;', '&#93;', '&#46;', '&#58;', '&#058;');
        $characters = array('<', '>', '[', ']', '.', ':', ':');
        $text = str_replace($entities, $characters, $text);
    
        $word_indizes = array();
        if (sizeof($words))
        {
            $match = '';
            // find the starting indizes of all words
            foreach ($words as $word)
            {
                if ($word)
                {
                    if (preg_match('#(?:[^\w]|^)(' . $word . ')(?:[^\w]|$)#i', $text, $match))
                    {
                        if (empty($match[1]))
                        {
                            continue;
                        }
    
                        $pos = utf8_strpos($text, $match[1]);
                        if ($pos !== false)
                        {
                            $word_indizes[] = $pos;
                        }
                    }
                }
            }
            unset($match);
    
            if (sizeof($word_indizes))
            {
                $word_indizes = array_unique($word_indizes);
                sort($word_indizes);
    
                $wordnum = sizeof($word_indizes);
                // number of characters on the right and left side of each word
                $sequence_length = (int) ($length / (2 * $wordnum)) - 2;
                $final_text = '';
                $word = $j = 0;
                $final_text_index = -1;
    
                // cycle through every character in the original text
                for ($i = $word_indizes[$word], $n = utf8_strlen($text); $i < $n; $i++)
                {
                    // if the current position is the start of one of the words then append $sequence_length characters to the final text
                    if (isset($word_indizes[$word]) && ($i == $word_indizes[$word]))
                    {
                        if ($final_text_index < $i - $sequence_length - 1)
                        {
                            $final_text .= '... ' . preg_replace('#^([^ ]*)#', '', utf8_substr($text, $i - $sequence_length, $sequence_length));
                        }
                        else
                        {
                            // if the final text is already nearer to the current word than $sequence_length we only append the text
                            // from its current index on and distribute the unused length to all other sequenes
                            $sequence_length += (int) (($final_text_index - $i + $sequence_length + 1) / (2 * $wordnum));
                            $final_text .= utf8_substr($text, $final_text_index + 1, $i - $final_text_index - 1);
                        }
                        $final_text_index = $i - 1;
    
                        // add the following characters to the final text (see below)
                        $word++;
                        $j = 1;
                    }
    
                    if ($j > 0)
                    {
                        // add the character to the final text and increment the sequence counter
                        $final_text .= utf8_substr($text, $i, 1);
                        $final_text_index++;
                        $j++;
    
                        // if this is a whitespace then check whether we are done with this sequence
                        if (utf8_substr($text, $i, 1) == ' ')
                        {
                            // only check whether we have to exit the context generation completely if we haven't already reached the end anyway
                            if ($i + 4 < $n)
                            {
                                if (($j > $sequence_length && $word >= $wordnum) || utf8_strlen($final_text) > $length)
                                {
                                    $final_text .= ' ...';
                                    break;
                                }
                            }
                            else
                            {
                                // make sure the text really reaches the end
                                $j -= 4;
                            }
    
                            // stop context generation and wait for the next word
                            if ($j > $sequence_length)
                            {
                                $j = 0;
                            }
                        }
                    }
                }
                return str_replace($characters, $entities, $final_text);
            }
        }
    
        if (!sizeof($words) || !sizeof($word_indizes))
        {
            return str_replace($characters, $entities, ((utf8_strlen($text) >= $length + 3) ? utf8_substr($text, 0, $length) . '...' : $text));
        }
    }
    
    /**
    * Decode text whereby text is coming from the db and expected to be pre-parsed content
    * We are placing this outside of the message parser because we are often in need of it...
    */
    function decode_message(&$message, $bbcode_uid = '')
    {
        global $config;
    
        if ($bbcode_uid)
        {
            $match = array('<br />', "[/*:m:$bbcode_uid]", ":u:$bbcode_uid", ":o:$bbcode_uid", ":$bbcode_uid");
            $replace = array("\n", '', '', '', '');
        }
        else
        {
            $match = array('<br />');
            $replace = array("\n");
        }
    
        $message = str_replace($match, $replace, $message);
    
        $match = get_preg_expression('bbcode_htm');
        $replace = array('\1', '\1', '\2', '\1', '', '');
    
        $message = preg_replace($match, $replace, $message);
    }
    
    /**
    * Strips all bbcode from a text and returns the plain content
    */
    function strip_bbcode(&$text, $uid = '')
    {
        if (!$uid)
        {
            $uid = '[0-9a-z]{5,}';
        }
    
        $text = preg_replace("#\[\/?[a-z0-9\*\+\-]+(?:=(?:".*"|[^\]]*))?(?::[a-z])?(\:$uid)\]#", ' ', $text);
    
        $match = get_preg_expression('bbcode_htm');
        $replace = array('\1', '\1', '\2', '\1', '', '');
    
        $text = preg_replace($match, $replace, $text);
    }
    
    /**
    * For display of custom parsed text on user-facing pages
    * Expects $text to be the value directly from the database (stored value)
    */
    function generate_text_for_display($text, $uid, $bitfield, $flags)
    {
        static $bbcode;
    
        if (!$text)
        {
            return '';
        }
    
        $text = censor_text($text);
    
        // Parse bbcode if bbcode uid stored and bbcode enabled
        if ($uid && ($flags & OPTION_FLAG_BBCODE))
        {
            if (!class_exists('bbcode'))
            {
                global $phpbb_root_path, $phpEx;
                include($phpbb_root_path . 'includes/bbcode.' . $phpEx);
            }
    
            if (empty($bbcode))
            {
                $bbcode = new bbcode($bitfield);
            }
            else
            {
                $bbcode->bbcode($bitfield);
            }
    
            $bbcode->bbcode_second_pass($text, $uid);
        }
    
        $text = bbcode_nl2br($text);
        $text = smiley_text($text, !($flags & OPTION_FLAG_SMILIES));
    
        return $text;
    }
    
    /**
    * For parsing custom parsed text to be stored within the database.
    * This function additionally returns the uid and bitfield that needs to be stored.
    * Expects $text to be the value directly from request_var() and in it's non-parsed form
    */
    function generate_text_for_storage(&$text, &$uid, &$bitfield, &$flags, $allow_bbcode = false, $allow_urls = false, $allow_smilies = false)
    {
        global $phpbb_root_path, $phpEx;
    
        $uid = $bitfield = '';
        $flags = (($allow_bbcode) ? OPTION_FLAG_BBCODE : 0) + (($allow_smilies) ? OPTION_FLAG_SMILIES : 0) + (($allow_urls) ? OPTION_FLAG_LINKS : 0);
    
        if (!$text)
        {
            return;
        }
    
        if (!class_exists('parse_message'))
        {
            include($phpbb_root_path . 'includes/message_parser.' . $phpEx);
        }
    
        $message_parser = new parse_message($text);
        $message_parser->parse($allow_bbcode, $allow_urls, $allow_smilies);
    
        $text = $message_parser->message;
        $uid = $message_parser->bbcode_uid;
    
        // If the bbcode_bitfield is empty, there is no need for the uid to be stored.
        if (!$message_parser->bbcode_bitfield)
        {
            $uid = '';
        }
    
        $bitfield = $message_parser->bbcode_bitfield;
    
        return;
    }
    
    /**
    * For decoding custom parsed text for edits as well as extracting the flags
    * Expects $text to be the value directly from the database (pre-parsed content)
    */
    function generate_text_for_edit($text, $uid, $flags)
    {
        global $phpbb_root_path, $phpEx;
    
        decode_message($text, $uid);
    
        return array(
            'allow_bbcode'    => ($flags & OPTION_FLAG_BBCODE) ? 1 : 0,
            'allow_smilies'    => ($flags & OPTION_FLAG_SMILIES) ? 1 : 0,
            'allow_urls'    => ($flags & OPTION_FLAG_LINKS) ? 1 : 0,
            'text'            => $text
        );
    }
    
    /**
    * A subroutine of make_clickable used with preg_replace
    * It places correct HTML around an url, shortens the displayed text
    * and makes sure no entities are inside URLs
    */
    function make_clickable_callback($type, $whitespace, $url, $relative_url, $class)
    {
        $orig_url        = $url;
        $orig_relative    = $relative_url;
        $append            = '';
        $url            = htmlspecialchars_decode($url);
        $relative_url    = htmlspecialchars_decode($relative_url);
    
        // make sure no HTML entities were matched
        $chars = array('<', '>', '"');
        $split = false;
    
        foreach ($chars as $char)
        {
            $next_split = strpos($url, $char);
            if ($next_split !== false)
            {
                $split = ($split !== false) ? min($split, $next_split) : $next_split;
            }
        }
    
        if ($split !== false)
        {
            // an HTML entity was found, so the URL has to end before it
            $append            = substr($url, $split) . $relative_url;
            $url            = substr($url, 0, $split);
            $relative_url    = '';
        }
        else if ($relative_url)
        {
            // same for $relative_url
            $split = false;
            foreach ($chars as $char)
            {
                $next_split = strpos($relative_url, $char);
                if ($next_split !== false)
                {
                    $split = ($split !== false) ? min($split, $next_split) : $next_split;
                }
            }
    
            if ($split !== false)
            {
                $append            = substr($relative_url, $split);
                $relative_url    = substr($relative_url, 0, $split);
            }
        }
    
        // if the last character of the url is a punctuation mark, exclude it from the url
        $last_char = ($relative_url) ? $relative_url[strlen($relative_url) - 1] : $url[strlen($url) - 1];
    
        switch ($last_char)
        {
            case '.':
            case '?':
            case '!':
            case ':':
            case ',':
                $append = $last_char;
                if ($relative_url)
                {
                    $relative_url = substr($relative_url, 0, -1);
                }
                else
                {
                    $url = substr($url, 0, -1);
                }
            break;
    
            // set last_char to empty here, so the variable can be used later to
            // check whether a character was removed
            default:
                $last_char = '';
            break;
        }
    
        $short_url = (strlen($url) > 55) ? substr($url, 0, 39) . ' ... ' . substr($url, -10) : $url;
    
        switch ($type)
        {
            case MAGIC_URL_LOCAL:
                $tag            = 'l';
                $relative_url    = preg_replace('/[&?]sid=[0-9a-f]{32}$/', '', preg_replace('/([&?])sid=[0-9a-f]{32}&/', '$1', $relative_url));
                $url            = $url . '/' . $relative_url;
                $text            = $relative_url;
    
                // this url goes to http://domain.tld/path/to/board/ which
                // would result in an empty link if treated as local so
                // don't touch it and let MAGIC_URL_FULL take care of it.
                if (!$relative_url)
                {
                    return $whitespace . $orig_url . '/' . $orig_relative; // slash is taken away by relative url pattern
                }
            break;
    
            case MAGIC_URL_FULL:
                $tag    = 'm';
                $text    = $short_url;
            break;
    
            case MAGIC_URL_WWW:
                $tag    = 'w';
                $url    = 'http://' . $url;
                $text    = $short_url;
            break;
    
            case MAGIC_URL_EMAIL:
                $tag    = 'e';
                $text    = $short_url;
                $url    = 'mailto:' . $url;
            break;
        }
    
        $url    = htmlspecialchars($url);
        $text    = htmlspecialchars($text);
        $append    = htmlspecialchars($append);
    
        if ($type == MAGIC_URL_EMAIL)
        {
            $html    = "$whitespace<!-- $tag --><a$class href=\"$url\">$text</a><!-- $tag -->$append";    
        }
        else 
        {
            $html    = "$whitespace<!-- $tag --><a$class href=\"$url\" onclick=\"window.open(this.href);return false;\">$text</a><!-- $tag -->$append";
        }
    
        return $html;
    }
    
    /**
    * make_clickable function
    *
    * Replace magic urls of form http://xxx.xxx., www.xxx. and xxx@xxx.xxx.
    * Cuts down displayed size of link if over 50 chars, turns absolute links
    * into relative versions when the server/script path matches the link
    */
    function make_clickable($text, $server_url = false, $class = 'postlink')
    {
        if ($server_url === false)
        {
            $server_url = generate_board_url();
        }
    
        static $magic_url_match;
        static $magic_url_replace;
        static $static_class;
    
        if (!is_array($magic_url_match) || $static_class != $class)
        {
            $static_class = $class;
            $class = ($static_class) ? ' class="' . $static_class . '"' : '';
            $local_class = ($static_class) ? ' class="' . $static_class . '-local"' : '';
    
            $magic_url_match = $magic_url_replace = array();
            // Be sure to not let the matches cross over. ;)
    
            // relative urls for this board
            $magic_url_match[] = '#(^|[\n\t (>.])(' . preg_quote($server_url, '#') . ')/(' . get_preg_expression('relative_url_inline') . ')#ie';
            $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_LOCAL, '\$1', '\$2', '\$3', '$local_class')";
    
            // matches a xxxx://aaaaa.bbb.cccc. ...
            $magic_url_match[] = '#(^|[\n\t (>.])(' . get_preg_expression('url_inline') . ')#ie';
            $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_FULL, '\$1', '\$2', '', '$class')";
    
            // matches a "www.xxxx.yyyy[/zzzz]" kinda lazy URL thing
            $magic_url_match[] = '#(^|[\n\t (>])(' . get_preg_expression('www_url_inline') . ')#ie';
            $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_WWW, '\$1', '\$2', '', '$class')";
    
            // matches an email@domain type address at the start of a line, or after a space or after what might be a BBCode.
            $magic_url_match[] = '/(^|[\n\t (>])(' . get_preg_expression('email') . ')/ie';
            $magic_url_replace[] = "make_clickable_callback(MAGIC_URL_EMAIL, '\$1', '\$2', '', '')";
        }
    
        return preg_replace($magic_url_match, $magic_url_replace, $text);
    }
    
    /**
    * Censoring
    */
    function censor_text($text)
    {
        static $censors;
    
        // Nothing to do?
        if ($text === '')
        {
            return '';
        }
    
        // We moved the word censor checks in here because we call this function quite often - and then only need to do the check once
        if (!isset($censors) || !is_array($censors))
        {
            global $config, $user, $auth, $cache;
    
            // We check here if the user is having viewing censors disabled (and also allowed to do so).
            if (!$user->optionget('viewcensors') && $config['allow_nocensors'] && $auth->acl_get('u_chgcensors'))
            {
                $censors = array();
            }
            else
            {
                $censors = $cache->obtain_word_list();
            }
        }
    
        if (sizeof($censors))
        {
            return preg_replace($censors['match'], $censors['replace'], $text);
        }
    
        return $text;
    }
    
    /**
    * custom version of nl2br which takes custom BBCodes into account
    */
    function bbcode_nl2br($text)
    {
        // custom BBCodes might contain carriage returns so they
        // are not converted into <br /> so now revert that
        $text = str_replace(array("\n", "\r"), array('<br />', "\n"), $text);
        return $text;
    }
    
    /**
    * Smiley processing
    */
    function smiley_text($text, $force_option = false)
    {
        global $config, $user, $phpbb_root_path;
    
        if ($force_option || !$config['allow_smilies'] || !$user->optionget('viewsmilies'))
        {
            return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/.*? \/><!\-\- s\1 \-\->#', '\1', $text);
        }
        else
        {
            $root_path = (defined('PHPBB_USE_BOARD_URL_PATH') && PHPBB_USE_BOARD_URL_PATH) ? generate_board_url() . '/' : $phpbb_root_path;
            return preg_replace('#<!\-\- s(.*?) \-\-><img src="\{SMILIES_PATH\}\/(.*?) \/><!\-\- s\1 \-\->#', '<img src="' . $root_path . $config['smilies_path'] . '/\2 />', $text);
        }
    }
    
    /**
    * General attachment parsing
    *
    * @param mixed $forum_id The forum id the attachments are displayed in (false if in private message)
    * @param string &$message The post/private message
    * @param array &$attachments The attachments to parse for (inline) display. The attachments array will hold templated data after parsing.
    * @param array &$update_count The attachment counts to be updated - will be filled
    * @param bool $preview If set to true the attachments are parsed for preview. Within preview mode the comments are fetched from the given $attachments array and not fetched from the database.
    */
    function parse_attachments($forum_id, &$message, &$attachments, &$update_count, $preview = false)
    {
        if (!sizeof($attachments))
        {
            return;
        }
    
        global $template, $cache, $user;
        global $extensions, $config, $phpbb_root_path, $phpEx;
    
        //
        $compiled_attachments = array();
    
        if (!isset($template->filename['attachment_tpl']))
        {
            $template->set_filenames(array(
                'attachment_tpl'    => 'attachment.html')
            );
        }
    
        if (empty($extensions) || !is_array($extensions))
        {
            $extensions = $cache->obtain_attach_extensions($forum_id);
        }
    
        // Look for missing attachment information...
        $attach_ids = array();
        foreach ($attachments as $pos => $attachment)
        {
            // If is_orphan is set, we need to retrieve the attachments again...
            if (!isset($attachment['extension']) && !isset($attachment['physical_filename']))
            {
                $attach_ids[(int) $attachment['attach_id']] = $pos;
            }
        }
    
        // Grab attachments (security precaution)
        if (sizeof($attach_ids))
        {
            global $db;
    
            $new_attachment_data = array();
    
            $sql = 'SELECT *
                FROM ' . ATTACHMENTS_TABLE . '
                WHERE ' . $db->sql_in_set('attach_id', array_keys($attach_ids));
            $result = $db->sql_query($sql);
    
            while ($row = $db->sql_fetchrow($result))
            {
                if (!isset($attach_ids[$row['attach_id']]))
                {
                    continue;
                }
    
                // If we preview attachments we will set some retrieved values here
                if ($preview)
                {
                    $row['attach_comment'] = $attachments[$attach_ids[$row['attach_id']]]['attach_comment'];
                }
    
                $new_attachment_data[$attach_ids[$row['attach_id']]] = $row;
            }
            $db->sql_freeresult($result);
    
            $attachments = $new_attachment_data;
            unset($new_attachment_data);
        }
    
        // Sort correctly
        if ($config['display_order'])
        {
            // Ascending sort
            krsort($attachments);
        }
        else
        {
            // Descending sort
            ksort($attachments);
        }
    
        foreach ($attachments as $attachment)
        {
            if (!sizeof($attachment))
            {
                continue;
            }
    
            // We need to reset/empty the _file block var, because this function might be called more than once
            $template->destroy_block_vars('_file');
    
            $block_array = array();
    
            // Some basics...
            $attachment['extension'] = strtolower(trim($attachment['extension']));
            $filename = $phpbb_root_path . $config['upload_path'] . '/' . utf8_basename($attachment['physical_filename']);
            $thumbnail_filename = $phpbb_root_path . $config['upload_path'] . '/thumb_' . utf8_basename($attachment['physical_filename']);
    
            $upload_icon = '';
    
            if (isset($extensions[$attachment['extension']]))
            {
                if ($user->img('icon_topic_attach', '') && !$extensions[$attachment['extension']]['upload_icon'])
                {
                    $upload_icon = $user->img('icon_topic_attach', '');
                }
                else if ($extensions[$attachment['extension']]['upload_icon'])
                {
                    $upload_icon = '<img src="' . $phpbb_root_path . $config['upload_icons_path'] . '/' . trim($extensions[$attachment['extension']]['upload_icon']) . '" alt="" />';
                }
            }
    
            $filesize = get_formatted_filesize($attachment['filesize'], false);
    
            $comment = bbcode_nl2br(censor_text($attachment['attach_comment']));
    
            $block_array += array(
                'UPLOAD_ICON'        => $upload_icon,
                'FILESIZE'            => $filesize['value'],
                'SIZE_LANG'            => $filesize['unit'],
                'DOWNLOAD_NAME'        => utf8_basename($attachment['real_filename']),
                'COMMENT'            => $comment,
            );
    
            $denied = false;
    
            if (!extension_allowed($forum_id, $attachment['extension'], $extensions))
            {
                $denied = true;
    
                $block_array += array(
                    'S_DENIED'            => true,
                    'DENIED_MESSAGE'    => sprintf($user->lang['EXTENSION_DISABLED_AFTER_POSTING'], $attachment['extension'])
                );
            }
    
            if (!$denied)
            {
                $l_downloaded_viewed = $download_link = '';
                $display_cat = $extensions[$attachment['extension']]['display_cat'];
    
                if ($display_cat == ATTACHMENT_CATEGORY_IMAGE)
                {
                    if ($attachment['thumbnail'])
                    {
                        $display_cat = ATTACHMENT_CATEGORY_THUMB;
                    }
                    else
                    {
                        if ($config['img_display_inlined'])
                        {
                            if ($config['img_link_width'] || $config['img_link_height'])
                            {
                                $dimension = @getimagesize($filename);
    
                                // If the dimensions could not be determined or the image being 0x0 we display it as a link for safety purposes
                                if ($dimension === false || empty($dimension[0]) || empty($dimension[1]))
                                {
                                    $display_cat = ATTACHMENT_CATEGORY_NONE;
                                }
                                else
                                {
                                    $display_cat = ($dimension[0] <= $config['img_link_width'] && $dimension[1] <= $config['img_link_height']) ? ATTACHMENT_CATEGORY_IMAGE : ATTACHMENT_CATEGORY_NONE;
                                }
                            }
                        }
                        else
                        {
                            $display_cat = ATTACHMENT_CATEGORY_NONE;
                        }
                    }
                }
    
                // Make some descisions based on user options being set.
                if (($display_cat == ATTACHMENT_CATEGORY_IMAGE || $display_cat == ATTACHMENT_CATEGORY_THUMB) && !$user->optionget('viewimg'))
                {
                    $display_cat = ATTACHMENT_CATEGORY_NONE;
                }
    
                if ($display_cat == ATTACHMENT_CATEGORY_FLASH && !$user->optionget('viewflash'))
                {
                    $display_cat = ATTACHMENT_CATEGORY_NONE;
                }
    
                $download_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
    
                switch ($display_cat)
                {
                    // Images
                    case ATTACHMENT_CATEGORY_IMAGE:
                        $l_downloaded_viewed = 'VIEWED_COUNT';
                        $inline_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id']);
                        $download_link .= '&mode=view';
    
                        $block_array += array(
                            'S_IMAGE'        => true,
                            'U_INLINE_LINK'        => $inline_link,
                        );
    
                        $update_count[] = $attachment['attach_id'];
                    break;
    
                    // Images, but display Thumbnail
                    case ATTACHMENT_CATEGORY_THUMB:
                        $l_downloaded_viewed = 'VIEWED_COUNT';
                        $thumbnail_link = append_sid("{$phpbb_root_path}download/file.$phpEx", 'id=' . $attachment['attach_id'] . '&t=1');
                        $download_link .= '&mode=view';
    
                        $block_array += array(
                            'S_THUMBNAIL'        => true,
                            'THUMB_IMAGE'        => $thumbnail_link,
                        );
    
                        $update_count[] = $attachment['attach_id'];
                    break;
    
                    // Windows Media Streams
                    case ATTACHMENT_CATEGORY_WM:
                        $l_downloaded_viewed = 'VIEWED_COUNT';
    
                        // Giving the filename directly because within the wm object all variables are in local context making it impossible
                        // to validate against a valid session (all params can differ)
                        // $download_link = $filename;
    
                        $block_array += array(
                            'U_FORUM'        => generate_board_url(),
                            'ATTACH_ID'        => $attachment['attach_id'],
                            'S_WM_FILE'        => true,
                        );
    
                        // Viewed/Heared File ... update the download count
                        $update_count[] = $attachment['attach_id'];
                    break;
    
                    // Real Media Streams
                    case ATTACHMENT_CATEGORY_RM:
                    case ATTACHMENT_CATEGORY_QUICKTIME:
                        $l_downloaded_viewed = 'VIEWED_COUNT';
    
                        $block_array += array(
                            'S_RM_FILE'            => ($display_cat == ATTACHMENT_CATEGORY_RM) ? true : false,
                            'S_QUICKTIME_FILE'    => ($display_cat == ATTACHMENT_CATEGORY_QUICKTIME) ? true : false,
                            'U_FORUM'            => generate_board_url(),
                            'ATTACH_ID'            => $attachment['attach_id'],
                        );
    
                        // Viewed/Heared File ... update the download count
                        $update_count[] = $attachment['attach_id'];
                    break;
    
                    // Macromedia Flash Files
                    case ATTACHMENT_CATEGORY_FLASH:
                        list($width, $height) = @getimagesize($filename);
    
                        $l_downloaded_viewed = 'VIEWED_COUNT';
    
                        $block_array += array(
                            'S_FLASH_FILE'    => true,
                            'WIDTH'            => $width,
                            'HEIGHT'        => $height,
                            'U_VIEW_LINK'    => $download_link . '&view=1',
                        );
    
                        // Viewed/Heared File ... update the download count
                        $update_count[] = $attachment['attach_id'];
                    break;
    
                    default:
                        $l_downloaded_viewed = 'DOWNLOAD_COUNT';
    
                        $block_array += array(
                            'S_FILE'        => true,
                        );
                    break;
                }
    
                $l_download_count = (!isset($attachment['download_count']) || $attachment['download_count'] == 0) ? $user->lang[$l_downloaded_viewed . '_NONE'] : (($attachment['download_count'] == 1) ? sprintf($user->lang[$l_downloaded_viewed], $attachment['download_count']) : sprintf($user->lang[$l_downloaded_viewed . 'S'], $attachment['download_count']));
    
                $block_array += array(
                    'U_DOWNLOAD_LINK'        => $download_link,
                    'L_DOWNLOAD_COUNT'        => $l_download_count
                );
            }
    
            $template->assign_block_vars('_file', $block_array);
    
            $compiled_attachments[] = $template->assign_display('attachment_tpl');
        }
    
        $attachments = $compiled_attachments;
        unset($compiled_attachments);
    
        $tpl_size = sizeof($attachments);
    
        $unset_tpl = array();
    
        preg_match_all('#<!\-\- ia([0-9]+) \-\->(.*?)<!\-\- ia\1 \-\->#', $message, $matches, PREG_PATTERN_ORDER);
    
        $replace = array();
        foreach ($matches[0] as $num => $capture)
        {
            // Flip index if we are displaying the reverse way
            $index = ($config['display_order']) ? ($tpl_size-($matches[1][$num] + 1)) : $matches[1][$num];
    
            $replace['from'][] = $matches[0][$num];
            $replace['to'][] = (isset($attachments[$index])) ? $attachments[$index] : sprintf($user->lang['MISSING_INLINE_ATTACHMENT'], $matches[2][array_search($index, $matches[1])]);
    
            $unset_tpl[] = $index;
        }
    
        if (isset($replace['from']))
        {
            $message = str_replace($replace['from'], $replace['to'], $message);
        }
    
        $unset_tpl = array_unique($unset_tpl);
    
        // Needed to let not display the inlined attachments at the end of the post again
        foreach ($unset_tpl as $index)
        {
            unset($attachments[$index]);
        }
    }
    
    /**
    * Check if extension is allowed to be posted.
    *
    * @param mixed $forum_id The forum id to check or false if private message
    * @param string $extension The extension to check, for example zip.
    * @param array &$extensions The extension array holding the information from the cache (will be obtained if empty)
    *
    * @return bool False if the extension is not allowed to be posted, else true.
    */
    function extension_allowed($forum_id, $extension, &$extensions)
    {
        if (empty($extensions))
        {
            global $cache;
            $extensions = $cache->obtain_attach_extensions($forum_id);
        }
    
        return (!isset($extensions['_allowed_'][$extension])) ? false : true;
    }
    
    /**
    * Truncates string while retaining special characters if going over the max length
    * The default max length is 60 at the moment
    * The maximum storage length is there to fit the string within the given length. The string may be further truncated due to html entities.
    * For example: string given is 'a "quote"' (length: 9), would be a stored as 'a "quote"' (length: 19)
    *
    * @param string $string The text to truncate to the given length. String is specialchared.
    * @param int $max_length Maximum length of string (multibyte character count as 1 char / Html entity count as 1 char)
    * @param int $max_store_length Maximum character length of string (multibyte character count as 1 char / Html entity count as entity chars).
    * @param bool $allow_reply Allow Re: in front of string 
    *     NOTE: This parameter can cause undesired behavior (returning strings longer than $max_store_length) and is deprecated. 
    * @param string $append String to be appended
    */
    function truncate_string($string, $max_length = 60, $max_store_length = 255, $allow_reply = false, $append = '')
    {
        $chars = array();
    
        $strip_reply = false;
        $stripped = false;
        if ($allow_reply && strpos($string, 'Re: ') === 0)
        {
            $strip_reply = true;
            $string = substr($string, 4);
        }
    
        $_chars = utf8_str_split(htmlspecialchars_decode($string));
        $chars = array_map('utf8_htmlspecialchars', $_chars);
    
        // Now check the length ;)
        if (sizeof($chars) > $max_length)
        {
            // Cut off the last elements from the array
            $string = implode('', array_slice($chars, 0, $max_length - utf8_strlen($append)));
            $stripped = true;
        }
    
        // Due to specialchars, we may not be able to store the string...
        if (utf8_strlen($string) > $max_store_length)
        {
            // let's split again, we do not want half-baked strings where entities are split
            $_chars = utf8_str_split(htmlspecialchars_decode($string));
            $chars = array_map('utf8_htmlspecialchars', $_chars);
    
            do
            {
                array_pop($chars);
                $string = implode('', $chars);
            }
            while (!empty($chars) && utf8_strlen($string) > $max_store_length);
        }
    
        if ($strip_reply)
        {
            $string = 'Re: ' . $string;
        }
    
        if ($append != '' && $stripped)
        {
            $string = $string . $append;
        }
    
        return $string;
    }
    
    /**
    * Get username details for placing into templates.
    * This function caches all modes on first call, except for no_profile and anonymous user - determined by $user_id.
    *
    * @param string $mode Can be profile (for getting an url to the profile), username (for obtaining the username), colour (for obtaining the user colour), full (for obtaining a html string representing a coloured link to the users profile) or no_profile (the same as full but forcing no profile link)
    * @param int $user_id The users id
    * @param string $username The users name
    * @param string $username_colour The users colour
    * @param string $guest_username optional parameter to specify the guest username. It will be used in favor of the GUEST language variable then.
    * @param string $custom_profile_url optional parameter to specify a profile url. The user id get appended to this url as &u={user_id}
    *
    * @return string A string consisting of what is wanted based on $mode.
    * @author BartVB, Acyd Burn
    */
    function get_username_string($mode, $user_id, $username, $username_colour = '', $guest_username = false, $custom_profile_url = false)
    {
        static $_profile_cache;
    
        // We cache some common variables we need within this function
        if (empty($_profile_cache))
        {
            global $phpbb_root_path, $phpEx;
    
            $_profile_cache['base_url'] = append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=viewprofile&u={USER_ID}');
            $_profile_cache['tpl_noprofile'] = '{USERNAME}';
            $_profile_cache['tpl_noprofile_colour'] = '<span style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</span>';
            $_profile_cache['tpl_profile'] = '<a href="{PROFILE_URL}">{USERNAME}</a>';
            $_profile_cache['tpl_profile_colour'] = '<a href="{PROFILE_URL}" style="color: {USERNAME_COLOUR};" class="username-coloured">{USERNAME}</a>';
        }
    
        global $user, $auth;
    
        // This switch makes sure we only run code required for the mode
        switch ($mode)
        {
            case 'full':
            case 'no_profile':
            case 'colour':
    
                // Build correct username colour
                $username_colour = ($username_colour) ? '#' . $username_colour : '';
    
                // Return colour
                if ($mode == 'colour')
                {
                    return $username_colour;
                }
    
            // no break;
    
            case 'username':
    
                // Build correct username
                if ($guest_username === false)
                {
                    $username = ($username) ? $username : $user->lang['GUEST'];
                }
                else
                {
                    $username = ($user_id && $user_id != ANONYMOUS) ? $username : ((!empty($guest_username)) ? $guest_username : $user->lang['GUEST']);
                }
    
                // Return username
                if ($mode == 'username')
                {
                    return $username;
                }
    
            // no break;
    
            case 'profile':
    
                // Build correct profile url - only show if not anonymous and permission to view profile if registered user
                // For anonymous the link leads to a login page.
                if ($user_id && $user_id != ANONYMOUS && ($user->data['user_id'] == ANONYMOUS || $auth->acl_get('u_viewprofile')))
                {
                    $profile_url = ($custom_profile_url !== false) ? $custom_profile_url . '&u=' . (int) $user_id : str_replace(array('={USER_ID}', '=%7BUSER_ID%7D'), '=' . (int) $user_id, $_profile_cache['base_url']);
                }
                else
                {
                    $profile_url = '';
                }
    
                // Return profile
                if ($mode == 'profile')
                {
                    return $profile_url;
                }
    
            // no break;
        }
    
        if (($mode == 'full' && !$profile_url) || $mode == 'no_profile')
        {
            return str_replace(array('{USERNAME_COLOUR}', '{USERNAME}'), array($username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_noprofile'] : $_profile_cache['tpl_noprofile_colour']);
        }
    
        return str_replace(array('{PROFILE_URL}', '{USERNAME_COLOUR}', '{USERNAME}'), array($profile_url, $username_colour, $username), (!$username_colour) ? $_profile_cache['tpl_profile'] : $_profile_cache['tpl_profile_colour']);
    }
    
    /**
    * @package phpBB3
    */
    class bitfield
    {
        var $data;
    
        function bitfield($bitfield = '')
        {
            $this->data = base64_decode($bitfield);
        }
    
        /**
        */
        function get($n)
        {
            // Get the ($n / 8)th char
            $byte = $n >> 3;
    
            if (strlen($this->data) >= $byte + 1)
            {
                $c = $this->data[$byte];
    
                // Lookup the ($n % 8)th bit of the byte
                $bit = 7 - ($n & 7);
                return (bool) (ord($c) & (1 << $bit));
            }
            else
            {
                return false;
            }
        }
    
        function set($n)
        {
            $byte = $n >> 3;
            $bit = 7 - ($n & 7);
    
            if (strlen($this->data) >= $byte + 1)
            {
                $this->data[$byte] = $this->data[$byte] | chr(1 << $bit);
            }
            else
            {
                $this->data .= str_repeat("\0", $byte - strlen($this->data));
                $this->data .= chr(1 << $bit);
            }
        }
    
        function clear($n)
        {
            $byte = $n >> 3;
    
            if (strlen($this->data) >= $byte + 1)
            {
                $bit = 7 - ($n & 7);
                $this->data[$byte] = $this->data[$byte] &~ chr(1 << $bit);
            }
        }
    
        function get_blob()
        {
            return $this->data;
        }
    
        function get_base64()
        {
            return base64_encode($this->data);
        }
    
        function get_bin()
        {
            $bin = '';
            $len = strlen($this->data);
    
            for ($i = 0; $i < $len; ++$i)
            {
                $bin .= str_pad(decbin(ord($this->data[$i])), 8, '0', STR_PAD_LEFT);
            }
    
            return $bin;
        }
    
        function get_all_set()
        {
            return array_keys(array_filter(str_split($this->get_bin())));
        }
    
        function merge($bitfield)
        {
            $this->data = $this->data | $bitfield->get_blob();
        }
    }
    
    ?>
    Editado pela última vez por robra em 28 Nov 2012, 02:44, em um total de 1 vez.
    Razão: Colocação dos 3 arquivos na mesma mensagem.
    Avatar do usuário
    robra
    Membro Sênior
    Membro Sênior
    Mensagens: 6065
    Registrado em: 02 Dez 2010, 23:29
    MSG 1500+Avatar

    Re: Problemas com links (estilo Skymiles)

  • Favoritar esta postagem
  • Mensagem por robra »

    A edição dos arquivos está correta. Você utilizou qual programa para editá-los :?:

    Abraço. Imagem
    [DICA] Antes de abrir um Novo Tópico, procure saber se a sua dúvida já foi respondida. Pesquise no Google por palavras(em inglês também) relacionadas à ela, somado à palavra "phpbb".
    gustavocave
    Novato
    Novato
    Mensagens: 60
    Registrado em: 16 Nov 2012, 10:24
    MSG 50+

    Re: Problemas com links (estilo Skymiles)

  • Favoritar esta postagem
  • Mensagem por gustavocave »

    Uso o dreamweaver!

    No tema prosilver deu certo, nos demais não!
    Avatar do usuário
    robra
    Membro Sênior
    Membro Sênior
    Mensagens: 6065
    Registrado em: 02 Dez 2010, 23:29
    MSG 1500+Avatar

    Re: Problemas com links (estilo Skymiles)

  • Favoritar esta postagem
  • Mensagem por robra »

    Não use o Dreamweaver para editar os arquivos porque ele muda a codificação deles e os altera. Use somente o Notepad++. Leia o tópico Erro ao instalar Ultimate SEO 0.6.8 com o Dreamwaver
    Como postei anteriormente, segui o artigo citado por você na mensagem viewtopic.php?f=147&t=47797#p284939 para editar o estilo Skymiles e funcionou perfeitamente.
    Pegue a versão anterior dos 3 arquivos citados no artigo, antes da edição pelo Dreamwaver e faça a edição pelo Notepad++.

    Abraço. Imagem
    [DICA] Antes de abrir um Novo Tópico, procure saber se a sua dúvida já foi respondida. Pesquise no Google por palavras(em inglês também) relacionadas à ela, somado à palavra "phpbb".
    gustavocave
    Novato
    Novato
    Mensagens: 60
    Registrado em: 16 Nov 2012, 10:24
    MSG 50+

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por gustavocave »

    Fiz o procedimento, sem sucesso. =/
    Avatar do usuário
    robra
    Membro Sênior
    Membro Sênior
    Mensagens: 6065
    Registrado em: 02 Dez 2010, 23:29
    MSG 1500+Avatar

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por robra »

    Limpe o cache de seu navegador, porque agora está funcionando corretamente.
    Na mensagem http://www.jackfish.com.br/viewtopic.ph ... 084#p86935, acabei de clicar no link e a página abriu em uma nova aba, tanto no Google Chrome como no Firefox.

    Abraço. Imagem
    [DICA] Antes de abrir um Novo Tópico, procure saber se a sua dúvida já foi respondida. Pesquise no Google por palavras(em inglês também) relacionadas à ela, somado à palavra "phpbb".
    gustavocave
    Novato
    Novato
    Mensagens: 60
    Registrado em: 16 Nov 2012, 10:24
    MSG 50+

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por gustavocave »

    Eu ia comentar isso agora, parece que alguns funcionam e outros não. Parece que os links novos, funcionam, o que ja veio com o banco não.

    Faz um teste: http://jackfish.com.br/viewtopic.php?f=53&t=9060 (1o link ai, casadoanzol )
    Avatar do usuário
    robra
    Membro Sênior
    Membro Sênior
    Mensagens: 6065
    Registrado em: 02 Dez 2010, 23:29
    MSG 1500+Avatar

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por robra »

    Se você converteu o Fórum de um outro sistema para o phpBB, então o problema está na conversão.
    Se você usava alguma MOD para postar links e a desinstalou de forma incorreta, então, o problema está aí.
    Na análise das 2 mensagens, o link em http://jackfish.com.br/viewtopic.php?f=53&t=9060#p86820 está desta forma:

    Código: Selecionar todos

    <!-- m --><a class="postlink" href="http://endereço_do_link">http://endereço_do_link</a><!-- m -->
    Já na mensagem http://www.jackfish.com.br/viewtopic.ph ... 084#p86935 o link está desta forma:

    Código: Selecionar todos

    <a href="http://endereço_do_site" onclick="window.open(this.href);return false;" class="postlink">http://endereço_do_site</a>
    Na primeira mensagem, o link está entre <!-- m -->

    Abraço. Imagem
    [DICA] Antes de abrir um Novo Tópico, procure saber se a sua dúvida já foi respondida. Pesquise no Google por palavras(em inglês também) relacionadas à ela, somado à palavra "phpbb".
    gustavocave
    Novato
    Novato
    Mensagens: 60
    Registrado em: 16 Nov 2012, 10:24
    MSG 50+

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por gustavocave »

    Sabe em dizer qual arquivo devo editar para retirar esse <--m--> ?
    Avatar do usuário
    robra
    Membro Sênior
    Membro Sênior
    Mensagens: 6065
    Registrado em: 02 Dez 2010, 23:29
    MSG 1500+Avatar

    Re: Problemas com links (estilo Subsilver2)

  • Favoritar esta postagem
  • Mensagem por robra »

    Tende ver se era alguma MOD para poder realizar a desinstalação completa.
    De qualquer forma, acredito que deve estar no viewtopic_body.html ou no bbcode.html.

    Abraço. Imagem
    [DICA] Antes de abrir um Novo Tópico, procure saber se a sua dúvida já foi respondida. Pesquise no Google por palavras(em inglês também) relacionadas à ela, somado à palavra "phpbb".
    Trancado