Browse Source

! More documentation.
! The parameters for indexedWordQuery (SearchAPI-Fulltext) are passed in the search_data array. The public function incorrectly used $no_regex when it should use what it was passed as $search_data['no_regex']
! FF header bug
! When session.hash_bits_per_character = 6, it can break sessions. Thanks rawlogic (http://www.simplemachines.org/community/index.php?topic=447598.0)
! Added smf_setcookie to give support for HTTP only cookies and added a setting to enable them.
! Typo in Calendar
! Preserve td tags with attributes, else we get the style but remove the actual tag breaking wysiwyg pasted tables

Spuds 12 years ago
parent
commit
2be623e0c9

+ 1 - 1
SSI.php

@@ -1439,7 +1439,7 @@ function ssi_pollVote()
 
 		require_once($sourcedir . '/Subs-Auth.php');
 		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
-		setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], 0);
+		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
 	}
 
 	redirectexit('topic=' . $row['id_topic'] . '.0');

+ 2 - 5
Sources/Calendar.php

@@ -377,11 +377,8 @@ function iCalDownload()
 	header('Last-Modified: ' . gmdate('D, d M Y H:i:s', time()) . 'GMT');
 	header('Accept-Ranges: bytes');
 	header('Connection: close');
-	header('Content-D isposition: attachment; filename=' . $event['title'] . '.ics');
-
-	// How big is it?
-	if (empty($modSettings['enableCompressedOutput']))
-		header('Content-Length: ' . $smcFunc['strlen']($filecontents));
+	header('Content-Disposition: attachment; filename=' . $event['title'] . '.ics');
+	header('Content-Length: ' . $smcFunc['strlen']($filecontents));
 
 	// This is a calendar item!
 	header('Content-Type: text/calendar');

+ 1 - 1
Sources/ManageServer.php

@@ -262,11 +262,11 @@ function ModifyCookieSettings($return_config = false)
 	// Define the variables we want to edit.
 	$config_vars = array(
 		// Cookies...
-		array('cookiename', $txt['cookie_name'], 'file', 'text', 20),
 		array('cookieTime', $txt['cookieTime'], 'db', 'int'),
 		array('localCookies', $txt['localCookies'], 'db', 'check', false, 'localCookies'),
 		array('globalCookies', $txt['globalCookies'], 'db', 'check', false, 'globalCookies'),
 		array('secureCookies', $txt['secureCookies'], 'db', 'check', false, 'secureCookies',  'disabled' => !isset($_SERVER['HTTPS']) || !(strtolower($_SERVER['HTTPS']) == 'on' || strtolower($_SERVER['HTTPS']) == '1')),
+		array('httponlyCookies', $txt['httponlyCookies'], 'db', 'check', false, 'httponlyCookies'),
 		'',
 		// Sessions
 		array('databaseSession_enable', $txt['databaseSession_enable'], 'db', 'check', false, 'databaseSession_enable'),

+ 1 - 1
Sources/Poll.php

@@ -211,7 +211,7 @@ function Vote()
 
 		require_once($sourcedir . '/Subs-Auth.php');
 		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
-		setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], 0);
+		smf_setcookie('guest_poll_vote', $_COOKIE['guest_poll_vote'], time() + 2500000, $cookie_url[1], $cookie_url[0], false, false);
 	}
 
 	// Return to the post...

+ 5 - 4
Sources/Profile-Modify.php

