<?php
/******************************************************************************
* Subs.php                                                                    *
*******************************************************************************
* SMF: Simple Machines Forum                                                  *
* Open-Source Project Inspired by Zef Hemel (zef@zefhemel.com)                *
* =========================================================================== *
* Software Version:           SMF 1.0.6                                       *
* Software by:                Simple Machines (http://www.simplemachines.org) *
* Copyright 2001-2005 by:     Lewis Media (http://www.lewismedia.com)         *
* Support, News, Updates at:  http://www.simplemachines.org                   *
*******************************************************************************
* This program is free software; you may redistribute it and/or modify it     *
* under the terms of the provided license as published by Lewis Media.        *
*                                                                             *
* This program is distributed in the hope that it is and will be useful,      *
* but WITHOUT ANY WARRANTIES; without even any implied warranty of            *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                        *
*                                                                             *
* See the "license.txt" file for details of the Simple Machines license.      *
* The latest version can always be found at http://www.simplemachines.org.    *
******************************************************************************/
if (!defined('SMF'))
	die('Hacking attempt...');

/*	This file has all the main functions in it that relate to, well,
	everything.  It provides all of the following functions:

	resource db_query(string database_query, string __FILE__, int __LINE__)
		- should always be used in place of mysql_query.
		- executes a query string, and implements needed error checking.
		- always use the magic constants __FILE__ and __LINE__.
		- returns a MySQL result resource, to be freed with mysql_free_result.

	int db_affected_rows()
		- should always be used in place of db_insert_id.
		- returns the number of affected rows by the most recently executed
		  query.
		- handles the current connection so the forum with other connections
		  active at the same time.

	int db_insert_id()
		- should always be used in place of mysql_insert_id.
		- returns the most recently generated auto_increment column.
		- handles the current connection so the forum with other connections
		  active at the same time.

	void updateLastMessages(array ID_BOARDs)
		- takes an array of board IDs and updates their last messages.
		- if the board has a parent, that parent board is also automatically
		  updated.
		- columns updated are ID_LAST_MSG and lastUpdated.
		- note that ID_LAST_MSG should always be updated using this function,
		  and is not automatically updated upon other changes.

	void updateStats(string statistic, string condition = '1')
		- statistic can be 'member', 'message', 'topic', 'calendar', or
		  'postgroups'.
		- the condition is optional, and is only used to update post groups.
		- the 'member' statistic updates the latest member, the total member
		  count, and the number of unapproved members.
		- updating 'message' changes the total number of messages, and the
		  highest message id by ID_MSG.
		- 'topic' updates the total number of topics.
		- the 'calendar' statistic updates the cache of the calendar
		  information for a day before and after today.
		- the 'postgroups' case updates those members who match condition's
		  post-based membergroups in the database.

	void updateMemberData(int ID_MEMBER, array data)
		- updates the columns in the members table.
		- ID_MEMBER is either an int or an array of ints to be updated.
		- data is an associative array of the columns to be updated and their
		  respective values.
		- any string values updated should be quoted and slashed.
		- the value of any column can be '+' or '-', which mean 'increment'
		  and decrement, respectively.
		- if the member's post number is updated, updates their post groups.
		- this function should be used whenever member data needs to be
		  updated in place of an UPDATE query.

	void updateSettings(array changeArray)
		- updates both the settings table and $modSettings array.
		- all of changeArray's indexes and values are assumed to have escaped
		  apostrophes (')!
		- if a variable is already set to what you want to change it to, that
		  variable will be skipped over; it would be unnecessary to reset.

	string constructPageIndex(string base_url, int &start, int max_value,
			int num_per_page, bool compact_start = false)
		- builds the page list, e.g. 1 ... 6 7 [8] 9 10 ... 15.
		- compact_start caused it to use "url.page" instead of
		  "url;start=page".
		- handles any wireless settings (adding special things to URLs.)
		- very importantly, cleans up the start value passed, and forces it to
		  be a multiple of num_per_page.
		- also checks that start is not more than max_value.
		- base_url should be the URL without any start parameter on it.
		- uses the compactTopicPagesEnable and compactTopicPagesContiguous
		  settings to decide how to display the menu.
		- an example is available near the function definition.

	string comma_format(float number)
		- formats a number to display in the style of the admins' choosing.
		- uses the format of number_format to decide how to format the number.
		- for example, it might display "1 234,50".
		- caches the formatting data from the setting for optimization.

	string timeformat(int time, bool show_today = true)
		- returns a pretty formated version of time based on the user's format
		  in $user_info['time_format'].
		- applies any necessary time offsets to the timestamp.
		- if todayMod is set and show_today was not not specified or true, an
		  alternate format string is used to show the date with something to
		  show it is "today" or "yesterday".
		- performs localization (more than just strftime would do alone.)

	string un_htmlspecialchars(string text)
		- removes the base entities (&lt;, &quot;, etc.) from text.
		- should be used instead of html_entity_decode for PHP version
		  compatibility reasons.
		- additionally converts &nbsp; and &#039;.

	int forum_time(bool use_user_offset = true)
		- returns the current time with offsets.
		- always applies the offset in the time_offset setting.
		- if use_user_offset is true, applies the user's offset as well.
		- returns seconds since the unix epoch.

	string doUBBC(string message, bool enableSmileys = true)
		- parses bbc tags in message.
		- only parses smileys if enableSmileys is true.
		- does nothing if the enableBBC setting is off.
		- applies the fixLongWords magic if the setting is set to on.
		- returns the modified message.

	void parsecode(string &message)
		- this very hefty function does the bulk of message parsing.
		- only parses BBC tags which are not disabled in disabledBBC.
		- also handles basic HTML, if enablePostHTML is on.
		- caches the from/to replace regular expressions so as not to reload
		  them every time a string is parsed.
		- returns nothing, but instead modifies message directly.

	void parsesmileys(string &message)
		- the smiley parsing function which makes pretty faces appear :).
		- if custom smiley sets are turned off by smiley_enable, the default
		  set of smileys will be used.
		- these are specifically not parsed in code tags [url=mailto:Dad@blah.com]
		- caches the smileys from the database or array in memory.
		- doesn't return anything, but rather modifies message directly.

	string highlight_php_code(string code)
		- Uses PHP's highlight_code() to highlight PHP syntax
		- does special handling to keep the tabs in the code available.
		- used to parse PHP code from inside [code] and [php] tags.
		- returns the code with highlighted HTML.

	void writeLog(bool force = false)
		// !!!

	void redirectexit(string setLocation = '', bool add = true, bool refresh = false)
		// !!!

	void obExit(bool do_header = true, bool do_footer = do_header)
		// !!!

	void adminIndex($area)
		// !!!

	int logAction($action, $extra = array())
		// !!!

	void trackStats($stats = array())
		- caches statistics changes, and flushes them if you pass nothing.
		- if '+' is used as a value, it will be incremented.
		- does not actually commit the changes until the end of the page view.
		- depends on the trackStats setting.

	void spamProtection(string error_type)
		- attempts to protect from spammed messages and the like.
		- takes a $txt index. (not an actual string.)
		- depends on the spamWaitTime setting.

	array url_image_size(string url)
		- uses getimagesize() to determine the size of a file.
		- attempts to connect to the server first so it won't time out.
		- returns false on failure, otherwise the output of getimagesize().

	void determineTopicClass(array &topic_context)
		// !!!

	void setupThemeContext()
		// !!!

	void template_rawdata()
		// !!!

	void template_header()
		// !!!

	void theme_copyright(bool get_it = false)
		// !!!

	void template_footer()
		// !!!

	void db_debug_junk()
		// !!!

	void getAttachmentFilename(string filename, int ID_ATTACH, bool new = true)
		// !!!
*/

