1<?php
2namespace TYPO3\CMS\Core\Utility;
3
4/*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17/**
18 * Class to handle and determine browser specific information.
19 */
20class ClientUtility
21{
22    /**
23     * Generates an array with abstracted browser information
24     *
25     * @param string $userAgent The useragent string, \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_USER_AGENT')
26     * @return array Contains keys "browser", "version", "system
27     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
28     */
29    public static function getBrowserInfo($userAgent)
30    {
31        trigger_error('ClientUtility::getBrowserInfo() will be removed with TYPO3 v10.0.', E_USER_DEPRECATED);
32        // Hook: $TYPO3_CONF_VARS['SC_OPTIONS']['t3lib/div/class.t3lib_utility_client.php']['getBrowserInfo']:
33        foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/div/class.t3lib_utility_client.php']['getBrowserInfo'] ?? [] as $hookFunction) {
34            $returnResult = true;
35            $hookParameters = [
36                'userAgent' => &$userAgent,
37                'returnResult' => &$returnResult
38            ];
39            // need reference for third parameter in \TYPO3\CMS\Core\Utility\GeneralUtility::callUserFunction,
40            // so create a reference to NULL
41            $null = null;
42            $hookResult = GeneralUtility::callUserFunction($hookFunction, $hookParameters, $null);
43            if ($returnResult && is_array($hookResult) && !empty($hookResult)) {
44                return $hookResult;
45            }
46        }
47        $userAgent = trim($userAgent);
48        $browserInfo = [
49            'useragent' => $userAgent
50        ];
51        // Analyze the userAgent string
52        // Declare known browsers to look for
53        $known = [
54            'msie',
55            'firefox',
56            'webkit',
57            'opera',
58            'netscape',
59            'konqueror',
60            'gecko',
61            'chrome',
62            'safari',
63            'seamonkey',
64            'navigator',
65            'mosaic',
66            'lynx',
67            'amaya',
68            'omniweb',
69            'avant',
70            'camino',
71            'flock',
72            'aol'
73        ];
74        $matches = [];
75        $pattern = '#(?P<browser>' . implode('|', $known) . ')[/ ]+(?P<version>[0-9]+(?:\\.[0-9]+)?)#';
76        // Find all phrases (or return empty array if none found)
77        if (!preg_match_all($pattern, strtolower($userAgent), $matches)) {
78            // Microsoft Internet-Explorer 11 does not have a sign of "MSIE" or so in the useragent.
79            // All checks from the pattern above fail here. Look for a special combination of
80            // "Mozilla/5.0" in front, "Trident/7.0" in the middle and "like Gecko" at the end.
81            // The version (revision) is given as "; rv:11.0" in the useragent then.
82            unset($matches);
83            $pattern = '#mozilla/5\\.0 \\(.*trident/7\\.0.*; rv:(?P<version>[0-9]+(?:\\.[0-9]+)?)\\) like gecko#i';
84            if (preg_match_all($pattern, $userAgent, $matches)) {
85                $browserInfo['browser'] = 'msie';
86                $browserInfo['version'] = $matches['version'][0];
87                $browserInfo['all'] = ['msie' => $matches['version'][0]];
88            } else {
89                $browserInfo['browser'] = 'unknown';
90                $browserInfo['version'] = '';
91                $browserInfo['all'] = [];
92            }
93        } else {
94            // Since some UAs have more than one phrase (e.g Firefox has a Gecko phrase,
95            // Opera 7,8 have a MSIE phrase), use the last one found (the right-most one
96            // in the UA).  That's usually the most correct.
97            // For IE use the first match as IE sends multiple MSIE with version, from higher to lower.
98            $lastIndex = count($matches['browser']) - 1;
99            $browserInfo['browser'] = $matches['browser'][$lastIndex];
100            $browserInfo['version'] = $browserInfo['browser'] === 'msie' ? $matches['version'][0] : $matches['version'][$lastIndex];
101            // But return all parsed browsers / version in an extra array
102            $browserInfo['all'] = [];
103            for ($i = 0; $i <= $lastIndex; $i++) {
104                if (!isset($browserInfo['all'][$matches['browser'][$i]])) {
105                    $browserInfo['all'][$matches['browser'][$i]] = $matches['version'][$i];
106                }
107            }
108            // Replace gecko build date with version given by rv
109            if (isset($browserInfo['all']['gecko'])) {
110                preg_match_all('/rv:([0-9\\.]*)/', strtolower($userAgent), $version);
111                if ($version[1][0]) {
112                    $browserInfo['all']['gecko'] = $version[1][0];
113                }
114            }
115        }
116        $browserInfo['all_systems'] = [];
117        if (strstr($userAgent, 'Win')) {
118            // Windows
119            if (strstr($userAgent, 'Windows NT 6.2') || strstr($userAgent, 'Windows NT 6.3')) {
120                $browserInfo['all_systems'][] = 'win8';
121                $browserInfo['all_systems'][] = 'winNT';
122            } elseif (strstr($userAgent, 'Windows NT 6.1')) {
123                $browserInfo['all_systems'][] = 'win7';
124                $browserInfo['all_systems'][] = 'winNT';
125            } elseif (strstr($userAgent, 'Windows NT 6.0')) {
126                $browserInfo['all_systems'][] = 'winVista';
127                $browserInfo['all_systems'][] = 'winNT';
128            } elseif (strstr($userAgent, 'Windows NT 5.1')) {
129                $browserInfo['all_systems'][] = 'winXP';
130                $browserInfo['all_systems'][] = 'winNT';
131            } elseif (strstr($userAgent, 'Windows NT 5.0')) {
132                $browserInfo['all_systems'][] = 'win2k';
133                $browserInfo['all_systems'][] = 'winNT';
134            } elseif (strstr($userAgent, 'Win98') || strstr($userAgent, 'Windows 98')) {
135                $browserInfo['all_systems'][] = 'win98';
136            } elseif (strstr($userAgent, 'Win95') || strstr($userAgent, 'Windows 95')) {
137                $browserInfo['all_systems'][] = 'win95';
138            } elseif (strstr($userAgent, 'WinNT') || strstr($userAgent, 'Windows NT')) {
139                $browserInfo['all_systems'][] = 'winNT';
140            } elseif (strstr($userAgent, 'Win16') || strstr($userAgent, 'Windows 311')) {
141                $browserInfo['all_systems'][] = 'win311';
142            }
143        } elseif (strstr($userAgent, 'Mac')) {
144            if (strstr($userAgent, 'iPad') || strstr($userAgent, 'iPhone') || strstr($userAgent, 'iPod')) {
145                $browserInfo['all_systems'][] = 'iOS';
146                $browserInfo['all_systems'][] = 'mac';
147            } else {
148                $browserInfo['all_systems'][] = 'mac';
149            }
150        } elseif (strstr($userAgent, 'Android')) {
151            $browserInfo['all_systems'][] = 'android';
152            $browserInfo['all_systems'][] = 'linux';
153        } elseif (strstr($userAgent, 'Linux')) {
154            $browserInfo['all_systems'][] = 'linux';
155        } elseif (strstr($userAgent, 'BSD')) {
156            $browserInfo['all_systems'][] = 'unix_bsd';
157        } elseif (strstr($userAgent, 'SGI') && strstr($userAgent, ' IRIX ')) {
158            $browserInfo['all_systems'][] = 'unix_sgi';
159        } elseif (strstr($userAgent, ' SunOS ')) {
160            $browserInfo['all_systems'][] = 'unix_sun';
161        } elseif (strstr($userAgent, ' HP-UX ')) {
162            $browserInfo['all_systems'][] = 'unix_hp';
163        } elseif (strstr($userAgent, 'CrOS')) {
164            $browserInfo['all_systems'][] = 'chrome';
165            $browserInfo['all_systems'][] = 'linux';
166        }
167        return $browserInfo;
168    }
169
170    /**
171     * Returns the version of a browser; Basically getting float value of the input string,
172     * stripping of any non-numeric values in the beginning of the string first.
173     *
174     * @param string $version A string with version number, eg. '/7.32 some text'
175     * @return float Returns double value, eg. 7.32
176     * @deprecated since TYPO3 v9, will be removed in TYPO3 v10.0.
177     */
178    public static function getVersion($version)
179    {
180        trigger_error('ClientUtility::getVersion() will be removed with TYPO3 v10.0.', E_USER_DEPRECATED);
181        return (float)preg_replace('/^[^0-9]*/', '', $version);
182    }
183}
184