' . NL; const SP = ' '; const DATEFMT = 'DateFmt'; const GRAPHATOM = 'atom'; const GRAPHMAX = 'max'; const GRAPHNAME ='GraphName'; const BARMAXWIDTH = 250; # px const BARCELLWIDTH = '260px'; const NULLUSERAGENT = '_nulluseragentvalue_'; const USERAGENTEMPTY = 'User Agent empty'; const MSGFMTID = __NAMESPACE__ . '\\' . TOTALCOUNTERNAME; const TCMAXITEMS = 'TotalCounterMaxItems'; // These constants are cell or column names in the TotalCounter array. DO NOT change. const KEYBOTS = 'Bots'; const KEYBROWSERS = 'Browsers'; const KEYDATECREATED = 'DateCreated'; const KEYLANGUAGES = 'Languages'; const KEYLASTTIMESTAMP = 'LastTimestamp'; const KEYLOCATIONS = 'Locations'; const KEYOSES = 'OSes'; const KEYPAGES = 'Pages'; const KEYPAGESTODAYCOUNTER = 'PagesTodayCounter'; const KEYPAGESTODAYDAY = 'PagesTodayDay'; const KEYREFERERS = 'Referers'; # spelling as per mispelling in HTTP header spec const KEYTOTAL = 'Total'; const KEYTOTALCOUNTERVERSION = 'TotalCounterVersion'; const KEYUSERS = 'Users'; const LASTDAY = 'LastDay'; const LASTMONTH = 'LastYear'; const KEYLASTSEEN = 'LastSeen'; const KEYMETADATA = 'Metadata'; const LASTWEEK = 'LastWeek'; const LASTYEAR = 'LastYear'; const LITUNKNOWN = 'Unknown'; const PREVIOUSYEARS = 'LastYears'; /* The following strings can be internationalised (note case) (using the markup $[ ]) */ const LITBROWSERS = '$[Browsers]'; const LITDAY = '$[day]'; const LITCOUNT = '$[Count]'; const LITDOWNLOADS = '$[downloads]'; const LITFILE = '$[File]'; const LITHOURS = '$[hours]'; const LITLANGUAGES = '$[Languages]'; const LITLAST = '$[Last]'; const LITLOCATIONS = '$[Locations]'; const LITMONTH = '$[month]'; const LITMORE = '$[more]'; const LITOS = '$[Operating systems]'; const LITPAGE = '$[Page]'; const LITPAGES = '$[pages]'; const LITPERCENT = '$[Percent]'; const LITPREVIOUS = '$[Previous]'; const LITREFERERS = '$[Referers]'; const LITSTATISTICS = '$[statistics]'; const LITTODAY = '$[today]'; const LITUSERS = '$[Users]'; const LITYEAR = '$[year]'; const LITYEARS = '$[years]'; const LITVIEWS = '$[views]'; const LITWEBBOTS = '$[Web bots]'; const LITWEEK = '$[week]'; # const ACTIONBROWSE = 'browse'; // ?action=browse const ACTIONDATACHECK = 'totalcountercheck'; // ?action=totalcountercheck const ACTIONDATAFIX = 'totalcounterfix'; // ?action=totalcounterfix \SDV($TotalCounterActionName, 'totalcounter'); // ?action=totalcounter \SDV($TotalCounterAuthLevel, 'read'); \SDV($TotalCounterMaxItems, 30); // default 30 \SDV($TotalCounterEnableLookup, 0); // default 0 \SDV($TotalCounterBarColor, '#5af'); // default '#5af' \SDV($TotalCounterCountBots, 0); // default 0 \SDV($TotalCounterShowNumbers, 1); // default 1 \SDV($TotalCounterEnableGeoIp, 0); // default 0 \SDV($TotalCounterGeoIPData, "$WorkDir/GeoIP.dat"); \SDV($TotalCounterEnableDownload, 0); //default 0 \SDV($TotalCounterDownloadManager, "$WorkDir/.download.manager"); \SDV($TotalCounterEnableChmods, 1); // default 1 \SDV($TotalCounterEnableUsers, 0); // default 0 \SDV($TotalCounterFile, "$WorkDir/totalcounter.stat"); \SDV($TotalCounterLockfile, "$WorkDir/totalcounter.lock"); \SDV($TotalCounterLogfile, "$WorkDir/totalcounter.log"); \SDV($TotalCounterEnableLog, 0); # default 0 (off), 1 (on, captures unknowns), 2 log server details (verbose) /*## \SDV($TotalCounterBrowsersUnset, ''); // used to remove individual totals from file, don't use this unless you know what you are doing \SDV($TotalCounterOSesUnset, ''); // used to remove individual totals from file, don't use this unless you know what you are doing \SDV($TotalCounterLocationsUnset, ''); // used to remove individual totals from file, don't use this unless you know what you are doing \SDV($TotalCounterBotsUnset, ''); // used to remove individual totals from file, don't use this unless you know what you are doing \SDV($TotalCounterReferersUnset, ''); // used to remove individual totals from file, don't use this unless you know what you are doing ##*/ \SDV($HTMLStylesFmt[TOTALCOUNTERNAME], '.TCbar {background-color:$TotalCounterBarColor; min-height:13px; width:13px; color:#fff;}' .NL . '.TCtxtr {text-align:right;}' .NL . '.TCtxtl {text-align:left;}' .NL . '.TCtxth {font-weight: bold;}' .NL . '.TCprogress {margin-left:auto; margin-right:auto;}' . NL . 'table.totalcounter td {font-size:x-small; text-align:left}' . NL . '.TCchklst {font-family: monospace; font-size:smaller;}' . NL); \SDVA($TotalCounterMonthsShort, array('$[Jan]','$[Feb]','$[Mar]','$[Apr]','$[May]','$[Jun]','$[Jul]','$[Aug]','$[Sep]','$[Oct]','$[Nov]','$[Dec]')); \SDV($TotalCounterBlacklist[KEYPAGES], array ()); \SDV($TotalCounterBlacklist[KEYUSERS], array ()); \SDV($TotalCounterBlacklist[KEYBROWSERS], array ()); \SDV($TotalCounterBlacklist[KEYOSES], array ()); \SDV($TotalCounterBlacklist[KEYREFERERS], array ()); \SDV($TotalCounterBlacklist[KEYLOCATIONS], array ()); \SDV($TotalCounterBlacklist[KEYBOTS], array ()); \SDV($TotalCounterBlacklist[KEYLANGUAGES], array ()); ## by MateuszCzaplinski ## last day, last week, ... - data & display descriptions \SDVA($TotalCounterTimeBins, array( LASTDAY => array( # LastDay = 24 hours; 1 hour = 60*60sec GRAPHNAME=>LITLAST . SP . LITDAY . SP . '(' . LITHOURS . ')', GRAPHMAX=>24, GRAPHATOM=>60*60, // DATEFMT => function($now, $atom, $maxnr, $nr) { return date ("H:00", $now - $atom * ($maxnr - 1 - $nr)); } ), LASTWEEK => array( # LastWeek = 7 days GRAPHNAME=>LITLAST . SP . LITWEEK, GRAPHMAX=>7, GRAPHATOM=>24*60*60, // DATEFMT => function($now, $atom, $maxnr, $nr) { return date ("D", $now - $atom * ($maxnr - 1 - $nr)); } ), LASTMONTH => array( # Month = 31 days GRAPHNAME=>LITLAST . SP . LITMONTH, GRAPHMAX=>31, GRAPHATOM=>24*60*60, // DATEFMT => function($now, $atom, $maxnr, $nr) { return date ("j", $now - $atom * ($maxnr - 1 - $nr)); } ), LASTYEAR => array( # date ('n') is the month of the year GRAPHNAME=>LITLAST . SP . LITYEAR, GRAPHMAX=>12, GRAPHATOM=>'n', // DATEFMT => function($now, $atom, $maxnr, $nr) use ($TotalCounterMonthsShort) { return $TotalCounterMonthsShort[(12 + intval(date ($atom, $now)) - $maxnr + $nr) % 12]; } ), PREVIOUSYEARS => array( GRAPHNAME=>LITPREVIOUS . SP . LITYEARS, GRAPHMAX=>30, GRAPHATOM=>'Y', // DATEFMT => function($now, $atom, $maxnr, $nr) { return strval(intval(date ($atom, $now)) - ($maxnr - 1 - $nr)); } ) )); \SDVA($HandleActions, array ( $TotalCounterActionName => __NAMESPACE__ . '\HandleTotalCounter' )); \SDVA($HandleAuth, array ( $TotalCounterActionName => $TotalCounterAuthLevel )); #global $TotalCounter; \SDV($TotalCounterDebug, false); # set default debug setting # set debug flag $totalCounter_debugOn = boolval ($TotalCounterDebug); # if on writes input and output to web page #xmp([$TotalCounterDebug, $totalCounter_debugOn]); if ($totalCounter_debugOn) { tcmsg (__FILE__, $RecipeInfo[TOTALCOUNTERNAME]['Version'] . ' using "' . $WorkDir . '" with action=' . $action . ', log=' . $TotalCounterEnableLog . ', IP lookup: '. $TotalCounterEnableLookup); } $TotalCounterLogOn = boolval ($TotalCounterEnableLog > 0); $TotalCounterLogVerbose = boolval ($TotalCounterEnableLog == 2); if ($TotalCounterMaxItems <= 0) $TotalCounterMaxItems = 1; # set up some debug actions for fixing the TotalCounter file if ($totalCounter_debugOn) { $HandleActions [ACTIONDATACHECK] = __NAMESPACE__ . '\HandleTotalCounterCheck'; $HandleAuth [ACTIONDATACHECK] = 'admin'; if ($TotalCounterLogOn) { $HandleActions [ACTIONDATAFIX] = __NAMESPACE__ . '\HandleTotalCounterCheck'; $HandleAuth [ACTIONDATAFIX] = 'admin'; } } # end if debug $statFileName = $TotalCounterFile; $lockFileName = $TotalCounterLockfile; $logFileName = $TotalCounterLogfile; $psft = function_exists('\PSFT') ? '\PSFT' : 'strftime'; $logFileTime = $psft ("%Y-%m-%d %H:%M:%S "); # identify this run, note trailing space $geoIpFile = $TotalCounterGeoIPData; // clear cached information about file clearstatcache(); // script to carry on working after the user has cancelled request or browser session closed ignore_user_abort(true); //------------------------------------------------------------------------------------ if ($TotalCounterLogOn) { $logFileHandle = fopen($logFileName, 'a'); # create or open logfile for appending if ($logFileHandle === false) { recordErrorMessage ('fopen failed', error_get_last()); if ($totalCounter_debugOn) \Abort (end($MessagesFmt [MSGFMTID])); } } if ($TotalCounterLogVerbose) { # write for every call, this can be very verbose $logMsg = 'Verbose: '; # initialise if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR'])) $logMsg .= 'XFF:"' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '" '; if (!empty ($_SERVER['HTTP_X_FORWARDED_HOST'])) $logMsg .= 'XFH:"' . $_SERVER['HTTP_X_FORWARDED_HOST'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED'])) $logMsg .= 'Fw:"' . $_SERVER['HTTP_FORWARDED'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED-HOST'])) $logMsg .= 'FH:"' . $_SERVER['HTTP_FORWARDED-HOST'] . '" '; if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; if (!empty ($_SERVER['REMOTE_ADDR'])) $logMsg .= 'RA:"' . $_SERVER['REMOTE_ADDR'] . '" '; if (!empty ($_SERVER['HTTP_USER_AGENT'])) $logMsg .= 'UA:"' . $_SERVER['HTTP_USER_AGENT'] . '" '; if (!empty ($_SERVER['HTTP_REFERER'])) $logMsg .= 'Rf:"' . $_SERVER['HTTP_REFERER'] . '" '; if (!empty ($_SERVER['HTTP_USER_REFERER'])) $logMsg .= 'URf:"' . $_SERVER['HTTP_USER_REFERER'] . '" '; tcLogMessage ($logMsg); } $tcPageName = \ResolvePageName($pagename); # PmWiki function $tcPageExists = true; switch (true) { case (empty($tcPageName)): $tcPageExists = false; $tcPageName = LITUNKNOWN; #"$DefaultGroup.$DefaultName"; if ($TotalCounterLogOn) tcLogMessage ('Called with empty pagename: "' . $pagename . '"'); break; case (\PageExists($tcPageName)): # PmWiki function break; default: # pagename does not exist $tcPageExists = false; if ($TotalCounterLogOn) tcLogMessage ('Called with non-existent page: "' . $tcPageName . '"'); } # end switch if (!$tcPageExists) { # page does not exist return; # finished TotalCounter initialisation processing <=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<=<= } # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= // update counts from page being browsed if ($action == ACTIONBROWSE) { //find users if (isset ($AuthId)) { $tc_user = $AuthId; } else { if (isset ($Author)) { $tc_user = $Author; } else { session_start(); if (isset ($_SESSION['authid'])) { $tc_user = $_SESSION['authid'][0]; } else { $tc_user = 'Guest (not authenticated)'; } // end isset $_SESSION } // end if else isset $Author } // end if else isset $AuthId ## detect user agent $tc_bot = NULLUSERAGENT; $tc_browser = NULLUSERAGENT; $tcBotFound = false; $tcBrowserFound = false; switch (true) { case empty($_SERVER['HTTP_USER_AGENT']): # it happens often if ($TotalCounterLogVerbose) { $logMsg = 'UA empty: '; # initialise if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; tcLogMessage ($logMsg . '; action: "' . $action . '"'); \Lock(0); # release lock } $tc_bot = USERAGENTEMPTY; $tc_browser = USERAGENTEMPTY; break; default: # user agent not empty $tc_bot = detectWebBot ($_SERVER['HTTP_USER_AGENT']); if ($tc_bot !== NULLUSERAGENT) { # found a bot $tcBotFound = true; break; } $tc_browser = detectBrowser ($_SERVER['HTTP_USER_AGENT']); if ($tc_browser !== NULLUSERAGENT) { $tcBrowserFound = true; break; } $tc_browser = LITUNKNOWN; $tcBrowserFound = false; # don't count unknown; if ($TotalCounterLogOn) { $logMsg = 'Browser unknown: '; # initialise if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR'])) $logMsg .= 'XFF:"' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED'])) $logMsg .= 'Fw:"' . $_SERVER['HTTP_FORWARDED'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED-HOST'])) $logMsg .= 'FH:"' . $_SERVER['HTTP_FORWARDED-HOST'] . '" '; #if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; # if (!empty ($_SERVER['REMOTE_ADDR'])) $logMsg .= 'RA:"' . $_SERVER['REMOTE_ADDR'] . '" '; if (!empty ($_SERVER['HTTP_USER_AGENT'])) $logMsg .= 'UA:"' . $_SERVER['HTTP_USER_AGENT'] . '" '; tcLogMessage ($logMsg); } } # end switch // decide if we are counting this visit $tc_count_visit = ((!$tcBotFound) // don't count bots || ($TotalCounterCountBots == 1)); // count bots (all visits) $tcOSFound = false; if ($tc_count_visit) { # don't count bots by default // find operating system if ($tc_browser == USERAGENTEMPTY) { $tc_os = USERAGENTEMPTY; } else { $tc_os = detectOS ($_SERVER['HTTP_USER_AGENT']); if ($tc_os == NULLUSERAGENT) { $tc_os = LITUNKNOWN; if ($TotalCounterLogOn) { $logMsg = 'OpSystem unknown: '; # initialise if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR'])) $logMsg .= 'XFF:"' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED'])) $logMsg .= 'Fw:"' . $_SERVER['HTTP_FORWARDED'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED-HOST'])) $logMsg .= 'FH:"' . $_SERVER['HTTP_FORWARDED-HOST'] . '" '; if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; if (!empty ($_SERVER['REMOTE_ADDR'])) $logMsg .= 'RA:"' . $_SERVER['REMOTE_ADDR'] . '" '; if (!empty ($_SERVER['HTTP_USER_AGENT'])) $logMsg .= 'UA:"' . $_SERVER['HTTP_USER_AGENT'] . '" '; tcLogMessage ($logMsg); } } else { $tcOSFound = true; } } } // end find OS if ($tc_count_visit) { # don't count bots by default // find referrer domain $matches = []; $referer = ''; $tc_referer = LITUNKNOWN; if (!empty ($_SERVER['HTTP_REFERER'])) { # remove the schema, see https://regex101.com/r/epmzHv/2 if (1 == preg_match("/^(?:https?:\/\/)?([^\/:\r\n]+)/", $_SERVER['HTTP_REFERER'], $matches)) { $referer = $matches[1]; } } if (!empty($referer)) { $tc_referer = $referer; } if ($tc_referer == LITUNKNOWN) { if ($tcBotFound) { # skip referer if it is a bot and referer not identified // 1.10.0 unset ($tc_referer); }; // end !empty $tc_bot if ($TotalCounterLogOn and !empty($_SERVER['HTTP_REFERER'])) { $logMsg = 'Referer unknown: '; # initialise if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR'])) $logMsg .= 'XFF:"' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED'])) $logMsg .= 'Fw:"' . $_SERVER['HTTP_FORWARDED'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED-HOST'])) $logMsg .= 'FH:"' . $_SERVER['HTTP_FORWARDED-HOST'] . '" '; if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; if (!empty ($_SERVER['REMOTE_ADDR'])) $logMsg .= 'RA:"' . $_SERVER['REMOTE_ADDR'] . '" '; if (!empty ($_SERVER['HTTP_USER_AGENT'])) $logMsg .= 'UA:"' . $_SERVER['HTTP_USER_AGENT'] . '" '; tcLogMessage ($logMsg . ' ^' . $referer . '^ ["' . implode ('", "', $matches) . '"] '); } } // end find referrer } // end count visit referrer if ($tc_count_visit) { # don't count bots by default // find location $dbgloc = ''; # de-facto standard header for identifying the originating IP address of a client connecting to a web server through an HTTP proxy or a load balancer $dbgloc = ''; # initialise $thehost = ''; # initialise switch (true) { case (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])): $dbgloc .= 'XFF'; if (false !== strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',')) { $ips = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); $thehost = trim($ips[0]); # see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For } else { $thehost = $_SERVER['HTTP_X_FORWARDED_FOR']; } break; case (!empty($_SERVER['HTTP_FORWARDED'])): $dbgloc = 'Fw'; $posn = strpos($_SERVER['HTTP_FORWARDED'], 'for='); if (false !== $posn) { $ips = explode(';', substr($_SERVER['HTTP_FORWARDED'], $posn + 4)); #for=192.0.2.60;proto=http $ips = explode(',', $ips[0]); # 192.0.2.43, for=198.51.100.17 $thehost = trim($ips[0]); } else { $thehost = $_SERVER['HTTP_FORWARDED']; } break; case (!empty($_SERVER['REMOTE_HOST'])): if (false !== strpos($_SERVER['REMOTE_HOST'], ',')) { $dbgloc = 'RH+'; # empty($_SERVER['HTTP_X_FORWARDED_FOR']) and empty($_SERVER['HTTP_FORWARDED']) $ips = explode(',', $_SERVER['REMOTE_HOST']); $thehost = trim($ips[0]); } else { $dbgloc = 'RH'; $thehost = $_SERVER['REMOTE_HOST']; } break; default: # no header information ! break; } # end switch // match any character not digits or period backwards from end of string if (1 == preg_match("/[^\.0-9]+$/", $thehost, $matches)) { $loc = $matches[0]; } $tc_location = LITUNKNOWN; while (true) { if (!empty($loc)) { $dbgloc .= '='; $tc_location = $loc; break; } if ($TotalCounterEnableLookup == 1) { $hostbyaddr = gethostbyaddr($_SERVER['REMOTE_ADDR']); $dbgloc .= 'L^' . $hostbyaddr . '^'; if (false === $hostbyaddr) { $tc_location = LITUNKNOWN; } else { $prmRetVal = preg_match("/[^\.0-9]+$/", $hostbyaddr, $matches); // match any character not digits or period (IP address?) switch (true) { case ($prmRetVal === false): # error occurred $tc_location = LITUNKNOWN; break; case ($prmRetVal == 1): # match found $gloc = $matches[0]; break; case ($prmRetVal == 0): # match not found $tc_location = LITUNKNOWN; break; } # end switch if (!empty($gloc)) { $tc_location = $gloc; break; } } } if ($TotalCounterEnableGeoIp == 1) { $dbgloc .= 'G'; include ('geoip/geoip.inc'); # return the two letter country code corresponding to a hostname or an IP address $gi = geoip_open($geoIpFile, GEOIP_STANDARD); # https://www.php.net/manual/en/function.geoip-country-code-by-name.php $gccba = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']); geoip_close($gi); if (!false === $gccba) { $tc_location = $gccba; break; } } # https://regex101.com/r/ONcmD7/1 if (1 == preg_match("/(?:10\.[0-9]{1,3}|172\.(?:1[6-9]|2[0-9]|3[0-1])|192\.168)(?:\.[0-2]?[0-9]{1,2}){2}/", $thehost, $matches)) { $dbgloc .= 'P'; # match for private IP address https://en.wikipedia.org/wiki/Private_network $tc_location = 'Private IP (' . stristr ($matches [0], '.', true) . ')'; break; } $dbgloc .= '.'; break; } # end while true if ($tc_location == LITUNKNOWN) { if ($tcBotFound) { # skip location if it is a bot and location not identified unset ($tc_location); } elseif ($TotalCounterLogVerbose) { # this happens so often we log only if verbose $logMsg = 'Location unknown: '; # initialise if (!empty ($_SERVER['HTTP_X_FORWARDED_FOR'])) $logMsg .= 'XFF:"' . $_SERVER['HTTP_X_FORWARDED_FOR'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED'])) $logMsg .= 'Fw:"' . $_SERVER['HTTP_FORWARDED'] . '" '; if (!empty ($_SERVER['HTTP_FORWARDED-HOST'])) $logMsg .= 'FH:"' . $_SERVER['HTTP_FORWARDED-HOST'] . '" '; if (!empty ($_SERVER['REMOTE_HOST'])) $logMsg .= 'RH:"' . $_SERVER['REMOTE_HOST'] . '" '; if (!empty ($_SERVER['REMOTE_ADDR'])) $logMsg .= 'RA:"' . $_SERVER['REMOTE_ADDR'] . '" '; # if (!empty ($_SERVER['HTTP_USER_AGENT'])) $logMsg .= 'UA:"' . $_SERVER['HTTP_USER_AGENT'] . '" '; # don't lok this variable tcLogMessage ($logMsg . ' (' . $thehost . ') ' . $dbgloc); } } else { //end = Unknown $tc_location = strtolower($tc_location); $tc_location = str_ireplace ([LITUNKNOWN, 'private ip'], [LITUNKNOWN, 'Private IP'], $tc_location); } } // end count visits location } // end if action = browse # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= $oldumask = umask(0); # set the default file permissions for newly created files and directories $TotalCounterDownloads = FALSE; if ($TotalCounterEnableDownload == 1) { $downloadFileName = $TotalCounterDownloadManager; if (file_exists($downloadFileName)) { $TotalCounterDownloadsFileContents = tc_file_get_contents($downloadFileName); if (FALSE === $TotalCounterDownloadsFileContents) { recordErrorMessage ('tc_file_get_contents failed', error_get_last()); } else { $TotalCounterDownloads = unserialize($TotalCounterDownloadsFileContents, ['allowed_classes' => false]); if (FALSE === $TotalCounterDownloads) recordErrorMessage ('unserialize failed', error_get_last()); } } } if (file_exists($statFileName)) { $TotalCounterFileContents = tc_file_get_contents($statFileName); if (FALSE === $TotalCounterFileContents) { recordErrorMessage ('tc_file_get_contents failed', error_get_last()); echo end ($MessagesFmt [MSGFMTID]); return end ($MessagesFmt [MSGFMTID]); # failed to read file, lets get out of here } $TotalCounter = unserialize($TotalCounterFileContents, ['allowed_classes' => false]); if (FALSE === $TotalCounter) { recordErrorMessage ('tc_file unserialize failed', error_get_last()); echo end ($MessagesFmt [MSGFMTID]); return end ($MessagesFmt [MSGFMTID]); # failed to unserialize file, lets get out of here } } else { # stat file does not exist touch($statFileName); # create the stat file $TotalCounter = []; $TotalCounter[KEYDATECREATED] = date ('c'); # ISO 8601 format $TotalCounter[KEYTOTAL] = 0; $TotalCounter[KEYPAGES][$tcPageName] = 0; } # $tcNowDateTime = time(); # fix current time for duration of processing $tcNowDate = date ("%y%m%d", $tcNowDateTime); # fix current date for duration of processing $TotalCount = 0; # initialise # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= if (($action == ACTIONBROWSE) && ($tcPageExists)) { if( dblock($statFileName) ) { $TotalCounterFileContents = tc_file_get_contents($statFileName); if (FALSE === $TotalCounterFileContents) { recordErrorMessage ('tc_file_get_contents failed', error_get_last()); } $TotalCounter = unserialize($TotalCounterFileContents, ['allowed_classes' => false]); if (FALSE === $TotalCounter) { recordErrorMessage ('tc_file unserialize failed', error_get_last()); } /*## // code intended only for developers to clean up entries in the stats file // use at your own risk if ($totalCounter_debugOn) { if (!empty ($TotalCounterBrowsersUnset)) { if (isset($TotalCounter[KEYBROWSERS][$TotalCounterBrowsersUnset])) { unset ($TotalCounter[KEYBROWSERS][$TotalCounterBrowsersUnset]); tcmsg ('unset Browser', $TotalCounterBrowsersUnset); } } # end Browsers if (!empty ($TotalCounterOSesUnset)) { if (isset($TotalCounter[KEYOSES][$TotalCounterOSesUnset])) { unset ($TotalCounter[KEYOSES][$TotalCounterOSesUnset]); tcmsg ('unset OS', $TotalCounterOSesUnset); } } # end OSes if (!empty ($TotalCounterLocationsUnset)) { if (isset($TotalCounter[KEYLOCATIONS][$TotalCounterLocationsUnset])) { unset ($TotalCounter[KEYLOCATIONS][$TotalCounterLocationsUnset]); tcmsg ('unset Location', $TotalCounterLocationsUnset); } } # end Locations if (!empty ($TotalCounterBotsUnset)) { if (isset($TotalCounter[KEYBOTS][$TotalCounterBotsUnset])) { unset ($TotalCounter[KEYBOTS][$TotalCounterBotsUnset]); tcmsg ('unset Bot', $TotalCounterBotsUnset); } } # end Bots if (!empty ($TotalCounterReferersUnset)) { if (isset($TotalCounter[KEYREFERERS][$TotalCounterReferersUnset])) { unset ($TotalCounter[KEYREFERERS][$TotalCounterReferersUnset]); tcmsg ('unset Referer', $TotalCounterReferersUnset); } } # end Referers } # $totalCounter_debugOn // end clean up code ##*/ $TotalCount = ++ $TotalCounter[KEYTOTAL]; $blockListed = false; # initialise if (in_array($tc_user, $TotalCounterBlacklist[KEYUSERS])) $blockListed = true; if (!$blockListed && !in_array($tc_user, $TotalCounterBlacklist[KEYUSERS])) { if (is_array($TotalCounterBlacklist[KEYUSERS])) { foreach ($TotalCounterBlacklist[KEYUSERS] as $value) if (substr($value, 0, 1) == '/' && preg_match($value, $tc_user) > 0) $blockListed = true; } if (isset ($tc_user)) { incrementCount(KEYUSERS, $tc_user); } } if (!$blockListed && !in_array($tcPageName, $TotalCounterBlacklist[KEYPAGES])) { if (is_array($TotalCounterBlacklist[KEYPAGES])) foreach ($TotalCounterBlacklist[KEYPAGES] as $value) if (substr($value, 0, 1) == '/') if (preg_match($value, $tcPageName) > 0) $blockListed = true; if (!$blockListed) { if (empty ($TotalCounter[KEYPAGES][$tcPageName])) { # initialise $TotalCounter[KEYPAGES][$tcPageName] = 0; $TotalCounter[KEYPAGESTODAYDAY][$tcPageName] = $tcNowDate; $TotalCounter[KEYPAGESTODAYCOUNTER][$tcPageName] = 0; } ++ $TotalCounter[KEYPAGES][$tcPageName]; ## handles the daily counter if ($TotalCounter[KEYPAGESTODAYDAY][$tcPageName] == $tcNowDate) ++ $TotalCounter[KEYPAGESTODAYCOUNTER][$tcPageName]; else { $TotalCounter[KEYPAGESTODAYDAY][$tcPageName] = $tcNowDate; $TotalCounter[KEYPAGESTODAYCOUNTER][$tcPageName] = 1; } } } if (!$blockListed && defined('MULTILANGUAGE')) { if (isset ($userlang2)) { incrementCount(KEYLANGUAGES, $userlang2); } } if (!$blockListed && ($tcBrowserFound) && !in_array($tc_browser, $TotalCounterBlacklist[KEYBROWSERS])) { incrementCount(KEYBROWSERS, $tc_browser, $tcNowDateTime); } if (!$blockListed && ($tcBotFound) && !in_array($tc_bot, $TotalCounterBlacklist[KEYBOTS])) { incrementCount(KEYBOTS, $tc_bot, $tcNowDateTime); } if (!$blockListed && ($tcOSFound) && !in_array($tc_os, $TotalCounterBlacklist[KEYOSES])) { // 1.10.0 isset incrementCount(KEYOSES, $tc_os, $tcNowDateTime); } switch (true) { case $blockListed: break; case !isset ($TotalCounterBlacklist[KEYREFERERS]): break; case !isset ($tc_referer): break; case !is_array($TotalCounterBlacklist[KEYREFERERS]): break; case in_array($tc_referer, $TotalCounterBlacklist[KEYREFERERS]): break; default: foreach ($TotalCounterBlacklist[KEYREFERERS] as $value) { if (substr($value, 0, 1) == '/') if (preg_match($value, $tc_referer) > 0) $blockListed = true; } if (!$blockListed) incrementCount(KEYREFERERS, $tc_referer); } # end switch # if (!$blockListed && isset ($tc_location) && !in_array($tc_location, $TotalCounterBlacklist[KEYLOCATIONS])) { // 1.10.0 isset incrementCount(KEYLOCATIONS, $tc_location); } if (!$blockListed && defined('MULTILANGUAGE')) { if (! in_array($tc_location, $TotalCounterBlacklist[KEYLANGUAGES])) incrementCount(KEYLANGUAGES, $userlang2); } ## by MateuszCzaplinski ## last day, last week, ... - collect data if (!$blockListed && (!$tcBotFound)) { // don't count if bot foreach ($TotalCounterTimeBins as $nBin=>$aBin) TCbins($nBin, $aBin[GRAPHMAX], $aBin[GRAPHATOM], $tcNowDateTime); $TotalCounter[KEYLASTTIMESTAMP] = $tcNowDateTime; $TotalCounter[KEYTOTALCOUNTERVERSION] = TOTALCOUNTERV; } dbexport_unlock($statFileName, serialize($TotalCounter), 'w'); } else { // could not acquire a lockfile // check if the lockfile isn't a stale one, try to delete it if so dblock_remove_stale($statFileName); } # end if browse } else { $TotalCount = $TotalCounter[KEYTOTAL]; ## by Schlaefer (==?) - fixed incrementCount(KEYPAGESTODAYCOUNTER, $tcPageName); $TotalCounter[KEYPAGESTODAYDAY][$tcPageName] ??= $tcNowDate; } # end if not browse # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= //add the {$PageCount} and {$TotalCount} markup $FmtPV['$PageCount'] = "'" . number_format ((int) $GLOBALS['TotalCounter'][KEYPAGES][$tcPageName] ?? 0) . "'"; // return count for current page $FmtPV['$TotalCount'] = "'" . number_format ((int) $GLOBALS["TotalCounter"]["Total"] ?? 0) . "'"; // return total count for wiki ## by Schlaefer ## adds vars for the input form $FmtPV['$TotalCounterMaxItems'] = "'" . ($_REQUEST[TCMAXITEMS] ?? $TotalCounterMaxItems) . "'"; # //add the {$PageViews} page variable $FmtPV['$PageViews'] = 'number_format ($GLOBALS["TotalCounter"]["Pages"][$pagename])'; # allows use in PageLists ## by Schlaefer ## add the {$PageCountToday} page variable $FmtPV['$PageCountToday'] = 'number_format ($GLOBALS["TotalCounter"]["PagesTodayCounter"][$pagename])'; # allows use in PageLists return; # finished TotalCounter processing //===================================================================================================================== function incrementCount (string $countType, $counter, ?int $lastSeenTimestamp = null) { // create variable if it does not exist to avoid PHP 8 errors # counter may be of type string or int global $TotalCounter; $TotalCounter[$countType] ??= []; // If not, initialise it as an empty array $TotalCounter[$countType][$counter] ??= 0; $TotalCounter[$countType][$counter]++; if (!is_null ($lastSeenTimestamp)) { // track the last time a counter was seen $TotalCounter[KEYMETADATA] ??= []; // If not, initialise it as an empty array $TotalCounter[KEYMETADATA][$countType] ??= []; // If not, initialise it as an empty array $TotalCounter[KEYMETADATA][$countType][$counter] ??= []; // If not, initialise it as an empty array $TotalCounter[KEYMETADATA][$countType][$counter][KEYLASTSEEN] = date ('c', $lastSeenTimestamp); # last seen date and time ISO 8601 format } } # end incrementCount //===================================================================================================================== function HandleTotalCounter(string $pagename, string $auth = 'read') { // handle PmWiki action=totalcounter global $action, $TotalCounter, $TotalCounterMaxItems, $TotalCounterShowNumbers, $tcNowDate; global $TotalCount, $TotalCounterEnableDownload, $TotalCounterDownloads, $TotalCounterTimeBins, $TotalCounterBinsFmt, $TotalCounterEnableUsers; global $PageStartFmt, $PageEndFmt, $MessagesFmt, $totalCounter_debugOn, $TotalCounterLogOn, $logFileHandle, $tcPageExists; //$page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT); $page = \RetrieveAuthPage($pagename, $auth); # PmWiki function if (!$page) { \Abort('?you are not permitted to perform this action "' . $action . '"'); # PmWiki function } if (!$tcPageExists) { \Abort('?action "'. $action . '" is not available on a non-existent page "' . $pagename . '"'); # PmWiki function } if (FALSE === $TotalCounter) { \Abort('?TotalCounter statistics could not be retrieved or unserialized'); # PmWiki function } if (! is_array ($TotalCounter)) { # should never happen \Abort('?TotalCounter data unavailable'); # PmWiki function } # $all_locations = SetAllLocations (); # top level domains ## by Schlaefer ## sets the max items if provided by the form if (array_key_exists (TCMAXITEMS, $_REQUEST)) $TotalCounterMaxItems = $_REQUEST[TCMAXITEMS]; $htmlOut = '

