Bladeren bron

! Small code documentation updates, many files
! spamProtection() moved from Subs.php to Security.php.
! db_debug_junk() and logAction, from Subs.php to Errors.php (wip)
! updateLastDatabaseError() from Subs-Admin.php to Errors.php.
! some un-refactoring getLegacyAttachmentFilename & getAttachmentFilename

Spuds 13 jaren geleden
bovenliggende
commit
444c9cb13a

+ 301 - 2
Sources/Errors.php

@@ -119,6 +119,307 @@ function log_error($error_message, $error_type = 'general', $file = null, $line
 	return $error_message;
 }
 
+/**
+ * This function logs an action in the respective log. (database log)
+ * @example logAction('remove', array('starter' => $id_member_started));
+ *
+ * @param string $action
+ * @param array $extra = array()
+ * @param string $log_type, options 'moderate', 'admin', ...etc.
+ */
+function logAction($action, $extra = array(), $log_type = 'moderate')
+{
+	global $modSettings, $user_info, $smcFunc, $sourcedir;
+
+	$log_types = array(
+		'moderate' => 1,
+		'user' => 2,
+		'admin' => 3,
+	);
+
+	if (!is_array($extra))
+		trigger_error('logAction(): data is not an array with action \'' . $action . '\'', E_USER_NOTICE);
+
+	// Pull out the parts we want to store separately, but also make sure that the data is proper
+	if (isset($extra['topic']))
+	{
+		if (!is_numeric($extra['topic']))
+			trigger_error('logAction(): data\'s topic is not a number', E_USER_NOTICE);
+		$topic_id = empty($extra['topic']) ? '0' : (int)$extra['topic'];
+		unset($extra['topic']);
+	}
+	else
+		$topic_id = '0';
+
+	if (isset($extra['message']))
+	{
+		if (!is_numeric($extra['message']))
+			trigger_error('logAction(): data\'s message is not a number', E_USER_NOTICE);
+		$msg_id = empty($extra['message']) ? '0' : (int)$extra['message'];
+		unset($extra['message']);
+	}
+	else
+		$msg_id = '0';
+
+	// Is there an associated report on this?
+	if (in_array($action, array('move', 'remove', 'split', 'merge')))
+	{
+		$request = $smcFunc['db_query']('', '
+			SELECT id_report
+			FROM {db_prefix}log_reported
+			WHERE {raw:column_name} = {int:reported}
+			LIMIT 1',
+			array(
+				'column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic',
+				'reported' => !empty($msg_id) ? $msg_id : $topic_id,
+		));
+
+		// Alright, if we get any result back, update open reports.
+		if ($smcFunc['db_num_rows']($request) > 0)
+		{
+			require_once($sourcedir . '/ModerationCenter.php');
+			updateSettings(array('last_mod_report_action' => time()));
+			recountOpenReports();
+		}
+		$smcFunc['db_free_result']($request);
+	}
+
+	// No point in doing anything else, if the log isn't even enabled.
+	if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type]))
+		return false;
+
+	if (isset($extra['member']) && !is_numeric($extra['member']))
+		trigger_error('logAction(): data\'s member is not a number', E_USER_NOTICE);
+
+	if (isset($extra['board']))
+	{
+		if (!is_numeric($extra['board']))
+			trigger_error('logAction(): data\'s board is not a number', E_USER_NOTICE);
+		$board_id = empty($extra['board']) ? '0' : (int)$extra['board'];
+		unset($extra['board']);
+	}
+	else
+		$board_id = '0';
+
+	if (isset($extra['board_to']))
+	{
+		if (!is_numeric($extra['board_to']))
+			trigger_error('logAction(): data\'s board_to is not a number', E_USER_NOTICE);
+		if (empty($board_id))
+		{
+			$board_id = empty($extra['board_to']) ? '0' : (int)$extra['board_to'];
+			unset($extra['board_to']);
+		}
+	}
+
+	$smcFunc['db_insert']('',
+		'{db_prefix}log_actions',
+		array(
+			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
+			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
+		),
+		array(
+			time(), $log_types[$log_type], $user_info['id'], $user_info['ip'], $action,
+			$board_id, $topic_id, $msg_id, serialize($extra),
+		),
+		array('id_action')
+	);
+
+	return $smcFunc['db_insert_id']('{db_prefix}log_actions', 'id_action');
+}
+
+/**
+ * Debugging.
+ */
+function db_debug_junk()
+{
+	global $context, $scripturl, $boarddir, $modSettings, $boarddir;
+	global $db_cache, $db_count, $db_show_debug, $cache_count, $cache_hits, $txt;
+
+	// Add to Settings.php if you want to show the debugging information.
+	if (!isset($db_show_debug) || $db_show_debug !== true || (isset($_GET['action']) && $_GET['action'] == 'viewquery') || WIRELESS)
+		return;
+
+	if (empty($_SESSION['view_queries']))
+		$_SESSION['view_queries'] = 0;
+	if (empty($context['debug']['language_files']))
+		$context['debug']['language_files'] = array();
+	if (empty($context['debug']['sheets']))
+		$context['debug']['sheets'] = array();
+
+	$files = get_included_files();
+	$total_size = 0;
+	for ($i = 0, $n = count($files); $i < $n; $i++)
+	{
+		if (file_exists($files[$i]))
+			$total_size += filesize($files[$i]);
+		$files[$i] = strtr($files[$i], array($boarddir => '.'));
+	}
+
+	$warnings = 0;
+	if (!empty($db_cache))
+	{
+		foreach ($db_cache as $q => $qq)
+		{
+			if (!empty($qq['w']))
+				$warnings += count($qq['w']);
+		}
+
+		$_SESSION['debug'] = &$db_cache;
+	}
+
+	// Gotta have valid HTML ;).
+	$temp = ob_get_contents();
+	if (function_exists('ob_clean'))
+		ob_clean();
+	else
+	{
+		ob_end_clean();
+		ob_start('ob_sessrewrite');
+	}
+
+	echo preg_replace('~</body>\s*</html>~', '', $temp), '
+<div class="smalltext" style="text-align: left; margin: 1ex;">
+	', $txt['debug_templates'], count($context['debug']['templates']), ': <em>', implode('</em>, <em>', $context['debug']['templates']), '</em>.<br />
+	', $txt['debug_subtemplates'], count($context['debug']['sub_templates']), ': <em>', implode('</em>, <em>', $context['debug']['sub_templates']), '</em>.<br />
+	', $txt['debug_language_files'], count($context['debug']['language_files']), ': <em>', implode('</em>, <em>', $context['debug']['language_files']), '</em>.<br />
+	', $txt['debug_stylesheets'], count($context['debug']['sheets']), ': <em>', implode('</em>, <em>', $context['debug']['sheets']), '</em>.<br />
+	', $txt['debug_files_included'], count($files), ' - ', round($total_size / 1024), $txt['debug_kb'], ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_include_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_include_info" style="display: none;"><em>', implode('</em>, <em>', $files), '</em></span>)<br />';
+
+	if (!empty($modSettings['cache_enable']) && !empty($cache_hits))
+	{
+		$entries = array();
+		$total_t = 0;
+		$total_s = 0;
+		foreach ($cache_hits as $cache_hit)
+		{
+			$entries[] = $cache_hit['d'] . ' ' . $cache_hit['k'] . ': ' . sprintf($txt['debug_cache_seconds_bytes'], comma_format($cache_hit['t'], 5), $cache_hit['s']);
+			$total_t += $cache_hit['t'];
+			$total_s += $cache_hit['s'];
+		}
+
+		echo '
+	', $txt['debug_cache_hits'], $cache_count, ': ', sprintf($txt['debug_cache_seconds_bytes_total'], comma_format($total_t, 5), comma_format($total_s)), ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_cache_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_cache_info" style="display: none;"><em>', implode('</em>, <em>', $entries), '</em></span>)<br />';
+	}
+
+	echo '
+	<a href="', $scripturl, '?action=viewquery" target="_blank" class="new_win">', $warnings == 0 ? sprintf($txt['debug_queries_used'], (int) $db_count) : sprintf($txt['debug_queries_used_and_warnings'], (int) $db_count, $warnings), '</a><br />
+	<br />';
+
+	if ($_SESSION['view_queries'] == 1 && !empty($db_cache))
+		foreach ($db_cache as $q => $qq)
+		{
+			$is_select = substr(trim($qq['q']), 0, 6) == 'SELECT' || preg_match('~^INSERT(?: IGNORE)? INTO \w+(?:\s+\([^)]+\))?\s+SELECT .+$~s', trim($qq['q'])) != 0;
+			// Temporary tables created in earlier queries are not explainable.
+			if ($is_select)
+			{
+				foreach (array('log_topics_unread', 'topics_posted_in', 'tmp_log_search_topics', 'tmp_log_search_messages') as $tmp)
+					if (strpos(trim($qq['q']), $tmp) !== false)
+					{
+						$is_select = false;
+						break;
+					}
+			}
+			// But actual creation of the temporary tables are.
+			elseif (preg_match('~^CREATE TEMPORARY TABLE .+?SELECT .+$~s', trim($qq['q'])) != 0)
+				$is_select = true;
+
+			// Make the filenames look a bit better.
+			if (isset($qq['f']))
+				$qq['f'] = preg_replace('~^' . preg_quote($boarddir, '~') . '~', '...', $qq['f']);
+
+			echo '
+	<strong>', $is_select ? '<a href="' . $scripturl . '?action=viewquery;qq=' . ($q + 1) . '#qq' . $q . '" target="_blank" class="new_win" style="text-decoration: none;">' : '', nl2br(str_replace("\t", '&nbsp;&nbsp;&nbsp;', htmlspecialchars(ltrim($qq['q'], "\n\r")))) . ($is_select ? '</a></strong>' : '</strong>') . '<br />
+	&nbsp;&nbsp;&nbsp;';
+			if (!empty($qq['f']) && !empty($qq['l']))
+				echo sprintf($txt['debug_query_in_line'], $qq['f'], $qq['l']);
+
+			if (isset($qq['s'], $qq['t']) && isset($txt['debug_query_which_took_at']))
+				echo sprintf($txt['debug_query_which_took_at'], round($qq['t'], 8), round($qq['s'], 8)) . '<br />';
+			elseif (isset($qq['t']))
+				echo sprintf($txt['debug_query_which_took'], round($qq['t'], 8)) . '<br />';
+			echo '
+	<br />';
+		}
+
+	echo '
+	<a href="' . $scripturl . '?action=viewquery;sa=hide">', $txt['debug_' . (empty($_SESSION['view_queries']) ? 'show' : 'hide') . '_queries'], '</a>
+</div></body></html>';
+}
+
+/**
+ * Logs the last database error into a file.
+ * Attempts to use the backup file first, to store the last database error
+ * and only update Settings.php if the first was successful.
+ */
+function updateLastDatabaseError()
+{
+	global $boarddir;
+
+	// Find out this way if we can even write things on this filesystem.
+	// In addition, store things first in the backup file
+
+	$last_settings_change = @filemtime($boarddir . '/Settings.php');
+
+	// Make sure the backup file is there...
+	$file = $boarddir . '/Settings_bak.php';
+	if ((!file_exists($file) || filesize($file) == 0) && !copy($boarddir . '/Settings.php', $file))
+			return false;
+
+	// ...and writable!
+	if (!is_writable($file))
+	{
+		chmod($file, 0755);
+		if (!is_writable($file))
+		{
+			chmod($file, 0775);
+			if (!is_writable($file))
+			{
+				chmod($file, 0777);
+				if (!is_writable($file))
+						return false;
+			}
+		}
+	}
+
+	// Put the new timestamp.
+	$data = file_get_contents($file);
+	$data = preg_replace('~\$db_last_error = \d+;~', '$db_last_error = ' . time() . ';', $data);
+
+	// Open the backup file for writing
+	if ($fp = @fopen($file, 'w'))
+	{
+		// Reset the file buffer.
+		set_file_buffer($fp, 0);
+
+		// Update the file.
+		$t = flock($fp, LOCK_EX);
+		$bytes = fwrite($fp, $data);
+		flock($fp, LOCK_UN);
+		fclose($fp);
+
+		// Was it a success?
+		// ...only relevant if we're still dealing with the same good ole' settings file.
+		clearstatcache();
+		if (($bytes == strlen($data)) && (filemtime($boarddir . '/Settings.php') === $last_settings_change))
+		{
+			// This is our new Settings file...
+			// At least this one is an atomic operation
+			@copy($file, $boarddir . '/Settings.php');
+			return true;
+		}
+		else
+		{
+			// Oops. Someone might have been faster
+			// or we have no more disk space left, troubles, troubles...
+			// Copy the file back and run for your life!
+			@copy($boarddir . '/Settings.php', $file);
+		}
+	}
+
+	return false;
+}
+
 /**
  * An irrecoverable error. This function stops execution and displays an error message.
  * It logs the error message if $log is specified.
@@ -365,8 +666,6 @@ function show_db_error($loadavg = false)
 
 		if ($db_last_error < time() - 3600 * 24 * 3 && empty($maintenance) && !empty($db_error_send))
 		{
-			require_once($sourcedir . '/Subs-Admin.php');
-
 			// Avoid writing to the Settings.php file if at all possible; use shared memory instead.
 			cache_put_data('db_last_error', time(), 600);
 			if (($temp = cache_get_data('db_last_error', 600)) == null)

+ 1 - 1
Sources/ManageMembergroups.php

@@ -1,7 +1,7 @@
 <?php
 
 /**
- * This file is concerned with anything in the Manage Membergroups screen.
+ * This file is concerned with anything in the Manage Membergroups admin screen.
  *
  * Simple Machines Forum (SMF)
  *

+ 1 - 1
Sources/ManagePosts.php

@@ -1,7 +1,7 @@
 <?php
 
 /**
- * This file contains all the screens that control settings for topics and posts.
+ * This file contains all the administration settings for topics and posts.
  *
  * Simple Machines Forum (SMF)
  *

+ 59 - 0
Sources/Security.php

@@ -976,4 +976,63 @@ function showEmailAddress($userProfile_hideEmail, $userProfile_id)
 	return (!empty($modSettings['guest_hideContacts']) && $user_info['is_guest']) || isset($_SESSION['ban']['cannot_post']) ? 'no' : ((!$user_info['is_guest'] && $user_info['id'] == $userProfile_id && !$userProfile_hideEmail) || allowedTo('moderate_forum') ? 'yes_permission_override' : ($userProfile_hideEmail ? 'no' : (!empty($modSettings['make_email_viewable']) ? 'yes' : 'no_through_forum')));
 }
 
+/**
+ * This function attempts to protect from spammed messages and the like.
+ * The time taken depends on error_type - generally uses the modSetting.
+ *
+ * @param string $error_type, used also as a $txt index. (not an actual string.)
+ * @return boolean
+ */
+function spamProtection($error_type)
+{
+	global $modSettings, $txt, $user_info, $smcFunc;
+
+	// Certain types take less/more time.
+	$timeOverrides = array(
+		'login' => 2,
+		'register' => 2,
+		'sendtopc' => $modSettings['spamWaitTime'] * 4,
+		'sendmail' => $modSettings['spamWaitTime'] * 5,
+		'reporttm' => $modSettings['spamWaitTime'] * 4,
+		'search' => !empty($modSettings['search_floodcontrol_time']) ? $modSettings['search_floodcontrol_time'] : 1,
+	);
+
+	// Moderators are free...
+	if (!allowedTo('moderate_board'))
+		$timeLimit = isset($timeOverrides[$error_type]) ? $timeOverrides[$error_type] : $modSettings['spamWaitTime'];
+	else
+		$timeLimit = 2;
+
+	// Delete old entries...
+	$smcFunc['db_query']('', '
+		DELETE FROM {db_prefix}log_floodcontrol
+		WHERE log_time < {int:log_time}
+			AND log_type = {string:log_type}',
+		array(
+			'log_time' => time() - $timeLimit,
+			'log_type' => $error_type,
+		)
+	);
+
+	// Add a new entry, deleting the old if necessary.
+	$smcFunc['db_insert']('replace',
+		'{db_prefix}log_floodcontrol',
+		array('ip' => 'string-16', 'log_time' => 'int', 'log_type' => 'string'),
+		array($user_info['ip'], time(), $error_type),
+		array('ip', 'log_type')
+	);
+
+	// If affected is 0 or 2, it was there already.
+	if ($smcFunc['db_affected_rows']() != 1)
+	{
+		// Spammer!  You only have to wait a *few* seconds!
+		fatal_lang_error($error_type . 'WaitTime_broken', false, array($timeLimit));
+		return true;
+	}
+
+	// They haven't posted within the limit.
+	return false;
+}
+
+
 ?>