@@ -1004,7 +1004,7 @@ function saveProfileChanges(&$profile_vars, &$post_errors, $memID)
  */
 function makeThemeChanges($memID, $id_theme)
 {
-	global $modSettings, $smcFunc, $context;
+	global $modSettings, $smcFunc, $context, $user_info;
 
 	$reservedVars = array(
 		'actual_theme_url',
@@ -2620,7 +2620,7 @@ function profileSaveAvatarData(&$value)
 	}
 
 	$downloadedExternalAvatar = false;
-	if ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && strlen($_POST['userpicpersonal']) > 7 && !empty($modSettings['avatar_download_external']))
+	if ($value == 'external' && allowedTo('profile_remote_avatar') && stripos($_POST['userpicpersonal'], 'http://') === 0 && strlen($_POST['userpicpersonal']) > 7 && !empty($modSettings['avatar_download_external']))
 	{
 		if (!is_writable($uploadDir))
 			fatal_lang_error('attachments_no_write', 'critical');
@@ -2664,7 +2664,7 @@ function profileSaveAvatarData(&$value)
 		// Get rid of their old avatar. (if uploaded.)
 		removeAttachments(array('id_member' => $memID));
 	}
-	elseif ($value == 'external' && allowedTo('profile_remote_avatar') && strtolower(substr($_POST['userpicpersonal'], 0, 7)) == 'http://' && empty($modSettings['avatar_download_external']))
+	elseif ($value == 'external' && allowedTo('profile_remote_avatar') && stripos($_POST['userpicpersonal'], 'http://') === 0 && empty($modSettings['avatar_download_external']))
 	{
 		// We need these clean...
 		$cur_profile['id_attach'] = 0;
@@ -2674,7 +2674,8 @@ function profileSaveAvatarData(&$value)
 		// Remove any attached avatar...
 		removeAttachments(array('id_member' => $memID));
 
-		$profile_vars['avatar'] = str_replace('%20', '', preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $_POST['userpicpersonal']));
+		// @todo http://www.simplemachines.org/community/index.php?topic=462089.msg3226650#msg3226650
+		$profile_vars['avatar'] = str_replace('%20', ' ', preg_replace('~action(?:=|%3d)(?!dlattach)~i', 'action-', $_POST['userpicpersonal']));
 
 		if ($profile_vars['avatar'] == 'http://' || $profile_vars['avatar'] == 'http:///')
 			$profile_vars['avatar'] = '';

+ 6 - 6
Sources/SearchAPI-Fulltext.php

@@ -169,8 +169,8 @@ class fulltext_search
 		if (empty($modSettings['search_simple_fulltext']))
 			foreach ($words['words'] as $regularWord)
 			{
-				$query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:complex_body_' . $count . '}';
-				$query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]';
+				$query_where[] = 'm.body' . (in_array($regularWord, $query_params['excluded_words']) ? ' NOT' : '') . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:complex_body_' . $count . '}';
+				$query_params['complex_body_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($regularWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $regularWord), '\\\'') . '[[:>:]]';
 			}
 
 		if ($query_params['user_query'])
@@ -189,15 +189,15 @@ class fulltext_search
 		if (!empty($query_params['excluded_phrases']) && empty($modSettings['search_force_index']))
 			foreach ($query_params['excluded_phrases'] as $phrase)
 			{
-				$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_phrase_' . $count . '}';
-				$query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
+				$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_phrase_' . $count . '}';
+				$query_params['exclude_subject_phrase_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($phrase, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $phrase), '\\\'') . '[[:>:]]';
 			}
 		$count = 0;
 		if (!empty($query_params['excluded_subject_words']) && empty($modSettings['search_force_index']))
 			foreach ($query_params['excluded_subject_words'] as $excludedWord)
 			{
-				$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $no_regexp ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_words_' . $count . '}';
-				$query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $no_regexp ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]';
+				$query_where[] = 'subject NOT ' . (empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? ' LIKE ' : 'RLIKE') . '{string:exclude_subject_words_' . $count . '}';
+				$query_params['exclude_subject_words_' . $count++] = empty($modSettings['search_match_words']) || $search_data['no_regexp'] ? '%' . strtr($excludedWord, array('_' => '\\_', '%' => '\\%')) . '%' : '[[:<:]]' . addcslashes(preg_replace(array('/([\[\]$.+*?|{}()])/'), array('[$1]'), $excludedWord), '\\\'') . '[[:>:]]';
 			}
 
 		if (!empty($modSettings['search_simple_fulltext']))

+ 2 - 2
Sources/Security.php

