Browse Source

! Sorting the memberlist by online status caused an error in PostgreSQL (Memberlist.php, thank you TheStupidOne!)
! Small doc changes.
+ Add support for IPv6 (Subs.php, Security.php, ManageSearchEngines.php, ManageBans.php, QueryString.php, install_2-1_*.sql, upgrade_2-1_*.sql) [Feature 3987,3153,4593]
+ Allow css to target actions and/or specific boards (index Template) [Feature 2958]
+ Added ability to remove spider stats (ManageSearchEngines.php, Search language, ManageSearch template) [Feature 4757]
& Moved Search Engine details to helptext and used better descriptions (Search language, Help language) [Feature 4568]
! Disabling guest access would break subscriptions. (subscriptions.php) [Bug 4835]
+ Allow for separate full backups and file backups during package manager operations (Subs-Package.php, Packages.php, Packages language, Packages template, upgrade_2-1_*.sql)
! Bad password checks still resulting in the language changing (Profile-Modify.php) [Bug 4805]
! Postgresql does not like && (Load.php) [Bug 4819]

Spuds 12 years ago
parent
commit
de099bdbc2

+ 3 - 2
Sources/Display.php

@@ -128,8 +128,8 @@ function Display()
 		$_REQUEST['start'] = 'new';
 	}
 
-	// Add 1 to the number of views of this topic.
-	if (!$user_info['possibly_robot'] && empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic)
+	// Add 1 to the number of views of this topic (except for robots).
+	if (!$user_info['possibly_robot'] && (empty($_SESSION['last_read_topic']) || $_SESSION['last_read_topic'] != $topic))
 	{
 		$smcFunc['db_query']('', '
 			UPDATE {db_prefix}topics
@@ -980,6 +980,7 @@ function Display()
 	);
 
 	// Set the callback.  (do you REALIZE how much memory all the messages would take?!?)
+	// This will be called from the template.
 	$context['get_message'] = 'prepareDisplayContext';
 
 	// Now set all the wonderful, wonderful permissions... like moderation ones...

+ 1 - 1
Sources/Load.php

@@ -767,7 +767,7 @@ function loadPermissions()
 	}
 
 	// If it is detected as a robot, and we are restricting permissions as a special group - then implement this.
-	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} && add_deny = 0)' : '';
+	$spider_restrict = $user_info['possibly_robot'] && !empty($modSettings['spider_group']) ? ' OR (id_group = {int:spider_group} AND add_deny = 0)' : '';
 
 	if (empty($user_info['permissions']))
 	{

+ 82 - 10
Sources/ManageBans.php

@@ -375,6 +375,14 @@ function BanEdit()
 			'ip_high3' => 0,
 			'ip_low4' => 0,
 			'ip_high4' => 0,
+			'ip_low5' => 0,
+			'ip_high5' => 0,
+			'ip_low6' => 0,
+			'ip_high6' => 0,
+			'ip_low7' => 0,
+			'ip_high7' => 0,
+			'ip_low8' => 0,
+			'ip_high8' => 0,
 		);
 
 		// Preset all values that are required.
@@ -393,6 +401,14 @@ function BanEdit()
 				'ip_high3' => 'int',
 				'ip_low4' => 'int',
 				'ip_high4' => 'int',
+				'ip_low5' => 'int',
+				'ip_high5' => 'int',
+				'ip_low6' => 'int',
+				'ip_high6' => 'int',
+				'ip_low7' => 'int',
+				'ip_high7' => 'int',
+				'ip_low8' => 'int',
+				'ip_high8' => 'int',
 			);
 		}
 		else
@@ -401,7 +417,11 @@ function BanEdit()
 				ip_low1 = {int:ip_low1}, ip_high1 = {int:ip_high1},
 				ip_low2 = {int:ip_low2}, ip_high2 = {int:ip_high2},
 				ip_low3 = {int:ip_low3}, ip_high3 = {int:ip_high3},
