You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

335 lines
14 KiB

<?php
// Check if we are serving HTTPS
function isHttps() {
// Check standard HTTPS indicators
if (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
return true;
}
if (!empty($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) {
return true;
}
// Check for proxy/load balancer headers
if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
return true;
}
if (!empty($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] === 'on') {
return true;
}
return false;
}
// Secure session configuration
ini_set('session.cookie_httponly', 1);
ini_set('session.cookie_samesite', 'Strict');
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') {
ini_set('session.cookie_secure', 1);
}
session_start();
// Security headers
$isHttps = isHttps();
header("X-Frame-Options: SAMEORIGIN");
header("X-Content-Type-Options: nosniff");
header("X-XSS-Protection: 1; mode=block");
header("Referrer-Policy: strict-origin-when-cross-origin");
header("Permissions-Policy: geolocation=(), microphone=(), camera=()");
// Build CSP based on protocol
// Allow external images via both http: and https: since we can't control external links
$imgSrc = $isHttps ? "'self' data: https:" : "'self' data: http: https:";
$csp = "default-src 'self'; " .
"script-src 'self' 'unsafe-inline'; " .
"style-src 'self' 'unsafe-inline'; " .
"img-src {$imgSrc}; " .
"connect-src 'self'; " .
"frame-ancestors 'self'";
header("Content-Security-Policy: " . $csp);
// Only add HSTS if served over HTTPS
if ($isHttps) {
// HSTS: Force HTTPS for 1 year, but don't include subdomains (might be on local network)
header("Strict-Transport-Security: max-age=31536000");
}
if (file_exists("./pgs/functions.php")) { require_once("./pgs/functions.php"); } else { die("functions.php does not exist."); }
if (file_exists("./pgs/config.inc.php")) { require_once("./pgs/config.inc.php"); } else { die("config.inc.php does not exist."); }
if (!class_exists('ParseXML')) require_once("./pgs/class.parsexml.php");
if (!class_exists('Node')) require_once("./pgs/class.node.php");
if (!class_exists('xReflector')) require_once("./pgs/class.reflector.php");
if (!class_exists('Station')) require_once("./pgs/class.station.php");
if (!class_exists('Peer')) require_once("./pgs/class.peer.php");
if (!class_exists('Interlink')) require_once("./pgs/class.interlink.php");
// Validate 'show' parameter
$allowed_pages = ['users', 'repeaters', 'liveircddb', 'peers', 'modules', 'reflectors', 'traffic', 'ysfpeers', 'nxdnpeers', 'p25peers'];
if (!isset($_GET['show'])) {
$_GET['show'] = '';
} elseif (!in_array($_GET['show'], $allowed_pages, true)) {
$_GET['show'] = '';
}
// Validate 'do' parameter for filter resets
if (isset($_GET['do']) && $_GET['do'] !== 'resetfilter') {
unset($_GET['do']);
}
// Validate 'callhome' parameter
if (isset($_GET['callhome'])) {
$_GET['callhome'] = filter_var($_GET['callhome'], FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE);
if ($_GET['callhome'] === null) {
unset($_GET['callhome']);
}
}
$Reflector = new xReflector();
$Reflector->SetFlagFile("./pgs/country.csv");
$Reflector->SetPIDFile($Service['PIDFile']);
$Reflector->SetXMLFile($Service['XMLFile']);
$Reflector->LoadXML();
// Pre-parse peer reflector data so nav menu can show live counts.
// Results are reused by the page files (they check isset() before re-parsing).
require_once('./pgs/parse_journal_peers.php');
// These pages need a local peer in xlxd.interlink, hide them (and skip journalctl) without one
$InterlinkFile = !empty($CallingHome['InterlinkFile']) ? $CallingHome['InterlinkFile'] : '/xlxd/xlxd.interlink';
foreach (['YSF' => 'YSFPeerPage', 'NXDN' => 'NXDNPeerPage', 'P25' => 'P25PeerPage'] as $PeerType => $PeerPage) {
if (!empty($PageOptions[$PeerPage]['Show']) && !HasLocalPeer($PeerType, $InterlinkFile)) {
$PageOptions[$PeerPage]['Show'] = false;
$PageOptions[$PeerPage]['NoLocalPeer'] = true;
}
}
$YSFPeers = [];
if (!empty($PageOptions['YSFPeerPage']['Show'])) {
$YSFPeers = ParseJournalPeers($PageOptions['YSFPeerPage']);
}
$NXDNPeers = [];
if (!empty($PageOptions['NXDNPeerPage']['Show'])) {
$NXDNPeers = ParseJournalPeers($PageOptions['NXDNPeerPage']);
}
$P25Peers = [];
if (!empty($PageOptions['P25PeerPage']['Show'])) {
$P25Peers = ParseJournalPeers($PageOptions['P25PeerPage']);
}
if ($CallingHome['Active']) {
$CallHomeNow = false;
$LastSync = 0;
$Hash = "";
if (!file_exists($CallingHome['HashFile'])) {
$Ressource = fopen($CallingHome['HashFile'], "w+");
if ($Ressource) {
$Hash = CreateCode(16);
@fwrite($Ressource, "<?php\n");
@fwrite($Ressource, "\n".'$Hash = "'.$Hash.'";');
@fwrite($Ressource, "\n\n".'?>');
@fflush($Ressource);
@fclose($Ressource);
@chmod($HashFile, 0777);
}
}
else {
require_once($CallingHome['HashFile']);
}
if (@file_exists($CallingHome['LastCallHomefile'])) {
if (@is_readable($CallingHome['LastCallHomefile'])) {
$tmp = @file($CallingHome['LastCallHomefile']);
if (isset($tmp[0])) {
$LastSync = $tmp[0];
}
unset($tmp);
}
}
if ($LastSync < (time() - $CallingHome['PushDelay'])) {
$CallHomeNow = true;
$Ressource = @fopen($CallingHome['LastCallHomefile'], "w+");
if ($Ressource) {
@fwrite($Ressource, time());
@fflush($Ressource);
@fclose($Ressource);
@chmod($HashFile, 0777);
}
}
if ($CallHomeNow || isset($_GET['callhome'])) {
$Reflector->SetCallingHome($CallingHome, $Hash);
$Reflector->ReadInterlinkFile();
$Reflector->PrepareInterlinkXML();
$Reflector->PrepareReflectorXML();
$Reflector->CallHome();
}
}
else {
$Hash = "";
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<meta name="description" content="<?php echo sanitize_attribute($PageOptions['MetaDescription']); ?>" />
<meta name="keywords" content="<?php echo sanitize_attribute($PageOptions['MetaKeywords']); ?>" />
<meta name="author" content="<?php echo sanitize_attribute($PageOptions['MetaAuthor']); ?>" />
<meta name="revisit" content="<?php echo sanitize_attribute($PageOptions['MetaRevisit']); ?>" />
<meta name="robots" content="<?php echo sanitize_attribute($PageOptions['MetaRobots']); ?>" />
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title><?php echo sanitize_output($Reflector->GetReflectorName()); ?> Reflector Dashboard</title>
<link rel="stylesheet" type="text/css" href="./css/layout.css">
<link rel="icon" href="./favicon.ico" type="image/vnd.microsoft.icon"><?php
if ($PageOptions['PageRefreshActive']) {
// Build refresh URL preserving show and page parameters
$refreshParams = array();
if (!empty($_GET['show'])) {
$refreshParams[] = 'show=' . urlencode($_GET['show']);
}
if (!empty($_GET['page']) && intval($_GET['page']) > 1) {
$refreshParams[] = 'page=' . intval($_GET['page']);
}
$refreshUrl = './index.php' . (!empty($refreshParams) ? '?' . implode('&', $refreshParams) : '');
echo '
<script src="./js/jquery-1.12.4.min.js"></script>
<script>
var PageRefresh;
function ReloadPage() {
$.get("'.addslashes($refreshUrl).'", function(data) {
var BodyStart = data.indexOf("<bo"+"dy");
var BodyEnd = data.indexOf("</bo"+"dy>");
if ((BodyStart >= 0) && (BodyEnd > BodyStart)) {
BodyStart = data.indexOf(">", BodyStart)+1;
$("body").html(data.substring(BodyStart, BodyEnd));
}
})
.always(function() {
PageRefresh = setTimeout(ReloadPage, '.$PageOptions['PageRefreshDelay'].');
});
}';
if (!isset($_GET['show']) || (($_GET['show'] != 'liveircddb') && ($_GET['show'] != 'reflectors') && ($_GET['show'] != 'interlinks'))) {
echo '
PageRefresh = setTimeout(ReloadPage, '.$PageOptions['PageRefreshDelay'].');';
}
echo '
function SuspendPageRefresh() {
clearTimeout(PageRefresh);
}
</script>';
}
if (!isset($_GET['show'])) $_GET['show'] = "";
?>
</head>
<body>
<?php if (file_exists("./tracking.php")) { include_once("tracking.php"); }?>
<div id="top"><img src="./img/header.jpg" alt="XLX Multiprotocol Gateway Reflector" style="margin-top:15px;" />
<br />&nbsp;&nbsp;&nbsp;<?php echo sanitize_output($Reflector->GetReflectorName()); ?>&nbsp;v<?php echo sanitize_output($Reflector->GetVersion()); ?>&nbsp;-&nbsp;Dashboard v<?php echo sanitize_output($PageOptions['DashboardVersion']); ?>&nbsp;<?php echo sanitize_output($PageOptions['CustomTXT']); ?>&nbsp;&nbsp;/&nbsp;&nbsp;Service uptime: <span id="suptime"><?php echo FormatSeconds($Reflector->GetServiceUptime()); ?></span></div>
<div id="menubar">
<div id="menu">
<table border="0">
<tr>
<td><a href="./index.php" class="menulink<?php if ($_GET['show'] == '') { echo 'active'; } ?>">Users / Modules</a></td>
<td><a href="./index.php?show=repeaters" class="menulink<?php if ($_GET['show'] == 'repeaters') { echo 'active'; } ?>">Repeaters / Nodes (<?php echo intval($Reflector->NodeCount()); ?>)</a></td>
<td><a href="./index.php?show=peers" class="menulink<?php if ($_GET['show'] == 'peers') { echo 'active'; } ?>">Peers (<?php echo intval($Reflector->PeerCount()); ?>)</a></td>
<?php
if (!empty($PageOptions['YSFPeerPage']['Show'])) {
echo '
<td><a href="./index.php?show=ysfpeers" class="menulink';
if ($_GET['show'] == 'ysfpeers') { echo 'active'; }
echo '">' . sanitize_output($PageOptions['YSFPeerPage']['PageTitle'])
. ' (' . intval(count($YSFPeers)) . ')</a></td>';
}
if (!empty($PageOptions['NXDNPeerPage']['Show'])) {
echo '
<td><a href="./index.php?show=nxdnpeers" class="menulink';
if ($_GET['show'] == 'nxdnpeers') { echo 'active'; }
echo '">' . sanitize_output($PageOptions['NXDNPeerPage']['PageTitle'])
. ' (' . intval(count($NXDNPeers)) . ')</a></td>';
}
if (!empty($PageOptions['P25PeerPage']['Show'])) {
echo '
<td><a href="./index.php?show=p25peers" class="menulink';
if ($_GET['show'] == 'p25peers') { echo 'active'; }
echo '">' . sanitize_output($PageOptions['P25PeerPage']['PageTitle'])
. ' (' . intval(count($P25Peers)) . ')</a></td>';
}
?>
<td><a href="./index.php?show=modules" class="menulink<?php if ($_GET['show'] == 'modules') { echo 'active'; } ?>">Modules list</a></td>
<td><a href="./index.php?show=reflectors" class="menulink<?php if ($_GET['show'] == 'reflectors') { echo 'active'; } ?>">Reflectors list</a></td>
<?php
if ($PageOptions['Traffic']['Show']) {
echo '
<td><a href="./index.php?show=traffic" class="menulink';
if ($_GET['show'] == 'traffic') { echo 'active'; }
echo '">Traffic statistics</a></td>';
}
if ($PageOptions['IRCDDB']['Show']) {
echo '
<td><a href="./index.php?show=liveircddb" class="menulink';
if ($_GET['show'] == 'liveircddb') { echo 'active'; }
echo '">D-Star live</a></td>';
}
?>
</tr>
</table>
</div>
</div>
<div id="content" align="center">
<?php
if ($CallingHome['Active']) {
if (!is_readable($CallingHome['HashFile']) && (!is_writeable($CallingHome['HashFile']))) {
echo '
<div class="error">
your private hash in '.sanitize_output($CallingHome['HashFile']).' could not be created, please check your config file and the permissions for the defined folder.
</div>';
}
}
switch ($_GET['show']) {
case 'users' : require_once("./pgs/users.php"); break;
case 'repeaters' : require_once("./pgs/repeaters.php"); break;
case 'liveircddb' : require_once("./pgs/liveircddb.php"); break;
case 'peers' : require_once("./pgs/peers.php"); break;
case 'modules' : require_once("./pgs/modules.php"); break;
case 'reflectors' : require_once("./pgs/reflectors.php"); break;
case 'traffic' : require_once("./pgs/traffic.php"); break;
case 'ysfpeers' : require_once("./pgs/ysfpeers.php"); break;
case 'nxdnpeers' : require_once("./pgs/nxdnpeers.php"); break;
case 'p25peers' : require_once("./pgs/p25peers.php"); break;
default : require_once("./pgs/users.php");
}
?>
<div style="width:100%;text-align:center;margin-top:50px;"><a href="mailto:<?php echo sanitize_attribute($PageOptions['ContactEmail']); ?>" style="font-family:verdana;color:#000000;font-size:12pt;text-decoration:none;"><?php echo sanitize_output($PageOptions['ContactEmail']); ?></a></div>
</div>
</body>
</html>

Powered by TurnKey Linux.