*/ class action_plugin_botmon extends DokuWiki_Action_Plugin { public function __construct() { // determine if a captcha should be loaded: $this->showCaptcha = 'Z'; // Captcha unknown $useCaptcha = $this->getConf('useCaptcha'); // should we show a captcha? if ($useCaptcha !== 'disabled') { if ($_SERVER['REQUEST_METHOD'] == 'HEAD') { $this->showCaptcha = 'H'; // Method is HEAD, no need for captcha } elseif ($this->captchaWhitelisted()) { $this->showCaptcha = 'W'; // IP is whitelisted, no captcha } elseif ($this->hasCaptchaCookie()) { $this->showCaptcha = 'N'; // No, user already has a cookie, don't show the captcha } else { $this->showCaptcha = 'Y'; // Yes, show the captcha } } } /** * Registers a callback functions * * @param EventHandler $controller DokuWiki's event controller object * @return void */ public function register(EventHandler $controller) { global $ACT; // populate the session id and type: $this->setSessionInfo(); // insert header data into the page: if ($ACT == 'show' || $ACT == 'edit' || $ACT == 'media') { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertHeader'); // Override the page rendering, if a captcha needs to be displayed: $controller->register_hook('TPL_ACT_RENDER', 'BEFORE', $this, 'insertCaptchaCode'); } else if ($ACT == 'admin' && isset($_REQUEST['page']) && $_REQUEST['page'] == 'botmon') { $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'insertAdminHeader'); } // also show a captcha before the image preview $controller->register_hook('TPL_IMG_DISPLAY', 'BEFORE', $this, 'showImageCaptcha'); // write to the log after the page content was displayed: $controller->register_hook('TPL_CONTENT_DISPLAY', 'AFTER', $this, 'writeServerLog'); } /* session information */ private $sessionId = null; private $sessionType = ''; private $showCaptcha = 'X'; /** * Inserts tracking code to the page header * (only called on 'show' actions) * * @param Event $event event object by reference * @return void */ public function insertHeader(Event $event, $param) { global $INFO; // build the tracker code: $code = $this->getBMHeader(); // add the deferred script loader:: $code .= DOKU_TAB . DOKU_TAB . "addEventListener('DOMContentLoaded', function(){" . NL; $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "const e=document.createElement('script');" . NL; $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.async=true;e.defer=true;" . NL; $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "e.src='".DOKU_BASE."lib/plugins/botmon/client.js';" . NL; $code .= DOKU_TAB . DOKU_TAB . DOKU_TAB . "document.getElementsByTagName('head')[0].appendChild(e);" . NL; $code .= DOKU_TAB . DOKU_TAB . "});"; $event->data['script'][] = ['_data' => $code]; } /* create the BM object code for insertion into a script element: */ private function getBMHeader() { // build the tracker code: $code = DOKU_TAB . DOKU_TAB . "document._botmon = {t0: Date.now(), session: " . json_encode($this->sessionId) . ", seed: " . json_encode($this->getConf('captchaSeed')) . ", ip: " . json_encode($_SERVER['REMOTE_ADDR']) . "};" . NL; // is there a user logged in? $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : ''); if ($username) { $code .= DOKU_TAB . DOKU_TAB . 'document._botmon.user = "' . $username . '";'. NL; } return $code; } /** * Inserts tracking code to the page header * (only called on 'show' actions) * * @param Event $event event object by reference * @return void */ public function insertAdminHeader(Event $event, $param) { $event->data['link'][] = ['rel' => 'stylesheet', 'href' => DOKU_BASE.'lib/plugins/botmon/admin.css', 'defer' => 'defer']; $event->data['script'][] = ['src' => DOKU_BASE.'lib/plugins/botmon/admin.js', 'defer' => 'defer', '_data' => '']; } /** * Writes data to the server log. * * @return void */ public function writeServerLog(Event $event, $param) { global $conf; global $INFO; // is there a user logged in? $username = ( !empty($INFO['userinfo']) && !empty($INFO['userinfo']['name']) ? $INFO['userinfo']['name'] : ''); // clean the page ID $pageId = preg_replace('/[\x00-\x1F]/', "\u{FFFD}", $INFO['id'] ?? ''); // create the log array: $logArr = Array( $_SERVER['REMOTE_ADDR'], /* remote IP */ $pageId, /* page ID */ $this->sessionId, /* Session ID */ $this->sessionType, /* session ID type */ $username, /* user name */ $_SERVER['HTTP_USER_AGENT'] ?? '', /* User agent */ $_SERVER['HTTP_REFERER'] ?? '', /* HTTP Referrer */ substr($conf['lang'],0,2), /* page language */ implode(',', array_unique(array_map( function($it) { return substr(trim($it),0,2); }, explode(',',trim($_SERVER['HTTP_ACCEPT_LANGUAGE'], " \t;,*"))))), /* accepted client languages */ $this->getCountryCode(), /* GeoIP country code */ $this->showCaptcha, /* show captcha? */ $_SERVER['REQUEST_METHOD'] ?? '' /* request method */ ); //* create the log line */ $filename = __DIR__ .'/logs/' . gmdate('Y-m-d') . '.srv.txt'; /* use GMT date for filename */ $logline = gmdate('Y-m-d H:i:s'); /* use GMT time for log entries */ foreach ($logArr as $tab) { $logline .= "\t" . $tab; }; /* write the log line to the file */ $logfile = fopen($filename, 'a'); if (!$logfile) die(); if (fwrite($logfile, $logline . "\n") === false) { fclose($logfile); die(); } /* Done */ fclose($logfile); } private function getCountryCode() { $country = ( $_SERVER['REMOTE_ADDR'] == '127.0.0.1' ? 'local' : 'ZZ' ); // default if no geoip is available! $lib = $this->getConf('geoiplib'); /* which library to use? (can only be phpgeoip or disabled) */ try { // use GeoIP module? if ($lib == 'phpgeoip' && extension_loaded('geoip') && geoip_db_avail(GEOIP_COUNTRY_EDITION)) { // Use PHP GeoIP module $result = geoip_country_code_by_name($_SERVER['REMOTE_ADDR']); $country = ($result ? $result : $country); } } catch (Exception $e) { Logger::error('BotMon Plugin: GeoIP Error', $e->getMessage()); } return $country; } private function setSessionInfo() { // what is the session identifier? if (isset($_SESSION)) { $sesKeys = array_keys($_SESSION); /* DokuWiki Session ID preferred */ foreach ($sesKeys as $key) { if (substr($key, 0, 2) == 'DW') { $this->sessionId = $key; $this->sessionType = 'dw'; return; } } } if (!$this->sessionId) { /* no DokuWiki Session ID, try PHP session ID */ $this->sessionId = session_id(); $this->sessionType = 'php'; } if (!$this->sessionId) { /* no PHP session ID, try IP address */ $this->sessionId = $_SERVER['REMOTE_ADDR']; $this->sessionType = 'ip'; } if (!$this->sessionId) { /* if all fails, use random data */ $this->sessionId = rand(100000000, 999999999); $this->sessionType = 'rnd'; } } public function insertCaptchaCode(Event $event) { $useCaptcha = $this->getConf('useCaptcha'); // which background to show? // only if we previously determined that we need a captcha: if ($this->showCaptcha == 'Y') { echo '
' . NL . 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'. NL . '
' . NL; echo '' . NL . 'At vero eos et accusamus et iusto odio dignissimos ducimus, qui blanditiis praesentium voluptatum deleniti atque corrupti, quos dolores et quas molestias excepturi sint, obcaecati cupiditate non provident, similique sunt in culpa, qui officia deserunt mollitia animi, id est laborum et dolorum fuga.'. NL . '
' . NL; echo '\n" . implode(' ', $paragraph) . "\n
\n"; } private function dadaMakeSentence($words, $totalWeight) { // how many words to generate? $wordCount = mt_rand(4, 20); // generate the sentence: $sentence = array(); for ($i=0; $i<$wordCount; $i++) { array_push($sentence, $this->dadaSelectRandomWord($words, $totalWeight)); } return ucfirst(implode(' ', $sentence)) . '.'; } private function dadaSelectRandomWord($list, $totalWeight) { // get a random selection: $rand = mt_rand(0, $totalWeight); // match the selection to the weighted list: $cumulativeWeight = 0; for ($i=0; $i