@@ -335,7 +335,7 @@ function is_not_banned($forceCheck = false)
 		{
 			require_once($sourcedir . '/Subs-Auth.php');
 			$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
-			setcookie($cookiename . '_', '', time() - 3600, $cookie_url[1], $cookie_url[0], 0);
+			smf_setcookie($modSettings['cookie_name'] . '_', '', time() - 3600, $cookie_url[1], $cookie_url[0], false, false);
 		}
 	}
 
@@ -375,7 +375,7 @@ function is_not_banned($forceCheck = false)
 		// A goodbye present.
 		require_once($sourcedir . '/Subs-Auth.php');
 		$cookie_url = url_parts(!empty($modSettings['localCookies']), !empty($modSettings['globalCookies']));
-		setcookie($cookiename . '_', implode(',', $_SESSION['ban']['cannot_access']['ids']), time() + 3153600, $cookie_url[1], $cookie_url[0], 0);
+		smf_setcookie($modSettings['cookie_name'] . '_', implode(',', $_SESSION['ban']['cannot_access']['ids']), time() + 3153600, $cookie_url[1], $cookie_url[0], false, false);
 
 		// Don't scare anyone, now.
 		$_GET['action'] = '';

+ 1 - 1
Sources/Session.php

@@ -51,7 +51,7 @@ function loadSession()
 			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()]))
+		if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9,-]{16,32}$~', $_REQUEST[session_name()]) == 0 && !isset($_COOKIE[session_name()]))
 		{
 			$session_id = md5(md5('smf_sess_' . time()) . mt_rand());
 			$_REQUEST[session_name()] = $session_id;

+ 41 - 4
Sources/Subs-Auth.php

@@ -43,7 +43,7 @@ function setLoginCookie($cookie_length, $id, $password = '')
 		if (isset($array[3]) && $array[3] != $cookie_state)
 		{
 			$cookie_url = url_parts($array[3] & 1 > 0, $array[3] & 2 > 0);
-			setcookie($cookiename, serialize(array(0, '', 0)), time() - 3600, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
+			smf_setcookie($modSettings['cookie_name'], serialize(array(0, '', 0)), time() - 3600, $cookie_url[1], $cookie_url[0]);
 		}
 	}
 
@@ -75,7 +75,7 @@ function setLoginCookie($cookie_length, $id, $password = '')
 			if ($cookie_url[0] == '')
 				$cookie_url[0] = strtok($alias, '/');
 
-			setcookie($cookiename, $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
+			smf_setcookie($modSettings['cookie_name'], $data, time() + $cookie_length, $cookie_url[1], $cookie_url[0]);
 		}
 
 		$boardurl = $temp;
@@ -103,7 +103,7 @@ function setLoginCookie($cookie_length, $id, $password = '')
 		if (version_compare(PHP_VERSION, '4.3.2', '==') || !isset($_COOKIE[session_name()]) || $_COOKIE[session_name()] != session_id())
 		{
 			$sessionCookieLifetime = ini_get('session.cookie_lifetime');
-			setcookie(session_name(), session_id(), time() + (empty($sessionCookieLifetime) ? $cookie_length : $sessionCookieLifetime), $cookie_url[1], $cookie_url[0], !empty($modSettings['secureCookies']));
+			smf_setcookie(session_name(), session_id(), time() + (empty($sessionCookieLifetime) ? $cookie_length : $sessionCookieLifetime), $cookie_url[1], $cookie_url[0]);
 		}
 
 		$_SESSION['login_' . $cookiename] = $data;
@@ -595,7 +595,7 @@ function resetPassword($memID, $username = null)
 
 /**
  * Checks a username obeys a load of rules
- * @param int $memID, 
+ * @param int $memID,
  * @param string $username
  * @return string Returns null if fine
  */
@@ -745,4 +745,41 @@ function rebuildModCache()
 	cleanTokens();
 }
 