Total Counter ' . LITSTATISTICS . '

' . NL; //------------------------------------------------------------------------------------------------------------ // PAGES $htmlOut .= graphHead (LITPAGE . SP . LITVIEWS, true); if (is_array($TotalCounter[KEYPAGES])) { arsort($TotalCounter[KEYPAGES]); // sort high to low $tar = array_slice($TotalCounter[KEYPAGES], 0, $TotalCounterMaxItems); $tar2 = array_slice($TotalCounter[KEYPAGES], $TotalCounterMaxItems, $TotalCounterMaxItems); $tot = $TotalCount; $max = current($tar); } else { # should never happen $tar = []; $tar2 = []; $tot = 0; } $i = 0; if (is_array($tar) && $tot) // by Florian Xaver foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, true, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); if (is_array($tar2)) { $htmlOut .= '
'; $htmlOut .= '' . LITMORE . SP . LITPAGES . '' . NL; $htmlOut .= graphHead (LITMORE . SP . LITPAGES, true); foreach ($tar2 as $pn => $cnt) { $htmlOut .= graphLine ($pn, true, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); $htmlOut .= '
'; } ## by Schlaefer //------------------------------------------------------------------------------------------------------------ ## PAGES daily $htmlOut .= graphHead (LITPAGE . SP . LITVIEWS . SP . LITTODAY, true); $pageviews = array (); foreach ($TotalCounter[KEYPAGESTODAYCOUNTER] as $pn => $cnt) { if ($TotalCounter[KEYPAGESTODAYDAY][$pn] === $tcNowDate) $pageviews[$pn] = $cnt; } arsort($pageviews); $tot = array_sum($pageviews); $tar = array_slice($pageviews, 0, $TotalCounterMaxItems); $tar2 = array_slice($pageviews, $TotalCounterMaxItems, $TotalCounterMaxItems); $max = current($tar); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, true, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); if (is_array($tar2)) { $htmlOut .= '
'; $htmlOut .= '' . LITMORE . SP . LITPAGE . SP . LITVIEWS . '' . NL; $htmlOut .= graphHead (LITMORE . SP . LITPAGE . SP . LITVIEWS, true); foreach ($tar2 as $pn => $cnt) { $htmlOut .= graphLine ($pn, true, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); $htmlOut .= '
'; } //------------------------------------------------------------------------------------------------------------ // USERS # TotalCounterEnableUsers if ($TotalCounterEnableUsers == 1) { $htmlOut .= graphHead (LITUSERS, true); arsort($TotalCounter[KEYUSERS]); $tar = array_slice($TotalCounter[KEYUSERS], 0, $TotalCounterMaxItems); $max = current($tar); $tot = array_sum($TotalCounter[KEYUSERS]); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); } //------------------------------------------------------------------------------------------------------------ // LANGUAGES if (defined('MULTILANGUAGE')) { $htmlOut .= graphHead (LITLANGUAGES, true); arsort($TotalCounter[KEYLANGUAGES]); $tar = array_slice($TotalCounter[KEYLANGUAGES], 0, $TotalCounterMaxItems); $max = current($tar); $tot = array_sum($TotalCounter[KEYLANGUAGES]); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); } //------------------------------------------------------------------------------------------------------------ // BROWSERS $htmlOut .= graphHead (LITBROWSERS, true); if (is_array($TotalCounter[KEYBROWSERS])) { arsort ($TotalCounter[KEYBROWSERS]); $tot = array_sum($TotalCounter[KEYBROWSERS]); $tar = array_slice($TotalCounter[KEYBROWSERS], 0, $TotalCounterMaxItems); } else { # should never happen $tar = []; $tot = 0; } $max = current($tar); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); //------------------------------------------------------------------------------------------------------------ // OPERATING SYSTEMS $htmlOut .= graphHead (LITOS, true); if (is_array($TotalCounter[KEYOSES])) { arsort($TotalCounter[KEYOSES]); $tar = array_slice($TotalCounter[KEYOSES], 0, $TotalCounterMaxItems); $tot = array_sum($TotalCounter[KEYOSES]); } else { # should never happen $tar = []; $tot = 0; } $max = current($tar); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); //------------------------------------------------------------------------------------------------------------ // REFERERS $htmlOut .= graphHead (LITREFERERS, true); if (is_array($TotalCounter[KEYREFERERS])) { arsort($TotalCounter[KEYREFERERS]); $tar = array_slice($TotalCounter[KEYREFERERS], 0, $TotalCounterMaxItems); $tot = array_sum($TotalCounter[KEYREFERERS]); } else { # should never happen $tar = []; $tot = 0; } $max = current($tar); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); //------------------------------------------------------------------------------------------------------------ // LOCATIONS $htmlOut .= graphHead (LITLOCATIONS, true); if (is_array($TotalCounter[KEYLOCATIONS])) { arsort($TotalCounter[KEYLOCATIONS]); $tar = array_slice($TotalCounter[KEYLOCATIONS], 0, $TotalCounterMaxItems); $tot = array_sum($TotalCounter[KEYLOCATIONS]); } else { # should never happen $tar = []; $tot = 0; } $max = current($tar); $i = 0; if (is_array($tar)) foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); //------------------------------------------------------------------------------------------------------------ // WEB BOTS $htmlOut .= graphHead (LITWEBBOTS, true); if (is_array($TotalCounter[KEYBOTS])) { arsort($TotalCounter[KEYBOTS]); $tar = array_slice($TotalCounter[KEYBOTS], 0, $TotalCounterMaxItems); $tar2 = array_slice($TotalCounter[KEYBOTS], $TotalCounterMaxItems, $TotalCounterMaxItems); $max = current($tar); $tot = array_sum($TotalCounter[KEYBOTS]); } else { # should never happen $tar = []; $tar2 = []; $tot = 0; } $i = 0; if (is_array($tar)) { foreach ($tar as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } } $htmlOut .= graphHead ('', false); if (is_array($tar2)) { $htmlOut .= '
'; $htmlOut .= '' . LITMORE . SP . LITWEBBOTS . '' . NL; $htmlOut .= graphHead (LITMORE . SP . LITWEBBOTS, true); foreach ($tar2 as $pn => $cnt) { $htmlOut .= graphLine ($pn, false, $cnt, $tot, $max, ++ $i); } $htmlOut .= graphHead ('', false); $htmlOut .= '
'; } //------------------------------------------------------------------------------------------------------------ // Downloads if ($TotalCounterEnableDownload == 1) { $htmlOut .= graphHead (LITFILE . SP . LITDOWNLOADS, true); arsort($TotalCounterDownloads); $max = count($TotalCounterDownloads); $tot = array_sum($TotalCounterDownloads); $i = 0; if (is_array($TotalCounterDownloads)) { for ($row = 0; $row < $max; $row++) { $tablerow = each($TotalCounterDownloads); $value = $tablerow['value']; $htmlOut .= '' . ($TotalCounterShowNumbers ? TD1 . ++ $i . '.' : '') . '' . $tablerow['key'] . '' . '' . ' ' . $value . '' . '' . NL; } } $htmlOut .= graphHead ('', false); } //------------------------------------------------------------------------------------------------------------ // Time statistics ## by MateuszCzaplinski foreach( $TotalCounterTimeBins as $nBin=>$aBin ) { $name = $aBin[GRAPHNAME]; $dateFmt = $aBin[DATEFMT]; $htmlOut .= BR . '
' . NL . '