+ 0 - 72
Sources/Subs-Admin.php

@@ -47,10 +47,6 @@ if (!defined('SMF'))
 		- uses the email template and replacements passed in the parameters.
 		- sends them an email.
 
-	bool updateLastDatabaseError()
-		- attempts to use the backup file first, to store the last database error
-		- and only update Settings.php if the first was successful.
-
 */
 
 function getServerVersions($checkFor)
@@ -479,72 +475,4 @@ function emailAdmins($template, $replacements = array(), $additional_recipients
 		}
 }
 
-function updateLastDatabaseError()
-{
-	global $boarddir;
-
-	// Find out this way if we can even write things on this filesystem.
-	// In addition, store things first in the backup file
-
-	$last_settings_change = @filemtime($boarddir . '/Settings.php');
-
-	// Make sure the backup file is there...
-	$file = $boarddir . '/Settings_bak.php';
-	if ((!file_exists($file) || filesize($file) == 0) && !copy($boarddir . '/Settings.php', $file))
-			return false;
-
-	// ...and writable!
-	if (!is_writable($file))
-	{
-		chmod($file, 0755);
-		if (!is_writable($file))
-		{
-			chmod($file, 0775);
-			if (!is_writable($file))
-			{
-				chmod($file, 0777);
-				if (!is_writable($file))
-						return false;
-			}
-		}
-	}
-
-	// Put the new timestamp.
-	$data = file_get_contents($file);
-	$data = preg_replace('~\$db_last_error = \d+;~', '$db_last_error = ' . time() . ';', $data);
-
-	// Open the backup file for writing
-	if ($fp = @fopen($file, 'w'))
-	{
-		// Reset the file buffer.
-		set_file_buffer($fp, 0);
-
-		// Update the file.
-		$t = flock($fp, LOCK_EX);
-		$bytes = fwrite($fp, $data);
-		flock($fp, LOCK_UN);
-		fclose($fp);
-
-		// Was it a success?
-		// ...only relevant if we're still dealing with the same good ole' settings file.
-		clearstatcache();
-		if (($bytes == strlen($data)) && (filemtime($boarddir . '/Settings.php') === $last_settings_change))
-		{
-			// This is our new Settings file...
-			// At least this one is an atomic operation
-			@copy($file, $boarddir . '/Settings.php');
-			return true;
-		}
-		else
-		{
-			// Oops. Someone might have been faster
-			// or we have no more disk space left, troubles, troubles...
-			// Copy the file back and run for your life!
-			@copy($boarddir . '/Settings.php', $file);
-		}
-	}
-
-	return false;
-}
-
 ?>

