From ff0e78e672dd62ad3b12931dab9fc5c90326c1b3 Mon Sep 17 00:00:00 2001 From: Paul Nicholls Date: Mon, 28 Apr 2014 09:29:20 +1200 Subject: [PATCH 1/4] MDL-43679 core_useragent: add basic MS Word detection Basic detection for MS Word's internal link checker. This is not perfect, as older versions use IE6/7 user agent string with no identifying marks, and newer versions don't necessarily identify themselves as Word (just as Office). Version information is not reliably available, even when there is sufficient other information to identify it as Word's link checker. This is also based on a small sample of Word / OS version pairings, so there may be some which are identifiable but not currently covered. --- lib/classes/useragent.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/classes/useragent.php b/lib/classes/useragent.php index a2c4dbafb44..212a41493fb 100644 --- a/lib/classes/useragent.php +++ b/lib/classes/useragent.php @@ -808,6 +808,24 @@ class core_useragent { return false; } + /** + * Checks if the user agent is MS Word. + * Not perfect, as older versions of Word use standard IE6/7 user agents without any identifying traits. + * + * @return bool true if user agent could be identified as MS Word. + */ + public static function is_msword() { + $useragent = self::get_user_agent_string(); + if (!preg_match('/(\bWord\b|ms-office|MSOffice|Microsoft Office)/i', $useragent)) { + return false; + } + if (strpos($useragent, 'Outlook') !== false) { + return false; + } + // It's Office and not Outlook - so it's probably Word, but we can't really be sure in most cases. + return true; + } + /** * Check if the user agent matches a given brand. * From 21ece8eebce92f18d9073cefe614c72c42a942f6 Mon Sep 17 00:00:00 2001 From: Paul Nicholls Date: Mon, 28 Apr 2014 11:09:43 +1200 Subject: [PATCH 2/4] MDL-43679 core_useragent: add test cases for MS Word The new is_msword() check should always return false for regular browsers. It should return true for known MS Word, but false for known MS Outlook. --- lib/tests/useragent_test.php | 45 ++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/lib/tests/useragent_test.php b/lib/tests/useragent_test.php index 530f1d90061..2c3e6e6bcb7 100644 --- a/lib/tests/useragent_test.php +++ b/lib/tests/useragent_test.php @@ -156,6 +156,17 @@ class core_useragent_testcase extends basic_testcase { 'Windows XP' => 'Opera/9.0 (Windows NT 5.1; U; en)', 'Debian Linux' => 'Opera/9.01 (X11; Linux i686; U; en)' ) + ), + 'MS Word' => array( + '2010' => array( + 'Windows 7' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; ms-office)', + 'Mac OS X' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X) Word/14.38.0' + ) + ), + 'MS Outlook' => array( + '2010' => array( + 'Windows 7' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; Microsoft Outlook 14.0.7113; ms-office; MSOffice 14)' + ) ) ); @@ -183,6 +194,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_chrome_version()); $this->assertFalse(core_useragent::is_safari_ios()); $this->assertFalse(core_useragent::check_safari_ios_version()); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Safari iOS']['528']['iPhone']); $this->assertTrue(core_useragent::is_safari_ios()); @@ -195,6 +207,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_safari_version('500')); $this->assertFalse(core_useragent::is_chrome()); $this->assertFalse(core_useragent::check_chrome_version()); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['WebKit Android']['530']['Nexus']); $this->assertTrue(core_useragent::is_webkit()); @@ -205,6 +218,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_safari_version()); $this->assertFalse(core_useragent::is_chrome()); $this->assertFalse(core_useragent::check_chrome_version()); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['WebKit Android']['537']['Samsung GT-9505']); $this->assertTrue(core_useragent::is_webkit()); @@ -234,12 +248,14 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_chrome_version(8)); $this->assertFalse(core_useragent::check_chrome_version(10)); $this->assertFalse(core_useragent::check_safari_version('1')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Opera']['9.0']['Windows XP']); $this->assertTrue(core_useragent::is_opera()); $this->assertTrue(core_useragent::check_opera_version()); $this->assertTrue(core_useragent::check_opera_version('8.0')); $this->assertFalse(core_useragent::check_opera_version('10.0')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['6.0']['Windows XP SP2']); $this->assertTrue(core_useragent::is_ie()); @@ -247,6 +263,7 @@ class core_useragent_testcase extends basic_testcase { $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')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['5.0']['Windows 98']); $this->assertFalse(core_useragent::is_ie()); @@ -255,6 +272,7 @@ class core_useragent_testcase extends basic_testcase { $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')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['9.0']['Windows 7']); $this->assertTrue(core_useragent::is_ie()); @@ -264,6 +282,7 @@ class core_useragent_testcase extends basic_testcase { $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')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['9.0i']['Windows 7']); $this->assertTrue(core_useragent::is_ie()); @@ -273,6 +292,7 @@ class core_useragent_testcase extends basic_testcase { $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')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['10.0']['Windows 8']); $this->assertTrue(core_useragent::is_ie()); @@ -283,6 +303,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('10')); $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('11')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['10.0i']['Windows 8']); $this->assertTrue(core_useragent::is_ie()); @@ -293,6 +314,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('10')); $this->assertTrue(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('11')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['11.0']['Windows 8.1']); $this->assertTrue(core_useragent::is_ie()); @@ -304,6 +326,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('11')); $this->assertFalse(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('12')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['MSIE']['11.0i']['Windows 8.1']); $this->assertTrue(core_useragent::is_ie()); @@ -315,6 +338,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_ie_version('11')); $this->assertTrue(core_useragent::check_ie_compatibility_view()); $this->assertFalse(core_useragent::check_ie_version('12')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['2.0']['Windows XP']); $this->assertTrue(core_useragent::is_firefox()); @@ -325,6 +349,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_gecko_version(20030516)); $this->assertTrue(core_useragent::check_gecko_version(20051106)); $this->assertTrue(core_useragent::check_gecko_version(2006010100)); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['1.0.6']['Windows XP']); $this->assertTrue(core_useragent::is_firefox()); @@ -336,6 +361,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_firefox_version('1.5')); $this->assertFalse(core_useragent::check_firefox_version('3.0')); $this->assertFalse(core_useragent::check_gecko_version('2')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['2.0']['Windows XP']); $this->assertTrue(core_useragent::is_firefox()); @@ -347,6 +373,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_gecko_version(20051106)); $this->assertTrue(core_useragent::check_gecko_version(2006010100)); $this->assertFalse(core_useragent::check_firefox_version('3.0')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['3.6']['Linux']); $this->assertTrue(core_useragent::is_firefox()); @@ -360,6 +387,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_gecko_version(2006010100)); $this->assertFalse(core_useragent::check_firefox_version('4')); $this->assertFalse(core_useragent::check_firefox_version('10')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['3.6']['Linux']); $this->assertTrue(core_useragent::is_firefox()); @@ -375,6 +403,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_firefox_version('10')); $this->assertFalse(core_useragent::check_firefox_version('18')); $this->assertFalse(core_useragent::check_gecko_version('4')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['15.0a2']['Windows']); $this->assertTrue(core_useragent::is_firefox()); @@ -392,6 +421,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_firefox_version('15')); $this->assertFalse(core_useragent::check_firefox_version('18')); $this->assertFalse(core_useragent::check_gecko_version('18')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['Firefox']['18.0']['Mac OS X']); $this->assertTrue(core_useragent::is_firefox()); @@ -411,6 +441,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_firefox_version('18')); $this->assertFalse(core_useragent::check_firefox_version('19')); $this->assertFalse(core_useragent::check_gecko_version('19')); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['SeaMonkey']['2.0']['Windows']); $this->assertTrue(core_useragent::check_gecko_version('2')); @@ -421,6 +452,7 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_gecko_version('4.0')); $this->assertFalse(core_useragent::is_firefox()); $this->assertFalse(core_useragent::check_firefox_version()); + $this->assertFalse(core_useragent::is_msword()); core_useragent::instance(true, $this->user_agents['SeaMonkey']['2.1']['Linux']); $this->assertTrue(core_useragent::check_gecko_version('2')); @@ -434,7 +466,18 @@ class core_useragent_testcase extends basic_testcase { $this->assertTrue(core_useragent::check_firefox_version(4.0)); $this->assertFalse(core_useragent::check_firefox_version(5)); $this->assertFalse(core_useragent::check_gecko_version('18.0')); + $this->assertFalse(core_useragent::is_msword()); + // We're not really interested in what MS Word gets identified as beyond MS Word itself. + core_useragent::instance(true, $this->user_agents['MS Word']['2010']['Windows 7']); + $this->assertTrue(core_useragent::is_msword()); + + core_useragent::instance(true, $this->user_agents['MS Word']['2010']['Mac OS X']); + $this->assertTrue(core_useragent::is_msword()); + + // Outlook should not be considered to be MS Word. + core_useragent::instance(true, $this->user_agents['MS Outlook']['2010']['Windows 7']); + $this->assertFalse(core_useragent::is_msword()); } /** @@ -442,8 +485,6 @@ class core_useragent_testcase extends basic_testcase { * works as expected. */ public function test_supports_svg() { - $this->assertTrue(core_useragent::supports_svg()); - // MSIE 5.0 is not considered a browser at all: known false positive. core_useragent::instance(true, $this->user_agents['MSIE']['5.0']['Windows 98']); $this->assertTrue(core_useragent::supports_svg()); From f63dec4bb2a6148ae0ea56e98af970aa2ed67505 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Thu, 18 Dec 2014 16:32:52 +0800 Subject: [PATCH 3/4] MDL-43679 core: Never use HTTP redirect when the user agent is MS Word --- lib/weblib.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/weblib.php b/lib/weblib.php index c843fa2e2b3..8fb28a643a0 100644 --- a/lib/weblib.php +++ b/lib/weblib.php @@ -2551,6 +2551,16 @@ function redirect($url, $message='', $delay=-1) { break; } + if (core_useragent::is_msword()) { + // Clicking a URL from MS Word sends a request to the server without cookies. If that + // causes a redirect Word will open a browser pointing the new URL. If not, the URL that + // was clicked is opened. Because the request from Word is without cookies, it almost + // always results in a redirect to the login page, even if the user is logged in in their + // browser. This is not what we want, so prevent the redirect for requests from Word. + $debugdisableredirect = true; + break; + } + if (empty($CFG->debugdisplay) or empty($CFG->debug)) { // No errors should be displayed. break; From d2fdad7c6c3d8c75b741618eb890710f63efe757 Mon Sep 17 00:00:00 2001 From: Frederic Massart Date: Fri, 2 Jan 2015 11:22:47 +0800 Subject: [PATCH 4/4] MDL-43679 core: Handle Meridio user agents with MS Word --- lib/classes/useragent.php | 7 ++++--- lib/tests/useragent_test.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/lib/classes/useragent.php b/lib/classes/useragent.php index 212a41493fb..2817f1ae31d 100644 --- a/lib/classes/useragent.php +++ b/lib/classes/useragent.php @@ -818,11 +818,12 @@ class core_useragent { $useragent = self::get_user_agent_string(); if (!preg_match('/(\bWord\b|ms-office|MSOffice|Microsoft Office)/i', $useragent)) { return false; - } - if (strpos($useragent, 'Outlook') !== false) { + } else if (strpos($useragent, 'Outlook') !== false) { + return false; + } else if (strpos($useragent, 'Meridio') !== false) { return false; } - // It's Office and not Outlook - so it's probably Word, but we can't really be sure in most cases. + // It's Office, not Outlook and not Meridio - so it's probably Word, but we can't really be sure in most cases. return true; } diff --git a/lib/tests/useragent_test.php b/lib/tests/useragent_test.php index 2c3e6e6bcb7..770012d3d97 100644 --- a/lib/tests/useragent_test.php +++ b/lib/tests/useragent_test.php @@ -49,6 +49,9 @@ class core_useragent_testcase extends basic_testcase { '7.0' => array( 'Windows XP SP2' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; YPC 3.0.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)' ), + '7.0b' => array( + 'Windows XP' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Meridio for Excel 5.0.251; Meridio for PowerPoint 5.0.251; Meridio for Word 5.0.251; Meridio Protocol; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)' + ), '8.0' => array( 'Windows Vista' => 'Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; .NET CLR 2.0.50727; .NET CLR 1.1.4322; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648)' ), @@ -167,6 +170,11 @@ class core_useragent_testcase extends basic_testcase { '2010' => array( 'Windows 7' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.3; .NET4.0C; .NET4.0E; Microsoft Outlook 14.0.7113; ms-office; MSOffice 14)' ) + ), + 'Meridio' => array( + '5.0.251' => array( + 'Windows XP' => 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Meridio for Excel 5.0.251; Meridio for PowerPoint 5.0.251; Meridio for Word 5.0.251; Meridio Protocol; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.04506.648; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)' + ) ) ); @@ -274,6 +282,22 @@ class core_useragent_testcase extends basic_testcase { $this->assertFalse(core_useragent::check_ie_version('7.0')); $this->assertFalse(core_useragent::is_msword()); + core_useragent::instance(true, $this->user_agents['MSIE']['7.0']['Windows XP SP2']); + $this->assertTrue(core_useragent::is_ie()); + $this->assertTrue(core_useragent::check_ie_version()); + $this->assertTrue(core_useragent::check_ie_version('7.0')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); + $this->assertFalse(core_useragent::check_ie_version('8.0')); + $this->assertFalse(core_useragent::is_msword()); + + core_useragent::instance(true, $this->user_agents['MSIE']['7.0b']['Windows XP']); + $this->assertTrue(core_useragent::is_ie()); + $this->assertTrue(core_useragent::check_ie_version()); + $this->assertTrue(core_useragent::check_ie_version('7.0')); + $this->assertFalse(core_useragent::check_ie_compatibility_view()); + $this->assertFalse(core_useragent::check_ie_version('8.0')); + $this->assertFalse(core_useragent::is_msword()); + core_useragent::instance(true, $this->user_agents['MSIE']['9.0']['Windows 7']); $this->assertTrue(core_useragent::is_ie()); $this->assertTrue(core_useragent::check_ie_version()); @@ -478,6 +502,10 @@ class core_useragent_testcase extends basic_testcase { // Outlook should not be considered to be MS Word. core_useragent::instance(true, $this->user_agents['MS Outlook']['2010']['Windows 7']); $this->assertFalse(core_useragent::is_msword()); + + // Meridio should not be considered to be MS Word. + core_useragent::instance(true, $this->user_agents['Meridio']['5.0.251']['Windows XP']); + $this->assertFalse(core_useragent::is_msword()); } /**