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