+/**
+ * The same thing as setcookie but gives support for HTTP-Only cookies in PHP < 5.2
+ * @param string $name
+ * @param string $value = ''
+ * @param int $expire = 0
+ * @param string $path = ''
+ * @param string $domain = ''
+ * @param bool $secure = false
+ * @param bool $httponly = null
+ */
+function smf_setcookie($name, $value = '', $expire = 0, $path = '', $domain = '', $secure = null, $httponly = null)
+{
+	global $modSettings;
+
+	// In case a customization wants to override the default settings
+	if ($httponly === null)
+		$httponly = !empty($modSettings['httponlyCookies']);
+	if ($secure === null)
+		$secure = !empty($modSettings['secureCookies']);
+		
+	// This function is pointless if we have PHP >= 5.2.
+	if (version_compare(PHP_VERSION, '5.2', '>='))
+		return setcookie($name, $value, $expire, $path, $domain, $secure, $httponly);
+
+	// $httponly is the only reason I made this function. If it's not being used, use setcookie().
+	if (!$httponly)
+		return setcookie($name, $value, $expire, $path, $domain, $secure);
+
+	// Ugh, looks like we have to resort to using a manual process.
+	header('Set-Cookie: '.rawurlencode($name).'='.rawurlencode($value)
+			.(empty($domain) ? '' : '; Domain='.$domain)
+			.(empty($expire) ? '' : '; Max-Age='.$expire)
+			.(empty($path) ? '' : '; Path='.$path)
+			.(!$secure ? '' : '; Secure')
+			.(!$httponly ? '' : '; HttpOnly'), false);
+}
+
 ?>

+ 3 - 2
Sources/Subs-Boards.php

