1<?php 2 3use dokuwiki\Extension\EventHandler; 4use dokuwiki\Extension\Event; 5use dokuwiki\Logger; 6 7/** 8 * Action Component for the Bot Monitoring Plugin 9 * 10 * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 11 * @author Sascha Leib <sascha.leib(at)kolmio.com> 12 */ 13 14class action_plugin_botmon extends DokuWiki_Action_Plugin { 15 16 /** 17 * Registers a callback functions 18 * 19 * @param EventHandler $controller DokuWiki's event controller object 20 * @return void 21 */ 22 public function register(EventHandler $controller) { 23 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); 24 } 25 26 /* session information */ 27 private $sessionId = 'unset'; 28 private $sessionType = ''; 29 30 /** 31 * Inserts tracking code to the page header 32 * 33 * @param Event $event event object by reference 34 * @return void 35 */ 36 public function insertHeader(Event $event, $param) { 37 38 global $INFO; 39 40 // populate the session id and type: 41 $this->getSessionInfo(); 42 43 // is there a user logged in? 44 $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) 45 ? $INFO['userinfo']['name'] : ''); 46 47 // build the tracker code: 48 $code = NL . DOKU_TAB . "document._botmon = {'t0': Date.now(), 'session': '" . json_encode($this->sessionId) . "'};" . NL; 49 if ($username) { 50 $code .= DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; 51 } 52 $code .= DOKU_TAB . "addEventListener('load',function(){" . NL; 53 54 $code .= DOKU_TAB . DOKU_TAB . "const e=document.createElement('script');" . NL; 55 $code .= DOKU_TAB . DOKU_TAB . "e.async=true;e.defer=true;" . NL; 56 $code .= DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; 57 $code .= DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; 58 $code .= DOKU_TAB . "});" . NL . DOKU_TAB; 59 60 $event->data['script'][] = [ 61 '_data' => $code 62 ]; 63 64 /* Write out server-side info to a server log: */ 65 $this->writeServerLog($username); 66 } 67 68 /** 69 * Writes data to the server log. 70 * 71 * @return void 72 */ 73 private function writeServerLog($username) { 74 75 global $conf; 76 global $INFO; 77 78 // clean the page ID 79 $pageId = preg_replace('/[\x00-\x1F]/', "\u{FFFD}", $INFO['id'] ?? ''); 80 81 // collect GeoIP information (if available): 82 $geoIp = ( $this->sessionId == 'localhost' ? 'local' : 'ZZ' ); /* User-defined code for unknown country */ 83 try { 84 if (extension_loaded('geoip') && geoip_db_avail(GEOIP_COUNTRY_EDITION)) { 85 $geoIp = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']); 86 } else { 87 Logger::debug('BotMon Plugin: GeoIP module not available'); 88 } 89 } catch (Exception $e) { 90 Logger::error('BotMon Plugin: GeoIP Error', $e->getMessage()); 91 } 92 93 // create the log array: 94 $logArr = Array( 95 $_SERVER['REMOTE_ADDR'] ?? '', /* remote IP */ 96 $pageId, /* page ID */ 97 $this->sessionId, /* Session ID */ 98 $this->sessionType, /* session ID type */ 99 $username, 100 $_SERVER['HTTP_USER_AGENT'] ?? '', /* User agent */ 101 $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ 102 substr($conf['lang'],0,2), /* page language */ 103 implode(',', array_unique(array_map( function($it) { return substr($it,0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ 104 $geoIp /* GeoIP country code */ 105 ); 106 107 //* create the log line */ 108 $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ 109 $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ 110 foreach ($logArr as $tab) { 111 $logline .= "\t" . $tab; 112 }; 113 114 /* write the log line to the file */ 115 $logfile = fopen($filename, 'a'); 116 if (!$logfile) die(); 117 if (fwrite($logfile, $logline . "\n") === false) { 118 fclose($logfile); 119 die(); 120 } 121 122 /* Done */ 123 fclose($logfile); 124 } 125 126 private function getSessionInfo() { 127 128 // what is the session identifier? 129 if (isset($_SESSION)) { 130 $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ 131 foreach ($sesKeys as $key) { 132 if (substr($key, 0, 2) == 'DW') { 133 $this->sessionId = $key; 134 $this->sessionType = 'dw'; 135 return; 136 } 137 } 138 } 139 if ($this->sessionId == '') { /* no DokuWiki Session ID, try PHP session ID */ 140 $this->sessionId = session_id(); 141 $this->sessionType = 'php'; 142 } 143 if ($this->sessionId == '') { /* no PHP session ID, try IP address */ 144 $this->sessionId = $_SERVER['REMOTE_ADDR'] ?? ''; 145 $this->sessionType = 'ip'; 146 } 147 if ($this->sessionId == '') { /* if everything else fails, just us a random ID */ 148 $this->sessionId = rand(1000000, 9999999); 149 $this->sessionType = 'rand'; 150 } 151 } 152}