1<?php
2/**
3 @file       nobadbrowser/action.php
4 @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
5 @author     Luis Machuca Bezzaza <lambalicious [at] tuta [dot] io>
6**/
7// must be run within Dokuwiki
8if(!defined('DOKU_INC')) die();
9if(!defined('DW_LF')) define('DW_LF',"\n");
10
11if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
12define('THIS_PLUGIN', DOKU_PLUGIN.'nobadbrowser/');
13require_once(DOKU_PLUGIN.'action.php');
14require_once(DOKU_INC.'inc/confutils.php');
15require_once(DOKU_INC.'inc/infoutils.php'); // for notify facilities
16
17/**
18 * All DokuWiki plugins to extend the admin function
19 * need to inherit from this class
20 */
21class action_plugin_nobadbrowser extends DokuWiki_Action_Plugin {
22
23	// detected browser info
24	var $binfo = array();
25	// the plugin's data object
26	var $cnf= array();
27
28	function action_plugin_nobadbrowser () {
29		//global $ID;
30		//global $ACT;
31		global $bid;
32		$this->cnf= array();
33		$this->binfo= [];
34	}
35
36	/***
37	 * Register its handlers with the DokuWiki's event controller
38	 */
39	function register (Doku_Event_Handler $controller) {
40		global $ACT;
41		if ($ACT != 'show') return;
42		$controller->register_hook('TPL_CONTENT_DISPLAY','BEFORE', $this, 'warning',array());
43	}
44
45
46/*
47  rendering options (from getConf)
48  notify       -> use the DokuWiki notification message area
49  splash       -> use a JS+CSS splash screen (not available in this release)
50*/
51	function warning (Doku_Event $event, $param) {
52		global $ACT;
53		static $done= false;
54
55		if ($ACT!='show') {
56			return;
57		}
58
59		$bc= extension_loaded('browscap');
60		$with= $this->getConf('with_browscap');
61
62		if ( $bc == false && $this->getConf('debug') == true ) {
63			msg(<<<EOF
64nobadbrowser notice: This PHP installation seems to lack ''browscap'' support. Results may be inconclusive.
65EOF
66, 2);
67		}
68
69		if ( $bc == true && $with == true ) {
70			$this->binfo= get_browser(null, true);
71		} else {
72			$this->binfo= $this->get_browser_local($_SERVER['HTTP_USER_AGENT']);
73			$this->binfo['browscap']= false;
74		}
75		$this->binfo['browscap']= $bc;
76		$this->binfo['with']= $with;
77		$this->binfo['_UA']= $_SERVER['HTTP_USER_AGENT'];
78
79
80		if ($this->getConf['debug'] ?? false) {
81			$this->render_debug($event, $param);
82		}
83
84		if ($this->whybadbrowser()) {
85			$parameter= $this->cnf['parameter'];
86			$reason= $this->cnf['reason'];
87			$see= $this->cnf['see'];
88			$mens = <<<EOF
89You seem to be using a bad browser. \\\\
90You are at risk because __${parameter}__ is: __${reason}__.
91Visit [[${see}|nobadbrowser @ DokuWiki]] for more details.
92EOF;
93			$outp= p_render('xhtml', p_get_instructions($mens), $info);
94			msg(($outp), -1);
95		}
96    // end function
97    }
98
99
100/**
101 *******************************************************************
102 * Helper Functions
103 */
104
105
106	function whybadbrowser () {
107
108		// minimum and maximum acceptable versions, in format MAJOR*100+MINOR
109		$minmaxversions= [
110			'Edge' 	=> [null, null],
111			'Internet Explorer' 	=> [800, 900],
112			'Opera' 	=> [1000, 1264],
113			'Chrome' 	=> [null, null],
114			'Firefox' 	=> [3800, 15000],
115			'SeaMonkey' => [249, 300],
116			'PaleMoon' 	=> [2500, 3000],
117			'Safari' 	=> [null, null]
118			];
119
120		// Match Internet Explorer
121		if (preg_match('/\bIE/', $this->binfo['browser']) && intval($this->binfo['version'])< 10 ) {
122			$this->cnf= array(
123				'parameter' => 'Browser old age',
124				'reason' => $this->binfo['browser'] . " " . $this->binfo['version'] ,
125				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#internet_explorer'
126			);
127			return true;
128		}
129		// ... or browsers made by Microsoft (incl. Edge)
130		if (preg_match('/^Microsoft.*/', $this->binfo['browser_maker'])) {
131			$this->cnf= array(
132				'reason' => $this->binfo['browser_maker'] ,
133				'parameter' => 'Browser Ecosystem',
134				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#internet_explorer'
135			);
136			return true;
137		}
138		// Match Opera post-Presto
139		if (preg_match('/^Opera/', $this->binfo['browser']) && intval($this->binfo['version'])>12 ) {
140			$this->cnf= [
141				'reason' => $this->binfo['browser'] . " " . $this->binfo['version'],
142				'parameter' => 'Browser is a Chrome reskin',
143				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#opera'
144			];
145			return true;
146		}
147		// Match Google Chrome
148		if (preg_match('/\bChrome/', $this->binfo['browser'])) {
149			$this->cnf= array(
150				'reason' => $this->binfo['browser'] ,
151				'parameter' => 'Browser',
152				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#google_chrome'
153			);
154			return true;
155		}
156		// ... or browsers made by Google (incl. Android WebView)
157		if (preg_match('/^Google.*/', $this->binfo['browser_maker'])) {
158			$this->cnf= array(
159				'reason' => $this->binfo['browser_maker'] ,
160				'parameter' => 'Browser Ecosystem',
161				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#google_chrome'
162			);
163			return true;
164		}
165		// ...or Chrome reskins
166		if (preg_match('/\bChrome/', $this->binfo['parent']) ) {
167			$this->cnf= array(
168				'reason' => $this->binfo['parent'] ,
169				'parameter' => 'Browser type',
170				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#google_chrome'
171			);
172			return true;
173		}
174		// ...or Chrome reskins "trying to pass"
175		if (preg_match('/.*Google Inc.*/', $this->binfo['renderingengine_maker']) ) {
176			$this->cnf= array(
177				'reason' => 'Chrome reskin' ,
178				'parameter' => 'Browser type',
179				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#google_chrome'
180			);
181			return true;
182		}
183		// match browsers made by Apple
184		if (preg_match('/^Safari/', $this->binfo['browser'])
185		|| preg_match('/^Apple/', $this->binfo['browser_maker']) ) {
186			$this->cnf= [
187				'reason' => $this->binfo['browser_maker'] ,
188				'parameter' => 'Browser Ecosystem',
189				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#apple'
190			];
191			return true;
192		}
193		// match browsers made by Facebook, for GAFAM completeness
194		if (preg_match('/^Facebook/', $this->binfo['browser_maker']) ) {
195			$this->cnf= [
196				'reason' => $this->binfo['browser_maker'] ,
197				'parameter' => 'Browser Ecosystem',
198				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#facebook'
199			];
200			return true;
201		}
202		// match browsers made by Amazon, for GAFAM completeness
203		if (preg_match('/^Amazon/', $this->binfo['browser_maker']) ) {
204			$this->cnf= [
205				'reason' => $this->binfo['browser_maker'] ,
206				'parameter' => 'Browser Ecosystem',
207				'see' => 'https://www.dokuwiki.org/plugin:nobadbrowser#amazon'
208			];
209			return true;
210		}
211		return false;
212	}
213
214	/**
215	 * This function is an alternative to get_browser when not available.
216	 * It does some parsing of the user agent string to provide acceptable, not necessarily accurate results, focused on returning five primary keys about the browser which are to concern for nobadbrowser :
217	 * * browser
218	 * * version
219	 * * browser_maker
220	 * * renderingengine_name
221	 * * renderingengine_maker
222	 *
223	 * Other keys that can also be of interest:
224	 *
225	 * * renderingengine_version
226	 * * parent
227	 *
228	 * */
229	function get_browser_local ($user_agent, $return_array=true) {
230		$ret= [ 'browser' => 'generic', 'browser_maker' => 'generic', 'parent' => '', 'version' => '0' ];
231		// Internet Explorer
232		if (strpos($user_agent, 'MSIE') !== false) {
233			$ret['browser']= 'IE';
234			$ret['browser_maker']= 'Microsoft Corporation';
235			if (preg_match ('#MSIE ([0-9\.]+);#', $user_agent, $browserversion) ) {
236				$ret['version']= $browserversion[1];
237				$ver= explode('.', $browserversion[1], 3);
238				list($ret['majorver'], $ret['minorver'])= $ver;
239				$ret['parent']= $ret['browser']. ' ' . $ret['majorver'];
240			}
241			$ret['renderingengine_name']= 'Trident';
242			$ret['renderingengine_maker']= 'Microsoft Corporation';
243			if (preg_match ('#Trident/([0-9\.]+)#', $user_agent, $rendererversion) ) {
244				$ret['renderingengine_version']= $rendererversion[1];
245			}
246		}
247		// Google Chrome and its various derivations
248		if (preg_match_all(
249		'#AppleWebKit/[0-9\.]+.*\(.*KHTML.*like.*Gecko.*\) Chrome/([0-9]+\.[0-9]+).+Safari/[0-9\.]+\b(.*)$#'
250		, $user_agent, $chrome_info) ) {
251			$ret['browser']= 'Chrome';
252			$ret['browser_maker']= 'Google Inc';
253			$ret['version']= $chrome_info[1][0];
254			$ret['parent']= 'Chrome';
255			$variants= $chrome_info[2];
256			//msg( print_r($variants, true), 2);
257			if (count($variants) > 0 && (isset($variants[0]) ) && ($variants[0] != '') ) {
258				//$ret['variant']= 1;
259				$variant= explode('/', $variants[0]);
260				$variantname= trim($variant[0]);
261				$variantmaker= $ret['browser_maker'];
262				if (false) {
263				} else if ($variantname=='Edg') {
264					$variantname= 'Edge';
265				} else if ($variantname=='OPR') {
266					$variantname= 'Opera'; $variantmaker= 'Opera Software ASA';
267				} else if ($variantname=='uacq') {
268					$variantname= 'User Agent Switcher';
269				}
270				$variantversion= explode('.', $variant[1],3);
271				$variantversion= $variantversion[0].'.'.$variantversion[1];
272				$ret['variant_found']= [ $variantname, $variantversion];
273				$ret['browser']= $variantname;
274				$ret['version']= $variantversion;
275				$ret['browser_maker']= $variantmaker;
276			}
277			//list($ret['majorver'], $ret['minorver'])= explode('.', $chrome_info[1],3);
278			$ret['renderingengine_name']= 'Blink';
279			$ret['renderingengine_maker']= 'Google Inc';
280			$ret['renderingengine_version']= '';
281		}
282		// Safari
283		if (preg_match_all(
284		'#AppleWebKit/[0-9\.]+.*\(.*KHTML.*like.*Gecko.*\) Version/([0-9]+\.[0-9]+).*Safari/([0-9]+\.[0-9]+).*#'
285		, $user_agent, $safari_info) ) {
286			$ret['browser']= 'Safari';
287			$ret['browser_maker']= 'Apple Inc';
288			$ret['version']= $safari_info[1][0];
289			$ret['renderingengine_name']= 'WebKit';
290			$ret['renderingengine_maker']= 'Apple Inc';
291			$ret['renderingengine_version']= '';
292		}
293		// TODO: Facebook Browser
294		if (preg_match_all(
295		'#AppleWebKit/[0-9\.]+.*\(.*KHTML.*like.*Gecko.*\) Version/([0-9]+\.[0-9]+).*Safari/([0-9]+\.[0-9]+).*(FBAV|FBIOS).*#'
296		, $user_agent, $facebook_info) ) {
297			$ret['browser']= 'Facebook';
298			$ret['browser_maker']= 'Facebook';
299			$ret['version']= $facebook_info[1][0];
300			$ret['renderingengine_name']= 'WebKit';
301			$ret['renderingengine_maker']= 'Apple Inc';
302			$ret['renderingengine_version']= '';
303		}
304		// Firefox and its variants
305		if (preg_match_all(
306		'#^Mozilla/[0-9\.]+.*Gecko/[0-9]+.+Firefox/([0-9]+\.[0-9]+).*$#'
307		, $user_agent, $firefox_info) ) {
308			$ret['browser']= 'Firefox';
309			$ret['browser_maker']= 'Mozilla Foundation';
310			$ret['version']= $firefox_info[1][0];
311			$rendererversion= $ret['version'];
312			$ret['renderingengine_name']= 'Gecko';
313			$ret['renderingengine_maker']= 'Mozilla Foundation';
314			$ret['renderingengine_version']= $rendererversion;
315		}
316		return $ret;
317	}
318
319	function render_debug (Doku_Event $event, $param) {
320		$x= print_r($this->binfo, true);
321		msg($x,3);
322	}
323
324}
325