16980370bSSascha Leib<?php 26980370bSSascha Leib 36980370bSSascha Leibuse dokuwiki\Extension\EventHandler; 46980370bSSascha Leibuse dokuwiki\Extension\Event; 55fbe88f7SSascha Leibuse dokuwiki\Logger; 66980370bSSascha Leib 76980370bSSascha Leib/** 87bd08c30SSascha Leib * Action Component for the Bot Monitoring Plugin 96980370bSSascha Leib * 106980370bSSascha Leib * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 116980370bSSascha Leib * @author Sascha Leib <sascha.leib(at)kolmio.com> 126980370bSSascha Leib */ 136980370bSSascha Leib 147bd08c30SSascha Leibclass action_plugin_botmon extends DokuWiki_Action_Plugin { 156980370bSSascha Leib 166980370bSSascha Leib /** 176980370bSSascha Leib * Registers a callback functions 186980370bSSascha Leib * 196980370bSSascha Leib * @param EventHandler $controller DokuWiki's event controller object 206980370bSSascha Leib * @return void 216980370bSSascha Leib */ 226980370bSSascha Leib public function register(EventHandler $controller) { 236980370bSSascha Leib $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); 246980370bSSascha Leib } 256980370bSSascha Leib 26*b148c85eSSascha Leib /* session information */ 27*b148c85eSSascha Leib private $sessionId = 'unset'; 28*b148c85eSSascha Leib private $sessionType = ''; 29*b148c85eSSascha Leib 306980370bSSascha Leib /** 316980370bSSascha Leib * Inserts tracking code to the page header 326980370bSSascha Leib * 336980370bSSascha Leib * @param Event $event event object by reference 346980370bSSascha Leib * @return void 356980370bSSascha Leib */ 366980370bSSascha Leib public function insertHeader(Event $event, $param) { 376980370bSSascha Leib 386980370bSSascha Leib global $INFO; 396980370bSSascha Leib 40*b148c85eSSascha Leib // populate the session id and type: 41*b148c85eSSascha Leib $this->getSessionInfo(); 42*b148c85eSSascha Leib 436980370bSSascha Leib // is there a user logged in? 446980370bSSascha Leib $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) 4513c9d29dSSascha Leib ? $INFO['userinfo']['name'] : ''); 466980370bSSascha Leib 476980370bSSascha Leib // build the tracker code: 48*b148c85eSSascha Leib $code = NL . DOKU_TAB . "document._botmon = {'t0': Date.now(), 'session': '" . json_encode($this->sessionId) . "'};" . NL; 496980370bSSascha Leib if ($username) { 507bd08c30SSascha Leib $code .= DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; 516980370bSSascha Leib } 526980370bSSascha Leib $code .= DOKU_TAB . "addEventListener('load',function(){" . NL; 536980370bSSascha Leib 546980370bSSascha Leib $code .= DOKU_TAB . DOKU_TAB . "const e=document.createElement('script');" . NL; 556980370bSSascha Leib $code .= DOKU_TAB . DOKU_TAB . "e.async=true;e.defer=true;" . NL; 567bd08c30SSascha Leib $code .= DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; 576980370bSSascha Leib $code .= DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; 581c16f1b7SSascha Leib $code .= DOKU_TAB . "});" . NL . DOKU_TAB; 596980370bSSascha Leib 606980370bSSascha Leib $event->data['script'][] = [ 616980370bSSascha Leib '_data' => $code 626980370bSSascha Leib ]; 63cf9f7fe8SSascha Leib 644cddc661SSascha Leib /* Write out server-side info to a server log: */ 65451abfadSSascha Leib $this->writeServerLog($username); 66451abfadSSascha Leib } 67451abfadSSascha Leib 68451abfadSSascha Leib /** 69451abfadSSascha Leib * Writes data to the server log. 70451abfadSSascha Leib * 71451abfadSSascha Leib * @return void 72451abfadSSascha Leib */ 73451abfadSSascha Leib private function writeServerLog($username) { 74451abfadSSascha Leib 75451abfadSSascha Leib global $conf; 76451abfadSSascha Leib global $INFO; 77091b5998SSascha Leib 78b2e3bd8bSSascha Leib // clean the page ID 79b2e3bd8bSSascha Leib $pageId = preg_replace('/[\x00-\x1F]/', "\u{FFFD}", $INFO['id'] ?? ''); 80b2e3bd8bSSascha Leib 815fbe88f7SSascha Leib // collect GeoIP information (if available): 82*b148c85eSSascha Leib $geoIp = ( $this->sessionId == 'localhost' ? 'local' : 'ZZ' ); /* User-defined code for unknown country */ 835fbe88f7SSascha Leib try { 845fbe88f7SSascha Leib if (extension_loaded('geoip') && geoip_db_avail(GEOIP_COUNTRY_EDITION)) { 855fbe88f7SSascha Leib $geoIp = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']); 865fbe88f7SSascha Leib } else { 875fbe88f7SSascha Leib Logger::debug('BotMon Plugin: GeoIP module not available'); 885fbe88f7SSascha Leib } 895fbe88f7SSascha Leib } catch (Exception $e) { 905fbe88f7SSascha Leib Logger::error('BotMon Plugin: GeoIP Error', $e->getMessage()); 915fbe88f7SSascha Leib } 925fbe88f7SSascha Leib 93451abfadSSascha Leib // create the log array: 94cf9f7fe8SSascha Leib $logArr = Array( 9587d76396SSascha Leib $_SERVER['REMOTE_ADDR'] ?? '', /* remote IP */ 96b2e3bd8bSSascha Leib $pageId, /* page ID */ 97*b148c85eSSascha Leib $this->sessionId, /* Session ID */ 98*b148c85eSSascha Leib $this->sessionType, /* session ID type */ 99cf9f7fe8SSascha Leib $username, 1002f2bc93aSSascha Leib $_SERVER['HTTP_USER_AGENT'] ?? '', /* User agent */ 101451abfadSSascha Leib $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ 102451abfadSSascha Leib substr($conf['lang'],0,2), /* page language */ 1035fbe88f7SSascha Leib implode(',', array_unique(array_map( function($it) { return substr($it,0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ 1045fbe88f7SSascha Leib $geoIp /* GeoIP country code */ 105cf9f7fe8SSascha Leib ); 106cf9f7fe8SSascha Leib 107cf9f7fe8SSascha Leib //* create the log line */ 1084cddc661SSascha Leib $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ 109cf9f7fe8SSascha Leib $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ 110cf9f7fe8SSascha Leib foreach ($logArr as $tab) { 111cf9f7fe8SSascha Leib $logline .= "\t" . $tab; 112cf9f7fe8SSascha Leib }; 113cf9f7fe8SSascha Leib 114cf9f7fe8SSascha Leib /* write the log line to the file */ 115cf9f7fe8SSascha Leib $logfile = fopen($filename, 'a'); 116cf9f7fe8SSascha Leib if (!$logfile) die(); 117cf9f7fe8SSascha Leib if (fwrite($logfile, $logline . "\n") === false) { 118cf9f7fe8SSascha Leib fclose($logfile); 119cf9f7fe8SSascha Leib die(); 1206980370bSSascha Leib } 121cf9f7fe8SSascha Leib 122cf9f7fe8SSascha Leib /* Done */ 123cf9f7fe8SSascha Leib fclose($logfile); 124cf9f7fe8SSascha Leib } 125*b148c85eSSascha Leib 126*b148c85eSSascha Leib private function getSessionInfo() { 127*b148c85eSSascha Leib 128*b148c85eSSascha Leib // what is the session identifier? 129*b148c85eSSascha Leib if (isset($_SESSION)) { 130*b148c85eSSascha Leib $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ 131*b148c85eSSascha Leib foreach ($sesKeys as $key) { 132*b148c85eSSascha Leib if (substr($key, 0, 2) == 'DW') { 133*b148c85eSSascha Leib $this->sessionId = $key; 134*b148c85eSSascha Leib $this->sessionType = 'dw'; 135*b148c85eSSascha Leib return; 136*b148c85eSSascha Leib } 137*b148c85eSSascha Leib } 138*b148c85eSSascha Leib } 139*b148c85eSSascha Leib if ($this->sessionId == '') { /* no DokuWiki Session ID, try PHP session ID */ 140*b148c85eSSascha Leib $this->sessionId = session_id(); 141*b148c85eSSascha Leib $this->sessionType = 'php'; 142*b148c85eSSascha Leib } 143*b148c85eSSascha Leib if ($this->sessionId == '') { /* no PHP session ID, try IP address */ 144*b148c85eSSascha Leib $this->sessionId = $_SERVER['REMOTE_ADDR'] ?? ''; 145*b148c85eSSascha Leib $this->sessionType = 'ip'; 146*b148c85eSSascha Leib } 147*b148c85eSSascha Leib if ($this->sessionId == '') { /* if everything else fails, just us a random ID */ 148*b148c85eSSascha Leib $this->sessionId = rand(1000000, 9999999); 149*b148c85eSSascha Leib $this->sessionType = 'rand'; 150*b148c85eSSascha Leib } 151*b148c85eSSascha Leib } 1526980370bSSascha Leib}