' . $name. '

' . NL . '' . NL . '' . '' . NL . '' . NL; $rows = []; # initialise $maxcount = max($TotalCounter[$nBin]); $direction = 'width'; $maxnr = $aBin[GRAPHMAX]; $atom = $aBin[GRAPHATOM]; for ($nr = 0; $nr < $maxnr; $nr++) { if (!isset ($TotalCounter[$nBin][$nr])) continue; # does not exist $count = $TotalCounter[$nBin][$nr]; $row = ''; # initialise if (($nBin !== PREVIOUSYEARS) || ($nBin === PREVIOUSYEARS && $count > 0)) { # should improve this to cater for varying number of days in a month // Directly evaluate the format function to display date column $output = $dateFmt(time(), $atom, $maxnr, $nr); $row .= "' . NL; $row .= "" . NL; # display count column $row .= "' . NL; $rows[] = $row; } } # end for $nr $htmlOut .= ''.implode(''.NL.'',$rows).'
' . $name . ' ' . LITCOUNT . '
 " . $output . '" . NL; if ($maxcount == 0) { $barWidth = 0; } else { $barWidth = Round(1 + BARMAXWIDTH * ($count / $maxcount)); } $row .= "
" . NL; $row .= "
" . number_format($count) . '
' . NL; // 1.10.0 } //------------------------------------------------------------------------------------------------------------ if ($totalCounter_debugOn) { $htmlOut .= '

