' . 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 .
'' . $name . ' | ' .
'' . LITCOUNT . ' |
' . 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 .= " " . $output . ' | ' . NL;
$row .= "" . NL;
if ($maxcount == 0) {
$barWidth = 0;
} else {
$barWidth = Round(1 + BARMAXWIDTH * ($count / $maxcount));
}
$row .= "" . NL;
$row .= " | " . NL;
# display count column
$row .= "" . number_format($count) . ' | ' . NL;
$rows[] = $row;
}
} # end for $nr
$htmlOut .= ''.implode('
'.NL.'',$rows).'
' . 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 .= '' . $headline . ' | ' .
'' . LITPERCENT . ' | ' .
'' . LITCOUNT . ' |
' . NL;
$retVal .= '' . NL;
} else { # create table footer
$retVal .= '
' . 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