n't need that due to php doing it. Reality offers a differing opinion, though header("Status: $code $message", true, $code); } else { if (isset($_SERVER['HTTP_VERSION'])) { $version = $_SERVER['HTTP_VERSION']; } 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_ 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 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$') { 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); } /** * 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 * @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 .= ''; } $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 .= ''; } $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 .= ''; } } 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 = $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' => " ( (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 AND t.topic_last_post_time > $last_mark) ) $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; // 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']; } } // 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 AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time) GROUP BY t.forum_id'; $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 topic_id FROM ' . TOPICS_TABLE . ' WHERE forum_id = ' . $forum_id . ' AND topic_last_post_time > ' . $mark_time_forum . ' AND topic_moved_id = 0'; $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 = '' . $user->lang['COMMA_SEPARATOR'] . ''; $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) ? '1' : '1'; 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) ? ' ... ' : $seperator; for ($i = $start_cnt + 1; $i < $end_cnt; $i++) { $page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . ''; if ($i < $end_cnt - 1) { $page_string .= $seperator; } } $page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator; } else { $page_string .= $seperator; for ($i = 2; $i < $total_pages; $i++) { $page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . ''; if ($i < $total_pages) { $page_string .= $seperator; } } } $page_string .= ($on_page == $total_pages) ? '' . $total_pages . '' : '' . $total_pages . ''; if ($add_prevnext_text) { if ($on_page != 1) { $page_string = '' . $user->lang['PREVIOUS'] . '  ' . $page_string; } if ($on_page != $total_pages) { $page_string .= '  ' . $user->lang['NEXT'] . ''; } } $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: * * 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)); * * */ function append_sid($url, $params = false, $is_amp = true, $session_id = false) { global $_SID, $_EXTRA_URL, $phpbb_hook; // 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) */ 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 ''; echo ''; echo ''; echo ''; echo ''; echo '' . $user->lang['REDIRECT'] . ''; echo ''; echo ''; echo '
' . sprintf($user->lang['URL_REDIRECT'], '', '') . '
'; echo ''; echo ''; 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' => '') ); 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 should
Fatal error: Call to undefined function: request_var() in D:\inetpub\vhosts\ncalcricket.org\subdomains\forum\httpdocs\install\index.php on line 172