+ 83 - 18
Sources/Subs-Db-mysql.php

@@ -1,6 +1,8 @@
 <?php
 
 /**
+ * This file has all the main functions in it that relate to the database.
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -14,14 +16,10 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	This file has all the main functions in it that relate to the database.
-
-	smf_db_initiate() maps the implementations in this file (smf_db_function_name)
-	to the $smcFunc['db_function_name'] variable.
-
-*/
-
-// Initialize the database settings
+/**
+ *  Maps the implementations in this file (smf_db_function_name)
+ *  to the $smcFunc['db_function_name'] variable.
+ */
 function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array())
 {
 	global $smcFunc, $mysql_set_mode;
@@ -80,7 +78,13 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix,
 	return $connection;
 }
 
-// Extend the database functionality.
+/**
+ * Extend the database functionality. It calls the respective file's init
+ * to add the implementations in that file to $smcFunc array.
+ *
+ * @param string $type, indicated which additional file to load.
+ * ('extra', 'packages')
+ */
 function db_extend($type = 'extra')
 {
 	global $sourcedir, $db_type;
@@ -90,12 +94,24 @@ function db_extend($type = 'extra')
 	$initFunc();
 }
 
-// Fix up the prefix so it doesn't require the database to be selected.
+/**
+ * Fix up the prefix so it doesn't require the database to be selected.
+ */
 function db_fix_prefix(&$db_prefix, $db_name)
 {
 	$db_prefix = is_numeric(substr($db_prefix, 0, 1)) ? $db_name . '.' . $db_prefix : '`' . $db_name . '`.' . $db_prefix;
 }
 
+/**
+ * Callback for preg_replace_calback on the query.
+ * It allows to replace on the fly a few pre-defined strings, for
+ * convenience ('query_see_board', 'query_wanna_see_board'), with
+ * their current values from $user_info.
+ * In addition, it performs checks and sanitization on the values
+ * sent to the database.
+ *
+ * @param $matches
+ */
 function smf_db_replacement__callback($matches)
 {
 	global $db_callback, $user_info, $db_prefix;
@@ -196,7 +212,10 @@ function smf_db_replacement__callback($matches)
 	}
 }
 
-// Just like the db_query, escape and quote a string, but not executing the query.
+/**
+ * Just like the db_query, escape and quote a string,
+ * but not executing the query.
+ */
 function smf_db_quote($db_string, $db_values, $connection = null)
 {
 	global $db_callback, $db_connection;
@@ -217,7 +236,9 @@ function smf_db_quote($db_string, $db_values, $connection = null)
 	return $db_string;
 }
 
-// Do a query.  Takes care of errors too.
+/**
+ * Do a query.  Takes care of errors too.
+ */
 function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
 {
 	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
@@ -361,6 +382,10 @@ function smf_db_query($identifier, $db_string, $db_values = array(), $connection
 	return $ret;
 }
 
+/**
+ * affected_rows
+ * @param resource $connection
+ */
 function smf_db_affected_rows($connection = null)
 {
 	global $db_connection;
@@ -368,6 +393,13 @@ function smf_db_affected_rows($connection = null)
 	return mysql_affected_rows($connection == null ? $db_connection : $connection);
 }
 
+/**
+ * insert_id
+ *
+ * @param string $table
+ * @param string $field = null
+ * @param resource $connection = null
+ */
 function smf_db_insert_id($table, $field = null, $connection = null)
 {
 	global $db_connection, $db_prefix;
@@ -378,7 +410,12 @@ function smf_db_insert_id($table, $field = null, $connection = null)
 	return mysql_insert_id($connection == null ? $db_connection : $connection);
 }
 
-// Do a transaction.
+/**
+ * Do a transaction.
+ *
+ * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
+ * @param resource $connection = null
+ */
 function smf_db_transaction($type = 'commit', $connection = null)
 {
 	global $db_connection;
@@ -396,7 +433,13 @@ function smf_db_transaction($type = 'commit', $connection = null)
 	return false;
 }
 
-// Database error!
+/**
+ * Database error!
+ * Backtrace, log, try to fix.
+ *
+ * @param string $db_string
+ * @param resource $connection = null
+ */
 function smf_db_error($db_string, $connection = null)
 {
 	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
@@ -585,7 +628,17 @@ function smf_db_error($db_string, $connection = null)
 	fatal_error($context['error_message'], false);
 }
 
-// Insert some data...
+/**
+ * insert
+ *
+ * @param string $method, options 'replace', 'ignore', 'insert'
+ * @param $table
+ * @param $columns
+ * @param $data
+ * @param $keys
+ * @param bool $disable_trans = false
+ * @param resource $connection = null
+ */
 function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
 {
 	global $smcFunc, $db_connection, $db_prefix;
@@ -640,7 +693,15 @@ function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $dis
 	);
 }
 
-// This function tries to work out additional error information from a back trace.
+/**
+ * This function tries to work out additional error information from a back trace.
+ *
+ * @param $error_message
+ * @param $log_message
+ * @param $error_type
+ * @param $file
+ * @param $line
+ */
 function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
 {
 	if (empty($log_message))
@@ -686,8 +747,12 @@ function smf_db_error_backtrace($error_message, $log_message = '', $error_type =
 		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
 }
 
-// Escape the LIKE wildcards so that they match the character and not the wildcard.
-// The optional second parameter turns human readable wildcards into SQL wildcards.
+/**
+ * Escape the LIKE wildcards so that they match the character and not the wildcard.
+ *
+ * @param $string
+ * @param bool $translate_human_wildcards = false, if true, turns human readable wildcards into SQL wildcards.
+ */
 function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false)
 {
 	$replacements = array(

+ 117 - 24
Sources/Subs-Db-postgresql.php

@@ -1,6 +1,8 @@
 <?php
 
 /**
+ * This file has all the main functions in it that relate to the database.
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -14,14 +16,11 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	This file has all the main functions in it that relate to the database.
-
-	smf_db_initiate() maps the implementations in this file (smf_db_function_name)
-	to the $smcFunc['db_function_name'] variable.
-
-*/
-
-// Initialize the database settings
+/**
+ *  Maps the implementations in this file (smf_db_function_name)
+ *  to the $smcFunc['db_function_name'] variable.
+ *  @see Subs-Db-mysql.php#smf_db_initiate
+ */
 function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, &$db_prefix, $db_options = array())
 {
 	global $smcFunc, $mysql_set_mode;
@@ -73,7 +72,10 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, &$db_prefix
 	return $connection;
 }
 
-// Extend the database functionality.
+/**
+ * Extend the database functionality. It calls the respective file's init
+ * to add the implementations in that file to $smcFunc array.
+ */
 function db_extend ($type = 'extra')
 {
 	global $sourcedir, $db_type;
@@ -83,12 +85,26 @@ function db_extend ($type = 'extra')
 	$initFunc();
 }
 
-// Do nothing on postgreSQL
+/**
+ * Fix the database prefix if necessary.
+ * Do nothing on postgreSQL
+ *
+ */
 function db_fix_prefix (&$db_prefix, $db_name)
 {
 	return;
 }
 
+/**
+ * Callback for preg_replace_calback on the query.
+ * It allows to replace on the fly a few pre-defined strings, for
+ * convenience ('query_see_board', 'query_wanna_see_board'), with
+ * their current values from $user_info.
+ * In addition, it performs checks and sanitization on the values
+ * sent to the database.
+ *
+ * @param $matches
+ */
 function smf_db_replacement__callback($matches)
 {
 	global $db_callback, $user_info, $db_prefix;
@@ -188,7 +204,10 @@ function smf_db_replacement__callback($matches)
 	}
 }
 
