From bc242bbda559c786bf8560fb9bd976eb7269543b Mon Sep 17 00:00:00 2001 From: Matteo Scaramuccia Date: Tue, 11 Jun 2013 20:15:42 +0200 Subject: [PATCH] MDL-39810 Files: IE doesn't recognize JSON MIME Type (RFC4627). Affected versions: - IE 8+, under Compatibility View. --- lib/classes/useragent.php | 89 +++++++++++++++++++++++++++++++----- lib/outputrenderers.php | 6 +++ lib/tests/useragent_test.php | 21 +++++++++ 3 files changed, 105 insertions(+), 11 deletions(-) diff --git a/lib/classes/useragent.php b/lib/classes/useragent.php index 7b92d231768..3782b5ce7e8 100644 --- a/lib/classes/useragent.php +++ b/lib/classes/useragent.php @@ -475,12 +475,13 @@ class core_useragent { } /** - * Checks the user agent is IE and that the version is equal to or greater than that specified. + * Checks the user agent is IE and returns its main properties: + * - browser version; + * - whether running in compatibility view. * - * @param string|int $version A version to check for, returns true if its equal to or greater than that specified. - * @return bool + * @return bool|array False if not IE, otherwise an associative array of properties. */ - public static function check_ie_version($version = null) { + public static function check_ie_properties() { // Internet Explorer. $useragent = self::get_user_agent_string(); if ($useragent === false) { @@ -490,12 +491,6 @@ class core_useragent { // Reject Opera. return false; } - // In case of IE we have to deal with BC of the version parameter. - if (is_null($version)) { - $version = 5.5; // Anything older is not considered a browser at all! - } - // IE uses simple versions, let's cast it to float to simplify the logic here. - $version = round($version, 1); // See: http://www.useragentstring.com/pages/Internet%20Explorer/. if (preg_match("/MSIE ([0-9\.]+)/", $useragent, $match)) { $browser = $match[1]; @@ -505,13 +500,62 @@ class core_useragent { } else { return false; } + $compat_view = false; // IE8 and later versions may pretend to be IE7 for intranet sites, use Trident version instead, // the Trident should always describe the capabilities of IE in any emulation mode. if ($browser === '7.0' and preg_match("/Trident\/([0-9\.]+)/", $useragent, $match)) { + $compat_view = true; $browser = $match[1] + 4; // NOTE: Hopefully this will work also for future IE versions. } $browser = round($browser, 1); - return ($browser >= $version); + return array( + 'version' => $browser, + 'compatview' => $compat_view + ); + } + + /** + * Checks the user agent is IE and that the version is equal to or greater than that specified. + * + * @param string|int $version A version to check for, returns true if its equal to or greater than that specified. + * @return bool + */ + public static function check_ie_version($version = null) { + // Internet Explorer. + $properties = self::check_ie_properties(); + if (!is_array($properties)) { + return false; + } + // In case of IE we have to deal with BC of the version parameter. + if (is_null($version)) { + $version = 5.5; // Anything older is not considered a browser at all! + } + // IE uses simple versions, let's cast it to float to simplify the logic here. + $version = round($version, 1); + return ($properties['version'] >= $version); + } + + /** + * Checks the user agent is IE and that IE is running under Compatibility View setting. + * + * @return bool true if internet explorer runs in Compatibility View mode. + */ + public static function check_ie_compatibility_view() { + // IE User Agent string when in Compatibility View: + // - IE 8: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/4.0; ...)". + // - IE 9: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/5.0; ...)". + // - IE 10: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; Trident/6.0; ...)". + // - IE 11: "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; ...)". + // Refs: + // - http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx. + // - http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx. + // - http://blogs.msdn.com/b/ie/archive/2011/04/15/the-ie10-user-agent-string.aspx. + // - http://msdn.microsoft.com/en-us/library/ie/hh869301%28v=vs.85%29.aspx. + $properties = self::check_ie_properties(); + if (!is_array($properties)) { + return false; + } + return $properties['compatview']; } /** @@ -832,4 +876,27 @@ class core_useragent { } return $instance->supportssvg; } + + /** + * Returns true if the user agent supports the MIME media type for JSON text, as defined in RFC 4627. + * + * @return bool + */ + public static function supports_json_contenttype() { + // Modern browsers other than IE correctly supports 'application/json' media type. + if (!self::is_ie()) { + return true; + } + + // IE8+ supports 'application/json' media type, when NOT in Compatibility View mode. + // Refs: + // - http://blogs.msdn.com/b/ie/archive/2008/09/10/native-json-in-ie8.aspx; + // - MDL-39810: issues when using 'text/plain' in Compatibility View for the body of an HTTP POST response. + if (self::check_ie_version(8) && !self::check_ie_compatibility_view()) { + return true; + } + + // This browser does not support json. + return false; + } } diff --git a/lib/outputrenderers.php b/lib/outputrenderers.php index 56b6f56cbde..39a7cec3c81 100644 --- a/lib/outputrenderers.php +++ b/lib/outputrenderers.php @@ -3485,6 +3485,12 @@ class core_renderer_ajax extends core_renderer { // unfortunately YUI iframe upload does not support application/json if (!empty($_FILES)) { @header('Content-type: text/plain; charset=utf-8'); + if (!core_useragent::supports_json_contenttype()) { + @header('X-Content-Type-Options: nosniff'); + } + } else if (!core_useragent::supports_json_contenttype()) { + @header('Content-type: text/plain; charset=utf-8'); + @header('X-Content-Type-Options: nosniff'); } else { @header('Content-type: application/json; charset=utf-8'); } diff --git a/lib/tests/useragent_test.php b/lib/tests/useragent_test.php index 87063f0c349..ddfadb8cafa 100644 --- a/lib/tests/useragent_test.php +++ b/lib/tests/useragent_test.php @@ -67,6 +67,9 @@ class core_useragent_testcase extends basic_testcase { '11.0' => array( 'Windows 8.1' => 'Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0)' ), + '11.0i' => array( + 'Windows 8.1' => ' Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.3; Trident/7.0; .NET4.0E; .NET4.0C)' + ), ), 'Firefox' => array( '1.0.6' => array( @@ -218,6 +221,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::is_ie()); $this->assertTrue(core_useragent::check_ie_version()); $this->assertTrue(core_useragent::check_ie_version('5.0')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('7.0')); core_useragent::instance(true, $this->user_agents['MSIE']['5.0']['Windows 98']); @@ -225,6 +229,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_ie_version()); $this->assertTrue(core_useragent::check_ie_version(0)); $this->assertTrue(core_useragent::check_ie_version('5.0')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('7.0')); core_useragent::instance(true, $this->user_agents['MSIE']['9.0']['Windows 7']); @@ -233,6 +238,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version(0)); $this->assertTrue(core_useragent::check_ie_version('5.0')); $this->assertTrue(core_useragent::check_ie_version('9.0')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('10')); core_useragent::instance(true, $this->user_agents['MSIE']['9.0i']['Windows 7']); @@ -241,6 +247,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version(0)); $this->assertTrue(core_useragent::check_ie_version('5.0')); $this->assertTrue(core_useragent::check_ie_version('9.0')); + $this->assertTrue(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('10')); core_useragent::instance(true, $this->user_agents['MSIE']['10.0']['Windows 8']); @@ -250,6 +257,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('5.0')); $this->assertTrue(core_useragent::check_ie_version('9.0')); $this->assertTrue(core_useragent::check_ie_version('10')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('11')); core_useragent::instance(true, $this->user_agents['MSIE']['10.0i']['Windows 8']); @@ -259,6 +267,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('5.0')); $this->assertTrue(core_useragent::check_ie_version('9.0')); $this->assertTrue(core_useragent::check_ie_version('10')); + $this->assertTrue(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('11')); core_useragent::instance(true, $this->user_agents['MSIE']['11.0']['Windows 8.1']); @@ -269,6 +278,18 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('9.0')); $this->assertTrue(core_useragent::check_ie_version('10')); $this->assertTrue(core_useragent::check_ie_version('11')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); + $this->assertFalse(core_useragent::check_ie_version('12')); + + core_useragent::instance(true, $this->user_agents['MSIE']['11.0i']['Windows 8.1']); + $this->assertTrue(core_useragent::is_ie()); + $this->assertTrue(core_useragent::check_ie_version()); + $this->assertTrue(core_useragent::check_ie_version(0)); + $this->assertTrue(core_useragent::check_ie_version('5.0')); + $this->assertTrue(core_useragent::check_ie_version('9.0')); + $this->assertTrue(core_useragent::check_ie_version('10')); + $this->assertTrue(core_useragent::check_ie_version('11')); + $this->assertTrue(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('12')); core_useragent::instance(true, $this->user_agents['Firefox']['2.0']['Windows XP']);