@@ -85,7 +85,7 @@ function markBoardsRead($boards, $unread = false)
 	if ($unread)
 	{
 		// Clear out all the places where this lovely info is stored.
-		// !! Maybe not log_mark_read?
+		// @todo Maybe not log_mark_read?
 		$smcFunc['db_query']('', '
 			DELETE FROM {db_prefix}log_mark_read
 			WHERE id_board IN ({array_int:board_list})
@@ -146,7 +146,7 @@ function markBoardsRead($boards, $unread = false)
 	if (empty($lowest_topic))
 		return;
 
-	// @todoSLOW This query seems to eat it sometimes.
+	// @todo SLOW This query seems to eat it sometimes.
 	$result = $smcFunc['db_query']('', '
 		SELECT lt.id_topic
 		FROM {db_prefix}log_topics AS lt
@@ -440,6 +440,7 @@ function MarkRead()
 /**
  * Get the id_member associated with the specified message.
  * @param int $messageID
+ * @return int the member id
  */
 function getMsgMemberID($messageID)
 {

+ 10 - 1
Sources/Subs-Compat.php

@@ -358,4 +358,13 @@ if (!function_exists('bcpowmod') && function_exists('bcpow'))
 	}
 }
 
-?>
+/**
+ * Random seed generator
+ */
+if (version_compare(PHP_VERSION, '4.2.0', '<'))
+{
+	$seed = ($modSettings['rand_seed'] + ((double) microtime() * 1000003)) & 0x7fffffff;
+	mt_srand($seed);
+}
+
+?>

+ 1 - 1
Sources/Subs-Editor.php

@@ -336,7 +336,7 @@ function html_to_bbc($text)
 				}
 
 				// Preserve some tags stripping the styling.
-				if (in_array($matches[2], array('a', 'font')))
+				if (in_array($matches[2], array('a', 'font', 'td')))
 				{
 					$replacement .= $precedingStyle . $afterStyle;
 					$curCloseTags = '</' . $matches[2] . '>' . $curCloseTags;

+ 3 - 9
Sources/Subs.php

@@ -2230,7 +2230,7 @@ function parse_bbc($message, $smileys = true, $cache_id = '', $parse_tags = arra
 				substr($message, $pos1, $pos2 - $pos1)
 			);
 
-			if (!empty($tag['block_level']) && substr($data[0], '<br />') === 0)
+			if (!empty($tag['block_level']) && strpos($data[0], '<br />') === 0)
 				$data[0] = substr($data[0], 6);
 
 			// Validation for my parking, please!
@@ -2839,6 +2839,7 @@ function setupThemeContext($forceload = false)
 	// Resize avatars the fancy, but non-GD requiring way.
 	if ($modSettings['avatar_action_too_large'] == 'option_js_resize' && (!empty($modSettings['avatar_max_width_external']) || !empty($modSettings['avatar_max_height_external'])))
 	{
+		// @todo Move this over to script.js?
 		$context['html_headers'] .= '
 	<script type="text/javascript"><!-- // --><![CDATA[
 		var smf_avatarMaxWidth = ' . (int) $modSettings['avatar_max_width_external'] . ';
@@ -2852,7 +2853,6 @@ function setupThemeContext($forceload = false)
 	var window_oldAvatarOnload = window.onload;
 	window.onload = smf_avatarResize;';
 
-		// @todo Move this over to script.js?
 		$context['html_headers'] .= '
 	// ]]></script>';
 	}
@@ -2929,6 +2929,7 @@ function template_header()
 		if (in_array($layer, array('body', 'main')) && allowedTo('admin_forum') && !$user_info['is_guest'] && !$checked_securityFiles)
 		{
 			$checked_securityFiles = true;
+			// @todo add a hook here
 			$securityFiles = array('install.php', 'webinstall.php', 'upgrade.php', 'convert.php', 'repair_paths.php', 'repair_settings.php', 'Settings.php~', 'Settings_bak.php~');
 			foreach ($securityFiles as $i => $securityFile)
 			{
@@ -3686,13 +3687,6 @@ function smf_seed_generator()
 		updateSettings(array('rand_seed' => $modSettings['rand_seed']));
 	}
 
-	// @todo remove this or move it to Subs-Compat.php
-	if (version_compare(PHP_VERSION, '4.2.0', '<'))
-	{
-		$seed = ($modSettings['rand_seed'] + ((double) microtime() * 1000003)) & 0x7fffffff;
-		mt_srand($seed);
-	}
-
 	// Change the seed.
 	updateSettings(array('rand_seed' => mt_rand()));
 }

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

@@ -48,6 +48,7 @@ $txt['cookieTime'] = 'Default login cookies length (in minutes)';
 $txt['localCookies'] = 'Enable local storage of cookies<div class="smalltext">(SSI won\'t work well with this on.)</div>';
 $txt['globalCookies'] = 'Use subdomain independent cookies<div class="smalltext">(turn off local cookies first!)</div>';
 $txt['secureCookies'] = 'Force cookies to be secure<div class="smalltext">(This only applies if you are using HTTPS - don\'t use otherwise!)</div>';
+$txt['httponlyCookies'] = 'Force cookies to be made accessible only through the HTTP protocol. <div class="smalltext">(Cookies won\'t be accessible by scripting languages, such as JavaScript. This setting can help to reduce identity theft through XSS attacks.)</div>';
 $txt['securityDisable'] = 'Disable administration security';
 $txt['securityDisable_moderate'] = 'Disable moderation security';
 $txt['send_validation_onChange'] = 'Require reactivation after email change';

+ 1 - 1
index.php

@@ -192,7 +192,7 @@ function smf_main()
 		fatal_lang_error('not_a_topic', false);
 
 	$no_stat_actions = array('dlattach', 'findmember', 'jseditor', 'jsoption', 'requestmembers', 'smstats', '.xml', 'xmlhttp', 'verificationcode', 'viewquery', 'viewsmfile');
-	call_integration_hook('integrate_pre_log_stats', &$no_stat_actions);
+	call_integration_hook('integrate_pre_log_stats', $no_stat_actions);
 	// Do some logging, unless this is an attachment, avatar, toggle of editor buttons, theme option, XML feed etc.
 	if (empty($_REQUEST['action']) || !in_array($_REQUEST['action'], $no_stat_actions))
 	{