Messages

' . NL . end ($MessagesFmt [MSGFMTID]) . NL; } $htmlOut .= '

TotalCounter ' . TOTALCOUNTERV . '

' . NL; \PrintFmt($pagename, array ( # PmWiki function & $PageStartFmt, $htmlOut, & $PageEndFmt )); if ($TotalCounterLogOn) { $fclosestatus = fclose($logFileHandle); if ($fclosestatus === false) { recordErrorMessage ('fclose failed', error_get_last()); if ($totalCounter_debugOn) \Abort (end ($MessagesFmt [MSGFMTID])); } } return; } # end HandleTotalCounter // //===================================================================================================================== function HandleTotalCounterCheck(string $pagename, string $auth = 'admin') { // handle PmWiki action=totalcountercheck and totalcounterfix global $action, $statFileName; global $TotalCounterTimeBins, $TotalCounterBinsFmt, $TotalCounterEnableUsers; global $PageStartFmt, $PageEndFmt, $MessagesFmt, $totalCounter_debugOn, $TotalCounterLogOn, $logFileHandle, $tcPageExists, $tcNowDateTime; $page = \RetrieveAuthPage($pagename, $auth); # PmWiki function if (!$page) { \Abort ('?you are not permitted to perform this action: "' . $action . '"'); # PmWiki function } if (!$tcPageExists) { \Abort ('?action "'. $action . '" is not available on a non-existent page "' . $pagename . '"'); # PmWiki function } if (!file_exists($statFileName)) { \Abort ('?"' . $statFileName . '" TotalCounter file does not exist'); # PmWiki function } $bupStatFilename = $statFileName . '.' . date ('Y-m-dTH_i_s', $tcNowDateTime); # with current date time appended $TotalCounterFileContents = tc_file_get_contents($statFileName); if (FALSE === $TotalCounterFileContents) { recordErrorMessage ('tc_file_get_contents failed', error_get_last()); \Abort ('?' . end ($MessagesFmt [MSGFMTID])); # failed to read file, lets get out of here } $TotalCounterStats = unserialize($TotalCounterFileContents, ['allowed_classes' => false]); if (FALSE === $TotalCounterStats) { recordErrorMessage ('tc_file unserialize failed', error_get_last()); \Abort ('?' . end ($MessagesFmt [MSGFMTID])); # failed to unserialize file, lets get out of here } # $retVal = '

Total Counter ' . LITSTATISTICS. '

' . NL; $retVal .= tc_find_invalid_pages ($TotalCounterStats [KEYREFERERS], KEYREFERERS, '#^(?:[a-z0-9.-]+)(?:\?n=[a-z0-9.]+)?$#i'); $retVal .= tc_find_ne_pages ($TotalCounterStats [KEYPAGES], KEYPAGES); $retVal .= tc_find_ne_pages ($TotalCounterStats [KEYPAGESTODAYCOUNTER], KEYPAGESTODAYCOUNTER); $retVal .= tc_find_ne_pages ($TotalCounterStats [KEYPAGESTODAYDAY], KEYPAGESTODAYDAY); # $retVal .= '

TotalCounter ' . TOTALCOUNTERV . SP . $bupStatFilename . '

' . NL; if ($action = ACTIONDATAFIX) { } \PrintFmt($pagename, array ( # PmWiki function & $PageStartFmt, $retVal, & $PageEndFmt )); if ($TotalCounterLogOn) { $fclosestatus = fclose($logFileHandle); if ($fclosestatus === false) { recordErrorMessage ('fclose failed', error_get_last()); if ($totalCounter_debugOn) \Abort (end ($MessagesFmt [MSGFMTID])); } } return; } # end HandleTotalCounterCheck //===================================================================================================================== function tc_find_ne_pages (array $countedPages, string $countType) { # find non-existant pages $existentPagesCount = 0; $nonExistentPagesCount = 0; $htmlOut = ''; #initialise ksort ($countedPages); $currentGrpPart = ''; # initialise $processedArray = []; # initialise foreach ($countedPages as $pageKey => $pageVal ) { $nameParts = explode('.', $pageKey); # separate groupname and pagename $grpPart = $nameParts[0]; $pgePart = $nameParts[1]; if (\PageExists($pageKey)) { # PmWiki function $existentPagesCount ++; continue; # skip to next iteration } if ($currentGrpPart !== $grpPart) { $htmlOut .= BR . $grpPart . ':  '; $grpPartLen = strlen($grpPart); if ($grpPartLen < 24) $htmlOut .= str_repeat (' ', 24 - $grpPartLen); $currentGrpPart = $grpPart; } $htmlOut .= $pgePart . ' (' . (is_numeric ($pageVal) ? number_format ($pageVal): $pageVal) . ') '; $nonExistentPagesCount ++; } # end foreach $retVal = ''; # initialise $retVal .= '

Non-existent "' . $countType . '" pages

' . NL; $retVal .= 'Non-existent pages: ' . number_format ($nonExistentPagesCount) . BR; $retVal .= 'Existent pages: ' . number_format ($existentPagesCount) . BR ; $retVal .= '
' . NL; $retVal .= '
' . NL . $htmlOut . NL . '
' . NL; $retVal .= '
' . NL; return $retVal; } # end tc_find_ne_pages //===================================================================================================================== function tc_find_invalid_pages (array $countedPages, string $countType, string $goodPattern) { # find non-existant pages based on $goodPattern regex $retVal = ''; # initialise $htmlOut = ''; #initialise $invalidPagesCount = 0; ksort ($countedPages); foreach ($countedPages as $pageKey => $pageVal ) { if (1 !== preg_match ($goodPattern, strval($pageKey))) { $invalidPagesCount ++; $htmlOut .= '[' . $pageKey . '] "'. $pageVal . '"' . BR; } } if ($invalidPagesCount > 0) { $retVal .= '

Invalid values in "' . $countType . '" counts

' . NL; $retVal .= 'Invalid values : ' . number_format ($invalidPagesCount) . BR; $retVal .= '
' . NL; $retVal .= '
' . NL . $htmlOut . NL . '
' . NL; $retVal .= '
' . NL; } return $retVal; } # end tc_find_invalid_pages //===================================================================================================================== function tc_file_get_contents (string $getFileName) { \Lock(1); # acquire shared lock $contents = file_get_contents ($getFileName); \Lock(0); # release lock return $contents; } # end function tc_file_get_contents //===================================================================================================================== ## by MateuszCzaplinski ## Manages an array of counters, each for a specified time interval. ## In $TotalCounter[$name] array there are $max counters. Each counter ## is for time interval of $atom length. ## Note: if $atom is a number, it is a length of interval measured in seconds. ## If $atom is a string, it means date ($atom) is executed and the result is the index of an interval. ## NOTE: See TODO below function TCbins(string $name, int $max, $atom, int $tcNowDateTime) { global $TotalCounter; $lastTS = $TotalCounter[KEYLASTTIMESTAMP]; if( $tcNowDateTime < $lastTS ) return; // some error? if( !$lastTS ) $TotalCounter[$name] = array_fill(0,$max,0); if( is_string($atom) ) { $diff = intval (date ($atom,$tcNowDateTime)) - intval (date ($atom,$lastTS)); if( $diff < 0 ) $diff += $max; # TODO: handle time delta > $max # Until fixed, if the site has no visitor for about a # year, statistics will get falsified (empty years will compress) } else $diff = intval ($tcNowDateTime/$atom) - intval ($lastTS/$atom); if( $diff < 0 ) return; if( $diff > 0 ) { $a = array_slice($TotalCounter[$name], $diff, max(0,$max-$diff)); if(!$a) $a = array(); $a = array_pad($a, $max, 0); $TotalCounter[$name] = $a; } incrementCount($name, $max-1); // put our visit in last bin } # end function TCbins //===================================================================================================================== // generate graph head and tail function graphHead (string $headline='', bool $heading=true):string { global $TotalCounterShowNumbers; $retVal = ''; # initialise if ($heading) { # create table heading $retVal .= BR . '

' . $headline . '