-				ip_low4 = {int:ip_low4}, ip_high4 = {int:ip_high4}';
+				ip_low4 = {int:ip_low4}, ip_high4 = {int:ip_high4},
+				ip_low5 = {int:ip_low5}, ip_high5 = {int:ip_high5},
+				ip_low6 = {int:ip_low6}, ip_high6 = {int:ip_high6},
+				ip_low7 = {int:ip_low7}, ip_high7 = {int:ip_high7},
+				ip_low8 = {int:ip_low8}, ip_high8 = {int:ip_high8}';
 
 		if ($_POST['bantype'] == 'ip_ban')
 		{
@@ -598,6 +618,14 @@ function BanEdit()
 						$ip_parts[2]['high'],
 						$ip_parts[3]['low'],
 						$ip_parts[3]['high'],
+						$ip_parts[4]['low'],
+						$ip_parts[4]['high'],
+						$ip_parts[5]['low'],
+						$ip_parts[5]['high'],
+						$ip_parts[6]['low'],
+						$ip_parts[6]['high'],
+						$ip_parts[7]['low'],
+						$ip_parts[7]['high'],
 						'',
 						'',
 						0,
@@ -616,7 +644,7 @@ function BanEdit()
 					$_POST['hostname'] = str_replace('*', '%', $_POST['hostname']);
 
 					$ban_triggers[] = array(
-						0, 0, 0, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 						substr($_POST['hostname'], 0, 255),
 						'',
 						0,
@@ -632,7 +660,7 @@ function BanEdit()
 					$_POST['email'] = strtolower(str_replace('*', '%', $_POST['email']));
 
 					$ban_triggers[] = array(
-						0, 0, 0, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 						'',
 						substr($_POST['email'], 0, 255),
 						0,
@@ -668,7 +696,7 @@ function BanEdit()
 					}
 
 					$ban_triggers[] = array(
-						0, 0, 0, 0, 0, 0, 0, 0,
+						0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
 						'',
 						'',
 						(int) $_POST['bannedUser'],
@@ -691,7 +719,7 @@ function BanEdit()
 						$ip_parts = ip2range($ip);
 
 						// They should be alright, but just to be sure...
-						if (count($ip_parts) != 4)
+						if (count($ip_parts) != 4 || count($ip_parts) != 8)
 							fatal_lang_error('invalid_ip', false);
 
 						$ban_triggers[] = array(
@@ -703,6 +731,14 @@ function BanEdit()
 							$ip_parts[2]['high'],
 							$ip_parts[3]['low'],
 							$ip_parts[3]['high'],
+							$ip_parts[4]['low'],
+							$ip_parts[4]['high'],
+							$ip_parts[5]['low'],
+							$ip_parts[5]['high'],
+							$ip_parts[6]['low'],
+							$ip_parts[6]['high'],
+							$ip_parts[7]['low'],
+							$ip_parts[7]['high'],
 							'',
 							'',
 							0,
@@ -744,8 +780,9 @@ function BanEdit()
 					'{db_prefix}ban_items',
 					array(
 						'id_ban_group' => 'int', 'ip_low1' => 'int', 'ip_high1' => 'int', 'ip_low2' => 'int', 'ip_high2' => 'int',
-						'ip_low3' => 'int', 'ip_high3' => 'int', 'ip_low4' => 'int', 'ip_high4' => 'int', 'hostname' => 'string-255',
-						'email_address' => 'string-255', 'id_member' => 'int',
+						'ip_low3' => 'int', 'ip_high3' => 'int', 'ip_low4' => 'int', 'ip_high4' => 'int', 'ip_low5' => 'int',
+						'ip_high5' => 'int', 'ip_low6' => 'int', 'ip_high6' => 'int', 'ip_low7' => 'int', 'ip_high7' => 'int',
+						'ip_low8' => 'int', 'ip_high8' => 'int', 'hostname' => 'string-255', 'email_address' => 'string-255', 'id_member' => 'int',
 					),
 					$ban_triggers,
 					array('id_ban')
@@ -791,6 +828,7 @@ function BanEdit()
 			SELECT
 				bi.id_ban, bi.hostname, bi.email_address, bi.id_member, bi.hits,
 				bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4,
+				bi.ip_low5, bi.ip_high5, bi.ip_low6, bi.ip_high6, bi.ip_low7, bi.ip_high7, bi.ip_low8, bi.ip_high8,
 				bg.id_ban_group, bg.name, bg.ban_time, bg.expire_time, bg.reason, bg.notes, bg.cannot_access, bg.cannot_register, bg.cannot_login, bg.cannot_post,
 				IFNULL(mem.id_member, 0) AS id_member, mem.member_name, mem.real_name
 			FROM {db_prefix}ban_groups AS bg
@@ -835,7 +873,7 @@ function BanEdit()
 				if (!empty($row['ip_high1']))
 				{
 					$context['ban_items'][$row['id_ban']]['type'] = 'ip';
-					$context['ban_items'][$row['id_ban']]['ip'] = range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4']));
+					$context['ban_items'][$row['id_ban']]['ip'] = range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4'] ,$row['ip_low5'], $row['ip_low6'], $row['ip_low7'], $row['ip_low8']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4'], $row['ip_high5'], $row['ip_high6'], $row['ip_high7'], $row['ip_high8']));
 				}
 				elseif (!empty($row['hostname']))
 				{
@@ -1036,6 +1074,7 @@ function BanEditTrigger()
 			SELECT
 				bi.id_ban, bi.id_ban_group, bi.hostname, bi.email_address, bi.id_member,
 				bi.ip_low1, bi.ip_high1, bi.ip_low2, bi.ip_high2, bi.ip_low3, bi.ip_high3, bi.ip_low4, bi.ip_high4,
+				bi.ip_low5, bi.ip_high5, bi.ip_low6, bi.ip_high6, bi.ip_low7, bi.ip_high7, bi.ip_low8, bi.ip_high8
 				mem.member_name, mem.real_name
 			FROM {db_prefix}ban_items AS bi
 				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = bi.id_member)
@@ -1056,7 +1095,7 @@ function BanEditTrigger()
 			'id' => $row['id_ban'],
 			'group' => $row['id_ban_group'],
 			'ip' => array(
-				'value' => empty($row['ip_low1']) ? '' : range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4'])),
+				'value' => empty($row['ip_low1']) ? '' : range2ip(array($row['ip_low1'], $row['ip_low2'], $row['ip_low3'], $row['ip_low4'], $row['ip_low5'], $row['ip_low6'], $row['ip_low7'], $row['ip_low8']), array($row['ip_high1'], $row['ip_high2'], $row['ip_high3'], $row['ip_high4'], $row['ip_high5'], $row['ip_high6'], $row['ip_high7'], $row['ip_high8'])),
 				'selected' => !empty($row['ip_low1']),
 			),
 			'hostname' => array(
@@ -1576,6 +1615,27 @@ function list_getNumBanLogEntries()
  */
 function range2ip($low, $high)
 {
+	// IPv6 check.
+	if (!empty($low[5]))
+	{
+		if (count($low) != 8 || count($high) != 8)
+			return '';
+
+		$ip = array();
+		for ($i = 0; $i < 8; $i++)
+		{
+			if ($low[$i] == $high[$i])
+				$ip[$i] = dechex($low[$i]);
+			elseif ($low[$i] == '0' && $high[$i] == '255')
+				$ip[$i] = '*';
+			else
+				$ip[$i] = dechex($low[$i]) . '-' . dechex($high[$i]);
+		}
+
+		return implode(':', $ip);
+	}
+
+	// Legacy IPv4 stuff.
 	if (count($low) != 4 || count($high) != 4)
 		return '';
 
@@ -1610,7 +1670,7 @@ function checkExistingTriggerIP($ip_array, $fullip = '')
 {
 	global $smcFunc, $scripturl;
 
-	if (count($ip_array) == 4)
+	if (count($ip_array) == 4 || count($ip_array) == 8)
 		$values = array(
 			'ip_low1' => $ip_array[0]['low'],
 			'ip_high1' => $ip_array[0]['high'],
@@ -1620,6 +1680,14 @@ function checkExistingTriggerIP($ip_array, $fullip = '')
 			'ip_high3' => $ip_array[2]['high'],
 			'ip_low4' => $ip_array[3]['low'],
 			'ip_high4' => $ip_array[3]['high'],
+			'ip_low5' => $ip_array[4]['low'],
+			'ip_high5' => $ip_array[4]['high'],
+			'ip_low6' => $ip_array[5]['low'],
+			'ip_high6' => $ip_array[5]['high'],
+			'ip_low7' => $ip_array[6]['low'],
+			'ip_high7' => $ip_array[6]['high'],
+			'ip_low8' => $ip_array[7]['low'],
+			'ip_high8' => $ip_array[7]['high'],
 		);
 	else
 		return false;
@@ -1633,6 +1701,10 @@ function checkExistingTriggerIP($ip_array, $fullip = '')
 			AND ip_low2 = {int:ip_low2} AND ip_high2 = {int:ip_high2}
 			AND ip_low3 = {int:ip_low3} AND ip_high3 = {int:ip_high3}
 			AND ip_low4 = {int:ip_low4} AND ip_high4 = {int:ip_high4}
+			AND ip_low5 = {int:ip_low5} AND ip_high5 = {int:ip_high5}
+			AND ip_low6 = {int:ip_low6} AND ip_high6 = {int:ip_high6}
+			AND ip_low7 = {int:ip_low7} AND ip_high7 = {int:ip_high7}
+			AND ip_low8 = {int:ip_low8} AND ip_high8 = {int:ip_high8}
 		LIMIT 1',
 		$values
 	);

+ 29 - 4
Sources/ManageSearchEngines.php

@@ -88,7 +88,7 @@ function ManageSearchEngineSettings($return_config = false)
 		}
 		disableFields();';
 
-	call_integration_function('integrate_search_engine_settings', array(&$config_vars));
+	call_integration_hook('integrate_search_engine_settings', array(&$config_vars));
 
 	if ($return_config)
 		return $config_vars;
@@ -124,7 +124,7 @@ function ManageSearchEngineSettings($return_config = false)
 	{
 		checkSession();
 
-		call_integration_function('integrate_save_search_engine_settings');
+		call_integration_hook('integrate_save_search_engine_settings');
 		saveDBSettings($config_vars);
 		recacheSpiderNames();
 		redirectexit('action=admin;area=sengines;sa=settings');
@@ -493,7 +493,12 @@ function SpiderCheck()
 
 	// Only do these bits once.
 	$ci_user_agent = strtolower($_SERVER['HTTP_USER_AGENT']);
-	preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $_SERVER['REMOTE_ADDR'], $ip_parts);
+
+	// Always attempt IPv6 first.
+	if (empty($ip_parts) && strpos($_SERVER['REMOTE_ADDR'], ':') !== false)
+		$ip_parts = convertIPv6toInts($_SERVER['REMOTE_ADDR']);
+	else
+		preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $_SERVER['REMOTE_ADDR'], $ip_parts);
 
 	foreach ($spider_data as $spider)
 	{
@@ -848,6 +853,24 @@ function SpiderStats()
 		$_SESSION['spider_stat'] = time();
 	}
 
+	// Are we cleaning up some old stats?
+	if (!empty($_POST['delete_entries']) && isset($_POST['older']))
+	{
+		checkSession();
+		validateToken('admin-ss');
+
+		$deleteTime = time() - (((int) $_POST['older']) * 24 * 60 * 60);
+
+		// Delete the entires.
+		$smcFunc['db_query']('', '
+			DELETE FROM {db_prefix}log_spider_stats
+			WHERE last_seen < {int:delete_period}',
+			array(
+				'delete_period' => $deleteTime,
+			)
+		);
+	}
+
 	// Get the earliest and latest dates.
 	$request = $smcFunc['db_query']('', '
 		SELECT MIN(stat_date) AS first_date, MAX(stat_date) AS last_date
@@ -980,10 +1003,12 @@ function SpiderStats()
 		),
 	);
 
