This commit is contained in:
2026-07-19 18:27:27 +08:00
parent fe4b286ce1
commit 0febcf69f4
170 changed files with 19490 additions and 11878 deletions
+79 -130
View File
@@ -1,43 +1,3 @@
<?php
/**
* D-Star reflector link/unlink page — service status grid + a form
* that posts back to itself to issue
* `sudo remotecontrold "<module>" link never "<reflector>"`
* (or `unlink`) against the running ircDDBGateway.
*
* Hit directly via /admin/admin.php (the form action self-posts);
* not inline-included by any current parent page. The historical
* "Loaded inline by /admin/index.php" docstring claim was stale —
* there is no `include 'admin.php'` site in the codebase today.
* Reachable only by direct navigation; admin/configure.php
* configures the *password* for the same `remotecontrold` daemon
* but doesn't issue link/unlink commands.
*
* Service-status row pgreps for the six daemons (DStarRepeater,
* MMDVMHost, ircDDBgateway, timeserver, pistar-watchdog,
* pistar-keeper) and renders a green/red dot per service.
*
* Input validation: preg_match whitelist on Module / RefName /
* Letter / Link verb (uppercase A-Z and digits only). That guards
* against shell-quote escape but does not substitute for CSRF —
* a forged POST with valid-looking values could otherwise trigger
* link/unlink under the operator's basic-auth session. csrf_verify()
* below closes that gap before any sudo runs.
*/
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/security_headers.php');
require_once($_SERVER['DOCUMENT_ROOT'] . '/config/csrf.php');
setSecurityHeaders();
// CSRF protection — see config/csrf.php for the full rationale.
// Must run BEFORE any output: bootstraps the session on GET (so
// Set-Cookie ships and csrf_field() at line ~98 has a token to
// emit) and rejects forged POSTs cleanly with 403 before the
// `sudo remotecontrold "<module>" link|unlink ...` state-change
// in the POST handler runs.
csrf_verify();
?>
<b>Service Status</b>
<table>
<tr>
@@ -60,29 +20,21 @@ csrf_verify();
<br />
<?php if (!empty($_POST)):
// Each $_POST read uses the `?? ''` null-coalesce so that:
// - a missing key (Link / RefName / Letter / Module never sent) and
// - a key unset by the preg_match sanitiser below (e.g. Link
// contained non-uppercase chars and was unset)
// both resolve to '' instead of triggering PHP 8's "Undefined array
// key" E_WARNING. Behaviour is unchanged: '' fails every "X" == "Y"
// comparison the way an undefined key did under PHP 7's E_NOTICE.
// `(... ?? '')` is parenthesised because == binds tighter than ??.
if (preg_match('/[^A-Z]/', $_POST["Link"] ?? '')) { unset ($_POST["Link"]);}
if (($_POST["Link"] ?? '') == "LINK") {
if (preg_match('/[^A-Z0-9]/', $_POST["RefName"] ?? '')) { unset ($_POST["RefName"]);}
if (preg_match('/[^A-Z]/', $_POST["Letter"] ?? '')) { unset ($_POST["Letter"]);}
if (preg_match('/[^A-Z0-9 ]/', $_POST["Module"] ?? '')) { unset ($_POST["Module"]);}
}
if (($_POST["Link"] ?? '') == "UNLINK") {
if (preg_match('/[^A-Z0-9 ]/', $_POST["Module"] ?? '')) { unset ($_POST["Module"]);}
}
if (preg_match('/[^A-Z]/',$_POST["Link"])) { unset ($_POST["Link"]);}
if ($_POST["Link"] == "LINK") {
if (preg_match('/[^A-Z0-9]/',$_POST["RefName"])) { unset ($_POST["RefName"]);}
if (preg_match('/[^A-Z]/',$_POST["Letter"])) { unset ($_POST["Letter"]);}
if (preg_match('/[^A-Z0-9 ]/',$_POST["Module"])) { unset ($_POST["Module"]);}
}
if ($_POST["Link"] == "UNLINK") {
if (preg_match('/[^A-Z0-9 ]/',$_POST["Module"])) { unset ($_POST["Module"]);}
}
if (empty($_POST["RefName"]) || empty($_POST["Letter"]) || empty($_POST["Module"])) { echo "Somthing wrong with your input, try again";}
else {
$targetRef = $_POST["RefName"]." ".$_POST["Letter"];
$module = $_POST["Module"];
$targetRef = $_POST["RefName"]." ".$_POST["Letter"];
$module = $_POST["Module"];
if (strlen($module) != 8) { //Fix the length of the module information
$moduleFixedCs= strlen($module) - 1; //Length of the string, -1
@@ -91,25 +43,22 @@ else {
$module = $moduleFixedCallPad.$moduleFixedBand; //Re add the band information
};
$unlinkCommand = "sudo remotecontrold \"".$module."\" unlink";
$linkCommand = "sudo remotecontrold \"".$module."\" link never \"".$targetRef."\"";
$unlinkCommand = "sudo remotecontrold \"".$module."\" unlink";
$linkCommand = "sudo remotecontrold \"".$module."\" link never \"".$targetRef."\"";
if (($_POST["Link"] ?? '') == "LINK") {
echo "<b>Reflector Connector</b>\n";
echo "<table>\n<tr><th><a class=tooltip href=\"#\">Command Output<span><b>Command Output</b></span></th></tr>\n<tr><td>";
// remotecontrold output is text from the daemon; escape on
// display defence-in-depth (the input fields above were
// already preg_match-whitelist-validated).
echo htmlspecialchars((string)exec($linkCommand), ENT_QUOTES, 'UTF-8');
echo "</tr></td>\n</table>\n";
}
if (($_POST["Link"] ?? '') == "UNLINK") {
echo "<b>Reflector Connector</b>\n";
echo "<table>\n<tr><th><a class=tooltip href=\"#\">Command Output<span><b>Command Output</b></span></th></tr>\n<tr><td>";
echo htmlspecialchars((string)exec($unlinkCommand), ENT_QUOTES, 'UTF-8');
echo "</tr></td>\n</table>\n";
}
}
if ($_POST["Link"] == "LINK") {
echo "<b>Reflector Connector</b>\n";
echo "<table>\n<tr><th><a class=tooltip href=\"#\">Command Output<span><b>Command Output</b></span></th></tr>\n<tr><td>";
echo exec($linkCommand);
echo "</tr></td>\n</table>\n";
}
if ($_POST["Link"] == "UNLINK") {
echo "<b>Reflector Connector</b>\n";
echo "<table>\n<tr><th><a class=tooltip href=\"#\">Command Output<span><b>Command Output</b></span></th></tr>\n<tr><td>";
echo exec($unlinkCommand);
echo "</tr></td>\n</table>\n";
}
}
unset($_POST);
echo '<script type="text/javascript">setTimeout(function() { window.location=window.location;},2000);</script>';
@@ -117,7 +66,6 @@ echo '<script type="text/javascript">setTimeout(function() { window.location=win
else: ?>
<b>Reflector Connector</b>
<form action="//<?php echo htmlentities($_SERVER['HTTP_HOST']).htmlentities($_SERVER['PHP_SELF']); ?>" method="post">
<?php csrf_field(); ?>
<table>
<tr>
<th id="lblModule" width="150"><a class=tooltip href="#">Radio Module<span><b>Radio Module</b></span></th>
@@ -150,61 +98,61 @@ $dplusFile = fopen("/usr/local/etc/DPlus_Hosts.txt", "r");
$dextraFile = fopen("/usr/local/etc/DExtra_Hosts.txt", "r");
while (!feof($dcsFile)) {
$dcsLine = fgets($dcsFile);
if (strpos($dcsLine, 'DCS') !== FALSE && strpos($dcsLine, '#') === FALSE)
echo " <option>".substr($dcsLine, 0, 6)."</option>\n";
$dcsLine = fgets($dcsFile);
if (strpos($dcsLine, 'DCS') !== FALSE && strpos($dcsLine, '#') === FALSE)
echo " <option>".substr($dcsLine, 0, 6)."</option>\n";
}
fclose($dcsFile);
echo " <option selected>REF001</option>\n";
echo " <option selected>REF001</option>\n";
while (!feof($dplusFile)) {
$dplusLine = fgets($dplusFile);
if (strpos($dplusLine, 'REF') !== FALSE && strpos($dplusLine, '#') === FALSE && strpos($dplusLine, 'REF001') === FALSE)
echo " <option>".substr($dplusLine, 0, 6)."</option>\n";
$dplusLine = fgets($dplusFile);
if (strpos($dplusLine, 'REF') !== FALSE && strpos($dplusLine, '#') === FALSE && strpos($dplusLine, 'REF001') === FALSE)
echo " <option>".substr($dplusLine, 0, 6)."</option>\n";
}
fclose($dplusFile);
while (!feof($dextraFile)) {
$dextraLine = fgets($dextraFile);
if (strpos($dextraLine, 'XRF') !== FALSE && strpos($dextraLine, '#') === FALSE)
echo " <option>".substr($dextraLine, 0, 6)."</option>\n";
$dextraLine = fgets($dextraFile);
if (strpos($dextraLine, 'XRF') !== FALSE && strpos($dextraLine, '#') === FALSE)
echo " <option>".substr($dextraLine, 0, 6)."</option>\n";
}
fclose($dextraFile);
?>
</select>
<select aria-label="Module" name="Letter">
<option>A</option>
<option>B</option>
<option selected>C</option>
<option>D</option>
<option>E</option>
<option>F</option>
<option>G</option>
<option>H</option>
<option>I</option>
<option>J</option>
<option>K</option>
<option>L</option>
<option>M</option>
<option>N</option>
<option>O</option>
<option>P</option>
<option>Q</option>
<option>R</option>
<option>S</option>
<option>T</option>
<option>U</option>
<option>V</option>
<option>W</option>
<option>X</option>
<option>Y</option>
<option>Z</option>
<option>A</option>
<option>B</option>
<option selected>C</option>
<option>D</option>
<option>E</option>
<option>F</option>
<option>G</option>
<option>H</option>
<option>I</option>
<option>J</option>
<option>K</option>
<option>L</option>
<option>M</option>
<option>N</option>
<option>O</option>
<option>P</option>
<option>Q</option>
<option>R</option>
<option>S</option>
<option>T</option>
<option>U</option>
<option>V</option>
<option>W</option>
<option>X</option>
<option>Y</option>
<option>Z</option>
</select>
</td>
<td role="radiogroup" aria-labelledby="lblLinkUnlink">
<input id="rbLink" type="radio" name="Link" value="LINK" checked><label for="rbLink">Link</label>
<input id="rbLink" type="radio" name="Link" value="LINK" checked><label for="rbLink">Link</label>
<input id="rbUnlink" type="radio" name="Link" value="UNLINK"><label for="rbUnlink">UnLink</label>
</td>
<td>
@@ -218,20 +166,21 @@ fclose($dextraFile);
<?php
exec ("pgrep pistar-keeper", $pids);
if (!empty($pids))
{
echo "<br />\n";
echo "<b>PiStar-Keeper Logbook</b><input type=button onClick=\"location.href='/admin/pistar-keeper-download.php'\" value=\"Download Logbook\">\n";
echo "<table>\n";
echo " <tr>\n";
echo " <th><a class=tooltip href=\"#\">PiStar-Keeper Log Entries (UTC)<span><b>PiStar-Keeper Log Entries (UTC)</b></span></th>\n";
echo " </tr>\n";
{
echo "<br />\n";
echo "<b>PiStar-Keeper Logbook</b><input type=button onClick=\"location.href='/admin/pistar-keeper-download.php'\" value=\"Download Logbook\">\n";
echo "<table>\n";
echo " <tr>\n";
echo " <th><a class=tooltip href=\"#\">PiStar-Keeper Log Entries (UTC)<span><b>PiStar-Keeper Log Entries (UTC)</b></span></th>\n";
echo " </tr>\n";
exec ("tail -n 5 /var/pistar-keeper/pistar-keeper.log", $lines);
$counter = 0;
foreach ($lines as $line) {
echo "<tr><td align=\"left\">".$lines[$counter]."</td></tr>\n";
$counter++;
}
exec ("tail -n 5 /var/pistar-keeper/pistar-keeper.log", $lines);
$counter = 0;
foreach ($lines as $line) {
echo "<tr><td align=\"left\">".$lines[$counter]."</td></tr>\n";
$counter++;
}
echo "</table>\n";
}
echo "</table>\n";
}
?>