Bladeren bron

Merge branch 'ImageMagick'

emanuele 13 jaren geleden
bovenliggende
commit
f878ab2654

+ 1 - 0
Sources/Admin.php

@@ -529,6 +529,7 @@ function AdminHome()
 	require_once($sourcedir . '/Subs-Admin.php');
 	$checkFor = array(
 		'gd',
+		'imagick',
 		'db_server',
 		'mmcache',
 		'eaccelerator',

+ 43 - 31
Sources/DbExtra-mysql.php

@@ -252,72 +252,81 @@ function smf_db_list_tables($db = false, $filter = false)
  * @return string, the query to insert the data back in, or an empty
  *  string if the table was empty.
  */
-function smf_db_insert_sql($tableName)
+function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
+	static $start = 0, $num_rows, $fields, $limit, $last_id;
+
+	if ($new_table)
+	{
+		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
+		$start = 0;
+		$last_id = 0;
+	}
 
+	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// Get everything from the table.
+	// This is done this way because retrieve data only with LIMIT will become slower after each query
+	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
+	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT /*!40001 SQL_NO_CACHE */ *
-		FROM `{raw:table}`',
+		FROM `' . $tableName . '`' .
+		(!empty($last_id) && !empty($detected_id) ? '
+		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
+		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
 		array(
-			'table' => $tableName,
+			'security_override' => true,
 		)
 	);
 
 	// The number of rows, just for record keeping and breaking INSERTs up.
 	$num_rows = $smcFunc['db_num_rows']($result);
-	$current_row = 0;
 
 	if ($num_rows == 0)
 		return '';
 
-	$fields = array_keys($smcFunc['db_fetch_assoc']($result));
-	$smcFunc['db_data_seek']($result, 0);
+	if ($new_table)
+	{
+		$fields = array_keys($smcFunc['db_fetch_assoc']($result));
+		$smcFunc['db_data_seek']($result, 0);
+	}
 
 	// Start it off with the basic INSERT INTO.
 	$data = 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
 
 	// Loop through each row.
-	while ($row = $smcFunc['db_fetch_row']($result))
+	while ($row = $smcFunc['db_fetch_assoc']($result))
 	{
-		$current_row++;
-
 		// Get the fields in this row...
 		$field_list = array();
-		for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+
+		foreach ($row as $key => $item)
 		{
 			// Try to figure out the type of each field. (NULL, number, or 'string'.)
-			if (!isset($row[$j]))
+			if (!isset($item))
 				$field_list[] = 'NULL';
-			elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
-				$field_list[] = $row[$j];
+			elseif (is_numeric($item) && (int) $item == $item)
+				$field_list[] = $item;
 			else
-				$field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
+		if (!empty($detected_id) && isset($row[$detected_id]))
+			$last_id = $row[$detected_id];
 
-		// 'Insert' the data.
-		$data .= '(' . implode(', ', $field_list) . ')';
-
-		// All done!
-		if ($current_row == $num_rows)
-			$data .= ';' . $crlf;
-		// Start a new INSERT statement after every 250....
-		elseif ($current_row > 249 && $current_row % 250 == 0)
-			$data .= ';' . $crlf . 'INSERT INTO `' . $tableName . '`' . $crlf . "\t" . '(`' . implode('`, `', $fields) . '`)' . $crlf . 'VALUES ';
-		// Otherwise, go to the next line.
-		else
-			$data .= ',' . $crlf . "\t";
+		$data .= '(' . implode(', ', $field_list) . ')' . ',' . $crlf . "\t";
 	}
+
 	$smcFunc['db_free_result']($result);
+	$data .= ';' . $crlf;
 
-	// Return an empty string if there were no rows.
-	return $num_rows == 0 ? '' : $data;
+	$start += $limit;
+
+	return $data;
 }
 
 /**
@@ -328,9 +337,10 @@ function smf_db_insert_sql($tableName)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";
@@ -373,6 +383,8 @@ function smf_db_table_sql($tableName)
 
 		// And now any extra information. (such as auto_increment.)
 		$schema_create .= ($row['Extra'] != '' ? ' ' . $row['Extra'] : '') . ',' . $crlf;
+		if ($row['Extra'] == 'auto_increment')
+			$detected_id = $row['Field'];
 	}
 	$smcFunc['db_free_result']($result);
 

+ 42 - 17
Sources/DbExtra-postgresql.php

@@ -147,21 +147,35 @@ function smf_db_list_tables($db = false, $filter = false)
  * @return string, the query to insert the data back in, or an empty
  *  string if the table was empty.
  */
-function smf_db_insert_sql($tableName)
+function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
+	static $start = 0, $num_rows, $fields, $limit, $last_id;
+
+	if ($new_table)
+	{
+		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
+		$start = 0;
+		$last_id = 0;
+	}
 
+	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// Get everything from the table.
+	// This is done this way because retrieve data only with LIMIT will become slower after each query
+	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
+	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT *
-		FROM {raw:table}',
+		FROM ' . $tableName .
+		(!empty($last_id) && !empty($detected_id) ? '
+		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
+		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
 		array(
-			'table' => $tableName,
+			'security_override' => true,
 		)
 	);
 
@@ -171,36 +185,45 @@ function smf_db_insert_sql($tableName)
 	if ($num_rows == 0)
 		return '';
 
-	$fields = array_keys($smcFunc['db_fetch_assoc']($result));
-	$smcFunc['db_data_seek']($result, 0);
+	if ($new_table)
+	{
+		$fields = array_keys($smcFunc['db_fetch_assoc']($result));
+		$smcFunc['db_data_seek']($result, 0);
+	}
 
 	// Start it off with the basic INSERT INTO.
 	$data = '';
 	$insert_msg = $crlf . 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
 
 	// Loop through each row.
-	while ($row = $smcFunc['db_fetch_row']($result))
+	while ($row = $smcFunc['db_fetch_assoc']($result))
 	{
 		// Get the fields in this row...
 		$field_list = array();
-		for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+
+		foreach ($row as $key => $item)
 		{
 			// Try to figure out the type of each field. (NULL, number, or 'string'.)
-			if (!isset($row[$j]))
+			if (!isset($item))
 				$field_list[] = 'NULL';
-			elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
-				$field_list[] = $row[$j];
+			elseif (is_numeric($item) && (int) $item == $item)
+				$field_list[] = $item;
 			else
-				$field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
+		if (!empty($detected_id) && isset($row[$detected_id]))
+			$last_id = $row[$detected_id];
 
 		// 'Insert' the data.
-		$data .= $insert_msg . '(' . implode(', ', $field_list) . ');';
+		$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
 	}
 	$smcFunc['db_free_result']($result);
 
-	// Return an empty string if there were no rows.
-	return $num_rows == 0 ? '' : $data;
+	$data .= $crlf;
+
+	$start += $limit;
+
+	return $data;
 }
 
 /**
@@ -211,9 +234,10 @@ function smf_db_insert_sql($tableName)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";
@@ -266,6 +290,7 @@ function smf_db_table_sql($tableName)
 				$smcFunc['db_free_result']($count_req);
 				// Get the right bloody start!
 				$seq_create .= 'CREATE SEQUENCE ' . $matches[1] . ' START WITH ' . ($max_ind + 1) . ';' . $crlf . $crlf;
+				$detected_id = $row['column_name'];
 			}
 		}
 

+ 48 - 21
Sources/DbExtra-sqlite.php

@@ -194,21 +194,35 @@ function smf_db_list_tables($db = false, $filter = false)
  * @return string, the query to insert the data back in, or an empty
  *  string if the table was empty.
  */
-function smf_db_insert_sql($tableName)
+function smf_db_insert_sql($tableName, $new_table = false)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
+	static $start = 0, $num_rows, $fields, $limit, $last_id;
+
+	if ($new_table)
+	{
+		$limit = strstr($tableName, 'log_') !== false ? 500 : 250;
+		$start = 0;
+		$last_id = 0;
+	}
 
+	$data = '';
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
 
 	// This will be handy...
 	$crlf = "\r\n";
 
-	// Get everything from the table.
+	// This is done this way because retrieve data only with LIMIT will become slower after each query
+	// and for long tables (e.g. {db_prefix}messages) it could be a pain...
+	// Instead using WHERE speeds up thing *a lot* (especially after the first 50'000 records)
 	$result = $smcFunc['db_query']('', '
 		SELECT *
-		FROM {raw:table}',
+		FROM ' . $tableName .
+		(!empty($last_id) && !empty($detected_id) ? '
+		WHERE ' . $detected_id . ' > ' . $last_id : '') . '
+		LIMIT ' . (empty($last_id) ? $start . ', ' : '') . $limit,
 		array(
-			'table' => $tableName,
+			'security_override' => true,
 		)
 	);
 
@@ -218,39 +232,51 @@ function smf_db_insert_sql($tableName)
 	if ($num_rows == 0)
 		return '';
 
-	$fields = array_keys($smcFunc['db_fetch_assoc']($result));
+	if ($new_table)
+	{
+		$fields = array_keys($smcFunc['db_fetch_assoc']($result));
 
-	// SQLite fetches an array so we need to filter out the numberic index for the columns.
-	foreach ($fields as $key => $name)
-		if (is_numeric($name))
-			unset($fields[$key]);
+		// SQLite fetches an array so we need to filter out the numberic index for the columns.
+		foreach ($fields as $key => $name)
+			if (is_numeric($name))
+				unset($fields[$key]);
 
-	$smcFunc['db_data_seek']($result, 0);
+		$smcFunc['db_data_seek']($result, 0);
+	}
 
 	// Start it off with the basic INSERT INTO.
 	$data = 'BEGIN TRANSACTION;' . $crlf;
+	$insert_msg = $crlf . 'INSERT INTO ' . $tableName . $crlf . "\t" . '(' . implode(', ', $fields) . ')' . $crlf . 'VALUES ' . $crlf . "\t";
 
 	// Loop through each row.
-	while ($row = $smcFunc['db_fetch_row']($result))
+	while ($row = $smcFunc['db_fetch_assoc']($result))
 	{
 		// Get the fields in this row...
 		$field_list = array();
-		for ($j = 0; $j < $smcFunc['db_num_fields']($result); $j++)
+
+		foreach ($row as $key => $item)
 		{
 			// Try to figure out the type of each field. (NULL, number, or 'string'.)
-			if (!isset($row[$j]))
+			if (!isset($item))
 				$field_list[] = 'NULL';
-			elseif (is_numeric($row[$j]) && (int) $row[$j] == $row[$j])
-				$field_list[] = $row[$j];
+			elseif (is_numeric($item) && (int) $item == $item)
+				$field_list[] = $item;
 			else
-				$field_list[] = '\'' . $smcFunc['db_escape_string']($row[$j]) . '\'';
+				$field_list[] = '\'' . $smcFunc['db_escape_string']($item) . '\'';
 		}
-		$data .= 'INSERT INTO ' . $tableName . ' (' . implode(', ', $fields) . ') VALUES (' . implode(', ', $field_list) . ');' . $crlf;
+		if (!empty($detected_id) && isset($row[$detected_id]))
+			$last_id = $row[$detected_id];
+
+		// 'Insert' the data.
+		$data .= $insert_msg . '(' . implode(', ', $field_list) . ');' . $crlf;
 	}
 	$smcFunc['db_free_result']($result);
 
-	// Return an empty string if there were no rows.
-	return $num_rows == 0 ? '' : $data . 'COMMIT;' . $crlf;
+	$data .= $crlf;
+
+	$start += $limit;
+
+	return $data;
 }
 
 /**
@@ -261,9 +287,10 @@ function smf_db_insert_sql($tableName)
  */
 function smf_db_table_sql($tableName)
 {
-	global $smcFunc, $db_prefix;
+	global $smcFunc, $db_prefix, $detected_id;
 
 	$tableName = str_replace('{db_prefix}', $db_prefix, $tableName);
+	$detected_id = '';
 
 	// This will be needed...
 	$crlf = "\r\n";

+ 94 - 45
Sources/DumpDatabase.php

@@ -27,12 +27,16 @@ if (!defined('SMF'))
  */
 function DumpDatabase2()
 {
-	global $db_name, $scripturl, $context, $modSettings, $crlf, $smcFunc, $db_prefix;
+	global $db_name, $scripturl, $context, $modSettings, $crlf, $smcFunc, $db_prefix, $db_show_debug;
 
 	// Administrators only!
 	if (!allowedTo('admin_forum'))
 		fatal_lang_error('no_dump_database', 'critical');
 
+	// We don't need debug when dumping the database
+	$modSettings['disableQueryCheck'] = true;
+	$db_show_debug = false;
+
 	// You can't dump nothing!
 	if (!isset($_REQUEST['struct']) && !isset($_REQUEST['data']))
 		$_REQUEST['data'] = true;
@@ -44,22 +48,22 @@ function DumpDatabase2()
 
 	// Attempt to stop from dying...
 	@set_time_limit(600);
+	$time_limit = ini_get('max_execution_time');
+	$start_time = time();
 	
 	// @todo ... fail on not getting the requested memory?
 	setMemoryLimit('256M');
+	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / 4;
+	$current_used_memory = 0;
+	$db_backup = '';
+	$output_function = 'un_compressed';
+
+	@ob_end_clean();
 
 	// Start saving the output... (don't do it otherwise for memory reasons.)
 	if (isset($_REQUEST['compress']) && function_exists('gzencode'))
 	{
-		// Make sure we're gzipping output, but then say we're not in the header ^_^.
-		if (empty($modSettings['enableCompressedOutput']))
-			@ob_start('ob_gzhandler', 65536);
-		// Try to clean any data already outputted.
-		elseif (ob_get_length() != 0)
-		{
-			ob_end_clean();
-			@ob_start('ob_gzhandler', 65536);
-		}
+		$output_function = 'gzencode';
 
 		// Send faked headers so it will just save the compressed output as a gzip.
 		header('Content-Type: application/x-gzip');
@@ -101,7 +105,7 @@ function DumpDatabase2()
 	}
 
 	// Send the proper headers to let them download this file.
-	header('Content-Disposition: filename="' . $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . strftime('%Y-%m-%d') . $extension . '"');
+	header('Content-Disposition: attachment; filename="' . $db_name . '-' . (empty($_REQUEST['struct']) ? 'data' : (empty($_REQUEST['data']) ? 'structure' : 'complete')) . '_' . strftime('%Y-%m-%d') . $extension . '"');
 	header('Cache-Control: private');
 	header('Connection: close');
 
@@ -109,13 +113,13 @@ function DumpDatabase2()
 	$crlf = "\r\n";
 
 	// SQL Dump Header.
-	echo
-		'-- ==========================================================', $crlf,
-		'--', $crlf,
-		'-- Database dump of tables in `', $db_name, '`', $crlf,
-		'-- ', timeformat(time(), false), $crlf,
-		'--', $crlf,
-		'-- ==========================================================', $crlf,
+	$db_chunks = 
+		'-- ==========================================================' . $crlf .
+		'--' . $crlf .
+		'-- Database dump of tables in `' . $db_name . '`' . $crlf .
+		'-- ' . timeformat(time(), false) . $crlf .
+		'--' . $crlf .
+		'-- ==========================================================' . $crlf .
 		$crlf;
 
 	// Get all tables in the database....
@@ -134,49 +138,94 @@ function DumpDatabase2()
 	$tables = $smcFunc['db_list_tables'](false, $db_prefix . '%');
 	foreach ($tables as $tableName)
 	{
-		if (function_exists('apache_reset_timeout'))
-			@apache_reset_timeout();
-
 		// Are we dumping the structures?
 		if (isset($_REQUEST['struct']))
 		{
-			echo
-				$crlf,
-				'--', $crlf,
-				'-- Table structure for table `', $tableName, '`', $crlf,
-				'--', $crlf,
-				$crlf,
-				$smcFunc['db_table_sql']($tableName), ';', $crlf;
+			$db_chunks .= 
+				$crlf .
+				'--' . $crlf .
+				'-- Table structure for table `' . $tableName . '`' . $crlf .
+				'--' . $crlf .
+				$crlf .
+				$smcFunc['db_table_sql']($tableName) . ';' . $crlf;
 		}
+		else
+			// This is needed to speedup things later
+			$smcFunc['db_table_sql']($tableName);
 
 		// How about the data?
 		if (!isset($_REQUEST['data']) || substr($tableName, -10) == 'log_errors')
 			continue;
 
+		$first_round = true;
+		$close_table = false;
+
 		// Are there any rows in this table?
-		$get_rows = $smcFunc['db_insert_sql']($tableName);
+		while ($get_rows = $smcFunc['db_insert_sql']($tableName, $first_round))
+		{
+			if (empty($get_rows))
+				break;
+
+			// Time is what we need here!
+			if (function_exists('apache_reset_timeout'))
+				@apache_reset_timeout();
+			elseif (!empty($time_limit) && ($start_time + $time_limit - 20 > time()))
+			{
+				$start_time = time();
+				@set_time_limit(150);
+			}
+
+			if ($first_round)
+			{
+				$db_chunks .= 
+					$crlf .
+					'--' . $crlf .
+					'-- Dumping data in `' . $tableName . '`' . $crlf .
+					'--' . $crlf .
+					$crlf;
+				$first_round = false;
+			}
+			$db_chunks .= 
+				$get_rows;
+			$current_used_memory += $smcFunc['strlen']($db_chunks);
+
+			$db_backup .= $db_chunks;
+			unset($db_chunks);
+			$db_chunks = '';
+			if ($current_used_memory > $memory_limit)
+			{
+				echo $output_function($db_backup);
+				$current_used_memory = 0;
+				// This is probably redundant
+				unset($db_backup);
+				$db_backup = '';
+			}
+			$close_table = true;
+		}
 
 		// No rows to get - skip it.
-		if (empty($get_rows))
-			continue;
-
-		echo
-			$crlf,
-			'--', $crlf,
-			'-- Dumping data in `', $tableName, '`', $crlf,
-			'--', $crlf,
-			$crlf,
-			$get_rows,
-			'-- --------------------------------------------------------', $crlf;
-
-		unset($get_rows);
+		if ($close_table)
+			$db_backup .= 
+			'-- --------------------------------------------------------' . $crlf;
 	}
 
-	echo
-		$crlf,
-		'-- Done', $crlf;
+	$db_backup .= 
+		$crlf .
+		'-- Done' . $crlf;
+
+	echo $output_function($db_backup);
 
 	exit;
 }
 
+/**
+ * Dummy/helper function, it simply returns the string passed as argument
+ * @param $string, a string
+ * @return the string passed
+ */
+function un_compressed($string = '')
+{
+	return $string;
+}
+
 ?>

+ 6 - 6
Sources/ManageAttachments.php

@@ -90,8 +90,8 @@ function ManageAttachmentSettings($return_config = false)
 
 	$context['valid_upload_dir'] = is_dir($modSettings['attachmentUploadDir']) && is_writable($modSettings['attachmentUploadDir']);
 
-	// Perform a test to see if the GD module is installed.
-	$testGD = get_extension_funcs('gd');
+	// Perform a test to see if the GD module or ImageMagick are installed.
+	$testImg = get_extension_funcs('gd') || class_exists('Imagick');
 	$txt['attachmentUploadDir_multiple_configure'] = '<a href="' . $scripturl . '?action=admin;area=manageattachments;sa=attachpaths">[' . $txt['attachmentUploadDir_multiple_configure'] . ']</a>';
 	
 	// See if we can find if the server is set up to support the attacment limits
@@ -123,7 +123,7 @@ function ManageAttachmentSettings($return_config = false)
 			array('text', 'attachmentExtensions', 40),
 		'',
 			// Image checks.
-			array('warning', empty($testGD) ? 'attachment_gd_warning' : ''),
+			array('warning', empty($testImg) ? 'attachment_img_enc_warning' : ''),
 			array('check', 'attachment_image_reencode'),
 		'',
 			array('warning', 'attachment_image_paranoid_warning'),
@@ -178,8 +178,8 @@ function ManageAvatarSettings($return_config = false)
 {
 	global $txt, $context, $modSettings, $sourcedir, $scripturl;
 
-	// Perform a test to see if the GD module is installed.
-	$testGD = get_extension_funcs('gd');
+	// Perform a test to see if the GD module or ImageMagick are installed.
+	$testImg = get_extension_funcs('gd') || class_exists('Imagick');
 
 	$context['valid_avatar_dir'] = is_dir($modSettings['avatar_directory']);
 	$context['valid_custom_avatar_dir'] = empty($modSettings['custom_avatar_enabled']) || (!empty($modSettings['custom_avatar_dir']) && is_dir($modSettings['custom_avatar_dir']) && is_writable($modSettings['custom_avatar_dir']));
@@ -187,7 +187,7 @@ function ManageAvatarSettings($return_config = false)
 	$config_vars = array(
 		// Server stored avatars!
 		array('title', 'avatar_server_stored'),
-			array('warning', empty($testGD) ? 'avatar_gd_warning' : ''),
+			array('warning', empty($testImg) ? 'avatar_img_enc_warning' : ''),
 			array('permissions', 'profile_server_avatar', 0, $txt['avatar_server_stored_groups']),
 			array('text', 'avatar_directory', 40, 'invalid' => !$context['valid_avatar_dir']),
 			array('text', 'avatar_url', 40),

+ 45 - 1
Sources/ManageMaintenance.php

@@ -126,12 +126,56 @@ function ManageMaintenance()
  */
 function MaintainDatabase()
 {
-	global $context, $db_type, $db_character_set, $modSettings, $smcFunc, $txt;
+	global $context, $db_type, $db_character_set, $modSettings, $smcFunc, $txt, $maintenance;
 
 	// Show some conversion options?
 	$context['convert_utf8'] = $db_type == 'mysql' && (!isset($db_character_set) || $db_character_set !== 'utf8' || empty($modSettings['global_character_set']) || $modSettings['global_character_set'] !== 'UTF-8') && version_compare('4.1.2', preg_replace('~\-.+?$~', '', $smcFunc['db_server_info']()), '<=');
 	$context['convert_entities'] = $db_type == 'mysql' && isset($db_character_set, $modSettings['global_character_set']) && $db_character_set === 'utf8' && $modSettings['global_character_set'] === 'UTF-8';
 
+	// Check few things to give advices before make a backup
+	// If safe mod is enable the external tool is *always* the best (and probably the only) solution
+	$context['safe_mode_enable'] = @ini_get('safe_mode');
+	// This is just a...guess
+	$result = $smcFunc['db_query']('', '
+		SELECT COUNT(*)
+		FROM {db_prefix}messages',
+		array()
+	);
+	list($messages) = $smcFunc['db_fetch_row']($result);
+	$smcFunc['db_free_result']($result);
+
+	// 256 is what we use in the backup script
+	setMemoryLimit('256M');
+	$memory_limit = memoryReturnBytes(ini_get('memory_limit')) / (1024 * 1024);
+	// Zip limit is set to more or less 1/4th the size of the available memory * 1500
+	// 1500 is an estimate of the number of messages that generates a database of 1 MB (yeah I know IT'S AN ESTIMATION!!!)
+	// Why that? Because the only reliable zip package is the one sent out the first time, 
+	// so when the backup takes 1/5th (just to stay on the safe side) of the memory available
+	$zip_limit = $memory_limit * 1500 / 5;
+	// Here is more tricky: it depends on many factors, but the main idea is that
+	// if it takes "too long" the backup is not reliable. So, I know that on my computer it take
+	// 20 minutes to backup 2.5 GB, of course my computer is not representative, so I'll multiply by 4 the time.
+	// I would consider "too long" 5 minutes (I know it can be a long time, but let's start with that:
+	// 80 minutes for a 2.5 GB and a 5 minutes limit means 160 MB approx
+	$plain_limit = 240000;
+
+	$context['use_maintenance'] = 0;
+
+	if ($context['safe_mode_enable'])
+		$context['suggested_method'] = 'use_exernal_tool';
+	elseif ($zip_limit < $plain_limit && $messages < $zip_limit)
+		$context['suggested_method'] = 'zipped_file';
+	elseif ($zip_limit > $plain_limit || ($zip_limit < $plain_limit && $plain_limit < $messages))
+	{
+		$context['suggested_method'] = 'use_exernal_tool';
+		$context['use_maintenance'] = empty($maintenance) ? 2 : 0;
+	}
+	else
+	{
+		$context['use_maintenance'] = 1;
+		$context['suggested_method'] = 'plain_text';
+	}
+
 	if (isset($_GET['done']) && $_GET['done'] == 'convertutf8')
 		$context['maintenance_finished'] = $txt['utf8_title'];
 	if (isset($_GET['done']) && $_GET['done'] == 'convertentities')

+ 8 - 0
Sources/Subs-Admin.php

@@ -35,6 +35,14 @@ function getServerVersions($checkFor)
 		$versions['gd'] = array('title' => $txt['support_versions_gd'], 'version' => $temp['GD Version']);
 	}
 
+	// Why not have a look at ImageMagick? If it is, we should show version information for it too.
+	if (in_array('imagick', $checkFor) && class_exists('Imagick'))
+	{
+		$temp = New Imagick;
+		$temp2 = $temp->getVersion();
+		$versions['imagick'] = array('title' => $txt['support_versions_imagick'], 'version' => $temp2['versionString']);
+	}
+
 	// Now lets check for the Database.
 	if (in_array('db_server', $checkFor))
 	{

+ 95 - 70
Sources/Subs-Graphics.php

@@ -184,10 +184,6 @@ function createThumbnail($source, $max_width, $max_height)
  */
 function reencodeImage($fileName, $preferred_format = 0)
 {
-	// There is nothing we can do without GD, sorry!
-	if (!checkGD())
-		return false;
-
 	if (!resizeImageFile($fileName, $fileName . '.tmp', null, null, $preferred_format))
 	{
 		if (file_exists($fileName . '.tmp'))
@@ -201,8 +197,6 @@ function reencodeImage($fileName, $preferred_format = 0)
 
 	if (!rename($fileName . '.tmp', $fileName))
 		return false;
-
-	return true;
 }
 
 /**
@@ -270,6 +264,16 @@ function checkGD()
 	return true;
 }
 
+/**
+ * Checks whether the Imagick class is present.
+ *
+ * @return whether or not Imagick is available.
+ */
+function checkImagick()
+{
+	return class_exists('Imagick', false);
+}
+
 /**
  * See if we have enough memory to thumbnail an image
  *
@@ -312,8 +316,8 @@ function resizeImageFile($source, $destination, $max_width, $max_height, $prefer
 {
 	global $sourcedir;
 
-	// Nothing to do without GD
-	if (!checkGD())
+	// Nothing to do without GD or IM
+	if (!checkGD() && !checkImagick())
 		return false;
 
 	static $default_formats = array(
@@ -326,8 +330,6 @@ function resizeImageFile($source, $destination, $max_width, $max_height, $prefer
 
 	require_once($sourcedir . '/Subs-Package.php');
 
-	$success = false;
-
 	// Get the image file, we have to work with something after all
 	$fp_destination = fopen($destination, 'wb');
 	if ($fp_destination && substr($source, 0, 7) == 'http://')
@@ -359,22 +361,25 @@ function resizeImageFile($source, $destination, $max_width, $max_height, $prefer
 		$sizes = array(-1, -1, -1);
 		
 	// See if we have -or- can get the needed memory for this operation
-	if (!imageMemoryCheck($sizes))
+	if (checkGD() && !imageMemoryCheck($sizes))
 		return false;
 
 	// A known and supported format?
 	// @todo test PSD and gif.
-	if (isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
+	if (checkImagick() && isset($default_formats[$sizes[2]]))
+	{
+		return resizeImage(null, $destination, null, null, $max_width, $max_height, true, $preferred_format);
+	}
+	elseif (checkGD() && isset($default_formats[$sizes[2]]) && function_exists('imagecreatefrom' . $default_formats[$sizes[2]]))
 	{
 		$imagecreatefrom = 'imagecreatefrom' . $default_formats[$sizes[2]];
 		if ($src_img = @$imagecreatefrom($destination))
 		{
-			resizeImage($src_img, $destination, imagesx($src_img), imagesy($src_img), $max_width === null ? imagesx($src_img) : $max_width, $max_height === null ? imagesy($src_img) : $max_height, true, $preferred_format);
-			$success = true;
+			return resizeImage($src_img, $destination, imagesx($src_img), imagesy($src_img), $max_width === null ? imagesx($src_img) : $max_width, $max_height === null ? imagesy($src_img) : $max_height, true, $preferred_format);
 		}
 	}
 
-	return $success;
+	return false;
 }
 
 /**
@@ -398,71 +403,96 @@ function resizeImage($src_img, $destName, $src_width, $src_height, $max_width, $
 {
 	global $gd2, $modSettings;
 
-	// Without GD, no image resizing at all.
-	if (!checkGD())
-		return false;
+	if (checkImagick())
+	{
+		static $default_formats = array(
+			'1' => 'gif',
+			'2' => 'jpeg',
+			'3' => 'png',
+			'6' => 'bmp',
+			'15' => 'wbmp'
+		);
+		$preferred_format = empty($preferred_format) || !isset($default_formats[$preferred_format]) ? 2 : $preferred_format;
+
+		$imagick = New Imagick($destName);
+		$src_width = empty($src_width) ? $imagick->getImageWidth() : $src_width;
+		$src_height = empty($src_height) ? $imagick->getImageHeight() : $src_height;
+		$dest_width = empty($max_width) ? $src_width : $max_width;
+		$dest_height = empty($max_height) ? $src_height : $max_height;
 
-	$success = false;
+		$imagick->setImageFormat($default_formats[$preferred_format]);
+		$imagick->resizeImage($dest_width, $dest_height, Imagick::FILTER_LANCZOS, 1, true);
+		$success = $imagick->writeImage($destName);
 
-	// Determine whether to resize to max width or to max height (depending on the limits.)
-	if (!empty($max_width) || !empty($max_height))
+		return !empty($success);
+	}
+	elseif (checkGD())
 	{
-		if (!empty($max_width) && (empty($max_height) || $src_height * $max_width / $src_width <= $max_height))
-		{
-			$dst_width = $max_width;
-			$dst_height = floor($src_height * $max_width / $src_width);
-		}
-		elseif (!empty($max_height))
-		{
-			$dst_width = floor($src_width * $max_height / $src_height);
-			$dst_height = $max_height;
-		}
+		$success = false;
 
-		// Don't bother resizing if it's already smaller...
-		if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
+		// Determine whether to resize to max width or to max height (depending on the limits.)
+		if (!empty($max_width) || !empty($max_height))
 		{
-			// (make a true color image, because it just looks better for resizing.)
-			if ($gd2)
+			if (!empty($max_width) && (empty($max_height) || $src_height * $max_width / $src_width <= $max_height))
 			{
-				$dst_img = imagecreatetruecolor($dst_width, $dst_height);
+				$dst_width = $max_width;
+				$dst_height = floor($src_height * $max_width / $src_width);
+			}
+			elseif (!empty($max_height))
+			{
+				$dst_width = floor($src_width * $max_height / $src_height);
+				$dst_height = $max_height;
+			}
 
-				// Deal nicely with a PNG - because we can.
-				if ((!empty($preferred_format)) && ($preferred_format == 3))
+			// Don't bother resizing if it's already smaller...
+			if (!empty($dst_width) && !empty($dst_height) && ($dst_width < $src_width || $dst_height < $src_height || $force_resize))
+			{
+				// (make a true color image, because it just looks better for resizing.)
+				if ($gd2)
 				{
-					imagealphablending($dst_img, false);
-					if (function_exists('imagesavealpha'))
-						imagesavealpha($dst_img, true);
+					$dst_img = imagecreatetruecolor($dst_width, $dst_height);
+
+					// Deal nicely with a PNG - because we can.
+					if ((!empty($preferred_format)) && ($preferred_format == 3))
+					{
+						imagealphablending($dst_img, false);
+						if (function_exists('imagesavealpha'))
+							imagesavealpha($dst_img, true);
+					}
 				}
-			}
-			else
-				$dst_img = imagecreate($dst_width, $dst_height);
+				else
+					$dst_img = imagecreate($dst_width, $dst_height);
 
-			// Resize it!
-			if ($gd2)
-				imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
+				// Resize it!
+				if ($gd2)
+					imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
+				else
+					imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
+			}
 			else
-				imagecopyresamplebicubic($dst_img, $src_img, 0, 0, 0, 0, $dst_width, $dst_height, $src_width, $src_height);
+				$dst_img = $src_img;
 		}
 		else
 			$dst_img = $src_img;
+
+		// Save the image as ...
+		if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
+			$success = imagepng($dst_img, $destName);
+		elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
+			$success = imagegif($dst_img, $destName);
+		elseif (function_exists('imagejpeg'))
+			$success = imagejpeg($dst_img, $destName);
+
+		// Free the memory.
+		imagedestroy($src_img);
+		if ($dst_img != $src_img)
+			imagedestroy($dst_img);
+
+		return $success;
 	}
 	else
-		$dst_img = $src_img;
-
-	// Save the image as ...
-	if (!empty($preferred_format) && ($preferred_format == 3) && function_exists('imagepng'))
-		$success = imagepng($dst_img, $destName);
-	elseif (!empty($preferred_format) && ($preferred_format == 1) && function_exists('imagegif'))
-		$success = imagegif($dst_img, $destName);
-	elseif (function_exists('imagejpeg'))
-		$success = imagejpeg($dst_img, $destName);
-
-	// Free the memory.
-	imagedestroy($src_img);
-	if ($dst_img != $src_img)
-		imagedestroy($dst_img);
-
-	return $success;
+		// Without GD, no image resizing at all.
+		return false;
 }
 
 /**
@@ -735,7 +765,7 @@ function gif_outputAsPng($gif, $lpszFileName, $background_color = -1)
  */
 function showCodeImage($code)
 {
-	global $settings, $user_info, $modSettings;
+	global $gd2, $settings, $user_info, $modSettings;
 
 	// Note: The higher the value of visual_verification_type the harder the verification is - from 0 as disabled through to 4 as "Very hard".
 
@@ -773,11 +803,6 @@ function showCodeImage($code)
 	// Give the image a border?
 	$hasBorder = $simpleBGColor;
 
-	// Is this GD2? Needed for pixel size.
-	$testGD = get_extension_funcs('gd');
-	$gd2 = in_array('imagecreatetruecolor', $testGD) && function_exists('imagecreatetruecolor');
-	unset($testGD);
-
 	// The amount of pixels inbetween characters.
 	$character_spacing = 1;
 

+ 14 - 2
Themes/default/ManageMaintenance.template.php

@@ -60,13 +60,25 @@ function template_maintain_database()
 					<p><input type="submit" value="', $txt['maintain_backup_save'], '" id="submitDump" class="button_submit" />
 					<br class="clear_right" /></p>';
 	else
+	{
+		if ($context['safe_mode_enable'])
+			echo '
+					<div class="errorbox">', $txt['safe_mode_enabled'], '</div>';
+		else
+			echo '
+					<div class="', $context['suggested_method'] == 'use_exernal_tool' || $context['use_maintenance'] != 0 ? 'errorbox' : 'noticebox', '">
+						', $txt[$context['suggested_method']],
+						$context['use_maintenance'] != 0 ? '<br />' . $txt['enable_maintenance' . $context['use_maintenance']] : '',
+					'</div>';
+
 		echo '
 					<p><label for="struct"><input type="checkbox" name="struct" id="struct" onclick="document.getElementById(\'submitDump\').disabled = !document.getElementById(\'struct\').checked &amp;&amp; !document.getElementById(\'data\').checked;" class="input_check" checked="checked" /> ', $txt['maintain_backup_struct'], '</label><br />
 					<label for="data"><input type="checkbox" name="data" id="data" onclick="document.getElementById(\'submitDump\').disabled = !document.getElementById(\'struct\').checked &amp;&amp; !document.getElementById(\'data\').checked;" checked="checked" class="input_check" /> ', $txt['maintain_backup_data'], '</label><br />
-					<label for="compress"><input type="checkbox" name="compress" id="compress" value="gzip" checked="checked" class="input_check" /> ', $txt['maintain_backup_gz'], '</label></p>
+					<label for="compress"><input type="checkbox" name="compress" id="compress" value="gzip"', $context['suggested_method'] == 'zipped_file' ? ' checked="checked"' : '', ' class="input_check" /> ', $txt['maintain_backup_gz'], '</label></p>
 					<hr class="hrcolor" />
-					<p><input type="submit" value="', $txt['maintain_backup_save'], '" id="submitDump" onclick="return document.getElementById(\'struct\').checked || document.getElementById(\'data\').checked;" class="button_submit" />
+					<p><input ', $context['use_maintenance'] == 2 ? 'disabled="disabled" ' : '', 'type="submit" value="', $txt['maintain_backup_save'], '" id="submitDump" onclick="return document.getElementById(\'struct\').checked || document.getElementById(\'data\').checked;" class="button_submit" />
 					<br class="clear_right" /></p>';
+	}
 
 	echo '
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />

+ 4 - 3
Themes/default/languages/Admin.english.php

@@ -299,6 +299,7 @@ $txt['support_versions_php'] = 'PHP version';
 $txt['support_versions_db'] = '%1$s version';
 $txt['support_versions_server'] = 'Server version';
 $txt['support_versions_gd'] = 'GD version';
+$txt['support_versions_imagick'] = 'Imagick version';
 $txt['support_versions'] = 'Version Information';
 $txt['support_resources'] = 'Support Resources';
 $txt['support_resources_p1'] = 'Our <a href="%1$s">Online Manual</a> provides the main documentation for SMF. The SMF Online Manual has many documents to help answer support questions and explain <a href="%2$s">Features</a>, <a href="%3$s">Settings</a>, <a href="%4$s">Themes</a>, <a href="%5$s">Packages</a>, etc. The Online Manual documents each area of SMF thoroughly and should answer most questions quickly.';
@@ -358,11 +359,11 @@ $txt['attachmentDirSizeLimit'] = 'Max attachment folder space';
 $txt['attachmentPostLimit'] = 'Max attachment size per post';
 $txt['attachmentSizeLimit'] = 'Max size per attachment';
 $txt['attachmentNumPerPostLimit'] = 'Max number of attachments per post';
-$txt['attachment_gd_warning'] = 'The GD module is currently not installed. Image re-encoding is not possible.';
+$txt['attachment_img_enc_warning'] = 'Neither the GD module nor ImageMagick are currently installed. Image re-encoding is not possible.';
 $txt['attachment_postsize_warning'] = 'The current php.ini setting \'post_max_size\' may not support this.';
 $txt['attachment_filesize_warning'] = 'The current php.ini setting \'upload_max_filesize\' may not support this.';
 $txt['attachment_image_reencode'] = 'Re-encode potentially dangerous image attachments';
-$txt['attachment_image_reencode_note'] = '(requires GD module)';
+$txt['attachment_image_reencode_note'] = '(requires GD module or ImageMagick)';
 $txt['attachment_image_paranoid_warning'] = 'The extensive security checks can result in a large number of rejected attachments.';
 $txt['attachment_image_paranoid'] = 'Perform extensive security checks on uploaded image attachments';
 $txt['attachmentThumbnails'] = 'Resize images when showing under posts';
@@ -406,7 +407,7 @@ $txt['avatar_max_height_upload'] = 'Maximum height of uploaded avatar';
 $txt['avatar_resize_upload'] = 'Resize oversized large avatars';
 $txt['avatar_resize_upload_note'] = '(requires GD module)';
 $txt['avatar_download_png'] = 'Use PNG for resized avatars';
-$txt['avatar_gd_warning'] = 'The GD module is currently not installed. Some avatar features are disabled.';
+$txt['avatar_img_enc_warning'] = 'Neither the GD module nor ImageMagick are currently installed. Some avatar features are disabled.';
 $txt['avatar_external'] = 'External avatars';
 $txt['avatar_upload'] = 'Uploadable avatars';
 $txt['avatar_server_stored'] = 'Server-stored avatars';

+ 7 - 0
Themes/default/languages/ManageMaintenance.english.php

@@ -216,4 +216,11 @@ $txt['reattribute_cannot_find_member'] = 'Could not find member to attribute pos
 $txt['maintain_recountposts'] = 'Recount User Posts';
 $txt['maintain_recountposts_info'] = 'Run this maintenance task to update your users total post count.  It will recount all (countable) posts made by each user and then update their profile post count totals';
 
+$txt['safe_mode_enabled'] = '<a href="http://php.net/manual/en/features.safe-mode.php">safe_mode</a> is enabled on your server!<br />The backup done with this tool cannot be considered reliable!';
+$txt['use_exernal_tool'] = 'Please take in consideration to use an external tool to backup your database, any backup done with this tool cannot be considered 100% reliable.';
+$txt['zipped_file'] = 'If you want you can create a zipped backup.';
+$txt['plain_text'] = 'The more appropriate method to backup your database is to create a plain text file, a compressed package could not be completely reliable.';
+$txt['enable_maintenance1'] = 'Due to the size of your forum, is reccomended to enable the maintenance mode before start the backup.';
+$txt['enable_maintenance2'] = 'If you want to procede, due to the size of your forum, before start the backup please enable the maintenance mode.';
+
 ?>