Blame Extras/phpBB/3.0.4/includes/functions.php

4c79b5
4c79b5
/**
4c79b5
*
4c79b5
* @package phpBB3
4c79b5
* @version $Id: functions.php 9153 2008-12-02 17:02:56Z acydburn $
4c79b5
* @copyright (c) 2005 phpBB Group
4c79b5
* @license http://opensource.org/licenses/gpl-license.php GNU Public License
4c79b5
*
4c79b5
*/
4c79b5
4c79b5
/**
4c79b5
* @ignore
4c79b5
*/
4c79b5
if (!defined('IN_PHPBB'))
4c79b5
{
4c79b5
	exit;
4c79b5
}
4c79b5
4c79b5
// Common global functions
4c79b5
4c79b5
/**
4c79b5
* set_var
4c79b5
*
4c79b5
* Set variable, used by {@link request_var the request_var function}
4c79b5
*
4c79b5
* @access private
4c79b5
*/
4c79b5
function set_var(&$result, $var, $type, $multibyte = false)
4c79b5
{
4c79b5
	settype($var, $type);
4c79b5
	$result = $var;
4c79b5
4c79b5
	if ($type == 'string')
4c79b5
	{
4c79b5
		$result = trim(htmlspecialchars(str_replace(array("\r\n", "\r", "\0"), array("\n", "\n", ''), $result), ENT_COMPAT, 'UTF-8'));
4c79b5
4c79b5
		if (!empty($result))
4c79b5
		{
4c79b5
			// Make sure multibyte characters are wellformed
4c79b5
			if ($multibyte)
4c79b5
			{
4c79b5
				if (!preg_match('/^./u', $result))
4c79b5
				{
4c79b5
					$result = '';
4c79b5
				}
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				// no multibyte, allow only ASCII (0-127)
4c79b5
				$result = preg_replace('/[\x80-\xFF]/', '?', $result);
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		$result = (STRIP) ? stripslashes($result) : $result;
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* request_var
4c79b5
*
4c79b5
* Used to get passed variable
4c79b5
*/
4c79b5
function request_var($var_name, $default, $multibyte = false, $cookie = false)
4c79b5
{
4c79b5
	if (!$cookie && isset($_COOKIE[$var_name]))
4c79b5
	{
4c79b5
		if (!isset($_GET[$var_name]) && !isset($_POST[$var_name]))
4c79b5
		{
4c79b5
			return (is_array($default)) ? array() : $default;
4c79b5
		}
4c79b5
		$_REQUEST[$var_name] = isset($_POST[$var_name]) ? $_POST[$var_name] : $_GET[$var_name];
4c79b5
	}
4c79b5
4c79b5
	if (!isset($_REQUEST[$var_name]) || (is_array($_REQUEST[$var_name]) && !is_array($default)) || (is_array($default) && !is_array($_REQUEST[$var_name])))
4c79b5
	{
4c79b5
		return (is_array($default)) ? array() : $default;
4c79b5
	}
4c79b5
4c79b5
	$var = $_REQUEST[$var_name];
4c79b5
	if (!is_array($default))
4c79b5
	{
4c79b5
		$type = gettype($default);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		list($key_type, $type) = each($default);
4c79b5
		$type = gettype($type);
4c79b5
		$key_type = gettype($key_type);
4c79b5
		if ($type == 'array')
4c79b5
		{
4c79b5
			reset($default);
4c79b5
			$default = current($default);
4c79b5
			list($sub_key_type, $sub_type) = each($default);
4c79b5
			$sub_type = gettype($sub_type);
4c79b5
			$sub_type = ($sub_type == 'array') ? 'NULL' : $sub_type;
4c79b5
			$sub_key_type = gettype($sub_key_type);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if (is_array($var))
4c79b5
	{
4c79b5
		$_var = $var;
4c79b5
		$var = array();
4c79b5
4c79b5
		foreach ($_var as $k => $v)
4c79b5
		{
4c79b5
			set_var($k, $k, $key_type);
4c79b5
			if ($type == 'array' && is_array($v))
4c79b5
			{
4c79b5
				foreach ($v as $_k => $_v)
4c79b5
				{
4c79b5
					if (is_array($_v))
4c79b5
					{
4c79b5
						$_v = null;
4c79b5
					}
4c79b5
					set_var($_k, $_k, $sub_key_type);
4c79b5
					set_var($var[$k][$_k], $_v, $sub_type, $multibyte);
4c79b5
				}
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				if ($type == 'array' || is_array($v))
4c79b5
				{
4c79b5
					$v = null;
4c79b5
				}
4c79b5
				set_var($var[$k], $v, $type, $multibyte);
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		set_var($var, $var, $type, $multibyte);
4c79b5
	}
4c79b5
4c79b5
	return $var;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Set config value. Creates missing config entry.
4c79b5
*/
4c79b5
function set_config($config_name, $config_value, $is_dynamic = false)
4c79b5
{
4c79b5
	global $db, $cache, $config;
4c79b5
4c79b5
	$sql = 'UPDATE ' . CONFIG_TABLE . "
4c79b5
		SET config_value = '" . $db->sql_escape($config_value) . "'
4c79b5
		WHERE config_name = '" . $db->sql_escape($config_name) . "'";
4c79b5
	$db->sql_query($sql);
4c79b5
4c79b5
	if (!$db->sql_affectedrows() && !isset($config[$config_name]))
4c79b5
	{
4c79b5
		$sql = 'INSERT INTO ' . CONFIG_TABLE . ' ' . $db->sql_build_array('INSERT', array(
4c79b5
			'config_name'	=> $config_name,
4c79b5
			'config_value'	=> $config_value,
4c79b5
			'is_dynamic'	=> ($is_dynamic) ? 1 : 0));
4c79b5
		$db->sql_query($sql);
4c79b5
	}
4c79b5
4c79b5
	$config[$config_name] = $config_value;
4c79b5
4c79b5
	if (!$is_dynamic)
4c79b5
	{
4c79b5
		$cache->destroy('config');
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generates an alphanumeric random string of given length
4c79b5
*/
4c79b5
function gen_rand_string($num_chars = 8)
4c79b5
{
4c79b5
	$rand_str = unique_id();
4c79b5
	$rand_str = str_replace('0', 'Z', strtoupper(base_convert($rand_str, 16, 35)));
4c79b5
4c79b5
	return substr($rand_str, 0, $num_chars);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Return unique id
4c79b5
* @param string $extra additional entropy
4c79b5
*/
4c79b5
function unique_id($extra = 'c')
4c79b5
{
4c79b5
	static $dss_seeded = false;
4c79b5
	global $config;
4c79b5
4c79b5
	$val = $config['rand_seed'] . microtime();
4c79b5
	$val = md5($val);
4c79b5
	$config['rand_seed'] = md5($config['rand_seed'] . $val . $extra);
4c79b5
4c79b5
	if ($dss_seeded !== true && ($config['rand_seed_last_update'] < time() - rand(1,10)))
4c79b5
	{
4c79b5
		set_config('rand_seed', $config['rand_seed'], true);
4c79b5
		set_config('rand_seed_last_update', time(), true);
4c79b5
		$dss_seeded = true;
4c79b5
	}
4c79b5
4c79b5
	return substr($val, 4, 16);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Return formatted string for filesizes
4c79b5
*/
4c79b5
function get_formatted_filesize($bytes, $add_size_lang = true)
4c79b5
{
4c79b5
	global $user;
4c79b5
4c79b5
	if ($bytes >= pow(2, 20))
4c79b5
	{
4c79b5
		return ($add_size_lang) ? round($bytes / 1024 / 1024, 2) . ' ' . $user->lang['MIB'] : round($bytes / 1024 / 1024, 2);
4c79b5
	}
4c79b5
4c79b5
	if ($bytes >= pow(2, 10))
4c79b5
	{
4c79b5
		return ($add_size_lang) ? round($bytes / 1024, 2) . ' ' . $user->lang['KIB'] : round($bytes / 1024, 2);
4c79b5
	}
4c79b5
4c79b5
	return ($add_size_lang) ? ($bytes) . ' ' . $user->lang['BYTES'] : ($bytes);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Determine whether we are approaching the maximum execution time. Should be called once
4c79b5
* at the beginning of the script in which it's used.
4c79b5
* @return	bool	Either true if the maximum execution time is nearly reached, or false
4c79b5
*					if some time is still left.
4c79b5
*/
4c79b5
function still_on_time($extra_time = 15)
4c79b5
{
4c79b5
	static $max_execution_time, $start_time;
4c79b5
4c79b5
	$time = explode(' ', microtime());
4c79b5
	$current_time = $time[0] + $time[1];
4c79b5
4c79b5
	if (empty($max_execution_time))
4c79b5
	{
4c79b5
		$max_execution_time = (function_exists('ini_get')) ? (int) @ini_get('max_execution_time') : (int) @get_cfg_var('max_execution_time');
4c79b5
4c79b5
		// If zero, then set to something higher to not let the user catch the ten seconds barrier.
4c79b5
		if ($max_execution_time === 0)
4c79b5
		{
4c79b5
			$max_execution_time = 50 + $extra_time;
4c79b5
		}
4c79b5
4c79b5
		$max_execution_time = min(max(10, ($max_execution_time - $extra_time)), 50);
4c79b5
4c79b5
		// For debugging purposes
4c79b5
		// $max_execution_time = 10;
4c79b5
4c79b5
		global $starttime;
4c79b5
		$start_time = (empty($starttime)) ? $current_time : $starttime;
4c79b5
	}
4c79b5
4c79b5
	return (ceil($current_time - $start_time) < $max_execution_time) ? true : false;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
*
4c79b5
* @version Version 0.1 / slightly modified for phpBB 3.0.x (using $H$ as hash type identifier)
4c79b5
*
4c79b5
* Portable PHP password hashing framework.
4c79b5
*
4c79b5
* Written by Solar Designer <solar at openwall.com> in 2004-2006 and placed in
4c79b5
* the public domain.
4c79b5
*
4c79b5
* There's absolutely no warranty.
4c79b5
*
4c79b5
* The homepage URL for this framework is:
4c79b5
*
4c79b5
*	http://www.openwall.com/phpass/
4c79b5
*
4c79b5
* Please be sure to update the Version line if you edit this file in any way.
4c79b5
* It is suggested that you leave the main version number intact, but indicate
4c79b5
* your project name (after the slash) and add your own revision information.
4c79b5
*
4c79b5
* Please do not change the "private" password hashing method implemented in
4c79b5
* here, thereby making your hashes incompatible.  However, if you must, please
4c79b5
* change the hash type identifier (the "$P$") to something different.
4c79b5
*
4c79b5
* Obviously, since this code is in the public domain, the above are not
4c79b5
* requirements (there can be none), but merely suggestions.
4c79b5
*
4c79b5
*
4c79b5
* Hash the password
4c79b5
*/
4c79b5
function phpbb_hash($password)
4c79b5
{
4c79b5
	$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
4c79b5
4c79b5
	$random_state = unique_id();
4c79b5
	$random = '';
4c79b5
	$count = 6;
4c79b5
4c79b5
	if (($fh = @fopen('/dev/urandom', 'rb')))
4c79b5
	{
4c79b5
		$random = fread($fh, $count);
4c79b5
		fclose($fh);
4c79b5
	}
4c79b5
4c79b5
	if (strlen($random) < $count)
4c79b5
	{
4c79b5
		$random = '';
4c79b5
4c79b5
		for ($i = 0; $i < $count; $i += 16)
4c79b5
		{
4c79b5
			$random_state = md5(unique_id() . $random_state);
4c79b5
			$random .= pack('H*', md5($random_state));
4c79b5
		}
4c79b5
		$random = substr($random, 0, $count);
4c79b5
	}
4c79b5
4c79b5
	$hash = _hash_crypt_private($password, _hash_gensalt_private($random, $itoa64), $itoa64);
4c79b5
4c79b5
	if (strlen($hash) == 34)
4c79b5
	{
4c79b5
		return $hash;
4c79b5
	}
4c79b5
4c79b5
	return md5($password);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Check for correct password
4c79b5
*
4c79b5
* @param string $password The password in plain text
4c79b5
* @param string $hash The stored password hash
4c79b5
*
4c79b5
* @return bool Returns true if the password is correct, false if not.
4c79b5
*/
4c79b5
function phpbb_check_hash($password, $hash)
4c79b5
{
4c79b5
	$itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
4c79b5
	if (strlen($hash) == 34)
4c79b5
	{
4c79b5
		return (_hash_crypt_private($password, $hash, $itoa64) === $hash) ? true : false;
4c79b5
	}
4c79b5
4c79b5
	return (md5($password) === $hash) ? true : false;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generate salt for hash generation
4c79b5
*/
4c79b5
function _hash_gensalt_private($input, &$itoa64, $iteration_count_log2 = 6)
4c79b5
{
4c79b5
	if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
4c79b5
	{
4c79b5
		$iteration_count_log2 = 8;
4c79b5
	}
4c79b5
4c79b5
	$output = '$H$';
4c79b5
	$output .= $itoa64[min($iteration_count_log2 + ((PHP_VERSION >= 5) ? 5 : 3), 30)];
4c79b5
	$output .= _hash_encode64($input, 6, $itoa64);
4c79b5
4c79b5
	return $output;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Encode hash
4c79b5
*/
4c79b5
function _hash_encode64($input, $count, &$itoa64)
4c79b5
{
4c79b5
	$output = '';
4c79b5
	$i = 0;
4c79b5
4c79b5
	do
4c79b5
	{
4c79b5
		$value = ord($input[$i++]);
4c79b5
		$output .= $itoa64[$value & 0x3f];
4c79b5
4c79b5
		if ($i < $count)
4c79b5
		{
4c79b5
			$value |= ord($input[$i]) << 8;
4c79b5
		}
4c79b5
4c79b5
		$output .= $itoa64[($value >> 6) & 0x3f];
4c79b5
4c79b5
		if ($i++ >= $count)
4c79b5
		{
4c79b5
			break;
4c79b5
		}
4c79b5
4c79b5
		if ($i < $count)
4c79b5
		{
4c79b5
			$value |= ord($input[$i]) << 16;
4c79b5
		}
4c79b5
4c79b5
		$output .= $itoa64[($value >> 12) & 0x3f];
4c79b5
4c79b5
		if ($i++ >= $count)
4c79b5
		{
4c79b5
			break;
4c79b5
		}
4c79b5
4c79b5
		$output .= $itoa64[($value >> 18) & 0x3f];
4c79b5
	}
4c79b5
	while ($i < $count);
4c79b5
4c79b5
	return $output;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* The crypt function/replacement
4c79b5
*/
4c79b5
function _hash_crypt_private($password, $setting, &$itoa64)
4c79b5
{
4c79b5
	$output = '*';
4c79b5
4c79b5
	// Check for correct hash
4c79b5
	if (substr($setting, 0, 3) != '$H$')
4c79b5
	{
4c79b5
		return $output;
4c79b5
	}
4c79b5
4c79b5
	$count_log2 = strpos($itoa64, $setting[3]);
4c79b5
4c79b5
	if ($count_log2 < 7 || $count_log2 > 30)
4c79b5
	{
4c79b5
		return $output;
4c79b5
	}
4c79b5
4c79b5
	$count = 1 << $count_log2;
4c79b5
	$salt = substr($setting, 4, 8);
4c79b5
4c79b5
	if (strlen($salt) != 8)
4c79b5
	{
4c79b5
		return $output;
4c79b5
	}
4c79b5
4c79b5
	/**
4c79b5
	* We're kind of forced to use MD5 here since it's the only
4c79b5
	* cryptographic primitive available in all versions of PHP
4c79b5
	* currently in use.  To implement our own low-level crypto
4c79b5
	* in PHP would result in much worse performance and
4c79b5
	* consequently in lower iteration counts and hashes that are
4c79b5
	* quicker to crack (by non-PHP code).
4c79b5
	*/
4c79b5
	if (PHP_VERSION >= 5)
4c79b5
	{
4c79b5
		$hash = md5($salt . $password, true);
4c79b5
		do
4c79b5
		{
4c79b5
			$hash = md5($hash . $password, true);
4c79b5
		}
4c79b5
		while (--$count);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$hash = pack('H*', md5($salt . $password));
4c79b5
		do
4c79b5
		{
4c79b5
			$hash = pack('H*', md5($hash . $password));
4c79b5
		}
4c79b5
		while (--$count);
4c79b5
	}
4c79b5
4c79b5
	$output = substr($setting, 0, 12);
4c79b5
	$output .= _hash_encode64($hash, 16, $itoa64);
4c79b5
4c79b5
	return $output;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Global function for chmodding directories and files for internal use
4c79b5
* This function determines owner and group whom the file belongs to and user and group of PHP and then set safest possible file permissions.
4c79b5
* The function determines owner and group from common.php file and sets the same to the provided file. Permissions are mapped to the group, user always has rw(x) permission.
4c79b5
* The function uses bit fields to build the permissions.
4c79b5
* The function sets the appropiate execute bit on directories.
4c79b5
*
4c79b5
* Supported constants representing bit fields are:
4c79b5
*
4c79b5
* CHMOD_ALL - all permissions (7)
4c79b5
* CHMOD_READ - read permission (4)
4c79b5
* CHMOD_WRITE - write permission (2)
4c79b5
* CHMOD_EXECUTE - execute permission (1)
4c79b5
*
4c79b5
* 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.
4c79b5
*
4c79b5
* @param $filename The file/directory to be chmodded
4c79b5
* @param $perms Permissions to set
4c79b5
* @return true on success, otherwise false
4c79b5
*
4c79b5
* @author faw, phpBB Group
4c79b5
*/
4c79b5
function phpbb_chmod($filename, $perms = CHMOD_READ)
4c79b5
{
4c79b5
	// Return if the file no longer exists.
4c79b5
	if (!file_exists($filename))
4c79b5
	{
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	if (!function_exists('fileowner') || !function_exists('filegroup'))
4c79b5
	{
4c79b5
		$file_uid = $file_gid = false;
4c79b5
		$common_php_owner = $common_php_group = false;
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		global $phpbb_root_path, $phpEx;
4c79b5
4c79b5
		// Determine owner/group of common.php file and the filename we want to change here
4c79b5
		$common_php_owner = fileowner($phpbb_root_path . 'common.' . $phpEx);
4c79b5
		$common_php_group = filegroup($phpbb_root_path . 'common.' . $phpEx);
4c79b5
4c79b5
		$file_uid = fileowner($filename);
4c79b5
		$file_gid = filegroup($filename);
4c79b5
4c79b5
		// Try to set the owner to the same common.php has
4c79b5
		if ($common_php_owner !== $file_uid && $common_php_owner !== false && $file_uid !== false)
4c79b5
		{
4c79b5
			// Will most likely not work
4c79b5
			if (@chown($filename, $common_php_owner));
4c79b5
			{
4c79b5
				clearstatcache();
4c79b5
				$file_uid = fileowner($filename);
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		// Try to set the group to the same common.php has
4c79b5
		if ($common_php_group !== $file_gid && $common_php_group !== false && $file_gid !== false)
4c79b5
		{
4c79b5
			if (@chgrp($filename, $common_php_group));
4c79b5
			{
4c79b5
				clearstatcache();
4c79b5
				$file_gid = filegroup($filename);
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// And the owner and the groups PHP is running under.
4c79b5
	$php_uid = (function_exists('posix_getuid')) ? @posix_getuid() : false;
4c79b5
	$php_gids = (function_exists('posix_getgroups')) ? @posix_getgroups() : false;
4c79b5
4c79b5
	// Who is PHP?
4c79b5
	if ($file_uid === false || $file_gid === false || $php_uid === false || $php_gids === false)
4c79b5
	{
4c79b5
		$php = NULL;
4c79b5
	}
4c79b5
	else if ($file_uid == $php_uid /* && $common_php_owner !== false && $common_php_owner === $file_uid*/)
4c79b5
	{
4c79b5
		$php = 'owner';
4c79b5
	}
4c79b5
	else if (in_array($file_gid, $php_gids))
4c79b5
	{
4c79b5
		$php = 'group';
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$php = 'other';
4c79b5
	}
4c79b5
4c79b5
	// Owner always has read/write permission
4c79b5
	$owner = CHMOD_READ | CHMOD_WRITE;
4c79b5
	if (is_dir($filename))
4c79b5
	{
4c79b5
		$owner |= CHMOD_EXECUTE;
4c79b5
4c79b5
		// Only add execute bit to the permission if the dir needs to be readable
4c79b5
		if ($perms & CHMOD_READ)
4c79b5
		{
4c79b5
			$perms |= CHMOD_EXECUTE;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	switch ($php)
4c79b5
	{
4c79b5
		case null:
4c79b5
		case 'owner':
4c79b5
			/* ATTENTION: if php is owner or NULL we set it to group here. This is the most failsafe combination for the vast majority of server setups.
4c79b5
4c79b5
			$result = @chmod($filename, ($owner << 6) + (0 << 3) + (0 << 0));
4c79b5
4c79b5
			clearstatcache();
4c79b5
4c79b5
			if (!is_null($php) || (is_readable($filename) && is_writable($filename)))
4c79b5
			{
4c79b5
				break;
4c79b5
			}
4c79b5
		*/
4c79b5
4c79b5
		case 'group':
4c79b5
			$result = @chmod($filename, ($owner << 6) + ($perms << 3) + (0 << 0));
4c79b5
4c79b5
			clearstatcache();
4c79b5
4c79b5
			if (!is_null($php) || ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename))))
4c79b5
			{
4c79b5
				break;
4c79b5
			}
4c79b5
4c79b5
		case 'other':
4c79b5
			$result = @chmod($filename, ($owner << 6) + ($perms << 3) + ($perms << 0));
4c79b5
4c79b5
			clearstatcache();
4c79b5
4c79b5
			if (!is_null($php) || ((!($perms & CHMOD_READ) || is_readable($filename)) && (!($perms & CHMOD_WRITE) || is_writable($filename))))
4c79b5
			{
4c79b5
				break;
4c79b5
			}
4c79b5
4c79b5
		default:
4c79b5
			return false;
4c79b5
		break;
4c79b5
	}
4c79b5
4c79b5
	return $result;
4c79b5
}
4c79b5
4c79b5
// Compatibility functions
4c79b5
4c79b5
if (!function_exists('array_combine'))
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for the PHP5 function array_combine()
4c79b5
	* @param array $keys contains keys for the resulting array
4c79b5
	* @param array $values contains values for the resulting array
4c79b5
	*
4c79b5
	* @return Returns an array by using the values from the keys array as keys and the
4c79b5
	* 	values from the values array as the corresponding values. Returns false if the
4c79b5
	* 	number of elements for each array isn't equal or if the arrays are empty.
4c79b5
	*/
4c79b5
	function array_combine($keys, $values)
4c79b5
	{
4c79b5
		$keys = array_values($keys);
4c79b5
		$values = array_values($values);
4c79b5
4c79b5
		$n = sizeof($keys);
4c79b5
		$m = sizeof($values);
4c79b5
		if (!$n || !$m || ($n != $m))
4c79b5
		{
4c79b5
			return false;
4c79b5
		}
4c79b5
4c79b5
		$combined = array();
4c79b5
		for ($i = 0; $i < $n; $i++)
4c79b5
		{
4c79b5
			$combined[$keys[$i]] = $values[$i];
4c79b5
		}
4c79b5
		return $combined;
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
if (!function_exists('str_split'))
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for the PHP5 function str_split()
4c79b5
	* @param array $string contains the string to be converted
4c79b5
	* @param array $split_length contains the length of each chunk
4c79b5
	*
4c79b5
	* @return  Converts a string to an array. If the optional split_length parameter is specified,
4c79b5
	*  	the returned array will be broken down into chunks with each being split_length in length,
4c79b5
	*  	otherwise each chunk will be one character in length. FALSE is returned if split_length is
4c79b5
	*  	less than 1. If the split_length length exceeds the length of string, the entire string is
4c79b5
	*  	returned as the first (and only) array element.
4c79b5
	*/
4c79b5
	function str_split($string, $split_length = 1)
4c79b5
	{
4c79b5
		if ($split_length < 1)
4c79b5
		{
4c79b5
			return false;
4c79b5
		}
4c79b5
		else if ($split_length >= strlen($string))
4c79b5
		{
4c79b5
			return array($string);
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			preg_match_all('#.{1,' . $split_length . '}#s', $string, $matches);
4c79b5
			return $matches[0];
4c79b5
		}
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
if (!function_exists('stripos'))
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for the PHP5 function stripos
4c79b5
	* Find position of first occurrence of a case-insensitive string
4c79b5
	*
4c79b5
	* @param string $haystack is the string to search in
4c79b5
	* @param string $needle is the string to search for
4c79b5
	*
4c79b5
	* @return mixed Returns the numeric position of the first occurrence of needle in the haystack string. Unlike strpos(), stripos() is case-insensitive.
4c79b5
	* Note that the needle may be a string of one or more characters.
4c79b5
	* If needle is not found, stripos() will return boolean FALSE.
4c79b5
	*/
4c79b5
	function stripos($haystack, $needle)
4c79b5
	{
4c79b5
		if (preg_match('#' . preg_quote($needle, '#') . '#i', $haystack, $m))
4c79b5
		{
4c79b5
			return strpos($haystack, $m[0]);
4c79b5
		}
4c79b5
4c79b5
		return false;
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Checks if a path ($path) is absolute or relative
4c79b5
*
4c79b5
* @param string $path Path to check absoluteness of
4c79b5
* @return boolean
4c79b5
*/
4c79b5
function is_absolute($path)
4c79b5
{
4c79b5
	return ($path[0] == '/' || (DIRECTORY_SEPARATOR == '\\' && preg_match('#^[a-z]:/#i', $path))) ? true : false;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* @author Chris Smith <chris@project-minerva.org>
4c79b5
* @copyright 2006 Project Minerva Team
4c79b5
* @param string $path The path which we should attempt to resolve.
4c79b5
* @return mixed
4c79b5
*/
4c79b5
function phpbb_own_realpath($path)
4c79b5
{
4c79b5
	// Now to perform funky shizzle
4c79b5
4c79b5
	// Switch to use UNIX slashes
4c79b5
	$path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
4c79b5
	$path_prefix = '';
4c79b5
4c79b5
	// Determine what sort of path we have
4c79b5
	if (is_absolute($path))
4c79b5
	{
4c79b5
		$absolute = true;
4c79b5
4c79b5
		if ($path[0] == '/')
4c79b5
		{
4c79b5
			// Absolute path, *NIX style
4c79b5
			$path_prefix = '';
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			// Absolute path, Windows style
4c79b5
			// Remove the drive letter and colon
4c79b5
			$path_prefix = $path[0] . ':';
4c79b5
			$path = substr($path, 2);
4c79b5
		}
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		// Relative Path
4c79b5
		// Prepend the current working directory
4c79b5
		if (function_exists('getcwd'))
4c79b5
		{
4c79b5
			// This is the best method, hopefully it is enabled!
4c79b5
			$path = str_replace(DIRECTORY_SEPARATOR, '/', getcwd()) . '/' . $path;
4c79b5
			$absolute = true;
4c79b5
			if (preg_match('#^[a-z]:#i', $path))
4c79b5
			{
4c79b5
				$path_prefix = $path[0] . ':';
4c79b5
				$path = substr($path, 2);
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$path_prefix = '';
4c79b5
			}
4c79b5
		}
4c79b5
		else if (isset($_SERVER['SCRIPT_FILENAME']) && !empty($_SERVER['SCRIPT_FILENAME']))
4c79b5
		{
4c79b5
			// Warning: If chdir() has been used this will lie!
4c79b5
			// Warning: This has some problems sometime (CLI can create them easily)
4c79b5
			$path = str_replace(DIRECTORY_SEPARATOR, '/', dirname($_SERVER['SCRIPT_FILENAME'])) . '/' . $path;
4c79b5
			$absolute = true;
4c79b5
			$path_prefix = '';
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			// We have no way of getting the absolute path, just run on using relative ones.
4c79b5
			$absolute = false;
4c79b5
			$path_prefix = '.';
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Remove any repeated slashes
4c79b5
	$path = preg_replace('#/{2,}#', '/', $path);
4c79b5
4c79b5
	// Remove the slashes from the start and end of the path
4c79b5
	$path = trim($path, '/');
4c79b5
4c79b5
	// Break the string into little bits for us to nibble on
4c79b5
	$bits = explode('/', $path);
4c79b5
4c79b5
	// Remove any . in the path, renumber array for the loop below
4c79b5
	$bits = array_values(array_diff($bits, array('.')));
4c79b5
4c79b5
	// Lets get looping, run over and resolve any .. (up directory)
4c79b5
	for ($i = 0, $max = sizeof($bits); $i < $max; $i++)
4c79b5
	{
4c79b5
		// @todo Optimise
4c79b5
		if ($bits[$i] == '..' )
4c79b5
		{
4c79b5
			if (isset($bits[$i - 1]))
4c79b5
			{
4c79b5
				if ($bits[$i - 1] != '..')
4c79b5
				{
4c79b5
					// We found a .. and we are able to traverse upwards, lets do it!
4c79b5
					unset($bits[$i]);
4c79b5
					unset($bits[$i - 1]);
4c79b5
					$i -= 2;
4c79b5
					$max -= 2;
4c79b5
					$bits = array_values($bits);
4c79b5
				}
4c79b5
			}
4c79b5
			else if ($absolute) // ie. !isset($bits[$i - 1]) && $absolute
4c79b5
			{
4c79b5
				// We have an absolute path trying to descend above the root of the filesystem
4c79b5
				// ... Error!
4c79b5
				return false;
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Prepend the path prefix
4c79b5
	array_unshift($bits, $path_prefix);
4c79b5
4c79b5
	$resolved = '';
4c79b5
4c79b5
	$max = sizeof($bits) - 1;
4c79b5
4c79b5
	// Check if we are able to resolve symlinks, Windows cannot.
4c79b5
	$symlink_resolve = (function_exists('readlink')) ? true : false;
4c79b5
4c79b5
	foreach ($bits as $i => $bit)
4c79b5
	{
4c79b5
		if (@is_dir("$resolved/$bit") || ($i == $max && @is_file("$resolved/$bit")))
4c79b5
		{
4c79b5
			// Path Exists
4c79b5
			if ($symlink_resolve && is_link("$resolved/$bit") && ($link = readlink("$resolved/$bit")))
4c79b5
			{
4c79b5
				// Resolved a symlink.
4c79b5
				$resolved = $link . (($i == $max) ? '' : '/');
4c79b5
				continue;
4c79b5
			}
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			// Something doesn't exist here!
4c79b5
			// This is correct realpath() behaviour but sadly open_basedir and safe_mode make this problematic
4c79b5
			// return false;
4c79b5
		}
4c79b5
		$resolved .= $bit . (($i == $max) ? '' : '/');
4c79b5
	}
4c79b5
4c79b5
	// @todo If the file exists fine and open_basedir only has one path we should be able to prepend it
4c79b5
	// because we must be inside that basedir, the question is where...
4c79b5
	// @internal The slash in is_dir() gets around an open_basedir restriction
4c79b5
	if (!@file_exists($resolved) || (!is_dir($resolved . '/') && !is_file($resolved)))
4c79b5
	{
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	// Put the slashes back to the native operating systems slashes
4c79b5
	$resolved = str_replace('/', DIRECTORY_SEPARATOR, $resolved);
4c79b5
4c79b5
	// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
4c79b5
	if (substr($resolved, -1) == DIRECTORY_SEPARATOR)
4c79b5
	{
4c79b5
		return substr($resolved, 0, -1);
4c79b5
	}
4c79b5
4c79b5
	return $resolved; // We got here, in the end!
4c79b5
}
4c79b5
4c79b5
if (!function_exists('realpath'))
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for realpath
4c79b5
	* @ignore
4c79b5
	*/
4c79b5
	function phpbb_realpath($path)
4c79b5
	{
4c79b5
		return phpbb_own_realpath($path);
4c79b5
	}
4c79b5
}
4c79b5
else
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for realpath
4c79b5
	*/
4c79b5
	function phpbb_realpath($path)
4c79b5
	{
4c79b5
		$realpath = realpath($path);
4c79b5
4c79b5
		// Strangely there are provider not disabling realpath but returning strange values. :o
4c79b5
		// We at least try to cope with them.
4c79b5
		if ($realpath === $path || $realpath === false)
4c79b5
		{
4c79b5
			return phpbb_own_realpath($path);
4c79b5
		}
4c79b5
4c79b5
		// Check for DIRECTORY_SEPARATOR at the end (and remove it!)
4c79b5
		if (substr($realpath, -1) == DIRECTORY_SEPARATOR)
4c79b5
		{
4c79b5
			$realpath = substr($realpath, 0, -1);
4c79b5
		}
4c79b5
4c79b5
		return $realpath;
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
if (!function_exists('htmlspecialchars_decode'))
4c79b5
{
4c79b5
	/**
4c79b5
	* A wrapper for htmlspecialchars_decode
4c79b5
	* @ignore
4c79b5
	*/
4c79b5
	function htmlspecialchars_decode($string, $quote_style = ENT_COMPAT)
4c79b5
	{
4c79b5
		return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
// functions used for building option fields
4c79b5
4c79b5
/**
4c79b5
* Pick a language, any language ...
4c79b5
*/
4c79b5
function language_select($default = '')
4c79b5
{
4c79b5
	global $db;
4c79b5
4c79b5
	$sql = 'SELECT lang_iso, lang_local_name
4c79b5
		FROM ' . LANG_TABLE . '
4c79b5
		ORDER BY lang_english_name';
4c79b5
	$result = $db->sql_query($sql);
4c79b5
4c79b5
	$lang_options = '';
4c79b5
	while ($row = $db->sql_fetchrow($result))
4c79b5
	{
4c79b5
		$selected = ($row['lang_iso'] == $default) ? ' selected="selected"' : '';
4c79b5
		$lang_options .= '<option value="' . $row['lang_iso'] . '"' . $selected . '>' . $row['lang_local_name'] . '</option>';
4c79b5
	}
4c79b5
	$db->sql_freeresult($result);
4c79b5
4c79b5
	return $lang_options;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Pick a template/theme combo,
4c79b5
*/
4c79b5
function style_select($default = '', $all = false)
4c79b5
{
4c79b5
	global $db;
4c79b5
4c79b5
	$sql_where = (!$all) ? 'WHERE style_active = 1 ' : '';
4c79b5
	$sql = 'SELECT style_id, style_name
4c79b5
		FROM ' . STYLES_TABLE . "
4c79b5
		$sql_where
4c79b5
		ORDER BY style_name";
4c79b5
	$result = $db->sql_query($sql);
4c79b5
4c79b5
	$style_options = '';
4c79b5
	while ($row = $db->sql_fetchrow($result))
4c79b5
	{
4c79b5
		$selected = ($row['style_id'] == $default) ? ' selected="selected"' : '';
4c79b5
		$style_options .= '<option value="' . $row['style_id'] . '"' . $selected . '>' . $row['style_name'] . '</option>';
4c79b5
	}
4c79b5
	$db->sql_freeresult($result);
4c79b5
4c79b5
	return $style_options;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Pick a timezone
4c79b5
*/
4c79b5
function tz_select($default = '', $truncate = false)
4c79b5
{
4c79b5
	global $user;
4c79b5
4c79b5
	$tz_select = '';
4c79b5
	foreach ($user->lang['tz_zones'] as $offset => $zone)
4c79b5
	{
4c79b5
		if ($truncate)
4c79b5
		{
4c79b5
			$zone_trunc = truncate_string($zone, 50, 255, false, '...');
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$zone_trunc = $zone;
4c79b5
		}
4c79b5
4c79b5
		if (is_numeric($offset))
4c79b5
		{
4c79b5
			$selected = ($offset == $default) ? ' selected="selected"' : '';
4c79b5
			$tz_select .= '<option title="'.$zone.'" value="' . $offset . '"' . $selected . '>' . $zone_trunc . '</option>';
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	return $tz_select;
4c79b5
}
4c79b5
4c79b5
// Functions handling topic/post tracking/marking
4c79b5
4c79b5
/**
4c79b5
* Marks a topic/forum as read
4c79b5
* Marks a topic as posted to
4c79b5
*
4c79b5
* @param int $user_id can only be used with $mode == 'post'
4c79b5
*/
4c79b5
function markread($mode, $forum_id = false, $topic_id = false, $post_time = 0, $user_id = 0)
4c79b5
{
4c79b5
	global $db, $user, $config;
4c79b5
4c79b5
	if ($mode == 'all')
4c79b5
	{
4c79b5
		if ($forum_id === false || !sizeof($forum_id))
4c79b5
		{
4c79b5
			if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
			{
4c79b5
				// Mark all forums read (index page)
4c79b5
				$db->sql_query('DELETE FROM ' . TOPICS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
4c79b5
				$db->sql_query('DELETE FROM ' . FORUMS_TRACK_TABLE . " WHERE user_id = {$user->data['user_id']}");
4c79b5
				$db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
4c79b5
			}
4c79b5
			else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
			{
4c79b5
				$tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
4c79b5
				$tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
4c79b5
4c79b5
				unset($tracking_topics['tf']);
4c79b5
				unset($tracking_topics['t']);
4c79b5
				unset($tracking_topics['f']);
4c79b5
				$tracking_topics['l'] = base_convert(time() - $config['board_startdate'], 10, 36);
4c79b5
4c79b5
				$user->set_cookie('track', tracking_serialize($tracking_topics), time() + 31536000);
4c79b5
				$_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking_topics)) : tracking_serialize($tracking_topics);
4c79b5
4c79b5
				unset($tracking_topics);
4c79b5
4c79b5
				if ($user->data['is_registered'])
4c79b5
				{
4c79b5
					$db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . time() . " WHERE user_id = {$user->data['user_id']}");
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		return;
4c79b5
	}
4c79b5
	else if ($mode == 'topics')
4c79b5
	{
4c79b5
		// Mark all topics in forums read
4c79b5
		if (!is_array($forum_id))
4c79b5
		{
4c79b5
			$forum_id = array($forum_id);
4c79b5
		}
4c79b5
4c79b5
		// Add 0 to forums array to mark global announcements correctly
4c79b5
		$forum_id[] = 0;
4c79b5
4c79b5
		if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
		{
4c79b5
			$sql = 'DELETE FROM ' . TOPICS_TRACK_TABLE . "
4c79b5
				WHERE user_id = {$user->data['user_id']}
4c79b5
					AND " . $db->sql_in_set('forum_id', $forum_id);
4c79b5
			$db->sql_query($sql);
4c79b5
4c79b5
			$sql = 'SELECT forum_id
4c79b5
				FROM ' . FORUMS_TRACK_TABLE . "
4c79b5
				WHERE user_id = {$user->data['user_id']}
4c79b5
					AND " . $db->sql_in_set('forum_id', $forum_id);
4c79b5
			$result = $db->sql_query($sql);
4c79b5
4c79b5
			$sql_update = array();
4c79b5
			while ($row = $db->sql_fetchrow($result))
4c79b5
			{
4c79b5
				$sql_update[] = $row['forum_id'];
4c79b5
			}
4c79b5
			$db->sql_freeresult($result);
4c79b5
4c79b5
			if (sizeof($sql_update))
4c79b5
			{
4c79b5
				$sql = 'UPDATE ' . FORUMS_TRACK_TABLE . '
4c79b5
					SET mark_time = ' . time() . "
4c79b5
					WHERE user_id = {$user->data['user_id']}
4c79b5
						AND " . $db->sql_in_set('forum_id', $sql_update);
4c79b5
				$db->sql_query($sql);
4c79b5
			}
4c79b5
4c79b5
			if ($sql_insert = array_diff($forum_id, $sql_update))
4c79b5
			{
4c79b5
				$sql_ary = array();
4c79b5
				foreach ($sql_insert as $f_id)
4c79b5
				{
4c79b5
					$sql_ary[] = array(
4c79b5
						'user_id'	=> (int) $user->data['user_id'],
4c79b5
						'forum_id'	=> (int) $f_id,
4c79b5
						'mark_time'	=> time()
4c79b5
					);
4c79b5
				}
4c79b5
4c79b5
				$db->sql_multi_insert(FORUMS_TRACK_TABLE, $sql_ary);
4c79b5
			}
4c79b5
		}
4c79b5
		else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
		{
4c79b5
			$tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
4c79b5
			$tracking = ($tracking) ? tracking_unserialize($tracking) : array();
4c79b5
4c79b5
			foreach ($forum_id as $f_id)
4c79b5
			{
4c79b5
				$topic_ids36 = (isset($tracking['tf'][$f_id])) ? $tracking['tf'][$f_id] : array();
4c79b5
4c79b5
				if (isset($tracking['tf'][$f_id]))
4c79b5
				{
4c79b5
					unset($tracking['tf'][$f_id]);
4c79b5
				}
4c79b5
4c79b5
				foreach ($topic_ids36 as $topic_id36)
4c79b5
				{
4c79b5
					unset($tracking['t'][$topic_id36]);
4c79b5
				}
4c79b5
4c79b5
				if (isset($tracking['f'][$f_id]))
4c79b5
				{
4c79b5
					unset($tracking['f'][$f_id]);
4c79b5
				}
4c79b5
4c79b5
				$tracking['f'][$f_id] = base_convert(time() - $config['board_startdate'], 10, 36);
4c79b5
			}
4c79b5
4c79b5
			if (isset($tracking['tf']) && empty($tracking['tf']))
4c79b5
			{
4c79b5
				unset($tracking['tf']);
4c79b5
			}
4c79b5
4c79b5
			$user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
4c79b5
			$_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
4c79b5
4c79b5
			unset($tracking);
4c79b5
		}
4c79b5
4c79b5
		return;
4c79b5
	}
4c79b5
	else if ($mode == 'topic')
4c79b5
	{
4c79b5
		if ($topic_id === false || $forum_id === false)
4c79b5
		{
4c79b5
			return;
4c79b5
		}
4c79b5
4c79b5
		if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
		{
4c79b5
			$sql = 'UPDATE ' . TOPICS_TRACK_TABLE . '
4c79b5
				SET mark_time = ' . (($post_time) ? $post_time : time()) . "
4c79b5
				WHERE user_id = {$user->data['user_id']}
4c79b5
					AND topic_id = $topic_id";
4c79b5
			$db->sql_query($sql);
4c79b5
4c79b5
			// insert row
4c79b5
			if (!$db->sql_affectedrows())
4c79b5
			{
4c79b5
				$db->sql_return_on_error(true);
4c79b5
4c79b5
				$sql_ary = array(
4c79b5
					'user_id'		=> (int) $user->data['user_id'],
4c79b5
					'topic_id'		=> (int) $topic_id,
4c79b5
					'forum_id'		=> (int) $forum_id,
4c79b5
					'mark_time'		=> ($post_time) ? (int) $post_time : time(),
4c79b5
				);
4c79b5
4c79b5
				$db->sql_query('INSERT INTO ' . TOPICS_TRACK_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
4c79b5
4c79b5
				$db->sql_return_on_error(false);
4c79b5
			}
4c79b5
		}
4c79b5
		else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
		{
4c79b5
			$tracking = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
4c79b5
			$tracking = ($tracking) ? tracking_unserialize($tracking) : array();
4c79b5
4c79b5
			$topic_id36 = base_convert($topic_id, 10, 36);
4c79b5
4c79b5
			if (!isset($tracking['t'][$topic_id36]))
4c79b5
			{
4c79b5
				$tracking['tf'][$forum_id][$topic_id36] = true;
4c79b5
			}
4c79b5
4c79b5
			$post_time = ($post_time) ? $post_time : time();
4c79b5
			$tracking['t'][$topic_id36] = base_convert($post_time - $config['board_startdate'], 10, 36);
4c79b5
4c79b5
			// If the cookie grows larger than 10000 characters we will remove the smallest value
4c79b5
			// This can result in old topics being unread - but most of the time it should be accurate...
4c79b5
			if (isset($_COOKIE[$config['cookie_name'] . '_track']) && strlen($_COOKIE[$config['cookie_name'] . '_track']) > 10000)
4c79b5
			{
4c79b5
				//echo 'Cookie grown too large' . print_r($tracking, true);
4c79b5
4c79b5
				// We get the ten most minimum stored time offsets and its associated topic ids
4c79b5
				$time_keys = array();
4c79b5
				for ($i = 0; $i < 10 && sizeof($tracking['t']); $i++)
4c79b5
				{
4c79b5
					$min_value = min($tracking['t']);
4c79b5
					$m_tkey = array_search($min_value, $tracking['t']);
4c79b5
					unset($tracking['t'][$m_tkey]);
4c79b5
4c79b5
					$time_keys[$m_tkey] = $min_value;
4c79b5
				}
4c79b5
4c79b5
				// Now remove the topic ids from the array...
4c79b5
				foreach ($tracking['tf'] as $f_id => $topic_id_ary)
4c79b5
				{
4c79b5
					foreach ($time_keys as $m_tkey => $min_value)
4c79b5
					{
4c79b5
						if (isset($topic_id_ary[$m_tkey]))
4c79b5
						{
4c79b5
							$tracking['f'][$f_id] = $min_value;
4c79b5
							unset($tracking['tf'][$f_id][$m_tkey]);
4c79b5
						}
4c79b5
					}
4c79b5
				}
4c79b5
4c79b5
				if ($user->data['is_registered'])
4c79b5
				{
4c79b5
					$user->data['user_lastmark'] = intval(base_convert(max($time_keys) + $config['board_startdate'], 36, 10));
4c79b5
					$db->sql_query('UPDATE ' . USERS_TABLE . ' SET user_lastmark = ' . $user->data['user_lastmark'] . " WHERE user_id = {$user->data['user_id']}");
4c79b5
				}
4c79b5
				else
4c79b5
				{
4c79b5
					$tracking['l'] = max($time_keys);
4c79b5
				}
4c79b5
			}
4c79b5
4c79b5
			$user->set_cookie('track', tracking_serialize($tracking), time() + 31536000);
4c79b5
			$_COOKIE[$config['cookie_name'] . '_track'] = (STRIP) ? addslashes(tracking_serialize($tracking)) : tracking_serialize($tracking);
4c79b5
		}
4c79b5
4c79b5
		return;
4c79b5
	}
4c79b5
	else if ($mode == 'post')
4c79b5
	{
4c79b5
		if ($topic_id === false)
4c79b5
		{
4c79b5
			return;
4c79b5
		}
4c79b5
4c79b5
		$use_user_id = (!$user_id) ? $user->data['user_id'] : $user_id;
4c79b5
4c79b5
		if ($config['load_db_track'] && $use_user_id != ANONYMOUS)
4c79b5
		{
4c79b5
			$db->sql_return_on_error(true);
4c79b5
4c79b5
			$sql_ary = array(
4c79b5
				'user_id'		=> (int) $use_user_id,
4c79b5
				'topic_id'		=> (int) $topic_id,
4c79b5
				'topic_posted'	=> 1
4c79b5
			);
4c79b5
4c79b5
			$db->sql_query('INSERT INTO ' . TOPICS_POSTED_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
4c79b5
4c79b5
			$db->sql_return_on_error(false);
4c79b5
		}
4c79b5
4c79b5
		return;
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Get topic tracking info by using already fetched info
4c79b5
*/
4c79b5
function get_topic_tracking($forum_id, $topic_ids, &$rowset, $forum_mark_time, $global_announce_list = false)
4c79b5
{
4c79b5
	global $config, $user;
4c79b5
4c79b5
	$last_read = array();
4c79b5
4c79b5
	if (!is_array($topic_ids))
4c79b5
	{
4c79b5
		$topic_ids = array($topic_ids);
4c79b5
	}
4c79b5
4c79b5
	foreach ($topic_ids as $topic_id)
4c79b5
	{
4c79b5
		if (!empty($rowset[$topic_id]['mark_time']))
4c79b5
		{
4c79b5
			$last_read[$topic_id] = $rowset[$topic_id]['mark_time'];
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$topic_ids = array_diff($topic_ids, array_keys($last_read));
4c79b5
4c79b5
	if (sizeof($topic_ids))
4c79b5
	{
4c79b5
		$mark_time = array();
4c79b5
4c79b5
		// Get global announcement info
4c79b5
		if ($global_announce_list && sizeof($global_announce_list))
4c79b5
		{
4c79b5
			if (!isset($forum_mark_time[0]))
4c79b5
			{
4c79b5
				global $db;
4c79b5
4c79b5
				$sql = 'SELECT mark_time
4c79b5
					FROM ' . FORUMS_TRACK_TABLE . "
4c79b5
					WHERE user_id = {$user->data['user_id']}
4c79b5
						AND forum_id = 0";
4c79b5
				$result = $db->sql_query($sql);
4c79b5
				$row = $db->sql_fetchrow($result);
4c79b5
				$db->sql_freeresult($result);
4c79b5
4c79b5
				if ($row)
4c79b5
				{
4c79b5
					$mark_time[0] = $row['mark_time'];
4c79b5
				}
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				if ($forum_mark_time[0] !== false)
4c79b5
				{
4c79b5
					$mark_time[0] = $forum_mark_time[0];
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		if (!empty($forum_mark_time[$forum_id]) && $forum_mark_time[$forum_id] !== false)
4c79b5
		{
4c79b5
			$mark_time[$forum_id] = $forum_mark_time[$forum_id];
4c79b5
		}
4c79b5
4c79b5
		$user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
4c79b5
4c79b5
		foreach ($topic_ids as $topic_id)
4c79b5
		{
4c79b5
			if ($global_announce_list && isset($global_announce_list[$topic_id]))
4c79b5
			{
4c79b5
				$last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$last_read[$topic_id] = $user_lastmark;
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	return $last_read;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Get topic tracking info from db (for cookie based tracking only this function is used)
4c79b5
*/
4c79b5
function get_complete_topic_tracking($forum_id, $topic_ids, $global_announce_list = false)
4c79b5
{
4c79b5
	global $config, $user;
4c79b5
4c79b5
	$last_read = array();
4c79b5
4c79b5
	if (!is_array($topic_ids))
4c79b5
	{
4c79b5
		$topic_ids = array($topic_ids);
4c79b5
	}
4c79b5
4c79b5
	if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
	{
4c79b5
		global $db;
4c79b5
4c79b5
		$sql = 'SELECT topic_id, mark_time
4c79b5
			FROM ' . TOPICS_TRACK_TABLE . "
4c79b5
			WHERE user_id = {$user->data['user_id']}
4c79b5
				AND " . $db->sql_in_set('topic_id', $topic_ids);
4c79b5
		$result = $db->sql_query($sql);
4c79b5
4c79b5
		while ($row = $db->sql_fetchrow($result))
4c79b5
		{
4c79b5
			$last_read[$row['topic_id']] = $row['mark_time'];
4c79b5
		}
4c79b5
		$db->sql_freeresult($result);
4c79b5
4c79b5
		$topic_ids = array_diff($topic_ids, array_keys($last_read));
4c79b5
4c79b5
		if (sizeof($topic_ids))
4c79b5
		{
4c79b5
			$sql = 'SELECT forum_id, mark_time
4c79b5
				FROM ' . FORUMS_TRACK_TABLE . "
4c79b5
				WHERE user_id = {$user->data['user_id']}
4c79b5
					AND forum_id " .
4c79b5
					(($global_announce_list && sizeof($global_announce_list)) ? "IN (0, $forum_id)" : "= $forum_id");
4c79b5
			$result = $db->sql_query($sql);
4c79b5
4c79b5
			$mark_time = array();
4c79b5
			while ($row = $db->sql_fetchrow($result))
4c79b5
			{
4c79b5
				$mark_time[$row['forum_id']] = $row['mark_time'];
4c79b5
			}
4c79b5
			$db->sql_freeresult($result);
4c79b5
4c79b5
			$user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user->data['user_lastmark'];
4c79b5
4c79b5
			foreach ($topic_ids as $topic_id)
4c79b5
			{
4c79b5
				if ($global_announce_list && isset($global_announce_list[$topic_id]))
4c79b5
				{
4c79b5
					$last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
4c79b5
				}
4c79b5
				else
4c79b5
				{
4c79b5
					$last_read[$topic_id] = $user_lastmark;
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
	else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
	{
4c79b5
		global $tracking_topics;
4c79b5
4c79b5
		if (!isset($tracking_topics) || !sizeof($tracking_topics))
4c79b5
		{
4c79b5
			$tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
4c79b5
			$tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
4c79b5
		}
4c79b5
4c79b5
		if (!$user->data['is_registered'])
4c79b5
		{
4c79b5
			$user_lastmark = (isset($tracking_topics['l'])) ? base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate'] : 0;
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$user_lastmark = $user->data['user_lastmark'];
4c79b5
		}
4c79b5
4c79b5
		foreach ($topic_ids as $topic_id)
4c79b5
		{
4c79b5
			$topic_id36 = base_convert($topic_id, 10, 36);
4c79b5
4c79b5
			if (isset($tracking_topics['t'][$topic_id36]))
4c79b5
			{
4c79b5
				$last_read[$topic_id] = base_convert($tracking_topics['t'][$topic_id36], 36, 10) + $config['board_startdate'];
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		$topic_ids = array_diff($topic_ids, array_keys($last_read));
4c79b5
4c79b5
		if (sizeof($topic_ids))
4c79b5
		{
4c79b5
			$mark_time = array();
4c79b5
			if ($global_announce_list && sizeof($global_announce_list))
4c79b5
			{
4c79b5
				if (isset($tracking_topics['f'][0]))
4c79b5
				{
4c79b5
					$mark_time[0] = base_convert($tracking_topics['f'][0], 36, 10) + $config['board_startdate'];
4c79b5
				}
4c79b5
			}
4c79b5
4c79b5
			if (isset($tracking_topics['f'][$forum_id]))
4c79b5
			{
4c79b5
				$mark_time[$forum_id] = base_convert($tracking_topics['f'][$forum_id], 36, 10) + $config['board_startdate'];
4c79b5
			}
4c79b5
4c79b5
			$user_lastmark = (isset($mark_time[$forum_id])) ? $mark_time[$forum_id] : $user_lastmark;
4c79b5
4c79b5
			foreach ($topic_ids as $topic_id)
4c79b5
			{
4c79b5
				if ($global_announce_list && isset($global_announce_list[$topic_id]))
4c79b5
				{
4c79b5
					$last_read[$topic_id] = (isset($mark_time[0])) ? $mark_time[0] : $user_lastmark;
4c79b5
				}
4c79b5
				else
4c79b5
				{
4c79b5
					$last_read[$topic_id] = $user_lastmark;
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	return $last_read;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Check for read forums and update topic tracking info accordingly
4c79b5
*
4c79b5
* @param int $forum_id the forum id to check
4c79b5
* @param int $forum_last_post_time the forums last post time
4c79b5
* @param int $f_mark_time the forums last mark time if user is registered and load_db_lastread enabled
4c79b5
* @param int $mark_time_forum false if the mark time needs to be obtained, else the last users forum mark time
4c79b5
*
4c79b5
* @return true if complete forum got marked read, else false.
4c79b5
*/
4c79b5
function update_forum_tracking_info($forum_id, $forum_last_post_time, $f_mark_time = false, $mark_time_forum = false)
4c79b5
{
4c79b5
	global $db, $tracking_topics, $user, $config;
4c79b5
4c79b5
	// Determine the users last forum mark time if not given.
4c79b5
	if ($mark_time_forum === false)
4c79b5
	{
4c79b5
		if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
		{
4c79b5
			$mark_time_forum = (!empty($f_mark_time)) ? $f_mark_time : $user->data['user_lastmark'];
4c79b5
		}
4c79b5
		else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
		{
4c79b5
			$tracking_topics = (isset($_COOKIE[$config['cookie_name'] . '_track'])) ? ((STRIP) ? stripslashes($_COOKIE[$config['cookie_name'] . '_track']) : $_COOKIE[$config['cookie_name'] . '_track']) : '';
4c79b5
			$tracking_topics = ($tracking_topics) ? tracking_unserialize($tracking_topics) : array();
4c79b5
4c79b5
			if (!$user->data['is_registered'])
4c79b5
			{
4c79b5
				$user->data['user_lastmark'] = (isset($tracking_topics['l'])) ? (int) (base_convert($tracking_topics['l'], 36, 10) + $config['board_startdate']) : 0;
4c79b5
			}
4c79b5
4c79b5
			$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'];
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Check the forum for any left unread topics.
4c79b5
	// If there are none, we mark the forum as read.
4c79b5
	if ($config['load_db_lastread'] && $user->data['is_registered'])
4c79b5
	{
4c79b5
		if ($mark_time_forum >= $forum_last_post_time)
4c79b5
		{
4c79b5
			// We do not need to mark read, this happened before. Therefore setting this to true
4c79b5
			$row = true;
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$sql = 'SELECT t.forum_id FROM ' . TOPICS_TABLE . ' t
4c79b5
				LEFT JOIN ' . TOPICS_TRACK_TABLE . ' tt ON (tt.topic_id = t.topic_id AND tt.user_id = ' . $user->data['user_id'] . ')
4c79b5
				WHERE t.forum_id = ' . $forum_id . '
4c79b5
					AND t.topic_last_post_time > ' . $mark_time_forum . '
4c79b5
					AND t.topic_moved_id = 0
4c79b5
					AND (tt.topic_id IS NULL OR tt.mark_time < t.topic_last_post_time)
4c79b5
				GROUP BY t.forum_id';
4c79b5
			$result = $db->sql_query_limit($sql, 1);
4c79b5
			$row = $db->sql_fetchrow($result);
4c79b5
			$db->sql_freeresult($result);
4c79b5
		}
4c79b5
	}
4c79b5
	else if ($config['load_anon_lastread'] || $user->data['is_registered'])
4c79b5
	{
4c79b5
		// Get information from cookie
4c79b5
		$row = false;
4c79b5
4c79b5
		if (!isset($tracking_topics['tf'][$forum_id]))
4c79b5
		{
4c79b5
			// We do not need to mark read, this happened before. Therefore setting this to true
4c79b5
			$row = true;
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$sql = 'SELECT topic_id
4c79b5
				FROM ' . TOPICS_TABLE . '
4c79b5
				WHERE forum_id = ' . $forum_id . '
4c79b5
					AND topic_last_post_time > ' . $mark_time_forum . '
4c79b5
					AND topic_moved_id = 0';
4c79b5
			$result = $db->sql_query($sql);
4c79b5
4c79b5
			$check_forum = $tracking_topics['tf'][$forum_id];
4c79b5
			$unread = false;
4c79b5
4c79b5
			while ($row = $db->sql_fetchrow($result))
4c79b5
			{
4c79b5
				if (!isset($check_forum[base_convert($row['topic_id'], 10, 36)]))
4c79b5
				{
4c79b5
					$unread = true;
4c79b5
					break;
4c79b5
				}
4c79b5
			}
4c79b5
			$db->sql_freeresult($result);
4c79b5
4c79b5
			$row = $unread;
4c79b5
		}
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$row = true;
4c79b5
	}
4c79b5
4c79b5
	if (!$row)
4c79b5
	{
4c79b5
		markread('topics', $forum_id);
4c79b5
		return true;
4c79b5
	}
4c79b5
4c79b5
	return false;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Transform an array into a serialized format
4c79b5
*/
4c79b5
function tracking_serialize($input)
4c79b5
{
4c79b5
	$out = '';
4c79b5
	foreach ($input as $key => $value)
4c79b5
	{
4c79b5
		if (is_array($value))
4c79b5
		{
4c79b5
			$out .= $key . ':(' . tracking_serialize($value) . ');';
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$out .= $key . ':' . $value . ';';
4c79b5
		}
4c79b5
	}
4c79b5
	return $out;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Transform a serialized array into an actual array
4c79b5
*/
4c79b5
function tracking_unserialize($string, $max_depth = 3)
4c79b5
{
4c79b5
	$n = strlen($string);
4c79b5
	if ($n > 10010)
4c79b5
	{
4c79b5
		die('Invalid data supplied');
4c79b5
	}
4c79b5
	$data = $stack = array();
4c79b5
	$key = '';
4c79b5
	$mode = 0;
4c79b5
	$level = &$data;
4c79b5
	for ($i = 0; $i < $n; ++$i)
4c79b5
	{
4c79b5
		switch ($mode)
4c79b5
		{
4c79b5
			case 0:
4c79b5
				switch ($string[$i])
4c79b5
				{
4c79b5
					case ':':
4c79b5
						$level[$key] = 0;
4c79b5
						$mode = 1;
4c79b5
					break;
4c79b5
					case ')':
4c79b5
						unset($level);
4c79b5
						$level = array_pop($stack);
4c79b5
						$mode = 3;
4c79b5
					break;
4c79b5
					default:
4c79b5
						$key .= $string[$i];
4c79b5
				}
4c79b5
			break;
4c79b5
4c79b5
			case 1:
4c79b5
				switch ($string[$i])
4c79b5
				{
4c79b5
					case '(':
4c79b5
						if (sizeof($stack) >= $max_depth)
4c79b5
						{
4c79b5
							die('Invalid data supplied');
4c79b5
						}
4c79b5
						$stack[] = &$level;
4c79b5
						$level[$key] = array();
4c79b5
						$level = &$level[$key];
4c79b5
						$key = '';
4c79b5
						$mode = 0;
4c79b5
					break;
4c79b5
					default:
4c79b5
						$level[$key] = $string[$i];
4c79b5
						$mode = 2;
4c79b5
					break;
4c79b5
				}
4c79b5
			break;
4c79b5
4c79b5
			case 2:
4c79b5
				switch ($string[$i])
4c79b5
				{
4c79b5
					case ')':
4c79b5
						unset($level);
4c79b5
						$level = array_pop($stack);
4c79b5
						$mode = 3;
4c79b5
					break;
4c79b5
					case ';':
4c79b5
						$key = '';
4c79b5
						$mode = 0;
4c79b5
					break;
4c79b5
					default:
4c79b5
						$level[$key] .= $string[$i];
4c79b5
					break;
4c79b5
				}
4c79b5
			break;
4c79b5
4c79b5
			case 3:
4c79b5
				switch ($string[$i])
4c79b5
				{
4c79b5
					case ')':
4c79b5
						unset($level);
4c79b5
						$level = array_pop($stack);
4c79b5
					break;
4c79b5
					case ';':
4c79b5
						$key = '';
4c79b5
						$mode = 0;
4c79b5
					break;
4c79b5
					default:
4c79b5
						die('Invalid data supplied');
4c79b5
					break;
4c79b5
				}
4c79b5
			break;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if (sizeof($stack) != 0 || ($mode != 0 && $mode != 3))
4c79b5
	{
4c79b5
		die('Invalid data supplied');
4c79b5
	}
4c79b5
4c79b5
	return $level;
4c79b5
}
4c79b5
4c79b5
// Pagination functions
4c79b5
4c79b5
/**
4c79b5
* Pagination routine, generates page number sequence
4c79b5
* tpl_prefix is for using different pagination blocks at one page
4c79b5
*/
4c79b5
function generate_pagination($base_url, $num_items, $per_page, $start_item, $add_prevnext_text = false, $tpl_prefix = '')
4c79b5
{
4c79b5
	global $template, $user;
4c79b5
4c79b5
	// Make sure $per_page is a valid value
4c79b5
	$per_page = ($per_page <= 0) ? 1 : $per_page;
4c79b5
4c79b5
	$seperator = '' . $user->lang['COMMA_SEPARATOR'] . '';
4c79b5
	$total_pages = ceil($num_items / $per_page);
4c79b5
4c79b5
	if ($total_pages == 1 || !$num_items)
4c79b5
	{
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	$on_page = floor($start_item / $per_page) + 1;
4c79b5
	$url_delim = (strpos($base_url, '?') === false) ? '?' : '&';
4c79b5
4c79b5
	$page_string = ($on_page == 1) ? '1' : '1';
4c79b5
4c79b5
	if ($total_pages > 5)
4c79b5
	{
4c79b5
		$start_cnt = min(max(1, $on_page - 4), $total_pages - 5);
4c79b5
		$end_cnt = max(min($total_pages, $on_page + 4), 6);
4c79b5
4c79b5
		$page_string .= ($start_cnt > 1) ? ' ... ' : $seperator;
4c79b5
4c79b5
		for ($i = $start_cnt + 1; $i < $end_cnt; $i++)
4c79b5
		{
4c79b5
			$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
4c79b5
			if ($i < $end_cnt - 1)
4c79b5
			{
4c79b5
				$page_string .= $seperator;
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		$page_string .= ($end_cnt < $total_pages) ? ' ... ' : $seperator;
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$page_string .= $seperator;
4c79b5
4c79b5
		for ($i = 2; $i < $total_pages; $i++)
4c79b5
		{
4c79b5
			$page_string .= ($i == $on_page) ? '' . $i . '' : '' . $i . '';
4c79b5
			if ($i < $total_pages)
4c79b5
			{
4c79b5
				$page_string .= $seperator;
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$page_string .= ($on_page == $total_pages) ? '' . $total_pages . '' : '' . $total_pages . '';
4c79b5
4c79b5
	if ($add_prevnext_text)
4c79b5
	{
4c79b5
		if ($on_page != 1)
4c79b5
		{
4c79b5
			$page_string = '' . $user->lang['PREVIOUS'] . '  ' . $page_string;
4c79b5
		}
4c79b5
4c79b5
		if ($on_page != $total_pages)
4c79b5
		{
4c79b5
			$page_string .= '  ' . $user->lang['NEXT'] . '';
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		$tpl_prefix . 'BASE_URL'		=> $base_url,
4c79b5
		'A_' . $tpl_prefix . 'BASE_URL'	=> addslashes($base_url),
4c79b5
		$tpl_prefix . 'PER_PAGE'		=> $per_page,
4c79b5
4c79b5
		$tpl_prefix . 'PREVIOUS_PAGE'	=> ($on_page == 1) ? '' : $base_url . "{$url_delim}start=" . (($on_page - 2) * $per_page),
4c79b5
		$tpl_prefix . 'NEXT_PAGE'		=> ($on_page == $total_pages) ? '' : $base_url . "{$url_delim}start=" . ($on_page * $per_page),
4c79b5
		$tpl_prefix . 'TOTAL_PAGES'		=> $total_pages,
4c79b5
	));
4c79b5
4c79b5
	return $page_string;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Return current page (pagination)
4c79b5
*/
4c79b5
function on_page($num_items, $per_page, $start)
4c79b5
{
4c79b5
	global $template, $user;
4c79b5
4c79b5
	// Make sure $per_page is a valid value
4c79b5
	$per_page = ($per_page <= 0) ? 1 : $per_page;
4c79b5
4c79b5
	$on_page = floor($start / $per_page) + 1;
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'ON_PAGE'		=> $on_page)
4c79b5
	);
4c79b5
4c79b5
	return sprintf($user->lang['PAGE_OF'], $on_page, max(ceil($num_items / $per_page), 1));
4c79b5
}
4c79b5
4c79b5
// Server functions (building urls, redirecting...)
4c79b5
4c79b5
/**
4c79b5
* Append session id to url.
4c79b5
* This function supports hooks.
4c79b5
*
4c79b5
* @param string $url The url the session id needs to be appended to (can have params)
4c79b5
* @param mixed $params String or array of additional url parameters
4c79b5
* @param bool $is_amp Is url using & (true) or & (false)
4c79b5
* @param string $session_id Possibility to use a custom session id instead of the global one
4c79b5
*
4c79b5
* Examples:
4c79b5
* 
4c79b5
* append_sid("{$phpbb_root_path}viewtopic.$phpEx?t=1&f=2");
4c79b5
* append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2');
4c79b5
* append_sid("{$phpbb_root_path}viewtopic.$phpEx", 't=1&f=2', false);
4c79b5
* append_sid("{$phpbb_root_path}viewtopic.$phpEx", array('t' => 1, 'f' => 2));
4c79b5
* 
4c79b5
*
4c79b5
*/
4c79b5
function append_sid($url, $params = false, $is_amp = true, $session_id = false)
4c79b5
{
4c79b5
	global $_SID, $_EXTRA_URL, $phpbb_hook;
4c79b5
4c79b5
	// Developers using the hook function need to globalise the $_SID and $_EXTRA_URL on their own and also handle it appropiatly.
4c79b5
	// They could mimick most of what is within this function
4c79b5
	if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__, $url, $params, $is_amp, $session_id))
4c79b5
	{
4c79b5
		if ($phpbb_hook->hook_return(__FUNCTION__))
4c79b5
		{
4c79b5
			return $phpbb_hook->hook_return_result(__FUNCTION__);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$params_is_array = is_array($params);
4c79b5
4c79b5
	// Get anchor
4c79b5
	$anchor = '';
4c79b5
	if (strpos($url, '#') !== false)
4c79b5
	{
4c79b5
		list($url, $anchor) = explode('#', $url, 2);
4c79b5
		$anchor = '#' . $anchor;
4c79b5
	}
4c79b5
	else if (!$params_is_array && strpos($params, '#') !== false)
4c79b5
	{
4c79b5
		list($params, $anchor) = explode('#', $params, 2);
4c79b5
		$anchor = '#' . $anchor;
4c79b5
	}
4c79b5
4c79b5
	// Handle really simple cases quickly
4c79b5
	if ($_SID == '' && $session_id === false && empty($_EXTRA_URL) && !$params_is_array && !$anchor)
4c79b5
	{
4c79b5
		if ($params === false)
4c79b5
		{
4c79b5
			return $url;
4c79b5
		}
4c79b5
4c79b5
		$url_delim = (strpos($url, '?') === false) ? '?' : (($is_amp) ? '&' : '&';;
4c79b5
		return $url . ($params !== false ? $url_delim. $params : '');
4c79b5
	}
4c79b5
4c79b5
	// Assign sid if session id is not specified
4c79b5
	if ($session_id === false)
4c79b5
	{
4c79b5
		$session_id = $_SID;
4c79b5
	}
4c79b5
4c79b5
	$amp_delim = ($is_amp) ? '&' : '&';
4c79b5
	$url_delim = (strpos($url, '?') === false) ? '?' : $amp_delim;
4c79b5
4c79b5
	// Appending custom url parameter?
4c79b5
	$append_url = (!empty($_EXTRA_URL)) ? implode($amp_delim, $_EXTRA_URL) : '';
4c79b5
4c79b5
	// Use the short variant if possible ;)
4c79b5
	if ($params === false)
4c79b5
	{
4c79b5
		// Append session id
4c79b5
		if (!$session_id)
4c79b5
		{
4c79b5
			return $url . (($append_url) ? $url_delim . $append_url : '') . $anchor;
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . 'sid=' . $session_id . $anchor;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Build string if parameters are specified as array
4c79b5
	if (is_array($params))
4c79b5
	{
4c79b5
		$output = array();
4c79b5
4c79b5
		foreach ($params as $key => $item)
4c79b5
		{
4c79b5
			if ($item === NULL)
4c79b5
			{
4c79b5
				continue;
4c79b5
			}
4c79b5
4c79b5
			if ($key == '#')
4c79b5
			{
4c79b5
				$anchor = '#' . $item;
4c79b5
				continue;
4c79b5
			}
4c79b5
4c79b5
			$output[] = $key . '=' . $item;
4c79b5
		}
4c79b5
4c79b5
		$params = implode($amp_delim, $output);
4c79b5
	}
4c79b5
4c79b5
	// Append session id and parameters (even if they are empty)
4c79b5
	// If parameters are empty, the developer can still append his/her parameters without caring about the delimiter
4c79b5
	return $url . (($append_url) ? $url_delim . $append_url . $amp_delim : $url_delim) . $params . ((!$session_id) ? '' : $amp_delim . 'sid=' . $session_id) . $anchor;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generate board url (example: http://www.example.com/phpBB)
4c79b5
* @param bool $without_script_path if set to true the script path gets not appended (example: http://www.example.com)
4c79b5
*/
4c79b5
function generate_board_url($without_script_path = false)
4c79b5
{
4c79b5
	global $config, $user;
4c79b5
4c79b5
	$server_name = $user->host;
4c79b5
	$server_port = (!empty($_SERVER['SERVER_PORT'])) ? (int) $_SERVER['SERVER_PORT'] : (int) getenv('SERVER_PORT');
4c79b5
4c79b5
	// Forcing server vars is the only way to specify/override the protocol
4c79b5
	if ($config['force_server_vars'] || !$server_name)
4c79b5
	{
4c79b5
		$server_protocol = ($config['server_protocol']) ? $config['server_protocol'] : (($config['cookie_secure']) ? 'https://' : 'http://');
4c79b5
		$server_name = $config['server_name'];
4c79b5
		$server_port = (int) $config['server_port'];
4c79b5
		$script_path = $config['script_path'];
4c79b5
4c79b5
		$url = $server_protocol . $server_name;
4c79b5
		$cookie_secure = $config['cookie_secure'];
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		// Do not rely on cookie_secure, users seem to think that it means a secured cookie instead of an encrypted connection
4c79b5
		$cookie_secure = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 1 : 0;
4c79b5
		$url = (($cookie_secure) ? 'https://' : 'http://') . $server_name;
4c79b5
4c79b5
		$script_path = $user->page['root_script_path'];
4c79b5
	}
4c79b5
4c79b5
	if ($server_port && (($cookie_secure && $server_port <> 443) || (!$cookie_secure && $server_port <> 80)))
4c79b5
	{
4c79b5
		// HTTP HOST can carry a port number (we fetch $user->host, but for old versions this may be true)
4c79b5
		if (strpos($server_name, ':') === false)
4c79b5
		{
4c79b5
			$url .= ':' . $server_port;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if (!$without_script_path)
4c79b5
	{
4c79b5
		$url .= $script_path;
4c79b5
	}
4c79b5
4c79b5
	// Strip / from the end
4c79b5
	if (substr($url, -1, 1) == '/')
4c79b5
	{
4c79b5
		$url = substr($url, 0, -1);
4c79b5
	}
4c79b5
4c79b5
	return $url;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Redirects the user to another page then exits the script nicely
4c79b5
* This function is intended for urls within the board. It's not meant to redirect to cross-domains.
4c79b5
*
4c79b5
* @param string $url The url to redirect to
4c79b5
* @param bool $return If true, do not redirect but return the sanitized URL. Default is no return.
4c79b5
* @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.
4c79b5
*/
4c79b5
function redirect($url, $return = false, $disable_cd_check = false)
4c79b5
{
4c79b5
	global $db, $cache, $config, $user, $phpbb_root_path;
4c79b5
4c79b5
	if (empty($user->lang))
4c79b5
	{
4c79b5
		$user->add_lang('common');
4c79b5
	}
4c79b5
4c79b5
	if (!$return)
4c79b5
	{
4c79b5
		garbage_collection();
4c79b5
	}
4c79b5
4c79b5
	// Make sure no &'s are in, this will break the redirect
4c79b5
	$url = str_replace('&', '&', $url);
4c79b5
4c79b5
	// Determine which type of redirect we need to handle...
4c79b5
	$url_parts = parse_url($url);
4c79b5
4c79b5
	if ($url_parts === false)
4c79b5
	{
4c79b5
		// Malformed url, redirect to current page...
4c79b5
		$url = generate_board_url() . '/' . $user->page['page'];
4c79b5
	}
4c79b5
	else if (!empty($url_parts['scheme']) && !empty($url_parts['host']))
4c79b5
	{
4c79b5
		// Attention: only able to redirect within the same domain if $disable_cd_check is false (yourdomain.com -> www.yourdomain.com will not work)
4c79b5
		if (!$disable_cd_check && $url_parts['host'] !== $user->host)
4c79b5
		{
4c79b5
			$url = generate_board_url();
4c79b5
		}
4c79b5
	}
4c79b5
	else if ($url[0] == '/')
4c79b5
	{
4c79b5
		// Absolute uri, prepend direct url...
4c79b5
		$url = generate_board_url(true) . $url;
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		// Relative uri
4c79b5
		$pathinfo = pathinfo($url);
4c79b5
4c79b5
		// Is the uri pointing to the current directory?
4c79b5
		if ($pathinfo['dirname'] == '.')
4c79b5
		{
4c79b5
			$url = str_replace('./', '', $url);
4c79b5
4c79b5
			// Strip / from the beginning
4c79b5
			if ($url && substr($url, 0, 1) == '/')
4c79b5
			{
4c79b5
				$url = substr($url, 1);
4c79b5
			}
4c79b5
4c79b5
			if ($user->page['page_dir'])
4c79b5
			{
4c79b5
				$url = generate_board_url() . '/' . $user->page['page_dir'] . '/' . $url;
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$url = generate_board_url() . '/' . $url;
4c79b5
			}
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			// Used ./ before, but $phpbb_root_path is working better with urls within another root path
4c79b5
			$root_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($phpbb_root_path)));
4c79b5
			$page_dirs = explode('/', str_replace('\\', '/', phpbb_realpath($pathinfo['dirname'])));
4c79b5
			$intersection = array_intersect_assoc($root_dirs, $page_dirs);
4c79b5
4c79b5
			$root_dirs = array_diff_assoc($root_dirs, $intersection);
4c79b5
			$page_dirs = array_diff_assoc($page_dirs, $intersection);
4c79b5
4c79b5
			$dir = str_repeat('../', sizeof($root_dirs)) . implode('/', $page_dirs);
4c79b5
4c79b5
			// Strip / from the end
4c79b5
			if ($dir && substr($dir, -1, 1) == '/')
4c79b5
			{
4c79b5
				$dir = substr($dir, 0, -1);
4c79b5
			}
4c79b5
4c79b5
			// Strip / from the beginning
4c79b5
			if ($dir && substr($dir, 0, 1) == '/')
4c79b5
			{
4c79b5
				$dir = substr($dir, 1);
4c79b5
			}
4c79b5
4c79b5
			$url = str_replace($pathinfo['dirname'] . '/', '', $url);
4c79b5
4c79b5
			// Strip / from the beginning
4c79b5
			if (substr($url, 0, 1) == '/')
4c79b5
			{
4c79b5
				$url = substr($url, 1);
4c79b5
			}
4c79b5
4c79b5
			$url = (!empty($dir) ? $dir . '/' : '') . $url;
4c79b5
			$url = generate_board_url() . '/' . $url;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Make sure no linebreaks are there... to prevent http response splitting for PHP < 4.4.2
4c79b5
	if (strpos(urldecode($url), "\n") !== false || strpos(urldecode($url), "\r") !== false || strpos($url, ';') !== false)
4c79b5
	{
4c79b5
		trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
4c79b5
	}
4c79b5
4c79b5
	// Now, also check the protocol and for a valid url the last time...
4c79b5
	$allowed_protocols = array('http', 'https', 'ftp', 'ftps');
4c79b5
	$url_parts = parse_url($url);
4c79b5
4c79b5
	if ($url_parts === false || empty($url_parts['scheme']) || !in_array($url_parts['scheme'], $allowed_protocols))
4c79b5
	{
4c79b5
		trigger_error('Tried to redirect to potentially insecure url.', E_USER_ERROR);
4c79b5
	}
4c79b5
4c79b5
	if ($return)
4c79b5
	{
4c79b5
		return $url;
4c79b5
	}
4c79b5
4c79b5
	// Redirect via an HTML form for PITA webservers
4c79b5
	if (@preg_match('#Microsoft|WebSTAR|Xitami#', getenv('SERVER_SOFTWARE')))
4c79b5
	{
4c79b5
		header('Refresh: 0; URL=' . $url);
4c79b5
4c79b5
		echo '';
4c79b5
		echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="' . $user->lang['DIRECTION'] . '" lang="' . $user->lang['USER_LANG'] . '" xml:lang="' . $user->lang['USER_LANG'] . '">';
4c79b5
		echo '<head>';
4c79b5
		echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
4c79b5
		echo '<meta http-equiv="refresh" content="0; url=' . str_replace('&', '&', $url) . '" />';
4c79b5
		echo '<title>' . $user->lang['REDIRECT'] . '</title>';
4c79b5
		echo '</head>';
4c79b5
		echo '<body>';
4c79b5
		echo '
' . sprintf($user->lang['URL_REDIRECT'], '', '') . '
';
4c79b5
		echo '</body>';
4c79b5
		echo '</html>';
4c79b5
4c79b5
		exit;
4c79b5
	}
4c79b5
4c79b5
	// Behave as per HTTP/1.1 spec for others
4c79b5
	header('Location: ' . $url);
4c79b5
	exit;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Re-Apply session id after page reloads
4c79b5
*/
4c79b5
function reapply_sid($url)
4c79b5
{
4c79b5
	global $phpEx, $phpbb_root_path;
4c79b5
4c79b5
	if ($url === "index.$phpEx")
4c79b5
	{
4c79b5
		return append_sid("index.$phpEx");
4c79b5
	}
4c79b5
	else if ($url === "{$phpbb_root_path}index.$phpEx")
4c79b5
	{
4c79b5
		return append_sid("{$phpbb_root_path}index.$phpEx");
4c79b5
	}
4c79b5
4c79b5
	// Remove previously added sid
4c79b5
	if (strpos($url, '?sid=') !== false)
4c79b5
	{
4c79b5
		$url = preg_replace('/(\?)sid=[a-z0-9]+(&|&)?/', '\1', $url);
4c79b5
	}
4c79b5
	else if (strpos($url, '&sid=') !== false)
4c79b5
	{
4c79b5
		$url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
4c79b5
	}
4c79b5
	else if (strpos($url, '&sid=') !== false)
4c79b5
	{
4c79b5
		$url = preg_replace('/&sid=[a-z0-9]+(&)?/', '\1', $url);
4c79b5
	}
4c79b5
4c79b5
	return append_sid($url);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Returns url from the session/current page with an re-appended SID with optionally stripping vars from the url
4c79b5
*/
4c79b5
function build_url($strip_vars = false)
4c79b5
{
4c79b5
	global $user, $phpbb_root_path;
4c79b5
4c79b5
	// Append SID
4c79b5
	$redirect = append_sid($user->page['page'], false, false);
4c79b5
4c79b5
	// Add delimiter if not there...
4c79b5
	if (strpos($redirect, '?') === false)
4c79b5
	{
4c79b5
		$redirect .= '?';
4c79b5
	}
4c79b5
4c79b5
	// Strip vars...
4c79b5
	if ($strip_vars !== false && strpos($redirect, '?') !== false)
4c79b5
	{
4c79b5
		if (!is_array($strip_vars))
4c79b5
		{
4c79b5
			$strip_vars = array($strip_vars);
4c79b5
		}
4c79b5
4c79b5
		$query = $_query = array();
4c79b5
4c79b5
		$args = substr($redirect, strpos($redirect, '?') + 1);
4c79b5
		$args = ($args) ? explode('&', $args) : array();
4c79b5
		$redirect = substr($redirect, 0, strpos($redirect, '?'));
4c79b5
4c79b5
		foreach ($args as $argument)
4c79b5
		{
4c79b5
			$arguments = explode('=', $argument);
4c79b5
			$key = $arguments[0];
4c79b5
			unset($arguments[0]);
4c79b5
4c79b5
			$query[$key] = implode('=', $arguments);
4c79b5
		}
4c79b5
4c79b5
		// Strip the vars off
4c79b5
		foreach ($strip_vars as $strip)
4c79b5
		{
4c79b5
			if (isset($query[$strip]))
4c79b5
			{
4c79b5
				unset($query[$strip]);
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		// Glue the remaining parts together... already urlencoded
4c79b5
		foreach ($query as $key => $value)
4c79b5
		{
4c79b5
			$_query[] = $key . '=' . $value;
4c79b5
		}
4c79b5
		$query = implode('&', $_query);
4c79b5
4c79b5
		$redirect .= ($query) ? '?' . $query : '';
4c79b5
	}
4c79b5
4c79b5
	return $phpbb_root_path . str_replace('&', '&', $redirect);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Meta refresh assignment
4c79b5
* Adds META template variable with meta http tag.
4c79b5
*
4c79b5
* @param int $time Time in seconds for meta refresh tag
4c79b5
* @param string $url URL to redirect to. The url will go through redirect() first before the template variable is assigned
4c79b5
* @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.
4c79b5
*/
4c79b5
function meta_refresh($time, $url, $disable_cd_check = false)
4c79b5
{
4c79b5
	global $template;
4c79b5
4c79b5
	$url = redirect($url, true, $disable_cd_check);
4c79b5
	$url = str_replace('&', '&', $url);
4c79b5
4c79b5
	// For XHTML compatibility we change back & to &
4c79b5
	$template->assign_vars(array(
4c79b5
		'META' => '<meta http-equiv="refresh" content="' . $time . ';url=' . $url . '" />')
4c79b5
	);
4c79b5
4c79b5
	return $url;
4c79b5
}
4c79b5
4c79b5
//Form validation
4c79b5
4c79b5
4c79b5
/**
4c79b5
* Add a secret hash   for use in links/GET requests
4c79b5
* @param string  $link_name The name of the link; has to match the name used in check_link_hash, otherwise no restrictions apply
4c79b5
* @return string the hash
4c79b5
4c79b5
*/
4c79b5
function generate_link_hash($link_name)
4c79b5
{
4c79b5
	global $user;
4c79b5
4c79b5
	if (!isset($user->data["hash_$link_name"]))
4c79b5
	{
4c79b5
		$user->data["hash_$link_name"] = substr(sha1($user->data['user_form_salt'] . $link_name), 0, 8);
4c79b5
	}
4c79b5
4c79b5
	return $user->data["hash_$link_name"];
4c79b5
}
4c79b5
4c79b5
4c79b5
/**
4c79b5
* checks a link hash - for GET requests
4c79b5
* @param string $token the submitted token
4c79b5
* @param string $link_name The name of the link
4c79b5
* @return boolean true if all is fine
4c79b5
*/
4c79b5
function check_link_hash($token, $link_name)
4c79b5
{
4c79b5
	return $token === generate_link_hash($link_name);
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Add a secret token to the form (requires the S_FORM_TOKEN template variable)
4c79b5
* @param string  $form_name The name of the form; has to match the name used in check_form_key, otherwise no restrictions apply
4c79b5
*/
4c79b5
function add_form_key($form_name)
4c79b5
{
4c79b5
	global $config, $template, $user;
4c79b5
4c79b5
	$now = time();
4c79b5
	$token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
4c79b5
	$token = sha1($now . $user->data['user_form_salt'] . $form_name . $token_sid);
4c79b5
4c79b5
	$s_fields = build_hidden_fields(array(
4c79b5
		'creation_time' => $now,
4c79b5
		'form_token'	=> $token,
4c79b5
	));
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'S_FORM_TOKEN'	=> $s_fields,
4c79b5
	));
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Check the form key. Required for all altering actions not secured by confirm_box
4c79b5
* @param string  $form_name The name of the form; has to match the name used in add_form_key, otherwise no restrictions apply
4c79b5
* @param int $timespan The maximum acceptable age for a submitted form in seconds. Defaults to the config setting.
4c79b5
* @param string $return_page The address for the return link
4c79b5
* @param bool $trigger If true, the function will triger an error when encountering an invalid form
4c79b5
*/
4c79b5
function check_form_key($form_name, $timespan = false, $return_page = '', $trigger = false)
4c79b5
{
4c79b5
	global $config, $user;
4c79b5
4c79b5
	if ($timespan === false)
4c79b5
	{
4c79b5
		// we enforce a minimum value of half a minute here.
4c79b5
		$timespan = ($config['form_token_lifetime'] == -1) ? -1 : max(30, $config['form_token_lifetime']);
4c79b5
	}
4c79b5
4c79b5
	if (isset($_POST['creation_time']) && isset($_POST['form_token']))
4c79b5
	{
4c79b5
		$creation_time	= abs(request_var('creation_time', 0));
4c79b5
		$token = request_var('form_token', '');
4c79b5
4c79b5
		$diff = time() - $creation_time;
4c79b5
4c79b5
		// If creation_time and the time() now is zero we can assume it was not a human doing this (the check for if ($diff)...
4c79b5
		if ($diff && ($diff <= $timespan || $timespan === -1))
4c79b5
		{
4c79b5
			$token_sid = ($user->data['user_id'] == ANONYMOUS && !empty($config['form_token_sid_guests'])) ? $user->session_id : '';
4c79b5
			$key = sha1($creation_time . $user->data['user_form_salt'] . $form_name . $token_sid);
4c79b5
4c79b5
			if ($key === $token)
4c79b5
			{
4c79b5
				return true;
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if ($trigger)
4c79b5
	{
4c79b5
		trigger_error($user->lang['FORM_INVALID'] . $return_page);
4c79b5
	}
4c79b5
4c79b5
	return false;
4c79b5
}
4c79b5
4c79b5
// Message/Login boxes
4c79b5
4c79b5
/**
4c79b5
* Build Confirm box
4c79b5
* @param boolean $check True for checking if confirmed (without any additional parameters) and false for displaying the confirm box
4c79b5
* @param string $title Title/Message used for confirm box.
4c79b5
*		message text is _CONFIRM appended to title.
4c79b5
*		If title cannot be found in user->lang a default one is displayed
4c79b5
*		If title_CONFIRM cannot be found in user->lang the text given is used.
4c79b5
* @param string $hidden Hidden variables
4c79b5
* @param string $html_body Template used for confirm box
4c79b5
* @param string $u_action Custom form action
4c79b5
*/
4c79b5
function confirm_box($check, $title = '', $hidden = '', $html_body = 'confirm_body.html', $u_action = '')
4c79b5
{
4c79b5
	global $user, $template, $db;
4c79b5
	global $phpEx, $phpbb_root_path;
4c79b5
4c79b5
	if (isset($_POST['cancel']))
4c79b5
	{
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	$confirm = false;
4c79b5
	if (isset($_POST['confirm']))
4c79b5
	{
4c79b5
		// language frontier
4c79b5
		if ($_POST['confirm'] === $user->lang['YES'])
4c79b5
		{
4c79b5
			$confirm = true;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if ($check && $confirm)
4c79b5
	{
4c79b5
		$user_id = request_var('user_id', 0);
4c79b5
		$session_id = request_var('sess', '');
4c79b5
		$confirm_key = request_var('confirm_key', '');
4c79b5
4c79b5
		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'])
4c79b5
		{
4c79b5
			return false;
4c79b5
		}
4c79b5
4c79b5
		// Reset user_last_confirm_key
4c79b5
		$sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = ''
4c79b5
			WHERE user_id = " . $user->data['user_id'];
4c79b5
		$db->sql_query($sql);
4c79b5
4c79b5
		return true;
4c79b5
	}
4c79b5
	else if ($check)
4c79b5
	{
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	$s_hidden_fields = build_hidden_fields(array(
4c79b5
		'user_id'	=> $user->data['user_id'],
4c79b5
		'sess'		=> $user->session_id,
4c79b5
		'sid'		=> $user->session_id)
4c79b5
	);
4c79b5
4c79b5
	// generate activation key
4c79b5
	$confirm_key = gen_rand_string(10);
4c79b5
4c79b5
	if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
4c79b5
	{
4c79b5
		adm_page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		page_header((!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title]);
4c79b5
	}
4c79b5
4c79b5
	$template->set_filenames(array(
4c79b5
		'body' => $html_body)
4c79b5
	);
4c79b5
4c79b5
	// If activation key already exist, we better do not re-use the key (something very strange is going on...)
4c79b5
	if (request_var('confirm_key', ''))
4c79b5
	{
4c79b5
		// This should not occur, therefore we cancel the operation to safe the user
4c79b5
		return false;
4c79b5
	}
4c79b5
4c79b5
	// re-add sid / transform & to & for user->page (user->page is always using &)
4c79b5
	$use_page = ($u_action) ? $phpbb_root_path . $u_action : $phpbb_root_path . str_replace('&', '&', $user->page['page']);
4c79b5
	$u_action = reapply_sid($use_page);
4c79b5
	$u_action .= ((strpos($u_action, '?') === false) ? '?' : '&') . 'confirm_key=' . $confirm_key;
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'MESSAGE_TITLE'		=> (!isset($user->lang[$title])) ? $user->lang['CONFIRM'] : $user->lang[$title],
4c79b5
		'MESSAGE_TEXT'		=> (!isset($user->lang[$title . '_CONFIRM'])) ? $title : $user->lang[$title . '_CONFIRM'],
4c79b5
4c79b5
		'YES_VALUE'			=> $user->lang['YES'],
4c79b5
		'S_CONFIRM_ACTION'	=> $u_action,
4c79b5
		'S_HIDDEN_FIELDS'	=> $hidden . $s_hidden_fields)
4c79b5
	);
4c79b5
4c79b5
	$sql = 'UPDATE ' . USERS_TABLE . " SET user_last_confirm_key = '" . $db->sql_escape($confirm_key) . "'
4c79b5
		WHERE user_id = " . $user->data['user_id'];
4c79b5
	$db->sql_query($sql);
4c79b5
4c79b5
	if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
4c79b5
	{
4c79b5
		adm_page_footer();
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		page_footer();
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generate login box or verify password
4c79b5
*/
4c79b5
function login_box($redirect = '', $l_explain = '', $l_success = '', $admin = false, $s_display = true)
4c79b5
{
4c79b5
	global $db, $user, $template, $auth, $phpEx, $phpbb_root_path, $config;
4c79b5
4c79b5
	$err = '';
4c79b5
4c79b5
	// Make sure user->setup() has been called
4c79b5
	if (empty($user->lang))
4c79b5
	{
4c79b5
		$user->setup();
4c79b5
	}
4c79b5
4c79b5
	// Print out error if user tries to authenticate as an administrator without having the privileges...
4c79b5
	if ($admin && !$auth->acl_get('a_'))
4c79b5
	{
4c79b5
		// Not authd
4c79b5
		// anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
4c79b5
		if ($user->data['is_registered'])
4c79b5
		{
4c79b5
			add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
4c79b5
		}
4c79b5
		trigger_error('NO_AUTH_ADMIN');
4c79b5
	}
4c79b5
4c79b5
	if (isset($_POST['login']))
4c79b5
	{
4c79b5
		// Get credential
4c79b5
		if ($admin)
4c79b5
		{
4c79b5
			$credential = request_var('credential', '');
4c79b5
4c79b5
			if (strspn($credential, 'abcdef0123456789') !== strlen($credential) || strlen($credential) != 32)
4c79b5
			{
4c79b5
				if ($user->data['is_registered'])
4c79b5
				{
4c79b5
					add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
4c79b5
				}
4c79b5
				trigger_error('NO_AUTH_ADMIN');
4c79b5
			}
4c79b5
4c79b5
			$password	= request_var('password_' . $credential, '', true);
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$password	= request_var('password', '', true);
4c79b5
		}
4c79b5
4c79b5
		$username	= request_var('username', '', true);
4c79b5
		$autologin	= (!empty($_POST['autologin'])) ? true : false;
4c79b5
		$viewonline = (!empty($_POST['viewonline'])) ? 0 : 1;
4c79b5
		$admin 		= ($admin) ? 1 : 0;
4c79b5
		$viewonline = ($admin) ? $user->data['session_viewonline'] : $viewonline;
4c79b5
4c79b5
		// Check if the supplied username is equal to the one stored within the database if re-authenticating
4c79b5
		if ($admin && utf8_clean_string($username) != utf8_clean_string($user->data['username']))
4c79b5
		{
4c79b5
			// We log the attempt to use a different username...
4c79b5
			add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
4c79b5
			trigger_error('NO_AUTH_ADMIN_USER_DIFFER');
4c79b5
		}
4c79b5
4c79b5
		// If authentication is successful we redirect user to previous page
4c79b5
		$result = $auth->login($username, $password, $autologin, $viewonline, $admin);
4c79b5
4c79b5
		// If admin authentication and login, we will log if it was a success or not...
4c79b5
		// We also break the operation on the first non-success login - it could be argued that the user already knows
4c79b5
		if ($admin)
4c79b5
		{
4c79b5
			if ($result['status'] == LOGIN_SUCCESS)
4c79b5
			{
4c79b5
				add_log('admin', 'LOG_ADMIN_AUTH_SUCCESS');
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				// Only log the failed attempt if a real user tried to.
4c79b5
				// anonymous/inactive users are never able to go to the ACP even if they have the relevant permissions
4c79b5
				if ($user->data['is_registered'])
4c79b5
				{
4c79b5
					add_log('admin', 'LOG_ADMIN_AUTH_FAIL');
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		// The result parameter is always an array, holding the relevant information...
4c79b5
		if ($result['status'] == LOGIN_SUCCESS)
4c79b5
		{
4c79b5
			$redirect = request_var('redirect', "{$phpbb_root_path}index.$phpEx");
4c79b5
			$message = ($l_success) ? $l_success : $user->lang['LOGIN_REDIRECT'];
4c79b5
			$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']);
4c79b5
4c79b5
			// append/replace SID (may change during the session for AOL users)
4c79b5
			$redirect = reapply_sid($redirect);
4c79b5
4c79b5
			// Special case... the user is effectively banned, but we allow founders to login
4c79b5
			if (defined('IN_CHECK_BAN') && $result['user_row']['user_type'] != USER_FOUNDER)
4c79b5
			{
4c79b5
				return;
4c79b5
			}
4c79b5
4c79b5
			$redirect = meta_refresh(3, $redirect);
4c79b5
			trigger_error($message . '

' . sprintf($l_redirect, '', ''));
4c79b5
		}
4c79b5
4c79b5
		// Something failed, determine what...
4c79b5
		if ($result['status'] == LOGIN_BREAK)
4c79b5
		{
4c79b5
			trigger_error($result['error_msg']);
4c79b5
		}
4c79b5
4c79b5
		// Special cases... determine
4c79b5
		switch ($result['status'])
4c79b5
		{
4c79b5
			case LOGIN_ERROR_ATTEMPTS:
4c79b5
4c79b5
				// Show confirm image
4c79b5
				$sql = 'DELETE FROM ' . CONFIRM_TABLE . "
4c79b5
					WHERE session_id = '" . $db->sql_escape($user->session_id) . "'
4c79b5
						AND confirm_type = " . CONFIRM_LOGIN;
4c79b5
				$db->sql_query($sql);
4c79b5
4c79b5
				// Generate code
4c79b5
				$code = gen_rand_string(mt_rand(5, 8));
4c79b5
				$confirm_id = md5(unique_id($user->ip));
4c79b5
				$seed = hexdec(substr(unique_id(), 4, 10));
4c79b5
4c79b5
				// compute $seed % 0x7fffffff
4c79b5
				$seed -= 0x7fffffff * floor($seed / 0x7fffffff);
4c79b5
4c79b5
				$sql = 'INSERT INTO ' . CONFIRM_TABLE . ' ' . $db->sql_build_array('INSERT', array(
4c79b5
					'confirm_id'	=> (string) $confirm_id,
4c79b5
					'session_id'	=> (string) $user->session_id,
4c79b5
					'confirm_type'	=> (int) CONFIRM_LOGIN,
4c79b5
					'code'			=> (string) $code,
4c79b5
					'seed'			=> (int) $seed)
4c79b5
				);
4c79b5
				$db->sql_query($sql);
4c79b5
4c79b5
				$template->assign_vars(array(
4c79b5
					'S_CONFIRM_CODE'			=> true,
4c79b5
					'CONFIRM_ID'				=> $confirm_id,
4c79b5
					'CONFIRM_IMAGE'				=> '',
4c79b5
					'L_LOGIN_CONFIRM_EXPLAIN'	=> sprintf($user->lang['LOGIN_CONFIRM_EXPLAIN'], '', ''),
4c79b5
				));
4c79b5
4c79b5
				$err = $user->lang[$result['error_msg']];
4c79b5
4c79b5
			break;
4c79b5
4c79b5
			case LOGIN_ERROR_PASSWORD_CONVERT:
4c79b5
				$err = sprintf(
4c79b5
					$user->lang[$result['error_msg']],
4c79b5
					($config['email_enable']) ? '' : '',
4c79b5
					($config['email_enable']) ? '' : '',
4c79b5
					($config['board_contact']) ? '' : '',
4c79b5
					($config['board_contact']) ? '' : ''
4c79b5
				);
4c79b5
			break;
4c79b5
4c79b5
			// Username, password, etc...
4c79b5
			default:
4c79b5
				$err = $user->lang[$result['error_msg']];
4c79b5
4c79b5
				// Assign admin contact to some error messages
4c79b5
				if ($result['error_msg'] == 'LOGIN_ERROR_USERNAME' || $result['error_msg'] == 'LOGIN_ERROR_PASSWORD')
4c79b5
				{
4c79b5
					$err = (!$config['board_contact']) ? sprintf($user->lang[$result['error_msg']], '', '') : sprintf($user->lang[$result['error_msg']], '', '');
4c79b5
				}
4c79b5
4c79b5
			break;
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	if (!$redirect)
4c79b5
	{
4c79b5
		// We just use what the session code determined...
4c79b5
		// If we are not within the admin directory we use the page dir...
4c79b5
		$redirect = '';
4c79b5
4c79b5
		if (!$admin)
4c79b5
		{
4c79b5
			$redirect .= ($user->page['page_dir']) ? $user->page['page_dir'] . '/' : '';
4c79b5
		}
4c79b5
4c79b5
		$redirect .= $user->page['page_name'] . (($user->page['query_string']) ? '?' . htmlspecialchars($user->page['query_string']) : '');
4c79b5
	}
4c79b5
4c79b5
	// Assign credential for username/password pair
4c79b5
	$credential = ($admin) ? md5(unique_id()) : false;
4c79b5
4c79b5
	$s_hidden_fields = array(
4c79b5
		'redirect'	=> $redirect,
4c79b5
		'sid'		=> $user->session_id,
4c79b5
	);
4c79b5
4c79b5
	if ($admin)
4c79b5
	{
4c79b5
		$s_hidden_fields['credential'] = $credential;
4c79b5
	}
4c79b5
4c79b5
	$s_hidden_fields = build_hidden_fields($s_hidden_fields);
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'LOGIN_ERROR'		=> $err,
4c79b5
		'LOGIN_EXPLAIN'		=> $l_explain,
4c79b5
4c79b5
		'U_SEND_PASSWORD' 		=> ($config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=sendpassword') : '',
4c79b5
		'U_RESEND_ACTIVATION'	=> ($config['require_activation'] != USER_ACTIVATION_NONE && $config['email_enable']) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=resend_act') : '',
4c79b5
		'U_TERMS_USE'			=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=terms'),
4c79b5
		'U_PRIVACY'				=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=privacy'),
4c79b5
4c79b5
		'S_DISPLAY_FULL_LOGIN'	=> ($s_display) ? true : false,
4c79b5
		'S_LOGIN_ACTION'		=> (!$admin) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login') : append_sid("index.$phpEx", false, true, $user->session_id), // Needs to stay index.$phpEx because we are within the admin directory
4c79b5
		'S_HIDDEN_FIELDS' 		=> $s_hidden_fields,
4c79b5
4c79b5
		'S_ADMIN_AUTH'			=> $admin,
4c79b5
		'USERNAME'				=> ($admin) ? $user->data['username'] : '',
4c79b5
4c79b5
		'USERNAME_CREDENTIAL'	=> 'username',
4c79b5
		'PASSWORD_CREDENTIAL'	=> ($admin) ? 'password_' . $credential : 'password',
4c79b5
	));
4c79b5
4c79b5
	page_header($user->lang['LOGIN'], false);
4c79b5
4c79b5
	$template->set_filenames(array(
4c79b5
		'body' => 'login_body.html')
4c79b5
	);
4c79b5
	make_jumpbox(append_sid("{$phpbb_root_path}viewforum.$phpEx"));
4c79b5
4c79b5
	page_footer();
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generate forum login box
4c79b5
*/
4c79b5
function login_forum_box($forum_data)
4c79b5
{
4c79b5
	global $db, $config, $user, $template, $phpEx;
4c79b5
4c79b5
	$password = request_var('password', '', true);
4c79b5
4c79b5
	$sql = 'SELECT forum_id
4c79b5
		FROM ' . FORUMS_ACCESS_TABLE . '
4c79b5
		WHERE forum_id = ' . $forum_data['forum_id'] . '
4c79b5
			AND user_id = ' . $user->data['user_id'] . "
4c79b5
			AND session_id = '" . $db->sql_escape($user->session_id) . "'";
4c79b5
	$result = $db->sql_query($sql);
4c79b5
	$row = $db->sql_fetchrow($result);
4c79b5
	$db->sql_freeresult($result);
4c79b5
4c79b5
	if ($row)
4c79b5
	{
4c79b5
		return true;
4c79b5
	}
4c79b5
4c79b5
	if ($password)
4c79b5
	{
4c79b5
		// Remove expired authorised sessions
4c79b5
		$sql = 'SELECT f.session_id
4c79b5
			FROM ' . FORUMS_ACCESS_TABLE . ' f
4c79b5
			LEFT JOIN ' . SESSIONS_TABLE . ' s ON (f.session_id = s.session_id)
4c79b5
			WHERE s.session_id IS NULL';
4c79b5
		$result = $db->sql_query($sql);
4c79b5
4c79b5
		if ($row = $db->sql_fetchrow($result))
4c79b5
		{
4c79b5
			$sql_in = array();
4c79b5
			do
4c79b5
			{
4c79b5
				$sql_in[] = (string) $row['session_id'];
4c79b5
			}
4c79b5
			while ($row = $db->sql_fetchrow($result));
4c79b5
4c79b5
			// Remove expired sessions
4c79b5
			$sql = 'DELETE FROM ' . FORUMS_ACCESS_TABLE . '
4c79b5
				WHERE ' . $db->sql_in_set('session_id', $sql_in);
4c79b5
			$db->sql_query($sql);
4c79b5
		}
4c79b5
		$db->sql_freeresult($result);
4c79b5
4c79b5
		if (phpbb_check_hash($password, $forum_data['forum_password']))
4c79b5
		{
4c79b5
			$sql_ary = array(
4c79b5
				'forum_id'		=> (int) $forum_data['forum_id'],
4c79b5
				'user_id'		=> (int) $user->data['user_id'],
4c79b5
				'session_id'	=> (string) $user->session_id,
4c79b5
			);
4c79b5
4c79b5
			$db->sql_query('INSERT INTO ' . FORUMS_ACCESS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
4c79b5
4c79b5
			return true;
4c79b5
		}
4c79b5
4c79b5
		$template->assign_var('LOGIN_ERROR', $user->lang['WRONG_PASSWORD']);
4c79b5
	}
4c79b5
4c79b5
	page_header($user->lang['LOGIN']);
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'S_HIDDEN_FIELDS'		=> build_hidden_fields(array('f' => $forum_data['forum_id'])))
4c79b5
	);
4c79b5
4c79b5
	$template->set_filenames(array(
4c79b5
		'body' => 'login_forum.html')
4c79b5
	);
4c79b5
4c79b5
	page_footer();
4c79b5
}
4c79b5
4c79b5
// Little helpers
4c79b5
4c79b5
/**
4c79b5
* Little helper for the build_hidden_fields function
4c79b5
*/
4c79b5
function _build_hidden_fields($key, $value, $specialchar, $stripslashes)
4c79b5
{
4c79b5
	$hidden_fields = '';
4c79b5
4c79b5
	if (!is_array($value))
4c79b5
	{
4c79b5
		$value = ($stripslashes) ? stripslashes($value) : $value;
4c79b5
		$value = ($specialchar) ? htmlspecialchars($value, ENT_COMPAT, 'UTF-8') : $value;
4c79b5
4c79b5
		$hidden_fields .= '<input type="hidden" name="' . $key . '" value="' . $value . '" />' . "\n";
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		foreach ($value as $_key => $_value)
4c79b5
		{
4c79b5
			$_key = ($stripslashes) ? stripslashes($_key) : $_key;
4c79b5
			$_key = ($specialchar) ? htmlspecialchars($_key, ENT_COMPAT, 'UTF-8') : $_key;
4c79b5
4c79b5
			$hidden_fields .= _build_hidden_fields($key . '[' . $_key . ']', $_value, $specialchar, $stripslashes);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	return $hidden_fields;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Build simple hidden fields from array
4c79b5
*
4c79b5
* @param array $field_ary an array of values to build the hidden field from
4c79b5
* @param bool $specialchar if true, keys and values get specialchared
4c79b5
* @param bool $stripslashes if true, keys and values get stripslashed
4c79b5
*
4c79b5
* @return string the hidden fields
4c79b5
*/
4c79b5
function build_hidden_fields($field_ary, $specialchar = false, $stripslashes = false)
4c79b5
{
4c79b5
	$s_hidden_fields = '';
4c79b5
4c79b5
	foreach ($field_ary as $name => $vars)
4c79b5
	{
4c79b5
		$name = ($stripslashes) ? stripslashes($name) : $name;
4c79b5
		$name = ($specialchar) ? htmlspecialchars($name, ENT_COMPAT, 'UTF-8') : $name;
4c79b5
4c79b5
		$s_hidden_fields .= _build_hidden_fields($name, $vars, $specialchar, $stripslashes);
4c79b5
	}
4c79b5
4c79b5
	return $s_hidden_fields;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Parse cfg file
4c79b5
*/
4c79b5
function parse_cfg_file($filename, $lines = false)
4c79b5
{
4c79b5
	$parsed_items = array();
4c79b5
4c79b5
	if ($lines === false)
4c79b5
	{
4c79b5
		$lines = file($filename);
4c79b5
	}
4c79b5
4c79b5
	foreach ($lines as $line)
4c79b5
	{
4c79b5
		$line = trim($line);
4c79b5
4c79b5
		if (!$line || $line[0] == '#' || ($delim_pos = strpos($line, '=')) === false)
4c79b5
		{
4c79b5
			continue;
4c79b5
		}
4c79b5
4c79b5
		// Determine first occurrence, since in values the equal sign is allowed
4c79b5
		$key = strtolower(trim(substr($line, 0, $delim_pos)));
4c79b5
		$value = trim(substr($line, $delim_pos + 1));
4c79b5
4c79b5
		if (in_array($value, array('off', 'false', '0')))
4c79b5
		{
4c79b5
			$value = false;
4c79b5
		}
4c79b5
		else if (in_array($value, array('on', 'true', '1')))
4c79b5
		{
4c79b5
			$value = true;
4c79b5
		}
4c79b5
		else if (!trim($value))
4c79b5
		{
4c79b5
			$value = '';
4c79b5
		}
4c79b5
		else if (($value[0] == "'" && $value[sizeof($value) - 1] == "'") || ($value[0] == '"' && $value[sizeof($value) - 1] == '"'))
4c79b5
		{
4c79b5
			$value = substr($value, 1, sizeof($value)-2);
4c79b5
		}
4c79b5
4c79b5
		$parsed_items[$key] = $value;
4c79b5
	}
4c79b5
4c79b5
	return $parsed_items;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Add log event
4c79b5
*/
4c79b5
function add_log()
4c79b5
{
4c79b5
	global $db, $user;
4c79b5
4c79b5
	$args = func_get_args();
4c79b5
4c79b5
	$mode			= array_shift($args);
4c79b5
	$reportee_id	= ($mode == 'user') ? intval(array_shift($args)) : '';
4c79b5
	$forum_id		= ($mode == 'mod') ? intval(array_shift($args)) : '';
4c79b5
	$topic_id		= ($mode == 'mod') ? intval(array_shift($args)) : '';
4c79b5
	$action			= array_shift($args);
4c79b5
	$data			= (!sizeof($args)) ? '' : serialize($args);
4c79b5
4c79b5
	$sql_ary = array(
4c79b5
		'user_id'		=> (empty($user->data)) ? ANONYMOUS : $user->data['user_id'],
4c79b5
		'log_ip'		=> $user->ip,
4c79b5
		'log_time'		=> time(),
4c79b5
		'log_operation'	=> $action,
4c79b5
		'log_data'		=> $data,
4c79b5
	);
4c79b5
4c79b5
	switch ($mode)
4c79b5
	{
4c79b5
		case 'admin':
4c79b5
			$sql_ary['log_type'] = LOG_ADMIN;
4c79b5
		break;
4c79b5
4c79b5
		case 'mod':
4c79b5
			$sql_ary += array(
4c79b5
				'log_type'	=> LOG_MOD,
4c79b5
				'forum_id'	=> $forum_id,
4c79b5
				'topic_id'	=> $topic_id
4c79b5
			);
4c79b5
		break;
4c79b5
4c79b5
		case 'user':
4c79b5
			$sql_ary += array(
4c79b5
				'log_type'		=> LOG_USERS,
4c79b5
				'reportee_id'	=> $reportee_id
4c79b5
			);
4c79b5
		break;
4c79b5
4c79b5
		case 'critical':
4c79b5
			$sql_ary['log_type'] = LOG_CRITICAL;
4c79b5
		break;
4c79b5
4c79b5
		default:
4c79b5
			return false;
4c79b5
	}
4c79b5
4c79b5
	$db->sql_query('INSERT INTO ' . LOG_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary));
4c79b5
4c79b5
	return $db->sql_nextid();
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Return a nicely formatted backtrace (parts from the php manual by diz at ysagoon dot com)
4c79b5
*/
4c79b5
function get_backtrace()
4c79b5
{
4c79b5
	global $phpbb_root_path;
4c79b5
4c79b5
	$output = '
';
4c79b5
	$backtrace = debug_backtrace();
4c79b5
	$path = phpbb_realpath($phpbb_root_path);
4c79b5
4c79b5
	foreach ($backtrace as $number => $trace)
4c79b5
	{
4c79b5
		// We skip the first one, because it only shows this file/function
4c79b5
		if ($number == 0)
4c79b5
		{
4c79b5
			continue;
4c79b5
		}
4c79b5
4c79b5
		// Strip the current directory from path
4c79b5
		if (empty($trace['file']))
4c79b5
		{
4c79b5
			$trace['file'] = '';
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$trace['file'] = str_replace(array($path, '\\'), array('', '/'), $trace['file']);
4c79b5
			$trace['file'] = substr($trace['file'], 1);
4c79b5
		}
4c79b5
		$args = array();
4c79b5
4c79b5
		// If include/require/include_once is not called, do not show arguments - they may contain sensible information
4c79b5
		if (!in_array($trace['function'], array('include', 'require', 'include_once')))
4c79b5
		{
4c79b5
			unset($trace['args']);
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			// Path...
4c79b5
			if (!empty($trace['args'][0]))
4c79b5
			{
4c79b5
				$argument = htmlspecialchars($trace['args'][0]);
4c79b5
				$argument = str_replace(array($path, '\\'), array('', '/'), $argument);
4c79b5
				$argument = substr($argument, 1);
4c79b5
				$args[] = "'{$argument}'";
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		$trace['class'] = (!isset($trace['class'])) ? '' : $trace['class'];
4c79b5
		$trace['type'] = (!isset($trace['type'])) ? '' : $trace['type'];
4c79b5
4c79b5
		$output .= '
';
4c79b5
		$output .= 'FILE: ' . htmlspecialchars($trace['file']) . '
';
4c79b5
		$output .= 'LINE: ' . ((!empty($trace['line'])) ? $trace['line'] : '') . '
';
4c79b5
4c79b5
		$output .= 'CALL: ' . htmlspecialchars($trace['class'] . $trace['type'] . $trace['function']) . '(' . ((sizeof($args)) ? implode(', ', $args) : '') . ')
';
4c79b5
	}
4c79b5
	$output .= '';
4c79b5
	return $output;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* This function returns a regular expression pattern for commonly used expressions
4c79b5
* Use with / as delimiter for email mode and # for url modes
4c79b5
* mode can be: email|bbcode_htm|url|url_inline|www_url|www_url_inline|relative_url|relative_url_inline|ipv4|ipv6
4c79b5
*/
4c79b5
function get_preg_expression($mode)
4c79b5
{
4c79b5
	switch ($mode)
4c79b5
	{
4c79b5
		case 'email':
4c79b5
			return '(?:[a-z0-9\'\.\-_\+\|]++|&)+@[a-z0-9\-]+\.(?:[a-z0-9\-]+\.)*[a-z]+';
4c79b5
		break;
4c79b5
4c79b5
		case 'bbcode_htm':
4c79b5
			return array(
4c79b5
				'#.*?#',
4c79b5
				'#.*?#',
4c79b5
				'#.*?#',
4c79b5
				'#
4c79b5
				'##s',
4c79b5
				'#<.*?>#s',
4c79b5
			);
4c79b5
		break;
4c79b5
4c79b5
		// Whoa these look impressive!
4c79b5
		// The code to generate the following two regular expressions which match valid IPv4/IPv6 addresses
4c79b5
		// can be found in the develop directory
4c79b5
		case 'ipv4':
4c79b5
			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])$#';
4c79b5
		break;
4c79b5
4c79b5
		case 'ipv6':
4c79b5
			return '#^(?:(?:(?:[\dA-F]{1,4}:){6}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:::(?:[\dA-F]{1,4}:){5}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:):(?:[\dA-F]{1,4}:){4}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,2}:(?:[\dA-F]{1,4}:){3}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,3}:(?:[\dA-F]{1,4}:){2}(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,4}:(?:[\dA-F]{1,4}:)(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,5}:(?:[\dA-F]{1,4}:[\dA-F]{1,4}|(?:(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d\d|2[0-4]\d|25[0-5])))|(?:(?:[\dA-F]{1,4}:){1,6}:[\dA-F]{1,4})|(?:(?:[\dA-F]{1,4}:){1,7}:))$#i';
4c79b5
		break;
4c79b5
4c79b5
		case 'url':
4c79b5
		case 'url_inline':
4c79b5
			$inline = ($mode == 'url') ? ')' : '';
4c79b5
			$scheme = ($mode == 'url') ? '[a-z\d+\-.]' : '[a-z\d+]'; // avoid automatic parsing of "word" in "last word.http://..."
4c79b5
			// generated with regex generation file in the develop folder
4c79b5
			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})*)?";
4c79b5
		break;
4c79b5
4c79b5
		case 'www_url':
4c79b5
		case 'www_url_inline':
4c79b5
			$inline = ($mode == 'www_url') ? ')' : '';
4c79b5
			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})*)?";
4c79b5
		break;
4c79b5
4c79b5
		case 'relative_url':
4c79b5
		case 'relative_url_inline':
4c79b5
			$inline = ($mode == 'relative_url') ? ')' : '';
4c79b5
			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})*)?";
4c79b5
		break;
4c79b5
	}
4c79b5
4c79b5
	return '';
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Returns the first block of the specified IPv6 address and as many additional
4c79b5
* ones as specified in the length paramater.
4c79b5
* If length is zero, then an empty string is returned.
4c79b5
* If length is greater than 3 the complete IP will be returned
4c79b5
*/
4c79b5
function short_ipv6($ip, $length)
4c79b5
{
4c79b5
	if ($length < 1)
4c79b5
	{
4c79b5
		return '';
4c79b5
	}
4c79b5
4c79b5
	// extend IPv6 addresses
4c79b5
	$blocks = substr_count($ip, ':') + 1;
4c79b5
	if ($blocks < 9)
4c79b5
	{
4c79b5
		$ip = str_replace('::', ':' . str_repeat('0000:', 9 - $blocks), $ip);
4c79b5
	}
4c79b5
	if ($ip[0] == ':')
4c79b5
	{
4c79b5
		$ip = '0000' . $ip;
4c79b5
	}
4c79b5
	if ($length < 4)
4c79b5
	{
4c79b5
		$ip = implode(':', array_slice(explode(':', $ip), 0, 1 + $length));
4c79b5
	}
4c79b5
4c79b5
	return $ip;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Wrapper for php's checkdnsrr function.
4c79b5
*
4c79b5
* The windows failover is from the php manual
4c79b5
* Please make sure to check the return value for === true and === false, since NULL could
4c79b5
* be returned too.
4c79b5
*
4c79b5
* @return true if entry found, false if not, NULL if this function is not supported by this environment
4c79b5
*/
4c79b5
function phpbb_checkdnsrr($host, $type = '')
4c79b5
{
4c79b5
	$type = (!$type) ? 'MX' : $type;
4c79b5
4c79b5
	if (DIRECTORY_SEPARATOR == '\\')
4c79b5
	{
4c79b5
		if (!function_exists('exec'))
4c79b5
		{
4c79b5
			return NULL;
4c79b5
		}
4c79b5
4c79b5
		// @exec('nslookup -retry=1 -timout=1 -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host), $output);
4c79b5
		@exec('nslookup -type=' . escapeshellarg($type) . ' ' . escapeshellarg($host) . '.', $output);
4c79b5
4c79b5
		// If output is empty, the nslookup failed
4c79b5
		if (empty($output))
4c79b5
		{
4c79b5
			return NULL;
4c79b5
		}
4c79b5
4c79b5
		foreach ($output as $line)
4c79b5
		{
4c79b5
			if (!trim($line))
4c79b5
			{
4c79b5
				continue;
4c79b5
			}
4c79b5
4c79b5
			// Valid records begin with host name:
4c79b5
			if (strpos($line, $host) === 0)
4c79b5
			{
4c79b5
				return true;
4c79b5
			}
4c79b5
		}
4c79b5
4c79b5
		return false;
4c79b5
	}
4c79b5
	else if (function_exists('checkdnsrr'))
4c79b5
	{
4c79b5
		// The dot indicates to search the DNS root (helps those having DNS prefixes on the same domain)
4c79b5
		return (checkdnsrr($host . '.', $type)) ? true : false;
4c79b5
	}
4c79b5
4c79b5
	return NULL;
4c79b5
}
4c79b5
4c79b5
// Handler, header and footer
4c79b5
4c79b5
/**
4c79b5
* Error and message handler, call with trigger_error if reqd
4c79b5
*/
4c79b5
function msg_handler($errno, $msg_text, $errfile, $errline)
4c79b5
{
4c79b5
	global $cache, $db, $auth, $template, $config, $user;
4c79b5
	global $phpEx, $phpbb_root_path, $msg_title, $msg_long_text;
4c79b5
4c79b5
	// Do not display notices if we suppress them via @
4c79b5
	if (error_reporting() == 0)
4c79b5
	{
4c79b5
		return;
4c79b5
	}
4c79b5
4c79b5
	// Message handler is stripping text. In case we need it, we are possible to define long text...
4c79b5
	if (isset($msg_long_text) && $msg_long_text && !$msg_text)
4c79b5
	{
4c79b5
		$msg_text = $msg_long_text;
4c79b5
	}
4c79b5
4c79b5
	switch ($errno)
4c79b5
	{
4c79b5
		case E_NOTICE:
4c79b5
		case E_WARNING:
4c79b5
4c79b5
			// Check the error reporting level and return if the error level does not match
4c79b5
			// If DEBUG is defined the default level is E_ALL
4c79b5
			if (($errno & ((defined('DEBUG')) ? E_ALL : error_reporting())) == 0)
4c79b5
			{
4c79b5
				return;
4c79b5
			}
4c79b5
4c79b5
			if (strpos($errfile, 'cache') === false && strpos($errfile, 'template.') === false)
4c79b5
			{
4c79b5
				// flush the content, else we get a white page if output buffering is on
4c79b5
				if ((int) @ini_get('output_buffering') === 1 || strtolower(@ini_get('output_buffering')) === 'on')
4c79b5
				{
4c79b5
					@ob_flush();
4c79b5
				}
4c79b5
4c79b5
				// Another quick fix for those having gzip compression enabled, but do not flush if the coder wants to catch "something". ;)
4c79b5
				if (!empty($config['gzip_compress']))
4c79b5
				{
4c79b5
					if (@extension_loaded('zlib') && !headers_sent() && !ob_get_level())
4c79b5
					{
4c79b5
						@ob_flush();
4c79b5
					}
4c79b5
				}
4c79b5
4c79b5
				// remove complete path to installation, with the risk of changing backslashes meant to be there
4c79b5
				$errfile = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $errfile);
4c79b5
				$msg_text = str_replace(array(phpbb_realpath($phpbb_root_path), '\\'), array('', '/'), $msg_text);
4c79b5
4c79b5
				echo '[phpBB Debug] PHP Notice: in file ' . $errfile . ' on line ' . $errline . ': ' . $msg_text . '
' . "\n";
4c79b5
			}
4c79b5
4c79b5
			return;
4c79b5
4c79b5
		break;
4c79b5
4c79b5
		case E_USER_ERROR:
4c79b5
4c79b5
			if (!empty($user) && !empty($user->lang))
4c79b5
			{
4c79b5
				$msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
4c79b5
				$msg_title = (!isset($msg_title)) ? $user->lang['GENERAL_ERROR'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
4c79b5
4c79b5
				$l_return_index = sprintf($user->lang['RETURN_INDEX'], '', '');
4c79b5
				$l_notify = '';
4c79b5
4c79b5
				if (!empty($config['board_contact']))
4c79b5
				{
4c79b5
					$l_notify = '

' . sprintf($user->lang['NOTIFY_ADMIN_EMAIL'], $config['board_contact']) . '

';
4c79b5
				}
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$msg_title = 'General Error';
4c79b5
				$l_return_index = 'Return to index page';
4c79b5
				$l_notify = '';
4c79b5
4c79b5
				if (!empty($config['board_contact']))
4c79b5
				{
4c79b5
					$l_notify = '

Please notify the board administrator or webmaster: ' . $config['board_contact'] . '

';
4c79b5
				}
4c79b5
			}
4c79b5
4c79b5
			garbage_collection();
4c79b5
4c79b5
			// Try to not call the adm page data...
4c79b5
4c79b5
			echo '';
4c79b5
			echo '<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr">';
4c79b5
			echo '<head>';
4c79b5
			echo '<meta http-equiv="content-type" content="text/html; charset=utf-8" />';
4c79b5
			echo '<title>' . $msg_title . '</title>';
4c79b5
			echo '<style type="text/css">' . "\n" . '/* 
4c79b5
			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; } ';
4c79b5
			echo 'a:link, a:active, a:visited { color: #006699; text-decoration: none; } a:hover { color: #DD6900; text-decoration: underline; } ';
4c79b5
			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; } ';
4c79b5
			echo '.panel { margin: 4px 0; background-color: #FFFFFF; border: solid 1px  #A9B8C2; } ';
4c79b5
			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; } ';
4c79b5
			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; } ';
4c79b5
			echo "\n" . '/* ]]> */' . "\n";
4c79b5
			echo '</style>';
4c79b5
			echo '</head>';
4c79b5
			echo '<body id="errorpage">';
4c79b5
			echo '
';
4c79b5
			echo '	
4c79b5
			echo '		' . $l_return_index;
4c79b5
			echo '	';
4c79b5
			echo '	
';
4c79b5
			echo '	
';
4c79b5
			echo '		
';
4c79b5
			echo '			

' . $msg_title . '

';
4c79b5
4c79b5
			echo '			
' . $msg_text . '
';
4c79b5
4c79b5
			echo $l_notify;
4c79b5
4c79b5
			echo '		';
4c79b5
			echo '	';
4c79b5
			echo '	';
4c79b5
			echo '	
4c79b5
			echo '		Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group';
4c79b5
			echo '	';
4c79b5
			echo '';
4c79b5
			echo '</body>';
4c79b5
			echo '</html>';
4c79b5
4c79b5
			exit_handler();
4c79b5
4c79b5
			// On a fatal error (and E_USER_ERROR *is* fatal) we never want other scripts to continue and force an exit here.
4c79b5
			exit;
4c79b5
		break;
4c79b5
4c79b5
		case E_USER_WARNING:
4c79b5
		case E_USER_NOTICE:
4c79b5
4c79b5
			define('IN_ERROR_HANDLER', true);
4c79b5
4c79b5
			if (empty($user->data))
4c79b5
			{
4c79b5
				$user->session_begin();
4c79b5
			}
4c79b5
4c79b5
			// We re-init the auth array to get correct results on login/logout
4c79b5
			$auth->acl($user->data);
4c79b5
4c79b5
			if (empty($user->lang))
4c79b5
			{
4c79b5
				$user->setup();
4c79b5
			}
4c79b5
4c79b5
			$msg_text = (!empty($user->lang[$msg_text])) ? $user->lang[$msg_text] : $msg_text;
4c79b5
			$msg_title = (!isset($msg_title)) ? $user->lang['INFORMATION'] : ((!empty($user->lang[$msg_title])) ? $user->lang[$msg_title] : $msg_title);
4c79b5
4c79b5
			if (!defined('HEADER_INC'))
4c79b5
			{
4c79b5
				if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
4c79b5
				{
4c79b5
					adm_page_header($msg_title);
4c79b5
				}
4c79b5
				else
4c79b5
				{
4c79b5
					page_header($msg_title);
4c79b5
				}
4c79b5
			}
4c79b5
4c79b5
			$template->set_filenames(array(
4c79b5
				'body' => 'message_body.html')
4c79b5
			);
4c79b5
4c79b5
			$template->assign_vars(array(
4c79b5
				'MESSAGE_TITLE'		=> $msg_title,
4c79b5
				'MESSAGE_TEXT'		=> $msg_text,
4c79b5
				'S_USER_WARNING'	=> ($errno == E_USER_WARNING) ? true : false,
4c79b5
				'S_USER_NOTICE'		=> ($errno == E_USER_NOTICE) ? true : false)
4c79b5
			);
4c79b5
4c79b5
			// We do not want the cron script to be called on error messages
4c79b5
			define('IN_CRON', true);
4c79b5
4c79b5
			if (defined('IN_ADMIN') && isset($user->data['session_admin']) && $user->data['session_admin'])
4c79b5
			{
4c79b5
				adm_page_footer();
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				page_footer();
4c79b5
			}
4c79b5
4c79b5
			exit_handler();
4c79b5
		break;
4c79b5
	}
4c79b5
4c79b5
	// If we notice an error not handled here we pass this back to PHP by returning false
4c79b5
	// This may not work for all php versions
4c79b5
	return false;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Queries the session table to get information about online guests
4c79b5
* @param int $forum_id Limits the search to the forum with this id
4c79b5
* @return int The number of active distinct guest sessions
4c79b5
*/
4c79b5
function obtain_guest_count($forum_id = 0)
4c79b5
{
4c79b5
	global $db, $config;
4c79b5
4c79b5
	if ($forum_id)
4c79b5
	{
4c79b5
		$reading_sql = ' AND s.session_forum_id = ' . (int) $forum_id;
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$reading_sql = '';
4c79b5
	}
4c79b5
	$time = (time() - (intval($config['load_online_time']) * 60));
4c79b5
4c79b5
	// Get number of online guests
4c79b5
4c79b5
	if ($db->sql_layer === 'sqlite')
4c79b5
	{
4c79b5
		$sql = 'SELECT COUNT(session_ip) as num_guests
4c79b5
			FROM (
4c79b5
				SELECT DISTINCT s.session_ip
4c79b5
				FROM ' . SESSIONS_TABLE . ' s
4c79b5
				WHERE s.session_user_id = ' . ANONYMOUS . '
4c79b5
					AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
4c79b5
				$reading_sql .
4c79b5
			')';
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$sql = 'SELECT COUNT(DISTINCT s.session_ip) as num_guests
4c79b5
			FROM ' . SESSIONS_TABLE . ' s
4c79b5
			WHERE s.session_user_id = ' . ANONYMOUS . '
4c79b5
				AND s.session_time >= ' . ($time - ((int) ($time % 60))) .
4c79b5
			$reading_sql;
4c79b5
	}
4c79b5
	$result = $db->sql_query($sql, 60);
4c79b5
	$guests_online = (int) $db->sql_fetchfield('num_guests');
4c79b5
	$db->sql_freeresult($result);
4c79b5
4c79b5
	return $guests_online;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Queries the session table to get information about online users
4c79b5
* @param int $forum_id Limits the search to the forum with this id
4c79b5
* @return array An array containing the ids of online, hidden and visible users, as well as statistical info
4c79b5
*/
4c79b5
function obtain_users_online($forum_id = 0)
4c79b5
{
4c79b5
	global $db, $config, $user;
4c79b5
4c79b5
	$reading_sql = '';
4c79b5
	if ($forum_id !== 0)
4c79b5
	{
4c79b5
		$reading_sql = ' AND s.session_forum_id = ' . (int) $forum_id;
4c79b5
	}
4c79b5
4c79b5
	$online_users = array(
4c79b5
		'online_users'			=> array(),
4c79b5
		'hidden_users'			=> array(),
4c79b5
		'total_online'			=> 0,
4c79b5
		'visible_online'		=> 0,
4c79b5
		'hidden_online'			=> 0,
4c79b5
		'guests_online'			=> 0,
4c79b5
	);
4c79b5
4c79b5
	if ($config['load_online_guests'])
4c79b5
	{
4c79b5
		$online_users['guests_online'] = obtain_guest_count($forum_id);
4c79b5
	}
4c79b5
4c79b5
	// a little discrete magic to cache this for 30 seconds
4c79b5
	$time = (time() - (intval($config['load_online_time']) * 60));
4c79b5
4c79b5
	$sql = 'SELECT s.session_user_id, s.session_ip, s.session_viewonline
4c79b5
		FROM ' . SESSIONS_TABLE . ' s
4c79b5
		WHERE s.session_time >= ' . ($time - ((int) ($time % 30))) .
4c79b5
			$reading_sql .
4c79b5
		' AND s.session_user_id <> ' . ANONYMOUS;
4c79b5
	$result = $db->sql_query($sql);
4c79b5
4c79b5
	while ($row = $db->sql_fetchrow($result))
4c79b5
	{
4c79b5
		// Skip multiple sessions for one user
4c79b5
		if (!isset($online_users['online_users'][$row['session_user_id']]))
4c79b5
		{
4c79b5
			$online_users['online_users'][$row['session_user_id']] = (int) $row['session_user_id'];
4c79b5
			if ($row['session_viewonline'])
4c79b5
			{
4c79b5
				$online_users['visible_online']++;
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$online_users['hidden_users'][$row['session_user_id']] = (int) $row['session_user_id'];
4c79b5
				$online_users['hidden_online']++;
4c79b5
			}
4c79b5
		}
4c79b5
	}
4c79b5
	$online_users['total_online'] = $online_users['guests_online'] + $online_users['visible_online'] + $online_users['hidden_online'];
4c79b5
	$db->sql_freeresult($result);
4c79b5
4c79b5
	return $online_users;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Uses the result of obtain_users_online to generate a localized, readable representation.
4c79b5
* @param mixed $online_users result of obtain_users_online - array with user_id lists for total, hidden and visible users, and statistics
4c79b5
* @param int $forum_id Indicate that the data is limited to one forum and not global.
4c79b5
* @return array An array containing the string for output to the template
4c79b5
*/
4c79b5
function obtain_users_online_string($online_users, $forum_id = 0)
4c79b5
{
4c79b5
	global $config, $db, $user, $auth;
4c79b5
4c79b5
	$user_online_link = $online_userlist = '';
4c79b5
4c79b5
	if (sizeof($online_users['online_users']))
4c79b5
	{
4c79b5
		$sql = 'SELECT username, username_clean, user_id, user_type, user_allow_viewonline, user_colour
4c79b5
				FROM ' . USERS_TABLE . '
4c79b5
				WHERE ' . $db->sql_in_set('user_id', $online_users['online_users']) . '
4c79b5
				ORDER BY username_clean ASC';
4c79b5
		$result = $db->sql_query($sql);
4c79b5
4c79b5
		while ($row = $db->sql_fetchrow($result))
4c79b5
		{
4c79b5
			// User is logged in and therefore not a guest
4c79b5
			if ($row['user_id'] != ANONYMOUS)
4c79b5
			{
4c79b5
				if (isset($online_users['hidden_users'][$row['user_id']]))
4c79b5
				{
4c79b5
					$row['username'] = '' . $row['username'] . '';
4c79b5
				}
4c79b5
4c79b5
				if (!isset($online_users['hidden_users'][$row['user_id']]) || $auth->acl_get('u_viewonline'))
4c79b5
				{
4c79b5
					$user_online_link = get_username_string(($row['user_type'] <> USER_IGNORE) ? 'full' : 'no_profile', $row['user_id'], $row['username'], $row['user_colour']);
4c79b5
					$online_userlist .= ($online_userlist != '') ? ', ' . $user_online_link : $user_online_link;
4c79b5
				}
4c79b5
			}
4c79b5
		}
4c79b5
		$db->sql_freeresult($result);
4c79b5
	}
4c79b5
4c79b5
	if (!$online_userlist)
4c79b5
	{
4c79b5
		$online_userlist = $user->lang['NO_ONLINE_USERS'];
4c79b5
	}
4c79b5
4c79b5
	if ($forum_id === 0)
4c79b5
	{
4c79b5
		$online_userlist = $user->lang['REGISTERED_USERS'] . ' ' . $online_userlist;
4c79b5
	}
4c79b5
	else if ($config['load_online_guests'])
4c79b5
	{
4c79b5
		$l_online = ($online_users['guests_online'] === 1) ? $user->lang['BROWSING_FORUM_GUEST'] : $user->lang['BROWSING_FORUM_GUESTS'];
4c79b5
		$online_userlist = sprintf($l_online, $online_userlist, $online_users['guests_online']);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$online_userlist = sprintf($user->lang['BROWSING_FORUM'], $online_userlist);
4c79b5
	}
4c79b5
	// Build online listing
4c79b5
	$vars_online = array(
4c79b5
		'ONLINE'	=> array('total_online', 'l_t_user_s', 0),
4c79b5
		'REG'		=> array('visible_online', 'l_r_user_s', !$config['load_online_guests']),
4c79b5
		'HIDDEN'	=> array('hidden_online', 'l_h_user_s', $config['load_online_guests']),
4c79b5
		'GUEST'		=> array('guests_online', 'l_g_user_s', 0)
4c79b5
	);
4c79b5
4c79b5
	foreach ($vars_online as $l_prefix => $var_ary)
4c79b5
	{
4c79b5
		if ($var_ary[2])
4c79b5
		{
4c79b5
			$l_suffix = '_AND';
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$l_suffix = '';
4c79b5
		}
4c79b5
		switch ($online_users[$var_ary[0]])
4c79b5
		{
4c79b5
			case 0:
4c79b5
				${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_ZERO_TOTAL' . $l_suffix];
4c79b5
			break;
4c79b5
4c79b5
			case 1:
4c79b5
				${$var_ary[1]} = $user->lang[$l_prefix . '_USER_TOTAL' . $l_suffix];
4c79b5
			break;
4c79b5
4c79b5
			default:
4c79b5
				${$var_ary[1]} = $user->lang[$l_prefix . '_USERS_TOTAL' . $l_suffix];
4c79b5
			break;
4c79b5
		}
4c79b5
	}
4c79b5
	unset($vars_online);
4c79b5
4c79b5
	$l_online_users = sprintf($l_t_user_s, $online_users['total_online']);
4c79b5
	$l_online_users .= sprintf($l_r_user_s, $online_users['visible_online']);
4c79b5
	$l_online_users .= sprintf($l_h_user_s, $online_users['hidden_online']);
4c79b5
4c79b5
	if ($config['load_online_guests'])
4c79b5
	{
4c79b5
		$l_online_users .= sprintf($l_g_user_s, $online_users['guests_online']);
4c79b5
	}
4c79b5
4c79b5
4c79b5
4c79b5
	return array(
4c79b5
		'online_userlist'	=> $online_userlist,
4c79b5
		'l_online_users'	=> $l_online_users,
4c79b5
	);
4c79b5
}
4c79b5
4c79b5
4c79b5
/**
4c79b5
* Generate page header
4c79b5
*/
4c79b5
function page_header($page_title = '', $display_online_list = true)
4c79b5
{
4c79b5
	global $db, $config, $template, $SID, $_SID, $user, $auth, $phpEx, $phpbb_root_path;
4c79b5
4c79b5
	if (defined('HEADER_INC'))
4c79b5
	{
4c79b5
		return;
4c79b5
	}
4c79b5
4c79b5
	define('HEADER_INC', true);
4c79b5
4c79b5
	// gzip_compression
4c79b5
	if ($config['gzip_compress'])
4c79b5
	{
4c79b5
		if (@extension_loaded('zlib') && !headers_sent())
4c79b5
		{
4c79b5
			ob_start('ob_gzhandler');
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Generate logged in/logged out status
4c79b5
	if ($user->data['user_id'] != ANONYMOUS)
4c79b5
	{
4c79b5
		$u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=logout', true, $user->session_id);
4c79b5
		$l_login_logout = sprintf($user->lang['LOGOUT_USER'], $user->data['username']);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$u_login_logout = append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=login');
4c79b5
		$l_login_logout = $user->lang['LOGIN'];
4c79b5
	}
4c79b5
4c79b5
	// Last visit date/time
4c79b5
	$s_last_visit = ($user->data['user_id'] != ANONYMOUS) ? $user->format_date($user->data['session_last_visit']) : '';
4c79b5
4c79b5
	// Get users online list ... if required
4c79b5
	$l_online_users = $online_userlist = $l_online_record = '';
4c79b5
4c79b5
	if ($config['load_online'] && $config['load_online_time'] && $display_online_list)
4c79b5
	{
4c79b5
		$f = request_var('f', 0);
4c79b5
		$f = max($f, 0);
4c79b5
		$online_users = obtain_users_online($f);
4c79b5
		$user_online_strings = obtain_users_online_string($online_users, $f);
4c79b5
4c79b5
		$l_online_users = $user_online_strings['l_online_users'];
4c79b5
		$online_userlist = $user_online_strings['online_userlist'];
4c79b5
		$total_online_users = $online_users['total_online'];
4c79b5
4c79b5
		if ($total_online_users > $config['record_online_users'])
4c79b5
		{
4c79b5
			set_config('record_online_users', $total_online_users, true);
4c79b5
			set_config('record_online_date', time(), true);
4c79b5
		}
4c79b5
4c79b5
		$l_online_record = sprintf($user->lang['RECORD_ONLINE_USERS'], $config['record_online_users'], $user->format_date($config['record_online_date']));
4c79b5
4c79b5
		$l_online_time = ($config['load_online_time'] == 1) ? 'VIEW_ONLINE_TIME' : 'VIEW_ONLINE_TIMES';
4c79b5
		$l_online_time = sprintf($user->lang[$l_online_time], $config['load_online_time']);
4c79b5
	}
4c79b5
	else
4c79b5
	{
4c79b5
		$l_online_time = '';
4c79b5
	}
4c79b5
4c79b5
	$l_privmsgs_text = $l_privmsgs_text_unread = '';
4c79b5
	$s_privmsg_new = false;
4c79b5
4c79b5
	// Obtain number of new private messages if user is logged in
4c79b5
	if (!empty($user->data['is_registered']))
4c79b5
	{
4c79b5
		if ($user->data['user_new_privmsg'])
4c79b5
		{
4c79b5
			$l_message_new = ($user->data['user_new_privmsg'] == 1) ? $user->lang['NEW_PM'] : $user->lang['NEW_PMS'];
4c79b5
			$l_privmsgs_text = sprintf($l_message_new, $user->data['user_new_privmsg']);
4c79b5
4c79b5
			if (!$user->data['user_last_privmsg'] || $user->data['user_last_privmsg'] > $user->data['session_last_visit'])
4c79b5
			{
4c79b5
				$sql = 'UPDATE ' . USERS_TABLE . '
4c79b5
					SET user_last_privmsg = ' . $user->data['session_last_visit'] . '
4c79b5
					WHERE user_id = ' . $user->data['user_id'];
4c79b5
				$db->sql_query($sql);
4c79b5
4c79b5
				$s_privmsg_new = true;
4c79b5
			}
4c79b5
			else
4c79b5
			{
4c79b5
				$s_privmsg_new = false;
4c79b5
			}
4c79b5
		}
4c79b5
		else
4c79b5
		{
4c79b5
			$l_privmsgs_text = $user->lang['NO_NEW_PM'];
4c79b5
			$s_privmsg_new = false;
4c79b5
		}
4c79b5
4c79b5
		$l_privmsgs_text_unread = '';
4c79b5
4c79b5
		if ($user->data['user_unread_privmsg'] && $user->data['user_unread_privmsg'] != $user->data['user_new_privmsg'])
4c79b5
		{
4c79b5
			$l_message_unread = ($user->data['user_unread_privmsg'] == 1) ? $user->lang['UNREAD_PM'] : $user->lang['UNREAD_PMS'];
4c79b5
			$l_privmsgs_text_unread = sprintf($l_message_unread, $user->data['user_unread_privmsg']);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// Which timezone?
4c79b5
	$tz = ($user->data['user_id'] != ANONYMOUS) ? strval(doubleval($user->data['user_timezone'])) : strval(doubleval($config['board_timezone']));
4c79b5
4c79b5
	// Send a proper content-language to the output
4c79b5
	$user_lang = $user->lang['USER_LANG'];
4c79b5
	if (strpos($user_lang, '-x-') !== false)
4c79b5
	{
4c79b5
		$user_lang = substr($user_lang, 0, strpos($user_lang, '-x-'));
4c79b5
	}
4c79b5
4c79b5
	// The following assigns all _common_ variables that may be used at any point in a template.
4c79b5
	$template->assign_vars(array(
4c79b5
		'SITENAME'						=> $config['sitename'],
4c79b5
		'SITE_DESCRIPTION'				=> $config['site_desc'],
4c79b5
		'PAGE_TITLE'					=> $page_title,
4c79b5
		'SCRIPT_NAME'					=> str_replace('.' . $phpEx, '', $user->page['page_name']),
4c79b5
		'LAST_VISIT_DATE'				=> sprintf($user->lang['YOU_LAST_VISIT'], $s_last_visit),
4c79b5
		'LAST_VISIT_YOU'				=> $s_last_visit,
4c79b5
		'CURRENT_TIME'					=> sprintf($user->lang['CURRENT_TIME'], $user->format_date(time(), false, true)),
4c79b5
		'TOTAL_USERS_ONLINE'			=> $l_online_users,
4c79b5
		'LOGGED_IN_USER_LIST'			=> $online_userlist,
4c79b5
		'RECORD_USERS'					=> $l_online_record,
4c79b5
		'PRIVATE_MESSAGE_INFO'			=> $l_privmsgs_text,
4c79b5
		'PRIVATE_MESSAGE_INFO_UNREAD'	=> $l_privmsgs_text_unread,
4c79b5
4c79b5
		'S_USER_NEW_PRIVMSG'			=> $user->data['user_new_privmsg'],
4c79b5
		'S_USER_UNREAD_PRIVMSG'			=> $user->data['user_unread_privmsg'],
4c79b5
4c79b5
		'SID'				=> $SID,
4c79b5
		'_SID'				=> $_SID,
4c79b5
		'SESSION_ID'		=> $user->session_id,
4c79b5
		'ROOT_PATH'			=> $phpbb_root_path,
4c79b5
4c79b5
		'L_LOGIN_LOGOUT'	=> $l_login_logout,
4c79b5
		'L_INDEX'			=> $user->lang['FORUM_INDEX'],
4c79b5
		'L_ONLINE_EXPLAIN'	=> $l_online_time,
4c79b5
4c79b5
		'U_PRIVATEMSGS'			=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
4c79b5
		'U_RETURN_INBOX'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&folder=inbox'),
4c79b5
		'U_POPUP_PM'			=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup'),
4c79b5
		'UA_POPUP_PM'			=> addslashes(append_sid("{$phpbb_root_path}ucp.$phpEx", 'i=pm&mode=popup')),
4c79b5
		'U_MEMBERLIST'			=> append_sid("{$phpbb_root_path}memberlist.$phpEx"),
4c79b5
		'U_VIEWONLINE'			=> ($auth->acl_gets('u_viewprofile', 'a_user', 'a_useradd', 'a_userdel')) ? append_sid("{$phpbb_root_path}viewonline.$phpEx") : '',
4c79b5
		'U_LOGIN_LOGOUT'		=> $u_login_logout,
4c79b5
		'U_INDEX'				=> append_sid("{$phpbb_root_path}index.$phpEx"),
4c79b5
		'U_SEARCH'				=> append_sid("{$phpbb_root_path}search.$phpEx"),
4c79b5
		'U_REGISTER'			=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=register'),
4c79b5
		'U_PROFILE'				=> append_sid("{$phpbb_root_path}ucp.$phpEx"),
4c79b5
		'U_MODCP'				=> append_sid("{$phpbb_root_path}mcp.$phpEx", false, true, $user->session_id),
4c79b5
		'U_FAQ'					=> append_sid("{$phpbb_root_path}faq.$phpEx"),
4c79b5
		'U_SEARCH_SELF'			=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=egosearch'),
4c79b5
		'U_SEARCH_NEW'			=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=newposts'),
4c79b5
		'U_SEARCH_UNANSWERED'	=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=unanswered'),
4c79b5
		'U_SEARCH_ACTIVE_TOPICS'=> append_sid("{$phpbb_root_path}search.$phpEx", 'search_id=active_topics'),
4c79b5
		'U_DELETE_COOKIES'		=> append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=delete_cookies'),
4c79b5
		'U_TEAM'				=> ($user->data['user_id'] != ANONYMOUS && !$auth->acl_get('u_viewprofile')) ? '' : append_sid("{$phpbb_root_path}memberlist.$phpEx", 'mode=leaders'),
4c79b5
		'U_RESTORE_PERMISSIONS'	=> ($user->data['user_perm_from'] && $auth->acl_get('a_switchperm')) ? append_sid("{$phpbb_root_path}ucp.$phpEx", 'mode=restore_perm') : '',
4c79b5
4c79b5
		'S_USER_LOGGED_IN'		=> ($user->data['user_id'] != ANONYMOUS) ? true : false,
4c79b5
		'S_AUTOLOGIN_ENABLED'	=> ($config['allow_autologin']) ? true : false,
4c79b5
		'S_BOARD_DISABLED'		=> ($config['board_disable']) ? true : false,
4c79b5
		'S_REGISTERED_USER'		=> (!empty($user->data['is_registered'])) ? true : false,
4c79b5
		'S_IS_BOT'				=> (!empty($user->data['is_bot'])) ? true : false,
4c79b5
		'S_USER_PM_POPUP'		=> $user->optionget('popuppm'),
4c79b5
		'S_USER_LANG'			=> $user_lang,
4c79b5
		'S_USER_BROWSER'		=> (isset($user->data['session_browser'])) ? $user->data['session_browser'] : $user->lang['UNKNOWN_BROWSER'],
4c79b5
		'S_USERNAME'			=> $user->data['username'],
4c79b5
		'S_CONTENT_DIRECTION'	=> $user->lang['DIRECTION'],
4c79b5
		'S_CONTENT_FLOW_BEGIN'	=> ($user->lang['DIRECTION'] == 'ltr') ? 'left' : 'right',
4c79b5
		'S_CONTENT_FLOW_END'	=> ($user->lang['DIRECTION'] == 'ltr') ? 'right' : 'left',
4c79b5
		'S_CONTENT_ENCODING'	=> 'UTF-8',
4c79b5
		'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], ''),
4c79b5
		'S_DISPLAY_ONLINE_LIST'	=> ($l_online_time) ? 1 : 0,
4c79b5
		'S_DISPLAY_SEARCH'		=> (!$config['load_search']) ? 0 : (isset($auth) ? ($auth->acl_get('u_search') && $auth->acl_getf_global('f_search')) : 1),
4c79b5
		'S_DISPLAY_PM'			=> ($config['allow_privmsg'] && !empty($user->data['is_registered']) && ($auth->acl_get('u_readpm') || $auth->acl_get('u_sendpm'))) ? true : false,
4c79b5
		'S_DISPLAY_MEMBERLIST'	=> (isset($auth)) ? $auth->acl_get('u_viewprofile') : 0,
4c79b5
		'S_NEW_PM'				=> ($s_privmsg_new) ? 1 : 0,
4c79b5
		'S_REGISTER_ENABLED'	=> ($config['require_activation'] != USER_ACTIVATION_DISABLE) ? true : false,
4c79b5
4c79b5
		'T_THEME_PATH'			=> "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme',
4c79b5
		'T_TEMPLATE_PATH'		=> "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
4c79b5
		'T_SUPER_TEMPLATE_PATH'	=> (isset($user->theme['template_inherit_path']) && $user->theme['template_inherit_path']) ? "{$phpbb_root_path}styles/" . $user->theme['template_inherit_path'] . '/template' : "{$phpbb_root_path}styles/" . $user->theme['template_path'] . '/template',
4c79b5
		'T_IMAGESET_PATH'		=> "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset',
4c79b5
		'T_IMAGESET_LANG_PATH'	=> "{$phpbb_root_path}styles/" . $user->theme['imageset_path'] . '/imageset/' . $user->data['user_lang'],
4c79b5
		'T_IMAGES_PATH'			=> "{$phpbb_root_path}images/",
4c79b5
		'T_SMILIES_PATH'		=> "{$phpbb_root_path}{$config['smilies_path']}/",
4c79b5
		'T_AVATAR_PATH'			=> "{$phpbb_root_path}{$config['avatar_path']}/",
4c79b5
		'T_AVATAR_GALLERY_PATH'	=> "{$phpbb_root_path}{$config['avatar_gallery_path']}/",
4c79b5
		'T_ICONS_PATH'			=> "{$phpbb_root_path}{$config['icons_path']}/",
4c79b5
		'T_RANKS_PATH'			=> "{$phpbb_root_path}{$config['ranks_path']}/",
4c79b5
		'T_UPLOAD_PATH'			=> "{$phpbb_root_path}{$config['upload_path']}/",
4c79b5
		'T_STYLESHEET_LINK'		=> (!$user->theme['theme_storedb']) ? "{$phpbb_root_path}styles/" . $user->theme['theme_path'] . '/theme/stylesheet.css' : "{$phpbb_root_path}style.$phpEx?sid=$user->session_id&id=" . $user->theme['style_id'] . '&lang=' . $user->data['user_lang'],
4c79b5
		'T_STYLESHEET_NAME'		=> $user->theme['theme_name'],
4c79b5
4c79b5
		'SITE_LOGO_IMG'			=> $user->img('site_logo'),
4c79b5
4c79b5
		'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')),
4c79b5
	));
4c79b5
4c79b5
	// application/xhtml+xml not used because of IE
4c79b5
	header('Content-type: text/html; charset=UTF-8');
4c79b5
4c79b5
	header('Cache-Control: private, no-cache="set-cookie"');
4c79b5
	header('Expires: 0');
4c79b5
	header('Pragma: no-cache');
4c79b5
4c79b5
	return;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Generate page footer
4c79b5
*/
4c79b5
function page_footer($run_cron = true)
4c79b5
{
4c79b5
	global $db, $config, $template, $user, $auth, $cache, $starttime, $phpbb_root_path, $phpEx;
4c79b5
4c79b5
	// Output page creation time
4c79b5
	if (defined('DEBUG'))
4c79b5
	{
4c79b5
		$mtime = explode(' ', microtime());
4c79b5
		$totaltime = $mtime[0] + $mtime[1] - $starttime;
4c79b5
4c79b5
		if (!empty($_REQUEST['explain']) && $auth->acl_get('a_') && defined('DEBUG_EXTRA') && method_exists($db, 'sql_report'))
4c79b5
		{
4c79b5
			$db->sql_report('display');
4c79b5
		}
4c79b5
4c79b5
		$debug_output = sprintf('Time : %.3fs | ' . $db->sql_num_queries() . ' Queries | GZIP : ' . (($config['gzip_compress']) ? 'On' : 'Off') . (($user->load) ? ' | Load : ' . $user->load : ''), $totaltime);
4c79b5
4c79b5
		if ($auth->acl_get('a_') && defined('DEBUG_EXTRA'))
4c79b5
		{
4c79b5
			if (function_exists('memory_get_usage'))
4c79b5
			{
4c79b5
				if ($memory_usage = memory_get_usage())
4c79b5
				{
4c79b5
					global $base_memory_usage;
4c79b5
					$memory_usage -= $base_memory_usage;
4c79b5
					$memory_usage = get_formatted_filesize($memory_usage);
4c79b5
4c79b5
					$debug_output .= ' | Memory Usage: ' . $memory_usage;
4c79b5
				}
4c79b5
			}
4c79b5
4c79b5
			$debug_output .= ' | Explain';
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$template->assign_vars(array(
4c79b5
		'DEBUG_OUTPUT'			=> (defined('DEBUG')) ? $debug_output : '',
4c79b5
		'TRANSLATION_INFO'		=> (!empty($user->lang['TRANSLATION_INFO'])) ? $user->lang['TRANSLATION_INFO'] : '',
4c79b5
4c79b5
		'U_ACP' => ($auth->acl_get('a_') && !empty($user->data['is_registered'])) ? append_sid("{$phpbb_root_path}adm/index.$phpEx", false, true, $user->session_id) : '')
4c79b5
	);
4c79b5
4c79b5
	// Call cron-type script
4c79b5
	if (!defined('IN_CRON') && $run_cron && !$config['board_disable'])
4c79b5
	{
4c79b5
		$cron_type = '';
4c79b5
4c79b5
		if (time() - $config['queue_interval'] > $config['last_queue_run'] && !defined('IN_ADMIN') && file_exists($phpbb_root_path . 'cache/queue.' . $phpEx))
4c79b5
		{
4c79b5
			// Process email queue
4c79b5
			$cron_type = 'queue';
4c79b5
		}
4c79b5
		else if (method_exists($cache, 'tidy') && time() - $config['cache_gc'] > $config['cache_last_gc'])
4c79b5
		{
4c79b5
			// Tidy the cache
4c79b5
			$cron_type = 'tidy_cache';
4c79b5
		}
4c79b5
		else if (time() - $config['warnings_gc'] > $config['warnings_last_gc'])
4c79b5
		{
4c79b5
			$cron_type = 'tidy_warnings';
4c79b5
		}
4c79b5
		else if (time() - $config['database_gc'] > $config['database_last_gc'])
4c79b5
		{
4c79b5
			// Tidy the database
4c79b5
			$cron_type = 'tidy_database';
4c79b5
		}
4c79b5
		else if (time() - $config['search_gc'] > $config['search_last_gc'])
4c79b5
		{
4c79b5
			// Tidy the search
4c79b5
			$cron_type = 'tidy_search';
4c79b5
		}
4c79b5
		else if (time() - $config['session_gc'] > $config['session_last_gc'])
4c79b5
		{
4c79b5
			$cron_type = 'tidy_sessions';
4c79b5
		}
4c79b5
4c79b5
		if ($cron_type)
4c79b5
		{
4c79b5
			$template->assign_var('RUN_CRON_TASK', 'cron');
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	$template->display('body');
4c79b5
4c79b5
	garbage_collection();
4c79b5
	exit_handler();
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Closing the cache object and the database
4c79b5
* Cool function name, eh? We might want to add operations to it later
4c79b5
*/
4c79b5
function garbage_collection()
4c79b5
{
4c79b5
	global $cache, $db;
4c79b5
4c79b5
	// Unload cache, must be done before the DB connection if closed
4c79b5
	if (!empty($cache))
4c79b5
	{
4c79b5
		$cache->unload();
4c79b5
	}
4c79b5
4c79b5
	// Close our DB connection.
4c79b5
	if (!empty($db))
4c79b5
	{
4c79b5
		$db->sql_close();
4c79b5
	}
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Handler for exit calls in phpBB.
4c79b5
* This function supports hooks.
4c79b5
*
4c79b5
* Note: This function is called after the template has been outputted.
4c79b5
*/
4c79b5
function exit_handler()
4c79b5
{
4c79b5
	global $phpbb_hook, $config;
4c79b5
4c79b5
	if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
4c79b5
	{
4c79b5
		if ($phpbb_hook->hook_return(__FUNCTION__))
4c79b5
		{
4c79b5
			return $phpbb_hook->hook_return_result(__FUNCTION__);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	// As a pre-caution... some setups display a blank page if the flush() is not there.
4c79b5
	(empty($config['gzip_compress'])) ? @flush() : @ob_flush();
4c79b5
4c79b5
	exit;
4c79b5
}
4c79b5
4c79b5
/**
4c79b5
* Handler for init calls in phpBB. This function is called in user::setup();
4c79b5
* This function supports hooks.
4c79b5
*/
4c79b5
function phpbb_user_session_handler()
4c79b5
{
4c79b5
	global $phpbb_hook;
4c79b5
4c79b5
	if (!empty($phpbb_hook) && $phpbb_hook->call_hook(__FUNCTION__))
4c79b5
	{
4c79b5
		if ($phpbb_hook->hook_return(__FUNCTION__))
4c79b5
		{
4c79b5
			return $phpbb_hook->hook_return_result(__FUNCTION__);
4c79b5
		}
4c79b5
	}
4c79b5
4c79b5
	return;
4c79b5
}
4c79b5
4c79b5
?>