$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, MAX(posterTime) AS posterTime
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']]);
// Update the board!
db_query("
UPDATE {$db_prefix}boards
SET ID_LAST_MSG = $row[ID_MSG], lastUpdated = $row[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 = $row[posterTime]
WHERE ID_BOARD IN (" . implode(',', $the_parent_boards) . ")
AND lastUpdated < $row[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__);
}
/* 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)
{
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 = '« ';
// 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 . '">' . $display_page++ . ' ';
// 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 . '">» ';
}
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 ';
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 . '">' . ($tmpStart / $num_per_page + 1) . ' ';
}
// 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 . '">' . ($start / $num_per_page + 1) . ' ';
// 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 . '">' . ($tmpStart / $num_per_page + 1) . ' ';
}
// 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 . '">' . ($tmpMaxPages / $num_per_page + 1) . ' ';
}
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:%S %p';
else
$today_fmt = '%H:%M:%S';
// 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 = ereg_replace('%a', ucwords(strftime('%a', $time)), $str);
$str = ereg_replace('%A', ucwords(strftime('%A', $time)), $str);
$str = ereg_replace('%b', ucwords(strftime('%b', $time)), $str);
$str = ereg_replace('%B', ucwords(strftime('%B', $time)), $str);
}
else
{
// Do-it-yourself time localization. Fun.
$str = ereg_replace('%a', @$days_short[(int) strftime('%w', $time)], $str);
$str = ereg_replace('%A', @$days[(int) strftime('%w', $time)], $str);
$str = ereg_replace('%b', @$months_short[(int) strftime('%m', $time)], $str);
$str = ereg_replace('%B', @$months[(int) strftime('%m', $time)], $str);
$str = ereg_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(''' => '\'', ' ' => ' '));
}
// 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)
{
global $txt, $modSettings, $context;
// Item codes - for
types. Used with [*], [@], [o], etc.
static $itemcode = array(
'[*]' => '
',
'[@]' => '
',
'[+]' => '
',
'[x]' => '
',
'[#]' => '
',
'[o]' => '
',
'[O]' => '
'
);
if (empty($modSettings['enableBBC']))
return $message;
if (substr($message, 0, 1) == ' ')
$message = ' ' . substr($message, 1);
// Rip apart code tags.
$parts = preg_split('~\[/?code\]( )?~', ' ' . strtr($message, array("\n" => ' ')));
// 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] = '' . $parts[$i];
// Find any [php] code tags.... CAPTURING the delimiter.
$php_parts = preg_split('~(\[php\])(?: )?|(\[/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) != '<?' ? '<?php' . $php_string . '?>' : $php_string);
if (substr(trim($php_string), 0, 5) != '<?')
$php_parts[$php_i] = preg_replace(array('~^(.+?)<\?php~', '~\?>((?:)*)$~'), '$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(''' => '\'', ' ' => '\xA0', '"' => '>">', '"' => '<"<', '<' => '\.(;\'"])((?: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('\'' => ''', '\xA0' => ' ', '>">' => '"', '<"<' => '"', ' '<'));
}
// Parse code.....
parsecode($php_parts[$php_i]);
// 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" => ' '));
// 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 = '';
// Opera...
elseif ($context['browser']['is_opera'])
$breaker = '';
// Internet Explorer...
else
$breaker = '';
// 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>]| )\[0\]~', '$1
',
// 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\[\]()*\\\]|$| | |>|<|"|'|\.(?:\.| |\s|$| ))~i' => '$1',
'~(?<= )([\w\-\.]{1,80}@[\w\-]+\.[\w\-\.]+[\w\-])(?=[?\.,\s\xA0\[\]()*\\\]|$| | |>|<|"|')~i' => '$1',
// This last one fixes spaces at the beginning of lines.
'~ ~' => ' ',
// Match a table... hopefully with everything in the right place.
'~\[table\](?:\s|\xA0| | )*((?:\[tr\](?:\s|\xA0| | )*\[td\]).*?(?:(?:\s|\xA0| | )*\[/td\]\[/tr\])*)(?:\s|\xA0| | )*\[/table\](?:\s|\xA0| | )?~i' => '