From 8fe8c9203e8b7da6274d041c0ba589f5ef8777b7 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 25 Jun 2016 20:59:14 +0900 Subject: [PATCH 1/6] Improve browser detection - Distinguish iOS from Mac OS X - Better detection of Android default browser and webview - Detect Chrome and Firefox on iOS - Detect common search robots --- common/framework/ua.php | 55 ++++++++++++---- tests/unit/framework/UATest.php | 111 +++++++++++++++++++++++++++++++- 2 files changed, 152 insertions(+), 14 deletions(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index d2b9010ad..b9cf7728b 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -173,6 +173,7 @@ class UA 'os' => null, 'is_mobile' => null, 'is_tablet' => null, + 'is_robot' => null, ); if (is_null($ua)) { @@ -180,29 +181,40 @@ class UA } // Try to guess the OS. - if (preg_match('#(Windows|Android|Linux|iOS|OS X|Macintosh)#i', $ua, $matches)) + if (preg_match('#(Windows|Android|Linux|i(?:Phone|P[ao]d)|OS X|Macintosh)#i', $ua, $matches)) { if ($matches[1] === 'Linux' && strpos($ua, 'Android') !== false) { - $matches[1] = 'Android'; + $result->os = 'Android'; } - if ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false) + elseif ($matches[1] === 'iPhone' || $matches[1] === 'iPad' || $matches[1] === 'iPod') { - $matches[1] = 'OS X'; + $result->os = 'iOS'; + } + elseif ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false) + { + $result->os = 'OS X'; + } + else + { + $result->os = $matches[1]; } - $result->os = $matches[1]; } // Fill in miscellaneous fields. $result->is_mobile = self::isMobile($ua); $result->is_tablet = self::isTablet($ua); + $result->is_robot = self::isRobot($ua); // Try to match some of the most common browsers. - if (preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches) && strpos($ua, 'Chrome') === false) + if ($result->os === 'Android' && preg_match('#Android ([0-9]+\\.[0-9]+)#', $ua, $matches)) { - $result->browser = 'Android'; - $result->version = $matches[1]; - return $result; + if (strpos($ua, 'Chrome') === false || preg_match('#(?:Version|Browser)/[0-9]+#', $ua) || preg_match('#\\bwv\\b#', $ua)) + { + $result->browser = 'Android'; + $result->version = $matches[1]; + return $result; + } } if (preg_match('#Edge/([0-9]+\\.)#', $ua, $matches)) { @@ -216,12 +228,33 @@ class UA $result->version = ($matches[1] + 4) . '.0'; return $result; } - if (preg_match('#(MSIE|Chrome|Firefox|Safari)[ /:]([0-9]+\\.[0-9]+)#', $ua, $matches)) + if (preg_match('#(MSIE|Chrome|CriOS|Firefox|FxiOS|Yeti|[a-z]+bot(?:-Image)?)[ /:]([0-9]+\\.[0-9]+)#i', $ua, $matches)) { - $result->browser = $matches[1] === 'MSIE' ? 'IE' : $matches[1]; + if ($matches[1] === 'MSIE') + { + $result->browser = 'IE'; + } + elseif ($matches[1] === 'CriOS') + { + $result->browser = 'Chrome'; + } + elseif ($matches[1] === 'FxiOS') + { + $result->browser = 'Firefox'; + } + else + { + $result->browser = ucfirst($matches[1]); + } $result->version = $matches[2]; return $result; } + if (preg_match('#Safari/[0-9]+#', $ua) && preg_match('#Version/([0-9]+\\.[0-9]+)#', $ua, $matches) && $matches[1] < 500) + { + $result->browser = 'Safari'; + $result->version = $matches[1]; + return $result; + } if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches)) { $result->browser = 'Opera'; diff --git a/tests/unit/framework/UATest.php b/tests/unit/framework/UATest.php index ca2a6f03e..7c5c48666 100644 --- a/tests/unit/framework/UATest.php +++ b/tests/unit/framework/UATest.php @@ -56,8 +56,25 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('Android', $browser->os); $this->assertTrue($browser->is_mobile); $this->assertTrue($browser->is_tablet); + $this->assertFalse($browser->is_robot); - // Mobile Chrome + // Android default browser (possible confusion with Chrome) + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 4.4.4; One Build/KTU84L.H4) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/33.0.0.0 Mobile Safari/537.36'); + $this->assertEquals('Android', $browser->browser); + $this->assertEquals('4.4', $browser->version); + $this->assertTrue($browser->is_mobile); + $this->assertFalse($browser->is_tablet); + $this->assertFalse($browser->is_robot); + + // Android webview (possible confusion with Chrome) + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 5.1.1; Nexus 5 Build/LMY48B; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36'); + $this->assertEquals('Android', $browser->browser); + $this->assertEquals('5.1', $browser->version); + $this->assertTrue($browser->is_mobile); + $this->assertFalse($browser->is_tablet); + $this->assertFalse($browser->is_robot); + + // Android Chrome $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Linux; Android 5.0; Nexus 5 Build/LPX13D) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.102 Mobile Safari/537.36'); $this->assertEquals('Chrome', $browser->browser); $this->assertEquals('38.0', $browser->version); @@ -65,13 +82,26 @@ class UATest extends \Codeception\TestCase\Test $this->assertTrue($browser->is_mobile); $this->assertFalse($browser->is_tablet); - // Edge + // Windows Chrome + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36'); + $this->assertEquals('Chrome', $browser->browser); + $this->assertEquals('51.0', $browser->version); + $this->assertEquals('Windows', $browser->os); + $this->assertFalse($browser->is_mobile); + + // Edge 13 + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586'); + $this->assertEquals('Edge', $browser->browser); + $this->assertEquals('13.0', $browser->version); + $this->assertEquals('Windows', $browser->os); + $this->assertFalse($browser->is_mobile); + + // Edge 12 $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36 Edge/12.10136'); $this->assertEquals('Edge', $browser->browser); $this->assertEquals('12.0', $browser->version); $this->assertEquals('Windows', $browser->os); $this->assertFalse($browser->is_mobile); - $this->assertFalse($browser->is_tablet); // IE 11 $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko'); @@ -84,6 +114,11 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('IE', $browser->browser); $this->assertEquals('10.0', $browser->version); + // IE 9 + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)'); + $this->assertEquals('IE', $browser->browser); + $this->assertEquals('9.0', $browser->version); + // IE 8 in compatibility mode $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Trident/4.0)'); $this->assertEquals('IE', $browser->browser); @@ -94,11 +129,81 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('Chrome', $browser->browser); $this->assertEquals('41.0', $browser->version); $this->assertEquals('Linux', $browser->os); + $this->assertFalse($browser->is_mobile); + + // Linux Konqueror + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9'); + $this->assertEquals('Konqueror', $browser->browser); + $this->assertEquals('4.9', $browser->version); + $this->assertEquals('Linux', $browser->os); + + // iOS Safari + $browser = Rhymix\Framework\UA::getBrowserInfo('iPad: Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3'); + $this->assertEquals('Safari', $browser->browser); + $this->assertEquals('5.1', $browser->version); + $this->assertEquals('iOS', $browser->os); + $this->assertTrue($browser->is_mobile); + $this->assertTrue($browser->is_tablet); + + // iOS Chrome + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3'); + $this->assertEquals('Chrome', $browser->browser); + $this->assertEquals('19.0', $browser->version); + $this->assertEquals('iOS', $browser->os); + $this->assertTrue($browser->is_mobile); + $this->assertFalse($browser->is_tablet); + + // iOS Firefox + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (iPad; CPU iPhone OS 8_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) FxiOS/1.0 Mobile/12F69 Safari/600.1.4'); + $this->assertEquals('Firefox', $browser->browser); + $this->assertEquals('1.0', $browser->version); + $this->assertEquals('iOS', $browser->os); + $this->assertTrue($browser->is_mobile); + $this->assertTrue($browser->is_tablet); + + // OS X Safari + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18'); + $this->assertEquals('Safari', $browser->browser); + $this->assertEquals('8.0', $browser->version); + $this->assertEquals('OS X', $browser->os); + $this->assertFalse($browser->is_mobile); + + // OS X Chrome + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36'); + $this->assertEquals('Chrome', $browser->browser); + $this->assertEquals('41.0', $browser->version); + $this->assertEquals('OS X', $browser->os); + $this->assertFalse($browser->is_mobile); // OS X Firefox $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0'); $this->assertEquals('Firefox', $browser->browser); $this->assertEquals('33.0', $browser->version); $this->assertEquals('OS X', $browser->os); + $this->assertFalse($browser->is_mobile); + + // Googlebot + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'); + $this->assertEquals('Googlebot', $browser->browser); + $this->assertEquals('2.1', $browser->version); + $this->assertTrue($browser->is_robot); + + // Googlebot-Image + $browser = Rhymix\Framework\UA::getBrowserInfo('Googlebot-Image/1.0'); + $this->assertEquals('Googlebot-Image', $browser->browser); + $this->assertEquals('1.0', $browser->version); + $this->assertTrue($browser->is_robot); + + // Bingbot + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'); + $this->assertEquals('Bingbot', $browser->browser); + $this->assertEquals('2.0', $browser->version); + $this->assertTrue($browser->is_robot); + + // Yeti + $browser = Rhymix\Framework\UA::getBrowserInfo('Yeti/1.0 (+http://help.naver.com/robots/)'); + $this->assertEquals('Yeti', $browser->browser); + $this->assertEquals('1.0', $browser->version); + $this->assertTrue($browser->is_robot); } } From c3fe8d265b64945d7a9c984ff70f94b872b60fc9 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 25 Jun 2016 21:22:32 +0900 Subject: [PATCH 2/6] Move RFC5987 encoding function to UA class --- common/framework/ua.php | 61 ++++++++++++++++++++++++++++++++ modules/file/file.controller.php | 21 ++--------- 2 files changed, 63 insertions(+), 19 deletions(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index b9cf7728b..341545831 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -270,4 +270,65 @@ class UA return $result; } + + /** + * This method encodes a UTF-8 filename for downloading in the current visitor's browser. + * + * @param string $filename + * @param string $ua (optional) + * @return string + */ + public static function encodeFilenameForDownload($filename, $ua = null) + { + // Get the User-Agent header if the caller did not specify $ua. + $ua = $ua ?: (isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null); + + // Get the browser name and version. + $browser = self::getBrowserInfo($ua); + + // Find the best format that this browser supports. + if ($browser->browser === 'Chrome' && $browser->version >= 11) + { + $output_format = 'rfc5987'; + } + elseif ($browser->browser === 'Firefox' && $browser->version >= 6) + { + $output_format = 'rfc5987'; + } + elseif ($browser->browser === 'Safari' && $browser->version >= 6) + { + $output_format = 'rfc5987'; + } + elseif ($browser->browser === 'IE' && $browser->version >= 10) + { + $output_format = 'rfc5987'; + } + elseif ($browser->browser === 'IE') + { + $output_format = 'old_ie'; + } + else + { + $output_format = 'raw'; + } + + // Clean the filename. + $filename = Filters\FilenameFilter::clean($filename); + + // Apply the format and return. + switch ($output_format) + { + case 'raw': + return 'filename="' . $filename . '"'; + + case 'rfc5987': + $filename = rawurlencode($filename); + return "filename*=UTF-8''" . $filename . '; filename="' . $filename . '"'; + + case 'old_ie': + default: + $filename = rawurlencode($filename); + return 'filename="' . preg_replace('/\./', '%2e', $filename, substr_count($filename, '.') - 1) . '"'; + } + } } diff --git a/modules/file/file.controller.php b/modules/file/file.controller.php index 730e19cab..2719cfc7a 100644 --- a/modules/file/file.controller.php +++ b/modules/file/file.controller.php @@ -342,25 +342,8 @@ class fileController extends file exit(); } - // Filename encoding for browsers that support RFC 5987 - if(preg_match('#(?:Chrome|Edge)/(\d+)\.#', $_SERVER['HTTP_USER_AGENT'], $matches) && $matches[1] >= 11) - { - $filename_param = "filename*=UTF-8''" . rawurlencode($filename) . '; filename="' . rawurlencode($filename) . '"'; - } - elseif(preg_match('#(?:Firefox|Safari|Trident)/(\d+)\.#', $_SERVER['HTTP_USER_AGENT'], $matches) && $matches[1] >= 6) - { - $filename_param = "filename*=UTF-8''" . rawurlencode($filename) . '; filename="' . rawurlencode($filename) . '"'; - } - // Filename encoding for browsers that do not support RFC 5987 - elseif(strpos($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== FALSE) - { - $filename = rawurlencode($filename); - $filename_param = 'filename="' . preg_replace('/\./', '%2e', $filename, substr_count($filename, '.') - 1) . '"'; - } - else - { - $filename_param = 'filename="' . $filename . '"'; - } + // Encode the filename. + $filename_param = Rhymix\Framework\UA::encodeFilenameForDownload($filename); // Close context to prevent blocking the session Context::close(); From c9c4d7cda1a0d930ea789502a5b5292a584d646d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sat, 25 Jun 2016 21:37:35 +0900 Subject: [PATCH 3/6] Improve filename encoding for miscellaneous old browsers --- common/framework/ua.php | 10 +++++++++- tests/unit/framework/UATest.php | 12 ++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index 341545831..f3d20d3ec 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -303,14 +303,22 @@ class UA { $output_format = 'rfc5987'; } + elseif ($browser->browser === 'Edge') + { + $output_format = 'rfc5987'; + } elseif ($browser->browser === 'IE') { $output_format = 'old_ie'; } - else + elseif ($browser->browser === 'Android' || $browser->browser === 'Chrome' || $browser->browser === 'Safari') { $output_format = 'raw'; } + else + { + $output_format = 'old_ie'; + } // Clean the filename. $filename = Filters\FilenameFilter::clean($filename); diff --git a/tests/unit/framework/UATest.php b/tests/unit/framework/UATest.php index 7c5c48666..951d2ce80 100644 --- a/tests/unit/framework/UATest.php +++ b/tests/unit/framework/UATest.php @@ -206,4 +206,16 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('1.0', $browser->version); $this->assertTrue($browser->is_robot); } + + public function testEncodeFilenameForDownload() + { + $this->assertEquals('filename*=UTF-8\'\'%ED%95%9C%EA%B8%80%20filename.jpg; filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Chrome/50.0')); + $this->assertEquals('filename*=UTF-8\'\'%ED%95%9C%EA%B8%80%20filename.jpg; filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Firefox/46.0')); + $this->assertEquals('filename*=UTF-8\'\'%ED%95%9C%EA%B8%80%20filename.jpg; filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Edge/12.10240')); + $this->assertEquals('filename*=UTF-8\'\'%ED%95%9C%EA%B8%80%20filename.jpg; filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'MSIE/7.0 Trident/7.0')); + $this->assertEquals('filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'MSIE 8.0')); + $this->assertEquals('filename="%ED%95%9C%EA%B8%80%20filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Unknown Browser')); + $this->assertEquals('filename="한글 filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Safari/5.0 Version/5.0')); + $this->assertEquals('filename="한글 filename.jpg"', Rhymix\Framework\UA::encodeFilenameForDownload('한글 filename.jpg', 'Linux; Android 5.1.1; Version/4.0 Chrome/43.0.2357.65 Mobile Safari/537.36')); + } } From 38b3db12383af55fa173c031019a7be39aee8713 Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 26 Jun 2016 15:41:07 +0900 Subject: [PATCH 4/6] Rename OS X to macOS (official name change since Sierra) --- common/framework/ua.php | 4 ++-- tests/unit/framework/UATest.php | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index f3d20d3ec..c1ffad4e9 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -191,9 +191,9 @@ class UA { $result->os = 'iOS'; } - elseif ($matches[1] === 'Macintosh' && strpos($ua, 'OS X') !== false) + elseif ($matches[1] === 'Macintosh' || $matches[1] === 'OS X') { - $result->os = 'OS X'; + $result->os = 'macOS'; } else { diff --git a/tests/unit/framework/UATest.php b/tests/unit/framework/UATest.php index 951d2ce80..3648b8aaa 100644 --- a/tests/unit/framework/UATest.php +++ b/tests/unit/framework/UATest.php @@ -161,25 +161,25 @@ class UATest extends \Codeception\TestCase\Test $this->assertTrue($browser->is_mobile); $this->assertTrue($browser->is_tablet); - // OS X Safari + // macOS (OS X) Safari $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/600.3.18 (KHTML, like Gecko) Version/8.0.3 Safari/600.3.18'); $this->assertEquals('Safari', $browser->browser); $this->assertEquals('8.0', $browser->version); - $this->assertEquals('OS X', $browser->os); + $this->assertEquals('macOS', $browser->os); $this->assertFalse($browser->is_mobile); - // OS X Chrome + // macOS (OS X) Chrome $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36'); $this->assertEquals('Chrome', $browser->browser); $this->assertEquals('41.0', $browser->version); - $this->assertEquals('OS X', $browser->os); + $this->assertEquals('macOS', $browser->os); $this->assertFalse($browser->is_mobile); - // OS X Firefox + // macOS (OS X) Firefox $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10; rv:33.0) Gecko/20100101 Firefox/33.0'); $this->assertEquals('Firefox', $browser->browser); $this->assertEquals('33.0', $browser->version); - $this->assertEquals('OS X', $browser->os); + $this->assertEquals('macOS', $browser->os); $this->assertFalse($browser->is_mobile); // Googlebot From 2086c7329585abeb9babd339445137a57799752d Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 26 Jun 2016 17:07:05 +0900 Subject: [PATCH 5/6] Change detection order of Opera/Konq/Chrome/Safari --- common/framework/ua.php | 37 +++++++++++++++++------- tests/unit/framework/UATest.php | 50 ++++++++++++++++++++++++--------- 2 files changed, 64 insertions(+), 23 deletions(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index c1ffad4e9..15187413c 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -228,7 +228,7 @@ class UA $result->version = ($matches[1] + 4) . '.0'; return $result; } - if (preg_match('#(MSIE|Chrome|CriOS|Firefox|FxiOS|Yeti|[a-z]+bot(?:-Image)?)[ /:]([0-9]+\\.[0-9]+)#i', $ua, $matches)) + if (preg_match('#(MSIE|OPR|CriOS|Firefox|FxiOS|Iceweasel|Yeti|[a-z]+bot(?:-Image)?)[ /:]([0-9]+\\.[0-9]+)#i', $ua, $matches)) { if ($matches[1] === 'MSIE') { @@ -238,10 +238,14 @@ class UA { $result->browser = 'Chrome'; } - elseif ($matches[1] === 'FxiOS') + elseif ($matches[1] === 'FxiOS' || $matches[1] === 'Iceweasel') { $result->browser = 'Firefox'; } + elseif ($matches[1] === 'OPR') + { + $result->browser = 'Opera'; + } else { $result->browser = ucfirst($matches[1]); @@ -249,16 +253,17 @@ class UA $result->version = $matches[2]; return $result; } - if (preg_match('#Safari/[0-9]+#', $ua) && preg_match('#Version/([0-9]+\\.[0-9]+)#', $ua, $matches) && $matches[1] < 500) - { - $result->browser = 'Safari'; - $result->version = $matches[1]; - return $result; - } - if (preg_match('#^Opera/.+(?:Opera |Version/)([0-9]+\\.[0-9]+)$#', $ua, $matches)) + if (preg_match('#(?:Opera|OPR)[/ ]([0-9]+\\.[0-9]+)#', $ua, $matches)) { $result->browser = 'Opera'; - $result->version = $matches[1]; + if ($matches[1] >= 9.79 && preg_match('#Version/([0-9]+\\.[0-9]+)#', $ua, $operamatches)) + { + $result->version = $operamatches[1]; + } + else + { + $result->version = $matches[1]; + } return $result; } if (preg_match('#(?:Konqueror|KHTML)/([0-9]+\\.[0-9]+)$#', $ua, $matches)) @@ -267,6 +272,18 @@ class UA $result->version = $matches[1]; return $result; } + if (preg_match('#Chrome/([0-9]+\\.[0-9]+)#', $ua, $matches)) + { + $result->browser = 'Chrome'; + $result->version = $matches[1]; + return $result; + } + if (preg_match('#Safari/[0-9]+#', $ua) && preg_match('#Version/([0-9]+\\.[0-9]+)#', $ua, $matches) && $matches[1] < 500) + { + $result->browser = 'Safari'; + $result->version = $matches[1]; + return $result; + } return $result; } diff --git a/tests/unit/framework/UATest.php b/tests/unit/framework/UATest.php index 3648b8aaa..573880ed9 100644 --- a/tests/unit/framework/UATest.php +++ b/tests/unit/framework/UATest.php @@ -89,6 +89,19 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('Windows', $browser->os); $this->assertFalse($browser->is_mobile); + // Linux Chrome + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'); + $this->assertEquals('Chrome', $browser->browser); + $this->assertEquals('41.0', $browser->version); + $this->assertEquals('Linux', $browser->os); + $this->assertFalse($browser->is_mobile); + + // Linux Konqueror + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9'); + $this->assertEquals('Konqueror', $browser->browser); + $this->assertEquals('4.9', $browser->version); + $this->assertEquals('Linux', $browser->os); + // Edge 13 $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586'); $this->assertEquals('Edge', $browser->browser); @@ -124,19 +137,6 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('IE', $browser->browser); $this->assertEquals('8.0', $browser->version); - // Linux Chrome - $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36'); - $this->assertEquals('Chrome', $browser->browser); - $this->assertEquals('41.0', $browser->version); - $this->assertEquals('Linux', $browser->os); - $this->assertFalse($browser->is_mobile); - - // Linux Konqueror - $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux) KHTML/4.9.1 (like Gecko) Konqueror/4.9'); - $this->assertEquals('Konqueror', $browser->browser); - $this->assertEquals('4.9', $browser->version); - $this->assertEquals('Linux', $browser->os); - // iOS Safari $browser = Rhymix\Framework\UA::getBrowserInfo('iPad: Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3'); $this->assertEquals('Safari', $browser->browser); @@ -182,6 +182,30 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('macOS', $browser->os); $this->assertFalse($browser->is_mobile); + // Opera 15 + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/28.0.1500.52 Safari/537.36 OPR/15.0.1147.100'); + $this->assertEquals('Opera', $browser->browser); + $this->assertEquals('15.0', $browser->version); + $this->assertFalse($browser->is_mobile); + + // Opera 12 + $browser = Rhymix\Framework\UA::getBrowserInfo('Opera/9.80 (X11; Linux i686; Ubuntu/14.10) Presto/2.12.388 Version/12.16'); + $this->assertEquals('Opera', $browser->browser); + $this->assertEquals('12.16', $browser->version); + $this->assertFalse($browser->is_mobile); + + // Opera 9.x + $browser = Rhymix\Framework\UA::getBrowserInfo('Opera/9.64 (X11; Linux x86_64; U; en) Presto/2.1.1'); + $this->assertEquals('Opera', $browser->browser); + $this->assertEquals('9.64', $browser->version); + $this->assertFalse($browser->is_mobile); + + // Iceweasel (Debian Firefox) + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (X11; Linux i686; rv:10.0.7) Gecko/20100101 Iceweasel/10.0.7'); + $this->assertEquals('Firefox', $browser->browser); + $this->assertEquals('10.0', $browser->version); + $this->assertFalse($browser->is_mobile); + // Googlebot $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'); $this->assertEquals('Googlebot', $browser->browser); From 976fa799f3123f5d48586eae3272443eac12142c Mon Sep 17 00:00:00 2001 From: Kijin Sung Date: Sun, 26 Jun 2016 17:25:59 +0900 Subject: [PATCH 6/6] Add more edge cases and tests, always default to Mozilla/X.X --- common/framework/ua.php | 20 ++++++++++++++- tests/unit/framework/UATest.php | 44 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/common/framework/ua.php b/common/framework/ua.php index 15187413c..961b93307 100644 --- a/common/framework/ua.php +++ b/common/framework/ua.php @@ -228,7 +228,7 @@ class UA $result->version = ($matches[1] + 4) . '.0'; return $result; } - if (preg_match('#(MSIE|OPR|CriOS|Firefox|FxiOS|Iceweasel|Yeti|[a-z]+bot(?:-Image)?)[ /:]([0-9]+\\.[0-9]+)#i', $ua, $matches)) + if (preg_match('#(MSIE|OPR|CriOS|Firefox|FxiOS|Iceweasel|Yeti|[a-z]+(?:bot|spider)(?:-Image)?|wget|curl)[ /:]([0-9]+\\.[0-9]+)#i', $ua, $matches)) { if ($matches[1] === 'MSIE') { @@ -284,6 +284,24 @@ class UA $result->version = $matches[1]; return $result; } + if (preg_match('#\\bPHP(/[0-9]+\\.[0-9]+)?#', $ua, $matches)) + { + $result->browser = 'PHP'; + $result->version = (isset($matches[1]) && $matches[1]) ? substr($matches[1], 1) : null; + return $result; + } + if (preg_match('#^Mozilla/([0-9]+\\.[0-9]+)#', $ua, $matches)) + { + $result->browser = 'Mozilla'; + $result->version = $matches[1]; + return $result; + } + if (preg_match('#^([a-zA-Z0-9_-]+)/([0-9]+\\.[0-9]+)#', $ua, $matches)) + { + $result->browser = ucfirst($matches[1]); + $result->version = $matches[2]; + return $result; + } return $result; } diff --git a/tests/unit/framework/UATest.php b/tests/unit/framework/UATest.php index 573880ed9..7c4a656b8 100644 --- a/tests/unit/framework/UATest.php +++ b/tests/unit/framework/UATest.php @@ -229,6 +229,50 @@ class UATest extends \Codeception\TestCase\Test $this->assertEquals('Yeti', $browser->browser); $this->assertEquals('1.0', $browser->version); $this->assertTrue($browser->is_robot); + + // Baiduspider + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (compatible; Baiduspider/2.0; +http://www.baidu.com/search/spider.html)'); + $this->assertEquals('Baiduspider', $browser->browser); + $this->assertEquals('2.0', $browser->version); + $this->assertTrue($browser->is_robot); + + // wget + $browser = Rhymix\Framework\UA::getBrowserInfo('wget/1.17.1'); + $this->assertEquals('Wget', $browser->browser); + $this->assertEquals('1.17', $browser->version); + $this->assertFalse($browser->is_robot); + + // curl + $browser = Rhymix\Framework\UA::getBrowserInfo('curl/7.47.0'); + $this->assertEquals('Curl', $browser->browser); + $this->assertEquals('7.47', $browser->version); + $this->assertFalse($browser->is_robot); + + // PHP with version + $browser = Rhymix\Framework\UA::getBrowserInfo('PHP/5.2.9'); + $this->assertEquals('PHP', $browser->browser); + $this->assertEquals('5.2', $browser->version); + $this->assertFalse($browser->is_robot); + + // PHP without version + $browser = Rhymix\Framework\UA::getBrowserInfo('PHP'); + $this->assertEquals('PHP', $browser->browser); + $this->assertNull($browser->version); + + // PHP with HTTP_Request2 + $browser = Rhymix\Framework\UA::getBrowserInfo('HTTP_Request2/2.1.1 (http://pear.php.net/package/http_request2) PHP/5.3.2'); + $this->assertEquals('PHP', $browser->browser); + $this->assertEquals('5.3', $browser->version); + + // Some random browser with the 'Mozilla' version + $browser = Rhymix\Framework\UA::getBrowserInfo('Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; XH; rv:8.578.498) fr, Gecko/20121021 Camino/8.723+ (Firefox compatible)'); + $this->assertEquals('Mozilla', $browser->browser); + $this->assertEquals('5.0', $browser->version); + + // Some random browser without the 'Mozilla' version + $browser = Rhymix\Framework\UA::getBrowserInfo('W3C_Validator/1.650'); + $this->assertEquals('W3C_Validator', $browser->browser); + $this->assertEquals('1.650', $browser->version); } public function testEncodeFilenameForDownload()