// Do a query.  Takes care of errors too.
function db_query($db_string, $file, $line)
{
	global $db_cache, $db_count, $db_connection, $modSettings;

	// One more query....
	$db_count = !isset($db_count) ? 1 : $db_count + 1;

	// Debugging.
	if (isset($GLOBALS['db_show_debug']) && $GLOBALS['db_show_debug'] === true)
	{
		$db_cache[$db_count]['q'] = $db_string;
		$db_cache[$db_count]['f'] = $file;
		$db_cache[$db_count]['l'] = $line;
		$st = explode(' ', microtime());
	}

	// First, we clean strings out of the query, reduce whitespace, lowercase, and trim - so we can check it over.
	if (empty($modSettings['disableQueryCheck']))
	{
		$clean = '';
		$old_pos = 0;
		$pos = -1;
		while (true)
		{
			$pos = strpos($db_string, '\'', $pos + 1);
			if ($pos === false)
				break;
			$clean .= substr($db_string, $old_pos, $pos - $old_pos);

			while (true)
			{
				$pos1 = strpos($db_string, '\'', $pos + 1);
				$pos2 = strpos($db_string, '\\', $pos + 1);
				if ($pos1 === false)
					break;
				elseif ($pos2 == false || $pos2 > $pos1)
				{
					$pos = $pos1;
					break;
				}

				$pos = $pos2 + 1;
			}
			$clean .= '%s';

			$old_pos = $pos + 1;
		}
		$clean .= substr($db_string, $old_pos);
		$clean = trim(strtolower(preg_replace(array('~\s+~s', '~/\*!40001 SQL_NO_CACHE \*/~', '~/\*!40000 USE INDEX \([A-Za-z\_]+?\) \*/~'), array(' ', '', ''), $clean)));

		// We don't use UNION in SMF, at least so far.  But it's useful for injections.
		if (strpos($clean, 'union') !== false && preg_match('~(^|[^a-z])union($|[^[a-z])~s', $clean) != 0)
			$fail = true;
		// Comments?  We don't use comments in our queries, we leave 'em outside!
		elseif (strpos($clean, '/*') > 2 || strpos($clean, '--') !== false)
			$fail = true;
		// Trying to change passwords, slow us down, or something?
		elseif (strpos($clean, 'set password') !== false && preg_match('~(^|[^a-z])set password($|[^[a-z])~s', $clean) != 0)
			$fail = true;
		elseif (strpos($clean, 'benchmark') !== false && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~s', $clean) != 0)
			$fail = true;
		// Sub selects?  We don't use those either.
		elseif (preg_match('~\([^)]*?select~s', $clean) != 0)
			$fail = true;

		if (!empty($fail))
		{
			log_error('Hacking attempt...' . "\n" . $db_string, $file, $line);
			fatal_error('Hacking attempt...', false);
		}
	}

	$ret = mysql_query($db_string, $db_connection);
	if ($ret === false)
		$ret = db_error($db_string, $file, $line);

	// Debugging.
	if (isset($GLOBALS['db_show_debug']) && $GLOBALS['db_show_debug'] === true)
	{
		$end = explode(' ', microtime());
		$db_cache[$db_count]['t'] = $end[0] + $end[1] - $st[0] - $st[1];
	}

	return $ret;
}

function db_affected_rows()
{
	global $db_connection;

	return mysql_affected_rows($db_connection);
}

function db_insert_id()
{
	global $db_connection;

	return mysql_insert_id($db_connection);
}

// Update the last message in a board, and its parents.
function updateLastMessages($setboards)
{
	global $db_prefix, $board_info, $board;

	if (!is_array($setboards))
		$setboards = array($setboards);

	// Find the latest message on this board.  (highest ID_MSG)
	$result = db_query("
		SELECT ID_BOARD, MAX(ID_MSG) AS ID_MSG
		FROM {$db_prefix}messages
		WHERE ID_BOARD" . (count($setboards) == 1 ? " = $setboards[0]" : ' IN (' . implode(', ', $setboards) . ')') . "
		GROUP BY ID_BOARD", __FILE__, __LINE__);
	$setboards = array_flip($setboards);
	while ($row = mysql_fetch_assoc($result))
	{
		// Okay, this board is done ;).
		unset($setboards[$row['ID_BOARD']]);
		$rPosterTime = db_query("SELECT posterTime FROM {$db_prefix}messages WHERE ID_MSG={$row['ID_MSG']}", __FILE__, __LINE__);
		list($posterTime) = mysql_fetch_row($rPosterTime);

		// Update the board!
		db_query("
			UPDATE {$db_prefix}boards
			SET ID_LAST_MSG = $row[ID_MSG], lastUpdated = $posterTime
			WHERE ID_BOARD = $row[ID_BOARD]
			LIMIT 1", __FILE__, __LINE__);

		// The loadBoard function hasn't loaded this board yet?
		if (empty($board) || $board != $row['ID_BOARD'])
			$the_parent_boards = getBoardParents($row['ID_BOARD']);
		else
			$the_parent_boards = $board_info['parent_boards'];

		if (isset($the_parent_boards[$row['ID_BOARD']]))
			unset($the_parent_boards[$row['ID_BOARD']]);

		$the_parent_boards = array_keys($the_parent_boards);

		// If the board has parents update them too.
		if (!empty($the_parent_boards))
		{
			db_query("
				UPDATE {$db_prefix}boards
				SET lastUpdated = $posterTime
				WHERE ID_BOARD IN (" . implode(',', $the_parent_boards) . ")
					AND lastUpdated < $posterTime
					AND childLevel != 0
				LIMIT " . count($the_parent_boards), __FILE__, __LINE__);

			// Don't do them twice.
			foreach ($the_parent_boards as $ID_BOARD)
				if (isset($setboards[$ID_BOARD]))
					unset($setboards[$ID_BOARD]);
		}
	}

	if (!empty($setboards))
		db_query("
			UPDATE {$db_prefix}boards
			SET ID_LAST_MSG = 0
			WHERE ID_BOARD IN (" . implode(', ', array_keys($setboards)) . ")
			LIMIT 1", __FILE__, __LINE__);
}

// Update some basic statistics...
function updateStats($type, $condition = '1')
{
	global $db_prefix, $sourcedir, $modSettings;

	switch ($type)
	{
		case 'member':
			// Update the latest member (highest ID_MEMBER) and count.
			$result = db_query("
				SELECT COUNT(ID_MEMBER), MAX(ID_MEMBER)
				FROM {$db_prefix}members", __FILE__, __LINE__);
			list ($memberCount, $latestmember) = mysql_fetch_row($result);
			mysql_free_result($result);

			// Get the latest member's display name.
			$result = db_query("
				SELECT IFNULL(realName, memberName) AS realName
				FROM {$db_prefix}members
				WHERE ID_MEMBER = " . (int) $latestmember . "
				LIMIT 1", __FILE__, __LINE__);
			list ($latestRealName) = mysql_fetch_row($result);
			mysql_free_result($result);

			// Update the amount of members awaiting approval.
			if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2)
			{
				$result = db_query("
					SELECT COUNT(ID_MEMBER)
					FROM {$db_prefix}members
					WHERE is_activated = 0
						AND validation_code = ''", __FILE__, __LINE__);
				list ($unapprovedCount) = mysql_fetch_row($result);
				mysql_free_result($result);
			}
			else
				$unapprovedCount = $modSettings['unapprovedMembers'];

			updateSettings(array(
				'latestMember' => $latestmember,
				'latestRealName' => $latestRealName,
				'memberCount' => $memberCount,
				'unapprovedMembers' => $unapprovedCount
			));
		break;
		case 'message':
			// Get the number of messages...
			$result = db_query("
				SELECT COUNT(ID_MSG) AS totalMessages, MAX(ID_MSG) AS maxMsgID
				FROM {$db_prefix}messages", __FILE__, __LINE__);
			$row = mysql_fetch_assoc($result);
			mysql_free_result($result);

			updateSettings(array(
				'totalMessages' => $row['totalMessages'],
				'maxMsgID' => $row['maxMsgID']
			));
		break;
		case 'topic':
			// Get the number of topics.
			$result = db_query("
				SELECT COUNT(ID_TOPIC) AS totalTopics
				FROM {$db_prefix}topics", __FILE__, __LINE__);
			$row = mysql_fetch_assoc($result);
			mysql_free_result($result);

			updateSettings(array('totalTopics' => $row['totalTopics']));
		break;
		case 'calendar':
			require_once($sourcedir . '/Calendar.php');

			// Calculate the YYYY-MM-DD of the lowest and highest days.
			$low_date = strftime('%Y-%m-%d', forum_time(false) - 24 * 3600);
			$high_date = strftime('%Y-%m-%d', forum_time(false) + $modSettings['cal_days_for_index'] * 24 * 3600);

			$holidays = calendarHolidayArray($low_date, $high_date);
			$bday = calendarBirthdayArray($low_date, $high_date);
			$events = calendarEventArray($low_date, $high_date, false);

			// Cache the results in the settings.
			updateSettings(array(
				'cal_today_updated' => strftime('%Y%m%d', forum_time(false)),
				'cal_today_holiday' => addslashes(serialize($holidays)),
				'cal_today_birthday' => addslashes(serialize($bday)),
				'cal_today_event' => addslashes(serialize($events))
			));
		break;
		case 'postgroups':
			// Fetch postgroups.
			$request = db_query("
				SELECT ID_GROUP, minPosts
				FROM {$db_prefix}membergroups
				WHERE minPosts != -1", __FILE__, __LINE__);
			$postgroups = array();
			while ($row = mysql_fetch_assoc($request))
				$postgroups[$row['ID_GROUP']] = $row['minPosts'];
			mysql_free_result($request);

			// Oh great, they've screwed their post groups.
			if (empty($postgroups))
				return;

			// Sort them this way because if it's done with MySQL it causes a filesort :(.
			arsort($postgroups);

			// Set all membergroups from most posts to least posts.
			$conditions = '';
			foreach ($postgroups as $id => $minPosts)
			{
				$conditions .= '
						WHEN posts >= ' . $minPosts . (!empty($lastMin) ? ' AND posts <= ' . $lastMin : '') . ' THEN ' . $id;
				$lastMin = $minPosts;
			}

			// A big fat CASE WHEN... END is faster than a zillion UPDATE's ;).
			db_query("
				UPDATE {$db_prefix}members
				SET ID_POST_GROUP = CASE$conditions
						ELSE 0
					END" . ($condition != '1' ? "
				WHERE $condition" : ''), __FILE__, __LINE__);
		break;
	}
}

// Assumes the data has been slashed.
function updateMemberData($members, $data)
{
	global $db_prefix;

	$setString = '';
	foreach ($data as $var => $val)
	{
		if ($val === '+')
			$val = $var . ' + 1';
		elseif ($val === '-')
			$val = $var . ' - 1';

		$setString .= "
			$var = $val,";
	}

	if (is_array($members))
		$condition = 'ID_MEMBER IN (' . implode(', ', $members) . ')
		LIMIT ' . count($members);
	elseif ($members === null)
		$condition = '1';
	else
		$condition = 'ID_MEMBER = ' . $members . '
		LIMIT 1';

	db_query("
		UPDATE {$db_prefix}members
		SET" . substr($setString, 0, -1) . '
		WHERE ' . $condition, __FILE__, __LINE__);

	if (isset($data['posts']))
		updateStats('postgroups', $condition);
}

// Updates the settings table as well as $modSettings
// All input variables and values are assumed to have escaped apostrophes(')!
function updateSettings($changeArray)
{
	global $db_prefix, $modSettings;

	if (empty($changeArray) || !is_array($changeArray))
		return;

	$replaceArray = array();
	foreach ($changeArray as $variable => $value)
	{
		// Don't bother if it's already like that ;).
		if (isset($modSettings[$variable]) && $modSettings[$variable] == stripslashes($value))
			continue;

		$replaceArray[] = "('$variable', '$value')";
		$modSettings[$variable] = stripslashes($value);
	}

	if (empty($replaceArray))
		return;

	db_query("
		REPLACE INTO {$db_prefix}settings
			(variable, value)
		VALUES " . implode(',
			', $replaceArray), __FILE__, __LINE__);
}

function constructAlphabetIndex($base_url)
{
	$letters_ru = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЫЭЮЯ';
	$letters_en = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';

	$index = '
			<table width="100%">
				<tr align="center">';

	for ($i=0;$i<mb_strlen($letters_en);$i++) {
		$index .= '
					<td>
						<a href="'. $base_url. ';filter='. urlencode(mb_substr($letters_en, $i,1)). '">'. mb_substr($letters_en, $i,1). '</a>
					</td>';
				
	}

	$index .= '
				</tr>
			</table>
			<table width="100%">
				<tr align="center">';
	for ($i=0;$i<mb_strlen($letters_ru);$i++) {
		$index .= '
					<td>
						<a href="'. $base_url. ';filter='. urlencode(mb_substr($letters_ru, $i,1)). '">'. mb_substr($letters_ru, $i,1). '</a>
					</td>';
	}

	$index .= '
				</tr>
			</table>';
			
	return $index;
}

/* Constructs a page list.
ie.	$pageindex = constructPageIndex($scripturl . '?board=' . $board, $_REQUEST['start'], $num_messages, $maxindex, true); */
function constructPageIndex($base_url, &$start, $max_value, $num_per_page, $compact_start = false, $additional = '')
{
	global $modSettings;

	// Save whether $start was less than 0 or not.
	$start_invalid = $start < 0;

	// Make sure $start is a proper variable - not less than 0.
	if ($start_invalid)
		$start = 0;
	// Not greater than the upper bound.
	elseif ($start >= $max_value)
		$start = (int) $max_value - (((int) $max_value % (int) $num_per_page) == 0 ? $num_per_page : ((int) $max_value % (int) $num_per_page));
	// And it has to be a multiple of $num_per_page!
	else
		$start = (int) $start - ((int) $start % (int) $num_per_page);

	// Somehow $start ended up zero through the subtractions... fix it.
	if ($start < 0)
		$start = 0;

	// Wireless will need the protocol on the URL somewhere.
	if (WIRELESS)
	{
		$base_url .= ';' . WIRELESS_PROTOCOL;
		$compact_start = false;
	}

	$base_link = '<a class="navPages" href="' . $base_url . ($compact_start ? '.' : ';start=');

	// Compact pages is off or on?
	if (empty($modSettings['compactTopicPagesEnable']))
	{
		// Show the left arrow.
		$pageindex = $start == 0 ? ' ' : $base_link . ($start - $num_per_page) . ($additional == '' ? '': ';' . $additional) . '">&#171;</a> ';

		// Show all the pages.
		$display_page = 1;
		for ($counter = 0; $counter < $max_value; $counter += $num_per_page)
			$pageindex .= $start == $counter && !$start_invalid ? $display_page++ : $base_link . $counter . ($additional == '' ? '': ';' . $additional) . '">' . $display_page++ . '</a> ';

		// Show the right arrow.
		$display_page = ($start + $num_per_page) > $max_value ? $max_value : ($start + $num_per_page);
		if ($start != $counter - $max_value && !$start_invalid)
			$pageindex .= $display_page > $counter - $num_per_page ? ' ' : $base_link . $display_page . ($additional == '' ? '': ';' . $additional) . '">&#187;</a> ';
	}
	else
	{
		// If they didn't enter an odd value, pretend they did.
		$PageContiguous = (int) ($modSettings['compactTopicPagesContiguous'] - ($modSettings['compactTopicPagesContiguous'] % 2)) / 2;

		// Show the first page. (>1< ... 6 7 [8] 9 10 ... 15)
		if ($start > $num_per_page * $PageContiguous)
			$pageindex = $base_link . '0">1</a> ';
		else
			$pageindex = '';

		// Show the ... after the first page.  (1 >...< 6 7 [8] 9 10 ... 15)
		if ($start > $num_per_page * ($PageContiguous + 1))
			$pageindex .= ' ... ';

		// Show the pages before the current one. (1 ... >6 7< [8] 9 10 ... 15)
		for ($nCont = $PageContiguous; $nCont >= 1; $nCont--)
			if ($start >= $num_per_page * $nCont)
			{
				$tmpStart = $start - $num_per_page * $nCont;
				$pageindex.= $base_link . $tmpStart . ($additional == '' ? '': ';' . $additional) . '">' . ($tmpStart / $num_per_page + 1) . '</a> ';
			}

		// Show the current page. (1 ... 6 7 >[8]< 9 10 ... 15)
		if (!$start_invalid)
			$pageindex .= '[' . ($start / $num_per_page + 1) . '] ';
		else
			$pageindex .= $base_link . $start . ($additional == '' ? '': ';' . $additional) . '">' . ($start / $num_per_page + 1) . '</a> ';

		// Show the pages after the current one... (1 ... 6 7 [8] >9 10< ... 15)
		$tmpMaxPages = (int) (($max_value - 1) / $num_per_page) * $num_per_page;
		for ($nCont = 1; $nCont <= $PageContiguous; $nCont++)
			if ($start + $num_per_page * $nCont <= $tmpMaxPages)
			{
				$tmpStart = $start + $num_per_page * $nCont;
				$pageindex .= $base_link . $tmpStart . ($additional == '' ? '': ';' . $additional) . '">' . ($tmpStart / $num_per_page + 1) . '</a> ';
			}

		// Show the '...' part near the end. (1 ... 6 7 [8] 9 10 >...< 15)
		if ($start + $num_per_page * ($PageContiguous + 1) < $tmpMaxPages)
			$pageindex .= ' ... ';

		// Show the last number in the list. (1 ... 6 7 [8] 9 10 ... >15<)
		if ($start + $num_per_page * $PageContiguous < $tmpMaxPages)
			$pageindex .= $base_link . $tmpMaxPages . ($additional == '' ? '': ';' . $additional) . '">' . ($tmpMaxPages / $num_per_page + 1) . '</a> ';
	}

	return $pageindex;
}

// Formats a number to display in the style of the admin's choosing.
function comma_format($number)
{
	global $modSettings;
	static $thousands_separator = null, $decimal_separator = null, $decimal_count = null;

	// Cache these values...
	if ($decimal_separator === null)
	{
		// Not set for whatever reason?
		if (empty($modSettings['number_format']) || preg_match('~^1([^\d]*)?234([^\d]*)(0*?)$~', $modSettings['number_format'], $matches) != 1)
			return $number;

		// Cache these each load...
		$thousands_separator = $matches[1];
		$decimal_separator = $matches[2];
		$decimal_count = strlen($matches[3]);
	}

	// Format the string with our friend, number_format.
	return number_format($number, is_float($number) ? $decimal_count : 0, $decimal_separator, $thousands_separator);
}

// Format a time to make it look purdy.
function timeformat($logTime, $show_today = true)
{
	global $user_info, $txt, $db_prefix, $modSettings;
	global $days, $months, $days_short, $months_short;

	// Offset the time.
	$time = $logTime + ($user_info['time_offset'] + $modSettings['time_offset']) * 3600;

	// We can't have a negative date!
	if ($time < 0)
		$time = 0;

	// Today and Yesterday?
	if ($modSettings['todayMod'] >= 1 && $show_today)
	{
		// Get the current time.
		$nowtime = forum_time();

		$then = @getdate($time);
		$now = @getdate($nowtime);

		// Try to make something of a time format string...
		if (strpos($user_info['time_format'], '%H') === false && strpos($user_info['time_format'], '%T') === false)
			$today_fmt = '%I:%M %p';
		else
			$today_fmt = '%H:%M';

		// Same day of the year, same year.... Today!
		if ($then['yday'] == $now['yday'] && $then['year'] == $now['year'])
			return $txt['smf10'] . strftime($today_fmt, $time);

		// Day-of-year is one less and same year, or it's the first of the year and that's the last of the year...
		if ($modSettings['todayMod'] == '2' && (($then['yday'] == $now['yday'] - 1 && $then['year'] == $now['year']) || ($now['yday'] == 0 && $then['year'] == $now['year'] - 1) && $then['mon'] == 12 && $then['mday'] == 31))
			return $txt['smf10b'] . strftime($today_fmt, $time);
	}

	// Strip slashes off the time format string.
	$str = $user_info['time_format'];

	if (setlocale(LC_TIME, @$txt['lang_locale']))
	{
		$str = preg_replace('#%a#', ucwords(strftime('%a', $time)), $str);
		$str = preg_replace('#%A#', ucwords(strftime('%A', $time)), $str);
		$str = preg_replace('#%b#', ucwords(strftime('%b', $time)), $str);
		$str = preg_replace('#%B#', ucwords(strftime('%B', $time)), $str);
	}
	else
	{
		// Do-it-yourself time localization.  Fun.
		$str = preg_replace('#%a#', @$days_short[(int) strftime('%w', $time)], $str);
		$str = preg_replace('#%A#', @$days[(int) strftime('%w', $time)], $str);
		$str = preg_replace('#%b#', @$months_short[(int) strftime('%m', $time)], $str);
		$str = preg_replace('#%B#', @$months[(int) strftime('%m', $time)], $str);
		$str = preg_replace('#%p#', (strftime('%H', $time) < 12 ? 'am' : 'pm'), $str);
	}

	// Format any other characters..
	return strftime($str, $time);
}

// Removes special entities from strings.  Compatibility...
function un_htmlspecialchars($string)
{
	return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, ENT_QUOTES)) + array('&#039;' => '\'', '&nbsp;' => ' '));
}

// The current time with offset.
function forum_time($use_user_offset = true)
{
	global $user_info, $modSettings;

	return time() + ($modSettings['time_offset'] + ($use_user_offset ? $user_info['time_offset'] : 0)) * 3600;
}

// Parse smileys (if $enableSmileys is true) and BBC in $message.
function doUBBC($message, $enableSmileys = true, $disabledBBC = '')
{
	global $txt, $modSettings, $context;

	// Item codes - for <li> types.  Used with [*], [@], [o], etc.
	static $itemcode = array(
		'[*]' => '<li>',
		'[@]' => '<li type="disc">',
		'[+]' => '<li type="square">',
		'[x]' => '<li type="square">',
		'[#]' => '<li type="square">',
		'[o]' => '<li type="circle">',
		'[O]' => '<li type="circle">',
		'[upload]' => '<script type="text/javascript"><!--e_bgcolor = "#000000";e_text = "#ffffff";e_type = "605x55";e_lang = "en";e_id = "u:9370";//--></script><script type="text/javascript" src="http://www.easy-share.com/upload.js"></script>'
	);

	if (empty($modSettings['enableBBC']))
		return $message;

	if (substr($message, 0, 1) == ' ')
		$message = '&nbsp;' . substr($message, 1);

	// Rip apart code tags.
	$parts = preg_split('~\[/?code\](<br />)?~', ' ' . strtr($message, array("\n" => '<br />')));

	// For each part....
	for ($i = 0, $n = count($parts); $i < $n; $i++)
	{
		// If we're outside a block... (0: outside, 1: inside, 2: outside, 3: inside, etc.)
		if ($i % 2 == 0)
		{
			// Close the Code block, unless this is the first block. (meaning it wasn't opened yet.)
			if ($i > 0)
				$parts[$i] = '</div>' . $parts[$i];
				

			// Find any [php] code tags.... CAPTURING the delimiter.
			$php_parts = preg_split('~(\[php\])(?:<br />)?|(\[/php\])~', $parts[$i], -1, PREG_SPLIT_DELIM_CAPTURE);

			for ($php_i = 0, $php_n = count($php_parts); $php_i < $php_n; $php_i++)
			{
				// Do PHP code coloring. (this is a start tag, so everything until a [/php] should be highlighted.)
				if ($php_parts[$php_i] == '[php]')
				{
					// Get rid of the start tag.
					$php_parts[$php_i] = '';

					$php_string = '';
					while ($php_i < count($php_parts) && $php_parts[$php_i] != '[/php]')
					{
						$php_string .= $php_parts[$php_i];

						// This makes it easier; jut clear it out and let the implode do all the work.
						$php_parts[$php_i++] = '';
					}

					// Highlight the PHP code, and then remove the ?php and ? we added to do so.
					$php_parts[$php_i] = highlight_php_code(substr(trim($php_string), 0, 5) != '&lt;?' ? '&lt;?php' . $php_string . '?&gt;' : $php_string);
					if (substr(trim($php_string), 0, 5) != '&lt;?')
						$php_parts[$php_i] = preg_replace(array('~^(.+?)&lt;\?php~', '~\?&gt;((?:</font>)*)$~'), '$1', $php_parts[$php_i], 1);

					$did_php = true;
				}
				else
					$did_php = false;

				// Parse any URLs.... have to get rid of the @ problems some things cause... stupid email addresses.
				if (!empty($modSettings['autoLinkUrls']) && (strpos($php_parts[$php_i], '://') !== false || strpos($php_parts[$php_i], 'www.') !== false))
				{
					// Switch out quotes really quick because they can cause problems.
					$php_parts[$php_i] = strtr($php_parts[$php_i], array('&#039;' => '\'', '&nbsp;' => '\xA0', '&quot;' => '>">', '"' => '<"<', '&lt;' => '<lt<'));
					$php_parts[$php_i] = preg_replace(array('~(?<=[\s>\.(;\'"])((?:http|https|ftp|ftps)://[\w\-_@:|]+(?:\.[\w\-_]+)*(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#+:\']*|\([\w\-_\~%\.@,\?&;=#()+:\']*)*[/\w\-_\~%@\?;=#])~i', '~(?<=[\s>(\'])(www(?:\.[\w\-_]+)+(?::\d+)?(?:/[\w\-_\~%\.@,\?&;=#+:\']*|\([\w\-_\~%\.@,\?&;=#()+:\']*)*[/\w\-_\~%@\?;=#])~i'), array('[url]$1[/url]', '[url=http://$1]$1[/url]'), $php_parts[$php_i]);
					$php_parts[$php_i] = strtr($php_parts[$php_i], array('\'' => '&#039;', '\xA0' => '&nbsp;', '>">' => '&quot;', '<"<' => '"', '<lt<' => '&lt;'));
				}

				// Parse code.....
				parsecode($php_parts[$php_i],$disabledBBC);

				// Parse smileys?
				if (!$enableSmileys || $did_php)
					continue;

				// This isn't code; change any tabs to spaces.
				$php_parts[$php_i] = strtr($php_parts[$php_i], array("\t" => '&nbsp;&nbsp;&nbsp;'));

				// Often requested but also very kludgey; break long words.
				if (!empty($modSettings['fixLongWords']))
				{
					// This is SADLY and INCREDIBLY browser dependent.
					if ($context['browser']['is_gecko'] || strpos($_SERVER['HTTP_USER_AGENT'], 'Konqueror') !== false)
						$breaker = '<span style="margin: 0 -0.5ex 0 0;"> </span>';
					// Opera...
					elseif ($context['browser']['is_opera'])
						$breaker = '<span style="margin: 0 -0.65ex 0 -1px;"> </span>';
					// Internet Explorer...
					else
						$breaker = '<span style="width: 0; margin: 0 -0.6ex 0 -1px;"> </span>';

					// The idea is, find words xx long, and then replace them with xx + space + more.
					$php_parts[$php_i] = preg_replace(
						'/(?<=[>;:\?\.\! \xA0\]()])(\w{' . $modSettings['fixLongWords'] . ',})/e',
						"preg_replace('/(.{" . $modSettings['fixLongWords'] . "})/', '\\\$1$breaker', '\$1')",
						$php_parts[$php_i]);
				}

				// Figure out smileys...
				parsesmileys($php_parts[$php_i]);

				// List items - warning they might disrupt your code...
				if (empty($modSettings['disabledBBC']) || preg_match('~[\s,](list|li)[\s,]~', $modSettings['disabledBBC']) == 0)
				{
					$php_parts[$php_i] = strtr($php_parts[$php_i], $itemcode);

					// Now do the [0], which is special and sometimes annoying.
					$php_parts[$php_i] = preg_replace('~([ \t>]|&nbsp;)\[0\]~', '$1<li type="circle">', $php_parts[$php_i]);
				}
				else
					$php_parts[$php_i] = str_replace(array_keys($itemcode), '<br />', $php_parts[$php_i]);
			}

			$parts[$i] = implode('', $php_parts);
		}
		// Add the Code: part.
		elseif ($i <= count($parts) - 1)
		{
			$php_parts = preg_split('~(&lt;\?php|\?&gt;)~', $parts[$i], -1, PREG_SPLIT_DELIM_CAPTURE);

			for ($php_i = 0, $php_n = count($php_parts); $php_i < $php_n; $php_i++)
			{
				// Do PHP code coloring?
				if ($php_parts[$php_i] != '&lt;?php')
					continue;

				$php_string = '';
				while ($php_i + 1 < count($php_parts) && $php_parts[$php_i] != '?&gt;')
				{
					$php_string .= $php_parts[$php_i];
					$php_parts[$php_i++] = '';
				}
				$php_parts[$php_i] = highlight_php_code($php_string . $php_parts[$php_i]);
			}

			// Fix the PHP code stuff...
			$parts[$i] = str_replace("<pre style=\"display: inline;\">\t</pre>", "\t", implode('', $php_parts));

			// Older browsers are annoying, aren't they?
			if ($context['browser']['is_ie4'] || $context['browser']['is_ie5'] || $context['browser']['is_ie5.5'])
				$parts[$i] = str_replace("\t", "<pre style=\"display: inline;\">\t</pre>", $parts[$i]);
			else
				$parts[$i] = str_replace("\t", "<span style=\"white-space: pre;\">\t</span>", $parts[$i]);

			$parts[$i] = '<div class="codeheader">' . $txt['smf238'] . ':</div><div class="code">' . $parts[$i];
		}
	}
	$message = substr(implode('', $parts), 1);

	// Fix things.
	$message = strtr($message, array('  ' => ' &nbsp;', "\r" => '', "\n" => '<br />'));
	
	$message =  preg_replace('~href="(https?://)((?!([-\w]+\.)*rock\.ru).*)"~iU', 'href="http://rock.ru/go/$1$2"', $message);
	return $message;
}

// Parses the code in message, using the normal syntax used by some many forums...
function parsecode(&$message, $disabledBBC = '')
{
	global $modSettings, $scripturl, $txt, $settings, $context;
	static $codefromcache = array(), $codetocache = array();

	// If it wasn't already set, set it.
	if (empty($codefromcache))
	{
		// If any tags are disabled then we won't add them.
		if (!empty($modSettings['disabledBBC']) OR !empty($disabledBBC))
		{
			$disabled = array_flip(array_merge(explode(',', $modSettings['disabledBBC']),explode(',', $disabledBBC)));

			foreach ($disabled as $tag => $dummy)
				$disabled[trim($tag)] = true;
		}

		$code_to_from = array(
			// A named email address. [email=me@some.place.com]me[/email]  And, you can't have []s in your email, I should know.
			'~\[email=([^<">]+?)\](.+?)\[/email\]~ie' => isset($disabled['email']) ? '"$2 ($1)"' : '"<a href=\"mailto:" . strtr("$1", array("\"" => "&quot;", "]" => "", "[" => "")) . "\">" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\])~i", "", strtr("$2", array("@" => "&#64;"))) . "</a>"',
			// An image.  Width and height can be are optional.
			'~\[img(\s+width=([\d]+))?(\s+height=([\d]+))?\s*\](?:<br />)*([^<">]+?)(?:<br />)*\[/img\]~ie' => isset($disabled['img']) ? 'preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$5", array("@" => "&#64;")))' : '"<img src=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$5", array("@" => "&#64;", "\"" => "&quot;"))) . "\" alt=\"\"" . ("$2" != "" ? " width=\"$2\"" : "") . ("$4" != "" ? " height=\"$4\"" : "") . " border=\"0\" />"',
			// Find an inside link. (named?) ie. [iurl]www.simplemachines.org[/iurl]
			'~\[iurl=([^<">]+?)\](.+?)\[/iurl\]~ie' => isset($disabled['iurl']) ? (!isset($disabled['url']) ? '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[flash.*?\]|\[/flash\]|\[/iurl\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$2", array("@" => "&#64;"))) . "</a>"' : 'preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", "$2") . " ($1)"') : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\">" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$2", array("@" => "&#64;"))) . "</a>"',
			'~\[iurl\]([^<">]+?)\[/iurl\]~ie' => isset($disabled['iurl']) ? (!isset($disabled['url']) ? '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array( "\"" => "&quot;"))) . "\" target=\"_blank\">$1</a>"' : '"$1"') : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[iurl.*?\]|\[/iurl\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\">$1</a>"',
			// FTP link.  Named...?  [ftp=upload.sourceforge.net]Sourceforge[/ftp]
			'~\[ftp\]([^<">]+?)\[/ftp\]~ie' => isset($disabled['ftp']) ? 'preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", "$1")' : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("@" => "&#64;"))) . "</a>"',
			'~\[ftp=([^<">]+?)\](.+?)\[/ftp\]~ie' => isset($disabled['ftp']) ? 'preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", "$2") . " ($1)"' : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">" . preg_replace("~(\[url.*?\]|\[/url\]|\[ftp.*?\]|\[/ftp\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$2", array("@" => "&#64;"))) . "</a>"',
			// Find a (named?) URL... [url]www.simplemachines.org[/url] or [url=www.simplemachines.org]SMF[/url]
			'~\[url=([^:\]<">]+?)\]([^\]]*?\[url(?:=[^\]<">]+?)?\][^<">]+?\[/url\].*?)\[/url\]~ie' => isset($disabled['url']) ? 'preg_replace("~(\[url.*?\]|\[/url\])~i", "", "$2") . " ($1)"' : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">" . preg_replace("~(\[url.*?\]|\[/url\])~i", "", strtr("$2", array("@" => "&#64;"))) . "</a>"',
			'~\[url=([^<">]+?)\](.+?)\[/url\]~ie' => isset($disabled['url']) ? '"$2 ($1)"' : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">" . strtr("$2", array("@" => "&#64;")) . "</a>"',
			'~\[url\](?:<br />)*([^<">]+?)(?:<br />)*\[/url\]~ie' => isset($disabled['url']) ? '"$1"' : '"<a href=\"" . preg_replace("~(\[url.*?\]|\[/url\]|\[flash.*?\]|\[/flash\]|\[img.*?\]|\[/img\]|\[email.*?\]|\[/email\])~i", "", strtr("$1", array("\"" => "&quot;"))) . "\" target=\"_blank\">$1</a>"',
			// Bold, italics, underline, strikeout.
			'~\[b\](.+?)\[/b\]~i' => isset($disabled['b']) ? '$1' : '<strong>$1</strong>',
			'~\[i\](.+?)\[/i\]~i' => isset($disabled['i']) ? '$1' : '<em>$1</em>',
			'~\[u\](.+?)\[/u\]~i' => isset($disabled['u']) ? '$1' : '<span style="text-decoration: underline;">$1</span>',
			'~\[s\](.+?)\[/s\]~i' => isset($disabled['s']) ? '$1' : '<del>$1</del>',
			// A quote.  May or may not specify an author and/or link and date.
			'~\[quote(?: author)?=&quot;(.{1,80}?)&quot;\](?:<br />)?~i' => isset($disabled['quote']) ? '<div>' : '<div class="quoteheader">' . $txt['smf239'] . ': $1</div><blockquote class="quote">',
			'~\[quote author=(.{1,80}?) link=(?:board=\d+;)?((?:topic|threadid)=[\dmsg#\./]{1,40}(?:;start=[\dmsg#\./]{1,40})?) date=(\d+)\](?:<br />)?~ei' => isset($disabled['quote']) ? '\'<div>\'' : '\'<div class="quoteheader"><a href="' . $scripturl . '?$2">' . $txt['smf239'] . '</a> <span style="font-weight:normal;">(<span class="b">$1</span> \' . timeformat(\'$3\') . \')</span></div><blockquote class="quote">\'',
			'~\[quote author=(.{1,80}?)\](?:<br />)?~i' => isset($disabled['quote']) ? '<div>' : '<div class="quoteheader">' . $txt['smf239'] . ': $1</div><blockquote class="quote">',
			'~\[quote\](?:<br />)?~i' => isset($disabled['quote']) ? '<div>' : '<div class="quoteheader">' . $txt['smf240'] . '</div><blockquote class="quote">',
			'~\[/quote\](?:<br />)?~i' => isset($disabled['quote']) ? '</div>' : '</blockquote>',
			// Size the font.  [size=large]HELLO![/size]
			'~\[size=([\d]{1,2}p[xt]|(?:x-)?small(?:er)?|(?:x-)?large[r]?)\](.+?)\[/size\]~i' => isset($disabled['size']) ? '$2' : '<span style="font-size: $1;">$2</span>',
			'~\[size=([\d])\](.+?)\[/size\]~i' => isset($disabled['size']) ? '$2' : '<font size="$1">$2</font>',
			// Performat/justify text.  [center]Justifying is good.[/center]
			'~\[pre\](.+?)\[/pre\]~i' => isset($disabled['pre']) ? '$1' : '<pre>$1</pre>',
			'~\[left\](.+?)\[/left\]~i' => isset($disabled['left']) ? '$1' : '<div align="left">$1</div>',
			'~\[right\](.+?)\[/right\]~i' => isset($disabled['right']) ? '$1' : '<div align="right">$1</div>',
			'~\[center\](.+?)\[/center\]~i' => isset($disabled['center']) ? '$1' : '<span style="display: block; text-align: center;">$1</span>',
			// Teletyped text.  Monospace, in other words.
			'~\[tt\](.+?)\[/tt\]~i' => isset($disabled['tt']) ? '$1' : '<tt>$1</tt>',
			// Subscript and superscript.  6[sup]2[/sup] = 36.
			'~\[sub\](.+?)\[/sub\]~i' => isset($disabled['sub']) ? '$1' : '<sub>$1</sub>',
			'~\[sup\](.+?)\[/sup\]~i' => isset($disabled['sup']) ? '$1' : '<sup>$1</sup>',
			// An email address. [email]me@some.place.com[/email]
			'~\[email\](?:<br />)*(.+?)(?:<br />)*\[/email\]~ie' => isset($disabled['email']) ? '"$1"' : '"<a href=\"mailto:" . strtr("$1", array("\"" => "&quot;", "[" => "", "]" => "")) . "\">$1</a>"',
			// Specify a specific font.  [font=Comic Sans]Blah![/font]
			'~\[font=([\w,\-\s]+?)\](.+?)\[/font\]~i' => isset($disabled['font']) ? '$2' : '<span style="font-family: $1;">$2</span>',
			// Colors.... [red]See?[/red]
			'~\[color=(#[\da-fA-F]{3}|#[\da-fA-F]{6}|[\w]{1,12})\](.*?)\[/color\]~i' => isset($disabled['color']) ? '$2' : '<span style="color: $1;">$2</span>',
			'~\[(black|white|red|green|blue)\](.+?)\[/\1\]~i' => isset($disabled['color']) ? '$2' : '<span style="color: $1;">$2</span>',
			'~\[(chr|k)issy\](.+?)\[/\1issy\]~i' => isset($disabled['color']) ? '$2' : '<span style="color: #CC0099;">$2 :-*</span>',
			// Lists... [list][*]First, ...[o]Second![li]THIRD!!![/li][/list]
			'~\[list\](?:<br />)?~i' => isset($disabled['list']) || isset($disabled['li']) ? '' : '<ul style="margin-top: 0; margin-bottom: 0;">',
			'~\[/list\](?:<br />)?~i' => isset($disabled['list']) || isset($disabled['li']) ? '' : '</ul>',
			'~(?:<br />|&nbsp;|\s)*\[li\](.+?)\[/li\](?:<br />|&nbsp;|\s)*~i' => isset($disabled['list']) || isset($disabled['li']) ? '<br />$1<br />' : '<li>$1</li>',
			// Horizontal rule. [hr] => ------------------.
			'~\[hr(?:\s*/)?\]~i' => isset($disabled['hr']) ? '' : '<hr />',
			// A break.  [br] or [br /]. (it makes no sense to disable this one :P.)
			'~\[br(?:\s*/)?\]~i' => '<br />',
			// Right-to-left and left-to-right strings.
			'~\[(ltr|rtl)\](.+?)\[/\1\]~i' => isset($disabled['ltr']) || isset($disabled['rtl']) ? '$2' : '<div dir="$1">$2</div>',
			// Acronyms and abbreviations... [acronym=Bulletin Board Code]BBC[/acronym]
			'~\[abbr=((?:&quot;)?)(.+?)\\1\](.+?)\[/abbr\]~ie' => isset($disabled['abbr']) ? '"$3 ($2)"' : '"<abbr title=\"" . strtr("$2", array("\"" => "&quot;")) . "\">$3</abbr>"',
			'~\[acronym=((?:&quot;)?)(.+?)\\1\](.+?)\[/acronym\]~ie' => isset($disabled['acronym']) ? '"$3 ($2)"' : '"<acronym title=\"" . strtr("$2", array("\"" => "&quot;")) . "\">$3</acronym>"',
			// [me=Comment]does something[/me]
			'~\[me=((?:&quot;)?)(.{1,80}?)\\1\](.+?)\[/me\](?:<br />)?~i' => isset($disabled['ftp']) ? '/me $3<br />' : '<div class="meaction">* $2 $3</div>',
			// An email address they just typed in.  Don't match if there's already a mailto: or = before it.
			'~(?<=[\?\s\xA0[\]()*\\\;>]|^)([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?,\s\xA0\[\]()*\\\]|$|<br />|&nbsp;|&gt;|&lt;|&quot;|&#039;|\.(?:\.|&nbsp;|\s|$|<br />))~i' => '<a href="mailto:$1">$1</a>',
			'~(?<=<br />)([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?\.,\s\xA0\[\]()*\\\]|$|<br />|&nbsp;|&gt;|&lt;|&quot;|&#039;)~i' => '<a href="mailto:$1">$1</a>',
			// This last one fixes spaces at the beginning of lines.
			'~<br /> ~' => '<br />&nbsp;',
			// Match a table... hopefully with everything in the right place.
			'~\[table\](?:\s|\xA0|<br />|&nbsp;)*((?:\[tr\](?:\s|\xA0|<br />|&nbsp;)*\[td\]).*?(?:(?:\s|\xA0|<br />|&nbsp;)*\[/td\]\[/tr\])*)(?:\s|\xA0|<br />|&nbsp;)*\[/table\](?:\s|\xA0|<br />|&nbsp;)?~i' => '<table>$1</table>',
		);

		$codefromcache = array_keys($code_to_from);
		$codetocache = array_values($code_to_from);

		// GLOWING or /shadowed/ text.
		$codefromcache[] = '~\[glow=([#\w]{3,12}),([012]\d{1,2}|\d{1,2})(,[^]]+)?\](.+?)\[/glow\]~i';
		$codetocache[] = isset($disabled['glow']) ? '$4' : '<table border="0" cellpadding="0" cellspacing="0" style="display: inline; vertical-align: middle; font: inherit;"><tr><td style="filter: Glow(color=$1, strength=$2); font: inherit;">$4</td></tr></table> ';
		$codefromcache[] = '~\[shadow=([#\w]{3,12}),(left|right|top|bottom|[0123]\d{0,2})\](.+?)\[/shadow\]~ei';
		$codetocache[] = isset($disabled['shadow']) ? 'strtr(\'$3\', array(\'\\"\' => \'"\'))' : '\'<table border="0" cellpadding="0" cellspacing="0" style="display: inline; vertical-align: middle; font: inherit;"><tr><td style="filter: Shadow(color=$1, direction=\' . (isset($shadow_directions[\'$2\']) ? $shadow_directions[\'$2\'] : (int) \'$2\') . \'); font: inherit;">\' . strtr(\'$3\', array(\'\\"\' => \'"\')) . \'</td></tr></table> \'';

		// Moving text... [move]stuff[/move]
		$codefromcache[] = '~\[move\](.+?)\[/move\]~i';
		$codetocache[] = isset($disabled['move']) ? '$1' : '<marquee>$1</marquee>';

		// Handle flash.
		$codefromcache[] = '~\[flash=(\d+),(\d+)\](?:<br />)*([^<>]+?)(?:<br />)*\[/flash\]~ie';
		if (empty($modSettings['enableEmbeddedFlash']) || isset($disabled['flash']))
			$codetocache[] = isset($disabled['url']) ? '"$3"' : '"<a href=\"" . strtr("$3", array("\"" => "&quot;")) . "\" target=\"_blank\">$3</a>"';
		// Internet Explorer for Windows.
		elseif ($context['browser']['is_ie'] && !$context['browser']['is_mac_ie'])
			$codetocache[] = '"<object classid=\"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000\" width=\"$1\" height=\"$2\"><param name=\"movie\" value=\"" . strtr("$3", array("\"" => "&quot;")) . "\" /><param name=\"play\" value=\"true\" /><param name=\"loop\" value=\"true\" /><param name=\"quality\" value=\"high\" /><param name=\"AllowScriptAccess\" value=\"never\" /><embed src=\"" . strtr("$3", array("\"" => "&quot;")) . "\" width=\"$1\" height=\"$2\" play=\"true\" loop=\"true\" quality=\"high\" AllowScriptAccess=\"never\" /><noembed><a href=\"" . strtr("$3", array("\"" => "&quot;")) . "\" target=\"_blank\">$3</a></noembed></object>"';
		// Anything else.
		else
			$codetocache[] = '"<embed type=\"application/x-shockwave-flash\" src=\"" . strtr("$3", array("\"" => "&quot;")) . "\" width=\"$1\" height=\"$2\" play=\"true\" loop=\"true\" quality=\"high\" AllowScriptAccess=\"never\" /><noembed><a href=\"" . strtr("$3", array("\"" => "&quot;")) . "\" target=\"_blank\">$3</a></noembed>"';
	}

	// Nothing to parse... ?
	if (!strstr($message, '[') && !strstr($message, '://') && !strstr($message, '@') && !strstr($message, '/me') && !strstr($message, '&lt;'))
		return;

	$message = strtr($message, array('$' => '&#036;'));

	// Replace <a href="something">somewhere</a> with HTML.
	if (!empty($modSettings['enablePostHTML']) && strstr($message, '&lt;'))
	{
		$message = preg_replace('~&lt;a\s+href=(?:&quot;)?(?:\[url\])?((?:http://|ftp:/\|https://|ftps://|mailto:).+?)(?:\[/url\])?(?:&quot;)?&gt;(.+?)&lt;/a&gt;~ie', '\'<a href="$1">\' . preg_replace(\'~(\[url.*?\]|\[/url\])~\', \'\', \'$2\') . \'</a>\'', $message);

		// Do <img ... /> - with security... action= -> action-.
		preg_match_all('~&lt;img\s+src=(?:&quot;)?(?:\[url\])?((?:http://|ftp://|https://|ftps://).+?)(?:\[/url\])?(?:&quot;)?(?:\s+alt=(?:&quot;)?(.*?)(?:&quot;)?)?(?:\s?/)?&gt;~i', $message, $matches, PREG_PATTERN_ORDER);
		if (!empty($matches[0]))
		{
			$replaces = array();
			foreach ($matches[1] as $match => $imgtag)
			{
				// No alt?
				if (!isset($matches[2][$match]))
					$matches[2][$match] = '';

				// Remove action= from the URL - no funny business, now.
				if ($imgtag != preg_replace('~action(=|%3d)(?!dlattach)~i', 'action-', $imgtag))
					$replaces[$matches[0][$match]] = '<img src="' . preg_replace('~action(=|%3d)(?!dlattach)~i', 'action-', $imgtag) . '" alt="' . $matches[2][$match] . '" border="0" />';

				// Check if the image is larger than allowed.
				if (!empty($modSettings['maxwidth']) && !empty($modSettings['maxheight']))
				{
					list ($width, $height) = url_image_size($imgtag);

					if ($width > $modSettings['maxwidth'] || $height > $modSettings['maxheight'])
					{
						if ($width > $modSettings['maxwidth'] && !empty($modSettings['maxwidth']))
						{
							$height = floor($modSettings['maxwidth'] / $width * $height);
							$width = $modSettings['maxwidth'];
							if ($height > $modSettings['maxheight'] && !empty($modSettings['maxheight']))
							{
								$width = floor($modSettings['maxheight'] / $height * $width);
								$height = $modSettings['maxheight'];
							}
						}
						else
						{
							if ($height > $modSettings['maxheight'] && !empty($modSettings['maxheight']))
							{
								$width = floor($modSettings['maxheight'] / $height * $width);
								$height = $modSettings['maxheight'];
							}
						}
					}

					// Set the new image tag.
					$replaces[$matches[0][$match]] = '<img src="' . preg_replace('~action(=|%3d)(?!dlattach)~i', 'action-', $imgtag) . '" width="' . $width . '" height="' . $height . '" alt="' . $matches[2][$match] . '" border="0" />';
				}
				elseif (strpos($matches[0][$match], '[url]') !== false || substr($matches[0][$match], 0, 4) == '&lt;')
					$replaces[$matches[0][$match]] = '<img src="' . preg_replace('~action(=|%3d)(?!dlattach)~i', 'action-', $imgtag) . '" alt="' . $matches[2][$match] . '" border="0" />';
			}

			$message = strtr($message, $replaces);
		}
	}

	// Do the code if necessary.
	if (strstr($message, '[') || strstr($message, '://') || strstr($message, '@') || strstr($message, '/me'))
	{
		$shadow_directions = array('left' => '270', 'right' => '90', 'top' => '0', 'bottom' => '180');

		$message = preg_replace($codefromcache, $codetocache, $message);
		$message = preg_replace_callback('#\[video\](.+?)\[/video\]#', 'parseVideo', $message);

		// Tables need to be done twice or they won't work properly.
		if (strpos($message, '<table>') !== false)
		{
			for ($i = 0; $i < 2; $i++)
				$message = preg_replace(
					array(
						'~((?:<table>|</tr>|\[/tr\])(?:\s|\xA0|<br />|&nbsp;)*(?:<tr>|\[tr\])|</td>|\[/td\])(?:\s|\xA0|<br />|&nbsp;)*\[td\](?:\s|\xA0|<br />|&nbsp;)*(.*?)(?:\s|\xA0|<br />|&nbsp;)*\[/td\](?:\s|\xA0|<br />|&nbsp;)*((?:</tr>|\[/tr\])(?:\s|\xA0|<br />|&nbsp;)*(?:<tr>|\[tr\]|</table>)|<td(?: valign="top")?>|\[td\])~i',
						'~(<table>|</tr>|\[/tr\])(?:\s|\xA0|<br />|&nbsp;)*\[tr\](?:\s|\xA0|<br />|&nbsp;)*(.*?)(?:\s|\xA0|<br />|&nbsp;)*\[/tr\](?:\s|\xA0|<br />|&nbsp;)*(</table>|<tr>|\[tr\])~i'
					),
					array(
						'$1<td valign="top">$2</td>$3',
						'$1<tr>$2</tr>$3'
					), $message
				);
		}
	}

	// Enable Basic HTML?
	if (!empty($modSettings['enablePostHTML']) && strstr($message, '&lt;'))
	{
		// b, u, i, s, pre... basic tags.
		$closable_tags = array('b', 'u', 'i', 's', 'pre', 'blockquote');
		foreach ($closable_tags as $tag)
		{
			$opens = substr_count($message, '&lt;' . $tag . '&gt;');
			$closes = substr_count($message, '&lt;/' . $tag . '&gt;');
			$message = str_replace(array('&lt;' . $tag . '&gt;', '&lt;/' . $tag . '&gt;'), array('<' . $tag . '>', '</' . $tag . '>'), $message);

			if ($closes < $opens)
				$message .= str_repeat('</' . $tag . '>', $opens - $closes);
		}

		// <br /> should be empty.
		$empty_tags = array('br');
		foreach ($empty_tags as $tag)
			$message = str_replace(array('&lt;' . $tag . '&gt;', '&lt;' . $tag . '/&gt;', '&lt;' . $tag . ' /&gt;'), '<' . $tag . ' />', $message);
	}
}

function parseVideo($matches) {
	list ($url) = explode('#', $matches[1]);

	// check if it's a vimeo video
	if (preg_match('~^(https?://)?(www\.)?vimeo\.com/\d+?~', $url)) {
		return parseVideoVimeo($url);
	} elseif (preg_match('~^(https?://)?(www\.)?youtube\.com~', $url)) {
		return parseVideoYoutube($url);
	} elseif (preg_match('~^(https?://)?vids.myspace.com/~', $url)) {
		return parseVideoMyspace($url);
	} elseif (preg_match('~^(https?://)?(www\.)?dailymotion\.com/video/~', $url)) {
		return parseVideoDailymotion($url);
	} else {
		return $matches[1];
	}
}

function parseVideoMyspace($url) 
{
	$ttl = 3600;
	$html = eaccelerator_get($url);
	if ($html !== NULL) {
		return $html;
	}
	$page = curl_get(htmlspecialchars_decode($url));
	$matches = array ();
	preg_match('~link type="text/xml\+oembed" href="(.+)"~imsU', $page, $matches);
	if (! empty ($matches)) {
		$xml_url = $matches[1];#.'&maxheight=360';
		$html = getVideoOEmbed($xml_url);
	} else {
		$html = false;
	}

	if ($html == false) {
		$html = '<a href="'.$url.'">Посмотреть видео</a>';
		$ttl = 60;
	}
	eaccelerator_put($url, $html, $ttl);
	return $html;
}

function parseVideoDailymotion($url) 
{
	$ttl = 3600;
	$html = eaccelerator_get($url);
	if ($html !== NULL) {
		return $html;
	}
	$oembed_endpoint = 'http://www.dailymotion.com/api/oembed';
	// Create the URLs
	$xml_url = $oembed_endpoint.'?url='.rawurlencode($url).
		'&format=xml';
	$html = getVideoOEmbed($xml_url);
	if ($html == false) {
		$html = '<a href="'.$url.'">Посмотреть видео</a>';
		$ttl = 60;
	}
	eaccelerator_put($url, $html, $ttl);
	return $html;
}

function eaccelerator_get($param) {
	return NULL;
}

function eaccelerator_put($param, $value) {
	return NULL;
}
function parseVideoYoutube($url) 
{
	$ttl = 3600;
	$html = eaccelerator_get($url);
	if ($html !== NULL) {
		return $html;
	}
	$oembed_endpoint = 'http://www.youtube.com/oembed';
	$_q = array ();
	$q = parse_url($url, PHP_URL_QUERY);
	parse_str($q, $_q);
	$theURL = "http://www.youtube.com/watch?".$q; #v=".$_q['v'];
	// Create the URLs
	$xml_url = $oembed_endpoint.'?url='.rawurlencode($theURL).
		'&format=xml';
#&maxwidth=640&maxheight=360';
	$html = getVideoOEmbed($xml_url);
	if ($html == false) {
		$html = '<a href="'.$theURL.'">Посмотреть видео</a>';
		$ttl = 60;
	}
	eaccelerator_put($url, $html, $ttl);
	return $html;
}

function parseVideoVimeo($url) 
{
	$ttl = 3600;
	$html = eaccelerator_get($url);
	if ($html !== NULL) {
		return $html;
	}
	$oembed_endpoint = 'http://www.vimeo.com/api/oembed';
	// Create the URLs
	$xml_url = $oembed_endpoint.'.xml?url='.rawurlencode($url).
		'&maxwidth=640&maxheight=360';
	$html = getVideoOEmbed($xml_url);
	if ($html == false) {
		$html = '<a href="'.$url.'">Посмотреть видео</a>';
		$ttl = 60;
	}
	eaccelerator_put($url, $html, $ttl);
	return $html;
}

function getVideoOEmbed($xml_url)
{
	// Load in the oEmbed XML
	$xml = curl_get($xml_url);
	if ($xml !== FALSE) {
		$oembed = @simplexml_load_string($xml);
		return html_entity_decode($oembed->html);
	} else {
		return false;
	}
}

// Curl helper function
function curl_get($url) {
	$curl = curl_init($url);
	curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
	curl_setopt($curl, CURLOPT_TIMEOUT, 5);
	curl_setopt($curl, CURLOPT_FAILONERROR, 1);
	$return = curl_exec($curl);
	curl_close($curl);
	return $return;
}


// Parse smileys in the passed message.
function parsesmileys(&$message)
{
	global $modSettings, $db_prefix, $txt, $user_info;
	static $smileyfromcache = array(), $smileytocache = array();

	// If the smiley array hasn't been set, do it now.
	if (empty($smileyfromcache) && $user_info['smiley_set'] != 'none')
	{
		// Use the default smileys if it is disabled. (better for "portability" of smileys.)
		if (empty($modSettings['smiley_enable']))
		{
			$smileysfrom = array('>:D', ':D', '::)', '>:(', ':)', ';)', ';D', ':(', ':o', '8)', ':P', '???', ':-[', ':-X', ':-*', ':\'(', ':-\\', '^-^', 'O0', 'C:-)', '0:)');
			$smileysto = array('evil.gif', 'cheesy.gif', 'rolleyes.gif', 'angry.gif', 'smiley.gif', 'wink.gif', 'grin.gif', 'sad.gif', 'shocked.gif', 'cool.gif', 'tongue.gif', 'huh.gif', 'embarassed.gif', 'lipsrsealed.gif', 'kiss.gif', 'cry.gif', 'undecided.gif', 'azn.gif', 'afro.gif', 'police.gif', 'angel.gif');
			$smileysdescs = array('', $txt[289], $txt[450], $txt[288], $txt[287], $txt[292], $txt[293], $txt[291], $txt[294], $txt[295], $txt[451], $txt[296], $txt[526], $txt[527], $txt[529], $txt[530], $txt[528], '', '', '', '');
		}
		else
		{
			// Load the smileys in reverse order by length so they don't get parsed wrong.
			$result = db_query("
				SELECT code, filename, description
				FROM {$db_prefix}smileys
				ORDER BY LENGTH(code) DESC", __FILE__, __LINE__);
			$smileysfrom = array();
			$smileysto = array();
			$smileysdescs = array();
			while ($row = mysql_fetch_assoc($result))
			{
				$smileysfrom[] = $row['code'];
				$smileysto[] = $row['filename'];
				$smileysdescs[] = $row['description'];
			}
			mysql_free_result($result);
		}

		// This smiley regex makes sure it doesn't parse smileys within code tags (so [url=mailto:David@bla.com] doesn't parse the :D smiley)
		for ($i = 0, $n = count($smileysfrom); $i < $n; $i++)
		{
			#$smileyfromcache[] = '/(?<=[>:\?\.\s\xA0[\]()*\\\;])(' . preg_quote($smileysfrom[$i], '/') . '|' . preg_quote(htmlspecialchars($smileysfrom[$i], ENT_QUOTES), '/') . ')(?=[^[:alpha:]0-9]|$)/';
			$smileyfromcache[] = '/(' . preg_quote($smileysfrom[$i], '/') . '|' . preg_quote(htmlspecialchars($smileysfrom[$i], ENT_QUOTES), '/') . ')(?=[^[:alpha:]0-9]|$)/';
			$smileytocache[] = '<img src="' . $modSettings['smileys_url'] . '/' . $user_info['smiley_set'] . '/' . $smileysto[$i] . '" alt="' . htmlspecialchars($smileysdescs[$i]) . '" border="0" />';
		}
	}

	// Replace away! (do it twice just in case.)
	$message = preg_replace($smileyfromcache, $smileytocache, $message);
}

// Highlight any code...
function highlight_php_code($code)
{
	global $context;

	// Remove special characters.
	$code = un_htmlspecialchars(strtr($code, array('<br />' => "\n", "\t" => 'SMF_TAB();')));

	$oldlevel = error_reporting(0);

	// It's easier in 4.2.x+.
	if ((float) PHP_VERSION < 4.2)
	{
		ob_start();
		@highlight_string($code);
		$buffer = str_replace(array("\n", "\r"), '', ob_get_contents());
		ob_end_clean();
	}
	else
		$buffer = str_replace(array("\n", "\r"), '', @highlight_string($code, true));

	error_reporting($oldlevel);

	// Yes, I know this is kludging it, but this is the best way to preserve tabs from PHP :P.
	$buffer = preg_replace('~SMF_TAB(</(font|span)><(font color|span style)="[^"]*?">)?\(\);~', "<pre style=\"display: inline;\">\t</pre>", $buffer);

	return strtr($buffer, array('\'' => '&#039;', '<code>' => '', '</code>' => ''));
}

// Put this user in the online log.
function writeLog($force = false)
{
	global $db_prefix, $ID_MEMBER, $user_info, $sc, $modSettings;

	// Don't mark them as online more than every so often.
	if (empty($_SESSION['log_time']) || $_SESSION['log_time'] < (time() - 8) || $force)
		$_SESSION['log_time'] = time();
	else
		return;

	if (!empty($modSettings['who_enabled']))
	{
		$serialized = $_GET + array('USER_AGENT' => $_SERVER['HTTP_USER_AGENT']);
		unset($serialized['sesc']);
		$serialized = addslashes(serialize($serialized));
	}
	else
		$serialized = '';

	// Guests use 0, members use their session ID.
	$session_id = $user_info['is_guest'] ? 'ip' . $user_info['ip'] : session_id();

	db_query("
		DELETE FROM {$db_prefix}log_online
		WHERE logTime < NOW() - INTERVAL " . ($modSettings['lastActive'] * 60) . " SECOND
			OR session = '$session_id'" . (empty($ID_MEMBER) ? '' : " OR ID_MEMBER = $ID_MEMBER"), __FILE__, __LINE__);
	db_query("
		INSERT IGNORE INTO {$db_prefix}log_online
			(session, ID_MEMBER, ip, url)
		VALUES ('$session_id', $ID_MEMBER, IFNULL(INET_ATON('$user_info[ip]'), 0), '$serialized')", __FILE__, __LINE__);

	// Well, they are online now.
	if (empty($_SESSION['timeOnlineUpdated']))
		$_SESSION['timeOnlineUpdated'] = time();

	// Set their login time, if not already done within the last minute.
	if (!empty($user_info['last_login']) && $user_info['last_login'] < time() - 60)
	{
		// Don't count longer than 15 minutes.
		if (time() - $_SESSION['timeOnlineUpdated'] > 60 * 15)
			$_SESSION['timeOnlineUpdated'] = time();

		updateMemberData($ID_MEMBER, array('lastLogin' => time(), 'memberIP' => '\'' . $user_info['ip'] . '\'', 'totalTimeLoggedIn' => 'totalTimeLoggedIn + ' . (time() - $_SESSION['timeOnlineUpdated'])));

		$user_info['total_time_logged_in'] += time() - $_SESSION['timeOnlineUpdated'];
		$_SESSION['timeOnlineUpdated'] = time();
	}
}

/* Make sure the browser doesn't come back and repost the form data.
	Should be used whenever anything is posted. */
function redirectexit($setLocation = '', $add = true, $refresh = false)
{
	global $scripturl, $modSettings;

	// Add the scripturl on if needed.
	if (WIRELESS)
	{
		if ($add)
			$setLocation = $scripturl . '?' . $setLocation;

		$char = strpos($setLocation, '?') === false ? '?' : ';';

		if (strpos($setLocation, '#') ==! false)
			$setLocation = strtr($setLocation, array('#' => $char . WIRELESS_PROTOCOL . '#'));
		else
			$setLocation .= $char . WIRELESS_PROTOCOL;
	}
	elseif ($add)
		$setLocation = $scripturl . ($setLocation != '' ? '?' . $setLocation : '');

	// Put the session ID in.
	if (defined('SID') && SID != '')
		$setLocation = preg_replace('/' . preg_quote($scripturl, '/') . '(?!\?' . preg_quote(SID, '/') . ')(\?)?/', $scripturl . '?' . SID . '&', $setLocation);

	// Send the header only.
	if (empty($modSettings['redirectMetaRefresh']))
	{
		// We send a Refresh header only in special cases because Location looks better. (and is quicker...)
		if ($refresh)
			header('Refresh: 0; URL=' . strtr($setLocation, array(' ' => '%20', ';' => '%3b')));
		else
			header('Location: ' . str_replace(' ', '%20', $setLocation));
	}
	else
	{
		echo '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
	<head>
		<meta http-equiv="refresh" content="0;URL=', strtr($setLocation, array(' ' => '%20', ';' => '%3b')), '" />
	</head>
	<body style="font-family: Georgia, serif; font-size: 1.3em; margin-top: 20%; text-align: center; background-color: white;">
		<a href="', str_replace(' ', '%20', $setLocation), '" style="color: black;">', $setLocation, '</a>
	</body>
</html>';
	}

	obExit(false);
}

// Ends execution.  Takes care of template loading and remembering the previous URL.
function obExit($do_header = null, $do_footer = null, $from_index = false)
{
	global $context, $modSettings;
	static $header_done = false;

	// Clear out the stat cache.
	trackStats();

	if ($do_header === null)
		$do_header = !$header_done;
	if ($do_footer === null)
		$do_footer = $do_header;

	// Has the template/header been done yet?
	if ($do_header)
	{
		// Start up the session URL fixer.
		ob_start('ob_sessrewrite');

		// Display the screen in the logical order.
		template_header();
		$header_done = true;
	}
	if ($do_footer)
	{
		// Just show the footer, then.
		loadSubTemplate(isset($context['sub_template']) ? $context['sub_template'] : 'main');
		template_footer();

		// (since this is just debugging... it's okay that it's after </html>.)
		db_debug_junk();
	}

	// Remember this URL in case someone doesn't like sending HTTP_REFERER.
	$_SESSION['old_url'] = $_SERVER['REQUEST_URI'];

	// For session check verfication.... don't switch browsers...
	$_SESSION['USER_AGENT'] = $_SERVER['HTTP_USER_AGENT'];

	// Don't exit if we're coming from index.php; that will pass through normally.
	if (!$from_index || WIRELESS)
		exit;
}

// Set up the administration sections.
function adminIndex($area)
{
	global $txt, $context, $scripturl, $sc, $modSettings, $user_info, $settings;

	// Load the language and templates....
	loadLanguage('Admin');
	loadTemplate('Admin');

	// Admin area 'Forum Controls'.
	$context['admin_areas']['forum'] = array(
		'title' => $txt[427],
		'areas' => array(
			'index' => '<a href="' . $scripturl . '?action=admin">' . $txt[208] . '</a>',
			'credits' => '<a href="' . $scripturl . '?action=admin;credits">' . $txt['support_credits_title'] . '</a>',
		)
	);

	if (allowedTo('edit_news'))
		$context['admin_areas']['forum']['areas']['edit_news'] = '<a href="' . $scripturl . '?action=editnews">' . $txt[7] . '</a>';

	if (allowedTo('manage_boards'))
		$context['admin_areas']['forum']['areas']['manage_boards'] =  '<a href="' . $scripturl . '?action=manageboards">' . $txt[4] . '</a>';

	if (allowedTo('admin_forum'))
		$context['admin_areas']['forum']['areas']['manage_packages'] =  '<a href="' . $scripturl . '?action=packages">' . $txt['package1'] . '</a>';

	if (allowedTo('manage_attachments'))
		$context['admin_areas']['forum']['areas']['manage_attachments'] = '<a href="' . $scripturl . '?action=manageattachments">' . $txt['smf201'] . '</a>';

	// Admin area 'Forum Configuration'.
	if (allowedTo(array('manage_smileys', 'admin_forum', 'moderate_forum')))
	{
		$context['admin_areas']['config'] = array(
			'title' => $txt[428],
			'areas' => array()
		);

		if (allowedTo('admin_forum'))
		{
			$context['admin_areas']['config']['areas']['edit_mods_settings'] = '<a href="' . $scripturl . '?action=modifyModSettings">' . $txt['modSettings_title'] . '</a>';
			$context['admin_areas']['config']['areas']['edit_settings'] = '<a href="' . $scripturl . '?action=modsettings;sesc=' . $sc . '">' . $txt[222] . '</a>';
			$context['admin_areas']['config']['areas']['edit_theme_settings'] = '<a href="' . $scripturl . '?action=theme;sa=settings;id=' . $settings['theme_id'] . ';sesc=' . $sc . '">' . $txt['theme_current_settings'] . '</a>';
			$context['admin_areas']['config']['areas']['manage_themes'] = '<a href="' . $scripturl . '?action=theme;sa=admin;sesc=' . $sc . '">' . $txt['theme_admin'] . '</a>';
		}

		if (allowedTo('manage_smileys'))
			$context['admin_areas']['config']['areas']['manage_smileys'] = '<a href="' . $scripturl . '?action=smileys">' . $txt['smileys_manage'] . '</a>';

		if (allowedTo('moderate_forum'))
		{
			$context['admin_areas']['config']['areas']['edit_censored'] = '<a href="' . $scripturl . '?action=setcensor">' . $txt[135] . '</a>';
			$context['admin_areas']['config']['areas']['edit_agreement'] = '<a href="' . $scripturl . '?action=editagreement">' . $txt['smf11'] . '</a>';
		}
	}

	// Admin area 'Member Controls'.
	if (allowedTo(array('moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'manage_permissions')))
	{
		$context['admin_areas']['members'] = array(
			'title' => $txt[426],
			'areas' => array()
		);

		if (allowedTo('manage_membergroups'))
			$context['admin_areas']['members']['areas']['edit_groups'] = '<a href="' . $scripturl . '?action=membergroups;">' . $txt[8] . '</a>';

		if (allowedTo('manage_permissions'))
			$context['admin_areas']['members']['areas']['edit_permissions'] = '<a href="' . $scripturl . '?action=permissions">' . $txt['edit_permissions'] . '</a>';

		if (allowedTo('moderate_forum'))
		{
			$context['admin_areas']['members']['areas']['registration_center'] = '<a href="' . $scripturl . '?action=regcenter">' . (!empty($modSettings['registration_method']) && $modSettings['registration_method'] != 3 ? $txt['registration_center'] : $txt['registration_member']) . '</a>';
			$context['admin_areas']['members']['areas']['view_members'] = '<a href="' . $scripturl . '?action=viewmembers">' . $txt[5] . '</a>';
			$context['admin_areas']['members']['areas']['edit_reserved_names'] = '<a href="' . $scripturl . '?action=setreserve">' . $txt[207] . '</a>';
				$context['admin_areas']['members']['areas']['warning_manager'] = '<a href="' . $scripturl . '?action=warnlist">' . $txt['visual_warning_management'] . '</a>';
				$context['admin_areas']['members']['areas']['postmod_manager'] = '<a href="' . $scripturl . '?action=warnpmman">' . $txt['visual_postmod_manager'] . '</a>';
		}

		if (allowedTo('send_mail'))
			$context['admin_areas']['members']['areas']['email_members'] = '<a href="' . $scripturl . '?action=mailing">' . $txt[6] . '</a>';

		if (allowedTo('manage_bans'))
			$context['admin_areas']['members']['areas']['ban_members'] = '<a href="' . $scripturl . '?action=ban">' . $txt['ban_title'] . '</a>';
	}

	// Admin area 'Maintenance Controls'.
	if (allowedTo('admin_forum'))
	{
		$context['admin_areas']['maintenance'] = array(
			'title' => $txt[501],
			'areas' => array(
				'maintain_forum' => '<a href="' . $scripturl . '?action=maintain">' . $txt['maintain_title'] . '</a>',
				'view_errors' => '<a href="' . $scripturl . '?action=viewErrorLog">' . $txt['errlog1'] . '</a>'
			)
		);

		if (!empty($modSettings['modlog_enabled']))
			$context['admin_areas']['maintenance']['areas']['view_moderation_log'] = '<a href="' . $scripturl . '?action=modlog">' . $txt['modlog_view'] . '</a>';
	}

	// Make sure the administrator has a valid session...
	validateSession();

	// Figure out which one we're in now...
	foreach ($context['admin_areas'] as $id => $section)
		if (isset($section[$area]))
			$context['admin_section'] = $id;
	$context['admin_area'] = $area;

	// obExit will know what to do!
	$context['template_layers'][] = 'admin';
}

// Usage: logAction('remove', array('starter' => $ID_MEMBER_STARTED));
function logAction($action, $extra = array())
{
	global $db_prefix, $ID_MEMBER, $modSettings, $user_info;

	if (!empty($modSettings['modlog_enabled']))
	{
		db_query("
			INSERT INTO {$db_prefix}log_actions
				(logTime, ID_MEMBER, IP, action, extra)
			VALUES (" . time() . ", $ID_MEMBER, '$user_info[ip]', '$action',
				'" . addslashes(serialize($extra)) . "')", __FILE__, __LINE__);

		return db_insert_id();
	}

	return false;
}

// Track Statistics.
function trackStats($stats = array())
{
	global $db_prefix, $modSettings;
	static $cache_stats = array();

	if (empty($modSettings['trackStats']))
		return false;
	if (!empty($stats))
		return $cache_stats = array_merge($cache_stats, $stats);
	elseif (empty($cache_stats))
		return false;

	$setStringUpdate = '';
	foreach ($cache_stats as $field => $change)
	{
		$setStringUpdate .= '
			' . $field . ' = ' . ($change === '+' ? $field . ' + 1' : $change) . ',';

		if ($change === '+')
			$cache_stats[$field] = 1;
	}

	$date = strftime('%Y%m%d', forum_time(false));
	db_query("
		UPDATE {$db_prefix}log_activity
		SET" . substr($setStringUpdate, 0, -1) . "
		WHERE date = $date
		LIMIT 1", __FILE__, __LINE__);
	if (db_affected_rows() == 0)
	{
		db_query("
			INSERT IGNORE INTO {$db_prefix}log_activity
				(date, " . implode(', ', array_keys($cache_stats)) . ")
			VALUES ($date, " . implode(', ', $cache_stats) . ')', __FILE__, __LINE__);
	}

	return true;
}

// Make sure the user isn't posting over and over again.
function spamProtection($error_type)
{
	global $modSettings, $txt, $db_prefix, $user_info;

	// Delete old entries... if you can moderate this board or this is login, override spamWaitTime with 2.
	if ($error_type == 'spam' && !allowedTo('moderate_board'))
		db_query("
			DELETE FROM {$db_prefix}log_floodcontrol
			WHERE logTime < " . (time() - $modSettings['spamWaitTime']), __FILE__, __LINE__);
	else
		db_query("
			DELETE FROM {$db_prefix}log_floodcontrol
			WHERE (logTime < " . (time() - 2) . " AND ip = '$user_info[ip]')
				OR logTime < " . (time() - $modSettings['spamWaitTime']), __FILE__, __LINE__);

	// Add a new entry, deleting the old if necessary.
	db_query("
		REPLACE INTO {$db_prefix}log_floodcontrol
			(ip, logTime)
		VALUES ('$user_info[ip]', " . time() . ")", __FILE__, __LINE__);
	// If affected is 0 or 2, it was there already.
	if (db_affected_rows() != 1)
	{
		// Spammer!  You only have to wait a *few* seconds!
		fatal_lang_error($error_type . 'WaitTime_broken', false, array($modSettings['spamWaitTime']));
		return true;
	}

	// They haven't posted within the limit.
	return false;
}

// Get the size of a specified image with better error handling.
function url_image_size($url)
{
	// Get the host to pester...
	preg_match('~^\w+://(.+?)/(.*)$~', $url, $match);

	// Can't figure it out, just try the image size.
	if ($url == '' || $url == 'http://' || $url == 'https://')
		return false;
	elseif (!isset($match[1]))
		return @getimagesize($url);

	// Try to connect to the server... give it one full second.
	$temp = 0;
	$fp = @fsockopen($match[1], 80, $temp, $temp, 1);

	// Successful?  Continue...
	if ($fp != false)
	{
		// Send the HEAD request.
		fwrite($fp, 'HEAD /' . $match[2] . ' HTTP/1.1' . "\r\n" . 'Connection: close' . "\r\n" . 'Host: ' . $match[1] . "\r\n\r\n");
		// Read in the HTTP/1.1 or whatever.
		$test = substr(fgets($fp, 11), -1);
		fclose($fp);

		// See if it returned a 404/403 or something.
		if ($test < 4)
			return @getimagesize($url);
	}

	// Didn't work.
	return false;
}

function determineTopicClass(&$topic_context)
{
	// Set topic class depending on locked status and number of replies.
	if ($topic_context['is_very_hot'])
		$topic_context['class'] = 'veryhot';
	elseif ($topic_context['is_hot'])
		$topic_context['class'] = 'hot';
	else
		$topic_context['class'] = 'normal';

	$topic_context['class'] .= $topic_context['is_poll'] ? '_poll' : '_post';

	if ($topic_context['is_locked'])
		$topic_context['class'] .= '_locked';

	if ($topic_context['is_sticky'])
		$topic_context['class'] .= '_sticky';

	// This is so old themes will still work.
	$topic_context['extended_class'] = &$topic_context['class'];
}

// Sets up the basic theme context stuff.
function setupThemeContext()
{
	global $modSettings, $user_info, $scripturl, $context, $settings, $options, $txt, $maintenance;
	global $db_prefix; // Grudge's Mod

	// Get some news...
	$context['news_lines'] = explode("\n", str_replace("\r", '', trim(addslashes($modSettings['news']))));
	$context['fader_news_lines'] = array();
	for ($i = 0, $n = count($context['news_lines']); $i < $n; $i++)
	{
		if (trim($context['news_lines'][$i]) == '')
			continue;

		// Clean it up for presentation ;).
		$context['news_lines'][$i] = doUBBC(stripslashes(trim($context['news_lines'][$i])));

		// Gotta be special for the javascript.
		$context['fader_news_lines'][$i] = strtr(addslashes($context['news_lines'][$i]), array('/' => '\/', '<a href=' => '<a hre" + "f='));
	}
	$context['random_news_line'] = $context['news_lines'][rand(0, count($context['news_lines']) - 1)];

	if (!$user_info['is_guest'])
	{
		$context['user']['messages'] = &$user_info['messages'];
		$context['user']['unread_messages'] = &$user_info['unread_messages'];
		if (allowedTo('visual_approve_any'))
		{
			$result = db_query("
					SELECT COUNT(*) 
					FROM {$db_prefix}postmoderation", __FILE__, __LINE__);
			list($modmsg) = mysql_fetch_row($result);
			if ($modmsg)
				$context['user']['awaiting_mod'] = $modmsg . ' ' . $txt['visual_require_mod'];
		}

		// Personal message popup...
		if ($user_info['unread_messages'] > (isset($_SESSION['unread_messages']) ? $_SESSION['unread_messages'] : 0))
			$context['user']['popup_messages'] = true;
		else
			$context['user']['popup_messages'] = false;
		$_SESSION['unread_messages'] = $user_info['unread_messages'];

		if (allowedTo('moderate_forum'))
			$context['unapproved_members'] = !empty($modSettings['registration_method']) && $modSettings['registration_method'] == 2 ? $modSettings['unapprovedMembers'] : 0;

		$context['user']['avatar'] = array();

		// Figure out the avatar... uploaded?
		if ($user_info['avatar']['url'] == '')
		{
			// If they are allowed to use an uploaded avatar.
			if (!empty($user_info['avatar']['ID_ATTACH']) && !empty($modSettings['avatar_allow_upload']))
			{
				$__avatar = glob('attachments/avatar_'.$context['user']['id'].'.{jpg,png,gif}', GLOB_BRACE);
				if (!empty($__avatar)) $__avatar = $__avatar[0];
				$context['user']['avatar']['href'] = 'http://img.rock.ru/forum/'.$__avatar;
			}
		}
		// Full URL?
		elseif (substr($user_info['avatar']['url'], 0, 7) == 'http://' && !empty($modSettings['avatar_allow_external_url']))
		{
			$context['user']['avatar']['href'] = $user_info['avatar']['url'];
			if ($modSettings['avatar_action_too_large'] == 'option_html_resize')
			{
				if (!empty($modSettings['avatar_max_width_external']))
					$context['user']['avatar']['width'] = $modSettings['avatar_max_width_external'];
				if (!empty($modSettings['avatar_max_height_external']))
					$context['user']['avatar']['height'] = $modSettings['avatar_max_height_external'];
			}
		}
		// Server stored?
		elseif (!empty($modSettings['avatar_allow_server_stored']))
			$context['user']['avatar']['href'] = $modSettings['avatar_url'] . '/' . htmlspecialchars($user_info['avatar']['url']);

		if (!empty($context['user']['avatar']))
			$context['user']['avatar']['image'] = '<img src="' . $context['user']['avatar']['href'] . '"' . (isset($context['user']['avatar']['width']) ? ' width="' . $context['user']['avatar']['width'] . '"' : '') . (isset($context['user']['avatar']['height']) ? ' height="' . $context['user']['avatar']['height'] . '"' : '') . ' alt="" border="0" />';

		// Figure out how long they've been logged in.
		$context['user']['total_time_logged_in'] = array(
			'days' => floor($user_info['total_time_logged_in'] / 86400),
			'hours' => floor(($user_info['total_time_logged_in'] % 86400) / 3600),
			'minutes' => floor(($user_info['total_time_logged_in'] % 3600) / 60)
		);
	}
	else
	{
		$context['user']['messages'] = 0;
		$context['user']['unread_messages'] = 0;
		$context['user']['avatar'] = array();
		$context['user']['total_time_logged_in'] = array('days' => 0, 'hours' => 0, 'minutes' => 0);
		$context['user']['popup_messages'] = false;

		if (!empty($modSettings['registration_method']) && $modSettings['registration_method'] == 1)
			$txt['welcome_guest'] .= $txt['welcome_guest_activate'];
	}

	// Set up the menu privileges.
	$context['allow_search'] = allowedTo('search_posts');
	$context['allow_admin'] = allowedTo(array('admin_forum', 'manage_boards', 'manage_permissions', 'moderate_forum', 'manage_membergroups', 'manage_bans', 'send_mail', 'edit_news', 'manage_attachments', 'manage_smileys'));
	$context['allow_edit_profile'] = !$user_info['is_guest'] && allowedTo(array('profile_view_own', 'profile_view_any', 'profile_identity_own', 'profile_identity_any', 'profile_extra_own', 'profile_extra_any', 'profile_remove_own', 'profile_remove_any', 'moderate_forum', 'manage_membergroups'));
	$context['allow_calendar'] = allowedTo('calendar_view') && !empty($modSettings['cal_enabled']);

	$context['allow_pm'] = allowedTo('pm_read');

	$context['in_maintenance'] = !empty($maintenance);
	$context['current_time'] = timeformat(time(), false);
	$context['show_vBlogin'] = !empty($modSettings['enableVBStyleLogin']) && $user_info['is_guest'];

	// This is here because old index templates might still use it.
	$context['show_news'] = !empty($settings['enable_news']);

	// This is done to make it easier to add to all themes...
	if ($context['user']['popup_messages'] && !empty($options['popup_messages']))
	{
		$context['html_headers'] .= '
	<script language="JavaScript" type="text/javascript"><!--
		if (confirm("' . $txt['show_personal_messages'] . '"))
			window.open("' . $scripturl . '?action=pm");
	// --></script>';
	}

	if (!isset($context['page_title']))
		$context['page_title'] = '';
}

// This is the only template included in the sources...
function template_rawdata()
{
	global $context;

	echo $context['raw_data'];
}

function template_header()
{
	global $txt, $modSettings, $context, $settings, $user_info;

	setupThemeContext();

	// Print stuff to prevent caching of pages.
	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
	header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');

	foreach ($context['template_layers'] as $layer)
	{
		loadSubTemplate($layer . '_above', true);

		// May seem contrived, but this is done in case the main layer isn't there...
		if ($layer == 'main' && allowedTo('admin_forum'))
		{
			$securityFiles = array('install.php', 'upgrade.php', 'repair_paths.php', 'repair_settings.php');
			foreach ($securityFiles as $i => $securityFile)
			{
				if (!file_exists($securityFile))
					unset($securityFiles[$i]);
			}

			if (!empty($securityFiles))
			{
				echo '
		<div class="windowbg" style="margin: 2ex; padding: 2ex; border: 2px dashed red; color: red;">
			<span style="text-decoration: underline;">', $txt['smf299'], '</span>
			<div style="padding-left: 4ex;">';

				foreach ($securityFiles as $securityFile)
					echo '
			', $txt['smf300'], $securityFile, '!<br />';

				echo '
			</div>
		</div>';
			}
		}
		// If the user is banned from posting inform them of it.
		elseif ($layer == 'main' && !empty($_SESSION['ban']['cannot_post']['is_banned']))
		{
			echo '
				<div class="windowbg" style="margin: 2ex; padding: 2ex; border: 2px dashed red; color: red;">
					', sprintf($txt['you_are_post_banned'], $user_info['is_guest'] ? $txt[28] : $user_info['name']);

			// The substr here is used to get rid of the first of two <br />s.
			if (isset($_SESSION['ban']['cannot_post']['reason']))
				echo '
					<div style="padding-left: 4ex;">', substr($_SESSION['ban']['cannot_post']['reason'], 6), '</div>';

			echo '
				</div>';
		}
	}

	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template']))
	{
		$settings['theme_url'] = $settings['default_theme_url'];
		$settings['images_url'] = $settings['default_images_url'];
		$settings['theme_dir'] = $settings['default_theme_dir'];
	}
}

// Show the copyright...
function theme_copyright($get_it = false)
{
	global $forum_copyright, $context, $boardurl, $forum_version, $txt;
	static $found = false;

	// Meaning, this is the footer checking in..
	if ($get_it === true)
		return $found;

	echo '
		<span class="smalltext" style="display: inline; visibility: visible; font-family: Verdana, Arial, sans-serif;">';

	if ($get_it == 'none')
	{
		$found = true;
		echo '
			The administrator doesn\'t want a copyright notice saying this is copyright 2001-2005 by <a href="http://www.lewismedia.com/" target="_blank">Lewis Media</a>, and named <a href="http://www.simplemachines.org/">SMF</a>, so the forum will honor this request.';
	}
	// If it's in the copyright, and we are outputting it... it's been found.
	elseif ((strpos($forum_copyright, '<a href="http://www.simplemachines.org/" onclick="this.href += \'referer.php?forum=' . urlencode($context['forum_name'] . '|' . $boardurl . '|' . $forum_version) . '\';" target="_blank">SMF') !== false || strpos($forum_copyright, '<a href="http://www.simplemachines.org/" target="_blank">SMF') !== false || strpos($forum_copyright, '<a href="http://www.simplemachines.org/" title="Simple Machines Forum" target="_blank">SMF') !== false) && (strpos($forum_copyright, '<a href="http://www.lewismedia.com/">Lewis Media</a>') !== false || strpos($forum_copyright, '<a href="http://www.lewismedia.com/" target="_blank">Lewis Media</a>') !== false))
	{
		$found = true;
		echo $forum_copyright;
	}

	echo '
		</span>';
}

function template_footer()
{
	global $context, $settings, $modSettings, $time_start, $db_count;

	// Show the load time?  (only makes sense for the footer.)
	$context['show_load_time'] = $modSettings['timeLoadPageEnable'] == 1;
	$time_start = explode(' ', $time_start);
	$time_end = explode(' ', microtime());
	$context['load_time'] = round($time_end[0] + $time_end[1] - $time_start[0] - $time_start[1], 3);
	$context['load_queries'] = $db_count;

	if (isset($settings['use_default_images']) && $settings['use_default_images'] == 'defaults' && isset($settings['default_template']))
	{
		$settings['theme_url'] = $settings['actual_theme_url'];
		$settings['images_url'] = $settings['actual_images_url'];
		$settings['theme_dir'] = $settings['actual_theme_dir'];
	}

	foreach (array_reverse($context['template_layers']) as $layer)
		loadSubTemplate($layer . '_below', true);

	// Do not remove hard-coded text - it's in here so users cannot change the text easily. (as if it were in language file)
	if (!theme_copyright(true) && !empty($context['template_layers']) && SMF !== 'SSI' && !WIRELESS)
	{
		echo '
			<div align="center" style="display: block !important; visibility: visible !important; font-size: xx-large !important; font-weight: bold; color: black !important; background-color: white !important;">
				Sorry, the copyright must be in the template.<br />
				Please notify this ' . "forum's" . ' administrator that this site is using an <span style="color: #FF0000;">ILLEGAL</span> copy of <a href="http://www.simplemachines.org/" style="color: black !important; font-size: xx-large !important;">SMF</a>!
			</div>';

		log_error('Copyright removed!!');
	}
}

// Debugging.
function db_debug_junk()
{
	global $db_cache, $db_count, $context, $scripturl;

	// Add to Settings.php if you want to show the debugging information.
	if (!isset($GLOBALS['db_show_debug']) || $GLOBALS['db_show_debug'] !== true || (isset($_GET['action']) && $_GET['action'] == 'viewquery'))
		return;

	if (empty($_SESSION['view_queries']))
		$_SESSION['view_queries'] = 0;

	$_SESSION['debug'] = $db_cache;
	echo '
<div align="left" class="smalltext">', (isset($context['template']) ? '
	Template: <i>' . $context['template'] . '</i>' . (isset($context['sub_template']) ? ' (<i>' . $context['sub_template'] . '</i>)' : '') . '.<br />' : ''), '
	<a href="', $scripturl, '?action=viewquery" target="_blank">Queries used: ', $db_count, '</a>.<br />
	<br />';

	if ($_SESSION['view_queries'] == 1)
		foreach ($db_cache as $q => $qq)
			echo '
	', substr(trim($qq['q']), 0, 6) == 'SELECT' ? '<a class="b" href="' . $scripturl . '?action=viewquery;qq=' . ($q + 1) . '#qq' . $q . '" target="_blank">' : '', nl2br(str_replace("\t", '&nbsp;&nbsp;&nbsp;', ltrim($qq['q'], "\n\r"))) . '</a> in <em>' . $qq['f'] . '</em> line <em>' . $qq['l'] . '</em>, which took ' . $qq['t'] . ' seconds.<br />
	<br />';

	echo '
	<a href="' . $scripturl . '?action=viewquery;sa=hide">[' . (empty($_SESSION['view_queries']) ? 'show' : 'hide') . ' queries]</a>
</div>';
}

// Get an attachment's encrypted filename.  If $new is true, won't check for file existence.
function getAttachmentFilename($filename, $attachment_id, $new = false)
{
	global $modSettings;

	// Remove special accented characters - ie. s?.
	$clean_name = strtr($filename, '????????????????????????????????????????????????????????????', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy');
	$clean_name = strtr($clean_name, array('?' => 'TH', '?' => 'th', '?' => 'DH', '?' => 'dh', '?' => 'ss', '?' => 'OE', '?' => 'oe', '?' => 'AE', '?' => 'ae', '?' => 'u'));

	// Sorry, no spaces, dots, or anything else but letters allowed.
	$clean_name = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $clean_name);

	$enc_name = $attachment_id . '_' . str_replace('.', '_', $clean_name) . md5($clean_name);
	$clean_name = preg_replace('~\.[\.]+~', '.', $clean_name);

	if ($attachment_id == false || ($new && empty($modSettings['attachmentEncryptFilenames'])))
		return $clean_name;
	elseif ($new)
		return $enc_name;

	if (file_exists($modSettings['attachmentUploadDir'] . '/' . $enc_name))
		$filename = $modSettings['attachmentUploadDir'] . '/' . $enc_name;
	else
		$filename = $modSettings['attachmentUploadDir'] . '/' . $clean_name;

	return $filename;
}

function getThumbnailFilename($filename, $attachment_id, $new = false)
{
	global $modSettings;
/*
	// Remove special accented characters - ie. s?.
	$clean_name = strtr($filename, '????????????????????????????????????????????????????????????', 'SZszYAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy');
	$clean_name = strtr($clean_name, array('?' => 'TH', '?' => 'th', '?' => 'DH', '?' => 'dh', '?' => 'ss', '?' => 'OE', '?' => 'oe', '?' => 'AE', '?' => 'ae', '?' => 'u'));

	// Sorry, no spaces, dots, or anything else but letters allowed.
	$clean_name = preg_replace(array('/\s/', '/[^\w_\.\-]/'), array('_', ''), $clean_name);

	$enc_name = $attachment_id . '_' . str_replace('.', '_', $clean_name) . md5($clean_name);
	$clean_name = preg_replace('~\.[\.]+~', '.', $clean_name);

	if ($attachment_id == false || ($new && empty($modSettings['attachmentEncryptFilenames'])))
		return $clean_name;
	elseif ($new)
		return $enc_name;

	if (file_exists($modSettings['attachmentUploadDir'] . '/thumbs/' . $enc_name))
		$filename = $modSettings['attachmentUploadDir'] . '/thumbs/' . $enc_name;
	else
		$filename = $modSettings['attachmentUploadDir'] . '/thumbs/' . $clean_name;

	return $filename;*/
	return $modSettings['attachmentUploadDir']."/thumbs/".$attachment_id.md5($filename);	
}

function ob_googlebot_getAgent( &$user_agent, &$user_name, &$result, $guest )
{
	//Search Spiders
	$spider_agents = array (
		array (
			'agent' => 'WISENutbot',
			'spidername' => 'Looksmart spider',
			'spider' => true,
		),
		array (
			'agent' => 'MSNBot',
			'spidername' => 'MSN spider',
			'spider' => true,
		),
		array (
			'agent' => 'W3C_Validator',
			'spidername' => 'W3C Validator',
			'spider' => true,
		),
		array (
			'agent' => 'Googlebot-Image',
			'spidername' => 'Google-Image Spider',
			'spider' => true,
		),
		array (
			'agent' => 'Googlebot',
			'spidername' => 'Google spider',
			'spider' => true,
		),
		array (
			'agent' => 'Mediapartners-Google',
			'spidername' => 'Google AdSense spider',
			'spider' => true,
		),
		array (
			'agent' => 'Scooter',
			'spidername' => 'Altavista spider',
			'spider' => true,
		),
		array (
			'agent' => 'Yahoo! Slurp',
			'spidername' => 'Yahoo spider',
			'spider' => true,
		),
		array (
			'agent' => 'Yandex',
			'spidername' => 'YandexBot',
			'spider' => true
		),
		array (
			'agent' => 'Rambler',
			'spidername' => 'RamblerBot',
			'spider' => true
		),
		array (
			'agent' => 'FAST-WebCrawler',
			'spider' => true,
		),
		array (
			'agent' => 'Wget',
			'spider' => true,
		),
		array (
			'agent' => 'Ask Jeeves', 
			'spider' => true,
		),
		array (
			'agent' => 'Speedy Spider',
			'spider' => true,
		),
		array (
			'agent' => 'SurveyBot',
			'spider' => true,
		),
		array (
			'agent' => 'IBM_Planetwide',
			'spider' => true,
		),
		array (
			'agent' => 'GigaBot',
			'spider' => true,
		),
		array (
			'agent' => 'ia_archiver',
			'spider' => true,
		),
		array (
			'agent' => 'FAST-WebCrawler',
			'spider' => true,
		),
		array (
			'agent' => 'Inktomi Slurp',
			'spider' => true,
		),
	);

	foreach( $spider_agents as $poss )
		if (strpos(strtolower($user_agent), strtolower($poss['agent'])) !== false)
		{
			if ( $guest && isset($poss['spider']) && $poss['spider'] )
				$user_name = isset($poss['spidername']) ? $poss['spidername'] : (isset($poss['name']) ? $poss['name'] : $poss['agent']); 
				
			$result = isset($poss['name']) ? $poss['name'] : $poss['agent']; 
			return isset($poss['spider']) && $poss['spider'];
		}
		
	$result = $user_agent;
	return false;
};

?>