' . NL; $retVal .= '' . NL; $retVal .= '' . '' . '' . NL; $retVal .= '' . NL; } else { # create table footer $retVal .= '
' . $headline . ' ' . LITPERCENT . '' . LITCOUNT . '
' . NL; } return $retVal; } # end function graphHead //===================================================================================================================== // generate graph line function graphLine (string $pgName, bool $bpage, int $pgCount, int $pgTotal, int $pgMax, int $linenr) :string { global $TotalCounterShowNumbers; $retVal = '' . NL; if ($TotalCounterShowNumbers) { $retVal .= '' . $linenr . '.'; } $retVal .= ''; if ($bpage) { $retVal .= "$pgName"; } else { $retVal .= $pgName; } $retVal .= ' ' . NL; if ($pgTotal == 0) { $perCent = 0; } elseif (0 == $perCent = Round(100 * $pgCount / $pgTotal)) { $perCent = Round(100 * $pgCount / $pgTotal, 1); } $retVal .= '' . $perCent . '%' . NL; if ($pgMax == 0) { $barWidth = 0; } else { $barWidth = Round(BARMAXWIDTH * $pgCount / $pgMax); } $retVal .= '
'; $retVal .= ' ' . number_format ($pgCount) . ''; return $retVal . '' . NL; } # end function graphLine //===================================================================================================================== // https://www.exakat.io/en/prevent-multiple-php-scripts-at-the-same-time/ // https://stackoverflow.com/questions/6967553/php-flock-alternative // Modified (breaks and returns 0 on failure, // or returns 1 on success) by Mateusz Czaplinski, 22.01.2008 function acquireLock(string $wp) { //Check if lock doesn't exist or our target is unwritable global $TotalCounterLogOn; if (!is_writable($wp)) return FALSE; $LckFileName = "$wp.l"; \Lock(2); # lock exclusive; stop race condition if(file_exists($LckFileName)) { $retVal = FALSE; } else { //create the lock - hide warnings and pass empty if already created from racing $retVal = @fopen($LckFileName, 'x'); if (($retVal === false) and ($TotalCounterLogOn)) { // Retrieve the last error that occurred recordErrorMessage ('fopen failed', error_get_last()); } } \Lock(0); # release lock return $retVal; } //===================================================================================================================== function dblock(string $wp):bool { global $TotalCounterEnableChmods; //Check for lockfile handle - if empty , another process raced the lock so report a failure $ftw = acquireLock($wp); if( $ftw === FALSE) return FALSE; if($TotalCounterEnableChmods) chmod($wp, 0444); //set the target file to read-only $fwStatus = fwrite($ftw, 'lock'); //write the lockfile with 4bytes if($TotalCounterEnableChmods) chmod("$wp.l", 0444); //set the lockfile to read only (OPTIONAL) fclose($ftw); //close our lockfile clearstatcache(); //Clear the stat cache return TRUE; } //===================================================================================================================== // Note: don't call it if 'dblock()' returned 0 ! function dbexport_unlock(string $wp, $data, string $meth) { global $TotalCounterEnableChmods; if($TotalCounterEnableChmods) chmod($wp, 0666); //Set the target file to read+write //Write the passed string to the target file then close \Lock(2); # acquire exclusive lock fwrite($ftw = fopen($wp, $meth), $data); fclose($ftw); \Lock(0); # release lock //Validate the written data using a string comparison $check = tc_file_get_contents($wp); # note there is a race condition here if ($check !== $data) echo "Data Mismatch - Locking FAILED!" . BR; chmod("$wp.l", 0666); //Set the lockfile to read+write (OPTIONAL) unlink("$wp.l"); //Release the lockfile by removing it } //===================================================================================================================== function dblock_remove_stale(string $wp) { global $TotalCounterLogOn; $modTime = @filemtime("$wp.l"); if (false === $modTime) { if ($TotalCounterLogOn) { // Retrieve the last error that occurred recordErrorMessage ('filemtime failed on ' . "$wp.l", error_get_last()); } return; } // 75 minutes - to make absolutely sure we're not tricked by Daylight // Savings on Windows - see https://www.php.net/manual/en/function.stat.php#58404 if ($modTime+(75*60) < time()) unlink("$wp.l"); } //===================================================================================================================== function detectWebBot (string $HttpUserAgent): string { // find web bot https://developers.whatismybrowser.com/useragents/explore/, https://bigdata-madesimple.com/top-50-open-source-web-crawlers-for-data-mining/ $tc_bot = NULLUSERAGENT; $tc_bot_info = ''; # initialise if (preg_match('/ia_archiver/i', $HttpUserAgent)) {$tc_bot = 'Alexa'; $tc_bot_info = 'https://support.alexa.com/hc/en-us/articles/200450194-Alexa-s-Web-and-Site-Audit-Crawlers';} elseif (preg_match('/360Spider/i', $HttpUserAgent)) {$tc_bot = '360Spider';} # elseif (preg_match('/A6-Indexer/i', $HttpUserAgent)) {$tc_bot = 'A6'; $tc_bot_info = 'http://www.a6corp.com/a6-web-scraping-policy/';} elseif (preg_match('/Abonti/i', $HttpUserAgent)) {$tc_bot = 'Abonti'; $tc_bot_info = 'http://www.abonti.com';} elseif (preg_match('/acebookexternalhit/i', $HttpUserAgent)) {$tc_bot = 'Facebook External hit'; $tc_bot_info = 'http://www.facebook.com/externalhit_uatext.php';} elseif (preg_match('/Adsbot/i', $HttpUserAgent)) {$tc_bot = 'Adsbot'; $tc_bot_info = 'https://seostar.co/robot/';} elseif (preg_match('/AhrefsBot/i', $HttpUserAgent)) {$tc_bot = 'Ahrefs'; $tc_bot_info = 'https://ahrefs.com/robot/';} elseif (preg_match('/aiohttp/i', $HttpUserAgent)) {$tc_bot = 'aiohttp';} # elseif (preg_match('/ALittle/i', $HttpUserAgent)) {$tc_bot = 'ALittle Client';} # elseif (preg_match('/Amazonbot/i', $HttpUserAgent)) {$tc_bot = 'Amazonbot'; $tc_bot_info = 'https://developer.amazon.com/support/amazonbot';} elseif (preg_match('/AntBot/i', $HttpUserAgent)) {$tc_bot = 'Ant'; $tc_bot_info = 'http://www.ant.com';} elseif (preg_match('/AnyEvent/i', $HttpUserAgent)) {$tc_bot = 'AnyEvent'; $tc_bot_info = 'http://software.schmorp.de/pkg/AnyEvent';} elseif (preg_match('/AppEngine-Google/i', $HttpUserAgent)) {$tc_bot = 'AppEngine-Google'; $tc_bot_info = 'http://code.google.com/appengine';} elseif (preg_match('/applebot/i', $HttpUserAgent)) {$tc_bot = 'Applebot'; $tc_bot_info = 'https://support.apple.com/en-us/HT204683';} elseif (preg_match('/archive.org_bot/i', $HttpUserAgent)) {$tc_bot = 'Web archive'; $tc_bot_info = 'https://webarchive.jira.com/wiki/display/ARIH/Robots+Exclusion+Protocol';} elseif (preg_match('/ask jeeves/i', $HttpUserAgent)) {$tc_bot = 'Ask Jeeves';} # elseif (preg_match('/AwarioBot/i', $HttpUserAgent)) {$tc_bot = 'AwarioBot'; $tc_bot_info = 'https://awario.com/bots.html';} elseif (preg_match('/AwarioSmartBot/i', $HttpUserAgent)) {$tc_bot = 'AwarioBot'; $tc_bot_info = 'https://awario.com/bots.html';} elseif (preg_match('/BacklinksExtendedBot/i', $HttpUserAgent)) {$tc_bot = 'BacklinksExtendedBot';} # elseif (preg_match('/baiduspider/i', $HttpUserAgent)) {$tc_bot = 'Baidu'; $tc_bot_info = 'http://www.baidu.com/search/spider.html';} elseif (preg_match('/becomebot/i', $HttpUserAgent)) {$tc_bot = 'Become';} # elseif (preg_match('/bibalex.org_bot/i', $HttpUserAgent)) {$tc_bot = 'Bibalex'; $tc_bot_info = 'http://archive.bibalex.org/bot/';} elseif (preg_match('/bingbot/i', $HttpUserAgent)) {$tc_bot = 'Bing'; $tc_bot_info = 'http://www.bing.com/bingbot.htm';} elseif (preg_match('/BitSightBot/i', $HttpUserAgent)) {$tc_bot = 'BitSightBot';} # elseif (preg_match('/BLEXBot/i', $HttpUserAgent)) {$tc_bot = 'WebMeUp'; $tc_bot_info = 'http://webmeup-crawler.com/';} elseif (preg_match('/Bytespider/i', $HttpUserAgent)) {$tc_bot = 'Bytespider'; $tc_bot_info = 'spider-feedback@bytedance.com';} elseif (preg_match('/CATExplorador/i', $HttpUserAgent)) {$tc_bot = 'CATExplorador'; $tc_bot_info = 'https://domini.cat/catexplorador/';} elseif (preg_match('/CCBot/i', $HttpUserAgent)) {$tc_bot = 'Common Crawl'; $tc_bot_info = 'http://commoncrawl.org/faqs/';} elseif (preg_match('/CensysInspect/i', $HttpUserAgent)) {$tc_bot = 'Censys Inspect'; $tc_bot_info = 'https://about.censys.io/';} elseif (preg_match('/centuryb/i', $HttpUserAgent)) {$tc_bot = 'centuryb'; $tc_bot_info = 'centuryb.o.t9@gmail.com';} elseif (preg_match('/ChatGPT-User/i', $HttpUserAgent)) {$tc_bot = 'ChatGPT'; $tc_bot_info = 'https://openai.com/bot';} elseif (preg_match('/CheckMarkNetwork/i', $HttpUserAgent)) {$tc_bot = 'CheckMark Network'; $tc_bot_info = 'http://www.checkmarknetwork.com/spider.html';} elseif (preg_match('/Cincraw/i', $HttpUserAgent)) {$tc_bot = 'Cincraw'; $tc_bot_info = 'http://cincrawdata.net/bot/';} elseif (preg_match('/ClaudeBot/i', $HttpUserAgent)) {$tc_bot = 'ClaudeBot'; $tc_bot_info = 'claudebot@anthropic.com';} elseif (preg_match('/coccocbot/i', $HttpUserAgent)) {$tc_bot = 'Coccocbot'; $tc_bot_info = 'http://help.coccoc.com/searchengine';} elseif (preg_match('/colly/i', $HttpUserAgent)) {$tc_bot = 'Colly'; $tc_bot_info = 'https://github.com/gocolly/colly/v2';} elseif (preg_match('/Crawlbot\/Nutch/i', $HttpUserAgent)) {$tc_bot = 'Crawlbot/Nutch'; $tc_bot_info = 'https://nutch.apache.org/';} elseif (preg_match('/Crawlson/i', $HttpUserAgent)) {$tc_bot = 'Crawlson'; $tc_bot_info = 'https://www.crawlson.com/about';} elseif (preg_match('/Dalvik/i', $HttpUserAgent)) {$tc_bot = 'Dalvik'; } # elseif (preg_match('/DataForSeoBot/i', $HttpUserAgent)) {$tc_bot = 'DataForSeoBot'; $tc_bot_info = 'https://dataforseo.com/dataforseo-bot';} elseif (preg_match('/Daum/i', $HttpUserAgent)) {$tc_bot = 'Daum'; $tc_bot_info = 'http://cs.daum.net/faq/15/4118.html?faqId=28966';} elseif (preg_match('/deepnoc/i', $HttpUserAgent)) {$tc_bot = 'Deepnoc'; $tc_bot_info = 'https://deepnoc.com/bot';} elseif (preg_match('/Discordbot/i', $HttpUserAgent)) {$tc_bot = 'Discordbot'; $tc_bot_info = 'https://discordapp.com';} elseif (preg_match('/DIVD/i', $HttpUserAgent)) {$tc_bot = 'DIVD'; $tc_bot_info = 'https://csirt.divd.nl/';} elseif (preg_match('/Domains Project/i', $HttpUserAgent)) {$tc_bot = 'Domains Project'; $tc_bot_info = 'https://domainsproject.org/';} elseif (preg_match('/DomainStatsBot/i', $HttpUserAgent)) {$tc_bot = 'DomainStatsBot'; $tc_bot_info = 'https://domainstats.com/pages/our-bot';} elseif (preg_match('/dotbot/i', $HttpUserAgent)) {$tc_bot = 'DotBot'; $tc_bot_info = 'http://www.opensiteexplorer.org/dotbot';} elseif (preg_match('/DuckDuckGo-Favicons-Bot/i', $HttpUserAgent)) {$tc_bot = 'DuckDuckGo-Favicons-Bot'; $tc_bot_info = 'http://duckduckgo.com';} elseif (preg_match('/DuckDuckBot/i', $HttpUserAgent)) {$tc_bot = 'DuckDuckBot'; $tc_bot_info = 'https://duckduckgo.com/duckduckbot';} elseif (preg_match('/DuckAssistBot/i', $HttpUserAgent)) {$tc_bot = 'DuckAssistBot'; $tc_bot_info = 'http://duckduckgo.com/duckassistbot.html';} elseif (preg_match('/Dy robot/i', $HttpUserAgent)) {$tc_bot = 'Dy robot';} # elseif (preg_match('/Embarcadero/i', $HttpUserAgent)) {$tc_bot = 'Embarcadero URI Client';} # elseif (preg_match('/EntferBot/i', $HttpUserAgent)) {$tc_bot = 'EntferBot'; $tc_bot_info = 'https://entfer.com';} elseif (preg_match('/exabot/i', $HttpUserAgent)) {$tc_bot = 'Exalead';} # elseif (preg_match('/Expanse/i', $HttpUserAgent)) {$tc_bot = 'Expanse'; $tc_bot_info = 'scaninfo@expanseinc.com';} elseif (preg_match('/facebookexternalhit/i', $HttpUserAgent)) {$tc_bot = 'Facebook'; $tc_bot_info = 'http://www.facebook.com/externalhit_uatext.php';} elseif (preg_match('/fast/i', $HttpUserAgent)) {$tc_bot = 'Fast/Alltheweb';} # elseif (preg_match('/gigabot/i', $HttpUserAgent)) {$tc_bot = 'Gigabot'; $tc_bot_info = 'http://www.gigablast.com/spider.html';} elseif (preg_match('/GigablastOpenSource/i', $HttpUserAgent)) {$tc_bot = 'Gigablast'; $tc_bot_info = 'https://github.com/gigablast/open-source-search-engine';} elseif (preg_match('/Go-http-client/i', $HttpUserAgent)) {$tc_bot = 'Go-http-client'; $tc_bot_info = 'Go-http-client';} elseif (preg_match('/googlebot/i', $HttpUserAgent)) {$tc_bot = 'Google'; $tc_bot_info = 'http://www.google.com/bot.html';} elseif (preg_match('/Google-Apps-Script/i', $HttpUserAgent)) {$tc_bot = 'Google-Apps-Script'; $tc_bot_info = 'https://script.google.com';} elseif (preg_match('/GPTBot/i', $HttpUserAgent)) {$tc_bot = 'GPTBot'; $tc_bot_info = 'https://openai.com/gptbot';} elseif (preg_match('/Grammarly/i', $HttpUserAgent)) {$tc_bot = 'Grammarly'; $tc_bot_info = 'http://www.grammarly.com';} elseif (preg_match('/hgfAlphaXCrawl/i', $HttpUserAgent)) {$tc_bot = 'hgfAlphaXCrawl'; $tc_bot_info = 'https://www.fim.uni-passau.de/data-science/forschung/open-search';} elseif (preg_match('/InfoTigerBot/i', $HttpUserAgent)) {$tc_bot = 'InfoTigerBot'; $tc_bot_info = 'https://infotiger.com/bot';} elseif (preg_match('/InternetMeasurement/i', $HttpUserAgent)) {$tc_bot = 'InternetMeasurement'; $tc_bot_info = 'https://internet-measurement.com/';} elseif (preg_match('/James BOT/i', $HttpUserAgent)) {$tc_bot = 'CognitiveSEO'; $tc_bot_info = 'http://cognitiveseo.com/bot.html';} elseif (preg_match('/libwww-perl/i', $HttpUserAgent)) {$tc_bot = 'Libwww-perl'; $tc_bot_info = 'libwww-perl';} elseif (preg_match('/linkdexbot/i', $HttpUserAgent)) {$tc_bot = 'Linkdex'; $tc_bot_info = 'http://www.linkdex.com/bots/';} elseif (preg_match('/linkfluence/i', $HttpUserAgent)) {$tc_bot = 'Linkfluence'; $tc_bot_info = 'https://linkfluence.com/';} elseif (preg_match('/Mediapartners-Google/i', $HttpUserAgent)) {$tc_bot = 'Google'; $tc_bot_info = 'https://support.google.com/webmasters/answer/1061943?hl=en';} elseif (preg_match('/AdsBot-Google/i', $HttpUserAgent)) {$tc_bot = 'GoogleAdsBot';} # elseif (preg_match('/grub-client/i', $HttpUserAgent)) {$tc_bot = 'Grub';} # elseif (preg_match('/ImagesiftBot/i', $HttpUserAgent)) {$tc_bot = 'ImagesiftBot'; $tc_bot_info = 'https://imagesift.com';} elseif (preg_match('/java/i', $HttpUserAgent)) {$tc_bot = 'Java';} # elseif (preg_match('/libcurl/i', $HttpUserAgent)) {$tc_bot = 'cURL';} # elseif (preg_match('/curl/i', $HttpUserAgent)) {$tc_bot = 'cURL';} # elseif (preg_match('/slurp@inktomi.com/i', $HttpUserAgent)) {$tc_bot = 'Inktomi';} # elseif (preg_match('/Keybot Translation-Search-Machine/i', $HttpUserAgent)) {$tc_bot = 'Keybot Translation-Search-Machine';} # elseif (preg_match('/Knowledge AI/i', $HttpUserAgent)) {$tc_bot = 'Knowledge AI';} # elseif (preg_match('/Linespider/i', $HttpUserAgent)) {$tc_bot = 'Linespider'; $tc_bot_info = 'https://lin.ee/4dwXkTH';} elseif (preg_match('/ltx71/i', $HttpUserAgent)) {$tc_bot = 'ltx71'; $tc_bot_info = 'http://ltx71.com/';} elseif (preg_match('/Mail\.RU_Bot/i', $HttpUserAgent)) {$tc_bot = 'mail.ru'; $tc_bot_info = 'http://go.mail.ru/help/robots';} elseif (preg_match('/marginalia/i', $HttpUserAgent)) {$tc_bot = 'marginalia'; $tc_bot_info = 'https://search.marginalia.nu';} elseif (preg_match('/meanpathbot/i', $HttpUserAgent)) {$tc_bot = 'meanpath';} # elseif (preg_match('/MJ12bot/i', $HttpUserAgent)) {$tc_bot = 'Majestic'; $tc_bot_info = 'http://www.majestic12.co.uk/bot.php';} elseif (preg_match('/MojeekBot/i', $HttpUserAgent)) {$tc_bot = 'MojeekBot'; $tc_bot_info = 'https://www.mojeek.com/bot.html';} elseif (preg_match('/msnbot/i', $HttpUserAgent)) {$tc_bot = 'MSN';} # elseif (preg_match('/NerdyBot/i', $HttpUserAgent)) {$tc_bot = 'Nerdy data';} # elseif (preg_match('/NetcraftSurveyAgent/i', $HttpUserAgent)) {$tc_bot = 'Netcraft'; $tc_bot_info = 'info@netcraft.com';} elseif (preg_match('/Netcraft Web Server Survey/i', $HttpUserAgent)) {$tc_bot = 'Netcraft'; } # elseif (preg_match('/NetpeakCheckerBot/i', $HttpUserAgent)) {$tc_bot = 'NetpeakCheckerBot'; $tc_bot_info = 'https://netpeaksoftware.com/checker';} elseif (preg_match('/NetSystemsResearch/i', $HttpUserAgent)) {$tc_bot = 'NetSystemsResearch'; $tc_bot_info = 'https://netsystemsresearch.com';} elseif (preg_match('/Neevabot/i', $HttpUserAgent)) {$tc_bot = 'Neevabot'; $tc_bot_info = 'https://neeva.com/neevabot';} elseif (preg_match('/Nimo Software/i', $HttpUserAgent)) {$tc_bot = 'Nimo Software HTTP Retriever';} # elseif (preg_match('/NLNZ_IAHarvester/i', $HttpUserAgent)) {$tc_bot = 'NLNZ_IAHarvester'; $tc_bot_info = 'https://natlib.govt.nz/publishers-and-authors/web-harvesting/domain-harvest';} elseif (preg_match('/node-fetch/i', $HttpUserAgent)) {$tc_bot = 'Node-fetch'; $tc_bot_info = 'https://github.com/bitinn/node-fetch';} elseif (preg_match('/OAI-SearchBot/i', $HttpUserAgent)) {$tc_bot = 'OpenAI'; $tc_bot_info = 'https://openai.com/searchbot';} elseif (preg_match('/Owler/i', $HttpUserAgent)) {$tc_bot = 'Owler'; $tc_bot_info = 'https://ows.eu/owler';} elseif (preg_match('/Pandalytics/i', $HttpUserAgent)) {$tc_bot = 'Pandalytics'; $tc_bot_info = 'https://domainsbot.com/pandalytics/';} elseif (preg_match('/panscient/i', $HttpUserAgent)) {$tc_bot = 'Panscient'; $tc_bot_info = 'panscient.com';} elseif (preg_match('/PerplexityBot/i', $HttpUserAgent)) {$tc_bot = 'PerplexityBot'; $tc_bot_info = 'https://docs.perplexity.ai/docs/perplexity-bot';} elseif (preg_match('/petalbot/i', $HttpUserAgent)) {$tc_bot = 'Petalbot'; $tc_bot_info = 'https://petalsearch.com/';} elseif (preg_match('/PHP-Curl-Class/i', $HttpUserAgent)) {$tc_bot = 'PHP-Curl-Class'; $tc_bot_info = 'https://github.com/php-curl-class/php-curl-class';} elseif (preg_match('/Pinterest/i', $HttpUserAgent)) {$tc_bot = 'Pinterest'; $tc_bot_info = 'http://www.pinterest.com';} elseif (preg_match('/PocketParser/i', $HttpUserAgent)) {$tc_bot = 'PocketParser'; $tc_bot_info = 'https://getpocket.com/pocketparser_ua';} elseif (preg_match('/python-requests/i', $HttpUserAgent)) {$tc_bot = 'Python-requests';} // # python-requests elseif (preg_match('/python-urllib/i', $HttpUserAgent)) {$tc_bot = 'Python-urllib';} // # python-urllib elseif (preg_match('/Qwantify/i', $HttpUserAgent)) {$tc_bot = 'Qwantify'; $tc_bot_info = 'https://www.qwant.com/';} elseif (preg_match('/RepoLookoutBot/i', $HttpUserAgent)) {$tc_bot = 'RepoLookoutBot'; $tc_bot_info = 'abuse reports to abuse@repo-lookout.org';} elseif (preg_match('/SafeDNSBot/i', $HttpUserAgent)) {$tc_bot = 'SafeDNSBot'; $tc_bot_info = 'https://www.safedns.com/searchbot';} elseif (preg_match('/Scrapy/i', $HttpUserAgent)) {$tc_bot = 'Scrapy'; $tc_bot_info = 'https://scrapy.org';} elseif (preg_match('/scooter/i', $HttpUserAgent)) {$tc_bot = 'Altavista'; $tc_bot_info = 'deprecated http://www.siteware.ch/webresources/useragents/spiders/altavista.html';} elseif (preg_match('/Screaming/i', $HttpUserAgent)) {$tc_bot = 'Screaming Frog SEO Spider'; $tc_bot_info = 'https://www.screamingfrog.co.uk/seo-spider/';} elseif (preg_match('/SISTRIX/i', $HttpUserAgent)) {$tc_bot = 'SISTRIX'; $tc_bot_info = 'http://crawler.007ac9.net/';} elseif (preg_match('/Crawler/i', $HttpUserAgent)) {$tc_bot = 'SISTRIX'; $tc_bot_info = 'http://crawler.007ac9.net/';} elseif (preg_match('/SeekportBot/i', $HttpUserAgent)) {$tc_bot = 'SeekportBot'; $tc_bot_info = 'https://bot.seekport.com';} elseif (preg_match('/SemrushBot/i', $HttpUserAgent)) {$tc_bot = 'SemrushBot'; $tc_bot_info = 'http://www.semrush.com/bot.html';} elseif (preg_match('/SenutoBot/i', $HttpUserAgent)) {$tc_bot = 'SenutoBot'; $tc_bot_info = 'https://www.senuto.com/';} elseif (preg_match('/SEOkicks/i', $HttpUserAgent)) {$tc_bot = 'SEOkicks'; $tc_bot_info = 'https://www.seokicks.de/robot.html';} elseif (preg_match('/SeobilityBot/i', $HttpUserAgent)) {$tc_bot = 'SeobilityBot'; $tc_bot_info = 'https://www.seobility.net/sites/bot.html';} elseif (preg_match('/serpstatbot/i', $HttpUserAgent)) {$tc_bot = 'Serpstatbot'; $tc_bot_info = 'https://serpstatbot.com/; abuse@serpstatbot.com';} elseif (preg_match('/SeznamBot/i', $HttpUserAgent)) {$tc_bot = 'SeznamBot'; $tc_bot_info = 'http://napoveda.seznam.cz/en/seznambot-intro/';} elseif (preg_match('/Sidetrade/i', $HttpUserAgent)) {$tc_bot = 'Sidetrade indexer bot'; } # elseif (preg_match('/SiteExplorer/i', $HttpUserAgent)) {$tc_bot = 'Site Explorer'; $tc_bot_info = 'http://siteexplorer.info/';} elseif (preg_match('/SkypeUriPreview/i', $HttpUserAgent)) {$tc_bot = 'Skype Uri Preview'; $tc_bot_info = 'skype-url-preview@microsoft.com';} elseif (preg_match('/Slackbot-LinkExpanding/i', $HttpUserAgent)) {$tc_bot = 'Slackbot-LinkExpanding'; $tc_bot_info = 'https://api.slack.com/robots';} elseif (preg_match('/snapchat/i', $HttpUserAgent)) {$tc_bot = 'Snapchat URL Preview Service'; $tc_bot_info = 'https://developers.snap.com/robots';} elseif (preg_match('/Sogou/i', $HttpUserAgent)) {$tc_bot = 'Sogou web spider'; $tc_bot_info = 'http://www.sogou.com/docs/help/webmasters.htm#07';} elseif (preg_match('/SpiceworksAgentShell/i', $HttpUserAgent)) {$tc_bot = 'Spiceworks Agent Shell'; $tc_bot_info = 'https://community.spiceworks.com/support/inventory-online/docs/deploy-agent';} elseif (preg_match('/SurdotlyBot/i', $HttpUserAgent)) {$tc_bot = 'SurdotlyBot'; $tc_bot_info = ' http://sur.ly/bot.html';} elseif (preg_match('/synapse/i', $HttpUserAgent)) {$tc_bot = 'Synapse';} # elseif (preg_match('/t3versionsBot/i', $HttpUserAgent)) {$tc_bot = 'T3versionsBot'; $tc_bot_info = 'https://www.t3versions.com/bot';} elseif (preg_match('/TenMillionDomainsBot/i', $HttpUserAgent)) {$tc_bot = 'TenMillionDomainsBot'; $tc_bot = 'https://github.com/tonywangcn/ten-million-domains';} elseif (preg_match('/ThinkChaos/i', $HttpUserAgent)) {$tc_bot = 'ThinkChaos'; } # elseif (preg_match('/Timpibot/i', $HttpUserAgent)) {$tc_bot = 'Timpibot'; $tc_bot_info = 'http://www.timpi.io';} elseif (preg_match('/Turnitin/i', $HttpUserAgent)) {$tc_bot = 'Turnitin'; $tc_bot_info = 'https://bit.ly/2UvnfoQ';} elseif (preg_match('/Twisted PageGetter/i', $HttpUserAgent)) {$tc_bot = 'Twisted PageGetter'; $tc_bot_info = 'https://twistedmatrix.com/trac/';} elseif (preg_match('/Twitterbot/i', $HttpUserAgent)) {$tc_bot = 'Twitterbot'; } # elseif (preg_match('/unirest-java/i', $HttpUserAgent)) {$tc_bot = 'unirest-java'; } # elseif (preg_match('/W3C-checklink/i', $HttpUserAgent)) {$tc_bot = 'W3C-checklink'; } # elseif (preg_match('/webpage-inspector/i', $HttpUserAgent)) {$tc_bot = 'Webpage Inspector'; $tc_bot_info = 'webpage-inspector.com';} elseif (preg_match('/webprosbot/i', $HttpUserAgent)) {$tc_bot = 'Webprosbot'; $tc_bot_info = 'mailto:abuse-6337@webpros.com';} elseif (preg_match('/WebTarantula/i', $HttpUserAgent)) {$tc_bot = 'WebTarantula'; $tc_bot_info = 'http://webtarantula.com/';} elseif (preg_match('/WhattsApp/i', $HttpUserAgent)) {$tc_bot = 'WhatsApp'; } # elseif (preg_match('/wget/i', $HttpUserAgent)) {$tc_bot = 'wget'; $tc_bot_info = 'https://www.gnu.org/software/wget/';} elseif (preg_match('/woorankreview/i', $HttpUserAgent)) {$tc_bot = 'WooRankReview'; $tc_bot_info = 'https://www.woorank.com/';} elseif (preg_match('/wp_is_mobile/i', $HttpUserAgent)) {$tc_bot = 'Wp_is_mobile'; } # elseif (preg_match('/wpbot/i', $HttpUserAgent)) {$tc_bot = 'wpbot'; $tc_bot_info = 'https://forms.gle/ajBaxygz9jSR8p8G9';} elseif (preg_match('/Xenu/i', $HttpUserAgent)) {$tc_bot = 'Xenu Link Sleuth'; } # elseif (preg_match('/XoviBot/i', $HttpUserAgent)) {$tc_bot = 'Xovi'; $tc_bot_info = 'http://www.xovibot.net/';} elseif (preg_match('/yahoo! slurp/i', $HttpUserAgent)) {$tc_bot = 'Yahoo!'; $tc_bot_info = 'http://help.yahoo.com/help/us/ysearch/slurp';} elseif (preg_match('/YandexBot/i', $HttpUserAgent)) {$tc_bot = 'Yandex'; $tc_bot_info = 'http://yandex.com/bots';} elseif (preg_match('/Yeti/i', $HttpUserAgent)) {$tc_bot = 'Yeti'; $tc_bot_info = 'http://naver.me/spd';} elseif (preg_match('/YisouSpider/i', $HttpUserAgent)) {$tc_bot = 'YisouSpider'; } # elseif (preg_match('/YottaaMonitor/i', $HttpUserAgent)) {$tc_bot = 'Yottaa'; $tc_bot_info = 'http://www.yottaa.com/blog/bid/223629/Google-Analytics-How-to-Segment-and-Filter-Robot-Traffic';} elseif (preg_match('/zyborg/i', $HttpUserAgent)) {$tc_bot = 'WiseNut!';} # elseif (preg_match('/zealbot/i', $HttpUserAgent)) {$tc_bot = 'WiseNut!';} # elseif (preg_match('/2ip bot/i', $HttpUserAgent)) {$tc_bot = '2ip bot'; $tc_bot_info = 'http://2ip.io';} # return $tc_bot; } # end detectWebBot //===================================================================================================================== function detectBrowser (string $HttpUserAgent): string { // not a bot, so find the browser, https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent $tc_browser = NULLUSERAGENT; if (preg_match('/arachne/i', $HttpUserAgent)) $tc_browser = 'Arachne GPL'; elseif (preg_match('/vivaldi/i', $HttpUserAgent)) $tc_browser = 'Vivaldi'; elseif (preg_match('/blazer/i', $HttpUserAgent)) $tc_browser = 'Blazer'; elseif (preg_match('/brave/i', $HttpUserAgent)) $tc_browser = 'Brave'; elseif (preg_match('/opera/i', $HttpUserAgent) || preg_match('/OPR/i', $HttpUserAgent)) $tc_browser = 'Opera'; # must be before Chrome below elseif (preg_match('/webtv/i', $HttpUserAgent)) $tc_browser = 'WebTV'; elseif (preg_match('/camino/i', $HttpUserAgent)) $tc_browser = 'Camino'; elseif (preg_match('/MAXTHON/i', $HttpUserAgent)) $tc_browser = 'MAXTHON'; # must be before msie below # http://www.maxthon.com/ elseif (preg_match('/netpositive/i', $HttpUserAgent)) $tc_browser = 'NetPositive'; elseif (preg_match('/internet explorer/i', $HttpUserAgent) || preg_match('/msie/i', $HttpUserAgent) || preg_match('/IEMobile/i', $HttpUserAgent) || preg_match('/mspie/i', $HttpUserAgent) || preg_match('/trident/i', $HttpUserAgent) ) $tc_browser = 'MS Internet Explorer'; # add trident elseif (preg_match('/avant browser/i', $HttpUserAgent)) $tc_browser = 'Avant Browser'; elseif (preg_match('/advanced browser/i', $HttpUserAgent)) $tc_browser = 'Avant Browser'; elseif (preg_match('/galeon/i', $HttpUserAgent)) $tc_browser = 'Galeon'; elseif (preg_match('/konqueror/i', $HttpUserAgent)) $tc_browser = 'Konqueror'; elseif (preg_match('/icab/i', $HttpUserAgent)) $tc_browser = 'iCab'; elseif (preg_match('/FBAN\/FBIOS/i', $HttpUserAgent)) $tc_browser = 'Facebook In-App Browser'; elseif (preg_match('/Nmap Scripting Engine/i', $HttpUserAgent)) $tc_browser = 'Nmap'; # http://nmap.org/book/nse.html elseif (preg_match('/omniweb/i', $HttpUserAgent)) $tc_browser = 'OmniWeb'; elseif (preg_match('/phoenix/i', $HttpUserAgent)) $tc_browser = 'Phoenix'; elseif (preg_match('/firebird/i', $HttpUserAgent)) $tc_browser = 'Firebird'; elseif (preg_match('/seamonkey/i', $HttpUserAgent)) $tc_browser = 'Seamonkey'; # must be before Firefox elseif (preg_match('/firefox/i', $HttpUserAgent)) $tc_browser = 'Firefox'; elseif (preg_match('/netscape/i', $HttpUserAgent)) $tc_browser = 'Netscape'; # must be before Mozilla below elseif (preg_match('/minimo/i', $HttpUserAgent)) $tc_browser = 'Minimo'; elseif (preg_match('/mozilla/i', $HttpUserAgent) && preg_match('/rv:[0-9].[0-9][a-b]/i', $HttpUserAgent)) $tc_browser = 'Mozilla'; # elseif (preg_match('/mozilla/i', $HttpUserAgent) && preg_match('/rv:[0-9].[0-9]/i', $HttpUserAgent)) $tc_browser = 'Mozilla'; # elseif (preg_match('/YaBrowser/i', $HttpUserAgent)) $tc_browser = 'Yandex browser'; # http://help.yandex.ru/yabrowser/?lang=en elseif (preg_match('/libwww/i', $HttpUserAgent)) { if (preg_match('/amaya/i', $HttpUserAgent)) { $tc_browser = 'Amaya'; } else { $tc_browser = 'Text browser'; } } elseif (preg_match('/edg/i', $HttpUserAgent)) $tc_browser = 'Edge'; // must be before Safari and Chrome elseif (preg_match('/edge/i', $HttpUserAgent)) $tc_browser = 'Edge'; // must be before Safari and Chrome elseif (preg_match('/chromium/i', $HttpUserAgent)) $tc_browser = 'Chromium'; // must be before Chrome elseif (preg_match('/chrome/i', $HttpUserAgent)) $tc_browser = 'Chrome'; // must be before Safari elseif (preg_match('/safari/i', $HttpUserAgent)) $tc_browser = 'Safari'; // must be after Chrome above elseif (preg_match('/AppleWebKit/i', $HttpUserAgent)) $tc_browser = 'Safari'; // must be after Safari above elseif (preg_match('/elinks/i', $HttpUserAgent)) $tc_browser = 'ELinks'; elseif (preg_match('/offbyone/i', $HttpUserAgent)) $tc_browser = 'Off By One'; elseif (preg_match('/playstation portable/i', $HttpUserAgent)) $tc_browser = 'PlayStation Portable'; elseif (preg_match('/links/i', $HttpUserAgent)) $tc_browser = 'Links'; elseif (preg_match('/ibrowse/i', $HttpUserAgent)) $tc_browser = 'iBrowse'; elseif (preg_match('/w3m/i', $HttpUserAgent)) $tc_browser = 'w3m'; elseif (preg_match('/aweb/i', $HttpUserAgent)) $tc_browser = 'AWeb'; elseif (preg_match('/voyager/i', $HttpUserAgent)) $tc_browser = 'Voyager'; elseif (preg_match('/oregano/i', $HttpUserAgent)) $tc_browser = 'Oregano'; return $tc_browser; } # end detectBrowser //===================================================================================================================== function detectOS (string $HttpUserAgent):string { // find operating system $tc_os = NULLUSERAGENT; if (preg_match('/android/i', $HttpUserAgent)) $tc_os = 'Android'; // # must be before linux below elseif (preg_match('/linux/i', $HttpUserAgent)) $tc_os = 'Linux'; elseif (preg_match('/irix/i', $HttpUserAgent)) $tc_os = 'IRIX'; elseif (preg_match('/hp-ux/i', $HttpUserAgent)) $tc_os = 'HP-Unix'; elseif (preg_match('/os2/i', $HttpUserAgent)) $tc_os = 'OS/2'; elseif (preg_match('/beos/i', $HttpUserAgent)) $tc_os = 'BeOS'; elseif (preg_match('/sunos/i', $HttpUserAgent)) $tc_os = 'SunOS'; elseif (preg_match('/palm/i', $HttpUserAgent)) $tc_os = 'PalmOS'; elseif (preg_match('/cygwin/i', $HttpUserAgent)) $tc_os = 'Cygwin'; elseif (preg_match('/amiga/i', $HttpUserAgent)) $tc_os = 'Amiga'; elseif (preg_match('/unix/i', $HttpUserAgent)) $tc_os = 'Unix'; elseif (preg_match('/qnx/i', $HttpUserAgent)) $tc_os = 'QNX'; elseif (preg_match('/Windows Phone/i', $HttpUserAgent)) $tc_os = 'Windows Phone'; # must be before Windows below elseif (preg_match('/windows/i', $HttpUserAgent)) $tc_os = 'Windows'; # elseif (preg_match('/openbsd/i', $HttpUserAgent)) $tc_os = 'OpenBSD'; # elseif (preg_match('/iphone os/i', $HttpUserAgent)) $tc_os = 'iPhone'; # must be before Mac OS below elseif (preg_match('/mac os/i', $HttpUserAgent)) $tc_os = 'Mac'; elseif (preg_match('/cros/i', $HttpUserAgent)) $tc_os = 'Chrome OS'; elseif (preg_match('/symbian/i', $HttpUserAgent)) $tc_os = 'Symbian'; elseif (preg_match('/risc/i', $HttpUserAgent)) $tc_os = 'RISC'; elseif (preg_match('/dreamcast/i', $HttpUserAgent)) $tc_os = 'Dreamcast'; elseif (preg_match('/freebsd/i', $HttpUserAgent)) $tc_os = 'FreeBSD'; elseif (preg_match('/dos/i', $HttpUserAgent)) $tc_os = 'dos'; return $tc_os; } # end detectOS //===================================================================================================================== function SetAllLocations ():array { // Define top level domains return array ( 'localhost' => 'localhost', LITUNKNOWN => 'Unknown', # original top level domains 'com' => 'Commercial', 'net' => 'Networks', 'org' => 'Organizations', 'int' => 'International organizations', 'edu' => 'US higher Education', 'gov' => 'US Government', 'mil' => 'US Dept of Defense', # selected ICANN TLDs 'academy' => 'Academy', 'aero' => 'Aviation', 'biz' => 'Business organizations', 'church' => 'Churches', 'city' => 'City', 'club' => 'Clubs', 'community' => 'Community', 'coop' => 'Co-operative organizations', 'education' => 'Education insitiutes', 'info' => 'Information', 'international' => 'International entities', 'mobi' => 'mobile devices', 'museum' => 'Museums', 'name' => 'Personal', 'place' => 'Place', 'travel' => 'Travelling', 'universite' => 'University', 'wiki' => 'Wikis', # selected geographic TLDs 'africa' => 'Africa', 'asia' => 'Asia', 'berlin' => 'Berlin', 'brussels' => 'Brussels', 'kiwi' => 'Kiwi', 'london' => 'London', 'paris' => 'Paris', 'quebec' => 'Quebec', 'scot' => 'Scotland', # country code top level https://icannwiki.org/Country_code_top-level_domain # updates from https://isotc.iso.org/livelink/livelink?func=ll&objId=16944257&objAction=browse&viewType=1 'ac' => 'Ascension Island', 'ad' => 'Andorra', 'ae' => 'United Arab Emirates', 'af' => 'Afghanistan', 'ag' => 'Antigua & Barbuda', 'ai' => 'Anguilla', 'al' => 'Albania', 'am' => 'Armenia', 'an' => 'Netherlands Antilles', 'ao' => 'Angola', 'aq' => 'Antarctica', 'ar' => 'Argentina', 'as' => 'American Samoa', 'at' => 'Austria', 'au' => 'Australia', 'aw' => 'Aruba', 'ax' => 'Åland', 'az' => 'Azerbaijan', 'ba' => 'Bosnia & Herzegovina', 'bb' => 'Barbados', 'bd' => 'Bangladesh', 'be' => 'Belgium', 'bf' => 'Burkina Faso', 'bg' => 'Bulgaria', 'bh' => 'Bahrain', 'bi' => 'Burundi', 'bj' => 'Benin', 'bm' => 'Bermuda', 'bn' => 'Brunei Darussalam', 'bo' => 'Bolivia', 'br' => 'Brazil', 'bs' => 'Bahamas', 'bt' => 'Bhutan', 'bv' => 'Bouvet Island', 'bw' => 'Botswana', 'by' => 'Belarus', 'bz' => 'Belize', 'ca' => 'Canada', 'cc' => 'Cocos (Keeling) Islands', 'cd' => 'Democratic republic of Congo', 'cf' => 'Central African Republic', 'cg' => 'Congo', 'ch' => 'Switzerland', 'ci' => 'Ivory Coast', 'ck' => 'Cook Islands', 'cl' => 'Chile', 'cm' => 'Cameroon', 'cn' => 'China', 'co' => 'Colombia', 'cr' => 'Costa Rica', 'cs' => 'Czechoslovakia/Sebia & Montenegro', // deleted 'cu' => 'Cuba', 'cv' => 'Cape Verde', 'cw' => 'Curaçao', 'cx' => 'Christmas Island', 'cy' => 'Cyprus', 'cz' => 'Czech Republic', 'de' => 'Germany', 'dj' => 'Djibouti', 'dk' => 'Denmark', 'dm' => 'Dominica', 'do' => 'Dominican Republic', 'dz' => 'Algeria', 'ec' => 'Ecuador', 'ee' => 'Estonia', 'eg' => 'Egypt', 'eh' => 'Western Sahara', 'er' => 'Eritrea', 'es' => 'Spain', 'et' => 'Ethiopia', 'eu' => 'European Union', 'fi' => 'Finland', 'fj' => 'Fiji', 'fk' => 'Falkland Islands', 'fm' => 'Micronesia', 'fo' => 'Faroe Islands', 'fr' => 'France', 'ga' => 'Gabon', 'gb' => 'United Kingdom', 'gd' => 'Grenada', 'ge' => 'Georgia', 'gf' => 'French Guiana', 'gg' => 'Guernsey', 'gh' => 'Ghana', 'gi' => 'Gibraltar', 'gl' => 'Greenland', 'gm' => 'Gambia', 'gn' => 'Guinea', 'gp' => 'Guadeloupe', 'gq' => 'Equatorial Guinea', 'gr' => 'Greece', 'gs' => 'South Georgia & South Sandwich Islands', 'gt' => 'Guatemala', 'gu' => 'Guam', 'gw' => 'Guinea-Bissau', 'gy' => 'Guyana', 'hk' => 'Hong Kong', 'hm' => 'Heard & McDonald Islands', 'hn' => 'Honduras', 'hr' => 'Croatia', 'ht' => 'Haiti', 'hu' => 'Hungary', 'id' => 'Indonesia', 'ie' => 'Ireland', 'il' => 'Israel', 'im' => 'Isle of Man', 'in' => 'India', 'io' => 'British Indian Ocean Territory', 'iq' => 'Iraq', 'ir' => 'Iran', 'is' => 'Iceland', 'it' => 'Italy', 'je' => 'Jersey', 'jm' => 'Jamaica', 'jo' => 'Jordan', 'jp' => 'Japan', 'ke' => 'Kenya', 'kg' => 'Kyrgyzstan', 'kh' => 'Cambodia', 'ki' => 'Kiribati', 'km' => 'Comoros', 'kn' => 'Saint Kitts & Nevis', 'kp' => 'North Korea', 'kr' => 'South Korea', 'kw' => 'Kuwait', 'ky' => 'Cayman Islands', 'kz' => 'Kazakhstan', 'la' => 'Laos', 'lb' => 'Lebanon', 'lc' => 'Saint Lucia', 'li' => 'Liechtenstein', 'lk' => 'Sri Lanka', 'lr' => 'Liberia', 'ls' => 'Lesotho', 'lt' => 'Lithuania', 'lu' => 'Luxembourg', 'lv' => 'Latvia', 'ly' => 'Libyan Arab Jamahiriya', 'ma' => 'Morocco', 'mc' => 'Monaco', 'md' => 'Moldova', 'me' => 'Montenegro', 'mg' => 'Madagascar', 'mh' => 'Marshall Islands', 'mk' => 'North Macedonia', 'ml' => 'Mali', 'mm' => 'Myanmar', 'mn' => 'Mongolia', 'mo' => 'Macau', 'mp' => 'Northern Mariana Islands', 'mq' => 'Martinique', 'mr' => 'Mauritania', 'ms' => 'Montserrat', 'mt' => 'Malta', 'mu' => 'Mauritius', 'mv' => 'Maldives', 'mw' => 'Malawi', 'mx' => 'Mexico', 'my' => 'Malaysia', 'mz' => 'Mozambique', 'na' => 'Namibia', 'nc' => 'New Caledonia', 'ne' => 'Niger', 'nf' => 'Norfolk Island', 'ng' => 'Nigeria', 'ni' => 'Nicaragua', 'nl' => 'The Netherlands', 'no' => 'Norway', 'np' => 'Nepal', 'nr' => 'Nauru', 'nu' => 'Niue', 'nz' => 'New Zealand', 'om' => 'Oman', 'pa' => 'Panama', 'pe' => 'Peru', 'pf' => 'French Polynesia', 'pg' => 'Papua New Guinea', 'ph' => 'Philippines', 'pk' => 'Pakistan', 'pl' => 'Poland', 'pm' => 'St. Pierre & Miquelon', 'pn' => 'Pitcairn', 'pr' => 'Puerto Rico', 'ps' => 'Palestine', 'pt' => 'Portugal', 'pw' => 'Palau', 'py' => 'Paraguay', 'qa' => 'Qatar', 're' => 'Réunion', 'ro' => 'Romania', 'rs' => 'Serbia', 'ru' => 'Russia', 'rw' => 'Rwanda', 'sa' => 'Saudi Arabia', 'sb' => 'Solomon Islands', 'sc' => 'Seychelles', 'sd' => 'Sudan', 'se' => 'Sweden', 'sg' => 'Singapore', 'sh' => 'St. Helena', 'si' => 'Slovenia', 'sj' => 'Svalbard & Jan Mayen Islands', 'sk' => 'Slovakia', 'sl' => 'Sierra Leone', 'sm' => 'San Marino', 'sn' => 'Senegal', 'so' => 'Somalia', 'sr' => 'Surinam', 'st' => 'Sao Tome & Principe', 'su' => 'USSR', 'sv' => 'El Salvador', 'sy' => 'Syrian Arab Republic', 'sz' => 'Swaziland', 'tc' => 'The Turks & Caicos Islands', 'td' => 'Chad', 'tf' => 'French Southern Territories', 'tg' => 'Togo', 'th' => 'Thailand', 'tj' => 'Tajikistan', 'tk' => 'Tokelau', 'tl' => 'Timor-Leste', 'tm' => 'Turkmenistan', 'tn' => 'Tunisia', 'to' => 'Tonga', 'tp' => 'East Timor', 'tr' => 'Turkey', 'tt' => 'Trinidad & Tobago', 'tv' => 'Tuvalu', 'tw' => 'Taiwan', 'tz' => 'Tanzania', 'ua' => 'Ukraine', 'ug' => 'Uganda', 'uk' => 'United Kingdom', 'um' => 'United States Minor Outlying Islands', 'us' => 'United States', 'uy' => 'Uruguay', 'uz' => 'Uzbekistan', 'va' => 'Vatican City', 'vc' => 'Saint Vincent & the Grenadines', 've' => 'Venezuela', 'vg' => 'British Virgin Islands', 'vi' => 'US Virgin Islands', 'vn' => 'Vietnam', 'vu' => 'Vanuatu', 'wf' => 'Wallis & Futuna Islands', 'ws' => 'Samoa', 'ye' => 'Yemen', 'yt' => 'Mayotte', 'yu' => 'Yugoslavia', 'za' => 'South Africa', 'zm' => 'Zambia', 'zr' => 'Zaire', // deprecated 'zw' => 'Zimbabwe', ); } //===================================================================================================================== function recordErrorMessage (string $errDesc, array $errMsg) { # record error message tcmsg ($errDesc, $errMsg ['message'] . ' - ' . $errMsg ['file'] . ' - line ' . $errMsg ['line']); return; } # end recordErrorMessage //===================================================================================================================== function tcLogMessage (string $tcLogMessage) { global $logFileHandle, $logFileTime; \Lock(2); # acquire exclusive lock $fwritestatus = fwrite($logFileHandle, $logFileTime . $tcLogMessage . NL); \Lock(0); # release lock if ($fwritestatus === false) { recordErrorMessage ('fwrite to log file failed', error_get_last()); } return; } #end logMessage //===================================================================================================================== // Record message to PmWiki for display by (:message:) directive function tcmsg(string $smsgprefix, string $smsgdata, array $LastError = []) { global $MessagesFmt, $TotalCounterLogOn, $logFileHandle, $logFileTime; if (!isset ($MessagesFmt [MSGFMTID])) $MessagesFmt [MSGFMTID] = []; $TcMsgs = ''; $TcMsgs .= '' . $smsgprefix . ': ' . \PHSC($smsgdata); if (!empty ($LastError)) $TcMsgs .= SP . TOTALCOUNTERNAME . ' {' . implode (', ', $LastError) . '}'; $MessagesFmt [MSGFMTID] [] = $TcMsgs . BR; if ($TotalCounterLogOn) { # also write message to the totalcounter log $TcLogMsg = ''; $TcLogMsg .= $smsgprefix . '> ' . \PHSC($smsgdata); if (!empty ($LastError)) $TcLogMsg .= SP . TOTALCOUNTERNAME . ' {' . implode (', ', $LastError) . '}'; \Lock(2); # acquire exclusive lock $fwritestatus = fwrite($logFileHandle, $logFileTime . $TcLogMsg . NL); # ignore failure conditions \Lock(0); # release lock } return; } # end tcmsg # end TotalCOunter