+	createToken('admin-ss');
+
 	require_once($sourcedir . '/Subs-List.php');
 	createList($listOptions);
 
-	$context['sub_template'] = 'show_list';
+	$context['sub_template'] = 'show_spider_stats';
 	$context['default_list'] = 'spider_stat_list';
 }
 

+ 2 - 2
Sources/Memberlist.php

@@ -277,8 +277,8 @@ function MLAll()
 	// List out the different sorting methods...
 	$sort_methods = array(
 		'is_online' => array(
-			'down' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) ASC, real_name ASC' : 'IF(mem.show_online, IFNULL(lo.log_time, 1), 1) ASC, real_name ASC',
-			'up' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) DESC, real_name DESC' : 'IF(mem.show_online, IFNULL(lo.log_time, 1), 1) DESC, real_name DESC'
+			'down' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) ASC, real_name ASC' : 'CASE WHEN mem.show_online THEN IFNULL(lo.log_time, 1) ELSE 1 END ASC, real_name ASC',
+			'up' => allowedTo('moderate_forum') ? 'IFNULL(lo.log_time, 1) DESC, real_name DESC' : 'CASE WHEN mem.show_online THEN IFNULL(lo.log_time, 1) ELSE 1 END DESC, real_name DESC'
 		),
 		'real_name' => array(
 			'down' => 'mem.real_name DESC',

+ 5 - 3
Sources/Packages.php

@@ -797,7 +797,7 @@ function PackageInstall()
 	$context['extract_type'] = isset($packageInfo['type']) ? $packageInfo['type'] : 'modification';
 
 	// Create a backup file to roll back to! (but if they do this more than once, don't run it a zillion times.)
-	if (!empty($modSettings['package_make_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$')))
+	if (!empty($modSettings['package_make_full_backups']) && (!isset($_SESSION['last_backup_for']) || $_SESSION['last_backup_for'] != $context['filename'] . ($context['uninstalling'] ? '$$' : '$')))
 	{
 		$_SESSION['last_backup_for'] = $context['filename'] . ($context['uninstalling'] ? '$$' : '$');
 		/**
@@ -1423,13 +1423,14 @@ function PackageOptions()
 			'package_server' => $_POST['pack_server'],
 			'package_port' => $_POST['pack_port'],
 			'package_username' => $_POST['pack_user'],
-			'package_make_backups' => !empty($_POST['package_make_backups'])
+			'package_make_backups' => !empty($_POST['package_make_backups']),
+			'package_make_full_backups' => !empty($_POST['package_make_full_backups'])
 		));
 
 		redirectexit('action=admin;area=packages;sa=options');
 	}
 
-	if (preg_match('~^/home/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
+	if (preg_match('~^/home\d*/([^/]+?)/public_html~', $_SERVER['DOCUMENT_ROOT'], $match))
 		$default_username = $match[1];
 	else
 		$default_username = '';
@@ -1441,6 +1442,7 @@ function PackageOptions()
 	$context['package_ftp_port'] = isset($modSettings['package_port']) ? $modSettings['package_port'] : '21';
 	$context['package_ftp_username'] = isset($modSettings['package_username']) ? $modSettings['package_username'] : $default_username;
 	$context['package_make_backups'] = !empty($modSettings['package_make_backups']);
+	$context['package_make_full_backups'] = !empty($modSettings['package_make_full_backups']);
 }
 
 /**

+ 1 - 1
Sources/Profile-Modify.php

@@ -315,7 +315,7 @@ function loadProfileFields($force_reload = false)
 
 				if (isset($context[\'profile_languages\'][$value]))
 				{
-					if ($context[\'user\'][\'is_owner\'])
+					if ($context[\'user\'][\'is_owner\'] && empty($context[\'password_auth_failed\']))
 						$_SESSION[\'language\'] = $value;
 					return true;
 				}

+ 1 - 1
Sources/Profile-View.php

@@ -1250,7 +1250,7 @@ function TrackIP($memID = 0)
 	if (isset($_REQUEST['searchip']))
 		$context['ip'] = trim($_REQUEST['searchip']);
 
-	if (preg_match('/^\d{1,3}\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $context['ip']) == 0)
+	if (preg_match('/^\d{1,3}\.(\d{1,3}|\*)\.(\d{1,3}|\*)\.(\d{1,3}|\*)$/', $context['ip']) == 0 && isValidIPv6($context['ip']) === false)
 		fatal_lang_error('invalid_tracking_ip', false);
 
 	$ip_var = str_replace('*', '%', $context['ip']);

+ 105 - 7
Sources/QueryString.php

@@ -218,14 +218,23 @@ function cleanRequest()
 		// A new magic variable to indicate we think this is command line.
 		$_SERVER['is_cli'] = true;
 	}
-	elseif (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 0)
-		$_SERVER['REMOTE_ADDR'] = 'unknown';
+	// Perhaps we have a IPv6 address.
+	elseif (!isValidIPv6($_SERVER['REMOTE_ADDR']) || preg_match('~::ffff:\d+\.\d+\.\d+\.\d+~', $_SERVER['REMOTE_ADDR']) !== 0)
+	{
+		$_SERVER['REMOTE_ADDR'] = preg_replace('~^::ffff:(\d+\.\d+\.\d+\.\d+)~', '\1', $_SERVER['REMOTE_ADDR']);
+
+		// Just incase we have a legacy IPv4 address.
+		// @ TODO: Convert to IPv6.
+		if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['REMOTE_ADDR']) === 0)
+			$_SERVER['REMOTE_ADDR'] = 'unknown';
+	}
 
 	// Try to calculate their most likely IP for those people behind proxies (And the like).
 	$_SERVER['BAN_CHECK_IP'] = $_SERVER['REMOTE_ADDR'];
 
 	// Find the user's IP address. (but don't let it give you 'unknown'!)
-	if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
+	// @ TODO: IPv6 really doesn't need this.
+	if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0))
 	{
 		// We have both forwarded for AND client IP... check the first forwarded for as the block - only switch if it's better that way.
 		if (strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.') && '.' . strtok($_SERVER['HTTP_X_FORWARDED_FOR'], '.') == strrchr($_SERVER['HTTP_CLIENT_IP'], '.') && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
@@ -233,7 +242,7 @@ function cleanRequest()
 		else
 			$_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_CLIENT_IP'];
 	}
-	if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0))
+	if (!empty($_SERVER['HTTP_CLIENT_IP']) && (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_CLIENT_IP']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0))
 	{
 		// Since they are in different blocks, it's probably reversed.
 		if (strtok($_SERVER['REMOTE_ADDR'], '.') != strtok($_SERVER['HTTP_CLIENT_IP'], '.'))
@@ -252,7 +261,7 @@ function cleanRequest()
 			foreach ($ips as $i => $ip)
 			{
 				// Make sure it's in a valid range...
-				if (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $ip) != 0 && preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) == 0)
+				if (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $ip) != 0 && preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) == 0)
 					continue;
 
 				// Otherwise, we've got an IP!
@@ -261,7 +270,7 @@ function cleanRequest()
 			}
 		}
 		// Otherwise just use the only one.
-		elseif (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown)~', $_SERVER['REMOTE_ADDR']) != 0)
+		elseif (preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['HTTP_X_FORWARDED_FOR']) == 0 || preg_match('~^((0|10|172\.(1[6-9]|2[0-9]|3[01])|192\.168|255|127)\.|unknown|::1|fe80::|fc00::)~', $_SERVER['REMOTE_ADDR']) != 0)
 			$_SERVER['BAN_CHECK_IP'] = $_SERVER['HTTP_X_FORWARDED_FOR'];
 	}
 
@@ -277,12 +286,101 @@ function cleanRequest()
 	$_SERVER['HTTP_USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? htmlspecialchars($smcFunc['db_unescape_string']($_SERVER['HTTP_USER_AGENT']), ENT_QUOTES) : '';
 
 	// Some final checking.
-	if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['BAN_CHECK_IP']) === 0)
+	if (preg_match('~^((([1]?\d)?\d|2[0-4]\d|25[0-5])\.){3}(([1]?\d)?\d|2[0-4]\d|25[0-5])$~', $_SERVER['BAN_CHECK_IP']) === 0 || !isValidIPv6($_SERVER['BAN_CHECK_IP']))
 		$_SERVER['BAN_CHECK_IP'] = '';
 	if ($_SERVER['REMOTE_ADDR'] == 'unknown')
 		$_SERVER['REMOTE_ADDR'] = '';
 }
 
+/**
+ * Validates a IPv6 address. returns true if it is ipv6.
+ * @param string $ip ip address to be validated
+ * @return bool true|false
+ */
+function isValidIPv6($ip)
+{
+	if (preg_match('~^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$~', $ip) === 0)
+		return false;
+
+	return true;
+}
+
+/**
+ * Converts IPv6s to numbers.  This makes ban checks much easier.
+ * @param string $ip ip address to be converted
+ * @return string 
+ */
+function convertIPv6toInts($ip)
+{
+	static $expanded = array();
+
+	// Check if we have done this already.
+	if (isset($expanded[$ip]))
+		return $expanded[$ip];
+
+	// Expand the IP out.
+	$ip = explode(':', smf_ipv6_expand($ip));
+
+	$new_ip = array();
+	foreach ($ip as $int)
+		$new_ip[] = hexdec($int);
+
+	// Save this incase of repeated use.
+	$expanded[$ip] = implode($new_ip, '-');
+
+	return $expanded[$ip];
+}
+
+/**
+ * Expands a IPv6 address to its full form.
+ * @param string $ip ip address to be converted
+ * @return bool/string expanded ipv6 address.
+ */
+function expandIPv6($addr, $strict_check = true)
+{
+	static $converted = array();
+
+	// Check if we have done this already.
+	if (isset($converted[$addr]))
+		return $converted[$addr];
+
+	// Check if there are segments missing, insert if necessary.
+	if (strpos($addr, '::') !== false)
+	{
+		$part = explode('::', $addr);
+		$part[0] = explode(':', $part[0]);
+		$part[1] = explode(':', $part[1]);
+		$missing = array();
+
+		for ($i = 0; $i < (8 - (count($part[0]) + count($part[1]))); $i++)
+			array_push($missing, '0000');
+
+		$part = array_merge($part[0], $missing, $part[1]);
+	}
+	else
+		$part = explode(':', $addr);
+
+	// Pad each segment until it has 4 digits.
+	foreach ($part as &$p)
+		while (strlen($p) < 4)
+			$p = '0' . $p;
+
+	unset($p);
+
+    // Join segments.
+	$result = implode(':', $part);
+
+	// Save this incase of repeated use.
+	$converted[$addr] = $result;
+
+	// Quick check to make sure the length is as expected. 
+	if (!$strict_check || strlen($result) == 39)
+		return $result;
+	else
+		return false;
+}
+
+
 /**
  * Adds slashes to the array/variable.
  * What it does:

+ 30 - 2
Sources/Security.php

@@ -251,8 +251,36 @@ function is_not_banned($forceCheck = false)
 		// Check both IP addresses.
 		foreach (array('ip', 'ip2') as $ip_number)
 		{
-			// Check if we have a valid IP address.
-			if (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $user_info[$ip_number], $ip_parts) == 1)
+			// First attempt a IPv6 address.
+			if (strpos($user_info[$ip_number], ':') !== false)
+			{
+				if ($ip_number == 'ip2' && $user_info['ip2'] == $user_info['ip'])
+					continue;
+
+				$ip_parts = array_map('hexdec', explode(':', expandIPv6($user_info[$ip_number])));
+
+				$ban_query[] = '((' . $ip_parts[0] . ' BETWEEN bi.ip_low1 AND bi.ip_high1)
+							AND (' . $ip_parts[1] . ' BETWEEN bi.ip_low2 AND bi.ip_high2)
+							AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low3 AND bi.ip_high3)
+							AND (' . $ip_parts[3] . ' BETWEEN bi.ip_low4 AND bi.ip_high4)
+							AND (' . $ip_parts[4] . ' BETWEEN bi.ip_low5 AND bi.ip_high5)
+							AND (' . $ip_parts[5] . ' BETWEEN bi.ip_low6 AND bi.ip_high6)
+							AND (' . $ip_parts[6] . ' BETWEEN bi.ip_low7 AND bi.ip_high7)
+							AND (' . $ip_parts[7] . ' BETWEEN bi.ip_low8 AND bi.ip_high8))';
+
+				// IP was valid, maybe there's also a hostname...
+				if (empty($modSettings['disableHostnameLookup']))
+				{
+					$hostname = host_from_ip($user_info[$ip_number]);
+					if (strlen($hostname) > 0)
+					{
+						$ban_query[] = '({string:hostname} LIKE bi.hostname)';
+						$ban_query_vars['hostname'] = $hostname;
+					}
+				}
+			}
+			// Check if we have a valid IPv4 address.
+			elseif (preg_match('/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $user_info[$ip_number], $ip_parts) == 1)
 			{
 				$ban_query[] = '((' . $ip_parts[1] . ' BETWEEN bi.ip_low1 AND bi.ip_high1)
 							AND (' . $ip_parts[2] . ' BETWEEN bi.ip_low2 AND bi.ip_high2)

+ 10 - 7
Sources/Session.php

@@ -48,7 +48,7 @@ function loadSession()
 	{
 		// Attempt to end the already-started session.
 		if (ini_get('session.auto_start') == 1)
-			@session_write_close();
+			session_write_close();
 
 		// This is here to stop people from using bad junky PHPSESSIDs.
 		if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9]{16,32}$~', $_REQUEST[session_name()]) == 0 && !isset($_COOKIE[session_name()]))
@@ -101,8 +101,8 @@ function loadSession()
  * Implementation of sessionOpen() replacing the standard open handler.
  * It simply returns true.
  *
- * @param $save_path
- * @param $session_name
+ * @param string $save_path
+ * @param string $session_name
  * @return bool
  */
 function sessionOpen($save_path, $session_name)
@@ -124,7 +124,7 @@ function sessionClose()
 /**
  * Implementation of sessionRead() replacing the standard read handler.
  *
- * @param $session_id
+ * @param string $session_id
  */
 function sessionRead($session_id)
 {
@@ -152,8 +152,9 @@ function sessionRead($session_id)
 /**
  * Implementation of sessionWrite() replacing the standard write handler.
  *
- * @param $session_id
- * @param $data
+ * @param string $session_id
+ * @param string $data
+ * @return bool
  */
 function sessionWrite($session_id, $data)
 {
@@ -189,7 +190,8 @@ function sessionWrite($session_id, $data)
 /**
  * Implementation of sessionDestroy() replacing the standard destroy handler.
  *
- * @param $session_id
+ * @param string $session_id
+ * @return bool
  */
 function sessionDestroy($session_id)
 {
@@ -213,6 +215,7 @@ function sessionDestroy($session_id)
  * Callback for garbage collection.
  *
  * @param int $max_lifetime
+ * @return bool
  */
 function sessionGC($max_lifetime)
 {

+ 2 - 0
Sources/Stats.php

@@ -629,6 +629,8 @@ function DisplayStats()
 /**
  * Loads the statistics on a daily basis in $context.
  * called by DisplayStats().
+ * @param string $condition_string
+ * @param array $condition_parameters = array()
  */
 function getDailyStats($condition_string, $condition_parameters = array())
 {

+ 1 - 1
Sources/Subs-Package.php

@@ -2693,7 +2693,7 @@ function package_create_backup($id = 'backup')
 
 	$files = array();
 
-	$base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml');
+	$base_files = array('index.php', 'SSI.php', 'agreement.txt', 'ssi_examples.php', 'ssi_examples.shtml', 'subscriptions.php');
 	foreach ($base_files as $file)
 	{
 		if (file_exists($boarddir . '/' . $file))

+ 59 - 4
Sources/Subs.php

@@ -118,7 +118,6 @@ if (!defined('SMF'))
  * does nothing if the functions is not available.
 */
 
-// 
 /**
  * Update some basic statistics.
  * 
@@ -871,7 +870,14 @@ function forum_time($use_user_offset = true, $timestamp = null)
 	return $timestamp + ($modSettings['time_offset'] + ($use_user_offset ? $user_info['time_offset'] : 0)) * 3600;
 }
 
-// This gets all possible permutations of an array.
+/**
+ * Calculates all the possible permutations (orders) of array.
+ * should not be called on huge arrays (bigger than like 10 elements.)
+ * returns an array containing each permutation.
+ * 
+ * @param array $array
+ * @return array
+ */
 function permute($array)
 {
 	$orders = array($array);
@@ -896,7 +902,25 @@ function permute($array)
 	return $orders;
 }
 
-// Parse bulletin board code in a string, as well as smileys optionally.
+/**
+ * Parse bulletin board code in a string, as well as smileys optionally.
+ * Only parses bbc tags which are not disabled in disabledBBC.
+ * Also handles basic HTML, if enablePostHTML is on.
+ * Caches the from/to replace regular expressions so as not to reload
+ *  them every time a string is parsed.
+ * Only parses smileys if smileys is true.
+ * Does nothing if the enableBBC setting is off.
+ * Applies the fixLongWords magic if the setting is set to on.
+ * Uses the cache_id as a unique identifier to facilitate any caching
+ *  it may do.
+ * Returns the modified message.
+ * 
+ * @param string $message
+ * @param bool $smileys = true
+ * @param string $cache_id = ''
+ * @param array $parse_tags = null
+ * @return string
+ */
 function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = array())
 {
 	global $txt, $scripturl, $context, $modSettings, $user_info, $smcFunc;
@@ -2759,7 +2783,10 @@ function determineTopicClass(&$topic_context)
 	$topic_context['extended_class'] = &$topic_context['class'];
 }
 
-// Sets up the basic theme context stuff.
+/**
+ * Sets up the basic theme context stuff.
+ * @param bool $forceload = false
+ */
 function setupThemeContext($forceload = false)
 {
 	global $modSettings, $user_info, $scripturl, $context, $settings, $options, $txt, $maintenance;
@@ -3157,6 +3184,28 @@ function getLegacyAttachmentFilename($filename, $attachment_id, $dir = null, $ne
 // Convert a single IP to a ranged IP.
 function ip2range($fullip)
 {
+	// If its IPv6, validate it first.
+	if (strpos($fullip, ':') !== false)
+	{
+		$ip_parts = explode(':', smf_ipv6_expand($fullip, false));
+		$ip_array = array();
+
+		if (count($ip_parts) != 8)
+			return array();
+
+		for ($i = 0; $i < 8; $i++)
+		{
+			if ($ip_parts[$i] == '*')
+				$ip_array[$i] = array('low' => '0', 'high' => hexdec('ffff'));
+			elseif (preg_match('/^([0-9A-Fa-f]{1,4})\-([0-9A-Fa-f]{1,4})$/', $ip_parts[$i], $range) == 1)
+				$ip_array[$i] = array('low' => hexdec($range[1]), 'high' => hexdec($range[2]));
+			elseif (is_numeric(hexdec($ip_parts[$i])))
+				$ip_array[$i] = array('low' => hexdec($ip_parts[$i]), 'high' => hexdec($ip_parts[$i]));
+		}
+
+		return $ip_array;
+	}
+
 	// Pretend that 'unknown' is 255.255.255.255. (since that can't be an IP anyway.)
 	if ($fullip == 'unknown')
 		$fullip = '255.255.255.255';
@@ -3177,6 +3226,12 @@ function ip2range($fullip)
 			$ip_array[$i] = array('low' => $ip_parts[$i], 'high' => $ip_parts[$i]);
 	}
 
+	// Makes it simpiler to work with.
+	$ip_array[5] = array('low' => 0, 'high' => 0);
+	$ip_array[6] = array('low' => 0, 'high' => 0);
+	$ip_array[7] = array('low' => 0, 'high' => 0);
+	$ip_array[8] = array('low' => 0, 'high' => 0);
+
 	return $ip_array;
 }
 

+ 37 - 17
Themes/default/ManageSearch.template.php

@@ -88,22 +88,7 @@ function template_modify_weights()
 			</div>
 		</form>
 	</div>
-	<br class="clear" />
-	<script type="text/javascript"><!-- // --><![CDATA[
-		function calculateNewValues()
-		{
-			var total = 0;
-			for (var i = 1; i <= 6; i++)
-			{
-				total += parseInt(document.getElementById(\'weight\' + i + \'_val\').value);
-			}
-			setInnerHTML(document.getElementById(\'weighttotal\'), total);
-			for (var i = 1; i <= 6; i++)
-			{
-				setInnerHTML(document.getElementById(\'weight\' + i), (Math.round(1000 * parseInt(document.getElementById(\'weight\' + i + \'_val\').value) / total) / 10) + \'%\');
-			}
-		}
-	// ]]></script>';
+	<br class="clear" />';
 }
 
 function template_select_search_method()
@@ -409,7 +394,7 @@ function template_show_spider_logs()
 		<div class="cat_bar">
 			<h3 class="catbg">', $txt['spider_logs_delete'], '</h3>
 		</div>
-		<form action="', $scripturl, '?action=admin;area=sengines;sa=logs;', $context['session_var'], '=', $context['session_id'], '" method="post" accept-charset="', $context['character_set'], '">
+		<form action="', $scripturl, '?action=admin;area=sengines;sa=logs" method="post" accept-charset="', $context['character_set'], '">
 			<div class="windowbg">
 				<span class="topslice"><span></span></span>
 				<div class="content">
@@ -419,6 +404,41 @@ function template_show_spider_logs()
 						', $txt['spider_logs_delete_day'], '
 					</p>
 					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<input type="hidden" name="', $context['admin-sl_token_var'], '" value="', $context['admin-sl_token'], '" />
+					<input type="submit" name="delete_entries" value="', $txt['spider_logs_delete_submit'], '" onclick="if (document.getElementById(\'older\').value &lt; 1 &amp;&amp; !confirm(\'' . addcslashes($txt['spider_logs_delete_confirm'], "'") . '\')) return false; return true;" class="button_submit" />
+				</div>
+				<span class="botslice"><span></span></span>
+			</div>
+		</form>
+	</div>
+	<br class="clear" />';
+}
+
+// Show... spider... stats...
+function template_show_spider_stats()
+{
+	global $context, $txt, $settings, $scripturl;
+
+	echo '
+	<div id="admincenter">';
+
+	// Standard fields.
+	template_show_list('spider_stat_list');
+
+	echo '
+		<br />
+		<div class="cat_bar">
+			<h3 class="catbg">', $txt['spider_logs_delete'], '</h3>
+		</div>
+		<form action="', $scripturl, '?action=admin;area=sengines;sa=stats" method="post" accept-charset="', $context['character_set'], '">
+			<div class="windowbg">
+				<span class="topslice"><span></span></span>
+				<div class="content">
+					<p>
+						', sprintf($txt['spider_stats_delete_older'], '<input type="text" name="older" id="older" value="90" size="3" class="input_text" />'), '
+					</p>
+					<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
+					<input type="hidden" name="', $context['admin-ss_token_var'], '" value="', $context['admin-ss_token'], '" />
 					<input type="submit" name="delete_entries" value="', $txt['spider_logs_delete_submit'], '" onclick="if (document.getElementById(\'older\').value &lt; 1 &amp;&amp; !confirm(\'' . addcslashes($txt['spider_logs_delete_confirm'], "'") . '\')) return false; return true;" class="button_submit" />
 				</div>
 				<span class="botslice"><span></span></span>

+ 1 - 104
Themes/default/Packages.template.php

@@ -351,12 +351,6 @@ function template_view_package()
 		var database_changes_area = document.getElementById(\'db_changes_div\');
 		var db_vis = false;
 		database_changes_area.style.display = "none";
-		function swap_database_changes()
-		{
-			db_vis = !db_vis;
-			database_changes_area.style.display = db_vis ? "" : "none";
-			return false;
-		}
 	// ]]></script>';
 }
 function template_extract_package()
@@ -590,15 +584,6 @@ function template_browse()
 	echo '
 		<script type="text/javascript"><!-- // --><![CDATA[
 			var tempOldOnload;
-
-			function smfSetLatestPackages()
-			{
-				if (typeof(window.smfLatestPackages) != "undefined")
-					setInnerHTML(document.getElementById("packagesLatest"), window.smfLatestPackages);
-
-				if (tempOldOnload)
-				tempOldOnload();
-			}
 		// ]]></script>';
 
 		echo '
@@ -1319,6 +1304,7 @@ function template_install_options()
 						</dd>
 					</dl>
 					<label for="package_make_backups"><input type="checkbox" name="package_make_backups" id="package_make_backups" value="1" class="input_check"', $context['package_make_backups'] ? ' checked="checked"' : '', ' /> ', $txt['package_install_options_make_backups'], '</label><br /><br />
+					<label for="package_make_full_backups"><input type="checkbox" name="package_make_full_backups" id="package_make_full_backups" value="1" class="input_check"', $context['package_make_full_backups'] ? ' checked="checked"' : '', ' /> ', $txt['package_install_options_make_full_backups'], '</label><br /><br />
 					<div class="righttext">
 						<input type="submit" name="submit" value="', $txt['save'], '" class="button_submit" />
 						<input type="hidden" name="', $context['session_var'], '" value="', $context['session_id'], '" />
@@ -1447,26 +1433,6 @@ function template_control_chmod()
 				document.getElementById("test_ftp_placeholder_full").appendChild(ftpTest);
 			}
 		}
-		function testFTP()
-		{
-			ajax_indicator(true);
-
-			// What we need to post.
-			var oPostData = {
-				0: "ftp_server",
-				1: "ftp_port",
-				2: "ftp_username",
-				3: "ftp_password",
-				4: "ftp_path"
-			}
-
-			var sPostData = "";
-			for (i = 0; i < 5; i++)
-				sPostData = sPostData + (sPostData.length == 0 ? "" : "&") + oPostData[i] + "=" + escape(document.getElementById(oPostData[i]).value);
-
-			// Post the data out.
-			sendXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=admin;area=packages;sa=ftptest;xml;', $context['session_var'], '=', $context['session_id'], '\', sPostData, testFTPResults);
-		}
 		function testFTPResults(oXMLDoc)
 		{
 			ajax_indicator(false);
@@ -1562,82 +1528,13 @@ function template_file_permissions()
 			3: "custom",
 			4: "no_change"
 		}
-		function expandFolder(folderIdent, folderReal)
-		{
-			// See if it already exists.
-			var possibleTags = document.getElementsByTagName("tr");
-			var foundOne = false;
-
-			for (var i = 0; i < possibleTags.length; i++)
-			{
-				if (possibleTags[i].id.indexOf("content_" + folderIdent + ":-:") == 0)
-				{
-					possibleTags[i].style.display = possibleTags[i].style.display == "none" ? "" : "none";
-					foundOne = true;
-				}
-			}
-
-			// Got something then we\'re done.
-			if (foundOne)
-			{
-				return false;
-			}
-			// Otherwise we need to get the wicked thing.
-			else if (window.XMLHttpRequest)
-			{
-				ajax_indicator(true);
-				getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=admin;area=packages;onlyfind=\' + escape(folderReal) + \';sa=perms;xml;', $context['session_var'], '=', $context['session_id'], '\', onNewFolderReceived);
-			}
-			// Otherwise reload.
-			else
-				return true;
-
-			return false;
-		}
-		function dynamicExpandFolder()
-		{
-			expandFolder(this.ident, this.path);
-
-			return false;
-		}
 		function dynamicAddMore()
 		{
 			ajax_indicator(true);
 
 			getXMLDocument(smf_prepareScriptUrl(smf_scripturl) + \'action=admin;area=packages;fileoffset=\' + (parseInt(this.offset) + ', $context['file_limit'], ') + \';onlyfind=\' + escape(this.path) + \';sa=perms;xml;', $context['session_var'], '=', $context['session_id'], '\', onNewFolderReceived);
 		}
-		function repeatString(sString, iTime)
-		{
-			if (iTime < 1)
-				return \'\';
-			else
-				return sString + repeatString(sString, iTime - 1);
-		}
-		// Create a named element dynamically - thanks to: http://www.thunderguy.com/semicolon/2005/05/23/setting-the-name-attribute-in-internet-explorer/
-		function createNamedElement(type, name, customFields)
-		{
-			var element = null;
-
-			if (!customFields)
-				customFields = "";
 
-			// Try the IE way; this fails on standards-compliant browsers
-			try
-			{
-				element = document.createElement("<" + type + \' name="\' + name + \'" \' + customFields + ">");
-			}
-			catch (e)
-			{
-			}
-			if (!element || element.nodeName != type.toUpperCase())
-			{
-				// Non-IE browser; use canonical method to create named element
-				element = document.createElement(type);
-				element.name = name;
-			}
-
-			return element;
-		}
 		// Getting something back?
 		function onNewFolderReceived(oXMLDoc)
 		{

+ 11 - 6
Themes/default/index.template.php

@@ -109,7 +109,10 @@ function template_html_above()
 		var smf_images_url = "', $settings['images_url'], '";
 		var smf_scripturl = "', $scripturl, '";
 		var smf_iso_case_folding = ', $context['server']['iso_case_folding'] ? 'true' : 'false', ';
-		var smf_charset = "', $context['character_set'], '";', $context['show_pm_popup'] ? '
+		var smf_charset = "', $context['character_set'], '";
+		var smf_session_id = "', $context['session_id'], '";
+		var smf_session_var = "', $context['session_var'], '";
+		var smf_member_id = "', $context['user']['id'], '";', $context['show_pm_popup'] ? '
 		var fPmPopup = function ()
 		{
 			if (confirm("' . $txt['show_personal_messages'] . '"))
@@ -163,7 +166,9 @@ function template_html_above()
 
 	echo '
 </head>
-<body', 
+<body class="action_', !empty($context['current_action']) ? htmlspecialchars($context['current_action']) : (!empty($context['current_board']) ? 'messageindex' : (!empty($context['current_topic']) ? 'display' : 'home')),
+	!empty($context['current_board']) ? ' board_' . htmlspecialchars($context['current_board']) : '',
+	'"',
 	// Style per page.
 	!empty($context['body_id']) ? ' id="' . $context['body_id'] . '"' : '', '>';
 
@@ -299,13 +304,13 @@ function template_body_above()
 					}
 				],
 				oThemeOptions: {
-					bUseThemeSettings: ', $context['user']['is_guest'] ? 'false' : 'true', ',
+					bUseThemeSettings: smf_member_id == 0 ? false : true,
 					sOptionName: \'collapse_header\',
-					sSessionVar: ', JavaScriptEscape($context['session_var']), ',
-					sSessionId: ', JavaScriptEscape($context['session_id']), '
+					sSessionVar: smf_session_var,
+					sSessionId: smf_session_id
 				},
 				oCookieOptions: {
-					bUseCookie: ', $context['user']['is_guest'] ? 'true' : 'false', ',
+					bUseCookie: smf_member_id == 0 ? true : false,
 					sCookieName: \'upshrink\'
 				}
 			});

+ 6 - 0
Themes/default/languages/Help.english.php

@@ -544,6 +544,12 @@ $helptxt['posts_and_topics'] = '
 			Modify the settings related to topics. The number of topics per page, whether sticky topics are enabled or not, the number of messages needed for a topic to be hot, etc.
 		</li>
 	</ul>';
+
+$helptxt['spider_mode'] = 'Sets the logging level.<br />
+Standard - Logs minimal spider activity.<br />
+Moderate - Provides more accurate statistics.<br />
+Agressive - As for &quot;Moderate&quot; but logs data about each page visited.';
+
 $helptxt['spider_group'] = 'By selecting a restrictive group, when a guest is detected as a search crawler it will automatically be assigned any &quot;deny&quot; deny permissions of this group in addition to the normal permissions of a guest. You can use this to provide lesser access to a search engine than you would a normal guest. You might for example wish to create a new group called &quot;Spiders&quot; and select that here. You could then deny permission for that group to view profiles to stop spiders indexing your members profiles.<br />Note: Spider detection is not perfect and can be simulated by users so this feature is not guaranteed to restrict content only to those search engines you have added.';
 $helptxt['show_spider_online'] = 'This setting allows you to select whether spiders should be listed in the who\'s online list on the board index and &quot;Who\'s Online&quot; page. Options are:
 	<ul class="normallist">

+ 1 - 0
Themes/default/languages/Packages.english.php

@@ -129,6 +129,7 @@ $txt['package_install_options_ftp_server'] = 'FTP Server';
 $txt['package_install_options_ftp_port'] = 'Port';
 $txt['package_install_options_ftp_user'] = 'Username';
 $txt['package_install_options_make_backups'] = 'Create Backup versions of replaced files with a tilde (~) on the end of their names.';
+$txt['package_install_options_make_full_backups'] = 'Create an entire backup (excluding smileys, avatars and attachments) of the SMF install.';
 
 $txt['package_ftp_necessary'] = 'FTP Information Required';
 $txt['package_ftp_why'] = 'Some of the files the package manager needs to modify are not writable.  This needs to be changed by logging into FTP and using it to chmod or create the files and folders.  Your FTP information may be temporarily cached for proper operation of the package manager. Note you can also do this manually using an FTP client - to view a list of the affected files please click <a href="#" onclick="%1$s">here</a>.';

+ 6 - 3
Themes/default/languages/Search.english.php

@@ -108,9 +108,9 @@ $txt['search_example'] = '<em>e.g.</em> Orwell "Animal Farm" -movie';
 $txt['search_engines_description'] = 'From this area you can decide in what detail you wish to track search engines as they index your forum as well as review search engine logs.';
 $txt['spider_mode'] = 'Search Engine Tracking Level<div class="smalltext">Note higher level tracking increases server resource requirement.</div>';
 $txt['spider_mode_off'] = 'Disabled';
-$txt['spider_mode_standard'] = 'Standard - Logs minimal spider activity.';
-$txt['spider_mode_high'] = 'High - Provides more accurate statistics.';
-$txt['spider_mode_vhigh'] = 'Very High - As for &quot;High&quot; but logs data about each page visited.';
+$txt['spider_mode_standard'] = 'Standard';
+$txt['spider_mode_high'] = 'Moderate';
+$txt['spider_mode_vhigh'] = 'Agressive';
 $txt['spider_settings_desc'] = 'You can change settings for spider tracking from this page. Note, if you wish to enable automatic pruning of the hit logs you can set this up <a href="%1$s">here</a>';
 
 $txt['spider_group'] = 'Apply restrictive permissions from group<div class="smalltext">To enable you to stop spiders indexing some pages.</div>';
@@ -149,6 +149,9 @@ $txt['spider_logs_delete'] = 'Delete Entries';
 $txt['spider_logs_delete_older'] = 'Delete all entries older than';
 $txt['spider_logs_delete_day'] = 'days.';
 $txt['spider_logs_delete_submit'] = 'Delete';
+
+$txt['spider_stats_delete_older'] = 'Delete all spider statistics from spiders not seen in %1$s days.';
+
 // Don't use entities in the below string.
 $txt['spider_logs_delete_confirm'] = 'Are you sure you wish to empty out all log entries?';
 

+ 1 - 0
subscriptions.php

@@ -20,6 +20,7 @@
 */
 
 // Start things rolling by getting SMF alive...
+$ssi_guest_access = true;
 if (!file_exists(dirname(__FILE__) . '/SSI.php'))
 	die('Cannot find SSI.php');