-// Just like the db_query, escape and quote a string, but not executing the query.
+/**
+ * Just like the db_query, escape and quote a string,
+ * but not executing the query.
+ */
 function smf_db_quote($db_string, $db_values, $connection = null)
 {
 	global $db_callback, $db_connection;
@@ -209,7 +228,11 @@ function smf_db_quote($db_string, $db_values, $connection = null)
 	return $db_string;
 }
 
-// Do a query.  Takes care of errors too.
+/**
+ * Do a query.  Takes care of errors too.
+ * Special queries may need additional replacements to be appropriate
+ * for PostgreSQL.
+ */
 function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
 {
 	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
@@ -432,6 +455,10 @@ function smf_db_query($identifier, $db_string, $db_values = array(), $connection
 	return $db_last_result;
 }
 
+/**
+ * affected_rows
+ * @param resource $connection
+ */
 function smf_db_affected_rows($result = null)
 {
 	global $db_last_result, $db_replace_result;
@@ -444,6 +471,13 @@ function smf_db_affected_rows($result = null)
 	return pg_affected_rows($result == null ? $db_last_result : $result);
 }
 
+/**
+ * insert_id
+ *
+ * @param string $table
+ * @param string $field = null
+ * @param resource $connection = null
+ */
 function smf_db_insert_id($table, $field = null, $connection = null)
 {
 	global $db_connection, $smcFunc, $db_prefix;
@@ -466,7 +500,12 @@ function smf_db_insert_id($table, $field = null, $connection = null)
 	return $lastID;
 }
 
-// Do a transaction.
+/**
+ * Do a transaction.
+ *
+ * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
+ * @param resource $connection = null
+ */
 function smf_db_transaction($type = 'commit', $connection = null)
 {
 	global $db_connection;
@@ -484,7 +523,13 @@ function smf_db_transaction($type = 'commit', $connection = null)
 	return false;
 }
 
-// Database error!
+/**
+ * Database error!
+ * Backtrace, log, try to fix.
+ *
+ * @param string $db_string
+ * @param resource $connection = null
+ */
 function smf_db_error($db_string, $connection = null)
 {
 	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
@@ -529,7 +574,12 @@ function smf_db_error($db_string, $connection = null)
 	fatal_error($context['error_message'], false);
 }
 
-// A PostgreSQL specific function for tracking the current row...
+/**
+ * A PostgreSQL specific function for tracking the current row...
+ *
+ * @param $request
+ * @param $counter
+ */
 function smf_db_fetch_row($request, $counter = false)
 {
 	global $db_row_count;
@@ -545,7 +595,12 @@ function smf_db_fetch_row($request, $counter = false)
 	return @pg_fetch_row($request, $db_row_count[(int) $request]++);
 }
 
-// Get an associative array
+/**
+ * Get an associative array
+ *
+ * @param $request
+ * @param $counter
+ */
 function smf_db_fetch_assoc($request, $counter = false)
 {
 	global $db_row_count;
@@ -561,7 +616,12 @@ function smf_db_fetch_assoc($request, $counter = false)
 	return @pg_fetch_assoc($request, $db_row_count[(int) $request]++);
 }
 
-// Reset the pointer...
+/**
+ * Reset the pointer...
+ *
+ * @param $request
+ * @param $counter
+ */
 function smf_db_data_seek($request, $counter)
 {
 	global $db_row_count;
@@ -571,13 +631,27 @@ function smf_db_data_seek($request, $counter)
 	return true;
 }
 
-// Unescape an escaped string!
+/**
+ * Unescape an escaped string!
+ *
+ * @param $string
+ */
 function smf_db_unescape_string($string)
 {
 	return strtr($string, array('\'\'' => '\''));
 }
 
-// For inserting data in a special way...
+/**
+ * insert
+ *
+ * @param string $method, options 'replace', 'ignore', 'insert'
+ * @param $table
+ * @param $columns
+ * @param $data
+ * @param $keys
+ * @param bool $disable_trans = false
+ * @param resource $connection = null
+ */
 function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
 {
 	global $db_replace_result, $db_in_transact, $smcFunc, $db_connection, $db_prefix;
@@ -673,13 +747,20 @@ function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $dis
 		$smcFunc['db_transaction']('commit', $connection);
 }
 
-// Dummy function really.
+/**
+ * Dummy function really. Doesn't do anything on PostgreSQL.
+ *
+ * @param unknown_type $db_name
+ * @param unknown_type $db_connection
+ */
 function smf_db_select_db($db_name, $db_connection)
 {
 	return true;
 }
 
-// Get the current version.
+/**
+ * Get the current version.
+ */
 function smf_db_version()
 {
 	$version = pg_version();
@@ -687,7 +768,15 @@ function smf_db_version()
 	return $version['client'];
 }
 
-// This function tries to work out additional error information from a back trace.
+/**
+ * This function tries to work out additional error information from a back trace.
+ *
+ * @param $error_message
+ * @param $log_message
+ * @param $error_type
+ * @param $file
+ * @param $line
+ */
 function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
 {
 	if (empty($log_message))
@@ -733,8 +822,12 @@ function smf_db_error_backtrace($error_message, $log_message = '', $error_type =
 		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
 }
 
-// Escape the LIKE wildcards so that they match the character and not the wildcard.
-// The optional second parameter turns human readable wildcards into SQL wildcards.
+/**
+ * Escape the LIKE wildcards so that they match the character and not the wildcard.
+ *
+ * @param $string
+ * @param bool $translate_human_wildcards = false, if true, turns human readable wildcards into SQL wildcards.
+ */
 function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false)
 {
 	$replacements = array(

+ 156 - 34
Sources/Subs-Db-sqlite.php

@@ -1,6 +1,8 @@
 <?php
 
 /**
+ * This file has all the main functions in it that relate to the database.
+ *
  * Simple Machines Forum (SMF)
  *
  * @package SMF
@@ -14,14 +16,10 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	This file has all the main functions in it that relate to the database.
-
-	smf_db_initiate() maps the implementations in this file (smf_db_function_name)
-	to the $smcFunc['db_function_name'] variable.
-
-*/
-
-// Initialize the database settings
+/**
+ *  Maps the implementations in this file (smf_db_function_name)
+ *  to the $smcFunc['db_function_name'] variable.
+ */
 function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix, $db_options = array())
 {
 	global $smcFunc, $mysql_set_mode, $db_in_transact, $sqlite_error;
@@ -88,7 +86,13 @@ function smf_db_initiate($db_server, $db_name, $db_user, $db_passwd, $db_prefix,
 	return $connection;
 }
 
-// Extend the database functionality.
+/**
+ * Extend the database functionality. It calls the respective file's init
+ * to add the implementations in that file to $smcFunc array.
+ *
+ * @param string $type, indicated which additional file to load.
+ * ('extra', 'packages')
+ */
 function db_extend($type = 'extra')
 {
 	global $sourcedir, $db_type;
@@ -98,12 +102,25 @@ function db_extend($type = 'extra')
 	$initFunc();
 }
 
-// SQLite doesn't actually need this!
+/**
+ * Fix db prefix if necessary.
+ * SQLite doesn't actually need this!
+ */
 function db_fix_prefix(&$db_prefix, $db_name)
 {
 	return false;
 }
 
+/**
+ * Callback for preg_replace_calback on the query.
+ * It allows to replace on the fly a few pre-defined strings, for
+ * convenience ('query_see_board', 'query_wanna_see_board'), with
+ * their current values from $user_info.
+ * In addition, it performs checks and sanitization on the values
+ * sent to the database.
+ *
+ * @param $matches
+ */
 function smf_db_replacement__callback($matches)
 {
 	global $db_callback, $user_info, $db_prefix;
@@ -203,7 +220,10 @@ function smf_db_replacement__callback($matches)
 	}
 }
 
-// Just like the db_query, escape and quote a string, but not executing the query.
+/**
+ * Just like the db_query, escape and quote a string,
+ * but not executing the query.
+ */
 function smf_db_quote($db_string, $db_values, $connection = null)
 {
 	global $db_callback, $db_connection;
@@ -224,7 +244,9 @@ function smf_db_quote($db_string, $db_values, $connection = null)
 	return $db_string;
 }
 
-// Do a query.  Takes care of errors too.
+/**
+ * Do a query.  Takes care of errors too.
+ */
 function smf_db_query($identifier, $db_string, $db_values = array(), $connection = null)
 {
 	global $db_cache, $db_count, $db_connection, $db_show_debug, $time_start;
@@ -368,6 +390,10 @@ function smf_db_query($identifier, $db_string, $db_values = array(), $connection
 	return $ret;
 }
 
+/**
+ * affected_rows
+ * @param resource $connection
+ */
 function smf_db_affected_rows($connection = null)
 {
 	global $db_connection;
@@ -375,6 +401,13 @@ function smf_db_affected_rows($connection = null)
 	return sqlite_changes($connection == null ? $db_connection : $connection);
 }
 
+/**
+ * insert_id
+ *
+ * @param string $table
+ * @param string $field = null
+ * @param resource $connection = null
+ */
 function smf_db_insert_id($table, $field = null, $connection = null)
 {
 	global $db_connection, $db_prefix;
@@ -385,7 +418,9 @@ function smf_db_insert_id($table, $field = null, $connection = null)
 	return sqlite_last_insert_rowid($connection == null ? $db_connection : $connection);
 }
 
-// Keeps the connection handle.
+/**
+ * Last error on SQLite
+ */
 function smf_db_last_error()
 {
 	global $db_connection, $sqlite_error;
@@ -394,7 +429,12 @@ function smf_db_last_error()
 	return $query_errno || empty($sqlite_error) ? sqlite_error_string($query_errno) : $sqlite_error;
 }
 
-// Do a transaction.
+/**
+ * Do a transaction.
+ *
+ * @param string $type - the step to perform (i.e. 'begin', 'commit', 'rollback')
+ * @param resource $connection = null
+ */
 function smf_db_transaction($type = 'commit', $connection = null)
 {
 	global $db_connection, $db_in_transact;
@@ -421,7 +461,13 @@ function smf_db_transaction($type = 'commit', $connection = null)
 	return false;
 }
 
-// Database error!
+/**
+ * Database error!
+ * Backtrace, log, try to fix.
+ *
+ * @param string $db_string
+ * @param resource $connection = null
+ */
 function smf_db_error($db_string, $connection = null)
 {
 	global $txt, $context, $sourcedir, $webmaster_email, $modSettings;
@@ -493,7 +539,17 @@ function smf_db_error($db_string, $connection = null)
 	fatal_error($context['error_message'], false);
 }
 
-// Insert some data...
+/**
+ * insert
+ *
+ * @param string $method, options 'replace', 'ignore', 'insert'
+ * @param $table
+ * @param $columns
+ * @param $data
+ * @param $keys
+ * @param bool $disable_trans = false
+ * @param resource $connection = null
+ */
 function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $disable_trans = false, $connection = null)
 {
 	global $db_in_transact, $db_connection, $smcFunc, $db_prefix;
@@ -556,25 +612,46 @@ function smf_db_insert($method = 'replace', $table, $columns, $data, $keys, $dis
 		$smcFunc['db_transaction']('commit', $connection);
 }
 
-// Doesn't do anything on sqlite!
+/**
+ * free_result. Doesn't do anything on sqlite!
+ *
+ * @param resource $handle = false
+ */
 function smf_db_free_result($handle = false)
 {
 	return true;
 }
 
-// Make sure we return no string indexes!
+/**
+ * fetch_row
+ * Make sure we return no string indexes!
+ *
+ * @param $handle
+ */
 function smf_db_fetch_row($handle)
 {
 	return sqlite_fetch_array($handle, SQLITE_NUM);
 }
 
-// Unescape an escaped string!
+/**
+ * Unescape an escaped string!
+ *
+ * @param $string
+ */
 function smf_db_unescape_string($string)
 {
 	return strtr($string, array('\'\'' => '\''));
 }
 
-// This function tries to work out additional error information from a back trace.
+/**
+ * This function tries to work out additional error information from a back trace.
+ *
+ * @param $error_message
+ * @param $log_message
+ * @param $error_type
+ * @param $file
+ * @param $line
+ */
 function smf_db_error_backtrace($error_message, $log_message = '', $error_type = false, $file = null, $line = null)
 {
 	if (empty($log_message))
@@ -620,20 +697,30 @@ function smf_db_error_backtrace($error_message, $log_message = '', $error_type =
 		trigger_error($error_message . ($line !== null ? '<em>(' . basename($file) . '-' . $line . ')</em>' : ''));
 }
 
-// Emulate UNIX_TIMESTAMP.
+/**
+ * Emulate UNIX_TIMESTAMP.
+ */
 function smf_udf_unix_timestamp()
 {
 	return strftime('%s', 'now');
 }
 
-// Emulate INET_ATON.
+/**
+ * Emulate INET_ATON.
+ *
+ * @param $ip
+ */
 function smf_udf_inet_aton($ip)
 {
 	$chunks = explode('.', $ip);
 	return @$chunks[0] * pow(256, 3) + @$chunks[1] * pow(256, 2) + @$chunks[2] * 256 + @$chunks[3];
 }
 
-// Emulate INET_NTOA.
+/**
+ * Emulate INET_NTOA.
+ *
+ * @param $n
+ */
 function smf_udf_inet_ntoa($n)
 {
 	$t = array(0, 0, 0, 0);
@@ -654,7 +741,12 @@ function smf_udf_inet_ntoa($n)
 	return $a;
 }
 
-// Emulate FIND_IN_SET.
+/**
+ * Emulate FIND_IN_SET.
+ *
+ * @param $find
+ * @param $groups
+ */
 function smf_udf_find_in_set($find, $groups)
 {
 	foreach (explode(',', $groups) as $key => $group)
@@ -666,32 +758,50 @@ function smf_udf_find_in_set($find, $groups)
 	return 0;
 }
 
-// Emulate YEAR.
+/**
+ * Emulate YEAR.
+ *
+ * @param $date
+ */
 function smf_udf_year($date)
 {
 	return substr($date, 0, 4);
 }
 
-// Emulate MONTH.
+/**
+ * Emulate MONTH.
+ *
+ * @param $date
+ */
 function smf_udf_month($date)
 {
 	return substr($date, 5, 2);
 }
 
-// Emulate DAYOFMONTH.
+/**
+ * Emulate DAYOFMONTH.
+ *
+ * @param $date
+ */
 function smf_udf_dayofmonth($date)
 {
 	return substr($date, 8, 2);
 }
 
-// We need this since sqlite_libversion() doesn't take any parameters.
+/**
+ * We need this since sqlite_libversion() doesn't take any parameters.
+ *
+ * @param $void
+ */
 function smf_db_libversion($void = null)
 {
 	return sqlite_libversion();
 }
 
-// This function uses variable argument lists so that it can handle more then two parameters.
-// Emulates the CONCAT function.
+/**
+ * This function uses variable argument lists so that it can handle more then two parameters.
+ * Emulates the CONCAT function.
+ */
 function smf_udf_concat()
 {
 	// Since we didn't specify any arguments we must get them from PHP.
@@ -701,13 +811,23 @@ function smf_udf_concat()
 	return implode('', $args);
 }
 
-// We need to use PHP to locate the position in the string.
+/**
+ * We need to use PHP to locate the position in the string.
+ *
+ * @param string $find
+ * @param string $string
+ */
 function smf_udf_locate($find, $string)
 {
 	return strpos($string, $find);
 }
 
-// This is used to replace RLIKE.
+/**
+ * This is used to replace RLIKE.
+ *
+ * @param string $exp
+ * @param string $search
+ */
 function smf_udf_regexp($exp, $search)
 {
 	if (preg_match($exp, $match))
@@ -715,8 +835,10 @@ function smf_udf_regexp($exp, $search)
 	return 0;
 }
 
-// Escape the LIKE wildcards so that they match the character and not the wildcard.
-// The optional second parameter turns human readable wildcards into SQL wildcards.
+/**
+ * Escape the LIKE wildcards so that they match the character and not the wildcard.
+ * The optional second parameter turns human readable wildcards into SQL wildcards.
+ */
 function smf_db_escape_wildcard_string($string, $translate_human_wildcards=false)
 {
 	$replacements = array(

+ 121 - 195
Sources/Subs-Graphics.php

@@ -2,8 +2,9 @@
 
 /**
  * This file deals with low-level graphics operations performed on images,
- * specially as needed for avatars (uploaded avatars), and attachments.
- * It uses, for gifs at least, Gif Util... for more information on that,
+ * specially as needed for avatars (uploaded avatars), attachments, or
+ * visual verification images.
+ * It uses, for gifs at least, Gif Util. For more information on that,
  * please see its website.
  * TrueType fonts supplied by www.LarabieFonts.com
  *
@@ -20,197 +21,16 @@
 if (!defined('SMF'))
 	die('Hacking attempt...');
 
-/*	This whole file deals almost exclusively with handling avatars,
-	specifically uploaded ones.  It uses, for gifs at least, Gif Util... for
-	more information on that, please see its website, shown above.  The other
-	functions are as follows:
-
-	bool downloadAvatar(string url, int id_member, int max_width,
-			int max_height)
-		- downloads file from url and stores it locally for avatar use
-		  by id_member.
-		- supports GIF, JPG, PNG, BMP and WBMP formats.
-		- detects if GD2 is available.
-		- if GIF support isn't present in GD, handles GIFs with gif_loadFile()
-		  and gif_outputAsPng().
-		- uses resizeImageFile() to resize to max_width by max_height,
-		  and saves the result to a file.
-		- updates the database info for the member's avatar.
-		- returns whether the download and resize was successful.
-
-	bool createThumbnail(string source, int max_width, int max_height)
-		- create a thumbnail of the given source.
-		- uses the resizeImageFile function to achieve the resize.
-		- returns whether the thumbnail creation was successful.
-
-	bool reencodeImage(string fileName, int preferred_format = 0)
-		- creates a copy of the file at the same location as fileName.
-		- the file would have the format preferred_format if possible,
-		  otherwise the default format is jpeg.
-		- makes sure that all non-essential image contents are disposed.
-		- returns true on success, false on failure.
-
-	bool checkImageContents(string fileName, bool extensiveCheck = false)
-		- searches through the file to see if there's non-binary content.
-		- if extensiveCheck is true, searches for asp/php short tags as well.
-		- returns true on success, false on failure.
-
-	bool checkGD()
-		- sets a global $gd2 variable needed by some functions to determine
-		  whetehr the GD2 library is present.
-		- returns whether or not GD1 is available.
-
-	void resizeImageFile(string source, string destination,
-			int max_width, int max_height, int preferred_format = 0)
-		- resizes an image from a remote location or a local file.
-		- puts the resized image at the destination location.
-		- the file would have the format preferred_format if possible,
-		  otherwise the default format is jpeg.
-		- returns whether it succeeded.
-
-	void resizeImage(resource src_img, string destination_filename,
-			int src_width, int src_height, int max_width, int max_height,
-			int preferred_format)
-		- resizes src_img proportionally to fit within max_width and
-		  max_height limits if it is too large.
-		- if GD2 is present, it'll use it to achieve better quality.
-		- saves the new image to destination_filename.
-		- saves as preferred_format if possible, default is jpeg.
-
-	void imagecopyresamplebicubic(resource dest_img, resource src_img,
-			int dest_x, int dest_y, int src_x, int src_y, int dest_w,
-			int dest_h, int src_w, int src_h)
-		- used when imagecopyresample() is not available.
-
-	resource gif_loadFile(string filename, int animation_index)
-		- loads a gif file with the Yamasoft GIF utility class.
-		- returns a new GD image.
-
-	bool gif_outputAsPng(resource gif, string destination_filename,
-			int bgColor = -1)
-		- writes a gif file to disk as a png file.
-		- returns whether it was successful or not.
-
-	bool imagecreatefrombmp(string filename)
-		- is set only if it doesn't already exist (for forwards compatiblity.)
-		- only supports uncompressed bitmaps.
-		- returns an image identifier representing the bitmap image obtained
-		  from the given filename.
-
-	bool showCodeImage(string code)
-		- show an image containing the visual verification code for registration.
-		- requires the GD extension.
-		- uses a random font for each letter from default_theme_dir/fonts.
-		- outputs a gif or a png (depending on whether gif ix supported).
-		- returns false if something goes wrong.
-
-	bool showLetterImage(string letter)
-		- show a letter for the visual verification code.
-		- alternative function for showCodeImage() in case GD is missing.
-		- includes an image from a random sub directory of
-		  default_theme_dir/fonts.
-*/
-
-function downloadAvatar($url, $memID, $max_width, $max_height)
-{
-	global $modSettings, $sourcedir, $smcFunc;
-
-	$ext = !empty($modSettings['avatar_download_png']) ? 'png' : 'jpeg';
-	$destName = 'avatar_' . $memID . '_' . time() . '.' . $ext;
-
-	// Just making sure there is a non-zero member.
-	if (empty($memID))
-		return false;
-
-	require_once($sourcedir . '/ManageAttachments.php');
-	removeAttachments(array('id_member' => $memID));
-
-	$id_folder = !empty($modSettings['currentAttachmentUploadDir']) ? $modSettings['currentAttachmentUploadDir'] : 1;
-	$avatar_hash = empty($modSettings['custom_avatar_enabled']) ? getAttachmentFilename($destName, false, null, true) : '';
-	$smcFunc['db_insert']('',
-		'{db_prefix}attachments',
-		array(
-			'id_member' => 'int', 'attachment_type' => 'int', 'filename' => 'string-255', 'file_hash' => 'string-255', 'fileext' => 'string-8', 'size' => 'int',
-			'id_folder' => 'int',
-		),
-		array(
-			$memID, empty($modSettings['custom_avatar_enabled']) ? 0 : 1, $destName, $avatar_hash, $ext, 1,
-			$id_folder,
-		),
-		array('id_attach')
-	);
-	$attachID = $smcFunc['db_insert_id']('{db_prefix}attachments', 'id_attach');
-	// Retain this globally in case the script wants it.
-	$modSettings['new_avatar_data'] = array(
-		'id' => $attachID,
-		'filename' => $destName,
-		'type' => empty($modSettings['custom_avatar_enabled']) ? 0 : 1,
-	);
-
-	$destName = (empty($modSettings['custom_avatar_enabled']) ? (is_array($modSettings['attachmentUploadDir']) ? $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']] : $modSettings['attachmentUploadDir']) : $modSettings['custom_avatar_dir']) . '/' . $destName . '.tmp';
-
-	// Resize it.
-	if (!empty($modSettings['avatar_download_png']))
-		$success = resizeImageFile($url, $destName, $max_width, $max_height, 3);
-	else
-		$success = resizeImageFile($url, $destName, $max_width, $max_height);
-
-	// Remove the .tmp extension.
-	$destName = substr($destName, 0, -4);
-
-	if ($success)
-	{
-		// Walk the right path.
-		if (!empty($modSettings['currentAttachmentUploadDir']))
-		{
-			if (!is_array($modSettings['attachmentUploadDir']))
-				$modSettings['attachmentUploadDir'] = unserialize($modSettings['attachmentUploadDir']);
-			$path = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
-		}
-		else
-			$path = $modSettings['attachmentUploadDir'];
-
-		// Remove the .tmp extension from the attachment.
-		if (rename($destName . '.tmp', empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash))
-		{
-			$destName = empty($avatar_hash) ? $destName : $path . '/' . $attachID . '_' . $avatar_hash;
-			list ($width, $height) = getimagesize($destName);
-			$mime_type = 'image/' . $ext;
-
-			// Write filesize in the database.
-			$smcFunc['db_query']('', '
-				UPDATE {db_prefix}attachments
-				SET size = {int:filesize}, width = {int:width}, height = {int:height},
-					mime_type = {string:mime_type}
-				WHERE id_attach = {int:current_attachment}',
-				array(
-					'filesize' => filesize($destName),
-					'width' => (int) $width,
-					'height' => (int) $height,
-					'current_attachment' => $attachID,
-					'mime_type' => $mime_type,
-				)
-			);
-			return true;
-		}
-		else
-			return false;
-	}
-	else
-	{
-		$smcFunc['db_query']('', '
-			DELETE FROM {db_prefix}attachments
-			WHERE id_attach = {int:current_attachment}',
-			array(
-				'current_attachment' => $attachID,
-			)
-		);
-
-		@unlink($destName . '.tmp');
-		return false;
-	}
-}
-
+/**
+ * Create a thumbnail of the given source.
+ *
+ * @uses resizeImageFile() function to achieve the resize.
+ *
+ * @param string $source
+ * @param int $max_width
+ * @param int $max_height
+ * @return bool, whether the thumbnail creation was successful.
+ */
 function createThumbnail($source, $max_width, $max_height)
 {
 	global $modSettings;
@@ -236,6 +56,16 @@ function createThumbnail($source, $max_width, $max_height)
 	}
 }
 
+/**
+ * Creates a copy of the file at the same location as fileName.
+ * The file would have the format preferred_format if possible,
+ * otherwise the default format is jpeg.
+ * The function makes sure that all non-essential image contents are disposed.
+ *
+ * @param string $fileName
+ * @param int $preferred_format = 0
+ * @return bool, true on success, false on failure.
+ */
 function reencodeImage($fileName, $preferred_format = 0)
 {
 	// There is nothing we can do without GD, sorry!
@@ -259,6 +89,14 @@ function reencodeImage($fileName, $preferred_format = 0)
 	return true;
 }
 
+/**
+ * Searches through the file to see if there's non-binary content.
+ * If extensiveCheck is true, searches for asp/php short tags as well.
+ *
+ * @param string $fileName
+ * @param bool $extensiveCheck = false
+ * @return true on success, false on failure.
+ */
 function checkImageContents($fileName, $extensiveCheck = false)
 {
 	$fp = fopen($fileName, 'rb');
@@ -296,6 +134,12 @@ function checkImageContents($fileName, $extensiveCheck = false)
 	return true;
 }
 
+/**
+ * Sets a global $gd2 variable needed by some functions to determine
+ * whether the GD2 library is present.
+ *
+ * @return whether or not GD1 is available.
+ */
 function checkGD()
 {
 	global $gd2;
@@ -310,6 +154,19 @@ function checkGD()
 	return true;
 }
 
+/**
+ * Resizes an image from a remote location or a local file.
+ * Puts the resized image at the destination location.
+ * The file would have the format preferred_format if possible,
+ * otherwise the default format is jpeg.
+ *
+ * @param string $source
+ * @param string $destination
+ * @param int $max_width
+ * @param int $max_height
+ * @param int $preferred_format = 0
+ * @return whether it succeeded.
+ */
 function resizeImageFile($source, $destination, $max_width, $max_height, $preferred_format = 0)
 {
 	global $sourcedir;
@@ -383,6 +240,23 @@ function resizeImageFile($source, $destination, $max_width, $max_height, $prefer
 	return $success;
 }
 
+/**
+ * Resizes src_img proportionally to fit within max_width and max_height limits
+ * if it is too large.
+ * If GD2 is present, it'll use it to achieve better quality.
+ * It saves the new image to destination_filename, as preferred_format
+ * if possible, default is jpeg.
+ * @uses GD
+ *
+ * @param resource $src_img
+ * @param string $destName
+ * @param int $src_width
+ * @param int $src_height
+ * @param int $max_width
+ * @param int $max_height
+ * @param bool $force_resize = false
+ * @param int $preferred_format = 0
+ */
 function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $max_height, $force_resize = false, $preferred_format = 0)
 {
 	global $gd2, $modSettings;
@@ -454,6 +328,21 @@ function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $
 	return $success;
 }
 
+/**
+ * Copy image.
+ * Used when imagecopyresample() is not available.
+
+ * @param resource $dst_img
+ * @param resource $src_img
+ * @param int $dst_x
+ * @param int $dst_y
+ * @param int $src_x
+ * @param int $src_y
+ * @param int $dst_w
+ * @param int $dst_h
+ * @param int $src_w
+ * @param int $src_h
+ */
 function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h)
 {
 	$palsize = imagecolorstotal($src_img);
@@ -503,6 +392,14 @@ function imagecopyresamplebicubic($dst_img, $src_img, $dst_x, $dst_y, $src_x, $s
 
 if (!function_exists('imagecreatefrombmp'))
 {
+	/**
+	 * It is set only if it doesn't already exist (for forwards compatiblity.)
+	 * It only supports uncompressed bitmaps.
+	 *
+	 * @param string $filename
+	 * @return resource, an image identifier representing the bitmap image
+	 * obtained from the given filename.
+	 */
 	function imagecreatefrombmp($filename)
 	{
 		global $gd2;
@@ -654,6 +551,13 @@ if (!function_exists('imagecreatefrombmp'))
 	}
 }
 
+/**
+ * Loads a gif file with the Yamasoft GIF utility class.
+ *
+ * @param string $lpszFileName
+ * @param int $iIndex
+ * @return resource, a new GD image.
+ */
 function gif_loadFile($lpszFileName, $iIndex = 0)
 {
 	// The classes needed are in this file.
@@ -666,6 +570,14 @@ function gif_loadFile($lpszFileName, $iIndex = 0)
 	return $gif;
 }
 
+/**
+ * Writes a gif file to disk as a png file.
+
+ * @param resource $gif
+ * @param string $lpszFileName
+ * @param int $background_color = -1
+ * @return bool, whether it was successful or not.
+ */
 function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
 {
 	if (!isset($gif) || @get_class($gif) != 'cgif' || !$gif->loaded || $lpszFileName == '')
@@ -685,7 +597,15 @@ function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
 	return true;
 }
 
-// Create the image for the visual verification code.
+/**
+ * Show an image containing the visual verification code for registration.
+ * Requires the GD extension.
+ * Uses a random font for each letter from default_theme_dir/fonts.
+ * Outputs a gif or a png (depending on whether gif ix supported).
+ *
+ * @param string $code
+ * @return false if something goes wrong.
+ */
 function showCodeImage($code)
 {
 	global $settings, $user_info, $modSettings;
@@ -1013,7 +933,13 @@ function showCodeImage($code)
 	die();
 }
 
-// Create a letter for the visual verification code.
+/**
+ * Show a letter for the visual verification code.
+ * Alternative function for showCodeImage() in case GD is missing.
+ * Includes an image from a random sub directory of default_theme_dir/fonts.
+ *
+ * @param string $letter
+ */
 function showLetterImage($letter)
 {
 	global $settings;

+ 0 - 274
Sources/Subs.php

@@ -155,11 +155,6 @@ if (!defined('SMF'))
 		- 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.)
-		- time taken depends on error_type - generally uses the modSetting.
-
 	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.
@@ -2849,107 +2844,7 @@ function obExit($header = null, $do_footer = null, $from_index = false, $from_fa
 		exit;
 }
 
-// Usage: logAction('remove', array('starter' => $id_member_started));
-function logAction($action, $extra = array(), $log_type = 'moderate')
-{
-	global $modSettings, $user_info, $smcFunc, $sourcedir;
-
-	$log_types = array(
-		'moderate' => 1,
-		'user' => 2,
-		'admin' => 3,
-	);
-
-	if (!is_array($extra))
-		trigger_error('logAction(): data is not an array with action \'' . $action . '\'', E_USER_NOTICE);
-
-	// Pull out the parts we want to store separately, but also make sure that the data is proper
-	if (isset($extra['topic']))
-	{
-		if (!is_numeric($extra['topic']))
-			trigger_error('logAction(): data\'s topic is not a number', E_USER_NOTICE);
-		$topic_id = empty($extra['topic']) ? '0' : (int)$extra['topic'];
-		unset($extra['topic']);
-	}
-	else
-		$topic_id = '0';
-
-	if (isset($extra['message']))
-	{
-		if (!is_numeric($extra['message']))
-			trigger_error('logAction(): data\'s message is not a number', E_USER_NOTICE);
-		$msg_id = empty($extra['message']) ? '0' : (int)$extra['message'];
-		unset($extra['message']);
-	}
-	else
-		$msg_id = '0';
-
-	// Is there an associated report on this?
-	if (in_array($action, array('move', 'remove', 'split', 'merge')))
-	{
-		$request = $smcFunc['db_query']('', '
-			SELECT id_report
-			FROM {db_prefix}log_reported
-			WHERE {raw:column_name} = {int:reported}
-			LIMIT 1',
-			array(
-				'column_name' => !empty($msg_id) ? 'id_msg' : 'id_topic',
-				'reported' => !empty($msg_id) ? $msg_id : $topic_id,
-		));
 
-		// Alright, if we get any result back, update open reports.
-		if ($smcFunc['db_num_rows']($request) > 0)
-		{
-			require_once($sourcedir . '/ModerationCenter.php');
-			updateSettings(array('last_mod_report_action' => time()));
-			recountOpenReports();
-		}
-		$smcFunc['db_free_result']($request);
-	}
-
-	// No point in doing anything else, if the log isn't even enabled.
-	if (empty($modSettings['modlog_enabled']) || !isset($log_types[$log_type]))
-		return false;
-
-	if (isset($extra['member']) && !is_numeric($extra['member']))
-		trigger_error('logAction(): data\'s member is not a number', E_USER_NOTICE);
-
-	if (isset($extra['board']))
-	{
-		if (!is_numeric($extra['board']))
-			trigger_error('logAction(): data\'s board is not a number', E_USER_NOTICE);
-		$board_id = empty($extra['board']) ? '0' : (int)$extra['board'];
-		unset($extra['board']);
-	}
-	else
-		$board_id = '0';
-
-	if (isset($extra['board_to']))
-	{
-		if (!is_numeric($extra['board_to']))
-			trigger_error('logAction(): data\'s board_to is not a number', E_USER_NOTICE);
-		if (empty($board_id))
-		{
-			$board_id = empty($extra['board_to']) ? '0' : (int)$extra['board_to'];
-			unset($extra['board_to']);
-		}
-	}
-
-	$smcFunc['db_insert']('',
-		'{db_prefix}log_actions',
-		array(
-			'log_time' => 'int', 'id_log' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string',
-			'id_board' => 'int', 'id_topic' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534',
-		),
-		array(
-			time(), $log_types[$log_type], $user_info['id'], $user_info['ip'], $action,
-			$board_id, $topic_id, $msg_id, serialize($extra),
-		),
-		array('id_action')
-	);
-
-	return $smcFunc['db_insert_id']('{db_prefix}log_actions', 'id_action');
-}
 
 // Track Statistics.
 function trackStats($stats = array())
@@ -3004,58 +2899,6 @@ function trackStats($stats = array())
 	return true;
 }
 
-// Make sure the user isn't posting over and over again.
-function spamProtection($error_type)
-{
-	global $modSettings, $txt, $user_info, $smcFunc;
-
-	// Certain types take less/more time.
-	$timeOverrides = array(
-		'login' => 2,
-		'register' => 2,
-		'sendtopc' => $modSettings['spamWaitTime'] * 4,
-		'sendmail' => $modSettings['spamWaitTime'] * 5,
-		'reporttm' => $modSettings['spamWaitTime'] * 4,
-		'search' => !empty($modSettings['search_floodcontrol_time']) ? $modSettings['search_floodcontrol_time'] : 1,
-	);
-
-	// Moderators are free...
-	if (!allowedTo('moderate_board'))
-		$timeLimit = isset($timeOverrides[$error_type]) ? $timeOverrides[$error_type] : $modSettings['spamWaitTime'];
-	else
-		$timeLimit = 2;
-
-	// Delete old entries...
-	$smcFunc['db_query']('', '
-		DELETE FROM {db_prefix}log_floodcontrol
-		WHERE log_time < {int:log_time}
-			AND log_type = {string:log_type}',
-		array(
-			'log_time' => time() - $timeLimit,
-			'log_type' => $error_type,
-		)
-	);
-
-	// Add a new entry, deleting the old if necessary.
-	$smcFunc['db_insert']('replace',
-		'{db_prefix}log_floodcontrol',
-		array('ip' => 'string-16', 'log_time' => 'int', 'log_type' => 'string'),
-		array($user_info['ip'], time(), $error_type),
-		array('ip', 'log_type')
-	);
-
-	// If affected is 0 or 2, it was there already.
-	if ($smcFunc['db_affected_rows']() != 1)
-	{
-		// Spammer!  You only have to wait a *few* seconds!
-		fatal_lang_error($error_type . 'WaitTime_broken', false, array($timeLimit));
-		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)
 {
@@ -3458,123 +3301,6 @@ function template_footer()
 
 }
 
-// Debugging.
-function db_debug_junk()
-{
-	global $context, $scripturl, $boarddir, $modSettings, $boarddir;
-	global $db_cache, $db_count, $db_show_debug, $cache_count, $cache_hits, $txt;
-
-	// Add to Settings.php if you want to show the debugging information.
-	if (!isset($db_show_debug) || $db_show_debug !== true || (isset($_GET['action']) && $_GET['action'] == 'viewquery') || WIRELESS)
-		return;
-
-	if (empty($_SESSION['view_queries']))
-		$_SESSION['view_queries'] = 0;
-	if (empty($context['debug']['language_files']))
-		$context['debug']['language_files'] = array();
-	if (empty($context['debug']['sheets']))
-		$context['debug']['sheets'] = array();
-
-	$files = get_included_files();
-	$total_size = 0;
-	for ($i = 0, $n = count($files); $i < $n; $i++)
-	{
-		if (file_exists($files[$i]))
-			$total_size += filesize($files[$i]);
-		$files[$i] = strtr($files[$i], array($boarddir => '.'));
-	}
-
-	$warnings = 0;
-	if (!empty($db_cache))
-	{
-		foreach ($db_cache as $q => $qq)
-		{
-			if (!empty($qq['w']))
-				$warnings += count($qq['w']);
-		}
-
-		$_SESSION['debug'] = &$db_cache;
-	}
-
-	// Gotta have valid HTML ;).
-	$temp = ob_get_contents();
-	if (function_exists('ob_clean'))
-		ob_clean();
-	else
-	{
-		ob_end_clean();
-		ob_start('ob_sessrewrite');
-	}
-
-	echo preg_replace('~</body>\s*</html>~', '', $temp), '
-<div class="smalltext" style="text-align: left; margin: 1ex;">
-	', $txt['debug_templates'], count($context['debug']['templates']), ': <em>', implode('</em>, <em>', $context['debug']['templates']), '</em>.<br />
-	', $txt['debug_subtemplates'], count($context['debug']['sub_templates']), ': <em>', implode('</em>, <em>', $context['debug']['sub_templates']), '</em>.<br />
-	', $txt['debug_language_files'], count($context['debug']['language_files']), ': <em>', implode('</em>, <em>', $context['debug']['language_files']), '</em>.<br />
-	', $txt['debug_stylesheets'], count($context['debug']['sheets']), ': <em>', implode('</em>, <em>', $context['debug']['sheets']), '</em>.<br />
-	', $txt['debug_files_included'], count($files), ' - ', round($total_size / 1024), $txt['debug_kb'], ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_include_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_include_info" style="display: none;"><em>', implode('</em>, <em>', $files), '</em></span>)<br />';
-
-	if (!empty($modSettings['cache_enable']) && !empty($cache_hits))
-	{
-		$entries = array();
-		$total_t = 0;
-		$total_s = 0;
-		foreach ($cache_hits as $cache_hit)
-		{
-			$entries[] = $cache_hit['d'] . ' ' . $cache_hit['k'] . ': ' . sprintf($txt['debug_cache_seconds_bytes'], comma_format($cache_hit['t'], 5), $cache_hit['s']);
-			$total_t += $cache_hit['t'];
-			$total_s += $cache_hit['s'];
-		}
-
-		echo '
-	', $txt['debug_cache_hits'], $cache_count, ': ', sprintf($txt['debug_cache_seconds_bytes_total'], comma_format($total_t, 5), comma_format($total_s)), ' (<a href="javascript:void(0);" onclick="document.getElementById(\'debug_cache_info\').style.display = \'inline\'; this.style.display = \'none\'; return false;">', $txt['debug_show'], '</a><span id="debug_cache_info" style="display: none;"><em>', implode('</em>, <em>', $entries), '</em></span>)<br />';
-	}
-
-	echo '
-	<a href="', $scripturl, '?action=viewquery" target="_blank" class="new_win">', $warnings == 0 ? sprintf($txt['debug_queries_used'], (int) $db_count) : sprintf($txt['debug_queries_used_and_warnings'], (int) $db_count, $warnings), '</a><br />
-	<br />';
-
-	if ($_SESSION['view_queries'] == 1 && !empty($db_cache))
-		foreach ($db_cache as $q => $qq)
-		{
-			$is_select = substr(trim($qq['q']), 0, 6) == 'SELECT' || preg_match('~^INSERT(?: IGNORE)? INTO \w+(?:\s+\([^)]+\))?\s+SELECT .+$~s', trim($qq['q'])) != 0;
-			// Temporary tables created in earlier queries are not explainable.
-			if ($is_select)
-			{
-				foreach (array('log_topics_unread', 'topics_posted_in', 'tmp_log_search_topics', 'tmp_log_search_messages') as $tmp)
-					if (strpos(trim($qq['q']), $tmp) !== false)
-					{
-						$is_select = false;
-						break;
-					}
-			}
-			// But actual creation of the temporary tables are.
-			elseif (preg_match('~^CREATE TEMPORARY TABLE .+?SELECT .+$~s', trim($qq['q'])) != 0)
-				$is_select = true;
-
-			// Make the filenames look a bit better.
-			if (isset($qq['f']))
-				$qq['f'] = preg_replace('~^' . preg_quote($boarddir, '~') . '~', '...', $qq['f']);
-
-			echo '
-	<strong>', $is_select ? '<a href="' . $scripturl . '?action=viewquery;qq=' . ($q + 1) . '#qq' . $q . '" target="_blank" class="new_win" style="text-decoration: none;">' : '', nl2br(str_replace("\t", '&nbsp;&nbsp;&nbsp;', htmlspecialchars(ltrim($qq['q'], "\n\r")))) . ($is_select ? '</a></strong>' : '</strong>') . '<br />
-	&nbsp;&nbsp;&nbsp;';
-			if (!empty($qq['f']) && !empty($qq['l']))
-				echo sprintf($txt['debug_query_in_line'], $qq['f'], $qq['l']);
-
-			if (isset($qq['s'], $qq['t']) && isset($txt['debug_query_which_took_at']))
-				echo sprintf($txt['debug_query_which_took_at'], round($qq['t'], 8), round($qq['s'], 8)) . '<br />';
-			elseif (isset($qq['t']))
-				echo sprintf($txt['debug_query_which_took'], round($qq['t'], 8)) . '<br />';
-			echo '
-	<br />';
-		}
-
-	echo '
-	<a href="' . $scripturl . '?action=viewquery;sa=hide">', $txt['debug_' . (empty($_SESSION['view_queries']) ? 'show' : 'hide') . '_queries'], '</a>
-</div></body></html>';
-}
-
 // Get an attachment's encrypted filename.  If $new is true, won't check for file existence.
 function getAttachmentFilename($filename, $attachment_id, $dir = null, $new = false, $file_hash = '')
 {